diff mbox

[2/3] vnc: support password expire

Message ID 1286450121-17153-3-git-send-email-kraxel@redhat.com
State New
Headers show

Commit Message

Gerd Hoffmann Oct. 7, 2010, 11:15 a.m. UTC
This patch adds support for expiring passwords to vnc.  It adds a new
lifetime parameter to the vnc_display_password() function, which
specifies the number of seconds the new password will be valid.  Passing
zero as lifetime maintains current behavior (password never expires).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 console.h |    2 +-
 monitor.c |    3 +--
 ui/vnc.c  |   15 ++++++++++++++-
 ui/vnc.h  |    1 +
 4 files changed, 17 insertions(+), 4 deletions(-)

Comments

Anthony Liguori Oct. 7, 2010, 7:53 p.m. UTC | #1
On 10/07/2010 06:15 AM, Gerd Hoffmann wrote:
> This patch adds support for expiring passwords to vnc.  It adds a new
> lifetime parameter to the vnc_display_password() function, which
> specifies the number of seconds the new password will be valid.  Passing
> zero as lifetime maintains current behavior (password never expires).
>
> Signed-off-by: Gerd Hoffmann<kraxel@redhat.com>
>    

This has been posted before and I've never understood it.  Why can't a 
management tool just expire passwords on it's own?

How does password expiration help with security at all?

Regards,

Anthony Liguori

> ---
>   console.h |    2 +-
>   monitor.c |    3 +--
>   ui/vnc.c  |   15 ++++++++++++++-
>   ui/vnc.h  |    1 +
>   4 files changed, 17 insertions(+), 4 deletions(-)
>
> diff --git a/console.h b/console.h
> index aafb031..24670e5 100644
> --- a/console.h
> +++ b/console.h
> @@ -368,7 +368,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
>   void vnc_display_init(DisplayState *ds);
>   void vnc_display_close(DisplayState *ds);
>   int vnc_display_open(DisplayState *ds, const char *display);
> -int vnc_display_password(DisplayState *ds, const char *password);
> +int vnc_display_password(DisplayState *ds, const char *password, int lifetime);
>   void do_info_vnc_print(Monitor *mon, const QObject *data);
>   void do_info_vnc(Monitor *mon, QObject **ret_data);
>   char *vnc_display_local_addr(DisplayState *ds);
> diff --git a/monitor.c b/monitor.c
> index fbb678d..d82eb9e 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -966,11 +966,10 @@ static int do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data)
>
>   static int change_vnc_password(const char *password)
>   {
> -    if (vnc_display_password(NULL, password)<  0) {
> +    if (vnc_display_password(NULL, password, 0)<  0) {
>           qerror_report(QERR_SET_PASSWD_FAILED);
>           return -1;
>       }
> -
>       return 0;
>   }
>
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 1ef0fc5..51aa9ca 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -2078,11 +2078,19 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
>       unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
>       int i, j, pwlen;
>       unsigned char key[8];
> +    time_t now;
>
>       if (!vs->vd->password || !vs->vd->password[0]) {
>           VNC_DEBUG("No password configured on server");
>           goto reject;
>       }
> +    if (vs->vd->expires) {
> +        time(&now);
> +        if (vs->vd->expires<  now) {
> +            VNC_DEBUG("Password is expired");
> +            goto reject;
> +        }
> +    }
>
>       memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
>
> @@ -2474,7 +2482,7 @@ void vnc_display_close(DisplayState *ds)
>   #endif
>   }
>
> -int vnc_display_password(DisplayState *ds, const char *password)
> +int vnc_display_password(DisplayState *ds, const char *password, int lifetime)
>   {
>       VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
>
> @@ -2492,6 +2500,11 @@ int vnc_display_password(DisplayState *ds, const char *password)
>           if (vs->auth == VNC_AUTH_NONE) {
>               vs->auth = VNC_AUTH_VNC;
>           }
> +        if (lifetime) {
> +            vs->expires = time(NULL) + lifetime;
> +        } else {
> +            vs->expires = 0;
> +        }
>       } else {
>           vs->auth = VNC_AUTH_NONE;
>       }
> diff --git a/ui/vnc.h b/ui/vnc.h
> index 9619b24..4f895be 100644
> --- a/ui/vnc.h
> +++ b/ui/vnc.h
> @@ -120,6 +120,7 @@ struct VncDisplay
>
>       char *display;
>       char *password;
> +    time_t expires;
>       int auth;
>       bool lossy;
>   #ifdef CONFIG_VNC_TLS
>
Daniel P. Berrangé Oct. 8, 2010, 10:08 a.m. UTC | #2
On Thu, Oct 07, 2010 at 02:53:05PM -0500, Anthony Liguori wrote:
> On 10/07/2010 06:15 AM, Gerd Hoffmann wrote:
> >This patch adds support for expiring passwords to vnc.  It adds a new
> >lifetime parameter to the vnc_display_password() function, which
> >specifies the number of seconds the new password will be valid.  Passing
> >zero as lifetime maintains current behavior (password never expires).
> >
> >Signed-off-by: Gerd Hoffmann<kraxel@redhat.com>
> >   
> 
> This has been posted before and I've never understood it.  Why can't a 
> management tool just expire passwords on it's own?

If the management tool crashes or is restarted for some reason
then it may miss the expiry task. 

> How does password expiration help with security at all?

VNC passwords are obviously rather weak, so if you can limit
the time the password is valid to the window in which you
are expecting the incoming VNC connection this limits the
time to attack the VNC password. A mgmt tool could do

  - Set a VNC password
  - Open the VNC connection
  - Clear the VNC password

If anything goes wrong in the mgmt tool at step 2 though,
then it may never to step 3, leaving the VNC server accessible.
If it had set a password expiry at step 1, it would have a
safety net that guarentees the password will be invalid after
'n' seconds, even if not explicitly cleared. Given how little
code this is in QEMU, I think it is a worthwhile feature.

Regards,
Daniel
Anthony Liguori Nov. 10, 2010, 3:50 p.m. UTC | #3
On 10/08/2010 05:08 AM, Daniel P. Berrange wrote:
> On Thu, Oct 07, 2010 at 02:53:05PM -0500, Anthony Liguori wrote:
>    
>> On 10/07/2010 06:15 AM, Gerd Hoffmann wrote:
>>      
>>> This patch adds support for expiring passwords to vnc.  It adds a new
>>> lifetime parameter to the vnc_display_password() function, which
>>> specifies the number of seconds the new password will be valid.  Passing
>>> zero as lifetime maintains current behavior (password never expires).
>>>
>>> Signed-off-by: Gerd Hoffmann<kraxel@redhat.com>
>>>
>>>        
>> This has been posted before and I've never understood it.  Why can't a
>> management tool just expire passwords on it's own?
>>      
> If the management tool crashes or is restarted for some reason
> then it may miss the expiry task.
>
>    
>> How does password expiration help with security at all?
>>      
> VNC passwords are obviously rather weak, so if you can limit
> the time the password is valid to the window in which you
> are expecting the incoming VNC connection this limits the
> time to attack the VNC password. A mgmt tool could do
>
>    - Set a VNC password
>    - Open the VNC connection
>    - Clear the VNC password
>
> If anything goes wrong in the mgmt tool at step 2 though,
> then it may never to step 3, leaving the VNC server accessible.
>    

I think the point is that you can expire the password by just changing 
it through the monitor.  Having an expiration policy builtin to QEMU (as 
opposed to libvirt) seems like the wrong place.

> If it had set a password expiry at step 1, it would have a
> safety net that guarentees the password will be invalid after
> 'n' seconds, even if not explicitly cleared. Given how little
> code this is in QEMU, I think it is a worthwhile feature.\
>    

It's a policy not a mechanism and I don't see a good reason to have the 
code in QEMU because it honestly is a policy for a specific product.  I 
don't think it's a strong enough policy that it's going to be seen as 
widely useful.

Regards,

Anthony Liguori

> Regards,
> Daniel
>
Gerd Hoffmann Nov. 11, 2010, 11:39 a.m. UTC | #4
Hi,

>> If anything goes wrong in the mgmt tool at step 2 though,
>> then it may never to step 3, leaving the VNC server accessible.
>
> I think the point is that you can expire the password by just changing
> it through the monitor.

Well, you can't really expire it, you can only set it to $randomvalue. 
Unsetting the vnc password also disables authentication (in unstable), 
which is *not* what you want here ...

> Having an expiration policy builtin to QEMU (as
> opposed to libvirt) seems like the wrong place.

IMHO it doesn't build policy into qemu.  It is still up to libvirt (or 
the management app building on top of libvirt) to decide if and when the 
password will expire.  qemu will just do what libvirt asks for.

Instead of passing a expire time as implemented by the patches:

   set-password $protocol $secret $time

we could add a expire-password command, then ask management to do

    set-password $protocol $secret
    [ let $time pass ]
    expire-password $protocol

I fail to see why this is better though.  The former is more robust and 
easier to implement in the management.  The amount of code needed in 
qemu is probably quite similar ...

cheers,
   Gerd
Anthony Liguori Nov. 16, 2010, 8:26 p.m. UTC | #5
On 11/11/2010 05:39 AM, Gerd Hoffmann wrote:
>   Hi,
>
>>> If anything goes wrong in the mgmt tool at step 2 though,
>>> then it may never to step 3, leaving the VNC server accessible.
>>
>> I think the point is that you can expire the password by just changing
>> it through the monitor.
>
> Well, you can't really expire it, you can only set it to $randomvalue. 
> Unsetting the vnc password also disables authentication (in unstable), 
> which is *not* what you want here ...
>
>> Having an expiration policy builtin to QEMU (as
>> opposed to libvirt) seems like the wrong place.
>
> IMHO it doesn't build policy into qemu.  It is still up to libvirt (or 
> the management app building on top of libvirt) to decide if and when 
> the password will expire.

Except if you want to cancel the expiration because the expiration 
policy changes.   You'd have to set the password without an expiration 
time and you may not have ready access to the password.

>   qemu will just do what libvirt asks for.
>
> Instead of passing a expire time as implemented by the patches:
>
>   set-password $protocol $secret $time
>
> we could add a expire-password command, then ask management to do
>
>    set-password $protocol $secret
>    [ let $time pass ]
>    expire-password $protocol
>
> I fail to see why this is better though.  The former is more robust 
> and easier to implement in the management.  The amount of code needed 
> in qemu is probably quite similar ...

But the later let's a management tool implement arbitrarily complex 
expiration policies.  It can also be used to generically disable any 
login which is effectively expiration but it may not be directly because 
of a timeout but rather because of some other operation.  For instance, 
a management tool might want to implement a login policy whereas you're 
only allowed to log into a VM during business hours (9-5).  Setting an 
expiration time for 8 hours is quite a bit less straight forward than 
just unsetting the password during the off hours.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
>
Gerd Hoffmann Nov. 17, 2010, 10:23 a.m. UTC | #6
Hi,

>>> Having an expiration policy builtin to QEMU (as
>>> opposed to libvirt) seems like the wrong place.
>>
>> IMHO it doesn't build policy into qemu. It is still up to libvirt (or
>> the management app building on top of libvirt) to decide if and when
>> the password will expire.
>
> Except if you want to cancel the expiration because the expiration
> policy changes. You'd have to set the password without an expiration
> time and you may not have ready access to the password.

Point.

>> set-password $protocol $secret
>> [ let $time pass ]
>> expire-password $protocol
>>
>> I fail to see why this is better though. The former is more robust and
>> easier to implement in the management. The amount of code needed in
>> qemu is probably quite similar ...
>
> But the later let's a management tool implement arbitrarily complex
> expiration policies.

Hmm, we could do this:

set-password $protocol $secret
expire-password $protocol [ now | never | $seconds ]

Comments?

cheers,
   Gerd
Anthony Liguori Nov. 20, 2010, 2:14 a.m. UTC | #7
On 11/17/2010 04:23 AM, Gerd Hoffmann wrote:
>> But the later let's a management tool implement arbitrarily complex
>> expiration policies.
>
>
> Hmm, we could do this:
>
> set-password $protocol $secret
> expire-password $protocol [ now | never | $seconds ]
>
> Comments?

I would be happy with this.  I don't mind a bit of policy creeping into 
qemu as long as we're exposing the underlying mechanisms.

If it were me, I'd do:

set-password $protocol $secret
unset-password $protocol
expire-password [never | $seconds]

And I would implement expire-password in terms of unset-password.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
>
diff mbox

Patch

diff --git a/console.h b/console.h
index aafb031..24670e5 100644
--- a/console.h
+++ b/console.h
@@ -368,7 +368,7 @@  void cocoa_display_init(DisplayState *ds, int full_screen);
 void vnc_display_init(DisplayState *ds);
 void vnc_display_close(DisplayState *ds);
 int vnc_display_open(DisplayState *ds, const char *display);
-int vnc_display_password(DisplayState *ds, const char *password);
+int vnc_display_password(DisplayState *ds, const char *password, int lifetime);
 void do_info_vnc_print(Monitor *mon, const QObject *data);
 void do_info_vnc(Monitor *mon, QObject **ret_data);
 char *vnc_display_local_addr(DisplayState *ds);
diff --git a/monitor.c b/monitor.c
index fbb678d..d82eb9e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -966,11 +966,10 @@  static int do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data)
 
 static int change_vnc_password(const char *password)
 {
-    if (vnc_display_password(NULL, password) < 0) {
+    if (vnc_display_password(NULL, password, 0) < 0) {
         qerror_report(QERR_SET_PASSWD_FAILED);
         return -1;
     }
-
     return 0;
 }
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 1ef0fc5..51aa9ca 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2078,11 +2078,19 @@  static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
     unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
     int i, j, pwlen;
     unsigned char key[8];
+    time_t now;
 
     if (!vs->vd->password || !vs->vd->password[0]) {
         VNC_DEBUG("No password configured on server");
         goto reject;
     }
+    if (vs->vd->expires) {
+        time(&now);
+        if (vs->vd->expires < now) {
+            VNC_DEBUG("Password is expired");
+            goto reject;
+        }
+    }
 
     memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
 
@@ -2474,7 +2482,7 @@  void vnc_display_close(DisplayState *ds)
 #endif
 }
 
-int vnc_display_password(DisplayState *ds, const char *password)
+int vnc_display_password(DisplayState *ds, const char *password, int lifetime)
 {
     VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
 
@@ -2492,6 +2500,11 @@  int vnc_display_password(DisplayState *ds, const char *password)
         if (vs->auth == VNC_AUTH_NONE) {
             vs->auth = VNC_AUTH_VNC;
         }
+        if (lifetime) {
+            vs->expires = time(NULL) + lifetime;
+        } else {
+            vs->expires = 0;
+        }
     } else {
         vs->auth = VNC_AUTH_NONE;
     }
diff --git a/ui/vnc.h b/ui/vnc.h
index 9619b24..4f895be 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -120,6 +120,7 @@  struct VncDisplay
 
     char *display;
     char *password;
+    time_t expires;
     int auth;
     bool lossy;
 #ifdef CONFIG_VNC_TLS