Annotation of src/usr.bin/ssh/auth2-jpake.c, Revision 1.5
1.5 ! djm 1: /* $OpenBSD: auth2-jpake.c,v 1.4 2010/08/31 11:54:45 djm Exp $ */
1.1 djm 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:
1.2 dtucker 28: #ifdef JPAKE
29:
1.1 djm 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:
1.3 djm 58: #include "schnorr.h"
1.1 djm 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@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@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);
1.4 djm 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));
1.1 djm 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: /*
1.3 djm 368: * Begin authentication attempt.
1.1 djm 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;
1.5 ! djm 559: userauth_finish(authctxt, authenticated, method_jpake.name, NULL);
1.1 djm 560: }
561:
562: #endif /* JPAKE */
563: