summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-10-31 21:18:28 +0000
committerDamien Miller <djm@mindrot.org>2019-11-01 09:46:09 +1100
commit884416bdb10468f1252e4d7c13d51b43dccba7f6 (patch)
treef81dc3ed23cddcda6163102363c5dc75a63430e6
parent01a0670f69c5b86e471e033b92145d6c7cc77c58 (diff)
upstream: ssh client support for U2F/FIDO keys
OpenBSD-Commit-ID: eb2cfa6cf7419a1895e06e398ea6d41516c5b0bc
-rw-r--r--readconf.c17
-rw-r--r--readconf.h3
-rw-r--r--ssh.c18
-rw-r--r--sshconnect2.c111
4 files changed, 120 insertions, 29 deletions
diff --git a/readconf.c b/readconf.c
index f78b4d6fe..f18194580 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.309 2019/09/06 14:45:34 naddy Exp $ */ 1/* $OpenBSD: readconf.c,v 1.310 2019/10/31 21:18:28 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -174,6 +174,7 @@ typedef enum {
174 oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, 174 oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
175 oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, 175 oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
176 oPubkeyAcceptedKeyTypes, oCASignatureAlgorithms, oProxyJump, 176 oPubkeyAcceptedKeyTypes, oCASignatureAlgorithms, oProxyJump,
177 oSecurityKeyProvider,
177 oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported 178 oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
178} OpCodes; 179} OpCodes;
179 180
@@ -214,6 +215,7 @@ static struct {
214 { "smartcarddevice", oUnsupported }, 215 { "smartcarddevice", oUnsupported },
215 { "pkcs11provider", oUnsupported }, 216 { "pkcs11provider", oUnsupported },
216#endif 217#endif
218 { "securitykeyprovider", oSecurityKeyProvider },
217 { "rsaauthentication", oUnsupported }, 219 { "rsaauthentication", oUnsupported },
218 { "rhostsrsaauthentication", oUnsupported }, 220 { "rhostsrsaauthentication", oUnsupported },
219 { "compressionlevel", oUnsupported }, 221 { "compressionlevel", oUnsupported },
@@ -1146,6 +1148,10 @@ parse_char_array:
1146 charptr = &options->pkcs11_provider; 1148 charptr = &options->pkcs11_provider;
1147 goto parse_string; 1149 goto parse_string;
1148 1150
1151 case oSecurityKeyProvider:
1152 charptr = &options->sk_provider;
1153 goto parse_string;
1154
1149 case oProxyCommand: 1155 case oProxyCommand:
1150 charptr = &options->proxy_command; 1156 charptr = &options->proxy_command;
1151 /* Ignore ProxyCommand if ProxyJump already specified */ 1157 /* Ignore ProxyCommand if ProxyJump already specified */
@@ -1906,6 +1912,7 @@ initialize_options(Options * options)
1906 options->bind_address = NULL; 1912 options->bind_address = NULL;
1907 options->bind_interface = NULL; 1913 options->bind_interface = NULL;
1908 options->pkcs11_provider = NULL; 1914 options->pkcs11_provider = NULL;
1915 options->sk_provider = NULL;
1909 options->enable_ssh_keysign = - 1; 1916 options->enable_ssh_keysign = - 1;
1910 options->no_host_authentication_for_localhost = - 1; 1917 options->no_host_authentication_for_localhost = - 1;
1911 options->identities_only = - 1; 1918 options->identities_only = - 1;
@@ -2043,6 +2050,8 @@ fill_default_options(Options * options)
2043 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0); 2050 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0);
2044#ifdef OPENSSL_HAS_ECC 2051#ifdef OPENSSL_HAS_ECC
2045 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0); 2052 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
2053 add_identity_file(options, "~/",
2054 _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
2046#endif 2055#endif
2047 add_identity_file(options, "~/", 2056 add_identity_file(options, "~/",
2048 _PATH_SSH_CLIENT_ID_ED25519, 0); 2057 _PATH_SSH_CLIENT_ID_ED25519, 0);
@@ -2118,6 +2127,8 @@ fill_default_options(Options * options)
2118 options->fingerprint_hash = SSH_FP_HASH_DEFAULT; 2127 options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
2119 if (options->update_hostkeys == -1) 2128 if (options->update_hostkeys == -1)
2120 options->update_hostkeys = 0; 2129 options->update_hostkeys = 0;
2130 if (options->sk_provider == NULL)
2131 options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
2121 2132
2122 /* Expand KEX name lists */ 2133 /* Expand KEX name lists */
2123 all_cipher = cipher_alg_list(',', 0); 2134 all_cipher = cipher_alg_list(',', 0);
@@ -2135,7 +2146,7 @@ fill_default_options(Options * options)
2135 ASSEMBLE(macs, KEX_CLIENT_MAC, all_mac); 2146 ASSEMBLE(macs, KEX_CLIENT_MAC, all_mac);
2136 ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, all_kex); 2147 ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, all_kex);
2137 ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); 2148 ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key);
2138 ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); 2149 ASSEMBLE(pubkey_key_types, PUBKEY_DEFAULT_PK_ALG, all_key);
2139 ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig); 2150 ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig);
2140#undef ASSEMBLE 2151#undef ASSEMBLE
2141 free(all_cipher); 2152 free(all_cipher);
@@ -2157,6 +2168,7 @@ fill_default_options(Options * options)
2157 CLEAR_ON_NONE(options->control_path); 2168 CLEAR_ON_NONE(options->control_path);
2158 CLEAR_ON_NONE(options->revoked_host_keys); 2169 CLEAR_ON_NONE(options->revoked_host_keys);
2159 CLEAR_ON_NONE(options->pkcs11_provider); 2170 CLEAR_ON_NONE(options->pkcs11_provider);
2171 CLEAR_ON_NONE(options->sk_provider);
2160 if (options->jump_host != NULL && 2172 if (options->jump_host != NULL &&
2161 strcmp(options->jump_host, "none") == 0 && 2173 strcmp(options->jump_host, "none") == 0 &&
2162 options->jump_port == 0 && options->jump_user == NULL) { 2174 options->jump_port == 0 && options->jump_user == NULL) {
@@ -2673,6 +2685,7 @@ dump_client_config(Options *o, const char *host)
2673#ifdef ENABLE_PKCS11 2685#ifdef ENABLE_PKCS11
2674 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); 2686 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
2675#endif 2687#endif
2688 dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
2676 dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); 2689 dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
2677 dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types); 2690 dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types);
2678 dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); 2691 dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
diff --git a/readconf.h b/readconf.h
index 8e36bf32a..51d540b88 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.129 2018/11/23 05:08:07 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.130 2019/10/31 21:18:28 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -82,6 +82,7 @@ typedef struct {
82 char *bind_address; /* local socket address for connection to sshd */ 82 char *bind_address; /* local socket address for connection to sshd */
83 char *bind_interface; /* local interface for bind address */ 83 char *bind_interface; /* local interface for bind address */
84 char *pkcs11_provider; /* PKCS#11 provider */ 84 char *pkcs11_provider; /* PKCS#11 provider */
85 char *sk_provider; /* Security key provider */
85 int verify_host_key_dns; /* Verify host key using DNS */ 86 int verify_host_key_dns; /* Verify host key using DNS */
86 87
87 int num_identity_files; /* Number of files for RSA/DSA identities. */ 88 int num_identity_files; /* Number of files for RSA/DSA identities. */
diff --git a/ssh.c b/ssh.c
index ee51823cd..4736b5dd0 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.507 2019/09/13 04:27:35 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.508 2019/10/31 21:18:28 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1344,6 +1344,22 @@ main(int ac, char **av)
1344 exit(0); 1344 exit(0);
1345 } 1345 }
1346 1346
1347 /* Expand SecurityKeyProvider if it refers to an environment variable */
1348 if (options.sk_provider != NULL && *options.sk_provider == '$' &&
1349 strlen(options.sk_provider) > 1) {
1350 if ((cp = getenv(options.sk_provider + 1)) == NULL) {
1351 debug("Security key provider %s did not resolve; "
1352 "disabling", options.sk_provider);
1353 free(options.sk_provider);
1354 options.sk_provider = NULL;
1355 } else {
1356 debug2("resolved SecurityKeyProvider %s => %s",
1357 options.sk_provider, cp);
1358 free(options.sk_provider);
1359 options.sk_provider = xstrdup(cp);
1360 }
1361 }
1362
1347 if (muxclient_command != 0 && options.control_path == NULL) 1363 if (muxclient_command != 0 && options.control_path == NULL)
1348 fatal("No ControlPath specified for \"-O\" command"); 1364 fatal("No ControlPath specified for \"-O\" command");
1349 if (options.control_path != NULL) { 1365 if (options.control_path != NULL) {
diff --git a/sshconnect2.c b/sshconnect2.c
index 87fa70a40..62f0c3e76 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect2.c,v 1.308 2019/08/05 11:50:33 dtucker Exp $ */ 1/* $OpenBSD: sshconnect2.c,v 1.309 2019/10/31 21:18:28 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2008 Damien Miller. All rights reserved. 4 * Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -72,6 +72,7 @@
72#include "hostfile.h" 72#include "hostfile.h"
73#include "ssherr.h" 73#include "ssherr.h"
74#include "utf8.h" 74#include "utf8.h"
75#include "ssh-sk.h"
75 76
76#ifdef GSSAPI 77#ifdef GSSAPI
77#include "ssh-gss.h" 78#include "ssh-gss.h"
@@ -601,17 +602,23 @@ static char *
601format_identity(Identity *id) 602format_identity(Identity *id)
602{ 603{
603 char *fp = NULL, *ret = NULL; 604 char *fp = NULL, *ret = NULL;
605 const char *note = "";
604 606
605 if (id->key != NULL) { 607 if (id->key != NULL) {
606 fp = sshkey_fingerprint(id->key, options.fingerprint_hash, 608 fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
607 SSH_FP_DEFAULT); 609 SSH_FP_DEFAULT);
608 } 610 }
611 if (id->key) {
612 if ((id->key->flags & SSHKEY_FLAG_EXT) != 0)
613 note = " token";
614 else if (sshkey_type_plain(id->key->type) == KEY_ECDSA_SK)
615 note = " security-key";
616 }
609 xasprintf(&ret, "%s %s%s%s%s%s%s", 617 xasprintf(&ret, "%s %s%s%s%s%s%s",
610 id->filename, 618 id->filename,
611 id->key ? sshkey_type(id->key) : "", id->key ? " " : "", 619 id->key ? sshkey_type(id->key) : "", id->key ? " " : "",
612 fp ? fp : "", 620 fp ? fp : "",
613 id->userprovided ? " explicit" : "", 621 id->userprovided ? " explicit" : "", note,
614 (id->key && (id->key->flags & SSHKEY_FLAG_EXT)) ? " token" : "",
615 id->agent_fd != -1 ? " agent" : ""); 622 id->agent_fd != -1 ? " agent" : "");
616 free(fp); 623 free(fp);
617 return ret; 624 return ret;
@@ -1140,8 +1147,11 @@ static int
1140identity_sign(struct identity *id, u_char **sigp, size_t *lenp, 1147identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
1141 const u_char *data, size_t datalen, u_int compat, const char *alg) 1148 const u_char *data, size_t datalen, u_int compat, const char *alg)
1142{ 1149{
1143 struct sshkey *prv; 1150 struct sshkey *sign_key = NULL, *prv = NULL;
1144 int r; 1151 int r = SSH_ERR_INTERNAL_ERROR;
1152
1153 *sigp = NULL;
1154 *lenp = 0;
1145 1155
1146 /* The agent supports this key. */ 1156 /* The agent supports this key. */
1147 if (id->key != NULL && id->agent_fd != -1) { 1157 if (id->key != NULL && id->agent_fd != -1) {
@@ -1155,27 +1165,46 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
1155 */ 1165 */
1156 if (id->key != NULL && 1166 if (id->key != NULL &&
1157 (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) { 1167 (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
1158 if ((r = sshkey_sign(id->key, sigp, lenp, data, datalen, 1168 sign_key = id->key;
1159 alg, compat)) != 0) 1169 } else {
1160 return r; 1170 /* Load the private key from the file. */
1161 /* 1171 if ((prv = load_identity_file(id)) == NULL)
1162 * PKCS#11 tokens may not support all signature algorithms, 1172 return SSH_ERR_KEY_NOT_FOUND;
1163 * so check what we get back. 1173 if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
1164 */ 1174 error("%s: private key %s contents do not match public",
1165 if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) 1175 __func__, id->filename);
1166 return r; 1176 r = SSH_ERR_KEY_NOT_FOUND;
1167 return 0; 1177 goto out;
1178 }
1179 sign_key = prv;
1168 } 1180 }
1169 1181
1170 /* Load the private key from the file. */ 1182 if (sshkey_type_plain(sign_key->type) == KEY_ECDSA_SK) {
1171 if ((prv = load_identity_file(id)) == NULL) 1183 if (options.sk_provider == NULL) {
1172 return SSH_ERR_KEY_NOT_FOUND; 1184 /* Shouldn't happen here; checked in pubkey_prepare() */
1173 if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { 1185 fatal("%s: missing SecurityKeyProvider", __func__);
1174 error("%s: private key %s contents do not match public", 1186 }
1175 __func__, id->filename); 1187 if ((r = sshsk_ecdsa_sign(options.sk_provider, sign_key,
1176 return SSH_ERR_KEY_NOT_FOUND; 1188 sigp, lenp, data, datalen, compat)) != 0) {
1189 debug("%s: sshsk_ecdsa_sign: %s", __func__, ssh_err(r));
1190 goto out;
1191 }
1192 } else if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen,
1193 alg, compat)) != 0) {
1194 debug("%s: sshkey_sign: %s", __func__, ssh_err(r));
1195 goto out;
1177 } 1196 }
1178 r = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat); 1197 /*
1198 * PKCS#11 tokens may not support all signature algorithms,
1199 * so check what we get back.
1200 */
1201 if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) {
1202 debug("%s: sshkey_check_sigtype: %s", __func__, ssh_err(r));
1203 goto out;
1204 }
1205 /* success */
1206 r = 0;
1207 out:
1179 sshkey_free(prv); 1208 sshkey_free(prv);
1180 return r; 1209 return r;
1181} 1210}
@@ -1450,6 +1479,15 @@ load_identity_file(Identity *id)
1450 quit = 1; 1479 quit = 1;
1451 break; 1480 break;
1452 } 1481 }
1482 if (private != NULL &&
1483 sshkey_type_plain(private->type) == KEY_ECDSA_SK &&
1484 options.sk_provider == NULL) {
1485 debug("key \"%s\" is a security key, but no "
1486 "provider specified", id->filename);
1487 sshkey_free(private);
1488 private = NULL;
1489 quit = 1;
1490 }
1453 if (!quit && private != NULL && id->agent_fd == -1 && 1491 if (!quit && private != NULL && id->agent_fd == -1 &&
1454 !(id->key && id->isprivate)) 1492 !(id->key && id->isprivate))
1455 maybe_add_key_to_agent(id->filename, private, comment, 1493 maybe_add_key_to_agent(id->filename, private, comment,
@@ -1520,8 +1558,20 @@ pubkey_prepare(Authctxt *authctxt)
1520 /* list of keys stored in the filesystem and PKCS#11 */ 1558 /* list of keys stored in the filesystem and PKCS#11 */
1521 for (i = 0; i < options.num_identity_files; i++) { 1559 for (i = 0; i < options.num_identity_files; i++) {
1522 key = options.identity_keys[i]; 1560 key = options.identity_keys[i];
1523 if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER) 1561 if (key && key->cert &&
1562 key->cert->type != SSH2_CERT_TYPE_USER) {
1563 debug("%s: ignoring certificate %s: not a user "
1564 "certificate", __func__,
1565 options.identity_files[i]);
1566 continue;
1567 }
1568 if (key && sshkey_type_plain(key->type) == KEY_ECDSA_SK &&
1569 options.sk_provider == NULL) {
1570 debug("%s: ignoring security key %s as no "
1571 "SecurityKeyProvider has been specified",
1572 __func__, options.identity_files[i]);
1524 continue; 1573 continue;
1574 }
1525 options.identity_keys[i] = NULL; 1575 options.identity_keys[i] = NULL;
1526 id = xcalloc(1, sizeof(*id)); 1576 id = xcalloc(1, sizeof(*id));
1527 id->agent_fd = -1; 1577 id->agent_fd = -1;
@@ -1534,8 +1584,19 @@ pubkey_prepare(Authctxt *authctxt)
1534 for (i = 0; i < options.num_certificate_files; i++) { 1584 for (i = 0; i < options.num_certificate_files; i++) {
1535 key = options.certificates[i]; 1585 key = options.certificates[i];
1536 if (!sshkey_is_cert(key) || key->cert == NULL || 1586 if (!sshkey_is_cert(key) || key->cert == NULL ||
1537 key->cert->type != SSH2_CERT_TYPE_USER) 1587 key->cert->type != SSH2_CERT_TYPE_USER) {
1588 debug("%s: ignoring certificate %s: not a user "
1589 "certificate", __func__,
1590 options.identity_files[i]);
1538 continue; 1591 continue;
1592 }
1593 if (key && sshkey_type_plain(key->type) == KEY_ECDSA_SK &&
1594 options.sk_provider == NULL) {
1595 debug("%s: ignoring security key certificate %s as no "
1596 "SecurityKeyProvider has been specified",
1597 __func__, options.identity_files[i]);
1598 continue;
1599 }
1539 id = xcalloc(1, sizeof(*id)); 1600 id = xcalloc(1, sizeof(*id));
1540 id->agent_fd = -1; 1601 id->agent_fd = -1;
1541 id->key = key; 1602 id->key = key;