auth2-jpake.c revision 197679
1/* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 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 */ 67static void input_userauth_jpake_client_step1(int, u_int32_t, void *); 68static void input_userauth_jpake_client_step2(int, u_int32_t, void *); 69static void input_userauth_jpake_client_confirm(int, u_int32_t, void *); 70 71static int auth2_jpake_start(Authctxt *); 72 73/* import */ 74extern ServerOptions options; 75extern u_char *session_id2; 76extern u_int session_id2_len; 77 78/* 79 * Attempt J-PAKE authentication. 80 */ 81static int 82userauth_jpake(Authctxt *authctxt) 83{ 84 int authenticated = 0; 85 86 packet_check_eom(); 87 88 debug("jpake-01@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 100Authmethod method_jpake = { 101 "jpake-01@openssh.com", 102 userauth_jpake, 103 &options.zero_knowledge_password_authentication 104}; 105 106/* Clear context and callbacks */ 107void 108auth2_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 */ 121static int 122valid_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 */ 139static void 140derive_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 default: 166 fatal("%s: unknown key type %d", __func__, k->type); 167 } 168 if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(), 169 &digest, &digest_len) != 0) 170 fatal("%s: hash_buffer", __func__); 171 buffer_free(&b); 172 if (len > digest_len) 173 fatal("%s: not enough bytes for rawsalt (want %u have %u)", 174 __func__, len, digest_len); 175 memcpy(rawsalt, digest, len); 176 bzero(digest, digest_len); 177 xfree(digest); 178} 179 180/* ASCII an integer [0, 64) for inclusion in a password/salt */ 181static char 182pw_encode64(u_int i64) 183{ 184 const u_char e64[] = 185 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 186 return e64[i64 % 64]; 187} 188 189/* Generate ASCII salt bytes for user */ 190static char * 191makesalt(u_int want, const char *user) 192{ 193 u_char rawsalt[32]; 194 static char ret[33]; 195 u_int i; 196 197 if (want > sizeof(ret) - 1) 198 fatal("%s: want %u", __func__, want); 199 200 derive_rawsalt(user, rawsalt, sizeof(rawsalt)); 201 bzero(ret, sizeof(ret)); 202 for (i = 0; i < want; i++) 203 ret[i] = pw_encode64(rawsalt[i]); 204 bzero(rawsalt, sizeof(rawsalt)); 205 206 return ret; 207} 208 209/* 210 * Select the system's default password hashing scheme and generate 211 * a stable fake salt under it for use by a non-existent account. 212 * Prevents jpake method being used to infer the validity of accounts. 213 */ 214static void 215fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme) 216{ 217 char *rounds_s, *style; 218 long long rounds; 219 login_cap_t *lc; 220 221 222 if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL && 223 (lc = login_getclass(NULL)) == NULL) 224 fatal("%s: login_getclass failed", __func__); 225 style = login_getcapstr(lc, "localcipher", NULL, NULL); 226 if (style == NULL) 227 style = xstrdup("blowfish,6"); 228 login_close(lc); 229 230 if ((rounds_s = strchr(style, ',')) != NULL) 231 *rounds_s++ = '\0'; 232 rounds = strtonum(rounds_s, 1, 1<<31, NULL); 233 234 if (strcmp(style, "md5") == 0) { 235 xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user)); 236 *scheme = xstrdup("md5"); 237 } else if (strcmp(style, "old") == 0) { 238 *salt = xstrdup(makesalt(2, authctxt->user)); 239 *scheme = xstrdup("crypt"); 240 } else if (strcmp(style, "newsalt") == 0) { 241 rounds = MAX(rounds, 7250); 242 rounds = MIN(rounds, (1<<24) - 1); 243 xasprintf(salt, "_%c%c%c%c%s", 244 pw_encode64(rounds), pw_encode64(rounds >> 6), 245 pw_encode64(rounds >> 12), pw_encode64(rounds >> 18), 246 makesalt(4, authctxt->user)); 247 *scheme = xstrdup("crypt-extended"); 248 } else { 249 /* Default to blowfish */ 250 rounds = MAX(rounds, 3); 251 rounds = MIN(rounds, 31); 252 xasprintf(salt, "$2a$%02lld$%s", rounds, 253 makesalt(22, authctxt->user)); 254 *scheme = xstrdup("bcrypt"); 255 } 256 xfree(style); 257 debug3("%s: fake %s salt for user %s: %s", 258 __func__, *scheme, authctxt->user, *salt); 259} 260 261/* 262 * Fetch password hashing scheme, password salt and derive shared secret 263 * for user. If user does not exist, a fake but stable and user-unique 264 * salt will be returned. 265 */ 266void 267auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s, 268 char **hash_scheme, char **salt) 269{ 270 char *cp; 271 u_char *secret; 272 u_int secret_len, salt_len; 273 274#ifdef JPAKE_DEBUG 275 debug3("%s: valid %d pw %.5s...", __func__, 276 authctxt->valid, authctxt->pw->pw_passwd); 277#endif 278 279 *salt = NULL; 280 *hash_scheme = NULL; 281 if (authctxt->valid) { 282 if (strncmp(authctxt->pw->pw_passwd, "$2$", 3) == 0 && 283 strlen(authctxt->pw->pw_passwd) > 28) { 284 /* 285 * old-variant bcrypt: 286 * "$2$", 2 digit rounds, "$", 22 bytes salt 287 */ 288 salt_len = 3 + 2 + 1 + 22 + 1; 289 *salt = xmalloc(salt_len); 290 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len); 291 *hash_scheme = xstrdup("bcrypt"); 292 } else if (strncmp(authctxt->pw->pw_passwd, "$2a$", 4) == 0 && 293 strlen(authctxt->pw->pw_passwd) > 29) { 294 /* 295 * current-variant bcrypt: 296 * "$2a$", 2 digit rounds, "$", 22 bytes salt 297 */ 298 salt_len = 4 + 2 + 1 + 22 + 1; 299 *salt = xmalloc(salt_len); 300 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len); 301 *hash_scheme = xstrdup("bcrypt"); 302 } else if (strncmp(authctxt->pw->pw_passwd, "$1$", 3) == 0 && 303 strlen(authctxt->pw->pw_passwd) > 5) { 304 /* 305 * md5crypt: 306 * "$1$", salt until "$" 307 */ 308 cp = strchr(authctxt->pw->pw_passwd + 3, '$'); 309 if (cp != NULL) { 310 salt_len = (cp - authctxt->pw->pw_passwd) + 1; 311 *salt = xmalloc(salt_len); 312 strlcpy(*salt, authctxt->pw->pw_passwd, 313 salt_len); 314 *hash_scheme = xstrdup("md5crypt"); 315 } 316 } else if (strncmp(authctxt->pw->pw_passwd, "_", 1) == 0 && 317 strlen(authctxt->pw->pw_passwd) > 9) { 318 /* 319 * BSDI extended crypt: 320 * "_", 4 digits count, 4 chars salt 321 */ 322 salt_len = 1 + 4 + 4 + 1; 323 *salt = xmalloc(salt_len); 324 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len); 325 *hash_scheme = xstrdup("crypt-extended"); 326 } else if (strlen(authctxt->pw->pw_passwd) == 13 && 327 valid_crypt_salt(authctxt->pw->pw_passwd[0]) && 328 valid_crypt_salt(authctxt->pw->pw_passwd[1])) { 329 /* 330 * traditional crypt: 331 * 2 chars salt 332 */ 333 salt_len = 2 + 1; 334 *salt = xmalloc(salt_len); 335 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len); 336 *hash_scheme = xstrdup("crypt"); 337 } 338 if (*salt == NULL) { 339 debug("%s: unrecognised crypt scheme for user %s", 340 __func__, authctxt->pw->pw_name); 341 } 342 } 343 if (*salt == NULL) 344 fake_salt_and_scheme(authctxt, salt, hash_scheme); 345 346 if (hash_buffer(authctxt->pw->pw_passwd, 347 strlen(authctxt->pw->pw_passwd), EVP_sha256(), 348 &secret, &secret_len) != 0) 349 fatal("%s: hash_buffer", __func__); 350 if ((*s = BN_bin2bn(secret, secret_len, NULL)) == NULL) 351 fatal("%s: BN_bin2bn (secret)", __func__); 352#ifdef JPAKE_DEBUG 353 debug3("%s: salt = %s (len %u)", __func__, 354 *salt, (u_int)strlen(*salt)); 355 debug3("%s: scheme = %s", __func__, *hash_scheme); 356 JPAKE_DEBUG_BN((*s, "%s: s = ", __func__)); 357#endif 358 bzero(secret, secret_len); 359 xfree(secret); 360} 361 362/* 363 * Begin authentication attempt. 364 * Note, sets authctxt->postponed while in subprotocol 365 */ 366static int 367auth2_jpake_start(Authctxt *authctxt) 368{ 369 struct jpake_ctx *pctx = authctxt->jpake_ctx; 370 u_char *x3_proof, *x4_proof; 371 u_int x3_proof_len, x4_proof_len; 372 char *salt, *hash_scheme; 373 374 debug("%s: start", __func__); 375 376 PRIVSEP(jpake_step1(pctx->grp, 377 &pctx->server_id, &pctx->server_id_len, 378 &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4, 379 &x3_proof, &x3_proof_len, 380 &x4_proof, &x4_proof_len)); 381 382 PRIVSEP(auth2_jpake_get_pwdata(authctxt, &pctx->s, 383 &hash_scheme, &salt)); 384 385 if (!use_privsep) 386 JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__)); 387 388 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1); 389 packet_put_cstring(hash_scheme); 390 packet_put_cstring(salt); 391 packet_put_string(pctx->server_id, pctx->server_id_len); 392 packet_put_bignum2(pctx->g_x3); 393 packet_put_bignum2(pctx->g_x4); 394 packet_put_string(x3_proof, x3_proof_len); 395 packet_put_string(x4_proof, x4_proof_len); 396 packet_send(); 397 packet_write_wait(); 398 399 bzero(hash_scheme, strlen(hash_scheme)); 400 bzero(salt, strlen(salt)); 401 xfree(hash_scheme); 402 xfree(salt); 403 bzero(x3_proof, x3_proof_len); 404 bzero(x4_proof, x4_proof_len); 405 xfree(x3_proof); 406 xfree(x4_proof); 407 408 /* Expect step 1 packet from peer */ 409 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, 410 input_userauth_jpake_client_step1); 411 412 authctxt->postponed = 1; 413 return 0; 414} 415 416/* ARGSUSED */ 417static void 418input_userauth_jpake_client_step1(int type, u_int32_t seq, void *ctxt) 419{ 420 Authctxt *authctxt = ctxt; 421 struct jpake_ctx *pctx = authctxt->jpake_ctx; 422 u_char *x1_proof, *x2_proof, *x4_s_proof; 423 u_int x1_proof_len, x2_proof_len, x4_s_proof_len; 424 425 /* Disable this message */ 426 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL); 427 428 /* Fetch step 1 values */ 429 if ((pctx->g_x1 = BN_new()) == NULL || 430 (pctx->g_x2 = BN_new()) == NULL) 431 fatal("%s: BN_new", __func__); 432 pctx->client_id = packet_get_string(&pctx->client_id_len); 433 packet_get_bignum2(pctx->g_x1); 434 packet_get_bignum2(pctx->g_x2); 435 x1_proof = packet_get_string(&x1_proof_len); 436 x2_proof = packet_get_string(&x2_proof_len); 437 packet_check_eom(); 438 439 if (!use_privsep) 440 JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__)); 441 442 PRIVSEP(jpake_step2(pctx->grp, pctx->s, pctx->g_x3, 443 pctx->g_x1, pctx->g_x2, pctx->x4, 444 pctx->client_id, pctx->client_id_len, 445 pctx->server_id, pctx->server_id_len, 446 x1_proof, x1_proof_len, 447 x2_proof, x2_proof_len, 448 &pctx->b, 449 &x4_s_proof, &x4_s_proof_len)); 450 451 bzero(x1_proof, x1_proof_len); 452 bzero(x2_proof, x2_proof_len); 453 xfree(x1_proof); 454 xfree(x2_proof); 455 456 if (!use_privsep) 457 JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__)); 458 459 /* Send values for step 2 */ 460 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2); 461 packet_put_bignum2(pctx->b); 462 packet_put_string(x4_s_proof, x4_s_proof_len); 463 packet_send(); 464 packet_write_wait(); 465 466 bzero(x4_s_proof, x4_s_proof_len); 467 xfree(x4_s_proof); 468 469 /* Expect step 2 packet from peer */ 470 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, 471 input_userauth_jpake_client_step2); 472} 473 474/* ARGSUSED */ 475static void 476input_userauth_jpake_client_step2(int type, u_int32_t seq, void *ctxt) 477{ 478 Authctxt *authctxt = ctxt; 479 struct jpake_ctx *pctx = authctxt->jpake_ctx; 480 u_char *x2_s_proof; 481 u_int x2_s_proof_len; 482 483 /* Disable this message */ 484 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL); 485 486 if ((pctx->a = BN_new()) == NULL) 487 fatal("%s: BN_new", __func__); 488 489 /* Fetch step 2 values */ 490 packet_get_bignum2(pctx->a); 491 x2_s_proof = packet_get_string(&x2_s_proof_len); 492 packet_check_eom(); 493 494 if (!use_privsep) 495 JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__)); 496 497 /* Derive shared key and calculate confirmation hash */ 498 PRIVSEP(jpake_key_confirm(pctx->grp, pctx->s, pctx->a, 499 pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2, 500 pctx->server_id, pctx->server_id_len, 501 pctx->client_id, pctx->client_id_len, 502 session_id2, session_id2_len, 503 x2_s_proof, x2_s_proof_len, 504 &pctx->k, 505 &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len)); 506 507 bzero(x2_s_proof, x2_s_proof_len); 508 xfree(x2_s_proof); 509 510 if (!use_privsep) 511 JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__)); 512 513 /* Send key confirmation proof */ 514 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM); 515 packet_put_string(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len); 516 packet_send(); 517 packet_write_wait(); 518 519 /* Expect confirmation from peer */ 520 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, 521 input_userauth_jpake_client_confirm); 522} 523 524/* ARGSUSED */ 525static void 526input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt) 527{ 528 Authctxt *authctxt = ctxt; 529 struct jpake_ctx *pctx = authctxt->jpake_ctx; 530 int authenticated = 0; 531 532 /* Disable this message */ 533 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL); 534 535 pctx->h_k_cid_sessid = packet_get_string(&pctx->h_k_cid_sessid_len); 536 packet_check_eom(); 537 538 if (!use_privsep) 539 JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__)); 540 541 /* Verify expected confirmation hash */ 542 if (PRIVSEP(jpake_check_confirm(pctx->k, 543 pctx->client_id, pctx->client_id_len, 544 session_id2, session_id2_len, 545 pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len)) == 1) 546 authenticated = authctxt->valid ? 1 : 0; 547 else 548 debug("%s: confirmation mismatch", __func__); 549 550 /* done */ 551 authctxt->postponed = 0; 552 jpake_free(authctxt->jpake_ctx); 553 authctxt->jpake_ctx = NULL; 554 userauth_finish(authctxt, authenticated, method_jpake.name); 555} 556 557#endif /* JPAKE */ 558 559