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