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