diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 107 |
1 files changed, 80 insertions, 27 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index 79a22e600..c47fc31a6 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect2.c,v 1.321 2020/04/17 03:38:47 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect2.c,v 1.326 2020/09/18 05:23:03 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. |
@@ -100,12 +100,25 @@ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) | |||
100 | return 0; | 100 | return 0; |
101 | } | 101 | } |
102 | 102 | ||
103 | /* Returns the first item from a comma-separated algorithm list */ | ||
104 | static char * | ||
105 | first_alg(const char *algs) | ||
106 | { | ||
107 | char *ret, *cp; | ||
108 | |||
109 | ret = xstrdup(algs); | ||
110 | if ((cp = strchr(ret, ',')) != NULL) | ||
111 | *cp = '\0'; | ||
112 | return ret; | ||
113 | } | ||
114 | |||
103 | static char * | 115 | static char * |
104 | order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | 116 | order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) |
105 | { | 117 | { |
106 | char *oavail, *avail, *first, *last, *alg, *hostname, *ret; | 118 | char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL; |
119 | char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL; | ||
107 | size_t maxlen; | 120 | size_t maxlen; |
108 | struct hostkeys *hostkeys; | 121 | struct hostkeys *hostkeys = NULL; |
109 | int ktype; | 122 | int ktype; |
110 | u_int i; | 123 | u_int i; |
111 | 124 | ||
@@ -117,6 +130,26 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
117 | for (i = 0; i < options.num_system_hostfiles; i++) | 130 | for (i = 0; i < options.num_system_hostfiles; i++) |
118 | load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); | 131 | load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); |
119 | 132 | ||
133 | /* | ||
134 | * If a plain public key exists that matches the type of the best | ||
135 | * preference HostkeyAlgorithms, then use the whole list as is. | ||
136 | * Note that we ignore whether the best preference algorithm is a | ||
137 | * certificate type, as sshconnect.c will downgrade certs to | ||
138 | * plain keys if necessary. | ||
139 | */ | ||
140 | best = first_alg(options.hostkeyalgorithms); | ||
141 | if (lookup_key_in_hostkeys_by_type(hostkeys, | ||
142 | sshkey_type_plain(sshkey_type_from_name(best)), NULL)) { | ||
143 | debug3("%s: have matching best-preference key type %s, " | ||
144 | "using HostkeyAlgorithms verbatim", __func__, best); | ||
145 | ret = xstrdup(options.hostkeyalgorithms); | ||
146 | goto out; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Otherwise, prefer the host key algorithms that match known keys | ||
151 | * while keeping the ordering of HostkeyAlgorithms as much as possible. | ||
152 | */ | ||
120 | oavail = avail = xstrdup(options.hostkeyalgorithms); | 153 | oavail = avail = xstrdup(options.hostkeyalgorithms); |
121 | maxlen = strlen(avail) + 1; | 154 | maxlen = strlen(avail) + 1; |
122 | first = xmalloc(maxlen); | 155 | first = xmalloc(maxlen); |
@@ -133,11 +166,23 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
133 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { | 166 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { |
134 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) | 167 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) |
135 | fatal("%s: unknown alg %s", __func__, alg); | 168 | fatal("%s: unknown alg %s", __func__, alg); |
169 | /* | ||
170 | * If we have a @cert-authority marker in known_hosts then | ||
171 | * prefer all certificate algorithms. | ||
172 | */ | ||
173 | if (sshkey_type_is_cert(ktype) && | ||
174 | lookup_marker_in_hostkeys(hostkeys, MRK_CA)) { | ||
175 | ALG_APPEND(first, alg); | ||
176 | continue; | ||
177 | } | ||
178 | /* If the key appears in known_hosts then prefer it */ | ||
136 | if (lookup_key_in_hostkeys_by_type(hostkeys, | 179 | if (lookup_key_in_hostkeys_by_type(hostkeys, |
137 | sshkey_type_plain(ktype), NULL)) | 180 | sshkey_type_plain(ktype), NULL)) { |
138 | ALG_APPEND(first, alg); | 181 | ALG_APPEND(first, alg); |
139 | else | 182 | continue; |
140 | ALG_APPEND(last, alg); | 183 | } |
184 | /* Otherwise, put it last */ | ||
185 | ALG_APPEND(last, alg); | ||
141 | } | 186 | } |
142 | #undef ALG_APPEND | 187 | #undef ALG_APPEND |
143 | xasprintf(&ret, "%s%s%s", first, | 188 | xasprintf(&ret, "%s%s%s", first, |
@@ -145,6 +190,8 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
145 | if (*first != '\0') | 190 | if (*first != '\0') |
146 | debug3("%s: prefer hostkeyalgs: %s", __func__, first); | 191 | debug3("%s: prefer hostkeyalgs: %s", __func__, first); |
147 | 192 | ||
193 | out: | ||
194 | free(best); | ||
148 | free(first); | 195 | free(first); |
149 | free(last); | 196 | free(last); |
150 | free(hostname); | 197 | free(hostname); |
@@ -1288,7 +1335,8 @@ key_sig_algorithm(struct ssh *ssh, const struct sshkey *key) | |||
1288 | while ((cp = strsep(&allowed, ",")) != NULL) { | 1335 | while ((cp = strsep(&allowed, ",")) != NULL) { |
1289 | if (sshkey_type_from_name(cp) != key->type) | 1336 | if (sshkey_type_from_name(cp) != key->type) |
1290 | continue; | 1337 | continue; |
1291 | tmp = match_list(sshkey_sigalg_by_name(cp), ssh->kex->server_sig_algs, NULL); | 1338 | tmp = match_list(sshkey_sigalg_by_name(cp), |
1339 | ssh->kex->server_sig_algs, NULL); | ||
1292 | if (tmp != NULL) | 1340 | if (tmp != NULL) |
1293 | alg = xstrdup(cp); | 1341 | alg = xstrdup(cp); |
1294 | free(tmp); | 1342 | free(tmp); |
@@ -1306,7 +1354,7 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1306 | struct sshkey *sign_key = NULL, *prv = NULL; | 1354 | struct sshkey *sign_key = NULL, *prv = NULL; |
1307 | int r = SSH_ERR_INTERNAL_ERROR; | 1355 | int r = SSH_ERR_INTERNAL_ERROR; |
1308 | struct notifier_ctx *notifier = NULL; | 1356 | struct notifier_ctx *notifier = NULL; |
1309 | char *fp = NULL; | 1357 | char *fp = NULL, *pin = NULL, *prompt = NULL; |
1310 | 1358 | ||
1311 | *sigp = NULL; | 1359 | *sigp = NULL; |
1312 | *lenp = 0; | 1360 | *lenp = 0; |
@@ -1335,20 +1383,28 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1335 | goto out; | 1383 | goto out; |
1336 | } | 1384 | } |
1337 | sign_key = prv; | 1385 | sign_key = prv; |
1338 | if (sshkey_is_sk(sign_key) && | 1386 | if (sshkey_is_sk(sign_key)) { |
1339 | (sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | 1387 | if ((sign_key->sk_flags & |
1340 | /* XXX match batch mode should just skip these keys? */ | 1388 | SSH_SK_USER_VERIFICATION_REQD)) { |
1341 | if ((fp = sshkey_fingerprint(sign_key, | 1389 | xasprintf(&prompt, "Enter PIN for %s key %s: ", |
1342 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | 1390 | sshkey_type(sign_key), id->filename); |
1343 | fatal("%s: sshkey_fingerprint", __func__); | 1391 | pin = read_passphrase(prompt, 0); |
1344 | notifier = notify_start(options.batch_mode, | 1392 | } |
1345 | "Confirm user presence for key %s %s", | 1393 | if ((sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { |
1346 | sshkey_type(sign_key), fp); | 1394 | /* XXX should batch mode just skip these? */ |
1347 | free(fp); | 1395 | if ((fp = sshkey_fingerprint(sign_key, |
1396 | options.fingerprint_hash, | ||
1397 | SSH_FP_DEFAULT)) == NULL) | ||
1398 | fatal("%s: fingerprint", __func__); | ||
1399 | notifier = notify_start(options.batch_mode, | ||
1400 | "Confirm user presence for key %s %s", | ||
1401 | sshkey_type(sign_key), fp); | ||
1402 | free(fp); | ||
1403 | } | ||
1348 | } | 1404 | } |
1349 | } | 1405 | } |
1350 | if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, | 1406 | if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, |
1351 | alg, options.sk_provider, compat)) != 0) { | 1407 | alg, options.sk_provider, pin, compat)) != 0) { |
1352 | debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); | 1408 | debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); |
1353 | goto out; | 1409 | goto out; |
1354 | } | 1410 | } |
@@ -1363,6 +1419,9 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1363 | /* success */ | 1419 | /* success */ |
1364 | r = 0; | 1420 | r = 0; |
1365 | out: | 1421 | out: |
1422 | free(prompt); | ||
1423 | if (pin != NULL) | ||
1424 | freezero(pin, strlen(pin)); | ||
1366 | notify_complete(notifier); | 1425 | notify_complete(notifier); |
1367 | sshkey_free(prv); | 1426 | sshkey_free(prv); |
1368 | return r; | 1427 | return r; |
@@ -1802,10 +1861,7 @@ pubkey_prepare(Authctxt *authctxt) | |||
1802 | } | 1861 | } |
1803 | ssh_free_identitylist(idlist); | 1862 | ssh_free_identitylist(idlist); |
1804 | /* append remaining agent keys */ | 1863 | /* append remaining agent keys */ |
1805 | for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { | 1864 | TAILQ_CONCAT(preferred, &agent, next); |
1806 | TAILQ_REMOVE(&agent, id, next); | ||
1807 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1808 | } | ||
1809 | authctxt->agent_fd = agent_fd; | 1865 | authctxt->agent_fd = agent_fd; |
1810 | } | 1866 | } |
1811 | /* Prefer PKCS11 keys that are explicitly listed */ | 1867 | /* Prefer PKCS11 keys that are explicitly listed */ |
@@ -1831,10 +1887,7 @@ pubkey_prepare(Authctxt *authctxt) | |||
1831 | } | 1887 | } |
1832 | } | 1888 | } |
1833 | /* append remaining keys from the config file */ | 1889 | /* append remaining keys from the config file */ |
1834 | for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { | 1890 | TAILQ_CONCAT(preferred, &files, next); |
1835 | TAILQ_REMOVE(&files, id, next); | ||
1836 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1837 | } | ||
1838 | /* finally, filter by PubkeyAcceptedKeyTypes */ | 1891 | /* finally, filter by PubkeyAcceptedKeyTypes */ |
1839 | TAILQ_FOREACH_SAFE(id, preferred, next, id2) { | 1892 | TAILQ_FOREACH_SAFE(id, preferred, next, id2) { |
1840 | if (id->key != NULL && !key_type_allowed_by_config(id->key)) { | 1893 | if (id->key != NULL && !key_type_allowed_by_config(id->key)) { |