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 */ |
67 |
static void input_userauth_jpake_client_step1(int, u_int32_t, void *); |
68 |
static void input_userauth_jpake_client_step2(int, u_int32_t, void *); |
69 |
static void input_userauth_jpake_client_confirm(int, u_int32_t, void *); |
70 |
|
71 |
static int auth2_jpake_start(Authctxt *); |
72 |
|
73 |
/* import */ |
74 |
extern ServerOptions options; |
75 |
extern u_char *session_id2; |
76 |
extern u_int session_id2_len; |
77 |
|
78 |
/* |
79 |
* Attempt J-PAKE authentication. |
80 |
*/ |
81 |
static int |
82 |
userauth_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 |
|
100 |
Authmethod method_jpake = { |
101 |
"jpake-01@openssh.com", |
102 |
userauth_jpake, |
103 |
&options.zero_knowledge_password_authentication |
104 |
}; |
105 |
|
106 |
/* Clear context and callbacks */ |
107 |
void |
108 |
auth2_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 */ |
121 |
static int |
122 |
valid_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 |
*/ |
139 |
static void |
140 |
derive_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 */ |
186 |
static char |
187 |
pw_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 */ |
195 |
static char * |
196 |
makesalt(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 |
*/ |
219 |
static void |
220 |
fake_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 |
*/ |
271 |
void |
272 |
auth2_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 |
*/ |
371 |
static int |
372 |
auth2_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 */ |
422 |
static void |
423 |
input_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 */ |
480 |
static void |
481 |
input_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 */ |
530 |
static void |
531 |
input_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 |
|