Home | History | Annotate | Download | only in sshd
      1 /*
      2  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 /*
     25  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     26  * Use is subject to license terms.
     27  */
     28 
     29 #include "includes.h"
     30 RCSID("$OpenBSD: auth2.c,v 1.95 2002/08/22 21:33:58 markus Exp $");
     31 
     32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     33 
     34 #include "ssh2.h"
     35 #include "xmalloc.h"
     36 #include "packet.h"
     37 #include "log.h"
     38 #include "servconf.h"
     39 #include "compat.h"
     40 #include "misc.h"
     41 #include "auth.h"
     42 #include "dispatch.h"
     43 #include "sshlogin.h"
     44 #include "pathnames.h"
     45 
     46 #ifdef HAVE_BSM
     47 #include "bsmaudit.h"
     48 extern adt_session_data_t *ah;
     49 #endif /* HAVE_BSM */
     50 
     51 #ifdef GSSAPI
     52 #include "ssh-gss.h"
     53 #endif
     54 
     55 /* import */
     56 extern ServerOptions options;
     57 extern u_char *session_id2;
     58 extern int session_id2_len;
     59 
     60 Authctxt *x_authctxt = NULL;
     61 
     62 /* methods */
     63 
     64 extern Authmethod method_none;
     65 extern Authmethod method_pubkey;
     66 extern Authmethod method_passwd;
     67 extern Authmethod method_kbdint;
     68 extern Authmethod method_hostbased;
     69 extern Authmethod method_external;
     70 extern Authmethod method_gssapi;
     71 
     72 static Authmethod *authmethods[] = {
     73 	&method_none,
     74 #ifdef GSSAPI
     75 	&method_external,
     76 	&method_gssapi,
     77 #endif
     78 	&method_pubkey,
     79 	&method_passwd,
     80 	&method_kbdint,
     81 	&method_hostbased,
     82 	NULL
     83 };
     84 
     85 /* protocol */
     86 
     87 static void input_service_request(int, u_int32_t, void *);
     88 static void input_userauth_request(int, u_int32_t, void *);
     89 
     90 /* helper */
     91 static Authmethod *authmethod_lookup(const char *);
     92 static char *authmethods_get(void);
     93 static char *authmethods_check_abandonment(Authctxt *authctxt,
     94 					  Authmethod *method);
     95 static void  authmethod_count_attempt(Authmethod *method);
     96 /*static char *authmethods_get_kbdint(void);*/
     97 int user_key_allowed(struct passwd *, Key *);
     98 int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
     99 static int   userauth_method_can_run(Authmethod *method);
    100 static void  userauth_reset_methods(void);
    101 
    102 /*
    103  * loop until authctxt->success == TRUE
    104  */
    105 
    106 Authctxt *
    107 do_authentication2(void)
    108 {
    109 	Authctxt *authctxt = authctxt_new();
    110 
    111 	x_authctxt = authctxt;		/*XXX*/
    112 
    113 #ifdef HAVE_BSM
    114 	fatal_add_cleanup(audit_failed_login_cleanup, authctxt);
    115 #endif /* HAVE_BSM */
    116 
    117 	/* challenge-response is implemented via keyboard interactive */
    118 	if (options.challenge_response_authentication)
    119 		options.kbd_interactive_authentication = 1;
    120 	if (options.pam_authentication_via_kbd_int)
    121 		options.kbd_interactive_authentication = 1;
    122 
    123 	dispatch_init(&dispatch_protocol_error);
    124 	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
    125 	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
    126 
    127 	return (authctxt);
    128 }
    129 
    130 static void
    131 input_service_request(int type, u_int32_t seq, void *ctxt)
    132 {
    133 	Authctxt *authctxt = ctxt;
    134 	u_int len;
    135 	int acceptit = 0;
    136 	char *service = packet_get_string(&len);
    137 	packet_check_eom();
    138 
    139 	if (authctxt == NULL)
    140 		fatal("input_service_request: no authctxt");
    141 
    142 	if (strcmp(service, "ssh-userauth") == 0) {
    143 		if (!authctxt->success) {
    144 			acceptit = 1;
    145 			/* now we can handle user-auth requests */
    146 			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
    147 		}
    148 	}
    149 	/* XXX all other service requests are denied */
    150 
    151 	if (acceptit) {
    152 		packet_start(SSH2_MSG_SERVICE_ACCEPT);
    153 		packet_put_cstring(service);
    154 		packet_send();
    155 		packet_write_wait();
    156 	} else {
    157 		debug("bad service request %s", service);
    158 		packet_disconnect("bad service request %s", service);
    159 	}
    160 	xfree(service);
    161 }
    162 
    163 static void
    164 input_userauth_request(int type, u_int32_t seq, void *ctxt)
    165 {
    166 	Authctxt *authctxt = ctxt;
    167 	Authmethod *m = NULL;
    168 	char *user, *service, *method, *style = NULL;
    169 
    170 	if (authctxt == NULL)
    171 		fatal("input_userauth_request: no authctxt");
    172 
    173 	user = packet_get_string(NULL);
    174 	service = packet_get_string(NULL);
    175 	method = packet_get_string(NULL);
    176 	debug("userauth-request for user %s service %s method %s", user,
    177 		service, method);
    178 	debug("attempt %d initial attempt %d failures %d initial failures %d",
    179 		authctxt->attempt, authctxt->init_attempt,
    180 		authctxt->failures, authctxt->init_failures);
    181 
    182 	m = authmethod_lookup(method);
    183 
    184 	if ((style = strchr(user, ':')) != NULL)
    185 		*style++ = 0;
    186 
    187 	authctxt->attempt++;
    188 	if (m != NULL && m->is_initial)
    189 		authctxt->init_attempt++;
    190 
    191 	if (authctxt->attempt == 1) {
    192 		/* setup auth context */
    193 		authctxt->pw = getpwnamallow(user);
    194 		/* May want to abstract SSHv2 services someday */
    195 		if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
    196 			/* enforced in userauth_finish() below */
    197 			authctxt->valid = 1;
    198 			debug2("input_userauth_request: setting up authctxt for %s", user);
    199 		} else {
    200 			log("input_userauth_request: illegal user %s", user);
    201 		}
    202 		setproctitle("%s", authctxt->pw ? user : "unknown");
    203 		authctxt->user = xstrdup(user);
    204 		authctxt->service = xstrdup(service);
    205 		authctxt->style = style ? xstrdup(style) : NULL;
    206 		userauth_reset_methods();
    207 	} else {
    208 		char *abandoned;
    209 
    210 		/*
    211 		 * Check for abandoned [multi-round-trip] userauths
    212 		 * methods (e.g., kbdint).  Userauth method abandonment
    213 		 * should be treated as userauth method failure and
    214 		 * counted against max_auth_tries.
    215 		 */
    216 		abandoned = authmethods_check_abandonment(authctxt, m);
    217 
    218 		if (abandoned != NULL &&
    219 		    authctxt->failures > options.max_auth_tries) {
    220 			/* userauth_finish() will now packet_disconnect() */
    221 			userauth_finish(authctxt, abandoned);
    222 			/* NOTREACHED */
    223 		}
    224 
    225 		/* Handle user|service changes, possibly packet_disconnect() */
    226 		userauth_user_svc_change(authctxt, user, service);
    227 	}
    228 
    229 	authctxt->method = m;
    230 
    231 	/* run userauth method, try to authenticate user */
    232 	if (m != NULL && userauth_method_can_run(m)) {
    233 		debug2("input_userauth_request: try method %s", method);
    234 
    235 		m->postponed = 0;
    236 		m->abandoned = 0;
    237 		m->authenticated = 0;
    238 
    239 		if (!m->is_initial ||
    240 		    authctxt->init_failures < options.max_init_auth_tries)
    241 			m->userauth(authctxt);
    242 
    243 		authmethod_count_attempt(m);
    244 
    245 		if (authctxt->unwind_dispatch_loop) {
    246 			/*
    247 			 * Method ran nested dispatch loop but was
    248 			 * abandoned.  Cleanup and return without doing
    249 			 * anything else; we're just unwinding the stack.
    250 			 */
    251 			authctxt->unwind_dispatch_loop = 0;
    252 			goto done;
    253 		}
    254 
    255 		if (m->postponed)
    256 			goto done; /* multi-round trip userauth not finished */
    257 
    258 		if (m->abandoned) {
    259 			/* multi-round trip userauth abandoned, log failure */
    260 			auth_log(authctxt, 0, method, " ssh2");
    261 			goto done;
    262 		}
    263 	}
    264 
    265 	userauth_finish(authctxt, method);
    266 
    267 done:
    268 	xfree(service);
    269 	xfree(user);
    270 	xfree(method);
    271 }
    272 
    273 void
    274 userauth_finish(Authctxt *authctxt, char *method)
    275 {
    276 	int authenticated, partial;
    277 
    278 	if (authctxt == NULL)
    279 		fatal("%s: missing context", __func__);
    280 
    281 	/* unknown method handling -- must elicit userauth failure msg */
    282 	if (authctxt->method == NULL) {
    283 		authenticated = 0;
    284 		partial = 0;
    285 		goto done_checking;
    286 	}
    287 
    288 #ifndef USE_PAM
    289 	/* Special handling for root (done elsewhere for PAM) */
    290 	if (authctxt->method->authenticated &&
    291 	    authctxt->pw != NULL && authctxt->pw->pw_uid == 0 &&
    292 	    !auth_root_allowed(method))
    293 		authctxt->method->authenticated = 0;
    294 #endif /* USE_PAM */
    295 
    296 #ifdef _UNICOS
    297 	if (authctxt->method->authenticated &&
    298 	    cray_access_denied(authctxt->user)) {
    299 		authctxt->method->authenticated = 0;
    300 		fatal("Access denied for user %s.",authctxt->user);
    301 	}
    302 #endif /* _UNICOS */
    303 
    304 	partial = userauth_check_partial_failure(authctxt);
    305 	authenticated = authctxt->method->authenticated;
    306 
    307 #ifdef USE_PAM
    308 	/*
    309 	 * If the userauth method failed to complete PAM work then force
    310 	 * partial failure.
    311 	 */
    312 	if (authenticated && !AUTHPAM_DONE(authctxt))
    313 		partial = 1;
    314 #endif /* USE_PAM */
    315 
    316 	/*
    317 	 * To properly support invalid userauth method names we set
    318 	 * authenticated=0, partial=0 above and know that
    319 	 * authctxt->method == NULL.
    320 	 *
    321 	 * No unguarded reference to authctxt->method allowed from here.
    322 	 * Checking authenticated != 0 is a valid guard; authctxt->method
    323 	 * MUST NOT be NULL if authenticated.
    324 	 */
    325 done_checking:
    326 	if (!authctxt->valid && authenticated) {
    327 		/*
    328 		 * Should never happen -- if it does PAM's at fault
    329 		 * but we need not panic, just treat as a failure.
    330 		 */
    331 		authctxt->method->authenticated = 0;
    332 		authenticated = 0;
    333 		log("Ignoring authenticated invalid user %s",
    334 		    authctxt->user);
    335 		auth_log(authctxt, 0, method, " ssh2");
    336 	}
    337 
    338 	/* Log before sending the reply */
    339 	auth_log(authctxt, authenticated, method, " ssh2");
    340 
    341 	if (authenticated && !partial) {
    342 
    343 		/* turn off userauth */
    344 		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
    345 		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
    346 		packet_send();
    347 		packet_write_wait();
    348 		/* now we can break out */
    349 		authctxt->success = 1;
    350 	} else {
    351 		char *methods;
    352 
    353 		if (authctxt->method && authctxt->method->is_initial)
    354 			authctxt->init_failures++;
    355 
    356 		authctxt->method = NULL;
    357 
    358 #ifdef USE_PAM
    359 		/*
    360 		 * Keep track of last PAM error (or PERM_DENIED) for BSM
    361 		 * login failure auditing, which may run after the PAM
    362 		 * state has been cleaned up.
    363 		 */
    364 		authctxt->pam_retval = AUTHPAM_ERROR(authctxt, PAM_PERM_DENIED);
    365 #endif /* USE_PAM */
    366 
    367 		if (authctxt->failures++ > options.max_auth_tries) {
    368 #ifdef HAVE_BSM
    369 			fatal_remove_cleanup(audit_failed_login_cleanup,
    370 				authctxt);
    371 			audit_sshd_login_failure(&ah, PAM_MAXTRIES);
    372 #endif /* HAVE_BSM */
    373 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
    374 		}
    375 
    376 #ifdef _UNICOS
    377 		if (strcmp(method, "password") == 0)
    378 			cray_login_failure(authctxt->user, IA_UDBERR);
    379 #endif /* _UNICOS */
    380 		packet_start(SSH2_MSG_USERAUTH_FAILURE);
    381 
    382 		/*
    383 		 * If (partial) then authmethods_get() will return only
    384 		 * required methods, likely only "keyboard-interactive;"
    385 		 * (methods == NULL) implies failure, even if (partial == 1)
    386 		 */
    387 		methods = authmethods_get();
    388 		packet_put_cstring(methods);
    389 		packet_put_char((authenticated && partial && methods) ? 1 : 0);
    390 		if (methods)
    391 			xfree(methods);
    392 		packet_send();
    393 		packet_write_wait();
    394 	}
    395 }
    396 
    397 /* get current user */
    398 
    399 struct passwd*
    400 auth_get_user(void)
    401 {
    402 	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
    403 }
    404 
    405 #define	DELIM	","
    406 
    407 #if 0
    408 static char *
    409 authmethods_get_kbdint(void)
    410 {
    411 	Buffer b;
    412 	int i;
    413 
    414 	for (i = 0; authmethods[i] != NULL; i++) {
    415 		if (strcmp(authmethods[i]->name, "keyboard-interactive") != 0)
    416 			continue;
    417 		return xstrdup(authmethods[i]->name);
    418 	}
    419 	return NULL;
    420 }
    421 #endif
    422 
    423 void
    424 userauth_user_svc_change(Authctxt *authctxt, char *user, char *service)
    425 {
    426 	/*
    427 	 * NOTE:
    428 	 *
    429 	 * SSHv2 services should be abstracted and service changes during
    430 	 * userauth should be supported as per the userauth draft.  In the PAM
    431 	 * case, support for multiple SSHv2 services means that we have to
    432 	 * format the PAM service name according to the SSHv2 service *and* the
    433 	 * SSHv2 userauth being attempted ("passwd", "kbdint" and "other").
    434 	 *
    435 	 * We'll cross that bridge when we come to it.  For now disallow service
    436 	 * changes during userauth if using PAM, but allow username changes.
    437 	 */
    438 
    439 	/* authctxt->service must == ssh-connection here */
    440 	if (service != NULL && strcmp(service, authctxt->service) != 0) {
    441 		packet_disconnect("Change of service not "
    442 				  "allowed: %s and %s",
    443 				  authctxt->service, service);
    444 	}
    445 	if (user != NULL && authctxt->user != NULL &&
    446 	    strcmp(user, authctxt->user) == 0)
    447 		return;
    448 
    449 	/* All good; update authctxt */
    450 	xfree(authctxt->user);
    451 	authctxt->user = xstrdup(user);
    452 	pwfree(&authctxt->pw);
    453 	authctxt->pw = getpwnamallow(user);
    454 	authctxt->valid = (authctxt->pw != NULL);
    455 
    456 	/* Forget method state; abandon postponed userauths */
    457 	userauth_reset_methods();
    458 }
    459 
    460 int
    461 userauth_check_partial_failure(Authctxt *authctxt)
    462 {
    463 	int i;
    464 	int required = 0;
    465 	int sufficient = 0;
    466 
    467 	/*
    468 	 * v1 does not set authctxt->method
    469 	 * partial userauth failure is a v2 concept
    470 	 */
    471 	if (authctxt->method == NULL)
    472 		return 0;
    473 
    474 	for (i = 0; authmethods[i] != NULL; i++) {
    475 		if (authmethods[i]->required)
    476 			required++;
    477 		if (authmethods[i]->sufficient)
    478 			sufficient++;
    479 	}
    480 
    481 	if (required == 0 && sufficient == 0)
    482 		return !authctxt->method->authenticated;
    483 
    484 	if (required == 1 && authctxt->method->required)
    485 		return !authctxt->method->authenticated;
    486 
    487 	if (sufficient && authctxt->method->sufficient)
    488 		return !authctxt->method->authenticated;
    489 
    490 	return 1;
    491 }
    492 
    493 int
    494 userauth_method_can_run(Authmethod *method)
    495 {
    496 	if (method->not_again)
    497 		return 0;
    498 
    499 	return 1;
    500 }
    501 
    502 static
    503 void
    504 userauth_reset_methods(void)
    505 {
    506 	int i;
    507 
    508 	for (i = 0; authmethods[i] != NULL; i++) {
    509 		/* note: counters not reset */
    510 		authmethods[i]->required = 0;
    511 		authmethods[i]->sufficient = 0;
    512 		authmethods[i]->authenticated = 0;
    513 		authmethods[i]->not_again = 0;
    514 		authmethods[i]->postponed = 0;
    515 		authmethods[i]->abandoned = 0;
    516 	}
    517 }
    518 
    519 void
    520 userauth_force_kbdint(void)
    521 {
    522 	int i;
    523 
    524 	for (i = 0; authmethods[i] != NULL; i++) {
    525 		authmethods[i]->required = 0;
    526 		authmethods[i]->sufficient = 0;
    527 	}
    528 	method_kbdint.required = 1;
    529 }
    530 
    531 /*
    532  * Check to see if a previously run multi-round trip userauth method has
    533  * been abandoned and call its cleanup function.
    534  *
    535  * Abandoned userauth method invocations are counted as userauth failures.
    536  */
    537 static
    538 char *
    539 authmethods_check_abandonment(Authctxt *authctxt, Authmethod *method)
    540 {
    541 	int i;
    542 
    543 	/* optimization: check current method first */
    544 	if (method && method->postponed) {
    545 		method->postponed = 0;
    546 		if (method->abandon)
    547 			method->abandon(authctxt, method);
    548 		else
    549 			method->abandons++;
    550 		authctxt->failures++; /* abandonment -> failure */
    551 		if (method->is_initial)
    552 			authctxt->init_failures++;
    553 
    554 		/*
    555 		 * Since we check for abandonment whenever a userauth is
    556 		 * requested we know only one method could have been
    557 		 * in postponed state, so we can return now.
    558 		 */
    559 		return (method->name);
    560 	}
    561 	for (i = 0; authmethods[i] != NULL; i++) {
    562 		if (!authmethods[i]->postponed)
    563 			continue;
    564 
    565 		/* some method was postponed and a diff one is being started */
    566 		if (method != authmethods[i]) {
    567 			authmethods[i]->postponed = 0;
    568 			if (authmethods[i]->abandon)
    569 				authmethods[i]->abandon(authctxt,
    570 							authmethods[i]);
    571 			else
    572 				authmethods[i]->abandons++;
    573 			authctxt->failures++;
    574 			if (authmethods[i]->is_initial)
    575 				authctxt->init_failures++;
    576 			return (authmethods[i]->name); /* see above */
    577 		}
    578 	}
    579 
    580 	return NULL;
    581 }
    582 
    583 static char *
    584 authmethods_get(void)
    585 {
    586 	Buffer b;
    587 	char *list;
    588 	int i;
    589 	int sufficient = 0;
    590 	int required = 0;
    591 	int authenticated = 0;
    592 	int partial = 0;
    593 
    594 	/*
    595 	 * If at least one method succeeded partially then at least one
    596 	 * authmethod will be required and only required methods should
    597 	 * continue.
    598 	 */
    599 	for (i = 0; authmethods[i] != NULL; i++) {
    600 		if (authmethods[i]->authenticated)
    601 			authenticated++;
    602 		if (authmethods[i]->required)
    603 			required++;
    604 		if (authmethods[i]->sufficient)
    605 			sufficient++;
    606 	}
    607 
    608 	partial = (required + sufficient) > 0;
    609 
    610 	buffer_init(&b);
    611 	for (i = 0; authmethods[i] != NULL; i++) {
    612 		if (strcmp(authmethods[i]->name, "none") == 0)
    613 			continue;
    614 		if (required && !authmethods[i]->required)
    615 			continue;
    616 		if (sufficient && !required && !authmethods[i]->sufficient)
    617 			continue;
    618 		if (authmethods[i]->not_again)
    619 			continue;
    620 
    621 		if (authmethods[i]->required) {
    622 			if (buffer_len(&b) > 0)
    623 				buffer_append(&b, ",", 1);
    624 			buffer_append(&b, authmethods[i]->name,
    625 			    strlen(authmethods[i]->name));
    626 			continue;
    627 		}
    628 
    629 		/*
    630 		 * A method can be enabled (marked sufficient)
    631 		 * dynamically provided that at least one other method
    632 		 * has succeeded partially.
    633 		 */
    634 		if ((partial && authmethods[i]->sufficient) ||
    635 		    (authmethods[i]->enabled != NULL &&
    636 		    *(authmethods[i]->enabled) != 0)) {
    637 			if (buffer_len(&b) > 0)
    638 				buffer_append(&b, ",", 1);
    639 			buffer_append(&b, authmethods[i]->name,
    640 			    strlen(authmethods[i]->name));
    641 		}
    642 	}
    643 	buffer_append(&b, "\0", 1);
    644 	list = xstrdup(buffer_ptr(&b));
    645 	buffer_free(&b);
    646 	return list;
    647 }
    648 
    649 static Authmethod *
    650 authmethod_lookup(const char *name)
    651 {
    652 	int i;
    653 
    654 	/*
    655 	 * Method must be sufficient, required or enabled and must not
    656 	 * be marked as not able to run again
    657 	 */
    658 	if (name != NULL)
    659 		for (i = 0; authmethods[i] != NULL; i++)
    660 			if (((authmethods[i]->sufficient ||
    661 			      authmethods[i]->required) ||
    662 			     (authmethods[i]->enabled != NULL &&
    663 			      *(authmethods[i]->enabled) != 0)) &&
    664 			    !authmethods[i]->not_again &&
    665 			    strcmp(name, authmethods[i]->name) == 0)
    666 				return authmethods[i];
    667 	debug2("Unrecognized authentication method name: %s",
    668 	    name ? name : "NULL");
    669 	return NULL;
    670 }
    671 
    672 static void
    673 authmethod_count_attempt(Authmethod *method)
    674 {
    675 	if (!method)
    676 		fatal("Internal error in authmethod_count_attempt()");
    677 
    678 	if (method->postponed)
    679 		return;
    680 
    681 	method->attempts++;
    682 
    683 	if (method->abandoned)
    684 		method->abandons++;
    685 	else if (method->authenticated)
    686 		method->successes++;
    687 	else
    688 		method->failures++;
    689 
    690 	return;
    691 }
    692