auth2-jpake.c revision 192595
133965Sjdp/* $OpenBSD: auth2-jpake.c,v 1.2 2008/11/07 23:34:48 dtucker Exp $ */ 233965Sjdp/* 333965Sjdp * Copyright (c) 2008 Damien Miller. All rights reserved. 433965Sjdp * 533965Sjdp * Permission to use, copy, modify, and distribute this software for any 633965Sjdp * purpose with or without fee is hereby granted, provided that the above 733965Sjdp * copyright notice and this permission notice appear in all copies. 833965Sjdp * 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 "buffer.h" 46#include "auth.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 "jpake.h" 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 */ 66static void input_userauth_jpake_client_step1(int, u_int32_t, void *); 67static void input_userauth_jpake_client_step2(int, u_int32_t, void *); 68static void input_userauth_jpake_client_confirm(int, u_int32_t, void *); 69 70static int auth2_jpake_start(Authctxt *); 71 72/* import */ 73extern ServerOptions options; 74extern u_char *session_id2; 75extern u_int session_id2_len; 76 77/* 78 * Attempt J-PAKE authentication. 79 */ 80static int 81userauth_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 99Authmethod method_jpake = { 100 "jpake-01@openssh.com", 101 userauth_jpake, 102 &options.zero_knowledge_password_authentication 103}; 104 105/* Clear context and callbacks */ 106void 107auth2_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 */ 120static int 121valid_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 */ 138static void 139derive_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 */ 180static char 181pw_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 */ 189static char * 190makesalt(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 */ 213static void 214fake_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 */ 265void 266auth2_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 */ 365static int 366auth2_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 */ 416static void 417input_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 */ 474static void 475input_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 */ 524static void 525input_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 558