summaryrefslogtreecommitdiff
path: root/auth2.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2012-11-04 23:21:40 +1100
committerDamien Miller <djm@mindrot.org>2012-11-04 23:21:40 +1100
commita6e3f01d1e230b8acfdd6b4cf3096459d2a325e0 (patch)
tree577022d2b31e9519d26bc614c3f5396e17d58ec6 /auth2.c
parentd0d1099b3b8a766480ce6df215631bf0af6e6bcd (diff)
- djm@cvs.openbsd.org 2012/11/04 11:09:15
[auth.h auth1.c auth2.c monitor.c servconf.c servconf.h sshd.c] [sshd_config.5] Support multiple required authentication via an AuthenticationMethods option. This option lists one or more comma-separated lists of authentication method names. Successful completion of all the methods in any list is required for authentication to complete; feedback and ok markus@
Diffstat (limited to 'auth2.c')
-rw-r--r--auth2.c218
1 files changed, 200 insertions, 18 deletions
diff --git a/auth2.c b/auth2.c
index b66bef64c..8114ec863 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.125 2012/11/04 11:09:15 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -96,8 +96,10 @@ static void input_service_request(int, u_int32_t, void *);
96static void input_userauth_request(int, u_int32_t, void *); 96static void input_userauth_request(int, u_int32_t, void *);
97 97
98/* helper */ 98/* helper */
99static Authmethod *authmethod_lookup(const char *); 99static Authmethod *authmethod_lookup(Authctxt *, const char *);
100static char *authmethods_get(void); 100static char *authmethods_get(Authctxt *authctxt);
101static int method_allowed(Authctxt *, const char *);
102static int list_starts_with(const char *, const char *);
101 103
102char * 104char *
103auth2_read_banner(void) 105auth2_read_banner(void)
@@ -255,6 +257,8 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
255 if (use_privsep) 257 if (use_privsep)
256 mm_inform_authserv(service, style); 258 mm_inform_authserv(service, style);
257 userauth_banner(); 259 userauth_banner();
260 if (auth2_setup_methods_lists(authctxt) != 0)
261 packet_disconnect("no authentication methods enabled");
258 } else if (strcmp(user, authctxt->user) != 0 || 262 } else if (strcmp(user, authctxt->user) != 0 ||
259 strcmp(service, authctxt->service) != 0) { 263 strcmp(service, authctxt->service) != 0) {
260 packet_disconnect("Change of username or service not allowed: " 264 packet_disconnect("Change of username or service not allowed: "
@@ -277,7 +281,7 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
277 authctxt->server_caused_failure = 0; 281 authctxt->server_caused_failure = 0;
278 282
279 /* try to authenticate user */ 283 /* try to authenticate user */
280 m = authmethod_lookup(method); 284 m = authmethod_lookup(authctxt, method);
281 if (m != NULL && authctxt->failures < options.max_authtries) { 285 if (m != NULL && authctxt->failures < options.max_authtries) {
282 debug2("input_userauth_request: try method %s", method); 286 debug2("input_userauth_request: try method %s", method);
283 authenticated = m->userauth(authctxt); 287 authenticated = m->userauth(authctxt);
@@ -293,6 +297,7 @@ void
293userauth_finish(Authctxt *authctxt, int authenticated, char *method) 297userauth_finish(Authctxt *authctxt, int authenticated, char *method)
294{ 298{
295 char *methods; 299 char *methods;
300 int partial = 0;
296 301
297 if (!authctxt->valid && authenticated) 302 if (!authctxt->valid && authenticated)
298 fatal("INTERNAL ERROR: authenticated invalid user %s", 303 fatal("INTERNAL ERROR: authenticated invalid user %s",
@@ -335,7 +340,13 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
335 if (authctxt->postponed) 340 if (authctxt->postponed)
336 return; 341 return;
337 342
338 /* XXX todo: check if multiple auth methods are needed */ 343 if (authenticated && options.num_auth_methods != 0) {
344 if (!auth2_update_methods_lists(authctxt, method)) {
345 authenticated = 0;
346 partial = 1;
347 }
348 }
349
339 if (authenticated == 1) { 350 if (authenticated == 1) {
340 /* turn off userauth */ 351 /* turn off userauth */
341 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); 352 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
@@ -356,34 +367,61 @@ userauth_finish(Authctxt *authctxt, int authenticated, char *method)
356#endif 367#endif
357 packet_disconnect(AUTH_FAIL_MSG, authctxt->user); 368 packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
358 } 369 }
359 methods = authmethods_get(); 370 methods = authmethods_get(authctxt);
371 debug3("%s: failure partial=%d next methods=\"%s\"", __func__,
372 partial, methods);
360 packet_start(SSH2_MSG_USERAUTH_FAILURE); 373 packet_start(SSH2_MSG_USERAUTH_FAILURE);
361 packet_put_cstring(methods); 374 packet_put_cstring(methods);
362 packet_put_char(0); /* XXX partial success, unused */ 375 packet_put_char(partial);
363 packet_send(); 376 packet_send();
364 packet_write_wait(); 377 packet_write_wait();
365 xfree(methods); 378 xfree(methods);
366 } 379 }
367} 380}
368 381
382/*
383 * Checks whether method is allowed by at least one AuthenticationMethods
384 * methods list. Returns 1 if allowed, or no methods lists configured.
385 * 0 otherwise.
386 */
387static int
388method_allowed(Authctxt *authctxt, const char *method)
389{
390 u_int i;
391
392 /*
393 * NB. authctxt->num_auth_methods might be zero as a result of
394 * auth2_setup_methods_lists(), so check the configuration.
395 */
396 if (options.num_auth_methods == 0)
397 return 1;
398 for (i = 0; i < authctxt->num_auth_methods; i++) {
399 if (list_starts_with(authctxt->auth_methods[i], method))
400 return 1;
401 }
402 return 0;
403}
404
369static char * 405static char *
370authmethods_get(void) 406authmethods_get(Authctxt *authctxt)
371{ 407{
372 Buffer b; 408 Buffer b;
373 char *list; 409 char *list;
374 int i; 410 u_int i;
375 411
376 buffer_init(&b); 412 buffer_init(&b);
377 for (i = 0; authmethods[i] != NULL; i++) { 413 for (i = 0; authmethods[i] != NULL; i++) {
378 if (strcmp(authmethods[i]->name, "none") == 0) 414 if (strcmp(authmethods[i]->name, "none") == 0)
379 continue; 415 continue;
380 if (authmethods[i]->enabled != NULL && 416 if (authmethods[i]->enabled == NULL ||
381 *(authmethods[i]->enabled) != 0) { 417 *(authmethods[i]->enabled) == 0)
382 if (buffer_len(&b) > 0) 418 continue;
383 buffer_append(&b, ",", 1); 419 if (!method_allowed(authctxt, authmethods[i]->name))
384 buffer_append(&b, authmethods[i]->name, 420 continue;
385 strlen(authmethods[i]->name)); 421 if (buffer_len(&b) > 0)
386 } 422 buffer_append(&b, ",", 1);
423 buffer_append(&b, authmethods[i]->name,
424 strlen(authmethods[i]->name));
387 } 425 }
388 buffer_append(&b, "\0", 1); 426 buffer_append(&b, "\0", 1);
389 list = xstrdup(buffer_ptr(&b)); 427 list = xstrdup(buffer_ptr(&b));
@@ -392,7 +430,7 @@ authmethods_get(void)
392} 430}
393 431
394static Authmethod * 432static Authmethod *
395authmethod_lookup(const char *name) 433authmethod_lookup(Authctxt *authctxt, const char *name)
396{ 434{
397 int i; 435 int i;
398 436
@@ -400,10 +438,154 @@ authmethod_lookup(const char *name)
400 for (i = 0; authmethods[i] != NULL; i++) 438 for (i = 0; authmethods[i] != NULL; i++)
401 if (authmethods[i]->enabled != NULL && 439 if (authmethods[i]->enabled != NULL &&
402 *(authmethods[i]->enabled) != 0 && 440 *(authmethods[i]->enabled) != 0 &&
403 strcmp(name, authmethods[i]->name) == 0) 441 strcmp(name, authmethods[i]->name) == 0 &&
442 method_allowed(authctxt, authmethods[i]->name))
404 return authmethods[i]; 443 return authmethods[i];
405 debug2("Unrecognized authentication method name: %s", 444 debug2("Unrecognized authentication method name: %s",
406 name ? name : "NULL"); 445 name ? name : "NULL");
407 return NULL; 446 return NULL;
408} 447}
409 448
449/*
450 * Check a comma-separated list of methods for validity. Is need_enable is
451 * non-zero, then also require that the methods are enabled.
452 * Returns 0 on success or -1 if the methods list is invalid.
453 */
454int
455auth2_methods_valid(const char *_methods, int need_enable)
456{
457 char *methods, *omethods, *method;
458 u_int i, found;
459 int ret = -1;
460
461 if (*_methods == '\0') {
462 error("empty authentication method list");
463 return -1;
464 }
465 omethods = methods = xstrdup(_methods);
466 while ((method = strsep(&methods, ",")) != NULL) {
467 for (found = i = 0; !found && authmethods[i] != NULL; i++) {
468 if (strcmp(method, authmethods[i]->name) != 0)
469 continue;
470 if (need_enable) {
471 if (authmethods[i]->enabled == NULL ||
472 *(authmethods[i]->enabled) == 0) {
473 error("Disabled method \"%s\" in "
474 "AuthenticationMethods list \"%s\"",
475 method, _methods);
476 goto out;
477 }
478 }
479 found = 1;
480 break;
481 }
482 if (!found) {
483 error("Unknown authentication method \"%s\" in list",
484 method);
485 goto out;
486 }
487 }
488 ret = 0;
489 out:
490 free(omethods);
491 return ret;
492}
493
494/*
495 * Prune the AuthenticationMethods supplied in the configuration, removing
496 * any methods lists that include disabled methods. Note that this might
497 * leave authctxt->num_auth_methods == 0, even when multiple required auth
498 * has been requested. For this reason, all tests for whether multiple is
499 * enabled should consult options.num_auth_methods directly.
500 */
501int
502auth2_setup_methods_lists(Authctxt *authctxt)
503{
504 u_int i;
505
506 if (options.num_auth_methods == 0)
507 return 0;
508 debug3("%s: checking methods", __func__);
509 authctxt->auth_methods = xcalloc(options.num_auth_methods,
510 sizeof(*authctxt->auth_methods));
511 authctxt->num_auth_methods = 0;
512 for (i = 0; i < options.num_auth_methods; i++) {
513 if (auth2_methods_valid(options.auth_methods[i], 1) != 0) {
514 logit("Authentication methods list \"%s\" contains "
515 "disabled method, skipping",
516 options.auth_methods[i]);
517 continue;
518 }
519 debug("authentication methods list %d: %s",
520 authctxt->num_auth_methods, options.auth_methods[i]);
521 authctxt->auth_methods[authctxt->num_auth_methods++] =
522 xstrdup(options.auth_methods[i]);
523 }
524 if (authctxt->num_auth_methods == 0) {
525 error("No AuthenticationMethods left after eliminating "
526 "disabled methods");
527 return -1;
528 }
529 return 0;
530}
531
532static int
533list_starts_with(const char *methods, const char *method)
534{
535 size_t l = strlen(method);
536
537 if (strncmp(methods, method, l) != 0)
538 return 0;
539 if (methods[l] != ',' && methods[l] != '\0')
540 return 0;
541 return 1;
542}
543
544/*
545 * Remove method from the start of a comma-separated list of methods.
546 * Returns 0 if the list of methods did not start with that method or 1
547 * if it did.
548 */
549static int
550remove_method(char **methods, const char *method)
551{
552 char *omethods = *methods;
553 size_t l = strlen(method);
554
555 if (!list_starts_with(omethods, method))
556 return 0;
557 *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0));
558 free(omethods);
559 return 1;
560}
561
562/*
563 * Called after successful authentication. Will remove the successful method
564 * from the start of each list in which it occurs. If it was the last method
565 * in any list, then authentication is deemed successful.
566 * Returns 1 if the method completed any authentication list or 0 otherwise.
567 */
568int
569auth2_update_methods_lists(Authctxt *authctxt, const char *method)
570{
571 u_int i, found = 0;
572
573 debug3("%s: updating methods list after \"%s\"", __func__, method);
574 for (i = 0; i < authctxt->num_auth_methods; i++) {
575 if (!remove_method(&(authctxt->auth_methods[i]), method))
576 continue;
577 found = 1;
578 if (*authctxt->auth_methods[i] == '\0') {
579 debug2("authentication methods list %d complete", i);
580 return 1;
581 }
582 debug3("authentication methods list %d remaining: \"%s\"",
583 i, authctxt->auth_methods[i]);
584 }
585 /* This should not happen, but would be bad if it did */
586 if (!found)
587 fatal("%s: method not in AuthenticationMethods", __func__);
588 return 0;
589}
590
591