Bugzilla – Attachment 2710 Details for
Bug 2319
[PATCH REVIEW] U2F authentication
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
patch rebased to current upstream
0001-Implement-U2F-support-with-u2f-requires-libu2f-host.patch (text/plain), 38.42 KB, created by
Jakub Jelen
on 2015-09-17 19:08:19 AEST
(
hide
)
Description:
patch rebased to current upstream
Filename:
MIME Type:
Creator:
Jakub Jelen
Created:
2015-09-17 19:08:19 AEST
Size:
38.42 KB
patch
obsolete
>From 56a40950fe760f1838598d2c990f3ae46d408af7 Mon Sep 17 00:00:00 2001 >From: Michael Stapelberg <stapelberg+openssh@google.com> >Date: Sat, 20 Dec 2014 00:19:50 +0100 >Subject: [PATCH 1/9] Implement U2F support (--with-u2f, requires libu2f-host). >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >Recently, the FIDO alliance announced U2F [1], and Google announced >that it supports U2F tokens (âsecurity keysâ) for Google accounts [2]. >As the spec is not a very short read, I gave a presentation last week >about U2F which may be a good quick introduction to the details [3]. > >For the rest of this description, Iâll assume that you read either my >presentation or the U2F spec. (side note: Iâm not working on U2F, >playing around with it and implementing it in OpenSSH is my private >fun project :)) > >This commit adds U2F support to OpenSSH. More specifically, it adds an >authentication mechanism called âu2fâ, together with the ssh-u2f key >format. > >The new u2f authentication mechanism can operate in two modes, specified >by the client with the U2FMode option: registration (necessary once per >U2F security key) or authentication (the default). > >Since U2F is a two-factor authentication mechanism, you should never use >it as the sole AuthenticationMethod. Therefore, whenever you enable >U2FAuthentication, please also set AuthenticationMethods on the server. >As an example, add the following to your sshd_config: > > U2FAuthentication yes > AuthenticationMethods publickey,u2f > >(This assumes that you always enter your passphrase for the pubkey, > otherwise perhaps AuthenticationMethods password,u2f would be a better > choice â YMMV.) > >For users without an ssh-u2f key in their authorized_keys file, this is >a noop and will not change behavior â the u2f authentication method will >just always report success in this case. > >For users with at least one ssh-u2f key in their authorized_keys, the >user must have the U2F security key in order to login. The server will >send a challenge, and ssh(1) on the userâs machine will ask the user to >touch the U2F security key. Upon being touched, the U2F security key >cryptographically signs the challenge, and the server can verify that >the registered security key is indeed present. > >To register a U2F security key, use: > > ssh -o U2FMode=registration my.server.example > /tmp/u2f-key.pub > >Now append the contents of /tmp/u2f-key.pub to your authorized_keys file >on the server. > >From now on, you should be prompted to touch the registered U2F security >key after successful publickey authentication. > >In case you want to register another U2F security key, just repeat the >process. > >Thanks to Thomas Habets, Christian Svensson and Axel Wagner for their >support in implementing/discussing/testing this feature. > >[1] https://fidoalliance.org/ >[2] http://googleonlinesecurity.blogspot.ch/2014/10/strengthening-2-step-verification-with.html >[3] https://www.noname-ev.de/w/File:C14h-u2f-how-security-keys-work.pdf >--- > Makefile.in | 1 + > audit-linux.c | 1 + > audit.c | 3 + > audit.h | 1 + > auth.h | 10 ++++ > auth2.c | 6 ++ > configure.ac | 60 ++++++++++++++++++- > monitor.c | 69 ++++++++++++++++++++++ > monitor.h | 2 + > monitor_wrap.c | 60 +++++++++++++++++++ > monitor_wrap.h | 4 ++ > readconf.c | 26 +++++++++ > readconf.h | 2 + > servconf.c | 20 +++++++ > servconf.h | 1 + > ssh.1 | 2 + > ssh.c | 11 ++++ > ssh_config.5 | 10 ++++ > sshconnect.c | 2 +- > sshconnect.h | 2 +- > sshconnect2.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- > sshd.c | 2 + > sshd_config.5 | 19 ++++++ > sshkey.c | 83 +++++++++++++++++++++++++++ > sshkey.h | 6 ++ > 25 files changed, 575 insertions(+), 6 deletions(-) > >diff --git a/Makefile.in b/Makefile.in >index 0901f42..5b89491 100644 >--- a/Makefile.in >+++ b/Makefile.in >@@ -106,6 +106,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ > auth2-none.o auth2-passwd.o auth2-pubkey.o \ > monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ > auth2-gss.o gss-serv.o gss-serv-krb5.o \ >+ auth-u2f.o \ > loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ > sftp-server.o sftp-common.o \ > roaming_common.o roaming_serv.o \ >diff --git a/audit-linux.c b/audit-linux.c >index b3ee2f4..c07cb7c 100644 >--- a/audit-linux.c >+++ b/audit-linux.c >@@ -113,6 +113,7 @@ audit_event(ssh_audit_event_t event) > case SSH_AUTH_FAIL_PUBKEY: > case SSH_AUTH_FAIL_HOSTBASED: > case SSH_AUTH_FAIL_GSSAPI: >+ case SSH_AUTH_FAIL_U2F: > case SSH_INVALID_USER: > linux_audit_record_event(-1, audit_username(), NULL, > get_remote_ipaddr(), "sshd", 0); >diff --git a/audit.c b/audit.c >index ced57fa..ddb949b 100644 >--- a/audit.c >+++ b/audit.c >@@ -63,6 +63,8 @@ audit_classify_auth(const char *method) > return SSH_AUTH_FAIL_HOSTBASED; > else if (strcmp(method, "gssapi-with-mic") == 0) > return SSH_AUTH_FAIL_GSSAPI; >+ else if (strcmp(method, "u2f") == 0) >+ return SSH_AUTH_FAIL_U2F; > else > return SSH_AUDIT_UNKNOWN; > } >@@ -98,6 +100,7 @@ audit_event_lookup(ssh_audit_event_t ev) > {SSH_AUTH_FAIL_PUBKEY, "AUTH_FAIL_PUBKEY"}, > {SSH_AUTH_FAIL_HOSTBASED, "AUTH_FAIL_HOSTBASED"}, > {SSH_AUTH_FAIL_GSSAPI, "AUTH_FAIL_GSSAPI"}, >+ {SSH_AUTH_FAIL_U2F, "AUTH_FAIL_U2F"}, > {SSH_INVALID_USER, "INVALID_USER"}, > {SSH_NOLOGIN, "NOLOGIN"}, > {SSH_CONNECTION_CLOSE, "CONNECTION_CLOSE"}, >diff --git a/audit.h b/audit.h >index 92ede5b..f99191a 100644 >--- a/audit.h >+++ b/audit.h >@@ -39,6 +39,7 @@ enum ssh_audit_event_type { > SSH_AUTH_FAIL_PUBKEY, /* ssh2 pubkey or ssh1 rsa */ > SSH_AUTH_FAIL_HOSTBASED, /* ssh2 hostbased or ssh1 rhostsrsa */ > SSH_AUTH_FAIL_GSSAPI, >+ SSH_AUTH_FAIL_U2F, > SSH_INVALID_USER, > SSH_NOLOGIN, /* denied by /etc/nologin, not implemented */ > SSH_CONNECTION_CLOSE, /* closed after attempting auth or session */ >diff --git a/auth.h b/auth.h >index 8b27575..355e925 100644 >--- a/auth.h >+++ b/auth.h >@@ -76,6 +76,11 @@ struct Authctxt { > char *krb5_ticket_file; > char *krb5_ccname; > #endif >+#ifdef U2F >+ Key *u2f_key; >+ char *u2f_challenge; >+ int u2f_attempt; >+#endif > Buffer *loginmsg; > void *methoddata; > >@@ -132,6 +137,11 @@ void pubkey_auth_info(Authctxt *, const Key *, const char *, ...) > void auth2_record_userkey(Authctxt *, struct sshkey *); > int auth2_userkey_already_used(Authctxt *, struct sshkey *); > >+#ifdef U2F >+Key *read_user_u2f_key(struct passwd *, u_int); >+int verify_u2f_user(Key *, u_char *, size_t, u_char *, size_t); >+#endif >+ > struct stat; > int auth_secure_path(const char *, struct stat *, const char *, uid_t, > char *, size_t); >diff --git a/auth2.c b/auth2.c >index 7177962..a0ac632 100644 >--- a/auth2.c >+++ b/auth2.c >@@ -72,6 +72,9 @@ extern Authmethod method_hostbased; > #ifdef GSSAPI > extern Authmethod method_gssapi; > #endif >+#ifdef U2F >+extern Authmethod method_u2f; >+#endif > > Authmethod *authmethods[] = { > &method_none, >@@ -79,6 +82,9 @@ Authmethod *authmethods[] = { > #ifdef GSSAPI > &method_gssapi, > #endif >+#ifdef U2F >+ &method_u2f, >+#endif > &method_passwd, > &method_kbdint, > &method_hostbased, >diff --git a/configure.ac b/configure.ac >index d900df4..1c1a433 100644 >--- a/configure.ac >+++ b/configure.ac >@@ -1423,6 +1423,59 @@ AC_ARG_WITH([skey], > ] > ) > >+# Check whether user wants u2f support (using libu2f-host) >+U2F_MSG="no" >+AC_ARG_WITH([u2f], >+ [ --with-u2f[[=PATH]] Enable U2F support (using libu2f-host)], >+ [ if test "x$withval" != "xno" ; then >+ if test "x$withval" = "xyes" ; then >+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) >+ if test "x$PKGCONFIG" != "xno"; then >+ AC_MSG_CHECKING([if $PKGCONFIG knows about u2f-host]) >+ if "$PKGCONFIG" u2f-host; then >+ AC_MSG_RESULT([yes]) >+ use_pkgconfig_for_libu2fhost=yes >+ else >+ AC_MSG_RESULT([no]) >+ fi >+ fi >+ else >+ CPPFLAGS="$CPPFLAGS -I${withval}/include" >+ if test -n "${need_dash_r}"; then >+ LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" >+ else >+ LDFLAGS="-L${withval}/lib ${LDFLAGS}" >+ fi >+ fi >+ if test "x$use_pkgconfig_for_libu2fhost" = "xyes"; then >+ LIBU2FHOST=`$PKGCONFIG --libs u2f-host` >+ CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags u2f-host`" >+ else >+ LIBU2FHOST="-lu2f-host" >+ fi >+ LIBS="$LIBS $LIBU2FHOST" >+ AC_CHECK_LIB([u2f-host], [u2fh_global_init], >+ [ AC_DEFINE([U2F], [1], [Enable U2F support (using libu2f-host)]) >+ U2F_MSG="yes" >+ AC_SUBST([LIBU2FHOST]) >+ ], >+ [ AC_MSG_ERROR([libu2f-host not found]) ], >+ [ $LIBS ] >+ ) >+ AC_MSG_CHECKING([if libu2f-host version is compatible]) >+ AC_COMPILE_IFELSE( >+ [AC_LANG_PROGRAM([[ #include <u2f-host.h> ]], >+ [[ >+ u2fh_global_init(0); >+ exit(0); >+ ]])], >+ [ AC_MSG_RESULT([yes]) ], >+ [ AC_MSG_RESULT([no]) >+ AC_MSG_ERROR([u2f-host version is not compatible]) ] >+ ) >+ fi ] >+) >+ > # Check whether user wants to use ldns > LDNS_MSG="no" > AC_ARG_WITH(ldns, >@@ -2318,7 +2371,7 @@ AC_ARG_WITH([ssl-engine], > ) > > if test "x$openssl" = "xyes" ; then >- LIBS="-lcrypto $LIBS" >+ LIBS="-lcrypto -lssl $LIBS" > AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1], > [Define if your ssl headers are included > with #include <openssl/header.h>])], >@@ -2401,8 +2454,8 @@ if test "x$openssl" = "xyes" ; then > ssl_library_ver=`cat conftest.ssllibver` > # Check version is supported. > case "$ssl_library_ver" in >- 0090[[0-7]]*|009080[[0-5]]*) >- AC_MSG_ERROR([OpenSSL >= 0.9.8f required (have "$ssl_library_ver")]) >+ 0090*) >+ AC_MSG_ERROR([OpenSSL >= 1.0.0 required (have "$ssl_library_ver")]) > ;; > *) ;; > esac >@@ -4931,6 +4984,7 @@ echo " KerberosV support: $KRB5_MSG" > echo " SELinux support: $SELINUX_MSG" > echo " Smartcard support: $SCARD_MSG" > echo " S/KEY support: $SKEY_MSG" >+echo " U2F support: $U2F_MSG" > echo " MD5 password support: $MD5_MSG" > echo " libedit support: $LIBEDIT_MSG" > echo " Solaris process contract support: $SPC_MSG" >diff --git a/monitor.c b/monitor.c >index 0af23d6..bd518ae 100644 >--- a/monitor.c >+++ b/monitor.c >@@ -2,6 +2,7 @@ > /* > * Copyright 2002 Niels Provos <provos@citi.umich.edu> > * Copyright 2002 Markus Friedl <markus@openbsd.org> >+ * Copyright 2014 Google Inc. > * All rights reserved. > * > * Redistribution and use in source and binary forms, with or without >@@ -164,6 +165,11 @@ int mm_answer_audit_event(int, Buffer *); > int mm_answer_audit_command(int, Buffer *); > #endif > >+#ifdef U2F >+int mm_answer_read_user_u2f_key(int, Buffer *); >+int mm_answer_verify_u2f_user(int, Buffer *); >+#endif >+ > static int monitor_read_log(struct monitor *); > > static Authctxt *authctxt; >@@ -235,6 +241,10 @@ struct mon_table mon_dispatch_proto20[] = { > {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, > {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, > #endif >+#ifdef U2F >+ {MONITOR_REQ_READUSERU2FKEY, MON_AUTH, mm_answer_read_user_u2f_key}, >+ {MONITOR_REQ_VERIFYU2FUSER, MON_AUTH, mm_answer_verify_u2f_user}, >+#endif > {0, 0, NULL} > }; > >@@ -1806,6 +1816,7 @@ mm_answer_audit_event(int socket, Buffer *m) > case SSH_AUTH_FAIL_PUBKEY: > case SSH_AUTH_FAIL_HOSTBASED: > case SSH_AUTH_FAIL_GSSAPI: >+ case SSH_AUTH_FAIL_U2F: > case SSH_LOGIN_EXCEED_MAXTRIES: > case SSH_LOGIN_ROOT_DENIED: > case SSH_CONNECTION_CLOSE: >@@ -2054,3 +2065,61 @@ mm_answer_gss_userok(int sock, Buffer *m) > } > #endif /* GSSAPI */ > >+#ifdef U2F >+int >+mm_answer_read_user_u2f_key(int sock, Buffer *m) >+{ >+ int authenticated = 0; >+ Key *key; >+ u_int key_idx; >+ u_char *blob = NULL; >+ u_int blen = 0; >+ >+ key_idx = buffer_get_int(m); >+ buffer_clear(m); >+ >+ key = read_user_u2f_key(authctxt->pw, key_idx); >+ buffer_put_int(m, key == NULL ? 1 : 0); >+ if (key != NULL) >+ { >+ if (key_to_blob(key, &blob, &blen) == 0) >+ fatal("%s: key_to_blob failed", __func__); >+ buffer_put_string(m, blob, blen); >+ debug3("%s: sending key", __func__); >+ } else { >+ debug3("%s: no key to send", __func__); >+ if (key_idx == 0) { >+ auth_method = "u2f"; >+ authenticated = 1; >+ } >+ } >+ >+ mm_request_send(sock, MONITOR_ANS_READUSERU2FKEY, m); >+ return authenticated; >+} >+ >+int >+mm_answer_verify_u2f_user(int sock, Buffer *m) >+{ >+ int authenticated = 0; >+ Key *key; >+ u_char *blob, *dgst, *sig; >+ u_int bloblen, dgstlen, siglen; >+ >+ blob = buffer_get_string(m, &bloblen); >+ key = key_from_blob(blob, bloblen); >+ dgst = buffer_get_string(m, &dgstlen); >+ sig = buffer_get_string(m, &siglen); >+ >+ buffer_clear(m); >+ >+ authenticated = verify_u2f_user(key, dgst, dgstlen, sig, siglen); >+ buffer_put_int(m, authenticated); >+ >+ auth_method = "u2f"; >+ mm_request_send(sock, MONITOR_ANS_VERIFYU2FUSER, m); >+ >+ key_free(key); >+ return authenticated; >+} >+#endif /* U2F */ >diff --git a/monitor.h b/monitor.h >index 93b8b66..1b26fe5 100644 >--- a/monitor.h >+++ b/monitor.h >@@ -56,6 +56,8 @@ enum monitor_reqtype { > MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47, > MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49, > MONITOR_REQ_TERM = 50, >+ MONITOR_REQ_READUSERU2FKEY = 52, MONITOR_ANS_READUSERU2FKEY = 53, >+ MONITOR_REQ_VERIFYU2FUSER = 54, MONITOR_ANS_VERIFYU2FUSER = 55, > > MONITOR_REQ_PAM_START = 100, > MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, >diff --git a/monitor_wrap.c b/monitor_wrap.c >index eac421b..2a31227 100644 >--- a/monitor_wrap.c >+++ b/monitor_wrap.c >@@ -2,6 +2,7 @@ > /* > * Copyright 2002 Niels Provos <provos@citi.umich.edu> > * Copyright 2002 Markus Friedl <markus@openbsd.org> >+ * Copyright 2014 Google Inc. > * All rights reserved. > * > * Redistribution and use in source and binary forms, with or without >@@ -1087,3 +1088,62 @@ mm_ssh_gssapi_userok(char *user) > } > #endif /* GSSAPI */ > >+#ifdef U2F >+Key * >+mm_read_user_u2f_key(struct passwd *pw, u_int key_idx) >+{ >+ Buffer m; >+ Key *key = NULL; >+ u_char *blob; >+ u_int blen; >+ u_int is_null; >+ >+ debug3("%s entering", __func__); >+ >+ buffer_init(&m); >+ buffer_put_int(&m, key_idx); >+ >+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_READUSERU2FKEY, &m); >+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_READUSERU2FKEY, &m); >+ >+ is_null = buffer_get_int(&m); >+ if (is_null == 0) { >+ blob = buffer_get_string(&m, &blen); >+ if ((key = key_from_blob(blob, blen)) == NULL) >+ fatal("%s: key_from_blob failed", __func__); >+ >+ free(blob); >+ } >+ >+ buffer_free(&m); >+ return key; >+} >+ >+int >+mm_verify_u2f_user(Key *key, u_char * dgst, size_t dgstlen, u_char * sig, size_t siglen) >+{ >+ int authenticated = 0; >+ Buffer m; >+ u_char *blob; >+ u_int blen; >+ >+ debug3("%s entering", __func__); >+ >+ if (key_to_blob(key, &blob, &blen) == 0) >+ fatal("%s: key_to_blob failed", __func__); >+ buffer_init(&m); >+ buffer_put_string(&m, blob, blen); >+ free(blob); >+ >+ buffer_put_string(&m, dgst, dgstlen); >+ buffer_put_string(&m, sig, siglen); >+ >+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_VERIFYU2FUSER, &m); >+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_VERIFYU2FUSER, &m); >+ >+ authenticated = buffer_get_int(&m); >+ buffer_free(&m); >+ >+ return authenticated; >+} >+#endif /* U2F */ >diff --git a/monitor_wrap.h b/monitor_wrap.h >index de4a08f..ce610c4 100644 >--- a/monitor_wrap.h >+++ b/monitor_wrap.h >@@ -53,6 +53,10 @@ int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); > int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); > int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *); > BIGNUM *mm_auth_rsa_generate_challenge(Key *); >+#ifdef U2F >+Key *mm_read_user_u2f_key(struct passwd *, u_int); >+int mm_verify_u2f_user(Key *, u_char *, size_t, u_char *, size_t); >+#endif > > #ifdef GSSAPI > OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); >diff --git a/readconf.c b/readconf.c >index 354e292..5b09705 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -149,6 +149,7 @@ typedef enum { > oAddressFamily, oGssAuthentication, oGssDelegateCreds, > oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, > oSendEnv, oControlPath, oControlMaster, oControlPersist, >+ oU2FAuthentication, oU2FMode, > oHashKnownHosts, > oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, > oVisualHostKey, oUseRoaming, >@@ -197,6 +198,13 @@ static struct { > { "gssapiauthentication", oUnsupported }, > { "gssapidelegatecredentials", oUnsupported }, > #endif >+#ifdef U2F >+ { "u2fauthentication", oU2FAuthentication }, >+ { "u2fmode", oU2FMode }, >+#else >+ { "u2fmode", oUnsupported }, >+ { "u2fauthentication", oUnsupported }, >+#endif > { "fallbacktorsh", oDeprecated }, > { "usersh", oDeprecated }, > { "identityfile", oIdentityFile }, >@@ -890,6 +898,14 @@ parse_time: > intptr = &options->challenge_response_authentication; > goto parse_flag; > >+ case oU2FAuthentication: >+ intptr = &options->u2f_authentication; >+ goto parse_flag; >+ >+ case oU2FMode: >+ charptr = &options->u2f_mode; >+ goto parse_string; >+ > case oGssAuthentication: > intptr = &options->gss_authentication; > goto parse_flag; >@@ -1605,6 +1621,8 @@ initialize_options(Options * options) > options->password_authentication = -1; > options->kbd_interactive_authentication = -1; > options->kbd_interactive_devices = NULL; >+ options->u2f_authentication = -1; >+ options->u2f_mode = NULL; > options->rhosts_rsa_authentication = -1; > options->hostbased_authentication = -1; > options->batch_mode = -1; >@@ -1833,6 +1851,10 @@ fill_default_options(Options * options) > options->tun_remote = SSH_TUNID_ANY; > if (options->permit_local_command == -1) > options->permit_local_command = 0; >+ if (options->u2f_authentication == -1) >+ options->u2f_authentication = 1; >+ if (options->u2f_mode == NULL) >+ options->u2f_mode = strdup("authentication"); > if (options->use_roaming == -1) > options->use_roaming = 1; > if (options->visual_host_key == -1) >@@ -2272,6 +2294,10 @@ dump_client_config(Options *o, const char *host) > dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); > dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); > #endif /* GSSAPI */ >+#ifdef U2F >+ dump_cfg_fmtint(oU2FAuthentication, o->u2f_authentication); >+ dump_cfg_string(oU2FMode, o->u2f_mode); >+#endif /* GSSAPI */ > dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); > dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); > dump_cfg_fmtint(oIdentitiesOnly, o->identities_only); >diff --git a/readconf.h b/readconf.h >index bb2d552..6a8644e 100644 >--- a/readconf.h >+++ b/readconf.h >@@ -46,10 +46,12 @@ typedef struct { > /* Try S/Key or TIS, authentication. */ > int gss_authentication; /* Try GSS authentication */ > int gss_deleg_creds; /* Delegate GSS credentials */ >+ int u2f_authentication; > int password_authentication; /* Try password > * authentication. */ > int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ > char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ >+ char *u2f_mode; /* mode (registration or authentication) for U2F auth. */ > int batch_mode; /* Batch mode: do not ask for passwords. */ > int check_host_ip; /* Also keep track of keys for IP address */ > int strict_host_key_checking; /* Strict host key checking. */ >diff --git a/servconf.c b/servconf.c >index b5db0f7..665b4d3 100644 >--- a/servconf.c >+++ b/servconf.c >@@ -117,6 +117,7 @@ initialize_server_options(ServerOptions *options) > options->kerberos_ticket_cleanup = -1; > options->kerberos_get_afs_token = -1; > options->gss_authentication=-1; >+ options->u2f_authentication = -1; > options->gss_cleanup_creds = -1; > options->gss_strict_acceptor = -1; > options->password_authentication = -1; >@@ -273,6 +274,11 @@ fill_default_server_options(ServerOptions *options) > options->kerberos_get_afs_token = 0; > if (options->gss_authentication == -1) > options->gss_authentication = 0; >+ // U2F authentication is disabled by default. On its own, it does not >+ // provide adequate security, and it should be used as a second factor in >+ // combination with publickey, for example. >+ if (options->u2f_authentication == -1) >+ options->u2f_authentication = 0; > if (options->gss_cleanup_creds == -1) > options->gss_cleanup_creds = 1; > if (options->gss_strict_acceptor == -1) >@@ -412,6 +418,7 @@ typedef enum { > sHostKeyAlgorithms, > sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, > sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, >+ sU2FAuthentication, > sAcceptEnv, sPermitTunnel, > sMatch, sPermitOpen, sForceCommand, sChrootDirectory, > sUsePrivilegeSeparation, sAllowAgentForwarding, >@@ -491,6 +498,11 @@ static struct { > { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, > { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, > #endif >+#ifdef U2F >+ { "u2fauthentication", sU2FAuthentication, SSHCFG_ALL }, >+#else >+ { "u2fauthentication", sUnsupported, SSHCFG_ALL }, >+#endif > { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, > { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, > { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, >@@ -1239,6 +1251,10 @@ process_server_config_line(ServerOptions *options, char *line, > intptr = &options->gss_strict_acceptor; > goto parse_flag; > >+ case sU2FAuthentication: >+ intptr = &options->u2f_authentication; >+ goto parse_flag; >+ > case sPasswordAuthentication: > intptr = &options->password_authentication; > goto parse_flag; >@@ -1972,6 +1988,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) > > M_CP_INTOPT(password_authentication); > M_CP_INTOPT(gss_authentication); >+ M_CP_INTOPT(u2f_authentication); > M_CP_INTOPT(rsa_authentication); > M_CP_INTOPT(pubkey_authentication); > M_CP_INTOPT(kerberos_authentication); >@@ -2248,6 +2265,9 @@ dump_config(ServerOptions *o) > dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); > dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); > #endif >+#ifdef U2F >+ dump_cfg_fmtint(sU2FAuthentication, o->u2f_authentication); >+#endif > dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); > dump_cfg_fmtint(sKbdInteractiveAuthentication, > o->kbd_interactive_authentication); >diff --git a/servconf.h b/servconf.h >index f4137af..9526aeb 100644 >--- a/servconf.h >+++ b/servconf.h >@@ -124,6 +124,7 @@ typedef struct { > * authentication. */ > int kbd_interactive_authentication; /* If true, permit */ > int challenge_response_authentication; >+ int u2f_authentication; > int permit_empty_passwd; /* If false, do not permit empty > * passwords. */ > int permit_user_env; /* If true, read ~/.ssh/environment */ >diff --git a/ssh.1 b/ssh.1 >index 495b787..0451a23 100644 >--- a/ssh.1 >+++ b/ssh.1 >@@ -536,6 +536,8 @@ For full details of the options listed below, and their possible values, see > .It TCPKeepAlive > .It Tunnel > .It TunnelDevice >+.It U2FAuthentication >+.It U2FMode > .It UpdateHostKeys > .It UsePrivilegedPort > .It User >diff --git a/ssh.c b/ssh.c >index 91911d3..4d02b0c 100644 >--- a/ssh.c >+++ b/ssh.c >@@ -74,10 +74,15 @@ > #ifdef WITH_OPENSSL > #include <openssl/evp.h> > #include <openssl/err.h> >+#include <openssl/ssl.h> > #endif > #include "openbsd-compat/openssl-compat.h" > #include "openbsd-compat/sys-queue.h" > >+#ifdef U2F >+#include <u2f-host.h> >+#endif >+ > #include "xmalloc.h" > #include "ssh.h" > #include "ssh1.h" >@@ -942,6 +947,12 @@ main(int ac, char **av) > #ifdef WITH_OPENSSL > OpenSSL_add_all_algorithms(); > ERR_load_crypto_strings(); >+ SSL_load_error_strings(); >+#endif >+ >+#ifdef U2F >+ if (u2fh_global_init(0) != U2FH_OK) >+ fatal("u2fh_global_init() failed"); > #endif > > /* Initialize the command to execute on remote host. */ >diff --git a/ssh_config.5 b/ssh_config.5 >index 6c3c1b2..31d8aa9 100644 >--- a/ssh_config.5 >+++ b/ssh_config.5 >@@ -1600,6 +1600,16 @@ Presently, only > from OpenSSH 6.8 and greater support the > .Dq hostkeys@openssh.com > protocol extension used to inform the client of all the server's hostkeys. >+.It Cm U2FAuthentication >+Specifies whether user authentication based on U2F (Universal Second Factor) is allowed. The default is >+.Dq yes . >+.It Cm U2FMode >+Specifies which mode the U2F authentication method should use. Can be either >+.Dq authentication >+or >+.Dq registration . >+The default is >+.Dq authentication . > .It Cm UsePrivilegedPort > Specifies whether to use a privileged port for outgoing connections. > The argument must be >diff --git a/sshconnect.c b/sshconnect.c >index c9f88e0..5a6174b 100644 >--- a/sshconnect.c >+++ b/sshconnect.c >@@ -1353,7 +1353,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, > debug("Authenticating to %s:%d as '%s'", host, port, server_user); > if (compat20) { > ssh_kex2(host, hostaddr, port); >- ssh_userauth2(local_user, server_user, host, sensitive); >+ ssh_userauth2(local_user, server_user, host, port, sensitive); > } else { > #ifdef WITH_SSH1 > ssh_kex(host, hostaddr); >diff --git a/sshconnect.h b/sshconnect.h >index 0ea6e99..58302ed 100644 >--- a/sshconnect.h >+++ b/sshconnect.h >@@ -50,7 +50,7 @@ void ssh_kex(char *, struct sockaddr *); > void ssh_kex2(char *, struct sockaddr *, u_short); > > void ssh_userauth1(const char *, const char *, char *, Sensitive *); >-void ssh_userauth2(const char *, const char *, char *, Sensitive *); >+void ssh_userauth2(const char *, const char *, char *, u_short, Sensitive *); > > void ssh_put_password(char *); > int ssh_local_cmd(const char *); >diff --git a/sshconnect2.c b/sshconnect2.c >index 7751031..753de31 100644 >--- a/sshconnect2.c >+++ b/sshconnect2.c >@@ -2,6 +2,7 @@ > /* > * Copyright (c) 2000 Markus Friedl. All rights reserved. > * Copyright (c) 2008 Damien Miller. All rights reserved. >+ * Copyright (c) 2014 Google Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -30,6 +31,7 @@ > #include <sys/socket.h> > #include <sys/wait.h> > #include <sys/stat.h> >+#include <time.h> > > #include <errno.h> > #include <fcntl.h> >@@ -44,6 +46,10 @@ > #include <vis.h> > #endif > >+#ifdef U2F >+#include <u2f-host.h> >+#endif >+ > #include "openbsd-compat/sys-queue.h" > > #include "xmalloc.h" >@@ -71,6 +77,7 @@ > #include "uidswap.h" > #include "hostfile.h" > #include "ssherr.h" >+#include "u2f.h" > > #ifdef GSSAPI > #include "ssh-gss.h" >@@ -258,6 +265,7 @@ struct cauthctxt { > const char *server_user; > const char *local_user; > const char *host; >+ char *host_port; > const char *service; > struct cauthmethod *method; > sig_atomic_t success; >@@ -308,6 +316,13 @@ int input_gssapi_error(int, u_int32_t, void *); > int input_gssapi_errtok(int, u_int32_t, void *); > #endif > >+#ifdef U2F >+int userauth_u2f(Authctxt *authctxt); >+int input_userauth_u2f_authenticate(int type, u_int32_t seq, void *ctxt); >+int input_userauth_u2f_register(int type, u_int32_t seq, void *ctxt); >+int input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt); >+#endif >+ > void userauth(Authctxt *, char *); > > static int sign_and_send_pubkey(Authctxt *, Identity *); >@@ -320,6 +335,16 @@ static Authmethod *authmethod_lookup(const char *name); > static char *authmethods_get(void); > > Authmethod authmethods[] = { >+ // U2F needs to be the first authentication method, so that we use it once >+ // the server allows it. This enables server configurations containing e.g.: >+ // AuthenticationMethods password,u2f pubkey,u2f >+#ifdef U2F >+ {"u2f", >+ userauth_u2f, >+ NULL, >+ &options.u2f_authentication, >+ NULL}, >+#endif > #ifdef GSSAPI > {"gssapi-with-mic", > userauth_gssapi, >@@ -357,7 +382,7 @@ Authmethod authmethods[] = { > > void > ssh_userauth2(const char *local_user, const char *server_user, char *host, >- Sensitive *sensitive) >+ u_short port, Sensitive *sensitive) > { > Authctxt authctxt; > int type; >@@ -392,6 +417,7 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, > authctxt.server_user = server_user; > authctxt.local_user = local_user; > authctxt.host = host; >+ get_hostfile_hostname_ipaddr(host, NULL, port, &authctxt.host_port, NULL); > authctxt.service = "ssh-connection"; /* service name */ > authctxt.success = 0; > authctxt.method = authmethod_lookup("none"); >@@ -852,6 +878,156 @@ input_gssapi_error(int type, u_int32_t plen, void *ctxt) > } > #endif /* GSSAPI */ > >+#ifdef U2F >+int >+userauth_u2f(Authctxt *authctxt) >+{ >+ // first step: we dont send anything, but install a custom dispatcher. >+ debug("sshconnect2:userauth_u2f"); >+ >+ // For U2F_MODE_REGISTRATION, this code path will return 0, meaning the >+ // authentication method will not be retried. If we did not do that, we >+ // would loop endlessly. >+ if (authctxt->info_req_seen) { >+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); >+ return 0; >+ } >+ >+ packet_start(SSH2_MSG_USERAUTH_REQUEST); >+ packet_put_cstring(authctxt->server_user); >+ packet_put_cstring(authctxt->service); >+ packet_put_cstring(authctxt->method->name); >+ if (options.u2f_mode == NULL || strcasecmp(options.u2f_mode, "authentication") == 0) { >+ packet_put_int(U2F_MODE_AUTHENTICATION); >+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_authenticate); >+ } else if (options.u2f_mode != NULL && strcasecmp(options.u2f_mode, "registration") == 0) { >+ packet_put_int(U2F_MODE_REGISTRATION); >+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_register); >+ } else { >+ fatal("Invalid U2F mode (\"%s\"), expected \"authentication\" or \"registration\".", >+ options.u2f_mode); >+ } >+ packet_send(); >+ >+ return 1; >+} >+ >+static void >+wait_for_u2f_devices(u2fh_devs *devs) >+{ >+ time_t looking; >+ int attempts = 0; >+ u2fh_rc rc; >+ >+ // The U2F implementation considerations recommend 3 seconds as the time a >+ // client implementation should grant for security keys to respond. We wait >+ // 3 times that for the user to insert a security key (and it being >+ // detected). >+ looking = monotime(); >+ do { >+ if ((rc = u2fh_devs_discover(devs, NULL)) != U2FH_OK && attempts++ == 0) >+ error("Please insert and touch your U2F security key."); >+ if (rc != U2FH_OK) >+ usleep(50); >+ } while (rc != U2FH_OK && (monotime() - looking) <= 9); >+ if (rc != U2FH_OK) >+ fatal("No U2F devices found (%s). Did you plug in your U2F security key?", >+ u2fh_strerror(rc)); >+ >+ if (attempts == 0) >+ error("Please touch your U2F security key now."); >+} >+ >+int >+input_userauth_u2f_register(int type, u_int32_t seq, void *ctxt) >+{ >+ Authctxt *authctxt = ctxt; >+ char *challenge, *response; >+ u2fh_devs *devs = NULL; >+ u2fh_rc rc; >+ const char *origin = authctxt->host_port; >+ >+ if (authctxt == NULL) >+ fatal("input_userauth_u2f_register: no authentication context"); >+ >+ authctxt->info_req_seen = 1; >+ >+ challenge = packet_get_string(NULL); >+ packet_check_eom(); >+ >+ if ((rc = u2fh_devs_init(&devs)) != U2FH_OK) >+ fatal("u2fh_devs_init() failed: %s", u2fh_strerror(rc)); >+ >+ wait_for_u2f_devices(devs); >+ >+ if ((rc = u2fh_register(devs, challenge, origin, &response, U2FH_REQUEST_USER_PRESENCE)) != U2FH_OK) >+ fatal("u2fh_register() failed: %s", u2fh_strerror(rc)); >+ >+ u2fh_devs_done(devs); >+ >+ packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); >+ packet_put_cstring(response); >+ packet_send(); >+ >+ free(response); >+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); >+ dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_u2f_register_response); >+ return 0; >+} >+ >+int >+input_userauth_u2f_register_response(int type, u_int32_t seq, void *ctxt) >+{ >+ char *response = packet_get_string(NULL); >+ printf("%s\n", response); >+ fflush(stdout); >+ return 0; >+} >+ >+int >+input_userauth_u2f_authenticate(int type, u_int32_t seq, void *ctxt) >+{ >+ Authctxt *authctxt = ctxt; >+ char *challenge, *response; >+ u2fh_devs *devs = NULL; >+ u2fh_rc rc; >+ const char *origin = authctxt->host_port; >+ >+ if (authctxt == NULL) >+ fatal("input_userauth_u2f_authenticate: no authentication context"); >+ >+ authctxt->info_req_seen = 1; >+ >+ challenge = packet_get_string(NULL); >+ packet_check_eom(); >+ >+ debug("Starting U2F authentication for origin \"%s\".", origin); >+ >+ if ((rc = u2fh_devs_init(&devs)) != U2FH_OK) >+ fatal("u2fh_devs_init() failed: %s", u2fh_strerror(rc)); >+ >+ wait_for_u2f_devices(devs); >+ >+ // TODO: refactor with input_userauth_u2f_register(), the following line is the only one that is different :) >+ if ((rc = u2fh_authenticate(devs, challenge, origin, &response, U2FH_REQUEST_USER_PRESENCE)) != U2FH_OK) >+ fatal("u2fh_authenticate() failed: %s", u2fh_strerror(rc)); >+ >+ u2fh_devs_done(devs); >+ >+ packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); >+ packet_put_cstring(response); >+ packet_send(); >+ >+ free(response); >+ return 0; >+ >+ // We intentionally do not set SSH2_MSG_USERAUTH_INFO_REQUEST to NULL, >+ // because the server might send us more challenges (in case more than one >+ // U2F security key is in the authorized_keys). >+} >+ >+#endif /* U2F */ >+ > int > userauth_none(Authctxt *authctxt) > { >diff --git a/sshd.c b/sshd.c >index d868089..b42d942 100644 >--- a/sshd.c >+++ b/sshd.c >@@ -77,6 +77,7 @@ > #include <openssl/dh.h> > #include <openssl/bn.h> > #include <openssl/rand.h> >+#include <openssl/ssl.h> > #include "openbsd-compat/openssl-compat.h" > #endif > >@@ -1629,6 +1630,7 @@ main(int ac, char **av) > > #ifdef WITH_OPENSSL > OpenSSL_add_all_algorithms(); >+ SSL_load_error_strings(); > #endif > > /* If requested, redirect the logs to the specified logfile. */ >diff --git a/sshd_config.5 b/sshd_config.5 >index cd3b5cf..1049c02 100644 >--- a/sshd_config.5 >+++ b/sshd_config.5 >@@ -1518,6 +1518,25 @@ for authentication using > .Cm TrustedUserCAKeys . > For more details on certificates, see the CERTIFICATES section in > .Xr ssh-keygen 1 . >+.It Cm U2FAuthentication >+Specifies whether user authentication based on U2F (Universal Second Factor) is allowed. The default is >+.Dq no . >+Note that U2F authentication should never be used alone, so specify for example: >+.Bd -literal -offset indent >+U2FAuthentication yes >+AuthenticationMethods pubkey,u2f >+.Ed >+.Pp >+That way, pubkey authentication will be performed and U2F will be required >+after pubkey authentication was successful. In case the user in question does >+not have any ssh-u2f lines in their authorized_keys file, the u2f >+authentication method will just return success. >+.Pp >+In order to register a U2F security key, enable this option as outlined above. >+Then, run >+.Dq ssh -o U2FMode=registration server.example.net >+in order to obtain a ssh-u2f line which you can then append to your >+authorized_keys. > .It Cm UseDNS > Specifies whether > .Xr sshd 8 >diff --git a/sshkey.c b/sshkey.c >index 3dd8907..0a222cc 100644 >--- a/sshkey.c >+++ b/sshkey.c >@@ -3,6 +3,7 @@ > * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. > * Copyright (c) 2008 Alexander von Gernler. All rights reserved. > * Copyright (c) 2010,2011 Damien Miller. All rights reserved. >+ * Copyright (c) 2014 Google Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -58,6 +59,10 @@ > #define SSHKEY_INTERNAL > #include "sshkey.h" > #include "match.h" >+#include "key.h" >+#include "hostfile.h" >+#include "auth.h" >+#include "u2f.h" > > /* openssh private key file format */ > #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" >@@ -112,6 +117,7 @@ static const struct keytype keytypes[] = { > # endif /* OPENSSL_HAS_NISTP521 */ > # endif /* OPENSSL_HAS_ECC */ > #endif /* WITH_OPENSSL */ >+ { "ssh-u2f", "U2F", KEY_U2F, 0, 0 }, > { NULL, NULL, -1, -1, 0 } > }; > >@@ -513,6 +519,8 @@ sshkey_new(int type) > break; > case KEY_UNSPEC: > break; >+ case KEY_U2F: >+ break; > default: > free(k); > return NULL; >@@ -793,6 +801,17 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) > key->ed25519_pk, ED25519_PK_SZ)) != 0) > return ret; > break; >+#ifdef U2F >+ case KEY_U2F: >+ if (key->u2f_pubkey == NULL) >+ return SSH_ERR_INVALID_ARGUMENT; >+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 || >+ (ret = sshbuf_put_string(b, key->u2f_pubkey, U2F_PUBKEY_LEN)) != 0 || >+ (ret = sshbuf_put_string(b, >+ key->u2f_key_handle, key->u2f_key_handle_len)) != 0) >+ return ret; >+ break; >+#endif > default: > return SSH_ERR_KEY_TYPE_UNKNOWN; > } >@@ -1262,6 +1281,42 @@ sshkey_read(struct sshkey *ret, char **cpp) > retval = 0; > #endif /* WITH_SSH1 */ > break; >+ case KEY_U2F: >+#ifdef U2F >+ space = strchr(cp, ' '); >+ if (space == NULL) >+ return SSH_ERR_INVALID_FORMAT; >+ *space = '\0'; >+ type = sshkey_type_from_name(cp); >+ if (type == KEY_UNSPEC) >+ return SSH_ERR_INVALID_FORMAT; >+ cp = space+1; >+ if (*cp == '\0') >+ return SSH_ERR_INVALID_FORMAT; >+ if (ret->type == KEY_UNSPEC) { >+ ret->type = type; >+ } else if (ret->type != type) >+ return SSH_ERR_KEY_TYPE_MISMATCH; >+ cp = space+1; >+ /* trim comment */ >+ space = strchr(cp, ' '); >+ if (space) >+ *space = '\0'; >+ blob = sshbuf_new(); >+ if ((r = sshbuf_b64tod(blob, cp)) != 0) { >+ sshbuf_free(blob); >+ return r; >+ } >+ // TODO: why do we _need_ to use malloc here? xmalloc gives memory that crashes! >+ ret->u2f_pubkey = malloc(U2F_PUBKEY_LEN); >+ memcpy(ret->u2f_pubkey, sshbuf_ptr(blob), U2F_PUBKEY_LEN); >+ ret->u2f_key_handle_len = sshbuf_len(blob) - U2F_PUBKEY_LEN; >+ ret->u2f_key_handle = malloc(ret->u2f_key_handle_len); >+ memcpy(ret->u2f_key_handle, sshbuf_ptr(blob) + U2F_PUBKEY_LEN, ret->u2f_key_handle_len); >+ sshbuf_free(blob); >+ retval = (r >= 0) ? 0 : 1; >+#endif /* U2F */ >+ break; > case KEY_UNSPEC: > case KEY_RSA: > case KEY_DSA: >@@ -1965,6 +2020,9 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, > #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) > EC_POINT *q = NULL; > #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ >+#ifdef U2F >+ u_char *khandle = NULL; >+#endif > > #ifdef DEBUG_PK /* XXX */ > sshbuf_dump(b, stderr); >@@ -2104,6 +2162,28 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, > key->ed25519_pk = pk; > pk = NULL; > break; >+#ifdef U2F >+ case KEY_U2F: >+ if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) >+ goto out; >+ if (len != U2F_PUBKEY_LEN) { >+ ret = SSH_ERR_INVALID_FORMAT; >+ goto out; >+ } >+ if ((ret = sshbuf_get_string(b, &khandle, &len)) != 0) >+ goto out; >+ if ((key = sshkey_new(type)) == NULL) { >+ ret = SSH_ERR_ALLOC_FAIL; >+ goto out; >+ } >+ key->u2f_pubkey = pk; >+ key->u2f_key_handle_len = len; >+ key->u2f_key_handle = khandle; >+ pk = NULL; >+ khandle = NULL; >+ ret = SSH_ERR_ALLOC_FAIL; >+ break; >+#endif > case KEY_UNSPEC: > if ((key = sshkey_new(type)) == NULL) { > ret = SSH_ERR_ALLOC_FAIL; >@@ -2136,6 +2216,9 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, > if (q != NULL) > EC_POINT_free(q); > #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ >+#ifdef U2F >+ free(khandle); >+#endif > return ret; > } > >diff --git a/sshkey.h b/sshkey.h >index 99f1b25..308321d 100644 >--- a/sshkey.h >+++ b/sshkey.h >@@ -62,6 +62,7 @@ enum sshkey_types { > KEY_DSA_CERT, > KEY_ECDSA_CERT, > KEY_ED25519_CERT, >+ KEY_U2F, > KEY_UNSPEC > }; > >@@ -106,6 +107,11 @@ struct sshkey { > u_char *ed25519_sk; > u_char *ed25519_pk; > struct sshkey_cert *cert; >+#ifdef U2F >+ u_char *u2f_pubkey; >+ u_int u2f_key_handle_len; >+ u_char *u2f_key_handle; >+#endif > }; > > #define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES >-- >2.1.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 2319
:
2511
|
2512
|
2521
|
2710
|
2782