]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - crypto/openssh/auth2-jpake.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / crypto / openssh / auth2-jpake.c
1 /* $OpenBSD: auth2-jpake.c,v 1.2 2008/11/07 23:34:48 dtucker 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 "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 */
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