*Warning* long post - OpenSSH patch to allow requiring *both* public key and password auth

From: Stephen Biggs (openssh_at_yihye-beseder.no-ip.com)
Date: 07/21/05

  • Next message: Stephen Biggs: "Oops.. mea culpa..."
    To: secureshell@securityfocus.com
    Date: Thu, 21 Jul 2005 23:26:20 +0300
    
    

    Greetings.

    I am submitting this patch to both the OpenBSD tech mailing list and the
    OpenSSH mailing list since this is a patch against the OpenBSD 3.7-
    stable version of OpenSSH and should interest both groups which might
    not have that much overlap.

    This is a patch that adds a "RequireBothPasswordAndPubKey" authorization
    option to the SSHD server. It is a hack but I tried to keep it as clean
    as possible. Owing to the deep dive into the code I had to do to get
    this to work, I found that the way that SSHd works is a bit strange to
    me, especially with PrivSep active; authentication is done twice, both
    in the privileged parent and in the child - I could most likely be
    missing something here, but that is the way it seems to me, thus I had
    to add my adjustments to two places in the code to get it to work.

    This is an attempt to enable SSHd to be able to enforce more of a
    paranoid security model in keeping with the classic "who you are"
    (biometrics, fingerprints, retina scan, brainwave), "what you know"
    (password), and "what you have" (smart card, private key). Since SSH
    isn't able to tie into any sort of biometric scanner, as far as I know
    (correct me if I'm wrong), two out of three is better than nothing.

    This works for me, but I would most appreciate comments on this patch as
    well as any other sets of eyes (especially any who have developed SSH!)
    to be able to read this patch and let me know if I have created any
    gaping security holes or race conditions.

    $ diffstat ssh-both-pubkey-passwd.patch
     auth.h | 9 +++++++++
     auth2-passwd.c | 1 +
     auth2-pubkey.c | 1 +
     auth2.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
     monitor.c | 3 +++
     servconf.c | 12 ++++++++++++
     servconf.h | 1 +
     sshd_config.5 | 14 ++++++++++++++
     8 files changed, 85 insertions(+), 1 deletion(-)

    $ cd /usr/src
    $ export CVSROOT=anoncvs@anoncvs.nyc.openbsd.org:/cvs
    $ cvs -q -d$CVSROOT diff -ud -rOPENBSD_3_7 usr.bin/ssh

    Index: usr.bin/ssh/auth.h
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/auth.h,v
    retrieving revision 1.50
    diff -u -d -r1.50 auth.h
    --- usr.bin/ssh/auth.h 23 May 2004 23:59:53 -0000 1.50
    +++ usr.bin/ssh/auth.h 21 Jul 2005 19:27:30 -0000
    @@ -68,6 +68,14 @@
             char *krb5_ticket_file;
     #endif
             void *methoddata;
    + union {
    + struct {
    + unsigned passwd : 1;
    + unsigned pubkey : 1;
    + } pubkey_passwd;
    + unsigned flags;
    + } multiple_auth;
    + unsigned multiple_auth_failure : 1;
     };
     /*
      * Every authentication method has to handle authentication requests for
    @@ -135,6 +143,7 @@
     
     int auth2_challenge(Authctxt *, char *);
     void auth2_challenge_stop(Authctxt *);
    +void auth2_multiple_auth(const char *, Authctxt *, int *);
     int bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **);
     int bsdauth_respond(void *, u_int, char **);
     int skey_query(void *, char **, char **, u_int *, char ***, u_int **);
    Index: usr.bin/ssh/auth2-passwd.c
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/auth2-passwd.c,v
    retrieving revision 1.5
    diff -u -d -r1.5 auth2-passwd.c
    --- usr.bin/ssh/auth2-passwd.c 31 Dec 2003 00:24:50 -0000 1.5
    +++ usr.bin/ssh/auth2-passwd.c 21 Jul 2005 19:27:30 -0000
    @@ -59,6 +59,7 @@
                     authenticated = 1;
             memset(password, 0, len);
             xfree(password);
    + auth2_multiple_auth("password", authctxt, &authenticated);
             return authenticated;
     }
     
    Index: usr.bin/ssh/auth2-pubkey.c
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/auth2-pubkey.c,v
    retrieving revision 1.9
    diff -u -d -r1.9 auth2-pubkey.c
    --- usr.bin/ssh/auth2-pubkey.c 11 Dec 2004 01:48:56 -0000 1.9
    +++ usr.bin/ssh/auth2-pubkey.c 21 Jul 2005 19:27:31 -0000
    @@ -129,6 +129,7 @@
                             authenticated = 1;
                     buffer_free(&b);
                     xfree(sig);
    + auth2_multiple_auth("publickey", authctxt, &authenticated);
             } else {
                     debug("test whether pkalg/pkblob are acceptable");
                     packet_check_eom();
    Index: usr.bin/ssh/auth2.c
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/auth2.c,v
    retrieving revision 1.107
    diff -u -d -r1.107 auth2.c
    --- usr.bin/ssh/auth2.c 28 Jul 2004 09:40:29 -0000 1.107
    +++ usr.bin/ssh/auth2.c 21 Jul 2005 19:27:31 -0000
    @@ -210,8 +210,12 @@
             /* Log before sending the reply */
             auth_log(authctxt, authenticated, method, " ssh2");
     
    - if (authctxt->postponed)
    + if (authctxt->postponed) {
    + if (!authctxt->multiple_auth_failure &&
    + authctxt->multiple_auth.flags)
    + goto fake_auth_failure;
                     return;
    + }
     
             /* XXX todo: check if multiple auth methods are needed */
             if (authenticated == 1) {
    @@ -225,6 +229,7 @@
             } else {
                     if (authctxt->failures++ > options.max_authtries)
                             packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
    +fake_auth_failure:
                     methods = authmethods_get();
                     packet_start(SSH2_MSG_USERAUTH_FAILURE);
                     packet_put_cstring(methods);
    @@ -233,6 +238,44 @@
                     packet_write_wait();
                     xfree(methods);
             }
    +}
    +
    +void
    +auth2_multiple_auth(const char *method, Authctxt *authctxt, int *authenticated)
    +{
    + debug3("%s[%d],entry,method %s,auth(%d,%d,%d),*auth %d,postponed %d",
    + __func__, getpid(), method, authctxt->multiple_auth_failure,
    + authctxt->multiple_auth.pubkey_passwd.passwd,
    + authctxt->multiple_auth.pubkey_passwd.pubkey, *authenticated,
    + authctxt->postponed);
    +
    + if (options.require_both_password_and_pub_key == 1 &&
    + (!strcasecmp(method, "password") ||
    + !strcasecmp(method, "publickey"))) {
    + if (!*authenticated && !strcasecmp(method, "publickey"))
    + authctxt->multiple_auth_failure = 1;
    + else if (!authctxt->multiple_auth_failure) {
    + if (!strcasecmp(method, "password"))
    + authctxt->
    + multiple_auth.pubkey_passwd.passwd = 1;
    + else if (!strcasecmp(method, "publickey"))
    + authctxt->
    + multiple_auth.pubkey_passwd.pubkey = 1;
    +
    + if (!authctxt->multiple_auth.pubkey_passwd.passwd ||
    + !authctxt->multiple_auth.pubkey_passwd.pubkey) {
    + authctxt->postponed = 1;
    + *authenticated = 0;
    + }
    + }
    + else
    + *authenticated = 0;
    + }
    + debug3("%s[%d],exit,multiple_auth (%d,%d,%d),*auth %d,postponed %d",
    + __func__, getpid(), authctxt->multiple_auth_failure,
    + authctxt->multiple_auth.pubkey_passwd.passwd,
    + authctxt->multiple_auth.pubkey_passwd.pubkey, *authenticated,
    + authctxt->postponed);
     }
     
     #define DELIM ","
    Index: usr.bin/ssh/monitor.c
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/monitor.c,v
    retrieving revision 1.63
    diff -u -d -r1.63 monitor.c
    --- usr.bin/ssh/monitor.c 10 Mar 2005 22:01:05 -0000 1.63
    +++ usr.bin/ssh/monitor.c 21 Jul 2005 19:27:34 -0000
    @@ -290,6 +290,9 @@
                             if (!authenticated)
                                     authctxt->failures++;
                     }
    + if ((ent->type == MONITOR_REQ_KEYVERIFY ||
    + ent->type == MONITOR_REQ_AUTHPASSWORD))
    + auth2_multiple_auth(auth_method, authctxt, &authenticated);
             }
     
             if (!authctxt->valid)
    Index: usr.bin/ssh/servconf.c
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/servconf.c,v
    retrieving revision 1.140
    diff -u -d -r1.140 servconf.c
    --- usr.bin/ssh/servconf.c 10 Mar 2005 22:01:05 -0000 1.140
    +++ usr.bin/ssh/servconf.c 21 Jul 2005 19:27:38 -0000
    @@ -96,6 +96,7 @@
             options->authorized_keys_file = NULL;
             options->authorized_keys_file2 = NULL;
             options->num_accept_env = 0;
    + options->require_both_password_and_pub_key = -1;
     
             /* Needs to be accessable in many places */
             use_privsep = -1;
    @@ -219,6 +220,11 @@
             }
             if (options->authorized_keys_file == NULL)
                     options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
    + if (options->require_both_password_and_pub_key == -1)
    + options->require_both_password_and_pub_key = 0;
    + else if (options->require_both_password_and_pub_key == 1)
    + options->password_authentication =
    + options->pubkey_authentication = 1;
     
             /* Turn privilege separation on by default */
             if (use_privsep == -1)
    @@ -249,6 +255,7 @@
             sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
             sGssAuthentication, sGssCleanupCreds, sAcceptEnv,
             sUsePrivilegeSeparation,
    + sRequireBothPasswordAndPubKey,
             sDeprecated, sUnsupported
     } ServerOpCodes;
     
    @@ -338,6 +345,7 @@
             { "authorizedkeysfile2", sAuthorizedKeysFile2 },
             { "useprivilegeseparation", sUsePrivilegeSeparation},
             { "acceptenv", sAcceptEnv },
    + { "requirebothpasswordandpubkey", sRequireBothPasswordAndPubKey },
             { NULL, sBadOption }
     };
     
    @@ -894,6 +902,10 @@
                                 xstrdup(arg);
                     }
                     break;
    +
    + case sRequireBothPasswordAndPubKey:
    + intptr = &options->require_both_password_and_pub_key;
    + goto parse_flag;
     
             case sDeprecated:
                     logit("%s line %d: Deprecated option %s",
    Index: usr.bin/ssh/servconf.h
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/servconf.h,v
    retrieving revision 1.71
    diff -u -d -r1.71 servconf.h
    --- usr.bin/ssh/servconf.h 23 Dec 2004 23:11:00 -0000 1.71
    +++ usr.bin/ssh/servconf.h 21 Jul 2005 19:27:38 -0000
    @@ -93,6 +93,7 @@
                                                      * authentication. */
             int kbd_interactive_authentication; /* If true, permit */
             int challenge_response_authentication;
    + int require_both_password_and_pub_key;
             int permit_empty_passwd; /* If false, do not permit empty
                                              * passwords. */
             int permit_user_env; /* If true, read ~/.ssh/environment */
    Index: usr.bin/ssh/sshd_config.5
    ===================================================================
    RCS file: /cvs/src/usr.bin/ssh/sshd_config.5,v
    retrieving revision 1.40
    diff -u -d -r1.40 sshd_config.5
    --- usr.bin/ssh/sshd_config.5 18 Mar 2005 17:05:00 -0000 1.40
    +++ usr.bin/ssh/sshd_config.5 21 Jul 2005 19:27:42 -0000
    @@ -566,6 +566,20 @@
     The default is
     .Dq yes .
     Note that this option applies to protocol version 2 only.
    +.It Cm RequireBothPasswordAndPubKey
    +Specifies whether both password and public key authentication must
    +be successful before authentication is allowed. If
    +.Dq yes ,
    +then this option performs both types of authentication, overriding any
    +values for the
    +.Cm PubkeyAuthentication
    +and
    +.Cm PasswordAuthentication
    +options, setting both implicitly to
    +.Dq yes .
    +The default is
    +.Dq no .
    +This option applies to protocol version 2 only.
     .It Cm RhostsRSAAuthentication
     Specifies whether rhosts or /etc/hosts.equiv authentication together
     with successful RSA host authentication is allowed.


  • Next message: Stephen Biggs: "Oops.. mea culpa..."

    Relevant Pages

    • fsck_ffs patch testers wanted
      ... This patch does not change the ... see if each inode is on the list. ... retrieving revision 1.29 ... diff -u -r1.32 fsck.h ...
      (freebsd-current)
    • New Patch [was: Re: cvs rm sys/posix4 && enable sem]
      ... posix4/sched.h to sys/psched.h in this patch). ... "steps" as, in place of a repo-copy, I'll commit the diff to ... retrieving revision 1.2 ... +To compile this driver into the kernel, ...
      (freebsd-arch)
    • Re: TCP listenall
      ... of the patch is at the end of this email. ... retrieving revision 1.40 ... diff -u -r1.370 tcp_input.c ...
      (freebsd-net)
    • Re: ATA driver races with interrupts
      ... > After applying the patch to sources cvsuped 2004.08.04.01.00.00, ... diff -u -r1.217 ata-all.c ... retrieving revision 1.79 ... /* record the request as running */ ...
      (freebsd-current)
    • Re: [PATCH] serial driver PMC MSP71xx, kernel linux-mips.git mast er
      ... > retrieving revision 1.1.1.7 ... Your patch is clearly garbled again, ... we have a regshift of 2 on this SoC and it could be different on other ... console output before the serial driver opens. ...
      (Linux-Kernel)