summaryrefslogtreecommitdiff
path: root/auth2-jpake.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2010-01-01 23:53:30 +0000
committerColin Watson <cjwatson@debian.org>2010-01-01 23:53:30 +0000
commitdf03186a4f9e0c2ece398b5c0571cb6263d7a752 (patch)
tree1aab079441dff9615274769b19f2d734ddf508dd /auth2-jpake.c
parent6ad6994c288662fca6949f42bf91fec2aff00bca (diff)
parent99b402ea4c8457b0a3cafff37f5b3410a8dc6476 (diff)
* New upstream release (closes: #536182). Yes, I know 5.3p1 has been out
for a while, but there's no GSSAPI patch available for it yet. - Change the default cipher order to prefer the AES CTR modes and the revised "arcfour256" mode to CBC mode ciphers that are susceptible to CPNI-957037 "Plaintext Recovery Attack Against SSH". - Add countermeasures to mitigate CPNI-957037-style attacks against the SSH protocol's use of CBC-mode ciphers. Upon detection of an invalid packet length or Message Authentication Code, ssh/sshd will continue reading up to the maximum supported packet length rather than immediately terminating the connection. This eliminates most of the known differences in behaviour that leaked information about the plaintext of injected data which formed the basis of this attack (closes: #506115, LP: #379329). - ForceCommand directive now accepts commandline arguments for the internal-sftp server (closes: #524423, LP: #362511). - Add AllowAgentForwarding to available Match keywords list (closes: #540623). - Make ssh(1) send the correct channel number for SSH2_MSG_CHANNEL_SUCCESS and SSH2_MSG_CHANNEL_FAILURE messages to avoid triggering 'Non-public channel' error messages on sshd(8) in openssh-5.1. - Avoid printing 'Non-public channel' warnings in sshd(8), since the ssh(1) has sent incorrect channel numbers since ~2004 (this reverts a behaviour introduced in openssh-5.1; closes: #496017). * Update to GSSAPI patch from http://www.sxw.org.uk/computing/patches/openssh-5.2p1-gsskex-all-20090726.patch, including cascading credentials support (LP: #416958).
Diffstat (limited to 'auth2-jpake.c')
-rw-r--r--auth2-jpake.c557
1 files changed, 557 insertions, 0 deletions
diff --git a/auth2-jpake.c b/auth2-jpake.c
new file mode 100644
index 000000000..efe7ff2a3
--- /dev/null
+++ b/auth2-jpake.c
@@ -0,0 +1,557 @@
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 */
66static void input_userauth_jpake_client_step1(int, u_int32_t, void *);
67static void input_userauth_jpake_client_step2(int, u_int32_t, void *);
68static void input_userauth_jpake_client_confirm(int, u_int32_t, void *);
69
70static int auth2_jpake_start(Authctxt *);
71
72/* import */
73extern ServerOptions options;
74extern u_char *session_id2;
75extern u_int session_id2_len;
76
77/*
78 * Attempt J-PAKE authentication.
79 */
80static int
81userauth_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
99Authmethod method_jpake = {
100 "jpake-01@openssh.com",
101 userauth_jpake,
102 &options.zero_knowledge_password_authentication
103};
104
105/* Clear context and callbacks */
106void
107auth2_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 */
120static int
121valid_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 */
138static void
139derive_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 */
180static char
181pw_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 */
189static char *
190makesalt(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 */
213static void
214fake_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 */
265void
266auth2_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 */
365static int
366auth2_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 */
416static void
417input_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 */
474static void
475input_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 */
524static void
525input_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