1/* $OpenBSD: auth2-jpake.c,v 1.6 2013/05/17 00:13:13 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	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));
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	free(digest);
183}
184
185/* ASCII an integer [0, 64) for inclusion in a password/salt */
186static char
187pw_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 */
195static char *
196makesalt(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 */
219static void
220fake_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	free(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 */
271void
272auth2_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	free(secret);
365}
366
367/*
368 * Begin authentication attempt.
369 * Note, sets authctxt->postponed while in subprotocol
370 */
371static int
372auth2_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	free(hash_scheme);
407	free(salt);
408	bzero(x3_proof, x3_proof_len);
409	bzero(x4_proof, x4_proof_len);
410	free(x3_proof);
411	free(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 */
422static void
423input_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	free(x1_proof);
459	free(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	free(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 */
480static void
481input_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	free(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 */
530static void
531input_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;
559	userauth_finish(authctxt, authenticated, method_jpake.name, NULL);
560}
561
562#endif /* JPAKE */
563
564