summaryrefslogtreecommitdiff
path: root/auth2.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2013-05-07 11:47:26 +0100
committerColin Watson <cjwatson@debian.org>2013-05-07 11:47:26 +0100
commit2ea3f720daeb1ca9f765365fce3a9546961fe624 (patch)
treec4fb7d1f51fa51e7677232de806aae150e29e2ac /auth2.c
parentf5efcd3450bbf8261915e0c4a6f851229dddaa79 (diff)
parentecebda56da46a03dafff923d91c382f31faa9eec (diff)
* New upstream release (http://www.openssh.com/txt/release-6.2).
- Add support for multiple required authentication in SSH protocol 2 via an AuthenticationMethods option (closes: #195716). - Fix Sophie Germain formula in moduli(5) (closes: #698612). - Update ssh-copy-id to Phil Hands' greatly revised version (closes: #99785, #322228, #620428; LP: #518883, #835901, #1074798).
Diffstat (limited to 'auth2.c')
-rw-r--r--auth2.c239
1 files changed, 212 insertions, 27 deletions
diff --git a/auth2.c b/auth2.c
index 1b7403904..f00f14764 100644
--- a/auth2.c
+++ b/auth2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth2.c,v 1.124 2011/12/07 05:44:38 djm Exp $ */ 1/* $OpenBSD: auth2.c,v 1.126 2012/12/02 20:34:09 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -98,8 +98,10 @@ static void input_service_request(int, u_int32_t, void *);
98static void input_userauth_request(int, u_int32_t, void *); 98static void input_userauth_request(int, u_int32_t, void *);
99 99
100/* helper */ 100/* helper */
101static Authmethod *authmethod_lookup(const char *); 101static Authmethod *authmethod_lookup(Authctxt *, const char *);
102static char *authmethods_get(void); 102static char *authmethods_get(Authctxt *authctxt);
103static int method_allowed(Authctxt *, const char *);
104static int list_starts_with(const char *, const char *);
103 105
104char * 106char *
105auth2_read_banner(void) 107auth2_read_banner(void)
@@ -263,6 +265,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
263 if (use_privsep) 265 if (use_privsep)
264 mm_inform_authserv(service, style, role); 266 mm_inform_authserv(service, style, role);
265 userauth_banner(); 267 userauth_banner();
268 if (auth2_setup_methods_lists(authctxt) != 0)
269 packet_disconnect("no authentication methods enabled");
266 } else if (strcmp(user, authctxt->user) != 0 || 270 } else if (strcmp(user, authctxt->user) != 0 ||
267 strcmp(service, authctxt->service) != 0) { 271 strcmp(service, authctxt->service) != 0) {
268 packet_disconnect("Change of username or service not allowed: " 272 packet_disconnect("Change of username or service not allowed: "
@@ -285,12 +289,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
285 authctxt->server_caused_failure = 0; 289 authctxt->server_caused_failure = 0;
286 290
287 /* try to authenticate user */ 291 /* try to authenticate user */
288 m = authmethod_lookup(method); 292 m = authmethod_lookup(authctxt, method);
289 if (m != NULL && authctxt->failures < options.max_authtries) { 293 if (m != NULL && authctxt->failures < options.max_authtries) {
290 debug2("input_userauth_request: try method %s", method); 294 debug2("input_userauth_request: try method %s", method);
291 authenticated = m->userauth(authctxt); 295 authenticated = m->userauth(authctxt);
292 } 296 }
293 userauth_finish(authctxt, authenticated, method); 297 userauth_finish(authctxt, authenticated, method, NULL);
294 298
295 xfree(service); 299 xfree(service);
296 xfree(user); 300 xfree(user);
@@ -298,13 +302,17 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
298} 302}
299 303
300void 304void
301userauth_finish(Authctxt *authctxt, int authenticated, char *method) 305userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
306 const char *submethod)
302{ 307{
303 char *methods; 308 char *methods;
309 int partial = 0;
304 310
305 if (!authctxt->valid && authenticated) 311 if (!authctxt->valid && authenticated)
306 fatal("INTERNAL ERROR: authenticated invalid user %s", 312 fatal("INTERNAL ERROR: authenticated invalid user %s",
307 authctxt->user); 313 authctxt->user);
314 if (authenticated && authctxt->postponed)
315 fatal("INTERNAL ERROR: authenticated and postponed");
308 316
309 /* Special handling for root */ 317 /* Special handling for root */
310 if (authenticated && authctxt->pw->pw_uid == 0 && 318 if (authenticated && authctxt->pw->pw_uid == 0 &&
@@ -315,6 +323,19 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
315#endif 323#endif
316 } 324 }
317 325
326 if (authenticated && options.num_auth_methods != 0) {
327 if (!auth2_update_methods_lists(authctxt, method)) {
328 authenticated = 0;
329 partial = 1;
330 }
331 }
332
333 /* Log before sending the reply */
334 auth_log(authctxt, authenticated, partial, method, submethod, " ssh2");
335
336 if (authctxt->postponed)
337 return;
338
318#ifdef USE_PAM 339#ifdef USE_PAM
319 if (options.use_pam && authenticated) { 340 if (options.use_pam && authenticated) {
320 if (!PRIVSEP(do_pam_account())) { 341 if (!PRIVSEP(do_pam_account())) {
@@ -333,17 +354,10 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
333#ifdef _UNICOS 354#ifdef _UNICOS
334 if (authenticated && cray_access_denied(authctxt->user)) { 355 if (authenticated && cray_access_denied(authctxt->user)) {
335 authenticated = 0; 356 authenticated = 0;
336 fatal("Access denied for user %s.",authctxt->user); 357 fatal("Access denied for user %s.", authctxt->user);
337 } 358 }
338#endif /* _UNICOS */ 359#endif /* _UNICOS */
339 360
340 /* Log before sending the reply */
341 auth_log(authctxt, authenticated, method, " ssh2");
342
343 if (authctxt->postponed)
344 return;
345
346 /* XXX todo: check if multiple auth methods are needed */
347 if (authenticated == 1) { 361 if (authenticated == 1) {
348 /* turn off userauth */ 362 /* turn off userauth */
349 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 363 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
@@ -364,34 +378,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
364#endif 378#endif
365 packet_disconnect(AUTH_FAIL_MSG, authctxt->user); 379 packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
366 } 380 }
367 methods = authmethods_get(); 381 methods = authmethods_get(authctxt);
382 debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
383 partial, methods);
368 packet_start(SSH2_MSG_USERAUTH_FAILURE); 384 packet_start(SSH2_MSG_USERAUTH_FAILURE);
369 packet_put_cstring(methods); 385 packet_put_cstring(methods);
370 packet_put_char(0); /* XXX partial success, unused */ 386 packet_put_char(partial);
371 packet_send(); 387 packet_send();
372 packet_write_wait(); 388 packet_write_wait();
373 xfree(methods); 389 xfree(methods);
374 } 390 }
375} 391}
376 392
393/*
394 * Checks whether method is allowed by at least one AuthenticationMethods
395 * methods list. Returns 1 if allowed, or no methods lists configured.
396 * 0 otherwise.
397 */
398static int
399method_allowed(Authctxt *authctxt, const char *method)
400{
401 u_int i;
402
403 /*
404 * NB. authctxt->num_auth_methods might be zero as a result of
405 * auth2_setup_methods_lists(), so check the configuration.
406 */
407 if (options.num_auth_methods == 0)
408 return 1;
409 for (i = 0; i < authctxt->num_auth_methods; i++) {
410 if (list_starts_with(authctxt->auth_methods[i], method))
411 return 1;
412 }
413 return 0;
414}
415
377static char * 416static char *
378authmethods_get(void) 417authmethods_get(Authctxt *authctxt)
379{ 418{
380 Buffer b; 419 Buffer b;
381 char *list; 420 char *list;
382 int i; 421 u_int i;
383 422
384 buffer_init(&b); 423 buffer_init(&b);
385 for (i = 0; authmethods[i] != NULL; i++) { 424 for (i = 0; authmethods[i] != NULL; i++) {
386 if (strcmp(authmethods[i]->name, "none") == 0) 425 if (strcmp(authmethods[i]->name, "none") == 0)
387 continue; 426 continue;
388 if (authmethods[i]->enabled != NULL && 427 if (authmethods[i]->enabled == NULL ||
389 *(authmethods[i]->enabled) != 0) { 428 *(authmethods[i]->enabled) == 0)
390 if (buffer_len(&b) > 0) 429 continue;
391 buffer_append(&b, ",", 1); 430 if (!method_allowed(authctxt, authmethods[i]->name))
392 buffer_append(&b, authmethods[i]->name, 431 continue;
393 strlen(authmethods[i]->name)); 432 if (buffer_len(&b) > 0)
394 } 433 buffer_append(&b, ",", 1);
434 buffer_append(&b, authmethods[i]->name,
435 strlen(authmethods[i]->name));
395 } 436 }
396 buffer_append(&b, "\0", 1); 437 buffer_append(&b, "\0", 1);
397 list = xstrdup(buffer_ptr(&b)); 438 list = xstrdup(buffer_ptr(&b));
@@ -400,7 +441,7 @@ authmethods_get(void)
400} 441}
401 442
402static Authmethod * 443static Authmethod *
403authmethod_lookup(const char *name) 444authmethod_lookup(Authctxt *authctxt, const char *name)
404{ 445{
405 int i; 446 int i;
406 447
@@ -408,10 +449,154 @@ authmethod_lookup(const char *name)
408 for (i = 0; authmethods[i] != NULL; i++) 449 for (i = 0; authmethods[i] != NULL; i++)
409 if (authmethods[i]->enabled != NULL && 450 if (authmethods[i]->enabled != NULL &&
410 *(authmethods[i]->enabled) != 0 && 451 *(authmethods[i]->enabled) != 0 &&
411 strcmp(name, authmethods[i]->name) == 0) 452 strcmp(name, authmethods[i]->name) == 0 &&
453 method_allowed(authctxt, authmethods[i]->name))
412 return authmethods[i]; 454 return authmethods[i];
413 debug2("Unrecognized authentication method name: %s", 455 debug2("Unrecognized authentication method name: %s",
414 name ? name : "NULL"); 456 name ? name : "NULL");
415 return NULL; 457 return NULL;
416} 458}
417 459
460/*
461 * Check a comma-separated list of methods for validity. Is need_enable is
462 * non-zero, then also require that the methods are enabled.
463 * Returns 0 on success or -1 if the methods list is invalid.
464 */
465int
466auth2_methods_valid(const char *_methods, int need_enable)
467{
468 char *methods, *omethods, *method;
469 u_int i, found;
470 int ret = -1;
471
472 if (*_methods == '\0') {
473 error("empty authentication method list");
474 return -1;
475 }
476 omethods = methods = xstrdup(_methods);
477 while ((method = strsep(&methods, ",")) != NULL) {
478 for (found = i = 0; !found && authmethods[i] != NULL; i++) {
479 if (strcmp(method, authmethods[i]->name) != 0)
480 continue;
481 if (need_enable) {
482 if (authmethods[i]->enabled == NULL ||
483 *(authmethods[i]->enabled) == 0) {
484 error("Disabled method \"%s\" in "
485 "AuthenticationMethods list \"%s\"",
486 method, _methods);
487 goto out;
488 }
489 }
490 found = 1;
491 break;
492 }
493 if (!found) {
494 error("Unknown authentication method \"%s\" in list",
495 method);
496 goto out;
497 }
498 }
499 ret = 0;
500 out:
501 free(omethods);
502 return ret;
503}
504
505/*
506 * Prune the AuthenticationMethods supplied in the configuration, removing
507 * any methods lists that include disabled methods. Note that this might
508 * leave authctxt->num_auth_methods == 0, even when multiple required auth
509 * has been requested. For this reason, all tests for whether multiple is
510 * enabled should consult options.num_auth_methods directly.
511 */
512int
513auth2_setup_methods_lists(Authctxt *authctxt)
514{
515 u_int i;
516
517 if (options.num_auth_methods == 0)
518 return 0;
519 debug3("%s: checking methods", __func__);
520 authctxt->auth_methods = xcalloc(options.num_auth_methods,
521 sizeof(*authctxt->auth_methods));
522 authctxt->num_auth_methods = 0;
523 for (i = 0; i < options.num_auth_methods; i++) {
524 if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
525 logit("Authentication methods list \"%s\" contains "
526 "disabled method, skipping",
527 options.auth_methods[i]);
528 continue;
529 }
530 debug("authentication methods list %d: %s",
531 authctxt->num_auth_methods, options.auth_methods[i]);
532 authctxt->auth_methods[authctxt->num_auth_methods++] =
533 xstrdup(options.auth_methods[i]);
534 }
535 if (authctxt->num_auth_methods == 0) {
536 error("No AuthenticationMethods left after eliminating "
537 "disabled methods");
538 return -1;
539 }
540 return 0;
541}
542
543static int
544list_starts_with(const char *methods, const char *method)
545{
546 size_t l = strlen(method);
547
548 if (strncmp(methods, method, l) != 0)
549 return 0;
550 if (methods[l] != ',' && methods[l] != '\0')
551 return 0;
552 return 1;
553}
554
555/*
556 * Remove method from the start of a comma-separated list of methods.
557 * Returns 0 if the list of methods did not start with that method or 1
558 * if it did.
559 */
560static int
561remove_method(char **methods, const char *method)
562{
563 char *omethods = *methods;
564 size_t l = strlen(method);
565
566 if (!list_starts_with(omethods, method))
567 return 0;
568 *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
569 free(omethods);
570 return 1;
571}
572
573/*
574 * Called after successful authentication. Will remove the successful method
575 * from the start of each list in which it occurs. If it was the last method
576 * in any list, then authentication is deemed successful.
577 * Returns 1 if the method completed any authentication list or 0 otherwise.
578 */
579int
580auth2_update_methods_lists(Authctxt *authctxt, const char *method)
581{
582 u_int i, found = 0;
583
584 debug3("%s: updating methods list after \"%s\"", __func__, method);
585 for (i = 0; i < authctxt->num_auth_methods; i++) {
586 if (!remove_method(&(authctxt->auth_methods[i]), method))
587 continue;
588 found = 1;
589 if (*authctxt->auth_methods[i] == '\0') {
590 debug2("authentication methods list %d complete", i);
591 return 1;
592 }
593 debug3("authentication methods list %d remaining: \"%s\"",
594 i, authctxt->auth_methods[i]);
595 }
596 /* This should not happen, but would be bad if it did */
597 if (!found)
598 fatal("%s: method not in AuthenticationMethods", __func__);
599 return 0;
600}
601
602