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 1a6545edf..f64aae66a 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. |
@@ -102,12 +102,25 @@ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) | |||
102 | return 0; | 102 | return 0; |
103 | } | 103 | } |
104 | 104 | ||
105 | /* Returns the first item from a comma-separated algorithm list */ | ||
106 | static char * | ||
107 | first_alg(const char *algs) | ||
108 | { | ||
109 | char *ret, *cp; | ||
110 | |||
111 | ret = xstrdup(algs); | ||
112 | if ((cp = strchr(ret, ',')) != NULL) | ||
113 | *cp = '\0'; | ||
114 | return ret; | ||
115 | } | ||
116 | |||
105 | static char * | 117 | static char * |
106 | order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | 118 | order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) |
107 | { | 119 | { |
108 | char *oavail, *avail, *first, *last, *alg, *hostname, *ret; | 120 | char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL; |
121 | char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL; | ||
109 | size_t maxlen; | 122 | size_t maxlen; |
110 | struct hostkeys *hostkeys; | 123 | struct hostkeys *hostkeys = NULL; |
111 | int ktype; | 124 | int ktype; |
112 | u_int i; | 125 | u_int i; |
113 | 126 | ||
@@ -119,6 +132,26 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
119 | for (i = 0; i < options.num_system_hostfiles; i++) | 132 | for (i = 0; i < options.num_system_hostfiles; i++) |
120 | load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); | 133 | load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); |
121 | 134 | ||
135 | /* | ||
136 | * If a plain public key exists that matches the type of the best | ||
137 | * preference HostkeyAlgorithms, then use the whole list as is. | ||
138 | * Note that we ignore whether the best preference algorithm is a | ||
139 | * certificate type, as sshconnect.c will downgrade certs to | ||
140 | * plain keys if necessary. | ||
141 | */ | ||
142 | best = first_alg(options.hostkeyalgorithms); | ||
143 | if (lookup_key_in_hostkeys_by_type(hostkeys, | ||
144 | sshkey_type_plain(sshkey_type_from_name(best)), NULL)) { | ||
145 | debug3("%s: have matching best-preference key type %s, " | ||
146 | "using HostkeyAlgorithms verbatim", __func__, best); | ||
147 | ret = xstrdup(options.hostkeyalgorithms); | ||
148 | goto out; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Otherwise, prefer the host key algorithms that match known keys | ||
153 | * while keeping the ordering of HostkeyAlgorithms as much as possible. | ||
154 | */ | ||
122 | oavail = avail = xstrdup(options.hostkeyalgorithms); | 155 | oavail = avail = xstrdup(options.hostkeyalgorithms); |
123 | maxlen = strlen(avail) + 1; | 156 | maxlen = strlen(avail) + 1; |
124 | first = xmalloc(maxlen); | 157 | first = xmalloc(maxlen); |
@@ -135,11 +168,23 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
135 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { | 168 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { |
136 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) | 169 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) |
137 | fatal("%s: unknown alg %s", __func__, alg); | 170 | fatal("%s: unknown alg %s", __func__, alg); |
171 | /* | ||
172 | * If we have a @cert-authority marker in known_hosts then | ||
173 | * prefer all certificate algorithms. | ||
174 | */ | ||
175 | if (sshkey_type_is_cert(ktype) && | ||
176 | lookup_marker_in_hostkeys(hostkeys, MRK_CA)) { | ||
177 | ALG_APPEND(first, alg); | ||
178 | continue; | ||
179 | } | ||
180 | /* If the key appears in known_hosts then prefer it */ | ||
138 | if (lookup_key_in_hostkeys_by_type(hostkeys, | 181 | if (lookup_key_in_hostkeys_by_type(hostkeys, |
139 | sshkey_type_plain(ktype), NULL)) | 182 | sshkey_type_plain(ktype), NULL)) { |
140 | ALG_APPEND(first, alg); | 183 | ALG_APPEND(first, alg); |
141 | else | 184 | continue; |
142 | ALG_APPEND(last, alg); | 185 | } |
186 | /* Otherwise, put it last */ | ||
187 | ALG_APPEND(last, alg); | ||
143 | } | 188 | } |
144 | #undef ALG_APPEND | 189 | #undef ALG_APPEND |
145 | xasprintf(&ret, "%s%s%s", first, | 190 | xasprintf(&ret, "%s%s%s", first, |
@@ -147,6 +192,8 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
147 | if (*first != '\0') | 192 | if (*first != '\0') |
148 | debug3("%s: prefer hostkeyalgs: %s", __func__, first); | 193 | debug3("%s: prefer hostkeyalgs: %s", __func__, first); |
149 | 194 | ||
195 | out: | ||
196 | free(best); | ||
150 | free(first); | 197 | free(first); |
151 | free(last); | 198 | free(last); |
152 | free(hostname); | 199 | free(hostname); |
@@ -1144,7 +1191,8 @@ key_sig_algorithm(struct ssh *ssh, const struct sshkey *key) | |||
1144 | while ((cp = strsep(&allowed, ",")) != NULL) { | 1191 | while ((cp = strsep(&allowed, ",")) != NULL) { |
1145 | if (sshkey_type_from_name(cp) != key->type) | 1192 | if (sshkey_type_from_name(cp) != key->type) |
1146 | continue; | 1193 | continue; |
1147 | tmp = match_list(sshkey_sigalg_by_name(cp), ssh->kex->server_sig_algs, NULL); | 1194 | tmp = match_list(sshkey_sigalg_by_name(cp), |
1195 | ssh->kex->server_sig_algs, NULL); | ||
1148 | if (tmp != NULL) | 1196 | if (tmp != NULL) |
1149 | alg = xstrdup(cp); | 1197 | alg = xstrdup(cp); |
1150 | free(tmp); | 1198 | free(tmp); |
@@ -1162,7 +1210,7 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1162 | struct sshkey *sign_key = NULL, *prv = NULL; | 1210 | struct sshkey *sign_key = NULL, *prv = NULL; |
1163 | int r = SSH_ERR_INTERNAL_ERROR; | 1211 | int r = SSH_ERR_INTERNAL_ERROR; |
1164 | struct notifier_ctx *notifier = NULL; | 1212 | struct notifier_ctx *notifier = NULL; |
1165 | char *fp = NULL; | 1213 | char *fp = NULL, *pin = NULL, *prompt = NULL; |
1166 | 1214 | ||
1167 | *sigp = NULL; | 1215 | *sigp = NULL; |
1168 | *lenp = 0; | 1216 | *lenp = 0; |
@@ -1191,20 +1239,28 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1191 | goto out; | 1239 | goto out; |
1192 | } | 1240 | } |
1193 | sign_key = prv; | 1241 | sign_key = prv; |
1194 | if (sshkey_is_sk(sign_key) && | 1242 | if (sshkey_is_sk(sign_key)) { |
1195 | (sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { | 1243 | if ((sign_key->sk_flags & |
1196 | /* XXX match batch mode should just skip these keys? */ | 1244 | SSH_SK_USER_VERIFICATION_REQD)) { |
1197 | if ((fp = sshkey_fingerprint(sign_key, | 1245 | xasprintf(&prompt, "Enter PIN for %s key %s: ", |
1198 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | 1246 | sshkey_type(sign_key), id->filename); |
1199 | fatal("%s: sshkey_fingerprint", __func__); | 1247 | pin = read_passphrase(prompt, 0); |
1200 | notifier = notify_start(options.batch_mode, | 1248 | } |
1201 | "Confirm user presence for key %s %s", | 1249 | if ((sign_key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { |
1202 | sshkey_type(sign_key), fp); | 1250 | /* XXX should batch mode just skip these? */ |
1203 | free(fp); | 1251 | if ((fp = sshkey_fingerprint(sign_key, |
1252 | options.fingerprint_hash, | ||
1253 | SSH_FP_DEFAULT)) == NULL) | ||
1254 | fatal("%s: fingerprint", __func__); | ||
1255 | notifier = notify_start(options.batch_mode, | ||
1256 | "Confirm user presence for key %s %s", | ||
1257 | sshkey_type(sign_key), fp); | ||
1258 | free(fp); | ||
1259 | } | ||
1204 | } | 1260 | } |
1205 | } | 1261 | } |
1206 | if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, | 1262 | if ((r = sshkey_sign(sign_key, sigp, lenp, data, datalen, |
1207 | alg, options.sk_provider, compat)) != 0) { | 1263 | alg, options.sk_provider, pin, compat)) != 0) { |
1208 | debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); | 1264 | debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); |
1209 | goto out; | 1265 | goto out; |
1210 | } | 1266 | } |
@@ -1219,6 +1275,9 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp, | |||
1219 | /* success */ | 1275 | /* success */ |
1220 | r = 0; | 1276 | r = 0; |
1221 | out: | 1277 | out: |
1278 | free(prompt); | ||
1279 | if (pin != NULL) | ||
1280 | freezero(pin, strlen(pin)); | ||
1222 | notify_complete(notifier); | 1281 | notify_complete(notifier); |
1223 | sshkey_free(prv); | 1282 | sshkey_free(prv); |
1224 | return r; | 1283 | return r; |
@@ -1658,10 +1717,7 @@ pubkey_prepare(Authctxt *authctxt) | |||
1658 | } | 1717 | } |
1659 | ssh_free_identitylist(idlist); | 1718 | ssh_free_identitylist(idlist); |
1660 | /* append remaining agent keys */ | 1719 | /* append remaining agent keys */ |
1661 | for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { | 1720 | TAILQ_CONCAT(preferred, &agent, next); |
1662 | TAILQ_REMOVE(&agent, id, next); | ||
1663 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1664 | } | ||
1665 | authctxt->agent_fd = agent_fd; | 1721 | authctxt->agent_fd = agent_fd; |
1666 | } | 1722 | } |
1667 | /* Prefer PKCS11 keys that are explicitly listed */ | 1723 | /* Prefer PKCS11 keys that are explicitly listed */ |
@@ -1687,10 +1743,7 @@ pubkey_prepare(Authctxt *authctxt) | |||
1687 | } | 1743 | } |
1688 | } | 1744 | } |
1689 | /* append remaining keys from the config file */ | 1745 | /* append remaining keys from the config file */ |
1690 | for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { | 1746 | TAILQ_CONCAT(preferred, &files, next); |
1691 | TAILQ_REMOVE(&files, id, next); | ||
1692 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1693 | } | ||
1694 | /* finally, filter by PubkeyAcceptedKeyTypes */ | 1747 | /* finally, filter by PubkeyAcceptedKeyTypes */ |
1695 | TAILQ_FOREACH_SAFE(id, preferred, next, id2) { | 1748 | TAILQ_FOREACH_SAFE(id, preferred, next, id2) { |
1696 | if (id->key != NULL && !key_type_allowed_by_config(id->key)) { | 1749 | if (id->key != NULL && !key_type_allowed_by_config(id->key)) { |