diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 625 |
1 files changed, 164 insertions, 461 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index 933c223ec..642b34b9e 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -23,9 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include "includes.h" | 25 | #include "includes.h" |
26 | RCSID("$OpenBSD: sshconnect2.c,v 1.124 2003/08/25 10:33:33 djm Exp $"); | 26 | RCSID("$OpenBSD: sshconnect2.c,v 1.114 2003/04/01 10:22:21 markus Exp $"); |
27 | |||
28 | #include "openbsd-compat/sys-queue.h" | ||
29 | 27 | ||
30 | #include "ssh.h" | 28 | #include "ssh.h" |
31 | #include "ssh2.h" | 29 | #include "ssh2.h" |
@@ -50,10 +48,6 @@ RCSID("$OpenBSD: sshconnect2.c,v 1.124 2003/08/25 10:33:33 djm Exp $"); | |||
50 | #include "msg.h" | 48 | #include "msg.h" |
51 | #include "pathnames.h" | 49 | #include "pathnames.h" |
52 | 50 | ||
53 | #ifdef GSSAPI | ||
54 | #include "ssh-gss.h" | ||
55 | #endif | ||
56 | |||
57 | /* import */ | 51 | /* import */ |
58 | extern char *client_version_string; | 52 | extern char *client_version_string; |
59 | extern char *server_version_string; | 53 | extern char *server_version_string; |
@@ -64,7 +58,7 @@ extern Options options; | |||
64 | */ | 58 | */ |
65 | 59 | ||
66 | u_char *session_id2 = NULL; | 60 | u_char *session_id2 = NULL; |
67 | u_int session_id2_len = 0; | 61 | int session_id2_len = 0; |
68 | 62 | ||
69 | char *xxx_host; | 63 | char *xxx_host; |
70 | struct sockaddr *xxx_hostaddr; | 64 | struct sockaddr *xxx_hostaddr; |
@@ -88,7 +82,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
88 | xxx_hostaddr = hostaddr; | 82 | xxx_hostaddr = hostaddr; |
89 | 83 | ||
90 | if (options.ciphers == (char *)-1) { | 84 | if (options.ciphers == (char *)-1) { |
91 | logit("No valid ciphers for protocol version 2 given, using defaults."); | 85 | log("No valid ciphers for protocol version 2 given, using defaults."); |
92 | options.ciphers = NULL; | 86 | options.ciphers = NULL; |
93 | } | 87 | } |
94 | if (options.ciphers != NULL) { | 88 | if (options.ciphers != NULL) { |
@@ -114,9 +108,6 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
114 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | 108 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = |
115 | options.hostkeyalgorithms; | 109 | options.hostkeyalgorithms; |
116 | 110 | ||
117 | if (options.rekey_limit) | ||
118 | packet_set_rekey_limit(options.rekey_limit); | ||
119 | |||
120 | /* start key exchange */ | 111 | /* start key exchange */ |
121 | kex = kex_setup(myproposal); | 112 | kex = kex_setup(myproposal); |
122 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; | 113 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; |
@@ -147,18 +138,10 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
147 | 138 | ||
148 | typedef struct Authctxt Authctxt; | 139 | typedef struct Authctxt Authctxt; |
149 | typedef struct Authmethod Authmethod; | 140 | typedef struct Authmethod Authmethod; |
150 | typedef struct identity Identity; | 141 | |
151 | typedef struct idlist Idlist; | 142 | typedef int sign_cb_fn( |
152 | 143 | Authctxt *authctxt, Key *key, | |
153 | struct identity { | 144 | u_char **sigp, u_int *lenp, u_char *data, u_int datalen); |
154 | TAILQ_ENTRY(identity) next; | ||
155 | AuthenticationConnection *ac; /* set if agent supports key */ | ||
156 | Key *key; /* public/private key */ | ||
157 | char *filename; /* comment for agent-only keys */ | ||
158 | int tried; | ||
159 | int isprivate; /* key points to the private key */ | ||
160 | }; | ||
161 | TAILQ_HEAD(idlist, identity); | ||
162 | 145 | ||
163 | struct Authctxt { | 146 | struct Authctxt { |
164 | const char *server_user; | 147 | const char *server_user; |
@@ -169,14 +152,14 @@ struct Authctxt { | |||
169 | int success; | 152 | int success; |
170 | char *authlist; | 153 | char *authlist; |
171 | /* pubkey */ | 154 | /* pubkey */ |
172 | Idlist keys; | 155 | Key *last_key; |
156 | sign_cb_fn *last_key_sign; | ||
157 | int last_key_hint; | ||
173 | AuthenticationConnection *agent; | 158 | AuthenticationConnection *agent; |
174 | /* hostbased */ | 159 | /* hostbased */ |
175 | Sensitive *sensitive; | 160 | Sensitive *sensitive; |
176 | /* kbd-interactive */ | 161 | /* kbd-interactive */ |
177 | int info_req_seen; | 162 | int info_req_seen; |
178 | /* generic */ | ||
179 | void *methoddata; | ||
180 | }; | 163 | }; |
181 | struct Authmethod { | 164 | struct Authmethod { |
182 | char *name; /* string to compare against server's list */ | 165 | char *name; /* string to compare against server's list */ |
@@ -198,35 +181,17 @@ int userauth_pubkey(Authctxt *); | |||
198 | int userauth_passwd(Authctxt *); | 181 | int userauth_passwd(Authctxt *); |
199 | int userauth_kbdint(Authctxt *); | 182 | int userauth_kbdint(Authctxt *); |
200 | int userauth_hostbased(Authctxt *); | 183 | int userauth_hostbased(Authctxt *); |
201 | int userauth_kerberos(Authctxt *); | ||
202 | |||
203 | #ifdef GSSAPI | ||
204 | int userauth_gssapi(Authctxt *authctxt); | ||
205 | void input_gssapi_response(int type, u_int32_t, void *); | ||
206 | void input_gssapi_token(int type, u_int32_t, void *); | ||
207 | void input_gssapi_hash(int type, u_int32_t, void *); | ||
208 | void input_gssapi_error(int, u_int32_t, void *); | ||
209 | void input_gssapi_errtok(int, u_int32_t, void *); | ||
210 | #endif | ||
211 | 184 | ||
212 | void userauth(Authctxt *, char *); | 185 | void userauth(Authctxt *, char *); |
213 | 186 | ||
214 | static int sign_and_send_pubkey(Authctxt *, Identity *); | 187 | static int sign_and_send_pubkey(Authctxt *, Key *, sign_cb_fn *); |
215 | static void pubkey_prepare(Authctxt *); | 188 | static void clear_auth_state(Authctxt *); |
216 | static void pubkey_cleanup(Authctxt *); | ||
217 | static Key *load_identity_file(char *); | ||
218 | 189 | ||
219 | static Authmethod *authmethod_get(char *authlist); | 190 | static Authmethod *authmethod_get(char *authlist); |
220 | static Authmethod *authmethod_lookup(const char *name); | 191 | static Authmethod *authmethod_lookup(const char *name); |
221 | static char *authmethods_get(void); | 192 | static char *authmethods_get(void); |
222 | 193 | ||
223 | Authmethod authmethods[] = { | 194 | Authmethod authmethods[] = { |
224 | #ifdef GSSAPI | ||
225 | {"gssapi", | ||
226 | userauth_gssapi, | ||
227 | &options.gss_authentication, | ||
228 | NULL}, | ||
229 | #endif | ||
230 | {"hostbased", | 195 | {"hostbased", |
231 | userauth_hostbased, | 196 | userauth_hostbased, |
232 | &options.hostbased_authentication, | 197 | &options.hostbased_authentication, |
@@ -283,7 +248,7 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
283 | 248 | ||
284 | /* setup authentication context */ | 249 | /* setup authentication context */ |
285 | memset(&authctxt, 0, sizeof(authctxt)); | 250 | memset(&authctxt, 0, sizeof(authctxt)); |
286 | pubkey_prepare(&authctxt); | 251 | authctxt.agent = ssh_get_authentication_connection(); |
287 | authctxt.server_user = server_user; | 252 | authctxt.server_user = server_user; |
288 | authctxt.local_user = local_user; | 253 | authctxt.local_user = local_user; |
289 | authctxt.host = host; | 254 | authctxt.host = host; |
@@ -291,7 +256,6 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
291 | authctxt.success = 0; | 256 | authctxt.success = 0; |
292 | authctxt.method = authmethod_lookup("none"); | 257 | authctxt.method = authmethod_lookup("none"); |
293 | authctxt.authlist = NULL; | 258 | authctxt.authlist = NULL; |
294 | authctxt.methoddata = NULL; | ||
295 | authctxt.sensitive = sensitive; | 259 | authctxt.sensitive = sensitive; |
296 | authctxt.info_req_seen = 0; | 260 | authctxt.info_req_seen = 0; |
297 | if (authctxt.method == NULL) | 261 | if (authctxt.method == NULL) |
@@ -306,19 +270,14 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
306 | dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); | 270 | dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); |
307 | dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ | 271 | dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ |
308 | 272 | ||
309 | pubkey_cleanup(&authctxt); | 273 | if (authctxt.agent != NULL) |
310 | dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); | 274 | ssh_close_authentication_connection(authctxt.agent); |
311 | 275 | ||
312 | debug("Authentication succeeded (%s).", authctxt.method->name); | 276 | debug("Authentication succeeded (%s).", authctxt.method->name); |
313 | } | 277 | } |
314 | |||
315 | void | 278 | void |
316 | userauth(Authctxt *authctxt, char *authlist) | 279 | userauth(Authctxt *authctxt, char *authlist) |
317 | { | 280 | { |
318 | if (authctxt->methoddata) { | ||
319 | xfree(authctxt->methoddata); | ||
320 | authctxt->methoddata = NULL; | ||
321 | } | ||
322 | if (authlist == NULL) { | 281 | if (authlist == NULL) { |
323 | authlist = authctxt->authlist; | 282 | authlist = authctxt->authlist; |
324 | } else { | 283 | } else { |
@@ -331,12 +290,6 @@ userauth(Authctxt *authctxt, char *authlist) | |||
331 | if (method == NULL) | 290 | if (method == NULL) |
332 | fatal("Permission denied (%s).", authlist); | 291 | fatal("Permission denied (%s).", authlist); |
333 | authctxt->method = method; | 292 | authctxt->method = method; |
334 | |||
335 | /* reset the per method handler */ | ||
336 | dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN, | ||
337 | SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL); | ||
338 | |||
339 | /* and try new method */ | ||
340 | if (method->userauth(authctxt) != 0) { | 293 | if (method->userauth(authctxt) != 0) { |
341 | debug2("we sent a %s packet, wait for reply", method->name); | 294 | debug2("we sent a %s packet, wait for reply", method->name); |
342 | break; | 295 | break; |
@@ -361,7 +314,7 @@ input_userauth_banner(int type, u_int32_t seq, void *ctxt) | |||
361 | debug3("input_userauth_banner"); | 314 | debug3("input_userauth_banner"); |
362 | msg = packet_get_string(NULL); | 315 | msg = packet_get_string(NULL); |
363 | lang = packet_get_string(NULL); | 316 | lang = packet_get_string(NULL); |
364 | logit("%s", msg); | 317 | fprintf(stderr, "%s", msg); |
365 | xfree(msg); | 318 | xfree(msg); |
366 | xfree(lang); | 319 | xfree(lang); |
367 | } | 320 | } |
@@ -374,8 +327,7 @@ input_userauth_success(int type, u_int32_t seq, void *ctxt) | |||
374 | fatal("input_userauth_success: no authentication context"); | 327 | fatal("input_userauth_success: no authentication context"); |
375 | if (authctxt->authlist) | 328 | if (authctxt->authlist) |
376 | xfree(authctxt->authlist); | 329 | xfree(authctxt->authlist); |
377 | if (authctxt->methoddata) | 330 | clear_auth_state(authctxt); |
378 | xfree(authctxt->methoddata); | ||
379 | authctxt->success = 1; /* break out */ | 331 | authctxt->success = 1; /* break out */ |
380 | } | 332 | } |
381 | 333 | ||
@@ -394,9 +346,10 @@ input_userauth_failure(int type, u_int32_t seq, void *ctxt) | |||
394 | packet_check_eom(); | 346 | packet_check_eom(); |
395 | 347 | ||
396 | if (partial != 0) | 348 | if (partial != 0) |
397 | logit("Authenticated with partial success."); | 349 | log("Authenticated with partial success."); |
398 | debug("Authentications that can continue: %s", authlist); | 350 | debug("Authentications that can continue: %s", authlist); |
399 | 351 | ||
352 | clear_auth_state(authctxt); | ||
400 | userauth(authctxt, authlist); | 353 | userauth(authctxt, authlist); |
401 | } | 354 | } |
402 | void | 355 | void |
@@ -404,7 +357,6 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) | |||
404 | { | 357 | { |
405 | Authctxt *authctxt = ctxt; | 358 | Authctxt *authctxt = ctxt; |
406 | Key *key = NULL; | 359 | Key *key = NULL; |
407 | Identity *id = NULL; | ||
408 | Buffer b; | 360 | Buffer b; |
409 | int pktype, sent = 0; | 361 | int pktype, sent = 0; |
410 | u_int alen, blen; | 362 | u_int alen, blen; |
@@ -427,267 +379,55 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) | |||
427 | } | 379 | } |
428 | packet_check_eom(); | 380 | packet_check_eom(); |
429 | 381 | ||
430 | debug("Server accepts key: pkalg %s blen %u", pkalg, blen); | 382 | debug("Server accepts key: pkalg %s blen %u lastkey %p hint %d", |
431 | 383 | pkalg, blen, authctxt->last_key, authctxt->last_key_hint); | |
432 | if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { | ||
433 | debug("unknown pkalg %s", pkalg); | ||
434 | goto done; | ||
435 | } | ||
436 | if ((key = key_from_blob(pkblob, blen)) == NULL) { | ||
437 | debug("no key from blob. pkalg %s", pkalg); | ||
438 | goto done; | ||
439 | } | ||
440 | if (key->type != pktype) { | ||
441 | error("input_userauth_pk_ok: type mismatch " | ||
442 | "for decoded key (received %d, expected %d)", | ||
443 | key->type, pktype); | ||
444 | goto done; | ||
445 | } | ||
446 | fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
447 | debug2("input_userauth_pk_ok: fp %s", fp); | ||
448 | xfree(fp); | ||
449 | 384 | ||
450 | TAILQ_FOREACH(id, &authctxt->keys, next) { | 385 | do { |
451 | if (key_equal(key, id->key)) { | 386 | if (authctxt->last_key == NULL || |
452 | sent = sign_and_send_pubkey(authctxt, id); | 387 | authctxt->last_key_sign == NULL) { |
388 | debug("no last key or no sign cb"); | ||
453 | break; | 389 | break; |
454 | } | 390 | } |
455 | } | 391 | if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { |
456 | done: | 392 | debug("unknown pkalg %s", pkalg); |
393 | break; | ||
394 | } | ||
395 | if ((key = key_from_blob(pkblob, blen)) == NULL) { | ||
396 | debug("no key from blob. pkalg %s", pkalg); | ||
397 | break; | ||
398 | } | ||
399 | if (key->type != pktype) { | ||
400 | error("input_userauth_pk_ok: type mismatch " | ||
401 | "for decoded key (received %d, expected %d)", | ||
402 | key->type, pktype); | ||
403 | break; | ||
404 | } | ||
405 | fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | ||
406 | debug2("input_userauth_pk_ok: fp %s", fp); | ||
407 | xfree(fp); | ||
408 | if (!key_equal(key, authctxt->last_key)) { | ||
409 | debug("key != last_key"); | ||
410 | break; | ||
411 | } | ||
412 | sent = sign_and_send_pubkey(authctxt, key, | ||
413 | authctxt->last_key_sign); | ||
414 | } while (0); | ||
415 | |||
457 | if (key != NULL) | 416 | if (key != NULL) |
458 | key_free(key); | 417 | key_free(key); |
459 | xfree(pkalg); | 418 | xfree(pkalg); |
460 | xfree(pkblob); | 419 | xfree(pkblob); |
461 | 420 | ||
421 | /* unregister */ | ||
422 | clear_auth_state(authctxt); | ||
423 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL); | ||
424 | |||
462 | /* try another method if we did not send a packet */ | 425 | /* try another method if we did not send a packet */ |
463 | if (sent == 0) | 426 | if (sent == 0) |
464 | userauth(authctxt, NULL); | 427 | userauth(authctxt, NULL); |
465 | } | ||
466 | |||
467 | #ifdef GSSAPI | ||
468 | int | ||
469 | userauth_gssapi(Authctxt *authctxt) | ||
470 | { | ||
471 | Gssctxt *gssctxt = NULL; | ||
472 | static gss_OID_set supported = NULL; | ||
473 | static int mech = 0; | ||
474 | OM_uint32 min; | ||
475 | int ok = 0; | ||
476 | |||
477 | /* Try one GSSAPI method at a time, rather than sending them all at | ||
478 | * once. */ | ||
479 | |||
480 | if (supported == NULL) | ||
481 | gss_indicate_mechs(&min, &supported); | ||
482 | |||
483 | /* Check to see if the mechanism is usable before we offer it */ | ||
484 | while (mech<supported->count && !ok) { | ||
485 | if (gssctxt) | ||
486 | ssh_gssapi_delete_ctx(&gssctxt); | ||
487 | ssh_gssapi_build_ctx(&gssctxt); | ||
488 | ssh_gssapi_set_oid(gssctxt, &supported->elements[mech]); | ||
489 | |||
490 | /* My DER encoding requires length<128 */ | ||
491 | if (supported->elements[mech].length < 128 && | ||
492 | !GSS_ERROR(ssh_gssapi_import_name(gssctxt, | ||
493 | authctxt->host))) { | ||
494 | ok = 1; /* Mechanism works */ | ||
495 | } else { | ||
496 | mech++; | ||
497 | } | ||
498 | } | ||
499 | |||
500 | if (!ok) return 0; | ||
501 | |||
502 | authctxt->methoddata=(void *)gssctxt; | ||
503 | |||
504 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
505 | packet_put_cstring(authctxt->server_user); | ||
506 | packet_put_cstring(authctxt->service); | ||
507 | packet_put_cstring(authctxt->method->name); | ||
508 | 428 | ||
509 | packet_put_int(1); | ||
510 | |||
511 | /* Some servers encode the OID incorrectly (as we used to) */ | ||
512 | if (datafellows & SSH_BUG_GSSAPI_BER) { | ||
513 | packet_put_string(supported->elements[mech].elements, | ||
514 | supported->elements[mech].length); | ||
515 | } else { | ||
516 | packet_put_int((supported->elements[mech].length)+2); | ||
517 | packet_put_char(SSH_GSS_OIDTYPE); | ||
518 | packet_put_char(supported->elements[mech].length); | ||
519 | packet_put_raw(supported->elements[mech].elements, | ||
520 | supported->elements[mech].length); | ||
521 | } | ||
522 | |||
523 | packet_send(); | ||
524 | |||
525 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response); | ||
526 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); | ||
527 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error); | ||
528 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); | ||
529 | |||
530 | mech++; /* Move along to next candidate */ | ||
531 | |||
532 | return 1; | ||
533 | } | ||
534 | |||
535 | void | ||
536 | input_gssapi_response(int type, u_int32_t plen, void *ctxt) | ||
537 | { | ||
538 | Authctxt *authctxt = ctxt; | ||
539 | Gssctxt *gssctxt; | ||
540 | OM_uint32 status, ms; | ||
541 | int oidlen; | ||
542 | char *oidv; | ||
543 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
544 | |||
545 | if (authctxt == NULL) | ||
546 | fatal("input_gssapi_response: no authentication context"); | ||
547 | gssctxt = authctxt->methoddata; | ||
548 | |||
549 | /* Setup our OID */ | ||
550 | oidv = packet_get_string(&oidlen); | ||
551 | |||
552 | if (datafellows & SSH_BUG_GSSAPI_BER) { | ||
553 | if (!ssh_gssapi_check_oid(gssctxt, oidv, oidlen)) | ||
554 | fatal("Server returned different OID than expected"); | ||
555 | } else { | ||
556 | if(oidv[0] != SSH_GSS_OIDTYPE || oidv[1] != oidlen-2) { | ||
557 | debug("Badly encoded mechanism OID received"); | ||
558 | userauth(authctxt, NULL); | ||
559 | xfree(oidv); | ||
560 | return; | ||
561 | } | ||
562 | if (!ssh_gssapi_check_oid(gssctxt, oidv+2, oidlen-2)) | ||
563 | fatal("Server returned different OID than expected"); | ||
564 | } | ||
565 | |||
566 | packet_check_eom(); | ||
567 | |||
568 | xfree(oidv); | ||
569 | |||
570 | status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, | ||
571 | GSS_C_NO_BUFFER, &send_tok, NULL); | ||
572 | if (GSS_ERROR(status)) { | ||
573 | if (send_tok.length > 0) { | ||
574 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); | ||
575 | packet_put_string(send_tok.value, send_tok.length); | ||
576 | packet_send(); | ||
577 | gss_release_buffer(&ms, &send_tok); | ||
578 | } | ||
579 | /* Start again with next method on list */ | ||
580 | debug("Trying to start again"); | ||
581 | userauth(authctxt, NULL); | ||
582 | return; | ||
583 | } | ||
584 | |||
585 | /* We must have data to send */ | ||
586 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); | ||
587 | packet_put_string(send_tok.value, send_tok.length); | ||
588 | packet_send(); | ||
589 | gss_release_buffer(&ms, &send_tok); | ||
590 | } | 429 | } |
591 | 430 | ||
592 | void | ||
593 | input_gssapi_token(int type, u_int32_t plen, void *ctxt) | ||
594 | { | ||
595 | Authctxt *authctxt = ctxt; | ||
596 | Gssctxt *gssctxt; | ||
597 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
598 | gss_buffer_desc recv_tok; | ||
599 | OM_uint32 status, ms; | ||
600 | u_int slen; | ||
601 | |||
602 | if (authctxt == NULL) | ||
603 | fatal("input_gssapi_response: no authentication context"); | ||
604 | gssctxt = authctxt->methoddata; | ||
605 | |||
606 | recv_tok.value = packet_get_string(&slen); | ||
607 | recv_tok.length = slen; /* safe typecast */ | ||
608 | |||
609 | packet_check_eom(); | ||
610 | |||
611 | status=ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, | ||
612 | &recv_tok, &send_tok, NULL); | ||
613 | |||
614 | xfree(recv_tok.value); | ||
615 | |||
616 | if (GSS_ERROR(status)) { | ||
617 | if (send_tok.length > 0) { | ||
618 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); | ||
619 | packet_put_string(send_tok.value, send_tok.length); | ||
620 | packet_send(); | ||
621 | gss_release_buffer(&ms, &send_tok); | ||
622 | } | ||
623 | /* Start again with the next method in the list */ | ||
624 | userauth(authctxt, NULL); | ||
625 | return; | ||
626 | } | ||
627 | |||
628 | if (send_tok.length > 0) { | ||
629 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); | ||
630 | packet_put_string(send_tok.value, send_tok.length); | ||
631 | packet_send(); | ||
632 | gss_release_buffer(&ms, &send_tok); | ||
633 | } | ||
634 | |||
635 | if (status == GSS_S_COMPLETE) { | ||
636 | /* If that succeeded, send a exchange complete message */ | ||
637 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); | ||
638 | packet_send(); | ||
639 | } | ||
640 | } | ||
641 | |||
642 | void | ||
643 | input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) | ||
644 | { | ||
645 | Authctxt *authctxt = ctxt; | ||
646 | Gssctxt *gssctxt; | ||
647 | gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; | ||
648 | gss_buffer_desc recv_tok; | ||
649 | OM_uint32 status, ms; | ||
650 | u_int len; | ||
651 | |||
652 | if (authctxt == NULL) | ||
653 | fatal("input_gssapi_response: no authentication context"); | ||
654 | gssctxt = authctxt->methoddata; | ||
655 | |||
656 | recv_tok.value = packet_get_string(&len); | ||
657 | recv_tok.length = len; | ||
658 | |||
659 | packet_check_eom(); | ||
660 | |||
661 | /* Stick it into GSSAPI and see what it says */ | ||
662 | status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, | ||
663 | &recv_tok, &send_tok, NULL); | ||
664 | |||
665 | xfree(recv_tok.value); | ||
666 | gss_release_buffer(&ms, &send_tok); | ||
667 | |||
668 | /* Server will be returning a failed packet after this one */ | ||
669 | } | ||
670 | |||
671 | void | ||
672 | input_gssapi_error(int type, u_int32_t plen, void *ctxt) | ||
673 | { | ||
674 | OM_uint32 maj, min; | ||
675 | char *msg; | ||
676 | char *lang; | ||
677 | |||
678 | maj=packet_get_int(); | ||
679 | min=packet_get_int(); | ||
680 | msg=packet_get_string(NULL); | ||
681 | lang=packet_get_string(NULL); | ||
682 | |||
683 | packet_check_eom(); | ||
684 | |||
685 | debug("Server GSSAPI Error:\n%s\n", msg); | ||
686 | xfree(msg); | ||
687 | xfree(lang); | ||
688 | } | ||
689 | #endif /* GSSAPI */ | ||
690 | |||
691 | int | 431 | int |
692 | userauth_none(Authctxt *authctxt) | 432 | userauth_none(Authctxt *authctxt) |
693 | { | 433 | { |
@@ -751,7 +491,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
751 | info = packet_get_string(NULL); | 491 | info = packet_get_string(NULL); |
752 | lang = packet_get_string(NULL); | 492 | lang = packet_get_string(NULL); |
753 | if (strlen(info) > 0) | 493 | if (strlen(info) > 0) |
754 | logit("%s", info); | 494 | log("%s", info); |
755 | xfree(info); | 495 | xfree(info); |
756 | xfree(lang); | 496 | xfree(lang); |
757 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | 497 | packet_start(SSH2_MSG_USERAUTH_REQUEST); |
@@ -783,7 +523,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
783 | if (strcmp(password, retype) != 0) { | 523 | if (strcmp(password, retype) != 0) { |
784 | memset(password, 0, strlen(password)); | 524 | memset(password, 0, strlen(password)); |
785 | xfree(password); | 525 | xfree(password); |
786 | logit("Mismatch; try again, EOF to quit."); | 526 | log("Mismatch; try again, EOF to quit."); |
787 | password = NULL; | 527 | password = NULL; |
788 | } | 528 | } |
789 | memset(retype, 0, strlen(retype)); | 529 | memset(retype, 0, strlen(retype)); |
@@ -799,44 +539,34 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
799 | &input_userauth_passwd_changereq); | 539 | &input_userauth_passwd_changereq); |
800 | } | 540 | } |
801 | 541 | ||
802 | static int | 542 | static void |
803 | identity_sign(Identity *id, u_char **sigp, u_int *lenp, | 543 | clear_auth_state(Authctxt *authctxt) |
804 | u_char *data, u_int datalen) | ||
805 | { | 544 | { |
806 | Key *prv; | 545 | /* XXX clear authentication state */ |
807 | int ret; | 546 | dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, NULL); |
808 | 547 | ||
809 | /* the agent supports this key */ | 548 | if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) { |
810 | if (id->ac) | 549 | debug3("clear_auth_state: key_free %p", authctxt->last_key); |
811 | return (ssh_agent_sign(id->ac, id->key, sigp, lenp, | 550 | key_free(authctxt->last_key); |
812 | data, datalen)); | 551 | } |
813 | /* | 552 | authctxt->last_key = NULL; |
814 | * we have already loaded the private key or | 553 | authctxt->last_key_hint = -2; |
815 | * the private key is stored in external hardware | 554 | authctxt->last_key_sign = NULL; |
816 | */ | ||
817 | if (id->isprivate || (id->key->flags & KEY_FLAG_EXT)) | ||
818 | return (key_sign(id->key, sigp, lenp, data, datalen)); | ||
819 | /* load the private key from the file */ | ||
820 | if ((prv = load_identity_file(id->filename)) == NULL) | ||
821 | return (-1); | ||
822 | ret = key_sign(prv, sigp, lenp, data, datalen); | ||
823 | key_free(prv); | ||
824 | return (ret); | ||
825 | } | 555 | } |
826 | 556 | ||
827 | static int | 557 | static int |
828 | sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | 558 | sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) |
829 | { | 559 | { |
830 | Buffer b; | 560 | Buffer b; |
831 | u_char *blob, *signature; | 561 | u_char *blob, *signature; |
832 | u_int bloblen, slen; | 562 | u_int bloblen, slen; |
833 | u_int skip = 0; | 563 | int skip = 0; |
834 | int ret = -1; | 564 | int ret = -1; |
835 | int have_sig = 1; | 565 | int have_sig = 1; |
836 | 566 | ||
837 | debug3("sign_and_send_pubkey"); | 567 | debug3("sign_and_send_pubkey"); |
838 | 568 | ||
839 | if (key_to_blob(id->key, &blob, &bloblen) == 0) { | 569 | if (key_to_blob(k, &blob, &bloblen) == 0) { |
840 | /* we cannot handle this key */ | 570 | /* we cannot handle this key */ |
841 | debug3("sign_and_send_pubkey: cannot handle key"); | 571 | debug3("sign_and_send_pubkey: cannot handle key"); |
842 | return 0; | 572 | return 0; |
@@ -861,12 +591,12 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
861 | } else { | 591 | } else { |
862 | buffer_put_cstring(&b, authctxt->method->name); | 592 | buffer_put_cstring(&b, authctxt->method->name); |
863 | buffer_put_char(&b, have_sig); | 593 | buffer_put_char(&b, have_sig); |
864 | buffer_put_cstring(&b, key_ssh_name(id->key)); | 594 | buffer_put_cstring(&b, key_ssh_name(k)); |
865 | } | 595 | } |
866 | buffer_put_string(&b, blob, bloblen); | 596 | buffer_put_string(&b, blob, bloblen); |
867 | 597 | ||
868 | /* generate signature */ | 598 | /* generate signature */ |
869 | ret = identity_sign(id, &signature, &slen, | 599 | ret = (*sign_callback)(authctxt, k, &signature, &slen, |
870 | buffer_ptr(&b), buffer_len(&b)); | 600 | buffer_ptr(&b), buffer_len(&b)); |
871 | if (ret == -1) { | 601 | if (ret == -1) { |
872 | xfree(blob); | 602 | xfree(blob); |
@@ -886,7 +616,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
886 | buffer_put_cstring(&b, authctxt->method->name); | 616 | buffer_put_cstring(&b, authctxt->method->name); |
887 | buffer_put_char(&b, have_sig); | 617 | buffer_put_char(&b, have_sig); |
888 | if (!(datafellows & SSH_BUG_PKAUTH)) | 618 | if (!(datafellows & SSH_BUG_PKAUTH)) |
889 | buffer_put_cstring(&b, key_ssh_name(id->key)); | 619 | buffer_put_cstring(&b, key_ssh_name(k)); |
890 | buffer_put_string(&b, blob, bloblen); | 620 | buffer_put_string(&b, blob, bloblen); |
891 | } | 621 | } |
892 | xfree(blob); | 622 | xfree(blob); |
@@ -910,19 +640,23 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id) | |||
910 | } | 640 | } |
911 | 641 | ||
912 | static int | 642 | static int |
913 | send_pubkey_test(Authctxt *authctxt, Identity *id) | 643 | send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, |
644 | int hint) | ||
914 | { | 645 | { |
915 | u_char *blob; | 646 | u_char *blob; |
916 | u_int bloblen, have_sig = 0; | 647 | u_int bloblen, have_sig = 0; |
917 | 648 | ||
918 | debug3("send_pubkey_test"); | 649 | debug3("send_pubkey_test"); |
919 | 650 | ||
920 | if (key_to_blob(id->key, &blob, &bloblen) == 0) { | 651 | if (key_to_blob(k, &blob, &bloblen) == 0) { |
921 | /* we cannot handle this key */ | 652 | /* we cannot handle this key */ |
922 | debug3("send_pubkey_test: cannot handle key"); | 653 | debug3("send_pubkey_test: cannot handle key"); |
923 | return 0; | 654 | return 0; |
924 | } | 655 | } |
925 | /* register callback for USERAUTH_PK_OK message */ | 656 | /* register callback for USERAUTH_PK_OK message */ |
657 | authctxt->last_key_sign = sign_callback; | ||
658 | authctxt->last_key_hint = hint; | ||
659 | authctxt->last_key = k; | ||
926 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); | 660 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); |
927 | 661 | ||
928 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | 662 | packet_start(SSH2_MSG_USERAUTH_REQUEST); |
@@ -931,7 +665,7 @@ send_pubkey_test(Authctxt *authctxt, Identity *id) | |||
931 | packet_put_cstring(authctxt->method->name); | 665 | packet_put_cstring(authctxt->method->name); |
932 | packet_put_char(have_sig); | 666 | packet_put_char(have_sig); |
933 | if (!(datafellows & SSH_BUG_PKAUTH)) | 667 | if (!(datafellows & SSH_BUG_PKAUTH)) |
934 | packet_put_cstring(key_ssh_name(id->key)); | 668 | packet_put_cstring(key_ssh_name(k)); |
935 | packet_put_string(blob, bloblen); | 669 | packet_put_string(blob, bloblen); |
936 | xfree(blob); | 670 | xfree(blob); |
937 | packet_send(); | 671 | packet_send(); |
@@ -976,134 +710,103 @@ load_identity_file(char *filename) | |||
976 | return private; | 710 | return private; |
977 | } | 711 | } |
978 | 712 | ||
979 | /* | 713 | static int |
980 | * try keys in the following order: | 714 | identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, |
981 | * 1. agent keys that are found in the config file | 715 | u_char *data, u_int datalen) |
982 | * 2. other agent keys | ||
983 | * 3. keys that are only listed in the config file | ||
984 | */ | ||
985 | static void | ||
986 | pubkey_prepare(Authctxt *authctxt) | ||
987 | { | 716 | { |
988 | Identity *id; | 717 | Key *private; |
989 | Idlist agent, files, *preferred; | 718 | int idx, ret; |
990 | Key *key; | 719 | |
991 | AuthenticationConnection *ac; | 720 | idx = authctxt->last_key_hint; |
992 | char *comment; | 721 | if (idx < 0) |
993 | int i, found; | 722 | return -1; |
994 | 723 | ||
995 | TAILQ_INIT(&agent); /* keys from the agent */ | 724 | /* private key is stored in external hardware */ |
996 | TAILQ_INIT(&files); /* keys from the config file */ | 725 | if (options.identity_keys[idx]->flags & KEY_FLAG_EXT) |
997 | preferred = &authctxt->keys; | 726 | return key_sign(options.identity_keys[idx], sigp, lenp, data, datalen); |
998 | TAILQ_INIT(preferred); /* preferred order of keys */ | 727 | |
999 | 728 | private = load_identity_file(options.identity_files[idx]); | |
1000 | /* list of keys stored in the filesystem */ | 729 | if (private == NULL) |
1001 | for (i = 0; i < options.num_identity_files; i++) { | 730 | return -1; |
1002 | key = options.identity_keys[i]; | 731 | ret = key_sign(private, sigp, lenp, data, datalen); |
1003 | if (key && key->type == KEY_RSA1) | 732 | key_free(private); |
1004 | continue; | 733 | return ret; |
1005 | options.identity_keys[i] = NULL; | ||
1006 | id = xmalloc(sizeof(*id)); | ||
1007 | memset(id, 0, sizeof(*id)); | ||
1008 | id->key = key; | ||
1009 | id->filename = xstrdup(options.identity_files[i]); | ||
1010 | TAILQ_INSERT_TAIL(&files, id, next); | ||
1011 | } | ||
1012 | /* list of keys supported by the agent */ | ||
1013 | if ((ac = ssh_get_authentication_connection())) { | ||
1014 | for (key = ssh_get_first_identity(ac, &comment, 2); | ||
1015 | key != NULL; | ||
1016 | key = ssh_get_next_identity(ac, &comment, 2)) { | ||
1017 | found = 0; | ||
1018 | TAILQ_FOREACH(id, &files, next) { | ||
1019 | /* agent keys from the config file are preferred */ | ||
1020 | if (key_equal(key, id->key)) { | ||
1021 | key_free(key); | ||
1022 | xfree(comment); | ||
1023 | TAILQ_REMOVE(&files, id, next); | ||
1024 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1025 | id->ac = ac; | ||
1026 | found = 1; | ||
1027 | break; | ||
1028 | } | ||
1029 | } | ||
1030 | if (!found) { | ||
1031 | id = xmalloc(sizeof(*id)); | ||
1032 | memset(id, 0, sizeof(*id)); | ||
1033 | id->key = key; | ||
1034 | id->filename = comment; | ||
1035 | id->ac = ac; | ||
1036 | TAILQ_INSERT_TAIL(&agent, id, next); | ||
1037 | } | ||
1038 | } | ||
1039 | /* append remaining agent keys */ | ||
1040 | for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { | ||
1041 | TAILQ_REMOVE(&agent, id, next); | ||
1042 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1043 | } | ||
1044 | authctxt->agent = ac; | ||
1045 | } | ||
1046 | /* append remaining keys from the config file */ | ||
1047 | for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { | ||
1048 | TAILQ_REMOVE(&files, id, next); | ||
1049 | TAILQ_INSERT_TAIL(preferred, id, next); | ||
1050 | } | ||
1051 | TAILQ_FOREACH(id, preferred, next) { | ||
1052 | debug2("key: %s (%p)", id->filename, id->key); | ||
1053 | } | ||
1054 | } | 734 | } |
1055 | 735 | ||
1056 | static void | 736 | static int |
1057 | pubkey_cleanup(Authctxt *authctxt) | 737 | agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, |
738 | u_char *data, u_int datalen) | ||
739 | { | ||
740 | return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen); | ||
741 | } | ||
742 | |||
743 | static int | ||
744 | key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, | ||
745 | u_char *data, u_int datalen) | ||
1058 | { | 746 | { |
1059 | Identity *id; | 747 | return key_sign(key, sigp, lenp, data, datalen); |
1060 | 748 | } | |
1061 | if (authctxt->agent != NULL) | 749 | |
1062 | ssh_close_authentication_connection(authctxt->agent); | 750 | static int |
1063 | for (id = TAILQ_FIRST(&authctxt->keys); id; | 751 | userauth_pubkey_agent(Authctxt *authctxt) |
1064 | id = TAILQ_FIRST(&authctxt->keys)) { | 752 | { |
1065 | TAILQ_REMOVE(&authctxt->keys, id, next); | 753 | static int called = 0; |
1066 | if (id->key) | 754 | int ret = 0; |
1067 | key_free(id->key); | 755 | char *comment; |
1068 | if (id->filename) | 756 | Key *k; |
1069 | xfree(id->filename); | 757 | |
1070 | xfree(id); | 758 | if (called == 0) { |
759 | if (ssh_get_num_identities(authctxt->agent, 2) == 0) | ||
760 | debug2("userauth_pubkey_agent: no keys at all"); | ||
761 | called = 1; | ||
762 | } | ||
763 | k = ssh_get_next_identity(authctxt->agent, &comment, 2); | ||
764 | if (k == NULL) { | ||
765 | debug2("userauth_pubkey_agent: no more keys"); | ||
766 | } else { | ||
767 | debug("Offering agent key: %s", comment); | ||
768 | xfree(comment); | ||
769 | ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1); | ||
770 | if (ret == 0) | ||
771 | key_free(k); | ||
1071 | } | 772 | } |
773 | if (ret == 0) | ||
774 | debug2("userauth_pubkey_agent: no message sent"); | ||
775 | return ret; | ||
1072 | } | 776 | } |
1073 | 777 | ||
1074 | int | 778 | int |
1075 | userauth_pubkey(Authctxt *authctxt) | 779 | userauth_pubkey(Authctxt *authctxt) |
1076 | { | 780 | { |
1077 | Identity *id; | 781 | static int idx = 0; |
1078 | int sent = 0; | 782 | int sent = 0; |
783 | Key *key; | ||
784 | char *filename; | ||
1079 | 785 | ||
1080 | while ((id = TAILQ_FIRST(&authctxt->keys))) { | 786 | if (authctxt->agent != NULL) { |
1081 | if (id->tried++) | 787 | do { |
1082 | return (0); | 788 | sent = userauth_pubkey_agent(authctxt); |
1083 | TAILQ_REMOVE(&authctxt->keys, id, next); | 789 | } while (!sent && authctxt->agent->howmany > 0); |
1084 | TAILQ_INSERT_TAIL(&authctxt->keys, id, next); | 790 | } |
1085 | /* | 791 | while (!sent && idx < options.num_identity_files) { |
1086 | * send a test message if we have the public key. for | 792 | key = options.identity_keys[idx]; |
1087 | * encrypted keys we cannot do this and have to load the | 793 | filename = options.identity_files[idx]; |
1088 | * private key instead | 794 | if (key == NULL) { |
1089 | */ | 795 | debug("Trying private key: %s", filename); |
1090 | if (id->key && id->key->type != KEY_RSA1) { | 796 | key = load_identity_file(filename); |
1091 | debug("Offering public key: %s", id->filename); | 797 | if (key != NULL) { |
1092 | sent = send_pubkey_test(authctxt, id); | 798 | sent = sign_and_send_pubkey(authctxt, key, |
1093 | } else if (id->key == NULL) { | 799 | key_sign_cb); |
1094 | debug("Trying private key: %s", id->filename); | 800 | key_free(key); |
1095 | id->key = load_identity_file(id->filename); | ||
1096 | if (id->key != NULL) { | ||
1097 | id->isprivate = 1; | ||
1098 | sent = sign_and_send_pubkey(authctxt, id); | ||
1099 | key_free(id->key); | ||
1100 | id->key = NULL; | ||
1101 | } | 801 | } |
802 | } else if (key->type != KEY_RSA1) { | ||
803 | debug("Offering public key: %s", filename); | ||
804 | sent = send_pubkey_test(authctxt, key, | ||
805 | identity_sign_cb, idx); | ||
1102 | } | 806 | } |
1103 | if (sent) | 807 | idx++; |
1104 | return (sent); | ||
1105 | } | 808 | } |
1106 | return (0); | 809 | return sent; |
1107 | } | 810 | } |
1108 | 811 | ||
1109 | /* | 812 | /* |
@@ -1159,9 +862,9 @@ input_userauth_info_req(int type, u_int32_t seq, void *ctxt) | |||
1159 | inst = packet_get_string(NULL); | 862 | inst = packet_get_string(NULL); |
1160 | lang = packet_get_string(NULL); | 863 | lang = packet_get_string(NULL); |
1161 | if (strlen(name) > 0) | 864 | if (strlen(name) > 0) |
1162 | logit("%s", name); | 865 | log("%s", name); |
1163 | if (strlen(inst) > 0) | 866 | if (strlen(inst) > 0) |
1164 | logit("%s", inst); | 867 | log("%s", inst); |
1165 | xfree(name); | 868 | xfree(name); |
1166 | xfree(inst); | 869 | xfree(inst); |
1167 | xfree(lang); | 870 | xfree(lang); |