diff options
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 625 |
1 files changed, 461 insertions, 164 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index 642b34b9e..933c223ec 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -23,7 +23,9 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include "includes.h" | 25 | #include "includes.h" |
26 | RCSID("$OpenBSD: sshconnect2.c,v 1.114 2003/04/01 10:22:21 markus Exp $"); | 26 | RCSID("$OpenBSD: sshconnect2.c,v 1.124 2003/08/25 10:33:33 djm Exp $"); |
27 | |||
28 | #include "openbsd-compat/sys-queue.h" | ||
27 | 29 | ||
28 | #include "ssh.h" | 30 | #include "ssh.h" |
29 | #include "ssh2.h" | 31 | #include "ssh2.h" |
@@ -48,6 +50,10 @@ RCSID("$OpenBSD: sshconnect2.c,v 1.114 2003/04/01 10:22:21 markus Exp $"); | |||
48 | #include "msg.h" | 50 | #include "msg.h" |
49 | #include "pathnames.h" | 51 | #include "pathnames.h" |
50 | 52 | ||
53 | #ifdef GSSAPI | ||
54 | #include "ssh-gss.h" | ||
55 | #endif | ||
56 | |||
51 | /* import */ | 57 | /* import */ |
52 | extern char *client_version_string; | 58 | extern char *client_version_string; |
53 | extern char *server_version_string; | 59 | extern char *server_version_string; |
@@ -58,7 +64,7 @@ extern Options options; | |||
58 | */ | 64 | */ |
59 | 65 | ||
60 | u_char *session_id2 = NULL; | 66 | u_char *session_id2 = NULL; |
61 | int session_id2_len = 0; | 67 | u_int session_id2_len = 0; |
62 | 68 | ||
63 | char *xxx_host; | 69 | char *xxx_host; |
64 | struct sockaddr *xxx_hostaddr; | 70 | struct sockaddr *xxx_hostaddr; |
@@ -82,7 +88,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
82 | xxx_hostaddr = hostaddr; | 88 | xxx_hostaddr = hostaddr; |
83 | 89 | ||
84 | if (options.ciphers == (char *)-1) { | 90 | if (options.ciphers == (char *)-1) { |
85 | log("No valid ciphers for protocol version 2 given, using defaults."); | 91 | logit("No valid ciphers for protocol version 2 given, using defaults."); |
86 | options.ciphers = NULL; | 92 | options.ciphers = NULL; |
87 | } | 93 | } |
88 | if (options.ciphers != NULL) { | 94 | if (options.ciphers != NULL) { |
@@ -108,6 +114,9 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
108 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = | 114 | myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = |
109 | options.hostkeyalgorithms; | 115 | options.hostkeyalgorithms; |
110 | 116 | ||
117 | if (options.rekey_limit) | ||
118 | packet_set_rekey_limit(options.rekey_limit); | ||
119 | |||
111 | /* start key exchange */ | 120 | /* start key exchange */ |
112 | kex = kex_setup(myproposal); | 121 | kex = kex_setup(myproposal); |
113 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; | 122 | kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; |
@@ -138,10 +147,18 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) | |||
138 | 147 | ||
139 | typedef struct Authctxt Authctxt; | 148 | typedef struct Authctxt Authctxt; |
140 | typedef struct Authmethod Authmethod; | 149 | typedef struct Authmethod Authmethod; |
141 | 150 | typedef struct identity Identity; | |
142 | typedef int sign_cb_fn( | 151 | typedef struct idlist Idlist; |
143 | Authctxt *authctxt, Key *key, | 152 | |
144 | u_char **sigp, u_int *lenp, u_char *data, u_int datalen); | 153 | struct identity { |
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); | ||
145 | 162 | ||
146 | struct Authctxt { | 163 | struct Authctxt { |
147 | const char *server_user; | 164 | const char *server_user; |
@@ -152,14 +169,14 @@ struct Authctxt { | |||
152 | int success; | 169 | int success; |
153 | char *authlist; | 170 | char *authlist; |
154 | /* pubkey */ | 171 | /* pubkey */ |
155 | Key *last_key; | 172 | Idlist keys; |
156 | sign_cb_fn *last_key_sign; | ||
157 | int last_key_hint; | ||
158 | AuthenticationConnection *agent; | 173 | AuthenticationConnection *agent; |
159 | /* hostbased */ | 174 | /* hostbased */ |
160 | Sensitive *sensitive; | 175 | Sensitive *sensitive; |
161 | /* kbd-interactive */ | 176 | /* kbd-interactive */ |
162 | int info_req_seen; | 177 | int info_req_seen; |
178 | /* generic */ | ||
179 | void *methoddata; | ||
163 | }; | 180 | }; |
164 | struct Authmethod { | 181 | struct Authmethod { |
165 | char *name; /* string to compare against server's list */ | 182 | char *name; /* string to compare against server's list */ |
@@ -181,17 +198,35 @@ int userauth_pubkey(Authctxt *); | |||
181 | int userauth_passwd(Authctxt *); | 198 | int userauth_passwd(Authctxt *); |
182 | int userauth_kbdint(Authctxt *); | 199 | int userauth_kbdint(Authctxt *); |
183 | int userauth_hostbased(Authctxt *); | 200 | 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 | ||
184 | 211 | ||
185 | void userauth(Authctxt *, char *); | 212 | void userauth(Authctxt *, char *); |
186 | 213 | ||
187 | static int sign_and_send_pubkey(Authctxt *, Key *, sign_cb_fn *); | 214 | static int sign_and_send_pubkey(Authctxt *, Identity *); |
188 | static void clear_auth_state(Authctxt *); | 215 | static void pubkey_prepare(Authctxt *); |
216 | static void pubkey_cleanup(Authctxt *); | ||
217 | static Key *load_identity_file(char *); | ||
189 | 218 | ||
190 | static Authmethod *authmethod_get(char *authlist); | 219 | static Authmethod *authmethod_get(char *authlist); |
191 | static Authmethod *authmethod_lookup(const char *name); | 220 | static Authmethod *authmethod_lookup(const char *name); |
192 | static char *authmethods_get(void); | 221 | static char *authmethods_get(void); |
193 | 222 | ||
194 | Authmethod authmethods[] = { | 223 | Authmethod authmethods[] = { |
224 | #ifdef GSSAPI | ||
225 | {"gssapi", | ||
226 | userauth_gssapi, | ||
227 | &options.gss_authentication, | ||
228 | NULL}, | ||
229 | #endif | ||
195 | {"hostbased", | 230 | {"hostbased", |
196 | userauth_hostbased, | 231 | userauth_hostbased, |
197 | &options.hostbased_authentication, | 232 | &options.hostbased_authentication, |
@@ -248,7 +283,7 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
248 | 283 | ||
249 | /* setup authentication context */ | 284 | /* setup authentication context */ |
250 | memset(&authctxt, 0, sizeof(authctxt)); | 285 | memset(&authctxt, 0, sizeof(authctxt)); |
251 | authctxt.agent = ssh_get_authentication_connection(); | 286 | pubkey_prepare(&authctxt); |
252 | authctxt.server_user = server_user; | 287 | authctxt.server_user = server_user; |
253 | authctxt.local_user = local_user; | 288 | authctxt.local_user = local_user; |
254 | authctxt.host = host; | 289 | authctxt.host = host; |
@@ -256,6 +291,7 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
256 | authctxt.success = 0; | 291 | authctxt.success = 0; |
257 | authctxt.method = authmethod_lookup("none"); | 292 | authctxt.method = authmethod_lookup("none"); |
258 | authctxt.authlist = NULL; | 293 | authctxt.authlist = NULL; |
294 | authctxt.methoddata = NULL; | ||
259 | authctxt.sensitive = sensitive; | 295 | authctxt.sensitive = sensitive; |
260 | authctxt.info_req_seen = 0; | 296 | authctxt.info_req_seen = 0; |
261 | if (authctxt.method == NULL) | 297 | if (authctxt.method == NULL) |
@@ -270,14 +306,19 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
270 | dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); | 306 | dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); |
271 | dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ | 307 | dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ |
272 | 308 | ||
273 | if (authctxt.agent != NULL) | 309 | pubkey_cleanup(&authctxt); |
274 | ssh_close_authentication_connection(authctxt.agent); | 310 | dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); |
275 | 311 | ||
276 | debug("Authentication succeeded (%s).", authctxt.method->name); | 312 | debug("Authentication succeeded (%s).", authctxt.method->name); |
277 | } | 313 | } |
314 | |||
278 | void | 315 | void |
279 | userauth(Authctxt *authctxt, char *authlist) | 316 | userauth(Authctxt *authctxt, char *authlist) |
280 | { | 317 | { |
318 | if (authctxt->methoddata) { | ||
319 | xfree(authctxt->methoddata); | ||
320 | authctxt->methoddata = NULL; | ||
321 | } | ||
281 | if (authlist == NULL) { | 322 | if (authlist == NULL) { |
282 | authlist = authctxt->authlist; | 323 | authlist = authctxt->authlist; |
283 | } else { | 324 | } else { |
@@ -290,6 +331,12 @@ userauth(Authctxt *authctxt, char *authlist) | |||
290 | if (method == NULL) | 331 | if (method == NULL) |
291 | fatal("Permission denied (%s).", authlist); | 332 | fatal("Permission denied (%s).", authlist); |
292 | authctxt->method = method; | 333 | 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 */ | ||
293 | if (method->userauth(authctxt) != 0) { | 340 | if (method->userauth(authctxt) != 0) { |
294 | debug2("we sent a %s packet, wait for reply", method->name); | 341 | debug2("we sent a %s packet, wait for reply", method->name); |
295 | break; | 342 | break; |
@@ -314,7 +361,7 @@ input_userauth_banner(int type, u_int32_t seq, void *ctxt) | |||
314 | debug3("input_userauth_banner"); | 361 | debug3("input_userauth_banner"); |
315 | msg = packet_get_string(NULL); | 362 | msg = packet_get_string(NULL); |
316 | lang = packet_get_string(NULL); | 363 | lang = packet_get_string(NULL); |
317 | fprintf(stderr, "%s", msg); | 364 | logit("%s", msg); |
318 | xfree(msg); | 365 | xfree(msg); |
319 | xfree(lang); | 366 | xfree(lang); |
320 | } | 367 | } |
@@ -327,7 +374,8 @@ input_userauth_success(int type, u_int32_t seq, void *ctxt) | |||
327 | fatal("input_userauth_success: no authentication context"); | 374 | fatal("input_userauth_success: no authentication context"); |
328 | if (authctxt->authlist) | 375 | if (authctxt->authlist) |
329 | xfree(authctxt->authlist); | 376 | xfree(authctxt->authlist); |
330 | clear_auth_state(authctxt); | 377 | if (authctxt->methoddata) |
378 | xfree(authctxt->methoddata); | ||
331 | authctxt->success = 1; /* break out */ | 379 | authctxt->success = 1; /* break out */ |
332 | } | 380 | } |
333 | 381 | ||
@@ -346,10 +394,9 @@ input_userauth_failure(int type, u_int32_t seq, void *ctxt) | |||
346 | packet_check_eom(); | 394 | packet_check_eom(); |
347 | 395 | ||
348 | if (partial != 0) | 396 | if (partial != 0) |
349 | log("Authenticated with partial success."); | 397 | logit("Authenticated with partial success."); |
350 | debug("Authentications that can continue: %s", authlist); | 398 | debug("Authentications that can continue: %s", authlist); |
351 | 399 | ||
352 | clear_auth_state(authctxt); | ||
353 | userauth(authctxt, authlist); | 400 | userauth(authctxt, authlist); |
354 | } | 401 | } |
355 | void | 402 | void |
@@ -357,6 +404,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) | |||
357 | { | 404 | { |
358 | Authctxt *authctxt = ctxt; | 405 | Authctxt *authctxt = ctxt; |
359 | Key *key = NULL; | 406 | Key *key = NULL; |
407 | Identity *id = NULL; | ||
360 | Buffer b; | 408 | Buffer b; |
361 | int pktype, sent = 0; | 409 | int pktype, sent = 0; |
362 | u_int alen, blen; | 410 | u_int alen, blen; |
@@ -379,55 +427,267 @@ input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) | |||
379 | } | 427 | } |
380 | packet_check_eom(); | 428 | packet_check_eom(); |
381 | 429 | ||
382 | debug("Server accepts key: pkalg %s blen %u lastkey %p hint %d", | 430 | debug("Server accepts key: pkalg %s blen %u", pkalg, blen); |
383 | pkalg, blen, authctxt->last_key, authctxt->last_key_hint); | ||
384 | 431 | ||
385 | do { | 432 | if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { |
386 | if (authctxt->last_key == NULL || | 433 | debug("unknown pkalg %s", pkalg); |
387 | authctxt->last_key_sign == NULL) { | 434 | goto done; |
388 | debug("no last key or no sign cb"); | 435 | } |
389 | break; | 436 | if ((key = key_from_blob(pkblob, blen)) == NULL) { |
390 | } | 437 | debug("no key from blob. pkalg %s", pkalg); |
391 | if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { | 438 | goto done; |
392 | debug("unknown pkalg %s", pkalg); | 439 | } |
393 | break; | 440 | if (key->type != pktype) { |
394 | } | 441 | error("input_userauth_pk_ok: type mismatch " |
395 | if ((key = key_from_blob(pkblob, blen)) == NULL) { | 442 | "for decoded key (received %d, expected %d)", |
396 | debug("no key from blob. pkalg %s", pkalg); | 443 | key->type, pktype); |
397 | break; | 444 | goto done; |
398 | } | 445 | } |
399 | if (key->type != pktype) { | 446 | fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); |
400 | error("input_userauth_pk_ok: type mismatch " | 447 | debug2("input_userauth_pk_ok: fp %s", fp); |
401 | "for decoded key (received %d, expected %d)", | 448 | xfree(fp); |
402 | key->type, pktype); | 449 | |
403 | break; | 450 | TAILQ_FOREACH(id, &authctxt->keys, next) { |
404 | } | 451 | if (key_equal(key, id->key)) { |
405 | fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); | 452 | sent = sign_and_send_pubkey(authctxt, id); |
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; | 453 | break; |
411 | } | 454 | } |
412 | sent = sign_and_send_pubkey(authctxt, key, | 455 | } |
413 | authctxt->last_key_sign); | 456 | done: |
414 | } while (0); | ||
415 | |||
416 | if (key != NULL) | 457 | if (key != NULL) |
417 | key_free(key); | 458 | key_free(key); |
418 | xfree(pkalg); | 459 | xfree(pkalg); |
419 | xfree(pkblob); | 460 | xfree(pkblob); |
420 | 461 | ||
421 | /* unregister */ | ||
422 | clear_auth_state(authctxt); | ||
423 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL); | ||
424 | |||
425 | /* try another method if we did not send a packet */ | 462 | /* try another method if we did not send a packet */ |
426 | if (sent == 0) | 463 | if (sent == 0) |
427 | userauth(authctxt, NULL); | 464 | 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); | ||
428 | 508 | ||
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); | ||
429 | } | 590 | } |
430 | 591 | ||
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 | |||
431 | int | 691 | int |
432 | userauth_none(Authctxt *authctxt) | 692 | userauth_none(Authctxt *authctxt) |
433 | { | 693 | { |
@@ -491,7 +751,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
491 | info = packet_get_string(NULL); | 751 | info = packet_get_string(NULL); |
492 | lang = packet_get_string(NULL); | 752 | lang = packet_get_string(NULL); |
493 | if (strlen(info) > 0) | 753 | if (strlen(info) > 0) |
494 | log("%s", info); | 754 | logit("%s", info); |
495 | xfree(info); | 755 | xfree(info); |
496 | xfree(lang); | 756 | xfree(lang); |
497 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | 757 | packet_start(SSH2_MSG_USERAUTH_REQUEST); |
@@ -523,7 +783,7 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
523 | if (strcmp(password, retype) != 0) { | 783 | if (strcmp(password, retype) != 0) { |
524 | memset(password, 0, strlen(password)); | 784 | memset(password, 0, strlen(password)); |
525 | xfree(password); | 785 | xfree(password); |
526 | log("Mismatch; try again, EOF to quit."); | 786 | logit("Mismatch; try again, EOF to quit."); |
527 | password = NULL; | 787 | password = NULL; |
528 | } | 788 | } |
529 | memset(retype, 0, strlen(retype)); | 789 | memset(retype, 0, strlen(retype)); |
@@ -539,34 +799,44 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
539 | &input_userauth_passwd_changereq); | 799 | &input_userauth_passwd_changereq); |
540 | } | 800 | } |
541 | 801 | ||
542 | static void | 802 | static int |
543 | clear_auth_state(Authctxt *authctxt) | 803 | identity_sign(Identity *id, u_char **sigp, u_int *lenp, |
804 | u_char *data, u_int datalen) | ||
544 | { | 805 | { |
545 | /* XXX clear authentication state */ | 806 | Key *prv; |
546 | dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, NULL); | 807 | int ret; |
547 | 808 | ||
548 | if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) { | 809 | /* the agent supports this key */ |
549 | debug3("clear_auth_state: key_free %p", authctxt->last_key); | 810 | if (id->ac) |
550 | key_free(authctxt->last_key); | 811 | return (ssh_agent_sign(id->ac, id->key, sigp, lenp, |
551 | } | 812 | data, datalen)); |
552 | authctxt->last_key = NULL; | 813 | /* |
553 | authctxt->last_key_hint = -2; | 814 | * we have already loaded the private key or |
554 | authctxt->last_key_sign = NULL; | 815 | * the private key is stored in external hardware |
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); | ||
555 | } | 825 | } |
556 | 826 | ||
557 | static int | 827 | static int |
558 | sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | 828 | sign_and_send_pubkey(Authctxt *authctxt, Identity *id) |
559 | { | 829 | { |
560 | Buffer b; | 830 | Buffer b; |
561 | u_char *blob, *signature; | 831 | u_char *blob, *signature; |
562 | u_int bloblen, slen; | 832 | u_int bloblen, slen; |
563 | int skip = 0; | 833 | u_int skip = 0; |
564 | int ret = -1; | 834 | int ret = -1; |
565 | int have_sig = 1; | 835 | int have_sig = 1; |
566 | 836 | ||
567 | debug3("sign_and_send_pubkey"); | 837 | debug3("sign_and_send_pubkey"); |
568 | 838 | ||
569 | if (key_to_blob(k, &blob, &bloblen) == 0) { | 839 | if (key_to_blob(id->key, &blob, &bloblen) == 0) { |
570 | /* we cannot handle this key */ | 840 | /* we cannot handle this key */ |
571 | debug3("sign_and_send_pubkey: cannot handle key"); | 841 | debug3("sign_and_send_pubkey: cannot handle key"); |
572 | return 0; | 842 | return 0; |
@@ -591,12 +861,12 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | |||
591 | } else { | 861 | } else { |
592 | buffer_put_cstring(&b, authctxt->method->name); | 862 | buffer_put_cstring(&b, authctxt->method->name); |
593 | buffer_put_char(&b, have_sig); | 863 | buffer_put_char(&b, have_sig); |
594 | buffer_put_cstring(&b, key_ssh_name(k)); | 864 | buffer_put_cstring(&b, key_ssh_name(id->key)); |
595 | } | 865 | } |
596 | buffer_put_string(&b, blob, bloblen); | 866 | buffer_put_string(&b, blob, bloblen); |
597 | 867 | ||
598 | /* generate signature */ | 868 | /* generate signature */ |
599 | ret = (*sign_callback)(authctxt, k, &signature, &slen, | 869 | ret = identity_sign(id, &signature, &slen, |
600 | buffer_ptr(&b), buffer_len(&b)); | 870 | buffer_ptr(&b), buffer_len(&b)); |
601 | if (ret == -1) { | 871 | if (ret == -1) { |
602 | xfree(blob); | 872 | xfree(blob); |
@@ -616,7 +886,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | |||
616 | buffer_put_cstring(&b, authctxt->method->name); | 886 | buffer_put_cstring(&b, authctxt->method->name); |
617 | buffer_put_char(&b, have_sig); | 887 | buffer_put_char(&b, have_sig); |
618 | if (!(datafellows & SSH_BUG_PKAUTH)) | 888 | if (!(datafellows & SSH_BUG_PKAUTH)) |
619 | buffer_put_cstring(&b, key_ssh_name(k)); | 889 | buffer_put_cstring(&b, key_ssh_name(id->key)); |
620 | buffer_put_string(&b, blob, bloblen); | 890 | buffer_put_string(&b, blob, bloblen); |
621 | } | 891 | } |
622 | xfree(blob); | 892 | xfree(blob); |
@@ -640,23 +910,19 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | |||
640 | } | 910 | } |
641 | 911 | ||
642 | static int | 912 | static int |
643 | send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, | 913 | send_pubkey_test(Authctxt *authctxt, Identity *id) |
644 | int hint) | ||
645 | { | 914 | { |
646 | u_char *blob; | 915 | u_char *blob; |
647 | u_int bloblen, have_sig = 0; | 916 | u_int bloblen, have_sig = 0; |
648 | 917 | ||
649 | debug3("send_pubkey_test"); | 918 | debug3("send_pubkey_test"); |
650 | 919 | ||
651 | if (key_to_blob(k, &blob, &bloblen) == 0) { | 920 | if (key_to_blob(id->key, &blob, &bloblen) == 0) { |
652 | /* we cannot handle this key */ | 921 | /* we cannot handle this key */ |
653 | debug3("send_pubkey_test: cannot handle key"); | 922 | debug3("send_pubkey_test: cannot handle key"); |
654 | return 0; | 923 | return 0; |
655 | } | 924 | } |
656 | /* register callback for USERAUTH_PK_OK message */ | 925 | /* 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; | ||
660 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); | 926 | dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); |
661 | 927 | ||
662 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | 928 | packet_start(SSH2_MSG_USERAUTH_REQUEST); |
@@ -665,7 +931,7 @@ send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, | |||
665 | packet_put_cstring(authctxt->method->name); | 931 | packet_put_cstring(authctxt->method->name); |
666 | packet_put_char(have_sig); | 932 | packet_put_char(have_sig); |
667 | if (!(datafellows & SSH_BUG_PKAUTH)) | 933 | if (!(datafellows & SSH_BUG_PKAUTH)) |
668 | packet_put_cstring(key_ssh_name(k)); | 934 | packet_put_cstring(key_ssh_name(id->key)); |
669 | packet_put_string(blob, bloblen); | 935 | packet_put_string(blob, bloblen); |
670 | xfree(blob); | 936 | xfree(blob); |
671 | packet_send(); | 937 | packet_send(); |
@@ -710,103 +976,134 @@ load_identity_file(char *filename) | |||
710 | return private; | 976 | return private; |
711 | } | 977 | } |
712 | 978 | ||
713 | static int | 979 | /* |
714 | identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, u_int *lenp, | 980 | * try keys in the following order: |
715 | u_char *data, u_int datalen) | 981 | * 1. agent keys that are found in the config file |
716 | { | 982 | * 2. other agent keys |
717 | Key *private; | 983 | * 3. keys that are only listed in the config file |
718 | int idx, ret; | 984 | */ |
719 | 985 | static void | |
720 | idx = authctxt->last_key_hint; | 986 | pubkey_prepare(Authctxt *authctxt) |
721 | if (idx < 0) | ||
722 | return -1; | ||
723 | |||
724 | /* private key is stored in external hardware */ | ||
725 | if (options.identity_keys[idx]->flags & KEY_FLAG_EXT) | ||
726 | return key_sign(options.identity_keys[idx], sigp, lenp, data, datalen); | ||
727 | |||
728 | private = load_identity_file(options.identity_files[idx]); | ||
729 | if (private == NULL) | ||
730 | return -1; | ||
731 | ret = key_sign(private, sigp, lenp, data, datalen); | ||
732 | key_free(private); | ||
733 | return ret; | ||
734 | } | ||
735 | |||
736 | static int | ||
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) | ||
746 | { | 987 | { |
747 | return key_sign(key, sigp, lenp, data, datalen); | 988 | Identity *id; |
989 | Idlist agent, files, *preferred; | ||
990 | Key *key; | ||
991 | AuthenticationConnection *ac; | ||
992 | char *comment; | ||
993 | int i, found; | ||
994 | |||
995 | TAILQ_INIT(&agent); /* keys from the agent */ | ||
996 | TAILQ_INIT(&files); /* keys from the config file */ | ||
997 | preferred = &authctxt->keys; | ||
998 | TAILQ_INIT(preferred); /* preferred order of keys */ | ||
999 | |||
1000 | /* list of keys stored in the filesystem */ | ||
1001 | for (i = 0; i < options.num_identity_files; i++) { | ||
1002 | key = options.identity_keys[i]; | ||
1003 | if (key && key->type == KEY_RSA1) | ||
1004 | continue; | ||
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 | } | ||
748 | } | 1054 | } |
749 | 1055 | ||
750 | static int | 1056 | static void |
751 | userauth_pubkey_agent(Authctxt *authctxt) | 1057 | pubkey_cleanup(Authctxt *authctxt) |
752 | { | 1058 | { |
753 | static int called = 0; | 1059 | Identity *id; |
754 | int ret = 0; | 1060 | |
755 | char *comment; | 1061 | if (authctxt->agent != NULL) |
756 | Key *k; | 1062 | ssh_close_authentication_connection(authctxt->agent); |
757 | 1063 | for (id = TAILQ_FIRST(&authctxt->keys); id; | |
758 | if (called == 0) { | 1064 | id = TAILQ_FIRST(&authctxt->keys)) { |
759 | if (ssh_get_num_identities(authctxt->agent, 2) == 0) | 1065 | TAILQ_REMOVE(&authctxt->keys, id, next); |
760 | debug2("userauth_pubkey_agent: no keys at all"); | 1066 | if (id->key) |
761 | called = 1; | 1067 | key_free(id->key); |
762 | } | 1068 | if (id->filename) |
763 | k = ssh_get_next_identity(authctxt->agent, &comment, 2); | 1069 | xfree(id->filename); |
764 | if (k == NULL) { | 1070 | xfree(id); |
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); | ||
772 | } | 1071 | } |
773 | if (ret == 0) | ||
774 | debug2("userauth_pubkey_agent: no message sent"); | ||
775 | return ret; | ||
776 | } | 1072 | } |
777 | 1073 | ||
778 | int | 1074 | int |
779 | userauth_pubkey(Authctxt *authctxt) | 1075 | userauth_pubkey(Authctxt *authctxt) |
780 | { | 1076 | { |
781 | static int idx = 0; | 1077 | Identity *id; |
782 | int sent = 0; | 1078 | int sent = 0; |
783 | Key *key; | ||
784 | char *filename; | ||
785 | 1079 | ||
786 | if (authctxt->agent != NULL) { | 1080 | while ((id = TAILQ_FIRST(&authctxt->keys))) { |
787 | do { | 1081 | if (id->tried++) |
788 | sent = userauth_pubkey_agent(authctxt); | 1082 | return (0); |
789 | } while (!sent && authctxt->agent->howmany > 0); | 1083 | TAILQ_REMOVE(&authctxt->keys, id, next); |
790 | } | 1084 | TAILQ_INSERT_TAIL(&authctxt->keys, id, next); |
791 | while (!sent && idx < options.num_identity_files) { | 1085 | /* |
792 | key = options.identity_keys[idx]; | 1086 | * send a test message if we have the public key. for |
793 | filename = options.identity_files[idx]; | 1087 | * encrypted keys we cannot do this and have to load the |
794 | if (key == NULL) { | 1088 | * private key instead |
795 | debug("Trying private key: %s", filename); | 1089 | */ |
796 | key = load_identity_file(filename); | 1090 | if (id->key && id->key->type != KEY_RSA1) { |
797 | if (key != NULL) { | 1091 | debug("Offering public key: %s", id->filename); |
798 | sent = sign_and_send_pubkey(authctxt, key, | 1092 | sent = send_pubkey_test(authctxt, id); |
799 | key_sign_cb); | 1093 | } else if (id->key == NULL) { |
800 | key_free(key); | 1094 | debug("Trying private key: %s", id->filename); |
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; | ||
801 | } | 1101 | } |
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); | ||
806 | } | 1102 | } |
807 | idx++; | 1103 | if (sent) |
1104 | return (sent); | ||
808 | } | 1105 | } |
809 | return sent; | 1106 | return (0); |
810 | } | 1107 | } |
811 | 1108 | ||
812 | /* | 1109 | /* |
@@ -862,9 +1159,9 @@ input_userauth_info_req(int type, u_int32_t seq, void *ctxt) | |||
862 | inst = packet_get_string(NULL); | 1159 | inst = packet_get_string(NULL); |
863 | lang = packet_get_string(NULL); | 1160 | lang = packet_get_string(NULL); |
864 | if (strlen(name) > 0) | 1161 | if (strlen(name) > 0) |
865 | log("%s", name); | 1162 | logit("%s", name); |
866 | if (strlen(inst) > 0) | 1163 | if (strlen(inst) > 0) |
867 | log("%s", inst); | 1164 | logit("%s", inst); |
868 | xfree(name); | 1165 | xfree(name); |
869 | xfree(inst); | 1166 | xfree(inst); |
870 | xfree(lang); | 1167 | xfree(lang); |