Annotation of src/usr.bin/ssh/auth2-jpake.c, Revision 1.2
1.2 ! dtucker 1: /* $OpenBSD: auth2-jpake.c,v 1.1 2008/11/04 08:22:12 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:
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 */
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: