diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 171 |
1 files changed, 102 insertions, 69 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index 6a7b69938..343ca7459 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect2.c,v 1.213 2015/01/08 10:14:08 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect2.c,v 1.214 2015/01/14 20:05:27 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. |
@@ -70,6 +70,7 @@ | |||
70 | #include "pathnames.h" | 70 | #include "pathnames.h" |
71 | #include "uidswap.h" | 71 | #include "uidswap.h" |
72 | #include "hostfile.h" | 72 | #include "hostfile.h" |
73 | #include "ssherr.h" | ||
73 | 74 | ||
74 | #ifdef GSSAPI | 75 | #ifdef GSSAPI |
75 | #include "ssh-gss.h" | 76 | #include "ssh-gss.h" |
@@ -131,10 +132,10 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) | |||
131 | } while (0) | 132 | } while (0) |
132 | 133 | ||
133 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { | 134 | while ((alg = strsep(&avail, ",")) && *alg != '\0') { |
134 | if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC) | 135 | if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) |
135 | fatal("%s: unknown alg %s", __func__, alg); | 136 | fatal("%s: unknown alg %s", __func__, alg); |
136 | if (lookup_key_in_hostkeys_by_type(hostkeys, | 137 | if (lookup_key_in_hostkeys_by_type(hostkeys, |
137 | key_type_plain(ktype), NULL)) | 138 | sshkey_type_plain(ktype), NULL)) |
138 | ALG_APPEND(first, alg); | 139 | ALG_APPEND(first, alg); |
139 | else | 140 | else |
140 | ALG_APPEND(last, alg); | 141 | ALG_APPEND(last, alg); |
@@ -242,15 +243,15 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) | |||
242 | * Authenticate user | 243 | * Authenticate user |
243 | */ | 244 | */ |
244 | 245 | ||
245 | typedef struct Authctxt Authctxt; | 246 | typedef struct cauthctxt Authctxt; |
246 | typedef struct Authmethod Authmethod; | 247 | typedef struct cauthmethod Authmethod; |
247 | typedef struct identity Identity; | 248 | typedef struct identity Identity; |
248 | typedef struct idlist Idlist; | 249 | typedef struct idlist Idlist; |
249 | 250 | ||
250 | struct identity { | 251 | struct identity { |
251 | TAILQ_ENTRY(identity) next; | 252 | TAILQ_ENTRY(identity) next; |
252 | AuthenticationConnection *ac; /* set if agent supports key */ | 253 | int agent_fd; /* >=0 if agent supports key */ |
253 | Key *key; /* public/private key */ | 254 | struct sshkey *key; /* public/private key */ |
254 | char *filename; /* comment for agent-only keys */ | 255 | char *filename; /* comment for agent-only keys */ |
255 | int tried; | 256 | int tried; |
256 | int isprivate; /* key points to the private key */ | 257 | int isprivate; /* key points to the private key */ |
@@ -258,17 +259,18 @@ struct identity { | |||
258 | }; | 259 | }; |
259 | TAILQ_HEAD(idlist, identity); | 260 | TAILQ_HEAD(idlist, identity); |
260 | 261 | ||
261 | struct Authctxt { | 262 | struct cauthctxt { |
262 | const char *server_user; | 263 | const char *server_user; |
263 | const char *local_user; | 264 | const char *local_user; |
264 | const char *host; | 265 | const char *host; |
265 | const char *service; | 266 | const char *service; |
266 | Authmethod *method; | 267 | struct cauthmethod *method; |
267 | sig_atomic_t success; | 268 | sig_atomic_t success; |
268 | char *authlist; | 269 | char *authlist; |
270 | int attempt; | ||
269 | /* pubkey */ | 271 | /* pubkey */ |
270 | Idlist keys; | 272 | struct idlist keys; |
271 | AuthenticationConnection *agent; | 273 | int agent_fd; |
272 | /* hostbased */ | 274 | /* hostbased */ |
273 | Sensitive *sensitive; | 275 | Sensitive *sensitive; |
274 | /* kbd-interactive */ | 276 | /* kbd-interactive */ |
@@ -276,7 +278,8 @@ struct Authctxt { | |||
276 | /* generic */ | 278 | /* generic */ |
277 | void *methoddata; | 279 | void *methoddata; |
278 | }; | 280 | }; |
279 | struct Authmethod { | 281 | |
282 | struct cauthmethod { | ||
280 | char *name; /* string to compare against server's list */ | 283 | char *name; /* string to compare against server's list */ |
281 | int (*userauth)(Authctxt *authctxt); | 284 | int (*userauth)(Authctxt *authctxt); |
282 | void (*cleanup)(Authctxt *authctxt); | 285 | void (*cleanup)(Authctxt *authctxt); |
@@ -582,7 +585,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) | |||
582 | key->type, pktype); | 585 | key->type, pktype); |
583 | goto done; | 586 | goto done; |
584 | } | 587 | } |
585 | fp = key_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); | 588 | fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); |
586 | debug2("input_userauth_pk_ok: fp %s", fp); | 589 | debug2("input_userauth_pk_ok: fp %s", fp); |
587 | free(fp); | 590 | free(fp); |
588 | 591 | ||
@@ -956,27 +959,29 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
956 | } | 959 | } |
957 | 960 | ||
958 | static int | 961 | static int |
959 | identity_sign(Identity *id, u_char **sigp, u_int *lenp, | 962 | identity_sign(struct identity *id, u_char **sigp, size_t *lenp, |
960 | u_char *data, u_int datalen) | 963 | const u_char *data, size_t datalen, u_int compat) |
961 | { | 964 | { |
962 | Key *prv; | 965 | Key *prv; |
963 | int ret; | 966 | int ret; |
964 | 967 | ||
965 | /* the agent supports this key */ | 968 | /* the agent supports this key */ |
966 | if (id->ac) | 969 | if (id->agent_fd) |
967 | return (ssh_agent_sign(id->ac, id->key, sigp, lenp, | 970 | return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, |
968 | data, datalen)); | 971 | data, datalen, compat); |
972 | |||
969 | /* | 973 | /* |
970 | * we have already loaded the private key or | 974 | * we have already loaded the private key or |
971 | * the private key is stored in external hardware | 975 | * the private key is stored in external hardware |
972 | */ | 976 | */ |
973 | if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) | 977 | if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) |
974 | return (key_sign(id->key, sigp, lenp, data, datalen)); | 978 | return (sshkey_sign(id->key, sigp, lenp, data, datalen, |
979 | compat)); | ||
975 | /* load the private key from the file */ | 980 | /* load the private key from the file */ |
976 | if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) | 981 | if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL) |
977 | return (-1); | 982 | return (-1); /* XXX return decent error code */ |
978 | ret = key_sign(prv, sigp, lenp, data, datalen); | 983 | ret = sshkey_sign(prv, sigp, lenp, data, datalen, compat); |
979 | key_free(prv); | 984 | sshkey_free(prv); |
980 | return (ret); | 985 | return (ret); |
981 | } | 986 | } |
982 | 987 | ||
@@ -985,7 +990,8 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
985 | { | 990 | { |
986 | Buffer b; | 991 | Buffer b; |
987 | u_char *blob, *signature; | 992 | u_char *blob, *signature; |
988 | u_int bloblen, slen; | 993 | u_int bloblen; |
994 | size_t slen; | ||
989 | u_int skip = 0; | 995 | u_int skip = 0; |
990 | int ret = -1; | 996 | int ret = -1; |
991 | int have_sig = 1; | 997 | int have_sig = 1; |
@@ -1026,8 +1032,8 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
1026 | 1032 | ||
1027 | /* generate signature */ | 1033 | /* generate signature */ |
1028 | ret = identity_sign(id, &signature, &slen, | 1034 | ret = identity_sign(id, &signature, &slen, |
1029 | buffer_ptr(&b), buffer_len(&b)); | 1035 | buffer_ptr(&b), buffer_len(&b), datafellows); |
1030 | if (ret == -1) { | 1036 | if (ret != 0) { |
1031 | free(blob); | 1037 | free(blob); |
1032 | buffer_free(&b); | 1038 | buffer_free(&b); |
1033 | return 0; | 1039 | return 0; |
@@ -1102,7 +1108,7 @@ load_identity_file(char *filename, int userprovided) | |||
1102 | { | 1108 | { |
1103 | Key *private; | 1109 | Key *private; |
1104 | char prompt[300], *passphrase; | 1110 | char prompt[300], *passphrase; |
1105 | int perm_ok = 0, quit, i; | 1111 | int r, perm_ok = 0, quit, i; |
1106 | struct stat st; | 1112 | struct stat st; |
1107 | 1113 | ||
1108 | if (stat(filename, &st) < 0) { | 1114 | if (stat(filename, &st) < 0) { |
@@ -1110,33 +1116,49 @@ load_identity_file(char *filename, int userprovided) | |||
1110 | filename, strerror(errno)); | 1116 | filename, strerror(errno)); |
1111 | return NULL; | 1117 | return NULL; |
1112 | } | 1118 | } |
1113 | private = key_load_private_type(KEY_UNSPEC, filename, "", NULL, &perm_ok); | 1119 | snprintf(prompt, sizeof prompt, |
1114 | if (!perm_ok) { | 1120 | "Enter passphrase for key '%.100s': ", filename); |
1115 | if (private != NULL) | 1121 | for (i = 0; i <= options.number_of_password_prompts; i++) { |
1116 | key_free(private); | 1122 | if (i == 0) |
1117 | return NULL; | 1123 | passphrase = ""; |
1118 | } | 1124 | else { |
1119 | if (private == NULL) { | ||
1120 | if (options.batch_mode) | ||
1121 | return NULL; | ||
1122 | snprintf(prompt, sizeof prompt, | ||
1123 | "Enter passphrase for key '%.100s': ", filename); | ||
1124 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
1125 | passphrase = read_passphrase(prompt, 0); | 1125 | passphrase = read_passphrase(prompt, 0); |
1126 | if (strcmp(passphrase, "") != 0) { | 1126 | if (*passphrase == '\0') { |
1127 | private = key_load_private_type(KEY_UNSPEC, | ||
1128 | filename, passphrase, NULL, NULL); | ||
1129 | quit = 0; | ||
1130 | } else { | ||
1131 | debug2("no passphrase given, try next key"); | 1127 | debug2("no passphrase given, try next key"); |
1128 | free(passphrase); | ||
1129 | break; | ||
1130 | } | ||
1131 | } | ||
1132 | switch ((r = sshkey_load_private_type(KEY_UNSPEC, filename, | ||
1133 | passphrase, &private, NULL, &perm_ok))) { | ||
1134 | case 0: | ||
1135 | break; | ||
1136 | case SSH_ERR_KEY_WRONG_PASSPHRASE: | ||
1137 | if (options.batch_mode) { | ||
1138 | quit = 1; | ||
1139 | break; | ||
1140 | } | ||
1141 | debug2("bad passphrase given, try again..."); | ||
1142 | break; | ||
1143 | case SSH_ERR_SYSTEM_ERROR: | ||
1144 | if (errno == ENOENT) { | ||
1145 | debug2("Load key \"%s\": %s", | ||
1146 | filename, ssh_err(r)); | ||
1132 | quit = 1; | 1147 | quit = 1; |
1148 | break; | ||
1133 | } | 1149 | } |
1150 | /* FALLTHROUGH */ | ||
1151 | default: | ||
1152 | error("Load key \"%s\": %s", filename, ssh_err(r)); | ||
1153 | quit = 1; | ||
1154 | break; | ||
1155 | } | ||
1156 | if (i > 0) { | ||
1134 | explicit_bzero(passphrase, strlen(passphrase)); | 1157 | explicit_bzero(passphrase, strlen(passphrase)); |
1135 | free(passphrase); | 1158 | free(passphrase); |
1136 | if (private != NULL || quit) | ||
1137 | break; | ||
1138 | debug2("bad passphrase given, try again..."); | ||
1139 | } | 1159 | } |
1160 | if (private != NULL || quit) | ||
1161 | break; | ||
1140 | } | 1162 | } |
1141 | return private; | 1163 | return private; |
1142 | } | 1164 | } |
@@ -1150,12 +1172,12 @@ load_identity_file(char *filename, int userprovided) | |||
1150 | static void | 1172 | static void |
1151 | pubkey_prepare(Authctxt *authctxt) | 1173 | pubkey_prepare(Authctxt *authctxt) |
1152 | { | 1174 | { |
1153 | Identity *id, *id2, *tmp; | 1175 | struct identity *id, *id2, *tmp; |
1154 | Idlist agent, files, *preferred; | 1176 | struct idlist agent, files, *preferred; |
1155 | Key *key; | 1177 | struct sshkey *key; |
1156 | AuthenticationConnection *ac; | 1178 | int agent_fd, i, r, found; |
1157 | char *comment; | 1179 | size_t j; |
1158 | int i, found; | 1180 | struct ssh_identitylist *idlist; |
1159 | 1181 | ||
1160 | TAILQ_INIT(&agent); /* keys from the agent */ | 1182 | TAILQ_INIT(&agent); /* keys from the agent */ |
1161 | TAILQ_INIT(&files); /* keys from the config file */ | 1183 | TAILQ_INIT(&files); /* keys from the config file */ |
@@ -1185,7 +1207,7 @@ pubkey_prepare(Authctxt *authctxt) | |||
1185 | if (id2->key == NULL || | 1207 | if (id2->key == NULL || |
1186 | (id2->key->flags & SSHKEY_FLAG_EXT) == 0) | 1208 | (id2->key->flags & SSHKEY_FLAG_EXT) == 0) |
1187 | continue; | 1209 | continue; |
1188 | if (key_equal(id->key, id2->key)) { | 1210 | if (sshkey_equal(id->key, id2->key)) { |
1189 | TAILQ_REMOVE(&files, id, next); | 1211 | TAILQ_REMOVE(&files, id, next); |
1190 | TAILQ_INSERT_TAIL(preferred, id, next); | 1212 | TAILQ_INSERT_TAIL(preferred, id, next); |
1191 | found = 1; | 1213 | found = 1; |
@@ -1200,37 +1222,48 @@ pubkey_prepare(Authctxt *authctxt) | |||
1200 | } | 1222 | } |
1201 | } | 1223 | } |
1202 | /* list of keys supported by the agent */ | 1224 | /* list of keys supported by the agent */ |
1203 | if ((ac = ssh_get_authentication_connection())) { | 1225 | if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { |
1204 | for (key = ssh_get_first_identity(ac, &comment, 2); | 1226 | if (r != SSH_ERR_AGENT_NOT_PRESENT) |
1205 | key != NULL; | 1227 | debug("%s: ssh_get_authentication_socket: %s", |
1206 | key = ssh_get_next_identity(ac, &comment, 2)) { | 1228 | __func__, ssh_err(r)); |
1229 | } else if ((r = ssh_fetch_identitylist(agent_fd, 2, &idlist)) != 0) { | ||
1230 | if (r != SSH_ERR_AGENT_NO_IDENTITIES) | ||
1231 | debug("%s: ssh_fetch_identitylist: %s", | ||
1232 | __func__, ssh_err(r)); | ||
1233 | } else { | ||
1234 | for (j = 0; j < idlist->nkeys; j++) { | ||
1207 | found = 0; | 1235 | found = 0; |
1208 | TAILQ_FOREACH(id, &files, next) { | 1236 | TAILQ_FOREACH(id, &files, next) { |
1209 | /* agent keys from the config file are preferred */ | 1237 | /* |
1210 | if (key_equal(key, id->key)) { | 1238 | * agent keys from the config file are |
1211 | key_free(key); | 1239 | * preferred |
1212 | free(comment); | 1240 | */ |
1241 | if (sshkey_equal(idlist->keys[j], id->key)) { | ||
1213 | TAILQ_REMOVE(&files, id, next); | 1242 | TAILQ_REMOVE(&files, id, next); |
1214 | TAILQ_INSERT_TAIL(preferred, id, next); | 1243 | TAILQ_INSERT_TAIL(preferred, id, next); |
1215 | id->ac = ac; | 1244 | id->agent_fd = agent_fd; |
1216 | found = 1; | 1245 | found = 1; |
1217 | break; | 1246 | break; |
1218 | } | 1247 | } |
1219 | } | 1248 | } |
1220 | if (!found && !options.identities_only) { | 1249 | if (!found && !options.identities_only) { |
1221 | id = xcalloc(1, sizeof(*id)); | 1250 | id = xcalloc(1, sizeof(*id)); |
1222 | id->key = key; | 1251 | /* XXX "steals" key/comment from idlist */ |
1223 | id->filename = comment; | 1252 | id->key = idlist->keys[j]; |
1224 | id->ac = ac; | 1253 | id->filename = idlist->comments[j]; |
1254 | idlist->keys[j] = NULL; | ||
1255 | idlist->comments[j] = NULL; | ||
1256 | id->agent_fd = agent_fd; | ||
1225 | TAILQ_INSERT_TAIL(&agent, id, next); | 1257 | TAILQ_INSERT_TAIL(&agent, id, next); |
1226 | } | 1258 | } |
1227 | } | 1259 | } |
1260 | ssh_free_identitylist(idlist); | ||
1228 | /* append remaining agent keys */ | 1261 | /* append remaining agent keys */ |
1229 | for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { | 1262 | for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { |
1230 | TAILQ_REMOVE(&agent, id, next); | 1263 | TAILQ_REMOVE(&agent, id, next); |
1231 | TAILQ_INSERT_TAIL(preferred, id, next); | 1264 | TAILQ_INSERT_TAIL(preferred, id, next); |
1232 | } | 1265 | } |
1233 | authctxt->agent = ac; | 1266 | authctxt->agent_fd = agent_fd; |
1234 | } | 1267 | } |
1235 | /* append remaining keys from the config file */ | 1268 | /* append remaining keys from the config file */ |
1236 | for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { | 1269 | for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { |
@@ -1248,13 +1281,13 @@ pubkey_cleanup(Authctxt *authctxt) | |||
1248 | { | 1281 | { |
1249 | Identity *id; | 1282 | Identity *id; |
1250 | 1283 | ||
1251 | if (authctxt->agent != NULL) | 1284 | if (authctxt->agent_fd != -1) |
1252 | ssh_close_authentication_connection(authctxt->agent); | 1285 | ssh_close_authentication_socket(authctxt->agent_fd); |
1253 | for (id = TAILQ_FIRST(&authctxt->keys); id; | 1286 | for (id = TAILQ_FIRST(&authctxt->keys); id; |
1254 | id = TAILQ_FIRST(&authctxt->keys)) { | 1287 | id = TAILQ_FIRST(&authctxt->keys)) { |
1255 | TAILQ_REMOVE(&authctxt->keys, id, next); | 1288 | TAILQ_REMOVE(&authctxt->keys, id, next); |
1256 | if (id->key) | 1289 | if (id->key) |
1257 | key_free(id->key); | 1290 | sshkey_free(id->key); |
1258 | free(id->filename); | 1291 | free(id->filename); |
1259 | free(id); | 1292 | free(id); |
1260 | } | 1293 | } |