Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: auth2-jpake.c,v 1.4 2010/08/31 11:54:45 djm Exp $ */
      2 /*
      3  * Copyright (c) 2008 Damien Miller.  All rights reserved.
      4  *
      5  * Permission to use, copy, modify, and distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  */
     17 
     18 /*
     19  * Server side of zero-knowledge password auth using J-PAKE protocol
     20  * as described in:
     21  *
     22  * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
     23  * 16th Workshop on Security Protocols, Cambridge, April 2008
     24  *
     25  * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
     26  */
     27 
     28 #ifdef JPAKE
     29 
     30 #include <sys/types.h>
     31 #include <sys/param.h>
     32 
     33 #include <pwd.h>
     34 #include <stdio.h>
     35 #include <string.h>
     36 #include <login_cap.h>
     37 
     38 #include <openssl/bn.h>
     39 #include <openssl/evp.h>
     40 
     41 #include "xmalloc.h"
     42 #include "ssh2.h"
     43 #include "key.h"
     44 #include "hostfile.h"
     45 #include "auth.h"
     46 #include "buffer.h"
     47 #include "packet.h"
     48 #include "dispatch.h"
     49 #include "log.h"
     50 #include "servconf.h"
     51 #include "auth-options.h"
     52 #include "canohost.h"
     53 #ifdef GSSAPI
     54 #include "ssh-gss.h"
     55 #endif
     56 #include "monitor_wrap.h"
     57 
     58 #include "schnorr.h"
     59 #include "jpake.h"
     60 
     61 /*
     62  * XXX options->permit_empty_passwd (at the moment, they will be refused
     63  * anyway because they will mismatch on fake salt.
     64  */
     65 
     66 /* Dispatch handlers */
     67 static void input_userauth_jpake_client_step1(int, u_int32_t, void *);
     68 static void input_userauth_jpake_client_step2(int, u_int32_t, void *);
     69 static void input_userauth_jpake_client_confirm(int, u_int32_t, void *);
     70 
     71 static int auth2_jpake_start(Authctxt *);
     72 
     73 /* import */
     74 extern ServerOptions options;
     75 extern u_char *session_id2;
     76 extern u_int session_id2_len;
     77 
     78 /*
     79  * Attempt J-PAKE authentication.
     80  */
     81 static int
     82 userauth_jpake(Authctxt *authctxt)
     83 {
     84 	int authenticated = 0;
     85 
     86 	packet_check_eom();
     87 
     88 	debug("jpake-01 (at) openssh.com requested");
     89 
     90 	if (authctxt->user != NULL) {
     91 		if (authctxt->jpake_ctx == NULL)
     92 			authctxt->jpake_ctx = jpake_new();
     93 		if (options.zero_knowledge_password_authentication)
     94 			authenticated = auth2_jpake_start(authctxt);
     95 	}
     96 
     97 	return authenticated;
     98 }
     99 
    100 Authmethod method_jpake = {
    101 	"jpake-01 (at) openssh.com",
    102 	userauth_jpake,
    103 	&options.zero_knowledge_password_authentication
    104 };
    105 
    106 /* Clear context and callbacks */
    107 void
    108 auth2_jpake_stop(Authctxt *authctxt)
    109 {
    110 	/* unregister callbacks */
    111 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
    112 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
    113 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
    114 	if (authctxt->jpake_ctx != NULL) {
    115 		jpake_free(authctxt->jpake_ctx);
    116 		authctxt->jpake_ctx = NULL;
    117 	}
    118 }
    119 
    120 /* Returns 1 if 'c' is a valid crypt(3) salt character, 0 otherwise */
    121 static int
    122 valid_crypt_salt(int c)
    123 {
    124 	if (c >= 'A' && c <= 'Z')
    125 		return 1;
    126 	if (c >= 'a' && c <= 'z')
    127 		return 1;
    128 	if (c >= '.' && c <= '9')
    129 		return 1;
    130 	return 0;
    131 }
    132 
    133 /*
    134  * Derive fake salt as H(username || first_private_host_key)
    135  * This provides relatively stable fake salts for non-existent
    136  * users and avoids the jpake method becoming an account validity
    137  * oracle.
    138  */
    139 static void
    140 derive_rawsalt(const char *username, u_char *rawsalt, u_int len)
    141 {
    142 	u_char *digest;
    143 	u_int digest_len;
    144 	Buffer b;
    145 	Key *k;
    146 
    147 	buffer_init(&b);
    148 	buffer_put_cstring(&b, username);
    149 	if ((k = get_hostkey_by_index(0)) == NULL ||
    150 	    (k->flags & KEY_FLAG_EXT))
    151 		fatal("%s: no hostkeys", __func__);
    152 	switch (k->type) {
    153 	case KEY_RSA1:
    154 	case KEY_RSA:
    155 		if (k->rsa->p == NULL || k->rsa->q == NULL)
    156 			fatal("%s: RSA key missing p and/or q", __func__);
    157 		buffer_put_bignum2(&b, k->rsa->p);
    158 		buffer_put_bignum2(&b, k->rsa->q);
    159 		break;
    160 	case KEY_DSA:
    161 		if (k->dsa->priv_key == NULL)
    162 			fatal("%s: DSA key missing priv_key", __func__);
    163 		buffer_put_bignum2(&b, k->dsa->priv_key);
    164 		break;
    165 	case KEY_ECDSA:
    166 		if (EC_KEY_get0_private_key(k->ecdsa) == NULL)
    167 			fatal("%s: ECDSA key missing priv_key", __func__);
    168 		buffer_put_bignum2(&b, EC_KEY_get0_private_key(k->ecdsa));
    169 		break;
    170 	default:
    171 		fatal("%s: unknown key type %d", __func__, k->type);
    172 	}
    173 	if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
    174 	    &digest, &digest_len) != 0)
    175 		fatal("%s: hash_buffer", __func__);
    176 	buffer_free(&b);
    177 	if (len > digest_len)
    178 		fatal("%s: not enough bytes for rawsalt (want %u have %u)",
    179 		    __func__, len, digest_len);
    180 	memcpy(rawsalt, digest, len);
    181 	bzero(digest, digest_len);
    182 	xfree(digest);
    183 }
    184 
    185 /* ASCII an integer [0, 64) for inclusion in a password/salt */
    186 static char
    187 pw_encode64(u_int i64)
    188 {
    189 	const u_char e64[] =
    190 	    "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    191 	return e64[i64 % 64];
    192 }
    193 
    194 /* Generate ASCII salt bytes for user */
    195 static char *
    196 makesalt(u_int want, const char *user)
    197 {
    198 	u_char rawsalt[32];
    199 	static char ret[33];
    200 	u_int i;
    201 
    202 	if (want > sizeof(ret) - 1)
    203 		fatal("%s: want %u", __func__, want);
    204 
    205 	derive_rawsalt(user, rawsalt, sizeof(rawsalt));
    206 	bzero(ret, sizeof(ret));
    207 	for (i = 0; i < want; i++)
    208 		ret[i] = pw_encode64(rawsalt[i]);
    209 	bzero(rawsalt, sizeof(rawsalt));
    210 
    211 	return ret;
    212 }
    213 
    214 /*
    215  * Select the system's default password hashing scheme and generate
    216  * a stable fake salt under it for use by a non-existent account.
    217  * Prevents jpake method being used to infer the validity of accounts.
    218  */
    219 static void
    220 fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme)
    221 {
    222 	char *rounds_s, *style;
    223 	long long rounds;
    224 	login_cap_t *lc;
    225 
    226 
    227 	if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL &&
    228 	    (lc = login_getclass(NULL)) == NULL)
    229 		fatal("%s: login_getclass failed", __func__);
    230 	style = login_getcapstr(lc, "localcipher", NULL, NULL);
    231 	if (style == NULL)
    232 		style = xstrdup("blowfish,6");
    233 	login_close(lc);
    234 
    235 	if ((rounds_s = strchr(style, ',')) != NULL)
    236 		*rounds_s++ = '\0';
    237 	rounds = strtonum(rounds_s, 1, 1<<31, NULL);
    238 
    239 	if (strcmp(style, "md5") == 0) {
    240 		xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user));
    241 		*scheme = xstrdup("md5");
    242 	} else if (strcmp(style, "old") == 0) {
    243 		*salt = xstrdup(makesalt(2, authctxt->user));
    244 		*scheme = xstrdup("crypt");
    245 	} else if (strcmp(style, "newsalt") == 0) {
    246 		rounds = MAX(rounds, 7250);
    247 		rounds = MIN(rounds, (1<<24) - 1);
    248 		xasprintf(salt, "_%c%c%c%c%s",
    249 		    pw_encode64(rounds), pw_encode64(rounds >> 6),
    250 		    pw_encode64(rounds >> 12), pw_encode64(rounds >> 18),
    251 		    makesalt(4, authctxt->user));
    252 		*scheme = xstrdup("crypt-extended");
    253 	} else {
    254 		/* Default to blowfish */
    255 		rounds = MAX(rounds, 3);
    256 		rounds = MIN(rounds, 31);
    257 		xasprintf(salt, "$2a$%02lld$%s", rounds,
    258 		    makesalt(22, authctxt->user));
    259 		*scheme = xstrdup("bcrypt");
    260 	}
    261 	xfree(style);
    262 	debug3("%s: fake %s salt for user %s: %s",
    263 	    __func__, *scheme, authctxt->user, *salt);
    264 }
    265 
    266 /*
    267  * Fetch password hashing scheme, password salt and derive shared secret
    268  * for user. If user does not exist, a fake but stable and user-unique
    269  * salt will be returned.
    270  */
    271 void
    272 auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
    273     char **hash_scheme, char **salt)
    274 {
    275 	char *cp;
    276 	u_char *secret;
    277 	u_int secret_len, salt_len;
    278 
    279 #ifdef JPAKE_DEBUG
    280 	debug3("%s: valid %d pw %.5s...", __func__,
    281 	    authctxt->valid, authctxt->pw->pw_passwd);
    282 #endif
    283 
    284 	*salt = NULL;
    285 	*hash_scheme = NULL;
    286 	if (authctxt->valid) {
    287 		if (strncmp(authctxt->pw->pw_passwd, "$2$", 3) == 0 &&
    288 		    strlen(authctxt->pw->pw_passwd) > 28) {
    289 			/*
    290 			 * old-variant bcrypt:
    291 			 *     "$2$", 2 digit rounds, "$", 22 bytes salt
    292 			 */
    293 			salt_len = 3 + 2 + 1 + 22 + 1;
    294 			*salt = xmalloc(salt_len);
    295 			strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
    296 			*hash_scheme = xstrdup("bcrypt");
    297 		} else if (strncmp(authctxt->pw->pw_passwd, "$2a$", 4) == 0 &&
    298 		    strlen(authctxt->pw->pw_passwd) > 29) {
    299 			/*
    300 			 * current-variant bcrypt:
    301 			 *     "$2a$", 2 digit rounds, "$", 22 bytes salt
    302 			 */
    303 			salt_len = 4 + 2 + 1 + 22 + 1;
    304 			*salt = xmalloc(salt_len);
    305 			strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
    306 			*hash_scheme = xstrdup("bcrypt");
    307 		} else if (strncmp(authctxt->pw->pw_passwd, "$1$", 3) == 0 &&
    308 		    strlen(authctxt->pw->pw_passwd) > 5) {
    309 			/*
    310 			 * md5crypt:
    311 			 *     "$1$", salt until "$"
    312 			 */
    313 			cp = strchr(authctxt->pw->pw_passwd + 3, '$');
    314 			if (cp != NULL) {
    315 				salt_len = (cp - authctxt->pw->pw_passwd) + 1;
    316 				*salt = xmalloc(salt_len);
    317 				strlcpy(*salt, authctxt->pw->pw_passwd,
    318 				    salt_len);
    319 				*hash_scheme = xstrdup("md5crypt");
    320 			}
    321 		} else if (strncmp(authctxt->pw->pw_passwd, "_", 1) == 0 &&
    322 		    strlen(authctxt->pw->pw_passwd) > 9) {
    323 			/*
    324 			 * BSDI extended crypt:
    325 			 *     "_", 4 digits count, 4 chars salt
    326 			 */
    327 			salt_len = 1 + 4 + 4 + 1;
    328 			*salt = xmalloc(salt_len);
    329 			strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
    330 			*hash_scheme = xstrdup("crypt-extended");
    331 		} else if (strlen(authctxt->pw->pw_passwd) == 13  &&
    332 		    valid_crypt_salt(authctxt->pw->pw_passwd[0]) &&
    333 		    valid_crypt_salt(authctxt->pw->pw_passwd[1])) {
    334 			/*
    335 			 * traditional crypt:
    336 			 *     2 chars salt
    337 			 */
    338 			salt_len = 2 + 1;
    339 			*salt = xmalloc(salt_len);
    340 			strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
    341 			*hash_scheme = xstrdup("crypt");
    342 		}
    343 		if (*salt == NULL) {
    344 			debug("%s: unrecognised crypt scheme for user %s",
    345 			    __func__, authctxt->pw->pw_name);
    346 		}
    347 	}
    348 	if (*salt == NULL)
    349 		fake_salt_and_scheme(authctxt, salt, hash_scheme);
    350 
    351 	if (hash_buffer(authctxt->pw->pw_passwd,
    352 	    strlen(authctxt->pw->pw_passwd), EVP_sha256(),
    353 	    &secret, &secret_len) != 0)
    354 		fatal("%s: hash_buffer", __func__);
    355 	if ((*s = BN_bin2bn(secret, secret_len, NULL)) == NULL)
    356 		fatal("%s: BN_bin2bn (secret)", __func__);
    357 #ifdef JPAKE_DEBUG
    358 	debug3("%s: salt = %s (len %u)", __func__,
    359 	    *salt, (u_int)strlen(*salt));
    360 	debug3("%s: scheme = %s", __func__, *hash_scheme);
    361 	JPAKE_DEBUG_BN((*s, "%s: s = ", __func__));
    362 #endif
    363 	bzero(secret, secret_len);
    364 	xfree(secret);
    365 }
    366 
    367 /*
    368  * Begin authentication attempt.
    369  * Note, sets authctxt->postponed while in subprotocol
    370  */
    371 static int
    372 auth2_jpake_start(Authctxt *authctxt)
    373 {
    374 	struct jpake_ctx *pctx = authctxt->jpake_ctx;
    375 	u_char *x3_proof, *x4_proof;
    376 	u_int x3_proof_len, x4_proof_len;
    377 	char *salt, *hash_scheme;
    378 
    379 	debug("%s: start", __func__);
    380 
    381 	PRIVSEP(jpake_step1(pctx->grp,
    382 	    &pctx->server_id, &pctx->server_id_len,
    383 	    &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
    384 	    &x3_proof, &x3_proof_len,
    385 	    &x4_proof, &x4_proof_len));
    386 
    387 	PRIVSEP(auth2_jpake_get_pwdata(authctxt, &pctx->s,
    388 	    &hash_scheme, &salt));
    389 
    390 	if (!use_privsep)
    391 		JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
    392 
    393 	packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1);
    394 	packet_put_cstring(hash_scheme);
    395 	packet_put_cstring(salt);
    396 	packet_put_string(pctx->server_id, pctx->server_id_len);
    397 	packet_put_bignum2(pctx->g_x3);
    398 	packet_put_bignum2(pctx->g_x4);
    399 	packet_put_string(x3_proof, x3_proof_len);
    400 	packet_put_string(x4_proof, x4_proof_len);
    401 	packet_send();
    402 	packet_write_wait();
    403 
    404 	bzero(hash_scheme, strlen(hash_scheme));
    405 	bzero(salt, strlen(salt));
    406 	xfree(hash_scheme);
    407 	xfree(salt);
    408 	bzero(x3_proof, x3_proof_len);
    409 	bzero(x4_proof, x4_proof_len);
    410 	xfree(x3_proof);
    411 	xfree(x4_proof);
    412 
    413 	/* Expect step 1 packet from peer */
    414 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1,
    415 	    input_userauth_jpake_client_step1);
    416 
    417 	authctxt->postponed = 1;
    418 	return 0;
    419 }
    420 
    421 /* ARGSUSED */
    422 static void
    423 input_userauth_jpake_client_step1(int type, u_int32_t seq, void *ctxt)
    424 {
    425 	Authctxt *authctxt = ctxt;
    426 	struct jpake_ctx *pctx = authctxt->jpake_ctx;
    427 	u_char *x1_proof, *x2_proof, *x4_s_proof;
    428 	u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
    429 
    430 	/* Disable this message */
    431 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
    432 
    433 	/* Fetch step 1 values */
    434 	if ((pctx->g_x1 = BN_new()) == NULL ||
    435 	    (pctx->g_x2 = BN_new()) == NULL)
    436 		fatal("%s: BN_new", __func__);
    437 	pctx->client_id = packet_get_string(&pctx->client_id_len);
    438 	packet_get_bignum2(pctx->g_x1);
    439 	packet_get_bignum2(pctx->g_x2);
    440 	x1_proof = packet_get_string(&x1_proof_len);
    441 	x2_proof = packet_get_string(&x2_proof_len);
    442 	packet_check_eom();
    443 
    444 	if (!use_privsep)
    445 		JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
    446 
    447 	PRIVSEP(jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
    448 	    pctx->g_x1, pctx->g_x2, pctx->x4,
    449 	    pctx->client_id, pctx->client_id_len,
    450 	    pctx->server_id, pctx->server_id_len,
    451 	    x1_proof, x1_proof_len,
    452 	    x2_proof, x2_proof_len,
    453 	    &pctx->b,
    454 	    &x4_s_proof, &x4_s_proof_len));
    455 
    456 	bzero(x1_proof, x1_proof_len);
    457 	bzero(x2_proof, x2_proof_len);
    458 	xfree(x1_proof);
    459 	xfree(x2_proof);
    460 
    461 	if (!use_privsep)
    462 		JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
    463 
    464 	/* Send values for step 2 */
    465 	packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2);
    466 	packet_put_bignum2(pctx->b);
    467 	packet_put_string(x4_s_proof, x4_s_proof_len);
    468 	packet_send();
    469 	packet_write_wait();
    470 
    471 	bzero(x4_s_proof, x4_s_proof_len);
    472 	xfree(x4_s_proof);
    473 
    474 	/* Expect step 2 packet from peer */
    475 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2,
    476 	    input_userauth_jpake_client_step2);
    477 }
    478 
    479 /* ARGSUSED */
    480 static void
    481 input_userauth_jpake_client_step2(int type, u_int32_t seq, void *ctxt)
    482 {
    483 	Authctxt *authctxt = ctxt;
    484 	struct jpake_ctx *pctx = authctxt->jpake_ctx;
    485 	u_char *x2_s_proof;
    486 	u_int x2_s_proof_len;
    487 
    488 	/* Disable this message */
    489 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
    490 
    491 	if ((pctx->a = BN_new()) == NULL)
    492 		fatal("%s: BN_new", __func__);
    493 
    494 	/* Fetch step 2 values */
    495 	packet_get_bignum2(pctx->a);
    496 	x2_s_proof = packet_get_string(&x2_s_proof_len);
    497 	packet_check_eom();
    498 
    499 	if (!use_privsep)
    500 		JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
    501 
    502 	/* Derive shared key and calculate confirmation hash */
    503 	PRIVSEP(jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
    504 	    pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
    505 	    pctx->server_id, pctx->server_id_len,
    506 	    pctx->client_id, pctx->client_id_len,
    507 	    session_id2, session_id2_len,
    508 	    x2_s_proof, x2_s_proof_len,
    509 	    &pctx->k,
    510 	    &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len));
    511 
    512 	bzero(x2_s_proof, x2_s_proof_len);
    513 	xfree(x2_s_proof);
    514 
    515 	if (!use_privsep)
    516 		JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
    517 
    518 	/* Send key confirmation proof */
    519 	packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM);
    520 	packet_put_string(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
    521 	packet_send();
    522 	packet_write_wait();
    523 
    524 	/* Expect confirmation from peer */
    525 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM,
    526 	    input_userauth_jpake_client_confirm);
    527 }
    528 
    529 /* ARGSUSED */
    530 static void
    531 input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
    532 {
    533 	Authctxt *authctxt = ctxt;
    534 	struct jpake_ctx *pctx = authctxt->jpake_ctx;
    535 	int authenticated = 0;
    536 
    537 	/* Disable this message */
    538 	dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
    539 
    540 	pctx->h_k_cid_sessid = packet_get_string(&pctx->h_k_cid_sessid_len);
    541 	packet_check_eom();
    542 
    543 	if (!use_privsep)
    544 		JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
    545 
    546 	/* Verify expected confirmation hash */
    547 	if (PRIVSEP(jpake_check_confirm(pctx->k,
    548 	    pctx->client_id, pctx->client_id_len,
    549 	    session_id2, session_id2_len,
    550 	    pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len)) == 1)
    551 		authenticated = authctxt->valid ? 1 : 0;
    552 	else
    553 		debug("%s: confirmation mismatch", __func__);
    554 
    555 	/* done */
    556 	authctxt->postponed = 0;
    557 	jpake_free(authctxt->jpake_ctx);
    558 	authctxt->jpake_ctx = NULL;
    559 	userauth_finish(authctxt, authenticated, method_jpake.name);
    560 }
    561 
    562 #endif /* JPAKE */
    563 
    564