diff options
author | Colin Watson <cjwatson@debian.org> | 2020-02-21 11:57:14 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2020-02-21 14:27:02 +0000 |
commit | 886e47e745586c34e81cfd5c5fb9b5dbc8e84d04 (patch) | |
tree | dd6c3b4dc64a17c520af7aaf213163f8a0a63e56 /sshconnect2.c | |
parent | ac2b4c0697fcac554041ab95f81736887eadf6ec (diff) | |
parent | a2dabf35ce0228c86a288d11cc847a9d9801604f (diff) |
New upstream release (8.2p1)
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 174 |
1 files changed, 125 insertions, 49 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index a4ec75ca1..03bc87eb4 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.320 2020/02/06 22:48:23 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. |
@@ -36,9 +36,9 @@ | |||
36 | #include <netdb.h> | 36 | #include <netdb.h> |
37 | #include <pwd.h> | 37 | #include <pwd.h> |
38 | #include <signal.h> | 38 | #include <signal.h> |
39 | #include <stdarg.h> | ||
40 | #include <stdio.h> | 39 | #include <stdio.h> |
41 | #include <string.h> | 40 | #include <string.h> |
41 | #include <stdarg.h> | ||
42 | #include <unistd.h> | 42 | #include <unistd.h> |
43 | #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) | 43 | #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) |
44 | #include <vis.h> | 44 | #include <vis.h> |
@@ -72,6 +72,8 @@ | |||
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" | ||
76 | #include "sk-api.h" | ||
75 | 77 | ||
76 | #ifdef GSSAPI | 78 | #ifdef GSSAPI |
77 | #include "ssh-gss.h" | 79 | #include "ssh-gss.h" |
@@ -115,7 +117,7 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
115 | for (i = 0; i < options.num_system_hostfiles; i++) | 117 | for (i = 0; i < options.num_system_hostfiles; i++) |
116 | load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); | 118 | load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); |
117 | 119 | ||
118 | oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); | 120 | oavail = avail = xstrdup(options.hostkeyalgorithms); |
119 | maxlen = strlen(avail) + 1; | 121 | maxlen = strlen(avail) + 1; |
120 | first = xmalloc(maxlen); | 122 | first = xmalloc(maxlen); |
121 | last = xmalloc(maxlen); | 123 | last = xmalloc(maxlen); |
@@ -157,7 +159,7 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
157 | { | 159 | { |
158 | char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; | 160 | char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; |
159 | char *s, *all_key; | 161 | char *s, *all_key; |
160 | int r; | 162 | int r, use_known_hosts_order = 0; |
161 | 163 | ||
162 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | 164 | #if defined(GSSAPI) && defined(WITH_OPENSSL) |
163 | char *orig = NULL, *gss = NULL; | 165 | char *orig = NULL, *gss = NULL; |
@@ -167,6 +169,23 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
167 | xxx_host = host; | 169 | xxx_host = host; |
168 | xxx_hostaddr = hostaddr; | 170 | xxx_hostaddr = hostaddr; |
169 | 171 | ||
172 | /* | ||
173 | * If the user has not specified HostkeyAlgorithms, or has only | ||
174 | * appended or removed algorithms from that list then prefer algorithms | ||
175 | * that are in the list that are supported by known_hosts keys. | ||
176 | */ | ||
177 | if (options.hostkeyalgorithms == NULL || | ||
178 | options.hostkeyalgorithms[0] == '-' || | ||
179 | options.hostkeyalgorithms[0] == '+') | ||
180 | use_known_hosts_order = 1; | ||
181 | |||
182 | /* Expand or fill in HostkeyAlgorithms */ | ||
183 | all_key = sshkey_alg_list(0, 0, 1, ','); | ||
184 | if (kex_assemble_names(&options.hostkeyalgorithms, | ||
185 | kex_default_pk_alg(), all_key) != 0) | ||
186 | fatal("%s: kex_assemble_namelist", __func__); | ||
187 | free(all_key); | ||
188 | |||
170 | if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) | 189 | if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) |
171 | fatal("%s: kex_names_cat", __func__); | 190 | fatal("%s: kex_names_cat", __func__); |
172 | myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); | 191 | myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); |
@@ -175,25 +194,19 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
175 | myproposal[PROPOSAL_ENC_ALGS_STOC] = | 194 | myproposal[PROPOSAL_ENC_ALGS_STOC] = |
176 | compat_cipher_proposal(options.ciphers); | 195 | compat_cipher_proposal(options.ciphers); |
177 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = | 196 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = |
178 | myproposal[PROPOSAL_COMP_ALGS_STOC] = options.compression ? | 197 | myproposal[PROPOSAL_COMP_ALGS_STOC] = |
179 | "zlib@openssh.com,zlib,none" : "none,zlib@openssh.com,zlib"; | 198 | (char *)compression_alg_list(options.compression); |
180 | myproposal[PROPOSAL_MAC_ALGS_CTOS] = | 199 | myproposal[PROPOSAL_MAC_ALGS_CTOS] = |
181 | myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; | 200 | myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; |
182 | if (options.hostkeyalgorithms != NULL) { | 201 | if (use_known_hosts_order) { |
183 | all_key = sshkey_alg_list(0, 0, 1, ','); | 202 | /* Query known_hosts and prefer algorithms that appear there */ |
184 | if (kex_assemble_names(&options.hostkeyalgorithms, | ||
185 | KEX_DEFAULT_PK_ALG, all_key) != 0) | ||
186 | fatal("%s: kex_assemble_namelist", __func__); | ||
187 | free(all_key); | ||
188 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | ||
189 | compat_pkalg_proposal(options.hostkeyalgorithms); | ||
190 | } else { | ||
191 | /* Enforce default */ | ||
192 | options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); | ||
193 | /* Prefer algorithms that we already have keys for */ | ||
194 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | 203 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = |
195 | compat_pkalg_proposal( | 204 | compat_pkalg_proposal( |
196 | order_hostkeyalgs(host, hostaddr, port)); | 205 | order_hostkeyalgs(host, hostaddr, port)); |
206 | } else { | ||
207 | /* Use specified HostkeyAlgorithms exactly */ | ||
208 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | ||
209 | compat_pkalg_proposal(options.hostkeyalgorithms); | ||
197 | } | 210 | } |
198 | 211 | ||
199 | #if defined(GSSAPI) && defined(WITH_OPENSSL) | 212 | #if defined(GSSAPI) && defined(WITH_OPENSSL) |
@@ -254,7 +267,7 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) | |||
254 | ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client; | 267 | ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client; |
255 | } | 268 | } |
256 | # endif | 269 | # endif |
257 | #endif | 270 | #endif /* WITH_OPENSSL */ |
258 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; | 271 | ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; |
259 | ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; | 272 | ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; |
260 | ssh->kex->verify_host_key=&verify_host_key_callback; | 273 | ssh->kex->verify_host_key=&verify_host_key_callback; |
@@ -669,17 +682,23 @@ static char * | |||
669 | format_identity(Identity *id) | 682 | format_identity(Identity *id) |
670 | { | 683 | { |
671 | char *fp = NULL, *ret = NULL; | 684 | char *fp = NULL, *ret = NULL; |
685 | const char *note = ""; | ||
672 | 686 | ||
673 | if (id->key != NULL) { | 687 | if (id->key != NULL) { |
674 | fp = sshkey_fingerprint(id->key, options.fingerprint_hash, | 688 | fp = sshkey_fingerprint(id->key, options.fingerprint_hash, |
675 | SSH_FP_DEFAULT); | 689 | SSH_FP_DEFAULT); |
676 | } | 690 | } |
691 | if (id->key) { | ||
692 | if ((id->key->flags & SSHKEY_FLAG_EXT) != 0) | ||
693 | note = " token"; | ||
694 | else if (sshkey_is_sk(id->key)) | ||
695 | note = " authenticator"; | ||
696 | } | ||
677 | xasprintf(&ret, "%s %s%s%s%s%s%s", | 697 | xasprintf(&ret, "%s %s%s%s%s%s%s", |
678 | id->filename, | 698 | id->filename, |
679 | id->key ? sshkey_type(id->key) : "", id->key ? " " : "", | 699 | id->key ? sshkey_type(id->key) : "", id->key ? " " : "", |
680 | fp ? fp : "", | 700 | fp ? fp : "", |
681 | id->userprovided ? " explicit" : "", | 701 | id->userprovided ? " explicit" : "", note, |
682 | (id->key && (id->key->flags & SSHKEY_FLAG_EXT)) ? " token" : "", | ||
683 | id->agent_fd != -1 ? " agent" : ""); | 702 | id->agent_fd != -1 ? " agent" : ""); |
684 | free(fp); | 703 | free(fp); |
685 | return ret; | 704 | return ret; |
@@ -1272,8 +1291,13 @@ static int | |||
1272 | identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | 1291 | identity_sign(struct identity *id, u_char **sigp, size_t *lenp, |
1273 | const u_char *data, size_t datalen, u_int compat, const char *alg) | 1292 | const u_char *data, size_t datalen, u_int compat, const char *alg) |
1274 | { | 1293 | { |
1275 | struct sshkey *prv; | 1294 | struct sshkey *sign_key = NULL, *prv = NULL; |
1276 | int r; | 1295 | int r = SSH_ERR_INTERNAL_ERROR; |
1296 | struct notifier_ctx *notifier = NULL; | ||
1297 | char *fp = NULL; | ||
1298 | |||
1299 | *sigp = NULL; | ||
1300 | *lenp = 0; | ||
1277 | 1301 | ||
1278 | /* The agent supports this key. */ | 1302 | /* The agent supports this key. */ |
1279 | if (id->key != NULL && id->agent_fd != -1) { | 1303 | if (id->key != NULL && id->agent_fd != -1) { |
@@ -1287,27 +1311,47 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1287 | */ | 1311 | */ |
1288 | if (id->key != NULL && | 1312 | if (id->key != NULL && |
1289 | (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) { | 1313 | (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) { |
1290 | if ((r = sshkey_sign(id->key, sigp, lenp, data, datalen, | 1314 | sign_key = id->key; |
1291 | alg, compat)) != 0) | 1315 | } else { |
1292 | return r; | 1316 | /* Load the private key from the file. */ |
1293 | /* | 1317 | if ((prv = load_identity_file(id)) == NULL) |
1294 | * PKCS#11 tokens may not support all signature algorithms, | 1318 | return SSH_ERR_KEY_NOT_FOUND; |
1295 | * so check what we get back. | 1319 | if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { |
1296 | */ | 1320 | error("%s: private key %s contents do not match public", |
1297 | if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) | 1321 | __func__, id->filename); |
1298 | return r; | 1322 | r = SSH_ERR_KEY_NOT_FOUND; |
1299 | return 0; | 1323 | goto out; |
1324 | } | ||
1325 | sign_key = prv; | ||
1326 | if (sshkey_is_sk(sign_key) && | ||
1327 | (sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | ||
1328 | /* XXX match batch mode should just skip these keys? */ | ||
1329 | if ((fp = sshkey_fingerprint(sign_key, | ||
1330 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
1331 | fatal("%s: sshkey_fingerprint", __func__); | ||
1332 | notifier = notify_start(options.batch_mode, | ||
1333 | "Confirm user presence for key %s %s", | ||
1334 | sshkey_type(sign_key), fp); | ||
1335 | free(fp); | ||
1336 | } | ||
1300 | } | 1337 | } |
1301 | 1338 | if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, | |
1302 | /* Load the private key from the file. */ | 1339 | alg, options.sk_provider, compat)) != 0) { |
1303 | if ((prv = load_identity_file(id)) == NULL) | 1340 | debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); |
1304 | return SSH_ERR_KEY_NOT_FOUND; | 1341 | goto out; |
1305 | if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { | ||
1306 | error("%s: private key %s contents do not match public", | ||
1307 | __func__, id->filename); | ||
1308 | return SSH_ERR_KEY_NOT_FOUND; | ||
1309 | } | 1342 | } |
1310 | r = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat); | 1343 | /* |
1344 | * PKCS#11 tokens may not support all signature algorithms, | ||
1345 | * so check what we get back. | ||
1346 | */ | ||
1347 | if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0) { | ||
1348 | debug("%s: sshkey_check_sigtype: %s", __func__, ssh_err(r)); | ||
1349 | goto out; | ||
1350 | } | ||
1351 | /* success */ | ||
1352 | r = 0; | ||
1353 | out: | ||
1354 | notify_complete(notifier); | ||
1311 | sshkey_free(prv); | 1355 | sshkey_free(prv); |
1312 | return r; | 1356 | return r; |
1313 | } | 1357 | } |
@@ -1410,7 +1454,7 @@ sign_and_send_pubkey(struct ssh *ssh, Identity *id) | |||
1410 | error("%s: no mutual signature supported", __func__); | 1454 | error("%s: no mutual signature supported", __func__); |
1411 | goto out; | 1455 | goto out; |
1412 | } | 1456 | } |
1413 | debug3("%s: signing using %s", __func__, alg); | 1457 | debug3("%s: signing using %s %s", __func__, alg, fp); |
1414 | 1458 | ||
1415 | sshbuf_free(b); | 1459 | sshbuf_free(b); |
1416 | if ((b = sshbuf_new()) == NULL) | 1460 | if ((b = sshbuf_new()) == NULL) |
@@ -1457,7 +1501,9 @@ sign_and_send_pubkey(struct ssh *ssh, Identity *id) | |||
1457 | loc, sshkey_type(id->key), fp); | 1501 | loc, sshkey_type(id->key), fp); |
1458 | continue; | 1502 | continue; |
1459 | } | 1503 | } |
1460 | error("%s: signing failed: %s", __func__, ssh_err(r)); | 1504 | error("%s: signing failed for %s \"%s\"%s: %s", __func__, |
1505 | sshkey_type(sign_id->key), sign_id->filename, | ||
1506 | id->agent_fd != -1 ? " from agent" : "", ssh_err(r)); | ||
1461 | goto out; | 1507 | goto out; |
1462 | } | 1508 | } |
1463 | if (slen == 0 || signature == NULL) /* shouldn't happen */ | 1509 | if (slen == 0 || signature == NULL) /* shouldn't happen */ |
@@ -1582,6 +1628,14 @@ load_identity_file(Identity *id) | |||
1582 | quit = 1; | 1628 | quit = 1; |
1583 | break; | 1629 | break; |
1584 | } | 1630 | } |
1631 | if (private != NULL && sshkey_is_sk(private) && | ||
1632 | options.sk_provider == NULL) { | ||
1633 | debug("key \"%s\" is an authenticator-hosted key, " | ||
1634 | "but no provider specified", id->filename); | ||
1635 | sshkey_free(private); | ||
1636 | private = NULL; | ||
1637 | quit = 1; | ||
1638 | } | ||
1585 | if (!quit && private != NULL && id->agent_fd == -1 && | 1639 | if (!quit && private != NULL && id->agent_fd == -1 && |
1586 | !(id->key && id->isprivate)) | 1640 | !(id->key && id->isprivate)) |
1587 | maybe_add_key_to_agent(id->filename, private, comment, | 1641 | maybe_add_key_to_agent(id->filename, private, comment, |
@@ -1652,8 +1706,19 @@ pubkey_prepare(Authctxt *authctxt) | |||
1652 | /* list of keys stored in the filesystem and PKCS#11 */ | 1706 | /* list of keys stored in the filesystem and PKCS#11 */ |
1653 | for (i = 0; i < options.num_identity_files; i++) { | 1707 | for (i = 0; i < options.num_identity_files; i++) { |
1654 | key = options.identity_keys[i]; | 1708 | key = options.identity_keys[i]; |
1655 | if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER) | 1709 | if (key && key->cert && |
1710 | key->cert->type != SSH2_CERT_TYPE_USER) { | ||
1711 | debug("%s: ignoring certificate %s: not a user " | ||
1712 | "certificate", __func__, | ||
1713 | options.identity_files[i]); | ||
1714 | continue; | ||
1715 | } | ||
1716 | if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { | ||
1717 | debug("%s: ignoring authenticator-hosted key %s as no " | ||
1718 | "SecurityKeyProvider has been specified", | ||
1719 | __func__, options.identity_files[i]); | ||
1656 | continue; | 1720 | continue; |
1721 | } | ||
1657 | options.identity_keys[i] = NULL; | 1722 | options.identity_keys[i] = NULL; |
1658 | id = xcalloc(1, sizeof(*id)); | 1723 | id = xcalloc(1, sizeof(*id)); |
1659 | id->agent_fd = -1; | 1724 | id->agent_fd = -1; |
@@ -1666,8 +1731,19 @@ pubkey_prepare(Authctxt *authctxt) | |||
1666 | for (i = 0; i < options.num_certificate_files; i++) { | 1731 | for (i = 0; i < options.num_certificate_files; i++) { |
1667 | key = options.certificates[i]; | 1732 | key = options.certificates[i]; |
1668 | if (!sshkey_is_cert(key) || key->cert == NULL || | 1733 | if (!sshkey_is_cert(key) || key->cert == NULL || |
1669 | key->cert->type != SSH2_CERT_TYPE_USER) | 1734 | key->cert->type != SSH2_CERT_TYPE_USER) { |
1735 | debug("%s: ignoring certificate %s: not a user " | ||
1736 | "certificate", __func__, | ||
1737 | options.identity_files[i]); | ||
1670 | continue; | 1738 | continue; |
1739 | } | ||
1740 | if (key && sshkey_is_sk(key) && options.sk_provider == NULL) { | ||
1741 | debug("%s: ignoring authenticator-hosted key " | ||
1742 | "certificate %s as no " | ||
1743 | "SecurityKeyProvider has been specified", | ||
1744 | __func__, options.identity_files[i]); | ||
1745 | continue; | ||
1746 | } | ||
1671 | id = xcalloc(1, sizeof(*id)); | 1747 | id = xcalloc(1, sizeof(*id)); |
1672 | id->agent_fd = -1; | 1748 | id->agent_fd = -1; |
1673 | id->key = key; | 1749 | id->key = key; |
@@ -1992,7 +2068,7 @@ ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, | |||
1992 | error("%s: fork: %s", __func__, strerror(errno)); | 2068 | error("%s: fork: %s", __func__, strerror(errno)); |
1993 | return -1; | 2069 | return -1; |
1994 | } | 2070 | } |
1995 | osigchld = signal(SIGCHLD, SIG_DFL); | 2071 | osigchld = ssh_signal(SIGCHLD, SIG_DFL); |
1996 | if (pid == 0) { | 2072 | if (pid == 0) { |
1997 | close(from[0]); | 2073 | close(from[0]); |
1998 | if (dup2(from[1], STDOUT_FILENO) == -1) | 2074 | if (dup2(from[1], STDOUT_FILENO) == -1) |
@@ -2064,11 +2140,11 @@ ssh_keysign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp, | |||
2064 | if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) { | 2140 | if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) { |
2065 | error("%s: buffer error: %s", __func__, ssh_err(r)); | 2141 | error("%s: buffer error: %s", __func__, ssh_err(r)); |
2066 | fail: | 2142 | fail: |
2067 | signal(SIGCHLD, osigchld); | 2143 | ssh_signal(SIGCHLD, osigchld); |
2068 | sshbuf_free(b); | 2144 | sshbuf_free(b); |
2069 | return -1; | 2145 | return -1; |
2070 | } | 2146 | } |
2071 | signal(SIGCHLD, osigchld); | 2147 | ssh_signal(SIGCHLD, osigchld); |
2072 | sshbuf_free(b); | 2148 | sshbuf_free(b); |
2073 | 2149 | ||
2074 | return 0; | 2150 | return 0; |