remember which public keys have been used for authentication and
authordjm <djm@openbsd.org>
Mon, 22 Dec 2014 07:51:30 +0000 (07:51 +0000)
committerdjm <djm@openbsd.org>
Mon, 22 Dec 2014 07:51:30 +0000 (07:51 +0000)
refuse to accept previously-used keys.

This allows AuthenticationMethods=publickey,publickey to require
that users authenticate using two _different_ pubkeys.

ok markus@

usr.bin/ssh/auth.h
usr.bin/ssh/auth2-pubkey.c
usr.bin/ssh/monitor.c
usr.bin/ssh/sshd_config.5

index 5287635..8a3eb5d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.78 2014/07/03 11:16:55 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.79 2014/12/22 07:51:30 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -37,6 +37,8 @@
 #include <krb5.h>
 #endif
 
+struct sshkey;
+
 typedef struct Authctxt Authctxt;
 typedef struct Authmethod Authmethod;
 typedef struct KbdintDevice KbdintDevice;
@@ -66,6 +68,9 @@ struct Authctxt {
        char            *krb5_ticket_file;
 #endif
        void            *methoddata;
+
+       struct sshkey   **prev_userkeys;
+       u_int            nprev_userkeys;
 };
 /*
  * Every authentication method has to handle authentication requests for
@@ -114,6 +119,8 @@ int  hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
 int     user_key_allowed(struct passwd *, Key *);
 void    pubkey_auth_info(Authctxt *, const Key *, const char *, ...)
            __attribute__((__format__ (printf, 3, 4)));
+void    auth2_record_userkey(Authctxt *, struct sshkey *);
+int     auth2_userkey_already_used(Authctxt *, struct sshkey *);
 
 struct stat;
 int     auth_secure_path(const char *, struct stat *, const char *, uid_t,
index 6fec8e5..f880717 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.43 2014/12/21 22:27:56 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.44 2014/12/22 07:51:30 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -38,6 +38,7 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <limits.h>
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -119,6 +120,10 @@ userauth_pubkey(Authctxt *authctxt)
                    "signature scheme");
                goto done;
        }
+       if (auth2_userkey_already_used(authctxt, key)) {
+               logit("refusing previously-used %s key", key_type(key));
+               goto done;
+       }
        if (have_sig) {
                sig = packet_get_string(&slen);
                packet_check_eom();
@@ -156,8 +161,12 @@ userauth_pubkey(Authctxt *authctxt)
                authenticated = 0;
                if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
                    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
-                   buffer_len(&b))) == 1)
+                   buffer_len(&b))) == 1) {
                        authenticated = 1;
+                       /* Record the successful key to prevent reuse */
+                       auth2_record_userkey(authctxt, key);
+                       key = NULL; /* Don't free below */
+               }
                buffer_free(&b);
                free(sig);
        } else {
@@ -679,6 +688,35 @@ user_key_allowed(struct passwd *pw, Key *key)
        return success;
 }
 
+/* Records a public key in the list of previously-successful keys */
+void
+auth2_record_userkey(Authctxt *authctxt, struct sshkey *key)
+{
+       struct sshkey **tmp;
+
+       if (authctxt->nprev_userkeys >= INT_MAX ||
+           (tmp = reallocarray(authctxt->prev_userkeys,
+           authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL)
+               fatal("%s: reallocarray failed", __func__);
+       authctxt->prev_userkeys = tmp;
+       authctxt->prev_userkeys[authctxt->nprev_userkeys] = key;
+       authctxt->nprev_userkeys++;
+}
+
+/* Checks whether a key has already been used successfully for authentication */
+int
+auth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key)
+{
+       u_int i;
+
+       for (i = 0; i < authctxt->nprev_userkeys; i++) {
+               if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
 Authmethod method_pubkey = {
        "publickey",
        userauth_pubkey,
index fb81186..637009c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.135 2014/07/15 15:54:14 millert Exp $ */
+/* $OpenBSD: monitor.c,v 1.136 2014/12/22 07:51:30 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -884,6 +884,7 @@ mm_answer_keyallowed(int sock, Buffer *m)
                switch (type) {
                case MM_USERKEY:
                        allowed = options.pubkey_authentication &&
+                           !auth2_userkey_already_used(authctxt, key) &&
                            user_key_allowed(authctxt->pw, key);
                        pubkey_auth_info(authctxt, key, NULL);
                        auth_method = "publickey";
@@ -1111,7 +1112,12 @@ mm_answer_keyverify(int sock, Buffer *m)
        debug3("%s: key %p signature %s",
            __func__, key, (verified == 1) ? "verified" : "unverified");
 
-       key_free(key);
+       /* If auth was successful then record key to ensure it isn't reused */
+       if (verified == 1)
+               auth2_record_userkey(authctxt, key);
+       else
+               key_free(key);
+
        free(blob);
        free(signature);
        free(data);
index 057eeaf..98985c0 100644 (file)
@@ -33,8 +33,8 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd_config.5,v 1.184 2014/12/21 23:35:14 jmc Exp $
-.Dd $Mdocdate: December 21 2014 $
+.\" $OpenBSD: sshd_config.5,v 1.185 2014/12/22 07:51:30 djm Exp $
+.Dd $Mdocdate: December 22 2014 $
 .Dt SSHD_CONFIG 5
 .Os
 .Sh NAME
@@ -210,6 +210,18 @@ would restrict keyboard interactive authentication to the
 .Dq bsdauth
 device.
 .Pp
+If the
+.Dq publickey
+method is listed more than one,
+.Xr sshd 8
+verifies that keys that have been used successfully are not reused for
+subsequent authentications.
+For example, an
+.Cm AuthenticationMethods
+of
+.Dq publickey,publickey
+will require successful authentication using two different public keys.
+.Pp
 This option is only available for SSH protocol 2 and will yield a fatal
 error if enabled if protocol 1 is also enabled.
 Note that each authentication method listed should also be explicitly enabled