No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / external / bsd / openssh / dist / auth2-jpake.c
blob803777a3b268f9491ade2f49830ef9d7dd9fae48
1 /* $NetBSD: auth2-jpake.c,v 1.1.1.2 2009/12/27 01:06:49 christos Exp $ */
2 /* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 djm Exp $ */
3 /*
4 * Copyright (c) 2008 Damien Miller. All rights reserved.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 * Server side of zero-knowledge password auth using J-PAKE protocol
21 * as described in:
23 * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
24 * 16th Workshop on Security Protocols, Cambridge, April 2008
26 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
29 #ifdef JPAKE
31 #include <sys/types.h>
32 #include <sys/param.h>
34 #include <pwd.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <login_cap.h>
39 #include <openssl/bn.h>
40 #include <openssl/evp.h>
42 #include "xmalloc.h"
43 #include "ssh2.h"
44 #include "key.h"
45 #include "hostfile.h"
46 #include "auth.h"
47 #include "buffer.h"
48 #include "packet.h"
49 #include "dispatch.h"
50 #include "log.h"
51 #include "servconf.h"
52 #include "auth-options.h"
53 #include "canohost.h"
54 #ifdef GSSAPI
55 #include "ssh-gss.h"
56 #endif
57 #include "monitor_wrap.h"
59 #include "schnorr.h"
60 #include "jpake.h"
63 * XXX options->permit_empty_passwd (at the moment, they will be refused
64 * anyway because they will mismatch on fake salt.
67 /* Dispatch handlers */
68 static void input_userauth_jpake_client_step1(int, u_int32_t, void *);
69 static void input_userauth_jpake_client_step2(int, u_int32_t, void *);
70 static void input_userauth_jpake_client_confirm(int, u_int32_t, void *);
72 static int auth2_jpake_start(Authctxt *);
74 /* import */
75 extern ServerOptions options;
76 extern u_char *session_id2;
77 extern u_int session_id2_len;
80 * Attempt J-PAKE authentication.
82 static int
83 userauth_jpake(Authctxt *authctxt)
85 int authenticated = 0;
87 packet_check_eom();
89 debug("jpake-01@openssh.com requested");
91 if (authctxt->user != NULL) {
92 if (authctxt->jpake_ctx == NULL)
93 authctxt->jpake_ctx = jpake_new();
94 if (options.zero_knowledge_password_authentication)
95 authenticated = auth2_jpake_start(authctxt);
98 return authenticated;
101 Authmethod method_jpake = {
102 "jpake-01@openssh.com",
103 userauth_jpake,
104 &options.zero_knowledge_password_authentication
107 /* Clear context and callbacks */
108 void
109 auth2_jpake_stop(Authctxt *authctxt)
111 /* unregister callbacks */
112 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
113 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
114 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
115 if (authctxt->jpake_ctx != NULL) {
116 jpake_free(authctxt->jpake_ctx);
117 authctxt->jpake_ctx = NULL;
121 /* Returns 1 if 'c' is a valid crypt(3) salt character, 0 otherwise */
122 static int
123 valid_crypt_salt(int c)
125 if (c >= 'A' && c <= 'Z')
126 return 1;
127 if (c >= 'a' && c <= 'z')
128 return 1;
129 if (c >= '.' && c <= '9')
130 return 1;
131 return 0;
135 * Derive fake salt as H(username || first_private_host_key)
136 * This provides relatively stable fake salts for non-existent
137 * users and avoids the jpake method becoming an account validity
138 * oracle.
140 static void
141 derive_rawsalt(const char *username, u_char *rawsalt, u_int len)
143 u_char *digest;
144 u_int digest_len;
145 Buffer b;
146 Key *k;
148 buffer_init(&b);
149 buffer_put_cstring(&b, username);
150 if ((k = get_hostkey_by_index(0)) == NULL ||
151 (k->flags & KEY_FLAG_EXT))
152 fatal("%s: no hostkeys", __func__);
153 switch (k->type) {
154 case KEY_RSA1:
155 case KEY_RSA:
156 if (k->rsa->p == NULL || k->rsa->q == NULL)
157 fatal("%s: RSA key missing p and/or q", __func__);
158 buffer_put_bignum2(&b, k->rsa->p);
159 buffer_put_bignum2(&b, k->rsa->q);
160 break;
161 case KEY_DSA:
162 if (k->dsa->priv_key == NULL)
163 fatal("%s: DSA key missing priv_key", __func__);
164 buffer_put_bignum2(&b, k->dsa->priv_key);
165 break;
166 default:
167 fatal("%s: unknown key type %d", __func__, k->type);
169 if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
170 &digest, &digest_len) != 0)
171 fatal("%s: hash_buffer", __func__);
172 buffer_free(&b);
173 if (len > digest_len)
174 fatal("%s: not enough bytes for rawsalt (want %u have %u)",
175 __func__, len, digest_len);
176 memcpy(rawsalt, digest, len);
177 bzero(digest, digest_len);
178 xfree(digest);
181 /* ASCII an integer [0, 64) for inclusion in a password/salt */
182 static char
183 pw_encode64(u_int i64)
185 const u_char e64[] =
186 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
187 return e64[i64 % 64];
190 /* Generate ASCII salt bytes for user */
191 static char *
192 makesalt(u_int want, const char *user)
194 u_char rawsalt[32];
195 static char ret[33];
196 u_int i;
198 if (want > sizeof(ret) - 1)
199 fatal("%s: want %u", __func__, want);
201 derive_rawsalt(user, rawsalt, sizeof(rawsalt));
202 bzero(ret, sizeof(ret));
203 for (i = 0; i < want; i++)
204 ret[i] = pw_encode64(rawsalt[i]);
205 bzero(rawsalt, sizeof(rawsalt));
207 return ret;
211 * Select the system's default password hashing scheme and generate
212 * a stable fake salt under it for use by a non-existent account.
213 * Prevents jpake method being used to infer the validity of accounts.
215 static void
216 fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme)
218 char *rounds_s, *style;
219 long long rounds;
220 login_cap_t *lc;
223 if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL &&
224 (lc = login_getclass(NULL)) == NULL)
225 fatal("%s: login_getclass failed", __func__);
226 style = login_getcapstr(lc, "localcipher", NULL, NULL);
227 if (style == NULL)
228 style = xstrdup("blowfish,6");
229 login_close(lc);
231 if ((rounds_s = strchr(style, ',')) != NULL)
232 *rounds_s++ = '\0';
233 rounds = strtonum(rounds_s, 1, 1<<31, NULL);
235 if (strcmp(style, "md5") == 0) {
236 xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user));
237 *scheme = xstrdup("md5");
238 } else if (strcmp(style, "old") == 0) {
239 *salt = xstrdup(makesalt(2, authctxt->user));
240 *scheme = xstrdup("crypt");
241 } else if (strcmp(style, "newsalt") == 0) {
242 rounds = MAX(rounds, 7250);
243 rounds = MIN(rounds, (1<<24) - 1);
244 xasprintf(salt, "_%c%c%c%c%s",
245 pw_encode64(rounds), pw_encode64(rounds >> 6),
246 pw_encode64(rounds >> 12), pw_encode64(rounds >> 18),
247 makesalt(4, authctxt->user));
248 *scheme = xstrdup("crypt-extended");
249 } else {
250 /* Default to blowfish */
251 rounds = MAX(rounds, 3);
252 rounds = MIN(rounds, 31);
253 xasprintf(salt, "$2a$%02lld$%s", rounds,
254 makesalt(22, authctxt->user));
255 *scheme = xstrdup("bcrypt");
257 xfree(style);
258 debug3("%s: fake %s salt for user %s: %s",
259 __func__, *scheme, authctxt->user, *salt);
263 * Fetch password hashing scheme, password salt and derive shared secret
264 * for user. If user does not exist, a fake but stable and user-unique
265 * salt will be returned.
267 void
268 auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
269 char **hash_scheme, char **salt)
271 char *cp;
272 u_char *secret;
273 u_int secret_len, salt_len;
275 #ifdef JPAKE_DEBUG
276 debug3("%s: valid %d pw %.5s...", __func__,
277 authctxt->valid, authctxt->pw->pw_passwd);
278 #endif
280 *salt = NULL;
281 *hash_scheme = NULL;
282 if (authctxt->valid) {
283 if (strncmp(authctxt->pw->pw_passwd, "$2$", 3) == 0 &&
284 strlen(authctxt->pw->pw_passwd) > 28) {
286 * old-variant bcrypt:
287 * "$2$", 2 digit rounds, "$", 22 bytes salt
289 salt_len = 3 + 2 + 1 + 22 + 1;
290 *salt = xmalloc(salt_len);
291 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
292 *hash_scheme = xstrdup("bcrypt");
293 } else if (strncmp(authctxt->pw->pw_passwd, "$2a$", 4) == 0 &&
294 strlen(authctxt->pw->pw_passwd) > 29) {
296 * current-variant bcrypt:
297 * "$2a$", 2 digit rounds, "$", 22 bytes salt
299 salt_len = 4 + 2 + 1 + 22 + 1;
300 *salt = xmalloc(salt_len);
301 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
302 *hash_scheme = xstrdup("bcrypt");
303 } else if (strncmp(authctxt->pw->pw_passwd, "$1$", 3) == 0 &&
304 strlen(authctxt->pw->pw_passwd) > 5) {
306 * md5crypt:
307 * "$1$", salt until "$"
309 cp = strchr(authctxt->pw->pw_passwd + 3, '$');
310 if (cp != NULL) {
311 salt_len = (cp - authctxt->pw->pw_passwd) + 1;
312 *salt = xmalloc(salt_len);
313 strlcpy(*salt, authctxt->pw->pw_passwd,
314 salt_len);
315 *hash_scheme = xstrdup("md5crypt");
317 } else if (strncmp(authctxt->pw->pw_passwd, "_", 1) == 0 &&
318 strlen(authctxt->pw->pw_passwd) > 9) {
320 * BSDI extended crypt:
321 * "_", 4 digits count, 4 chars salt
323 salt_len = 1 + 4 + 4 + 1;
324 *salt = xmalloc(salt_len);
325 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
326 *hash_scheme = xstrdup("crypt-extended");
327 } else if (strlen(authctxt->pw->pw_passwd) == 13 &&
328 valid_crypt_salt(authctxt->pw->pw_passwd[0]) &&
329 valid_crypt_salt(authctxt->pw->pw_passwd[1])) {
331 * traditional crypt:
332 * 2 chars salt
334 salt_len = 2 + 1;
335 *salt = xmalloc(salt_len);
336 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
337 *hash_scheme = xstrdup("crypt");
339 if (*salt == NULL) {
340 debug("%s: unrecognised crypt scheme for user %s",
341 __func__, authctxt->pw->pw_name);
344 if (*salt == NULL)
345 fake_salt_and_scheme(authctxt, salt, hash_scheme);
347 if (hash_buffer(authctxt->pw->pw_passwd,
348 strlen(authctxt->pw->pw_passwd), EVP_sha256(),
349 &secret, &secret_len) != 0)
350 fatal("%s: hash_buffer", __func__);
351 if ((*s = BN_bin2bn(secret, secret_len, NULL)) == NULL)
352 fatal("%s: BN_bin2bn (secret)", __func__);
353 #ifdef JPAKE_DEBUG
354 debug3("%s: salt = %s (len %u)", __func__,
355 *salt, (u_int)strlen(*salt));
356 debug3("%s: scheme = %s", __func__, *hash_scheme);
357 JPAKE_DEBUG_BN((*s, "%s: s = ", __func__));
358 #endif
359 bzero(secret, secret_len);
360 xfree(secret);
364 * Begin authentication attempt.
365 * Note, sets authctxt->postponed while in subprotocol
367 static int
368 auth2_jpake_start(Authctxt *authctxt)
370 struct jpake_ctx *pctx = authctxt->jpake_ctx;
371 u_char *x3_proof, *x4_proof;
372 u_int x3_proof_len, x4_proof_len;
373 char *salt, *hash_scheme;
375 debug("%s: start", __func__);
377 PRIVSEP(jpake_step1(pctx->grp,
378 &pctx->server_id, &pctx->server_id_len,
379 &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
380 &x3_proof, &x3_proof_len,
381 &x4_proof, &x4_proof_len));
383 PRIVSEP(auth2_jpake_get_pwdata(authctxt, &pctx->s,
384 &hash_scheme, &salt));
386 if (!use_privsep)
387 JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
389 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1);
390 packet_put_cstring(hash_scheme);
391 packet_put_cstring(salt);
392 packet_put_string(pctx->server_id, pctx->server_id_len);
393 packet_put_bignum2(pctx->g_x3);
394 packet_put_bignum2(pctx->g_x4);
395 packet_put_string(x3_proof, x3_proof_len);
396 packet_put_string(x4_proof, x4_proof_len);
397 packet_send();
398 packet_write_wait();
400 bzero(hash_scheme, strlen(hash_scheme));
401 bzero(salt, strlen(salt));
402 xfree(hash_scheme);
403 xfree(salt);
404 bzero(x3_proof, x3_proof_len);
405 bzero(x4_proof, x4_proof_len);
406 xfree(x3_proof);
407 xfree(x4_proof);
409 /* Expect step 1 packet from peer */
410 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1,
411 input_userauth_jpake_client_step1);
413 authctxt->postponed = 1;
414 return 0;
417 /* ARGSUSED */
418 static void
419 input_userauth_jpake_client_step1(int type, u_int32_t seq, void *ctxt)
421 Authctxt *authctxt = ctxt;
422 struct jpake_ctx *pctx = authctxt->jpake_ctx;
423 u_char *x1_proof, *x2_proof, *x4_s_proof;
424 u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
426 /* Disable this message */
427 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
429 /* Fetch step 1 values */
430 if ((pctx->g_x1 = BN_new()) == NULL ||
431 (pctx->g_x2 = BN_new()) == NULL)
432 fatal("%s: BN_new", __func__);
433 pctx->client_id = packet_get_string(&pctx->client_id_len);
434 packet_get_bignum2(pctx->g_x1);
435 packet_get_bignum2(pctx->g_x2);
436 x1_proof = packet_get_string(&x1_proof_len);
437 x2_proof = packet_get_string(&x2_proof_len);
438 packet_check_eom();
440 if (!use_privsep)
441 JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
443 PRIVSEP(jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
444 pctx->g_x1, pctx->g_x2, pctx->x4,
445 pctx->client_id, pctx->client_id_len,
446 pctx->server_id, pctx->server_id_len,
447 x1_proof, x1_proof_len,
448 x2_proof, x2_proof_len,
449 &pctx->b,
450 &x4_s_proof, &x4_s_proof_len));
452 bzero(x1_proof, x1_proof_len);
453 bzero(x2_proof, x2_proof_len);
454 xfree(x1_proof);
455 xfree(x2_proof);
457 if (!use_privsep)
458 JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
460 /* Send values for step 2 */
461 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2);
462 packet_put_bignum2(pctx->b);
463 packet_put_string(x4_s_proof, x4_s_proof_len);
464 packet_send();
465 packet_write_wait();
467 bzero(x4_s_proof, x4_s_proof_len);
468 xfree(x4_s_proof);
470 /* Expect step 2 packet from peer */
471 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2,
472 input_userauth_jpake_client_step2);
475 /* ARGSUSED */
476 static void
477 input_userauth_jpake_client_step2(int type, u_int32_t seq, void *ctxt)
479 Authctxt *authctxt = ctxt;
480 struct jpake_ctx *pctx = authctxt->jpake_ctx;
481 u_char *x2_s_proof;
482 u_int x2_s_proof_len;
484 /* Disable this message */
485 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
487 if ((pctx->a = BN_new()) == NULL)
488 fatal("%s: BN_new", __func__);
490 /* Fetch step 2 values */
491 packet_get_bignum2(pctx->a);
492 x2_s_proof = packet_get_string(&x2_s_proof_len);
493 packet_check_eom();
495 if (!use_privsep)
496 JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
498 /* Derive shared key and calculate confirmation hash */
499 PRIVSEP(jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
500 pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
501 pctx->server_id, pctx->server_id_len,
502 pctx->client_id, pctx->client_id_len,
503 session_id2, session_id2_len,
504 x2_s_proof, x2_s_proof_len,
505 &pctx->k,
506 &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len));
508 bzero(x2_s_proof, x2_s_proof_len);
509 xfree(x2_s_proof);
511 if (!use_privsep)
512 JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
514 /* Send key confirmation proof */
515 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM);
516 packet_put_string(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
517 packet_send();
518 packet_write_wait();
520 /* Expect confirmation from peer */
521 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM,
522 input_userauth_jpake_client_confirm);
525 /* ARGSUSED */
526 static void
527 input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
529 Authctxt *authctxt = ctxt;
530 struct jpake_ctx *pctx = authctxt->jpake_ctx;
531 int authenticated = 0;
533 /* Disable this message */
534 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
536 pctx->h_k_cid_sessid = packet_get_string(&pctx->h_k_cid_sessid_len);
537 packet_check_eom();
539 if (!use_privsep)
540 JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
542 /* Verify expected confirmation hash */
543 if (PRIVSEP(jpake_check_confirm(pctx->k,
544 pctx->client_id, pctx->client_id_len,
545 session_id2, session_id2_len,
546 pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len)) == 1)
547 authenticated = authctxt->valid ? 1 : 0;
548 else
549 debug("%s: confirmation mismatch", __func__);
551 /* done */
552 authctxt->postponed = 0;
553 jpake_free(authctxt->jpake_ctx);
554 authctxt->jpake_ctx = NULL;
555 userauth_finish(authctxt, authenticated, method_jpake.name);
558 #endif /* JPAKE */