diff options
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | Makefile.in | 6 | ||||
-rw-r--r-- | auth.h | 6 | ||||
-rw-r--r-- | auth2-jpake.c | 557 | ||||
-rw-r--r-- | auth2.c | 12 | ||||
-rw-r--r-- | jpake.c | 604 | ||||
-rw-r--r-- | jpake.h | 134 | ||||
-rw-r--r-- | monitor.c | 227 | ||||
-rw-r--r-- | monitor.h | 9 | ||||
-rw-r--r-- | monitor_wrap.c | 167 | ||||
-rw-r--r-- | monitor_wrap.h | 22 | ||||
-rw-r--r-- | readconf.c | 18 | ||||
-rw-r--r-- | readconf.h | 3 | ||||
-rw-r--r-- | schnorr.c | 407 | ||||
-rw-r--r-- | servconf.c | 20 | ||||
-rw-r--r-- | servconf.h | 4 | ||||
-rw-r--r-- | ssh2.h | 9 | ||||
-rw-r--r-- | ssh_config.5 | 15 | ||||
-rw-r--r-- | sshconnect2.c | 303 | ||||
-rw-r--r-- | sshd_config.5 | 18 |
20 files changed, 2537 insertions, 25 deletions
@@ -7,6 +7,25 @@ | |||
7 | [auth.c] | 7 | [auth.c] |
8 | need unistd.h for close() prototype | 8 | need unistd.h for close() prototype |
9 | (ID sync only) | 9 | (ID sync only) |
10 | - djm@cvs.openbsd.org 2008/11/04 08:22:13 | ||
11 | [auth.h auth2.c monitor.c monitor.h monitor_wrap.c monitor_wrap.h] | ||
12 | [readconf.c readconf.h servconf.c servconf.h ssh2.h ssh_config.5] | ||
13 | [sshconnect2.c sshd_config.5 jpake.c jpake.h schnorr.c auth2-jpake.c] | ||
14 | [Makefile.in] | ||
15 | Add support for an experimental zero-knowledge password authentication | ||
16 | method using the J-PAKE protocol described in F. Hao, P. Ryan, | ||
17 | "Password Authenticated Key Exchange by Juggling", 16th Workshop on | ||
18 | Security Protocols, Cambridge, April 2008. | ||
19 | |||
20 | This method allows password-based authentication without exposing | ||
21 | the password to the server. Instead, the client and server exchange | ||
22 | cryptographic proofs to demonstrate of knowledge of the password while | ||
23 | revealing nothing useful to an attacker or compromised endpoint. | ||
24 | |||
25 | This is experimental, work-in-progress code and is presently | ||
26 | compiled-time disabled (turn on -DJPAKE in Makefile.inc). | ||
27 | |||
28 | "just commit it. It isn't too intrusive." deraadt@ | ||
10 | 29 | ||
11 | 20081103 | 30 | 20081103 |
12 | - OpenBSD CVS Sync | 31 | - OpenBSD CVS Sync |
@@ -4857,4 +4876,4 @@ | |||
4857 | OpenServer 6 and add osr5bigcrypt support so when someone migrates | 4876 | OpenServer 6 and add osr5bigcrypt support so when someone migrates |
4858 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ | 4877 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ |
4859 | 4878 | ||
4860 | $Id: ChangeLog,v 1.5129 2008/11/05 05:12:54 djm Exp $ | 4879 | $Id: ChangeLog,v 1.5130 2008/11/05 05:20:46 djm Exp $ |
diff --git a/Makefile.in b/Makefile.in index c1b7ab5a6..312b8d2b1 100644 --- a/Makefile.in +++ b/Makefile.in | |||
@@ -1,4 +1,4 @@ | |||
1 | # $Id: Makefile.in,v 1.297 2008/07/08 14:21:12 djm Exp $ | 1 | # $Id: Makefile.in,v 1.298 2008/11/05 05:20:46 djm Exp $ |
2 | 2 | ||
3 | # uncomment if you run a non bourne compatable shell. Ie. csh | 3 | # uncomment if you run a non bourne compatable shell. Ie. csh |
4 | #SHELL = @SH@ | 4 | #SHELL = @SH@ |
@@ -71,7 +71,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \ | |||
71 | atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ | 71 | atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ |
72 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ | 72 | monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ |
73 | kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ | 73 | kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ |
74 | entropy.o scard-opensc.o gss-genr.o umac.o | 74 | entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o |
75 | 75 | ||
76 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ | 76 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ |
77 | sshconnect.o sshconnect1.o sshconnect2.o mux.o | 77 | sshconnect.o sshconnect1.o sshconnect2.o mux.o |
@@ -81,7 +81,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ | |||
81 | auth.o auth1.o auth2.o auth-options.o session.o \ | 81 | auth.o auth1.o auth2.o auth-options.o session.o \ |
82 | auth-chall.o auth2-chall.o groupaccess.o \ | 82 | auth-chall.o auth2-chall.o groupaccess.o \ |
83 | auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ | 83 | auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ |
84 | auth2-none.o auth2-passwd.o auth2-pubkey.o \ | 84 | auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \ |
85 | monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ | 85 | monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ |
86 | auth-krb5.o \ | 86 | auth-krb5.o \ |
87 | auth2-gss.o gss-serv.o gss-serv-krb5.o \ | 87 | auth2-gss.o gss-serv.o gss-serv-krb5.o \ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth.h,v 1.61 2008/07/02 12:03:51 dtucker Exp $ */ | 1 | /* $OpenBSD: auth.h,v 1.62 2008/11/04 08:22:12 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
@@ -59,6 +59,7 @@ struct Authctxt { | |||
59 | struct passwd *pw; /* set if 'valid' */ | 59 | struct passwd *pw; /* set if 'valid' */ |
60 | char *style; | 60 | char *style; |
61 | void *kbdintctxt; | 61 | void *kbdintctxt; |
62 | void *jpake_ctx; | ||
62 | #ifdef BSD_AUTH | 63 | #ifdef BSD_AUTH |
63 | auth_session_t *as; | 64 | auth_session_t *as; |
64 | #endif | 65 | #endif |
@@ -156,6 +157,9 @@ int bsdauth_respond(void *, u_int, char **); | |||
156 | int skey_query(void *, char **, char **, u_int *, char ***, u_int **); | 157 | int skey_query(void *, char **, char **, u_int *, char ***, u_int **); |
157 | int skey_respond(void *, u_int, char **); | 158 | int skey_respond(void *, u_int, char **); |
158 | 159 | ||
160 | void auth2_jpake_get_pwdata(Authctxt *, BIGNUM **, char **, char **); | ||
161 | void auth2_jpake_stop(Authctxt *); | ||
162 | |||
159 | int allowed_user(struct passwd *); | 163 | int allowed_user(struct passwd *); |
160 | struct passwd * getpwnamallow(const char *user); | 164 | struct passwd * getpwnamallow(const char *user); |
161 | 165 | ||
diff --git a/auth2-jpake.c b/auth2-jpake.c new file mode 100644 index 000000000..0029ec26b --- /dev/null +++ b/auth2-jpake.c | |||
@@ -0,0 +1,557 @@ | |||
1 | /* $OpenBSD: auth2-jpake.c,v 1.1 2008/11/04 08:22:12 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 | #include <sys/types.h> | ||
29 | #include <sys/param.h> | ||
30 | |||
31 | #include <pwd.h> | ||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | #include <login_cap.h> | ||
35 | |||
36 | #include <openssl/bn.h> | ||
37 | #include <openssl/evp.h> | ||
38 | |||
39 | #include "xmalloc.h" | ||
40 | #include "ssh2.h" | ||
41 | #include "key.h" | ||
42 | #include "hostfile.h" | ||
43 | #include "buffer.h" | ||
44 | #include "auth.h" | ||
45 | #include "packet.h" | ||
46 | #include "dispatch.h" | ||
47 | #include "log.h" | ||
48 | #include "servconf.h" | ||
49 | #include "auth-options.h" | ||
50 | #include "canohost.h" | ||
51 | #ifdef GSSAPI | ||
52 | #include "ssh-gss.h" | ||
53 | #endif | ||
54 | #include "monitor_wrap.h" | ||
55 | |||
56 | #include "jpake.h" | ||
57 | |||
58 | #ifdef JPAKE | ||
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 | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth2.c,v 1.119 2008/07/04 23:30:16 djm Exp $ */ | 1 | /* $OpenBSD: auth2.c,v 1.120 2008/11/04 08:22:12 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -71,6 +71,9 @@ extern Authmethod method_hostbased; | |||
71 | #ifdef GSSAPI | 71 | #ifdef GSSAPI |
72 | extern Authmethod method_gssapi; | 72 | extern Authmethod method_gssapi; |
73 | #endif | 73 | #endif |
74 | #ifdef JPAKE | ||
75 | extern Authmethod method_jpake; | ||
76 | #endif | ||
74 | 77 | ||
75 | Authmethod *authmethods[] = { | 78 | Authmethod *authmethods[] = { |
76 | &method_none, | 79 | &method_none, |
@@ -78,6 +81,9 @@ Authmethod *authmethods[] = { | |||
78 | #ifdef GSSAPI | 81 | #ifdef GSSAPI |
79 | &method_gssapi, | 82 | &method_gssapi, |
80 | #endif | 83 | #endif |
84 | #ifdef JPAKE | ||
85 | &method_jpake, | ||
86 | #endif | ||
81 | &method_passwd, | 87 | &method_passwd, |
82 | &method_kbdint, | 88 | &method_kbdint, |
83 | &method_hostbased, | 89 | &method_hostbased, |
@@ -257,8 +263,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt) | |||
257 | } | 263 | } |
258 | /* reset state */ | 264 | /* reset state */ |
259 | auth2_challenge_stop(authctxt); | 265 | auth2_challenge_stop(authctxt); |
266 | #ifdef JPAKE | ||
267 | auth2_jpake_stop(authctxt); | ||
268 | #endif | ||
260 | 269 | ||
261 | #ifdef GSSAPI | 270 | #ifdef GSSAPI |
271 | /* XXX move to auth2_gssapi_stop() */ | ||
262 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); | 272 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); |
263 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); | 273 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); |
264 | #endif | 274 | #endif |
diff --git a/jpake.c b/jpake.c new file mode 100644 index 000000000..565f2e255 --- /dev/null +++ b/jpake.c | |||
@@ -0,0 +1,604 @@ | |||
1 | /* $OpenBSD: jpake.c,v 1.1 2008/11/04 08:22:12 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 | * Shared components 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 | #include "includes.h" | ||
29 | |||
30 | #include <sys/types.h> | ||
31 | |||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | #include <stdarg.h> | ||
35 | |||
36 | #include <openssl/bn.h> | ||
37 | #include <openssl/evp.h> | ||
38 | |||
39 | #include "xmalloc.h" | ||
40 | #include "ssh2.h" | ||
41 | #include "key.h" | ||
42 | #include "hostfile.h" | ||
43 | #include "auth.h" | ||
44 | #include "buffer.h" | ||
45 | #include "packet.h" | ||
46 | #include "dispatch.h" | ||
47 | #include "log.h" | ||
48 | |||
49 | #include "jpake.h" | ||
50 | |||
51 | #ifdef JPAKE | ||
52 | |||
53 | /* RFC3526 group 5, 1536 bits */ | ||
54 | #define JPAKE_GROUP_G "2" | ||
55 | #define JPAKE_GROUP_P \ | ||
56 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74" \ | ||
57 | "020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437" \ | ||
58 | "4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ | ||
59 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05" \ | ||
60 | "98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB" \ | ||
61 | "9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" | ||
62 | |||
63 | struct jpake_group * | ||
64 | jpake_default_group(void) | ||
65 | { | ||
66 | struct jpake_group *ret; | ||
67 | |||
68 | ret = xmalloc(sizeof(*ret)); | ||
69 | ret->p = ret->q = ret->g = NULL; | ||
70 | if (BN_hex2bn(&ret->p, JPAKE_GROUP_P) == 0 || | ||
71 | BN_hex2bn(&ret->g, JPAKE_GROUP_G) == 0) | ||
72 | fatal("%s: BN_hex2bn", __func__); | ||
73 | /* Subgroup order is p/2 (p is a safe prime) */ | ||
74 | if ((ret->q = BN_new()) == NULL) | ||
75 | fatal("%s: BN_new", __func__); | ||
76 | if (BN_rshift1(ret->q, ret->p) != 1) | ||
77 | fatal("%s: BN_rshift1", __func__); | ||
78 | |||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Generate uniformly distributed random number in range (1, high). | ||
84 | * Return number on success, NULL on failure. | ||
85 | */ | ||
86 | BIGNUM * | ||
87 | bn_rand_range_gt_one(const BIGNUM *high) | ||
88 | { | ||
89 | BIGNUM *r, *tmp; | ||
90 | int success = -1; | ||
91 | |||
92 | if ((tmp = BN_new()) == NULL) { | ||
93 | error("%s: BN_new", __func__); | ||
94 | return NULL; | ||
95 | } | ||
96 | if ((r = BN_new()) == NULL) { | ||
97 | error("%s: BN_new failed", __func__); | ||
98 | goto out; | ||
99 | } | ||
100 | if (BN_set_word(tmp, 2) != 1) { | ||
101 | error("%s: BN_set_word(tmp, 2)", __func__); | ||
102 | goto out; | ||
103 | } | ||
104 | if (BN_sub(tmp, high, tmp) == -1) { | ||
105 | error("%s: BN_sub failed (tmp = high - 2)", __func__); | ||
106 | goto out; | ||
107 | } | ||
108 | if (BN_rand_range(r, tmp) == -1) { | ||
109 | error("%s: BN_rand_range failed", __func__); | ||
110 | goto out; | ||
111 | } | ||
112 | if (BN_set_word(tmp, 2) != 1) { | ||
113 | error("%s: BN_set_word(tmp, 2)", __func__); | ||
114 | goto out; | ||
115 | } | ||
116 | if (BN_add(r, r, tmp) == -1) { | ||
117 | error("%s: BN_add failed (r = r + 2)", __func__); | ||
118 | goto out; | ||
119 | } | ||
120 | success = 0; | ||
121 | out: | ||
122 | BN_clear_free(tmp); | ||
123 | if (success == 0) | ||
124 | return r; | ||
125 | BN_clear_free(r); | ||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Hash contents of buffer 'b' with hash 'md'. Returns 0 on success, | ||
131 | * with digest via 'digestp' (caller to free) and length via 'lenp'. | ||
132 | * Returns -1 on failure. | ||
133 | */ | ||
134 | int | ||
135 | hash_buffer(const u_char *buf, u_int len, const EVP_MD *md, | ||
136 | u_char **digestp, u_int *lenp) | ||
137 | { | ||
138 | u_char digest[EVP_MAX_MD_SIZE]; | ||
139 | u_int digest_len; | ||
140 | EVP_MD_CTX evp_md_ctx; | ||
141 | int success = -1; | ||
142 | |||
143 | EVP_MD_CTX_init(&evp_md_ctx); | ||
144 | |||
145 | if (EVP_DigestInit_ex(&evp_md_ctx, md, NULL) != 1) { | ||
146 | error("%s: EVP_DigestInit_ex", __func__); | ||
147 | goto out; | ||
148 | } | ||
149 | if (EVP_DigestUpdate(&evp_md_ctx, buf, len) != 1) { | ||
150 | error("%s: EVP_DigestUpdate", __func__); | ||
151 | goto out; | ||
152 | } | ||
153 | if (EVP_DigestFinal_ex(&evp_md_ctx, digest, &digest_len) != 1) { | ||
154 | error("%s: EVP_DigestFinal_ex", __func__); | ||
155 | goto out; | ||
156 | } | ||
157 | *digestp = xmalloc(digest_len); | ||
158 | *lenp = digest_len; | ||
159 | memcpy(*digestp, digest, *lenp); | ||
160 | success = 0; | ||
161 | out: | ||
162 | EVP_MD_CTX_cleanup(&evp_md_ctx); | ||
163 | bzero(digest, sizeof(digest)); | ||
164 | digest_len = 0; | ||
165 | return success; | ||
166 | } | ||
167 | |||
168 | /* print formatted string followed by bignum */ | ||
169 | void | ||
170 | jpake_debug3_bn(const BIGNUM *n, const char *fmt, ...) | ||
171 | { | ||
172 | char *out, *h; | ||
173 | va_list args; | ||
174 | |||
175 | out = NULL; | ||
176 | va_start(args, fmt); | ||
177 | vasprintf(&out, fmt, args); | ||
178 | va_end(args); | ||
179 | if (out == NULL) | ||
180 | fatal("%s: vasprintf failed", __func__); | ||
181 | |||
182 | if (n == NULL) | ||
183 | debug3("%s(null)", out); | ||
184 | else { | ||
185 | h = BN_bn2hex(n); | ||
186 | debug3("%s0x%s", out, h); | ||
187 | free(h); | ||
188 | } | ||
189 | free(out); | ||
190 | } | ||
191 | |||
192 | /* print formatted string followed by buffer contents in hex */ | ||
193 | void | ||
194 | jpake_debug3_buf(const u_char *buf, u_int len, const char *fmt, ...) | ||
195 | { | ||
196 | char *out, h[65]; | ||
197 | u_int i, j; | ||
198 | va_list args; | ||
199 | |||
200 | out = NULL; | ||
201 | va_start(args, fmt); | ||
202 | vasprintf(&out, fmt, args); | ||
203 | va_end(args); | ||
204 | if (out == NULL) | ||
205 | fatal("%s: vasprintf failed", __func__); | ||
206 | |||
207 | debug3("%s length %u%s", out, len, buf == NULL ? " (null)" : ""); | ||
208 | free(out); | ||
209 | if (buf == NULL) | ||
210 | return; | ||
211 | |||
212 | *h = '\0'; | ||
213 | for (i = j = 0; i < len; i++) { | ||
214 | snprintf(h + j, sizeof(h) - j, "%02x", buf[i]); | ||
215 | j += 2; | ||
216 | if (j >= sizeof(h) - 1 || i == len - 1) { | ||
217 | debug3(" %s", h); | ||
218 | *h = '\0'; | ||
219 | j = 0; | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | |||
224 | struct jpake_ctx * | ||
225 | jpake_new(void) | ||
226 | { | ||
227 | struct jpake_ctx *ret; | ||
228 | |||
229 | ret = xcalloc(1, sizeof(*ret)); | ||
230 | |||
231 | ret->grp = jpake_default_group(); | ||
232 | |||
233 | ret->s = ret->k = NULL; | ||
234 | ret->x1 = ret->x2 = ret->x3 = ret->x4 = NULL; | ||
235 | ret->g_x1 = ret->g_x2 = ret->g_x3 = ret->g_x4 = NULL; | ||
236 | ret->a = ret->b = NULL; | ||
237 | |||
238 | ret->client_id = ret->server_id = NULL; | ||
239 | ret->h_k_cid_sessid = ret->h_k_sid_sessid = NULL; | ||
240 | |||
241 | debug3("%s: alloc %p", __func__, ret); | ||
242 | |||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | |||
247 | void | ||
248 | jpake_free(struct jpake_ctx *pctx) | ||
249 | { | ||
250 | debug3("%s: free %p", __func__, pctx); | ||
251 | |||
252 | #define JPAKE_BN_CLEAR_FREE(v) \ | ||
253 | do { \ | ||
254 | if ((v) != NULL) { \ | ||
255 | BN_clear_free(v); \ | ||
256 | (v) = NULL; \ | ||
257 | } \ | ||
258 | } while (0) | ||
259 | #define JPAKE_BUF_CLEAR_FREE(v, l) \ | ||
260 | do { \ | ||
261 | if ((v) != NULL) { \ | ||
262 | bzero((v), (l)); \ | ||
263 | xfree(v); \ | ||
264 | (v) = NULL; \ | ||
265 | (l) = 0; \ | ||
266 | } \ | ||
267 | } while (0) | ||
268 | |||
269 | JPAKE_BN_CLEAR_FREE(pctx->s); | ||
270 | JPAKE_BN_CLEAR_FREE(pctx->k); | ||
271 | JPAKE_BN_CLEAR_FREE(pctx->x1); | ||
272 | JPAKE_BN_CLEAR_FREE(pctx->x2); | ||
273 | JPAKE_BN_CLEAR_FREE(pctx->x3); | ||
274 | JPAKE_BN_CLEAR_FREE(pctx->x4); | ||
275 | JPAKE_BN_CLEAR_FREE(pctx->g_x1); | ||
276 | JPAKE_BN_CLEAR_FREE(pctx->g_x2); | ||
277 | JPAKE_BN_CLEAR_FREE(pctx->g_x3); | ||
278 | JPAKE_BN_CLEAR_FREE(pctx->g_x4); | ||
279 | JPAKE_BN_CLEAR_FREE(pctx->a); | ||
280 | JPAKE_BN_CLEAR_FREE(pctx->b); | ||
281 | |||
282 | JPAKE_BUF_CLEAR_FREE(pctx->client_id, pctx->client_id_len); | ||
283 | JPAKE_BUF_CLEAR_FREE(pctx->server_id, pctx->server_id_len); | ||
284 | JPAKE_BUF_CLEAR_FREE(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len); | ||
285 | JPAKE_BUF_CLEAR_FREE(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len); | ||
286 | |||
287 | #undef JPAKE_BN_CLEAR_FREE | ||
288 | #undef JPAKE_BUF_CLEAR_FREE | ||
289 | |||
290 | bzero(pctx, sizeof(pctx)); | ||
291 | xfree(pctx); | ||
292 | } | ||
293 | |||
294 | /* dump entire jpake_ctx. NB. includes private values! */ | ||
295 | void | ||
296 | jpake_dump(struct jpake_ctx *pctx, const char *fmt, ...) | ||
297 | { | ||
298 | char *out; | ||
299 | va_list args; | ||
300 | |||
301 | out = NULL; | ||
302 | va_start(args, fmt); | ||
303 | vasprintf(&out, fmt, args); | ||
304 | va_end(args); | ||
305 | if (out == NULL) | ||
306 | fatal("%s: vasprintf failed", __func__); | ||
307 | |||
308 | debug3("%s: %s (ctx at %p)", __func__, out, pctx); | ||
309 | if (pctx == NULL) { | ||
310 | free(out); | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | #define JPAKE_DUMP_BN(a) do { \ | ||
315 | if ((a) != NULL) \ | ||
316 | JPAKE_DEBUG_BN(((a), "%s = ", #a)); \ | ||
317 | } while (0) | ||
318 | #define JPAKE_DUMP_BUF(a, b) do { \ | ||
319 | if ((a) != NULL) \ | ||
320 | JPAKE_DEBUG_BUF((a, b, "%s", #a)); \ | ||
321 | } while (0) | ||
322 | |||
323 | JPAKE_DUMP_BN(pctx->s); | ||
324 | JPAKE_DUMP_BN(pctx->k); | ||
325 | JPAKE_DUMP_BN(pctx->x1); | ||
326 | JPAKE_DUMP_BN(pctx->x2); | ||
327 | JPAKE_DUMP_BN(pctx->x3); | ||
328 | JPAKE_DUMP_BN(pctx->x4); | ||
329 | JPAKE_DUMP_BN(pctx->g_x1); | ||
330 | JPAKE_DUMP_BN(pctx->g_x2); | ||
331 | JPAKE_DUMP_BN(pctx->g_x3); | ||
332 | JPAKE_DUMP_BN(pctx->g_x4); | ||
333 | JPAKE_DUMP_BN(pctx->a); | ||
334 | JPAKE_DUMP_BN(pctx->b); | ||
335 | |||
336 | JPAKE_DUMP_BUF(pctx->client_id, pctx->client_id_len); | ||
337 | JPAKE_DUMP_BUF(pctx->server_id, pctx->server_id_len); | ||
338 | JPAKE_DUMP_BUF(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len); | ||
339 | JPAKE_DUMP_BUF(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len); | ||
340 | |||
341 | debug3("%s: %s done", __func__, out); | ||
342 | free(out); | ||
343 | } | ||
344 | |||
345 | /* Shared parts of step 1 exchange calculation */ | ||
346 | void | ||
347 | jpake_step1(struct jpake_group *grp, | ||
348 | u_char **id, u_int *id_len, | ||
349 | BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2, | ||
350 | u_char **priv1_proof, u_int *priv1_proof_len, | ||
351 | u_char **priv2_proof, u_int *priv2_proof_len) | ||
352 | { | ||
353 | BN_CTX *bn_ctx; | ||
354 | |||
355 | if ((bn_ctx = BN_CTX_new()) == NULL) | ||
356 | fatal("%s: BN_CTX_new", __func__); | ||
357 | |||
358 | /* Random nonce to prevent replay */ | ||
359 | *id = xmalloc(KZP_ID_LEN); | ||
360 | *id_len = KZP_ID_LEN; | ||
361 | arc4random_buf(*id, *id_len); | ||
362 | |||
363 | /* | ||
364 | * x1/x3 is a random element of Zq | ||
365 | * x2/x4 is a random element of Z*q | ||
366 | * We also exclude [1] from x1/x3 candidates and [0, 1] from | ||
367 | * x2/x4 candiates to avoid possible degeneracy (i.e. g^0, g^1). | ||
368 | */ | ||
369 | if ((*priv1 = bn_rand_range_gt_one(grp->q)) == NULL || | ||
370 | (*priv2 = bn_rand_range_gt_one(grp->q)) == NULL) | ||
371 | fatal("%s: bn_rand_range_gt_one", __func__); | ||
372 | |||
373 | /* | ||
374 | * client: g_x1 = g^x1 mod p / server: g_x3 = g^x3 mod p | ||
375 | * client: g_x2 = g^x2 mod p / server: g_x4 = g^x4 mod p | ||
376 | */ | ||
377 | if ((*g_priv1 = BN_new()) == NULL || | ||
378 | (*g_priv2 = BN_new()) == NULL) | ||
379 | fatal("%s: BN_new", __func__); | ||
380 | if (BN_mod_exp(*g_priv1, grp->g, *priv1, grp->p, bn_ctx) == -1) | ||
381 | fatal("%s: BN_mod_exp", __func__); | ||
382 | if (BN_mod_exp(*g_priv2, grp->g, *priv2, grp->p, bn_ctx) == -1) | ||
383 | fatal("%s: BN_mod_exp", __func__); | ||
384 | |||
385 | /* Generate proofs for holding x1/x3 and x2/x4 */ | ||
386 | if (schnorr_sign(grp->p, grp->q, grp->g, | ||
387 | *priv1, *g_priv1, *id, *id_len, | ||
388 | priv1_proof, priv1_proof_len) != 0) | ||
389 | fatal("%s: schnorr_sign", __func__); | ||
390 | if (schnorr_sign(grp->p, grp->q, grp->g, | ||
391 | *priv2, *g_priv2, *id, *id_len, | ||
392 | priv2_proof, priv2_proof_len) != 0) | ||
393 | fatal("%s: schnorr_sign", __func__); | ||
394 | |||
395 | BN_CTX_free(bn_ctx); | ||
396 | } | ||
397 | |||
398 | /* Shared parts of step 2 exchange calculation */ | ||
399 | void | ||
400 | jpake_step2(struct jpake_group *grp, BIGNUM *s, | ||
401 | BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2, | ||
402 | const u_char *theirid, u_int theirid_len, | ||
403 | const u_char *myid, u_int myid_len, | ||
404 | const u_char *theirpub1_proof, u_int theirpub1_proof_len, | ||
405 | const u_char *theirpub2_proof, u_int theirpub2_proof_len, | ||
406 | BIGNUM **newpub, | ||
407 | u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len) | ||
408 | { | ||
409 | BN_CTX *bn_ctx; | ||
410 | BIGNUM *tmp, *exponent; | ||
411 | |||
412 | /* Validate peer's step 1 values */ | ||
413 | if (BN_cmp(theirpub1, BN_value_one()) <= 0) | ||
414 | fatal("%s: theirpub1 <= 1", __func__); | ||
415 | if (BN_cmp(theirpub2, BN_value_one()) <= 0) | ||
416 | fatal("%s: theirpub2 <= 1", __func__); | ||
417 | |||
418 | if (schnorr_verify(grp->p, grp->q, grp->g, theirpub1, | ||
419 | theirid, theirid_len, theirpub1_proof, theirpub1_proof_len) != 1) | ||
420 | fatal("%s: schnorr_verify theirpub1 failed", __func__); | ||
421 | if (schnorr_verify(grp->p, grp->q, grp->g, theirpub2, | ||
422 | theirid, theirid_len, theirpub2_proof, theirpub2_proof_len) != 1) | ||
423 | fatal("%s: schnorr_verify theirpub2 failed", __func__); | ||
424 | |||
425 | if ((bn_ctx = BN_CTX_new()) == NULL) | ||
426 | fatal("%s: BN_CTX_new", __func__); | ||
427 | |||
428 | if ((*newpub = BN_new()) == NULL || | ||
429 | (tmp = BN_new()) == NULL || | ||
430 | (exponent = BN_new()) == NULL) | ||
431 | fatal("%s: BN_new", __func__); | ||
432 | |||
433 | /* | ||
434 | * client: exponent = x2 * s mod p | ||
435 | * server: exponent = x4 * s mod p | ||
436 | */ | ||
437 | if (BN_mod_mul(exponent, mypriv2, s, grp->q, bn_ctx) != 1) | ||
438 | fatal("%s: BN_mod_mul (exponent = mypriv2 * s mod p)", | ||
439 | __func__); | ||
440 | |||
441 | /* | ||
442 | * client: tmp = g^(x1 + x3 + x4) mod p | ||
443 | * server: tmp = g^(x1 + x2 + x3) mod p | ||
444 | */ | ||
445 | if (BN_mod_mul(tmp, mypub1, theirpub1, grp->p, bn_ctx) != 1) | ||
446 | fatal("%s: BN_mod_mul (tmp = mypub1 * theirpub1 mod p)", | ||
447 | __func__); | ||
448 | if (BN_mod_mul(tmp, tmp, theirpub2, grp->p, bn_ctx) != 1) | ||
449 | fatal("%s: BN_mod_mul (tmp = tmp * theirpub2 mod p)", __func__); | ||
450 | |||
451 | /* | ||
452 | * client: a = tmp^exponent = g^((x1+x3+x4) * x2 * s) mod p | ||
453 | * server: b = tmp^exponent = g^((x1+x2+x3) * x4 * s) mod p | ||
454 | */ | ||
455 | if (BN_mod_exp(*newpub, tmp, exponent, grp->p, bn_ctx) != 1) | ||
456 | fatal("%s: BN_mod_mul (newpub = tmp^exponent mod p)", __func__); | ||
457 | |||
458 | JPAKE_DEBUG_BN((tmp, "%s: tmp = ", __func__)); | ||
459 | JPAKE_DEBUG_BN((exponent, "%s: exponent = ", __func__)); | ||
460 | |||
461 | /* Note the generator here is 'tmp', not g */ | ||
462 | if (schnorr_sign(grp->p, grp->q, tmp, exponent, *newpub, | ||
463 | myid, myid_len, | ||
464 | newpub_exponent_proof, newpub_exponent_proof_len) != 0) | ||
465 | fatal("%s: schnorr_sign newpub", __func__); | ||
466 | |||
467 | BN_clear_free(tmp); /* XXX stash for later use? */ | ||
468 | BN_clear_free(exponent); /* XXX stash for later use? (yes, in conf) */ | ||
469 | |||
470 | BN_CTX_free(bn_ctx); | ||
471 | } | ||
472 | |||
473 | /* Confirmation hash calculation */ | ||
474 | void | ||
475 | jpake_confirm_hash(const BIGNUM *k, | ||
476 | const u_char *endpoint_id, u_int endpoint_id_len, | ||
477 | const u_char *sess_id, u_int sess_id_len, | ||
478 | u_char **confirm_hash, u_int *confirm_hash_len) | ||
479 | { | ||
480 | Buffer b; | ||
481 | |||
482 | /* | ||
483 | * Calculate confirmation proof: | ||
484 | * client: H(k || client_id || session_id) | ||
485 | * server: H(k || server_id || session_id) | ||
486 | */ | ||
487 | buffer_init(&b); | ||
488 | buffer_put_bignum2(&b, k); | ||
489 | buffer_put_string(&b, endpoint_id, endpoint_id_len); | ||
490 | buffer_put_string(&b, sess_id, sess_id_len); | ||
491 | if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(), | ||
492 | confirm_hash, confirm_hash_len) != 0) | ||
493 | fatal("%s: hash_buffer", __func__); | ||
494 | buffer_free(&b); | ||
495 | } | ||
496 | |||
497 | /* Shared parts of key derivation and confirmation calculation */ | ||
498 | void | ||
499 | jpake_key_confirm(struct jpake_group *grp, BIGNUM *s, BIGNUM *step2_val, | ||
500 | BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2, | ||
501 | BIGNUM *theirpub1, BIGNUM *theirpub2, | ||
502 | const u_char *my_id, u_int my_id_len, | ||
503 | const u_char *their_id, u_int their_id_len, | ||
504 | const u_char *sess_id, u_int sess_id_len, | ||
505 | const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len, | ||
506 | BIGNUM **k, | ||
507 | u_char **confirm_hash, u_int *confirm_hash_len) | ||
508 | { | ||
509 | BN_CTX *bn_ctx; | ||
510 | BIGNUM *tmp; | ||
511 | |||
512 | if ((bn_ctx = BN_CTX_new()) == NULL) | ||
513 | fatal("%s: BN_CTX_new", __func__); | ||
514 | if ((tmp = BN_new()) == NULL || | ||
515 | (*k = BN_new()) == NULL) | ||
516 | fatal("%s: BN_new", __func__); | ||
517 | |||
518 | /* Validate step 2 values */ | ||
519 | if (BN_cmp(step2_val, BN_value_one()) <= 0) | ||
520 | fatal("%s: step2_val <= 1", __func__); | ||
521 | |||
522 | /* | ||
523 | * theirpriv2_s_proof is calculated with a different generator: | ||
524 | * tmp = g^(mypriv1+mypriv2+theirpub1) = g^mypub1*g^mypub2*g^theirpub1 | ||
525 | * Calculate it here so we can check the signature. | ||
526 | */ | ||
527 | if (BN_mod_mul(tmp, mypub1, mypub2, grp->p, bn_ctx) != 1) | ||
528 | fatal("%s: BN_mod_mul (tmp = mypub1 * mypub2 mod p)", __func__); | ||
529 | if (BN_mod_mul(tmp, tmp, theirpub1, grp->p, bn_ctx) != 1) | ||
530 | fatal("%s: BN_mod_mul (tmp = tmp * theirpub1 mod p)", __func__); | ||
531 | |||
532 | JPAKE_DEBUG_BN((tmp, "%s: tmp = ", __func__)); | ||
533 | |||
534 | if (schnorr_verify(grp->p, grp->q, tmp, step2_val, | ||
535 | their_id, their_id_len, | ||
536 | theirpriv2_s_proof, theirpriv2_s_proof_len) != 1) | ||
537 | fatal("%s: schnorr_verify theirpriv2_s_proof failed", __func__); | ||
538 | |||
539 | /* | ||
540 | * Derive shared key: | ||
541 | * client: k = (b / g^(x2*x4*s))^x2 = g^((x1+x3)*x2*x4*s) | ||
542 | * server: k = (a / g^(x2*x4*s))^x4 = g^((x1+x3)*x2*x4*s) | ||
543 | * | ||
544 | * Computed as: | ||
545 | * client: k = (g_x4^(q - (x2 * s)) * b)^x2 mod p | ||
546 | * server: k = (g_x2^(q - (x4 * s)) * b)^x4 mod p | ||
547 | */ | ||
548 | if (BN_mul(tmp, mypriv2, s, bn_ctx) != 1) | ||
549 | fatal("%s: BN_mul (tmp = mypriv2 * s)", __func__); | ||
550 | if (BN_mod_sub(tmp, grp->q, tmp, grp->q, bn_ctx) != 1) | ||
551 | fatal("%s: BN_mod_sub (tmp = q - tmp mod q)", __func__); | ||
552 | if (BN_mod_exp(tmp, theirpub2, tmp, grp->p, bn_ctx) != 1) | ||
553 | fatal("%s: BN_mod_exp (tmp = theirpub2^tmp) mod p", __func__); | ||
554 | if (BN_mod_mul(tmp, tmp, step2_val, grp->p, bn_ctx) != 1) | ||
555 | fatal("%s: BN_mod_mul (tmp = tmp * step2_val) mod p", __func__); | ||
556 | if (BN_mod_exp(*k, tmp, mypriv2, grp->p, bn_ctx) != 1) | ||
557 | fatal("%s: BN_mod_exp (k = tmp^mypriv2) mod p", __func__); | ||
558 | |||
559 | BN_CTX_free(bn_ctx); | ||
560 | BN_clear_free(tmp); | ||
561 | |||
562 | jpake_confirm_hash(*k, my_id, my_id_len, sess_id, sess_id_len, | ||
563 | confirm_hash, confirm_hash_len); | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | * Calculate and check confirmation hash from peer. Returns 1 on success | ||
568 | * 0 on failure/mismatch. | ||
569 | */ | ||
570 | int | ||
571 | jpake_check_confirm(const BIGNUM *k, | ||
572 | const u_char *peer_id, u_int peer_id_len, | ||
573 | const u_char *sess_id, u_int sess_id_len, | ||
574 | const u_char *peer_confirm_hash, u_int peer_confirm_hash_len) | ||
575 | { | ||
576 | u_char *expected_confirm_hash; | ||
577 | u_int expected_confirm_hash_len; | ||
578 | int success = 0; | ||
579 | |||
580 | /* Calculate and verify expected confirmation hash */ | ||
581 | jpake_confirm_hash(k, peer_id, peer_id_len, sess_id, sess_id_len, | ||
582 | &expected_confirm_hash, &expected_confirm_hash_len); | ||
583 | |||
584 | JPAKE_DEBUG_BUF((expected_confirm_hash, expected_confirm_hash_len, | ||
585 | "%s: expected confirm hash", __func__)); | ||
586 | JPAKE_DEBUG_BUF((peer_confirm_hash, peer_confirm_hash_len, | ||
587 | "%s: received confirm hash", __func__)); | ||
588 | |||
589 | if (peer_confirm_hash_len != expected_confirm_hash_len) | ||
590 | error("%s: confirmation length mismatch (my %u them %u)", | ||
591 | __func__, expected_confirm_hash_len, peer_confirm_hash_len); | ||
592 | else if (memcmp(peer_confirm_hash, expected_confirm_hash, | ||
593 | expected_confirm_hash_len) == 0) | ||
594 | success = 1; | ||
595 | bzero(expected_confirm_hash, expected_confirm_hash_len); | ||
596 | xfree(expected_confirm_hash); | ||
597 | debug3("%s: success = %d", __func__, success); | ||
598 | return success; | ||
599 | } | ||
600 | |||
601 | /* XXX main() function with tests */ | ||
602 | |||
603 | #endif /* JPAKE */ | ||
604 | |||
diff --git a/jpake.h b/jpake.h new file mode 100644 index 000000000..a3d800cd3 --- /dev/null +++ b/jpake.h | |||
@@ -0,0 +1,134 @@ | |||
1 | /* $OpenBSD: jpake.h,v 1.1 2008/11/04 08:22: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 | #ifndef JPAKE_H | ||
19 | #define JPAKE_H | ||
20 | |||
21 | #include <sys/types.h> | ||
22 | |||
23 | #include <openssl/bn.h> | ||
24 | |||
25 | /* Set JPAKE_DEBUG in CFLAGS for privacy-violating debugging */ | ||
26 | #ifndef JPAKE_DEBUG | ||
27 | # define JPAKE_DEBUG_BN(a) | ||
28 | # define JPAKE_DEBUG_BUF(a) | ||
29 | # define JPAKE_DEBUG_CTX(a) | ||
30 | #else | ||
31 | # define JPAKE_DEBUG_BN(a) jpake_debug3_bn a | ||
32 | # define JPAKE_DEBUG_BUF(a) jpake_debug3_buf a | ||
33 | # define JPAKE_DEBUG_CTX(a) jpake_dump a | ||
34 | #endif /* SCHNORR_DEBUG */ | ||
35 | |||
36 | struct jpake_group { | ||
37 | BIGNUM *p, *q, *g; | ||
38 | }; | ||
39 | |||
40 | #define KZP_ID_LEN 16 /* Length of client and server IDs */ | ||
41 | |||
42 | struct jpake_ctx { | ||
43 | /* Parameters */ | ||
44 | struct jpake_group *grp; | ||
45 | |||
46 | /* Private values shared by client and server */ | ||
47 | BIGNUM *s; /* Secret (salted, crypted password) */ | ||
48 | BIGNUM *k; /* Derived key */ | ||
49 | |||
50 | /* Client private values (NULL for server) */ | ||
51 | BIGNUM *x1; /* random in Zq */ | ||
52 | BIGNUM *x2; /* random in Z*q */ | ||
53 | |||
54 | /* Server private values (NULL for server) */ | ||
55 | BIGNUM *x3; /* random in Zq */ | ||
56 | BIGNUM *x4; /* random in Z*q */ | ||
57 | |||
58 | /* Step 1: C->S */ | ||
59 | u_char *client_id; /* Anti-replay nonce */ | ||
60 | u_int client_id_len; | ||
61 | BIGNUM *g_x1; /* g^x1 */ | ||
62 | BIGNUM *g_x2; /* g^x2 */ | ||
63 | |||
64 | /* Step 1: S->C */ | ||
65 | u_char *server_id; /* Anti-replay nonce */ | ||
66 | u_int server_id_len; | ||
67 | BIGNUM *g_x3; /* g^x3 */ | ||
68 | BIGNUM *g_x4; /* g^x4 */ | ||
69 | |||
70 | /* Step 2: C->S */ | ||
71 | BIGNUM *a; /* g^((x1+x3+x4)*x2*s) */ | ||
72 | |||
73 | /* Step 2: S->C */ | ||
74 | BIGNUM *b; /* g^((x1+x2+x3)*x4*s) */ | ||
75 | |||
76 | /* Confirmation: C->S */ | ||
77 | u_char *h_k_cid_sessid; /* H(k || client_id || session_id) */ | ||
78 | u_int h_k_cid_sessid_len; | ||
79 | |||
80 | /* Confirmation: S->C */ | ||
81 | u_char *h_k_sid_sessid; /* H(k || server_id || session_id) */ | ||
82 | u_int h_k_sid_sessid_len; | ||
83 | }; | ||
84 | |||
85 | /* jpake.c */ | ||
86 | struct jpake_group *jpake_default_group(void); | ||
87 | BIGNUM *bn_rand_range_gt_one(const BIGNUM *high); | ||
88 | int hash_buffer(const u_char *, u_int, const EVP_MD *, u_char **, u_int *); | ||
89 | void jpake_debug3_bn(const BIGNUM *, const char *, ...) | ||
90 | __attribute__((__nonnull__ (2))) | ||
91 | __attribute__((format(printf, 2, 3))); | ||
92 | void jpake_debug3_buf(const u_char *, u_int, const char *, ...) | ||
93 | __attribute__((__nonnull__ (3))) | ||
94 | __attribute__((format(printf, 3, 4))); | ||
95 | void jpake_dump(struct jpake_ctx *, const char *, ...) | ||
96 | __attribute__((__nonnull__ (2))) | ||
97 | __attribute__((format(printf, 2, 3))); | ||
98 | struct jpake_ctx *jpake_new(void); | ||
99 | void jpake_free(struct jpake_ctx *); | ||
100 | |||
101 | void jpake_step1(struct jpake_group *, u_char **, u_int *, | ||
102 | BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **, | ||
103 | u_char **, u_int *, u_char **, u_int *); | ||
104 | |||
105 | void jpake_step2(struct jpake_group *, BIGNUM *, | ||
106 | BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, | ||
107 | const u_char *, u_int, const u_char *, u_int, | ||
108 | const u_char *, u_int, const u_char *, u_int, | ||
109 | BIGNUM **, u_char **, u_int *); | ||
110 | |||
111 | void jpake_confirm_hash(const BIGNUM *, | ||
112 | const u_char *, u_int, | ||
113 | const u_char *, u_int, | ||
114 | u_char **, u_int *); | ||
115 | |||
116 | void jpake_key_confirm(struct jpake_group *, BIGNUM *, BIGNUM *, | ||
117 | BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, | ||
118 | const u_char *, u_int, const u_char *, u_int, | ||
119 | const u_char *, u_int, const u_char *, u_int, | ||
120 | BIGNUM **, u_char **, u_int *); | ||
121 | |||
122 | int jpake_check_confirm(const BIGNUM *, const u_char *, u_int, | ||
123 | const u_char *, u_int, const u_char *, u_int); | ||
124 | |||
125 | /* schnorr.c */ | ||
126 | int schnorr_sign(const BIGNUM *, const BIGNUM *, const BIGNUM *, | ||
127 | const BIGNUM *, const BIGNUM *, const u_char *, u_int , | ||
128 | u_char **, u_int *); | ||
129 | int schnorr_verify(const BIGNUM *, const BIGNUM *, const BIGNUM *, | ||
130 | const BIGNUM *, const u_char *, u_int, | ||
131 | const u_char *, u_int); | ||
132 | |||
133 | #endif /* JPAKE_H */ | ||
134 | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: monitor.c,v 1.99 2008/07/10 18:08:11 markus Exp $ */ | 1 | /* $OpenBSD: monitor.c,v 1.100 2008/11/04 08:22:13 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | 3 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> |
4 | * Copyright 2002 Markus Friedl <markus@openbsd.org> | 4 | * Copyright 2002 Markus Friedl <markus@openbsd.org> |
@@ -87,6 +87,7 @@ | |||
87 | #include "misc.h" | 87 | #include "misc.h" |
88 | #include "compat.h" | 88 | #include "compat.h" |
89 | #include "ssh2.h" | 89 | #include "ssh2.h" |
90 | #include "jpake.h" | ||
90 | 91 | ||
91 | #ifdef GSSAPI | 92 | #ifdef GSSAPI |
92 | static Gssctxt *gsscontext = NULL; | 93 | static Gssctxt *gsscontext = NULL; |
@@ -149,6 +150,11 @@ int mm_answer_rsa_challenge(int, Buffer *); | |||
149 | int mm_answer_rsa_response(int, Buffer *); | 150 | int mm_answer_rsa_response(int, Buffer *); |
150 | int mm_answer_sesskey(int, Buffer *); | 151 | int mm_answer_sesskey(int, Buffer *); |
151 | int mm_answer_sessid(int, Buffer *); | 152 | int mm_answer_sessid(int, Buffer *); |
153 | int mm_answer_jpake_get_pwdata(int, Buffer *); | ||
154 | int mm_answer_jpake_step1(int, Buffer *); | ||
155 | int mm_answer_jpake_step2(int, Buffer *); | ||
156 | int mm_answer_jpake_key_confirm(int, Buffer *); | ||
157 | int mm_answer_jpake_check_confirm(int, Buffer *); | ||
152 | 158 | ||
153 | #ifdef USE_PAM | 159 | #ifdef USE_PAM |
154 | int mm_answer_pam_start(int, Buffer *); | 160 | int mm_answer_pam_start(int, Buffer *); |
@@ -234,6 +240,13 @@ struct mon_table mon_dispatch_proto20[] = { | |||
234 | {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, | 240 | {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, |
235 | {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, | 241 | {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, |
236 | #endif | 242 | #endif |
243 | #ifdef JPAKE | ||
244 | {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata}, | ||
245 | {MONITOR_REQ_JPAKE_STEP1, MON_ISAUTH, mm_answer_jpake_step1}, | ||
246 | {MONITOR_REQ_JPAKE_STEP2, MON_ONCE, mm_answer_jpake_step2}, | ||
247 | {MONITOR_REQ_JPAKE_KEY_CONFIRM, MON_ONCE, mm_answer_jpake_key_confirm}, | ||
248 | {MONITOR_REQ_JPAKE_CHECK_CONFIRM, MON_AUTH, mm_answer_jpake_check_confirm}, | ||
249 | #endif | ||
237 | {0, 0, NULL} | 250 | {0, 0, NULL} |
238 | }; | 251 | }; |
239 | 252 | ||
@@ -379,6 +392,15 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) | |||
379 | if (!authenticated) | 392 | if (!authenticated) |
380 | authctxt->failures++; | 393 | authctxt->failures++; |
381 | } | 394 | } |
395 | #ifdef JPAKE | ||
396 | /* Cleanup JPAKE context after authentication */ | ||
397 | if (ent->flags & MON_AUTHDECIDE) { | ||
398 | if (authctxt->jpake_ctx != NULL) { | ||
399 | jpake_free(authctxt->jpake_ctx); | ||
400 | authctxt->jpake_ctx = NULL; | ||
401 | } | ||
402 | } | ||
403 | #endif | ||
382 | } | 404 | } |
383 | 405 | ||
384 | if (!authctxt->valid) | 406 | if (!authctxt->valid) |
@@ -1969,3 +1991,206 @@ mm_answer_gss_userok(int sock, Buffer *m) | |||
1969 | return (authenticated); | 1991 | return (authenticated); |
1970 | } | 1992 | } |
1971 | #endif /* GSSAPI */ | 1993 | #endif /* GSSAPI */ |
1994 | |||
1995 | #ifdef JPAKE | ||
1996 | int | ||
1997 | mm_answer_jpake_step1(int sock, Buffer *m) | ||
1998 | { | ||
1999 | struct jpake_ctx *pctx; | ||
2000 | u_char *x3_proof, *x4_proof; | ||
2001 | u_int x3_proof_len, x4_proof_len; | ||
2002 | |||
2003 | if (!options.zero_knowledge_password_authentication) | ||
2004 | fatal("zero_knowledge_password_authentication disabled"); | ||
2005 | |||
2006 | if (authctxt->jpake_ctx != NULL) | ||
2007 | fatal("%s: authctxt->jpake_ctx already set (%p)", | ||
2008 | __func__, authctxt->jpake_ctx); | ||
2009 | authctxt->jpake_ctx = pctx = jpake_new(); | ||
2010 | |||
2011 | jpake_step1(pctx->grp, | ||
2012 | &pctx->server_id, &pctx->server_id_len, | ||
2013 | &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4, | ||
2014 | &x3_proof, &x3_proof_len, | ||
2015 | &x4_proof, &x4_proof_len); | ||
2016 | |||
2017 | JPAKE_DEBUG_CTX((pctx, "step1 done in %s", __func__)); | ||
2018 | |||
2019 | buffer_clear(m); | ||
2020 | |||
2021 | buffer_put_string(m, pctx->server_id, pctx->server_id_len); | ||
2022 | buffer_put_bignum2(m, pctx->g_x3); | ||
2023 | buffer_put_bignum2(m, pctx->g_x4); | ||
2024 | buffer_put_string(m, x3_proof, x3_proof_len); | ||
2025 | buffer_put_string(m, x4_proof, x4_proof_len); | ||
2026 | |||
2027 | debug3("%s: sending step1", __func__); | ||
2028 | mm_request_send(sock, MONITOR_ANS_JPAKE_STEP1, m); | ||
2029 | |||
2030 | bzero(x3_proof, x3_proof_len); | ||
2031 | bzero(x4_proof, x4_proof_len); | ||
2032 | xfree(x3_proof); | ||
2033 | xfree(x4_proof); | ||
2034 | |||
2035 | monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_GET_PWDATA, 1); | ||
2036 | monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 0); | ||
2037 | |||
2038 | return 0; | ||
2039 | } | ||
2040 | |||
2041 | int | ||
2042 | mm_answer_jpake_get_pwdata(int sock, Buffer *m) | ||
2043 | { | ||
2044 | struct jpake_ctx *pctx = authctxt->jpake_ctx; | ||
2045 | char *hash_scheme, *salt; | ||
2046 | |||
2047 | if (pctx == NULL) | ||
2048 | fatal("%s: pctx == NULL", __func__); | ||
2049 | |||
2050 | auth2_jpake_get_pwdata(authctxt, &pctx->s, &hash_scheme, &salt); | ||
2051 | |||
2052 | buffer_clear(m); | ||
2053 | /* pctx->s is sensitive, not returned to slave */ | ||
2054 | buffer_put_cstring(m, hash_scheme); | ||
2055 | buffer_put_cstring(m, salt); | ||
2056 | |||
2057 | debug3("%s: sending pwdata", __func__); | ||
2058 | mm_request_send(sock, MONITOR_ANS_JPAKE_GET_PWDATA, m); | ||
2059 | |||
2060 | bzero(hash_scheme, strlen(hash_scheme)); | ||
2061 | bzero(salt, strlen(salt)); | ||
2062 | xfree(hash_scheme); | ||
2063 | xfree(salt); | ||
2064 | |||
2065 | monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP2, 1); | ||
2066 | |||
2067 | return 0; | ||
2068 | } | ||
2069 | |||
2070 | int | ||
2071 | mm_answer_jpake_step2(int sock, Buffer *m) | ||
2072 | { | ||
2073 | struct jpake_ctx *pctx = authctxt->jpake_ctx; | ||
2074 | u_char *x1_proof, *x2_proof, *x4_s_proof; | ||
2075 | u_int x1_proof_len, x2_proof_len, x4_s_proof_len; | ||
2076 | |||
2077 | if (pctx == NULL) | ||
2078 | fatal("%s: pctx == NULL", __func__); | ||
2079 | |||
2080 | if ((pctx->g_x1 = BN_new()) == NULL || | ||
2081 | (pctx->g_x2 = BN_new()) == NULL) | ||
2082 | fatal("%s: BN_new", __func__); | ||
2083 | buffer_get_bignum2(m, pctx->g_x1); | ||
2084 | buffer_get_bignum2(m, pctx->g_x2); | ||
2085 | pctx->client_id = buffer_get_string(m, &pctx->client_id_len); | ||
2086 | x1_proof = buffer_get_string(m, &x1_proof_len); | ||
2087 | x2_proof = buffer_get_string(m, &x2_proof_len); | ||
2088 | |||
2089 | jpake_step2(pctx->grp, pctx->s, pctx->g_x3, | ||
2090 | pctx->g_x1, pctx->g_x2, pctx->x4, | ||
2091 | pctx->client_id, pctx->client_id_len, | ||
2092 | pctx->server_id, pctx->server_id_len, | ||
2093 | x1_proof, x1_proof_len, | ||
2094 | x2_proof, x2_proof_len, | ||
2095 | &pctx->b, | ||
2096 | &x4_s_proof, &x4_s_proof_len); | ||
2097 | |||
2098 | JPAKE_DEBUG_CTX((pctx, "step2 done in %s", __func__)); | ||
2099 | |||
2100 | bzero(x1_proof, x1_proof_len); | ||
2101 | bzero(x2_proof, x2_proof_len); | ||
2102 | xfree(x1_proof); | ||
2103 | xfree(x2_proof); | ||
2104 | |||
2105 | buffer_clear(m); | ||
2106 | |||
2107 | buffer_put_bignum2(m, pctx->b); | ||
2108 | buffer_put_string(m, x4_s_proof, x4_s_proof_len); | ||
2109 | |||
2110 | debug3("%s: sending step2", __func__); | ||
2111 | mm_request_send(sock, MONITOR_ANS_JPAKE_STEP2, m); | ||
2112 | |||
2113 | bzero(x4_s_proof, x4_s_proof_len); | ||
2114 | xfree(x4_s_proof); | ||
2115 | |||
2116 | monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_KEY_CONFIRM, 1); | ||
2117 | |||
2118 | return 0; | ||
2119 | } | ||
2120 | |||
2121 | int | ||
2122 | mm_answer_jpake_key_confirm(int sock, Buffer *m) | ||
2123 | { | ||
2124 | struct jpake_ctx *pctx = authctxt->jpake_ctx; | ||
2125 | u_char *x2_s_proof; | ||
2126 | u_int x2_s_proof_len; | ||
2127 | |||
2128 | if (pctx == NULL) | ||
2129 | fatal("%s: pctx == NULL", __func__); | ||
2130 | |||
2131 | if ((pctx->a = BN_new()) == NULL) | ||
2132 | fatal("%s: BN_new", __func__); | ||
2133 | buffer_get_bignum2(m, pctx->a); | ||
2134 | x2_s_proof = buffer_get_string(m, &x2_s_proof_len); | ||
2135 | |||
2136 | jpake_key_confirm(pctx->grp, pctx->s, pctx->a, | ||
2137 | pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2, | ||
2138 | pctx->server_id, pctx->server_id_len, | ||
2139 | pctx->client_id, pctx->client_id_len, | ||
2140 | session_id2, session_id2_len, | ||
2141 | x2_s_proof, x2_s_proof_len, | ||
2142 | &pctx->k, | ||
2143 | &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len); | ||
2144 | |||
2145 | JPAKE_DEBUG_CTX((pctx, "key_confirm done in %s", __func__)); | ||
2146 | |||
2147 | bzero(x2_s_proof, x2_s_proof_len); | ||
2148 | buffer_clear(m); | ||
2149 | |||
2150 | /* pctx->k is sensitive, not sent */ | ||
2151 | buffer_put_string(m, pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len); | ||
2152 | |||
2153 | debug3("%s: sending confirmation hash", __func__); | ||
2154 | mm_request_send(sock, MONITOR_ANS_JPAKE_KEY_CONFIRM, m); | ||
2155 | |||
2156 | monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_CHECK_CONFIRM, 1); | ||
2157 | |||
2158 | return 0; | ||
2159 | } | ||
2160 | |||
2161 | int | ||
2162 | mm_answer_jpake_check_confirm(int sock, Buffer *m) | ||
2163 | { | ||
2164 | int authenticated = 0; | ||
2165 | u_char *peer_confirm_hash; | ||
2166 | u_int peer_confirm_hash_len; | ||
2167 | struct jpake_ctx *pctx = authctxt->jpake_ctx; | ||
2168 | |||
2169 | if (pctx == NULL) | ||
2170 | fatal("%s: pctx == NULL", __func__); | ||
2171 | |||
2172 | peer_confirm_hash = buffer_get_string(m, &peer_confirm_hash_len); | ||
2173 | |||
2174 | authenticated = jpake_check_confirm(pctx->k, | ||
2175 | pctx->client_id, pctx->client_id_len, | ||
2176 | session_id2, session_id2_len, | ||
2177 | peer_confirm_hash, peer_confirm_hash_len) && authctxt->valid; | ||
2178 | |||
2179 | JPAKE_DEBUG_CTX((pctx, "check_confirm done in %s", __func__)); | ||
2180 | |||
2181 | bzero(peer_confirm_hash, peer_confirm_hash_len); | ||
2182 | xfree(peer_confirm_hash); | ||
2183 | |||
2184 | buffer_clear(m); | ||
2185 | buffer_put_int(m, authenticated); | ||
2186 | |||
2187 | debug3("%s: sending result %d", __func__, authenticated); | ||
2188 | mm_request_send(sock, MONITOR_ANS_JPAKE_CHECK_CONFIRM, m); | ||
2189 | |||
2190 | monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1); | ||
2191 | |||
2192 | auth_method = "jpake-01@openssh.com"; | ||
2193 | return authenticated; | ||
2194 | } | ||
2195 | |||
2196 | #endif /* JPAKE */ | ||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: monitor.h,v 1.14 2006/03/25 22:22:43 djm Exp $ */ | 1 | /* $OpenBSD: monitor.h,v 1.15 2008/11/04 08:22:13 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | 4 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> |
@@ -60,7 +60,12 @@ enum monitor_reqtype { | |||
60 | MONITOR_REQ_PAM_RESPOND, MONITOR_ANS_PAM_RESPOND, | 60 | MONITOR_REQ_PAM_RESPOND, MONITOR_ANS_PAM_RESPOND, |
61 | MONITOR_REQ_PAM_FREE_CTX, MONITOR_ANS_PAM_FREE_CTX, | 61 | MONITOR_REQ_PAM_FREE_CTX, MONITOR_ANS_PAM_FREE_CTX, |
62 | MONITOR_REQ_AUDIT_EVENT, MONITOR_REQ_AUDIT_COMMAND, | 62 | MONITOR_REQ_AUDIT_EVENT, MONITOR_REQ_AUDIT_COMMAND, |
63 | MONITOR_REQ_TERM | 63 | MONITOR_REQ_TERM, |
64 | MONITOR_REQ_JPAKE_STEP1, MONITOR_ANS_JPAKE_STEP1, | ||
65 | MONITOR_REQ_JPAKE_GET_PWDATA, MONITOR_ANS_JPAKE_GET_PWDATA, | ||
66 | MONITOR_REQ_JPAKE_STEP2, MONITOR_ANS_JPAKE_STEP2, | ||
67 | MONITOR_REQ_JPAKE_KEY_CONFIRM, MONITOR_ANS_JPAKE_KEY_CONFIRM, | ||
68 | MONITOR_REQ_JPAKE_CHECK_CONFIRM, MONITOR_ANS_JPAKE_CHECK_CONFIRM, | ||
64 | }; | 69 | }; |
65 | 70 | ||
66 | struct mm_master; | 71 | struct mm_master; |
diff --git a/monitor_wrap.c b/monitor_wrap.c index 40463d078..0986fc518 100644 --- a/monitor_wrap.c +++ b/monitor_wrap.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: monitor_wrap.c,v 1.63 2008/07/10 18:08:11 markus Exp $ */ | 1 | /* $OpenBSD: monitor_wrap.c,v 1.64 2008/11/04 08:22:13 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | 3 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> |
4 | * Copyright 2002 Markus Friedl <markus@openbsd.org> | 4 | * Copyright 2002 Markus Friedl <markus@openbsd.org> |
@@ -40,6 +40,7 @@ | |||
40 | 40 | ||
41 | #include <openssl/bn.h> | 41 | #include <openssl/bn.h> |
42 | #include <openssl/dh.h> | 42 | #include <openssl/dh.h> |
43 | #include <openssl/evp.h> | ||
43 | 44 | ||
44 | #include "openbsd-compat/sys-queue.h" | 45 | #include "openbsd-compat/sys-queue.h" |
45 | #include "xmalloc.h" | 46 | #include "xmalloc.h" |
@@ -70,7 +71,7 @@ | |||
70 | #include "atomicio.h" | 71 | #include "atomicio.h" |
71 | #include "monitor_fdpass.h" | 72 | #include "monitor_fdpass.h" |
72 | #include "misc.h" | 73 | #include "misc.h" |
73 | #include "servconf.h" | 74 | #include "jpake.h" |
74 | 75 | ||
75 | #include "channels.h" | 76 | #include "channels.h" |
76 | #include "session.h" | 77 | #include "session.h" |
@@ -1256,3 +1257,165 @@ mm_ssh_gssapi_userok(char *user) | |||
1256 | return (authenticated); | 1257 | return (authenticated); |
1257 | } | 1258 | } |
1258 | #endif /* GSSAPI */ | 1259 | #endif /* GSSAPI */ |
1260 | |||
1261 | #ifdef JPAKE | ||
1262 | void | ||
1263 | mm_auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s, | ||
1264 | char **hash_scheme, char **salt) | ||
1265 | { | ||
1266 | Buffer m; | ||
1267 | |||
1268 | debug3("%s entering", __func__); | ||
1269 | |||
1270 | buffer_init(&m); | ||
1271 | mm_request_send(pmonitor->m_recvfd, | ||
1272 | MONITOR_REQ_JPAKE_GET_PWDATA, &m); | ||
1273 | |||
1274 | debug3("%s: waiting for MONITOR_ANS_JPAKE_GET_PWDATA", __func__); | ||
1275 | mm_request_receive_expect(pmonitor->m_recvfd, | ||
1276 | MONITOR_ANS_JPAKE_GET_PWDATA, &m); | ||
1277 | |||
1278 | *hash_scheme = buffer_get_string(&m, NULL); | ||
1279 | *salt = buffer_get_string(&m, NULL); | ||
1280 | |||
1281 | buffer_free(&m); | ||
1282 | } | ||
1283 | |||
1284 | void | ||
1285 | mm_jpake_step1(struct jpake_group *grp, | ||
1286 | u_char **id, u_int *id_len, | ||
1287 | BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2, | ||
1288 | u_char **priv1_proof, u_int *priv1_proof_len, | ||
1289 | u_char **priv2_proof, u_int *priv2_proof_len) | ||
1290 | { | ||
1291 | Buffer m; | ||
1292 | |||
1293 | debug3("%s entering", __func__); | ||
1294 | |||
1295 | buffer_init(&m); | ||
1296 | mm_request_send(pmonitor->m_recvfd, | ||
1297 | MONITOR_REQ_JPAKE_STEP1, &m); | ||
1298 | |||
1299 | debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP1", __func__); | ||
1300 | mm_request_receive_expect(pmonitor->m_recvfd, | ||
1301 | MONITOR_ANS_JPAKE_STEP1, &m); | ||
1302 | |||
1303 | if ((*priv1 = BN_new()) == NULL || | ||
1304 | (*priv2 = BN_new()) == NULL || | ||
1305 | (*g_priv1 = BN_new()) == NULL || | ||
1306 | (*g_priv2 = BN_new()) == NULL) | ||
1307 | fatal("%s: BN_new", __func__); | ||
1308 | |||
1309 | *id = buffer_get_string(&m, id_len); | ||
1310 | /* priv1 and priv2 are, well, private */ | ||
1311 | buffer_get_bignum2(&m, *g_priv1); | ||
1312 | buffer_get_bignum2(&m, *g_priv2); | ||
1313 | *priv1_proof = buffer_get_string(&m, priv1_proof_len); | ||
1314 | *priv2_proof = buffer_get_string(&m, priv2_proof_len); | ||
1315 | |||
1316 | buffer_free(&m); | ||
1317 | } | ||
1318 | |||
1319 | void | ||
1320 | mm_jpake_step2(struct jpake_group *grp, BIGNUM *s, | ||
1321 | BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2, | ||
1322 | const u_char *theirid, u_int theirid_len, | ||
1323 | const u_char *myid, u_int myid_len, | ||
1324 | const u_char *theirpub1_proof, u_int theirpub1_proof_len, | ||
1325 | const u_char *theirpub2_proof, u_int theirpub2_proof_len, | ||
1326 | BIGNUM **newpub, | ||
1327 | u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len) | ||
1328 | { | ||
1329 | Buffer m; | ||
1330 | |||
1331 | debug3("%s entering", __func__); | ||
1332 | |||
1333 | buffer_init(&m); | ||
1334 | /* monitor already has all bignums except theirpub1, theirpub2 */ | ||
1335 | buffer_put_bignum2(&m, theirpub1); | ||
1336 | buffer_put_bignum2(&m, theirpub2); | ||
1337 | /* monitor already knows our id */ | ||
1338 | buffer_put_string(&m, theirid, theirid_len); | ||
1339 | buffer_put_string(&m, theirpub1_proof, theirpub1_proof_len); | ||
1340 | buffer_put_string(&m, theirpub2_proof, theirpub2_proof_len); | ||
1341 | |||
1342 | mm_request_send(pmonitor->m_recvfd, | ||
1343 | MONITOR_REQ_JPAKE_STEP2, &m); | ||
1344 | |||
1345 | debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP2", __func__); | ||
1346 | mm_request_receive_expect(pmonitor->m_recvfd, | ||
1347 | MONITOR_ANS_JPAKE_STEP2, &m); | ||
1348 | |||
1349 | if ((*newpub = BN_new()) == NULL) | ||
1350 | fatal("%s: BN_new", __func__); | ||
1351 | |||
1352 | buffer_get_bignum2(&m, *newpub); | ||
1353 | *newpub_exponent_proof = buffer_get_string(&m, | ||
1354 | newpub_exponent_proof_len); | ||
1355 | |||
1356 | buffer_free(&m); | ||
1357 | } | ||
1358 | |||
1359 | void | ||
1360 | mm_jpake_key_confirm(struct jpake_group *grp, BIGNUM *s, BIGNUM *step2_val, | ||
1361 | BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2, | ||
1362 | BIGNUM *theirpub1, BIGNUM *theirpub2, | ||
1363 | const u_char *my_id, u_int my_id_len, | ||
1364 | const u_char *their_id, u_int their_id_len, | ||
1365 | const u_char *sess_id, u_int sess_id_len, | ||
1366 | const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len, | ||
1367 | BIGNUM **k, | ||
1368 | u_char **confirm_hash, u_int *confirm_hash_len) | ||
1369 | { | ||
1370 | Buffer m; | ||
1371 | |||
1372 | debug3("%s entering", __func__); | ||
1373 | |||
1374 | buffer_init(&m); | ||
1375 | /* monitor already has all bignums except step2_val */ | ||
1376 | buffer_put_bignum2(&m, step2_val); | ||
1377 | /* monitor already knows all the ids */ | ||
1378 | buffer_put_string(&m, theirpriv2_s_proof, theirpriv2_s_proof_len); | ||
1379 | |||
1380 | mm_request_send(pmonitor->m_recvfd, | ||
1381 | MONITOR_REQ_JPAKE_KEY_CONFIRM, &m); | ||
1382 | |||
1383 | debug3("%s: waiting for MONITOR_ANS_JPAKE_KEY_CONFIRM", __func__); | ||
1384 | mm_request_receive_expect(pmonitor->m_recvfd, | ||
1385 | MONITOR_ANS_JPAKE_KEY_CONFIRM, &m); | ||
1386 | |||
1387 | /* 'k' is sensitive and stays in the monitor */ | ||
1388 | *confirm_hash = buffer_get_string(&m, confirm_hash_len); | ||
1389 | |||
1390 | buffer_free(&m); | ||
1391 | } | ||
1392 | |||
1393 | int | ||
1394 | mm_jpake_check_confirm(const BIGNUM *k, | ||
1395 | const u_char *peer_id, u_int peer_id_len, | ||
1396 | const u_char *sess_id, u_int sess_id_len, | ||
1397 | const u_char *peer_confirm_hash, u_int peer_confirm_hash_len) | ||
1398 | { | ||
1399 | Buffer m; | ||
1400 | int success = 0; | ||
1401 | |||
1402 | debug3("%s entering", __func__); | ||
1403 | |||
1404 | buffer_init(&m); | ||
1405 | /* k is dummy in slave, ignored */ | ||
1406 | /* monitor knows all the ids */ | ||
1407 | buffer_put_string(&m, peer_confirm_hash, peer_confirm_hash_len); | ||
1408 | mm_request_send(pmonitor->m_recvfd, | ||
1409 | MONITOR_REQ_JPAKE_CHECK_CONFIRM, &m); | ||
1410 | |||
1411 | debug3("%s: waiting for MONITOR_ANS_JPAKE_CHECK_CONFIRM", __func__); | ||
1412 | mm_request_receive_expect(pmonitor->m_recvfd, | ||
1413 | MONITOR_ANS_JPAKE_CHECK_CONFIRM, &m); | ||
1414 | |||
1415 | success = buffer_get_int(&m); | ||
1416 | buffer_free(&m); | ||
1417 | |||
1418 | debug3("%s: success = %d", __func__, success); | ||
1419 | return success; | ||
1420 | } | ||
1421 | #endif /* JPAKE */ | ||
diff --git a/monitor_wrap.h b/monitor_wrap.h index 329189c2a..55c4b99f3 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: monitor_wrap.h,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */ | 1 | /* $OpenBSD: monitor_wrap.h,v 1.21 2008/11/04 08:22:13 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> | 4 | * Copyright 2002 Niels Provos <provos@citi.umich.edu> |
@@ -101,6 +101,26 @@ int mm_bsdauth_respond(void *, u_int, char **); | |||
101 | int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); | 101 | int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); |
102 | int mm_skey_respond(void *, u_int, char **); | 102 | int mm_skey_respond(void *, u_int, char **); |
103 | 103 | ||
104 | /* jpake */ | ||
105 | struct jpake_group; | ||
106 | void mm_auth2_jpake_get_pwdata(struct Authctxt *, BIGNUM **, char **, char **); | ||
107 | void mm_jpake_step1(struct jpake_group *, u_char **, u_int *, | ||
108 | BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **, | ||
109 | u_char **, u_int *, u_char **, u_int *); | ||
110 | void mm_jpake_step2(struct jpake_group *, BIGNUM *, | ||
111 | BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, | ||
112 | const u_char *, u_int, const u_char *, u_int, | ||
113 | const u_char *, u_int, const u_char *, u_int, | ||
114 | BIGNUM **, u_char **, u_int *); | ||
115 | void mm_jpake_key_confirm(struct jpake_group *, BIGNUM *, BIGNUM *, | ||
116 | BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, | ||
117 | const u_char *, u_int, const u_char *, u_int, | ||
118 | const u_char *, u_int, const u_char *, u_int, | ||
119 | BIGNUM **, u_char **, u_int *); | ||
120 | int mm_jpake_check_confirm(const BIGNUM *, | ||
121 | const u_char *, u_int, const u_char *, u_int, const u_char *, u_int); | ||
122 | |||
123 | |||
104 | /* zlib allocation hooks */ | 124 | /* zlib allocation hooks */ |
105 | 125 | ||
106 | void *mm_zalloc(struct mm_master *, u_int, u_int); | 126 | void *mm_zalloc(struct mm_master *, u_int, u_int); |
diff --git a/readconf.c b/readconf.c index 7f7bbfee2..ba70d9da0 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readconf.c,v 1.170 2008/11/03 02:44:41 stevesk Exp $ */ | 1 | /* $OpenBSD: readconf.c,v 1.171 2008/11/04 08:22:13 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -130,7 +130,7 @@ typedef enum { | |||
130 | oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, | 130 | oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, |
131 | oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, | 131 | oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, |
132 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, | 132 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, |
133 | oVisualHostKey, | 133 | oVisualHostKey, oZeroKnowledgePasswordAuthentication, |
134 | oDeprecated, oUnsupported | 134 | oDeprecated, oUnsupported |
135 | } OpCodes; | 135 | } OpCodes; |
136 | 136 | ||
@@ -228,6 +228,13 @@ static struct { | |||
228 | { "localcommand", oLocalCommand }, | 228 | { "localcommand", oLocalCommand }, |
229 | { "permitlocalcommand", oPermitLocalCommand }, | 229 | { "permitlocalcommand", oPermitLocalCommand }, |
230 | { "visualhostkey", oVisualHostKey }, | 230 | { "visualhostkey", oVisualHostKey }, |
231 | #ifdef JPAKE | ||
232 | { "zeroknowledgepasswordauthentication", | ||
233 | oZeroKnowledgePasswordAuthentication }, | ||
234 | #else | ||
235 | { "zeroknowledgepasswordauthentication", oUnsupported }, | ||
236 | #endif | ||
237 | |||
231 | { NULL, oBadOption } | 238 | { NULL, oBadOption } |
232 | }; | 239 | }; |
233 | 240 | ||
@@ -412,6 +419,10 @@ parse_flag: | |||
412 | intptr = &options->password_authentication; | 419 | intptr = &options->password_authentication; |
413 | goto parse_flag; | 420 | goto parse_flag; |
414 | 421 | ||
422 | case oZeroKnowledgePasswordAuthentication: | ||
423 | intptr = &options->zero_knowledge_password_authentication; | ||
424 | goto parse_flag; | ||
425 | |||
415 | case oKbdInteractiveAuthentication: | 426 | case oKbdInteractiveAuthentication: |
416 | intptr = &options->kbd_interactive_authentication; | 427 | intptr = &options->kbd_interactive_authentication; |
417 | goto parse_flag; | 428 | goto parse_flag; |
@@ -1054,6 +1065,7 @@ initialize_options(Options * options) | |||
1054 | options->local_command = NULL; | 1065 | options->local_command = NULL; |
1055 | options->permit_local_command = -1; | 1066 | options->permit_local_command = -1; |
1056 | options->visual_host_key = -1; | 1067 | options->visual_host_key = -1; |
1068 | options->zero_knowledge_password_authentication = -1; | ||
1057 | } | 1069 | } |
1058 | 1070 | ||
1059 | /* | 1071 | /* |
@@ -1190,6 +1202,8 @@ fill_default_options(Options * options) | |||
1190 | options->permit_local_command = 0; | 1202 | options->permit_local_command = 0; |
1191 | if (options->visual_host_key == -1) | 1203 | if (options->visual_host_key == -1) |
1192 | options->visual_host_key = 0; | 1204 | options->visual_host_key = 0; |
1205 | if (options->zero_knowledge_password_authentication == -1) | ||
1206 | options->zero_knowledge_password_authentication = 0; | ||
1193 | /* options->local_command should not be set by default */ | 1207 | /* options->local_command should not be set by default */ |
1194 | /* options->proxy_command should not be set by default */ | 1208 | /* options->proxy_command should not be set by default */ |
1195 | /* options->user will be set in the main program if appropriate */ | 1209 | /* options->user will be set in the main program if appropriate */ |
diff --git a/readconf.h b/readconf.h index c1387a896..c9e5f6a41 100644 --- a/readconf.h +++ b/readconf.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readconf.h,v 1.75 2008/11/01 17:40:33 stevesk Exp $ */ | 1 | /* $OpenBSD: readconf.h,v 1.76 2008/11/04 08:22:13 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -49,6 +49,7 @@ typedef struct { | |||
49 | * authentication. */ | 49 | * authentication. */ |
50 | int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ | 50 | int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ |
51 | char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ | 51 | char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ |
52 | int zero_knowledge_password_authentication; /* Try jpake */ | ||
52 | int batch_mode; /* Batch mode: do not ask for passwords. */ | 53 | int batch_mode; /* Batch mode: do not ask for passwords. */ |
53 | int check_host_ip; /* Also keep track of keys for IP address */ | 54 | int check_host_ip; /* Also keep track of keys for IP address */ |
54 | int strict_host_key_checking; /* Strict host key checking. */ | 55 | int strict_host_key_checking; /* Strict host key checking. */ |
diff --git a/schnorr.c b/schnorr.c new file mode 100644 index 000000000..e3abe5702 --- /dev/null +++ b/schnorr.c | |||
@@ -0,0 +1,407 @@ | |||
1 | /* $OpenBSD: schnorr.c,v 1.1 2008/11/04 08:22: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 | * Implementation of Schnorr signatures / zero-knowledge proofs, based on | ||
20 | * description 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 | #include "includes.h" | ||
29 | |||
30 | #include <sys/types.h> | ||
31 | |||
32 | #include <string.h> | ||
33 | #include <stdarg.h> | ||
34 | #include <stdio.h> | ||
35 | |||
36 | #include <openssl/evp.h> | ||
37 | #include <openssl/bn.h> | ||
38 | |||
39 | #include "xmalloc.h" | ||
40 | #include "buffer.h" | ||
41 | #include "log.h" | ||
42 | |||
43 | #include "jpake.h" | ||
44 | |||
45 | /* #define SCHNORR_DEBUG */ /* Privacy-violating debugging */ | ||
46 | /* #define SCHNORR_MAIN */ /* Include main() selftest */ | ||
47 | |||
48 | /* XXX */ | ||
49 | /* Parametise signature hash? (sha256, sha1, etc.) */ | ||
50 | /* Signature format - include type name, hash type, group params? */ | ||
51 | |||
52 | #ifndef SCHNORR_DEBUG | ||
53 | # define SCHNORR_DEBUG_BN(a) | ||
54 | # define SCHNORR_DEBUG_BUF(a) | ||
55 | #else | ||
56 | # define SCHNORR_DEBUG_BN(a) jpake_debug3_bn a | ||
57 | # define SCHNORR_DEBUG_BUF(a) jpake_debug3_buf a | ||
58 | #endif /* SCHNORR_DEBUG */ | ||
59 | |||
60 | /* | ||
61 | * Calculate hash component of Schnorr signature H(g || g^v || g^x || id) | ||
62 | * using SHA1. Returns signature as bignum or NULL on error. | ||
63 | */ | ||
64 | static BIGNUM * | ||
65 | schnorr_hash(const BIGNUM *p, const BIGNUM *q, const BIGNUM *g, | ||
66 | const BIGNUM *g_v, const BIGNUM *g_x, | ||
67 | const u_char *id, u_int idlen) | ||
68 | { | ||
69 | u_char *digest; | ||
70 | u_int digest_len; | ||
71 | BIGNUM *h; | ||
72 | EVP_MD_CTX evp_md_ctx; | ||
73 | Buffer b; | ||
74 | int success = -1; | ||
75 | |||
76 | if ((h = BN_new()) == NULL) { | ||
77 | error("%s: BN_new", __func__); | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | buffer_init(&b); | ||
82 | EVP_MD_CTX_init(&evp_md_ctx); | ||
83 | |||
84 | /* h = H(g || g^v || g^x || id) */ | ||
85 | buffer_put_bignum2(&b, g); | ||
86 | buffer_put_bignum2(&b, g_v); | ||
87 | buffer_put_bignum2(&b, g_x); | ||
88 | buffer_put_string(&b, id, idlen); | ||
89 | |||
90 | SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b), | ||
91 | "%s: hashblob", __func__)); | ||
92 | if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(), | ||
93 | &digest, &digest_len) != 0) { | ||
94 | error("%s: hash_buffer", __func__); | ||
95 | goto out; | ||
96 | } | ||
97 | if (BN_bin2bn(digest, (int)digest_len, h) == NULL) { | ||
98 | error("%s: BN_bin2bn", __func__); | ||
99 | goto out; | ||
100 | } | ||
101 | success = 0; | ||
102 | SCHNORR_DEBUG_BN((h, "%s: h = ", __func__)); | ||
103 | out: | ||
104 | buffer_free(&b); | ||
105 | EVP_MD_CTX_cleanup(&evp_md_ctx); | ||
106 | bzero(digest, digest_len); | ||
107 | xfree(digest); | ||
108 | digest_len = 0; | ||
109 | if (success == 0) | ||
110 | return h; | ||
111 | BN_clear_free(h); | ||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Generate Schnorr signature to prove knowledge of private value 'x' used | ||
117 | * in public exponent g^x, under group defined by 'grp_p', 'grp_q' and 'grp_g' | ||
118 | * 'idlen' bytes from 'id' will be included in the signature hash as an anti- | ||
119 | * replay salt. | ||
120 | * On success, 0 is returned and *siglen bytes of signature are returned in | ||
121 | * *sig (caller to free). Returns -1 on failure. | ||
122 | */ | ||
123 | int | ||
124 | schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g, | ||
125 | const BIGNUM *x, const BIGNUM *g_x, const u_char *id, u_int idlen, | ||
126 | u_char **sig, u_int *siglen) | ||
127 | { | ||
128 | int success = -1; | ||
129 | Buffer b; | ||
130 | BIGNUM *h, *tmp, *v, *g_v, *r; | ||
131 | BN_CTX *bn_ctx; | ||
132 | |||
133 | SCHNORR_DEBUG_BN((x, "%s: x = ", __func__)); | ||
134 | SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__)); | ||
135 | |||
136 | /* Avoid degenerate cases: g^0 yields a spoofable signature */ | ||
137 | if (BN_cmp(g_x, BN_value_one()) <= 0) { | ||
138 | error("%s: g_x < 1", __func__); | ||
139 | return -1; | ||
140 | } | ||
141 | |||
142 | h = g_v = r = tmp = v = NULL; | ||
143 | if ((bn_ctx = BN_CTX_new()) == NULL) { | ||
144 | error("%s: BN_CTX_new", __func__); | ||
145 | goto out; | ||
146 | } | ||
147 | if ((g_v = BN_new()) == NULL || | ||
148 | (r = BN_new()) == NULL || | ||
149 | (tmp = BN_new()) == NULL) { | ||
150 | error("%s: BN_new", __func__); | ||
151 | goto out; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * v must be a random element of Zq, so 1 <= v < q | ||
156 | * we also exclude v = 1, since g^1 looks dangerous | ||
157 | */ | ||
158 | if ((v = bn_rand_range_gt_one(grp_p)) == NULL) { | ||
159 | error("%s: bn_rand_range2", __func__); | ||
160 | goto out; | ||
161 | } | ||
162 | SCHNORR_DEBUG_BN((v, "%s: v = ", __func__)); | ||
163 | |||
164 | /* g_v = g^v mod p */ | ||
165 | if (BN_mod_exp(g_v, grp_g, v, grp_p, bn_ctx) == -1) { | ||
166 | error("%s: BN_mod_exp (g^v mod p)", __func__); | ||
167 | goto out; | ||
168 | } | ||
169 | SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__)); | ||
170 | |||
171 | /* h = H(g || g^v || g^x || id) */ | ||
172 | if ((h = schnorr_hash(grp_p, grp_q, grp_g, g_v, g_x, | ||
173 | id, idlen)) == NULL) { | ||
174 | error("%s: schnorr_hash failed", __func__); | ||
175 | goto out; | ||
176 | } | ||
177 | |||
178 | /* r = v - xh mod q */ | ||
179 | if (BN_mod_mul(tmp, x, h, grp_q, bn_ctx) == -1) { | ||
180 | error("%s: BN_mod_mul (tmp = xv mod q)", __func__); | ||
181 | goto out; | ||
182 | } | ||
183 | if (BN_mod_sub(r, v, tmp, grp_q, bn_ctx) == -1) { | ||
184 | error("%s: BN_mod_mul (r = v - tmp)", __func__); | ||
185 | goto out; | ||
186 | } | ||
187 | SCHNORR_DEBUG_BN((r, "%s: r = ", __func__)); | ||
188 | |||
189 | /* Signature is (g_v, r) */ | ||
190 | buffer_init(&b); | ||
191 | /* XXX sigtype-hash as string? */ | ||
192 | buffer_put_bignum2(&b, g_v); | ||
193 | buffer_put_bignum2(&b, r); | ||
194 | *siglen = buffer_len(&b); | ||
195 | *sig = xmalloc(*siglen); | ||
196 | memcpy(*sig, buffer_ptr(&b), *siglen); | ||
197 | SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b), | ||
198 | "%s: sigblob", __func__)); | ||
199 | buffer_free(&b); | ||
200 | success = 0; | ||
201 | out: | ||
202 | BN_CTX_free(bn_ctx); | ||
203 | if (h != NULL) | ||
204 | BN_clear_free(h); | ||
205 | if (v != NULL) | ||
206 | BN_clear_free(v); | ||
207 | BN_clear_free(r); | ||
208 | BN_clear_free(g_v); | ||
209 | BN_clear_free(tmp); | ||
210 | |||
211 | return success; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * Verify Schnorr signature 'sig' of length 'siglen' against public exponent | ||
216 | * g_x (g^x) under group defined by 'grp_p', 'grp_q' and 'grp_g'. | ||
217 | * Signature hash will be salted with 'idlen' bytes from 'id'. | ||
218 | * Returns -1 on failure, 0 on incorrect signature or 1 on matching signature. | ||
219 | */ | ||
220 | int | ||
221 | schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g, | ||
222 | const BIGNUM *g_x, const u_char *id, u_int idlen, | ||
223 | const u_char *sig, u_int siglen) | ||
224 | { | ||
225 | int success = -1; | ||
226 | Buffer b; | ||
227 | BIGNUM *g_v, *h, *r, *g_xh, *g_r, *expected; | ||
228 | BN_CTX *bn_ctx; | ||
229 | u_int rlen; | ||
230 | |||
231 | SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__)); | ||
232 | |||
233 | /* Avoid degenerate cases: g^0 yields a spoofable signature */ | ||
234 | if (BN_cmp(g_x, BN_value_one()) <= 0) { | ||
235 | error("%s: g_x < 1", __func__); | ||
236 | return -1; | ||
237 | } | ||
238 | |||
239 | g_v = h = r = g_xh = g_r = expected = NULL; | ||
240 | if ((bn_ctx = BN_CTX_new()) == NULL) { | ||
241 | error("%s: BN_CTX_new", __func__); | ||
242 | goto out; | ||
243 | } | ||
244 | if ((g_v = BN_new()) == NULL || | ||
245 | (r = BN_new()) == NULL || | ||
246 | (g_xh = BN_new()) == NULL || | ||
247 | (g_r = BN_new()) == NULL || | ||
248 | (expected = BN_new()) == NULL) { | ||
249 | error("%s: BN_new", __func__); | ||
250 | goto out; | ||
251 | } | ||
252 | |||
253 | /* Extract g^v and r from signature blob */ | ||
254 | buffer_init(&b); | ||
255 | buffer_append(&b, sig, siglen); | ||
256 | SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b), | ||
257 | "%s: sigblob", __func__)); | ||
258 | buffer_get_bignum2(&b, g_v); | ||
259 | buffer_get_bignum2(&b, r); | ||
260 | rlen = buffer_len(&b); | ||
261 | buffer_free(&b); | ||
262 | if (rlen != 0) { | ||
263 | error("%s: remaining bytes in signature %d", __func__, rlen); | ||
264 | goto out; | ||
265 | } | ||
266 | buffer_free(&b); | ||
267 | SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__)); | ||
268 | SCHNORR_DEBUG_BN((r, "%s: r = ", __func__)); | ||
269 | |||
270 | /* h = H(g || g^v || g^x || id) */ | ||
271 | if ((h = schnorr_hash(grp_p, grp_q, grp_g, g_v, g_x, | ||
272 | id, idlen)) == NULL) { | ||
273 | error("%s: schnorr_hash failed", __func__); | ||
274 | goto out; | ||
275 | } | ||
276 | |||
277 | /* g_xh = (g^x)^h */ | ||
278 | if (BN_mod_exp(g_xh, g_x, h, grp_p, bn_ctx) == -1) { | ||
279 | error("%s: BN_mod_exp (g_x^h mod p)", __func__); | ||
280 | goto out; | ||
281 | } | ||
282 | SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__)); | ||
283 | |||
284 | /* g_r = g^r */ | ||
285 | if (BN_mod_exp(g_r, grp_g, r, grp_p, bn_ctx) == -1) { | ||
286 | error("%s: BN_mod_exp (g_x^h mod p)", __func__); | ||
287 | goto out; | ||
288 | } | ||
289 | SCHNORR_DEBUG_BN((g_r, "%s: g_r = ", __func__)); | ||
290 | |||
291 | /* expected = g^r * g_xh */ | ||
292 | if (BN_mod_mul(expected, g_r, g_xh, grp_p, bn_ctx) == -1) { | ||
293 | error("%s: BN_mod_mul (expected = g_r mod p)", __func__); | ||
294 | goto out; | ||
295 | } | ||
296 | SCHNORR_DEBUG_BN((expected, "%s: expected = ", __func__)); | ||
297 | |||
298 | /* Check g_v == expected */ | ||
299 | success = BN_cmp(expected, g_v) == 0; | ||
300 | out: | ||
301 | BN_CTX_free(bn_ctx); | ||
302 | if (h != NULL) | ||
303 | BN_clear_free(h); | ||
304 | BN_clear_free(g_v); | ||
305 | BN_clear_free(r); | ||
306 | BN_clear_free(g_xh); | ||
307 | BN_clear_free(g_r); | ||
308 | BN_clear_free(expected); | ||
309 | return success; | ||
310 | } | ||
311 | |||
312 | #ifdef SCHNORR_MAIN | ||
313 | static void | ||
314 | schnorr_selftest_one(const BIGNUM *grp_p, const BIGNUM *grp_q, | ||
315 | const BIGNUM *grp_g, const BIGNUM *x) | ||
316 | { | ||
317 | BIGNUM *g_x; | ||
318 | u_char *sig; | ||
319 | u_int siglen; | ||
320 | BN_CTX *bn_ctx; | ||
321 | |||
322 | if ((bn_ctx = BN_CTX_new()) == NULL) | ||
323 | fatal("%s: BN_CTX_new", __func__); | ||
324 | if ((g_x = BN_new()) == NULL) | ||
325 | fatal("%s: BN_new", __func__); | ||
326 | |||
327 | if (BN_mod_exp(g_x, grp_g, x, grp_p, bn_ctx) == -1) | ||
328 | fatal("%s: g_x", __func__); | ||
329 | if (schnorr_sign(grp_p, grp_q, grp_g, x, g_x, "junk", 4, &sig, &siglen)) | ||
330 | fatal("%s: schnorr_sign", __func__); | ||
331 | if (schnorr_verify(grp_p, grp_q, grp_g, g_x, "junk", 4, | ||
332 | sig, siglen) != 1) | ||
333 | fatal("%s: verify fail", __func__); | ||
334 | if (schnorr_verify(grp_p, grp_q, grp_g, g_x, "JUNK", 4, | ||
335 | sig, siglen) != 0) | ||
336 | fatal("%s: verify should have failed (bad ID)", __func__); | ||
337 | sig[4] ^= 1; | ||
338 | if (schnorr_verify(grp_p, grp_q, grp_g, g_x, "junk", 4, | ||
339 | sig, siglen) != 0) | ||
340 | fatal("%s: verify should have failed (bit error)", __func__); | ||
341 | xfree(sig); | ||
342 | BN_free(g_x); | ||
343 | BN_CTX_free(bn_ctx); | ||
344 | } | ||
345 | |||
346 | static void | ||
347 | schnorr_selftest(void) | ||
348 | { | ||
349 | BIGNUM *x; | ||
350 | struct jpake_group *grp; | ||
351 | u_int i; | ||
352 | char *hh; | ||
353 | |||
354 | grp = jpake_default_group(); | ||
355 | if ((x = BN_new()) == NULL) | ||
356 | fatal("%s: BN_new", __func__); | ||
357 | SCHNORR_DEBUG_BN((grp->p, "%s: grp->p = ", __func__)); | ||
358 | SCHNORR_DEBUG_BN((grp->q, "%s: grp->q = ", __func__)); | ||
359 | SCHNORR_DEBUG_BN((grp->g, "%s: grp->g = ", __func__)); | ||
360 | |||
361 | /* [1, 20) */ | ||
362 | for (i = 1; i < 20; i++) { | ||
363 | printf("x = %u\n", i); | ||
364 | fflush(stdout); | ||
365 | if (BN_set_word(x, i) != 1) | ||
366 | fatal("%s: set x word", __func__); | ||
367 | schnorr_selftest_one(grp->p, grp->q, grp->g, x); | ||
368 | } | ||
369 | |||
370 | /* 100 x random [0, p) */ | ||
371 | for (i = 0; i < 100; i++) { | ||
372 | if (BN_rand_range(x, grp->p) != 1) | ||
373 | fatal("%s: BN_rand_range", __func__); | ||
374 | hh = BN_bn2hex(x); | ||
375 | printf("x = (random) 0x%s\n", hh); | ||
376 | free(hh); | ||
377 | fflush(stdout); | ||
378 | schnorr_selftest_one(grp->p, grp->q, grp->g, x); | ||
379 | } | ||
380 | |||
381 | /* [q-20, q) */ | ||
382 | if (BN_set_word(x, 20) != 1) | ||
383 | fatal("%s: BN_set_word (x = 20)", __func__); | ||
384 | if (BN_sub(x, grp->q, x) != 1) | ||
385 | fatal("%s: BN_sub (q - x)", __func__); | ||
386 | for (i = 0; i < 19; i++) { | ||
387 | hh = BN_bn2hex(x); | ||
388 | printf("x = (q - %d) 0x%s\n", 20 - i, hh); | ||
389 | free(hh); | ||
390 | fflush(stdout); | ||
391 | schnorr_selftest_one(grp->p, grp->q, grp->g, x); | ||
392 | if (BN_add(x, x, BN_value_one()) != 1) | ||
393 | fatal("%s: BN_add (x + 1)", __func__); | ||
394 | } | ||
395 | BN_free(x); | ||
396 | } | ||
397 | |||
398 | int | ||
399 | main(int argc, char **argv) | ||
400 | { | ||
401 | log_init(argv[0], SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_USER, 1); | ||
402 | |||
403 | schnorr_selftest(); | ||
404 | return 0; | ||
405 | } | ||
406 | #endif | ||
407 | |||
diff --git a/servconf.c b/servconf.c index f2d414334..c7d2d0b94 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.c,v 1.189 2008/11/03 08:59:41 djm Exp $ */ | 1 | /* $OpenBSD: servconf.c,v 1.190 2008/11/04 08:22:13 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | * All rights reserved | 4 | * All rights reserved |
@@ -127,6 +127,7 @@ initialize_server_options(ServerOptions *options) | |||
127 | options->num_permitted_opens = -1; | 127 | options->num_permitted_opens = -1; |
128 | options->adm_forced_command = NULL; | 128 | options->adm_forced_command = NULL; |
129 | options->chroot_directory = NULL; | 129 | options->chroot_directory = NULL; |
130 | options->zero_knowledge_password_authentication = -1; | ||
130 | } | 131 | } |
131 | 132 | ||
132 | void | 133 | void |
@@ -258,6 +259,8 @@ fill_default_server_options(ServerOptions *options) | |||
258 | options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; | 259 | options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; |
259 | if (options->permit_tun == -1) | 260 | if (options->permit_tun == -1) |
260 | options->permit_tun = SSH_TUNMODE_NO; | 261 | options->permit_tun = SSH_TUNMODE_NO; |
262 | if (options->zero_knowledge_password_authentication == -1) | ||
263 | options->zero_knowledge_password_authentication = 0; | ||
261 | 264 | ||
262 | /* Turn privilege separation on by default */ | 265 | /* Turn privilege separation on by default */ |
263 | if (use_privsep == -1) | 266 | if (use_privsep == -1) |
@@ -302,6 +305,7 @@ typedef enum { | |||
302 | sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, | 305 | sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, |
303 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, | 306 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, |
304 | sUsePrivilegeSeparation, sAllowAgentForwarding, | 307 | sUsePrivilegeSeparation, sAllowAgentForwarding, |
308 | sZeroKnowledgePasswordAuthentication, | ||
305 | sDeprecated, sUnsupported | 309 | sDeprecated, sUnsupported |
306 | } ServerOpCodes; | 310 | } ServerOpCodes; |
307 | 311 | ||
@@ -368,6 +372,11 @@ static struct { | |||
368 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, | 372 | { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, |
369 | { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, | 373 | { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, |
370 | { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ | 374 | { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ |
375 | #ifdef JPAKE | ||
376 | { "zeroknowledgepasswordauthentication", sZeroKnowledgePasswordAuthentication, SSHCFG_ALL }, | ||
377 | #else | ||
378 | { "zeroknowledgepasswordauthentication", sUnsupported, SSHCFG_ALL }, | ||
379 | #endif | ||
371 | { "checkmail", sDeprecated, SSHCFG_GLOBAL }, | 380 | { "checkmail", sDeprecated, SSHCFG_GLOBAL }, |
372 | { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, | 381 | { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, |
373 | { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, | 382 | { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, |
@@ -890,6 +899,10 @@ process_server_config_line(ServerOptions *options, char *line, | |||
890 | intptr = &options->password_authentication; | 899 | intptr = &options->password_authentication; |
891 | goto parse_flag; | 900 | goto parse_flag; |
892 | 901 | ||
902 | case sZeroKnowledgePasswordAuthentication: | ||
903 | intptr = &options->zero_knowledge_password_authentication; | ||
904 | goto parse_flag; | ||
905 | |||
893 | case sKbdInteractiveAuthentication: | 906 | case sKbdInteractiveAuthentication: |
894 | intptr = &options->kbd_interactive_authentication; | 907 | intptr = &options->kbd_interactive_authentication; |
895 | goto parse_flag; | 908 | goto parse_flag; |
@@ -1377,6 +1390,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) | |||
1377 | M_CP_INTOPT(kerberos_authentication); | 1390 | M_CP_INTOPT(kerberos_authentication); |
1378 | M_CP_INTOPT(hostbased_authentication); | 1391 | M_CP_INTOPT(hostbased_authentication); |
1379 | M_CP_INTOPT(kbd_interactive_authentication); | 1392 | M_CP_INTOPT(kbd_interactive_authentication); |
1393 | M_CP_INTOPT(zero_knowledge_password_authentication); | ||
1380 | M_CP_INTOPT(permit_root_login); | 1394 | M_CP_INTOPT(permit_root_login); |
1381 | M_CP_INTOPT(permit_empty_passwd); | 1395 | M_CP_INTOPT(permit_empty_passwd); |
1382 | 1396 | ||
@@ -1579,6 +1593,10 @@ dump_config(ServerOptions *o) | |||
1579 | dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); | 1593 | dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); |
1580 | dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); | 1594 | dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); |
1581 | #endif | 1595 | #endif |
1596 | #ifdef JPAKE | ||
1597 | dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication, | ||
1598 | o->zero_knowledge_password_authentication); | ||
1599 | #endif | ||
1582 | dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); | 1600 | dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); |
1583 | dump_cfg_fmtint(sKbdInteractiveAuthentication, | 1601 | dump_cfg_fmtint(sKbdInteractiveAuthentication, |
1584 | o->kbd_interactive_authentication); | 1602 | o->kbd_interactive_authentication); |
diff --git a/servconf.h b/servconf.h index 40ac64f13..1d4c3a01a 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.h,v 1.85 2008/06/10 04:50:25 dtucker Exp $ */ | 1 | /* $OpenBSD: servconf.h,v 1.86 2008/11/04 08:22:13 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -96,6 +96,8 @@ typedef struct { | |||
96 | * authentication. */ | 96 | * authentication. */ |
97 | int kbd_interactive_authentication; /* If true, permit */ | 97 | int kbd_interactive_authentication; /* If true, permit */ |
98 | int challenge_response_authentication; | 98 | int challenge_response_authentication; |
99 | int zero_knowledge_password_authentication; | ||
100 | /* If true, permit jpake auth */ | ||
99 | int permit_empty_passwd; /* If false, do not permit empty | 101 | int permit_empty_passwd; /* If false, do not permit empty |
100 | * passwords. */ | 102 | * passwords. */ |
101 | int permit_user_env; /* If true, read ~/.ssh/environment */ | 103 | int permit_user_env; /* If true, read ~/.ssh/environment */ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh2.h,v 1.10 2006/03/25 22:22:43 djm Exp $ */ | 1 | /* $OpenBSD: ssh2.h,v 1.11 2008/11/04 08:22:13 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
@@ -111,6 +111,12 @@ | |||
111 | #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 | 111 | #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 |
112 | #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 | 112 | #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 |
113 | #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 | 113 | #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 |
114 | #define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1 60 | ||
115 | #define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1 61 | ||
116 | #define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2 62 | ||
117 | #define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2 63 | ||
118 | #define SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM 64 | ||
119 | #define SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM 65 | ||
114 | 120 | ||
115 | /* connection protocol: generic */ | 121 | /* connection protocol: generic */ |
116 | 122 | ||
@@ -159,3 +165,4 @@ | |||
159 | #define SSH2_OPEN_RESOURCE_SHORTAGE 4 | 165 | #define SSH2_OPEN_RESOURCE_SHORTAGE 4 |
160 | 166 | ||
161 | #define SSH2_EXTENDED_DATA_STDERR 1 | 167 | #define SSH2_EXTENDED_DATA_STDERR 1 |
168 | |||
diff --git a/ssh_config.5 b/ssh_config.5 index 254940ef8..abc3b0b16 100644 --- a/ssh_config.5 +++ b/ssh_config.5 | |||
@@ -34,8 +34,8 @@ | |||
34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | .\" | 36 | .\" |
37 | .\" $OpenBSD: ssh_config.5,v 1.114 2008/10/17 18:36:24 stevesk Exp $ | 37 | .\" $OpenBSD: ssh_config.5,v 1.115 2008/11/04 08:22:13 djm Exp $ |
38 | .Dd $Mdocdate: October 17 2008 $ | 38 | .Dd $Mdocdate: November 4 2008 $ |
39 | .Dt SSH_CONFIG 5 | 39 | .Dt SSH_CONFIG 5 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -1079,6 +1079,17 @@ Specifies the full pathname of the | |||
1079 | program. | 1079 | program. |
1080 | The default is | 1080 | The default is |
1081 | .Pa /usr/X11R6/bin/xauth . | 1081 | .Pa /usr/X11R6/bin/xauth . |
1082 | .It Cm ZeroKnowledgePasswordAuthentication | ||
1083 | Specifies whether to use zero knowledge password authentication. | ||
1084 | This authentication method avoids exposure of password to untrusted | ||
1085 | hosts. | ||
1086 | The argument to this keyword must be | ||
1087 | .Dq yes | ||
1088 | or | ||
1089 | .Dq no . | ||
1090 | The default is currently | ||
1091 | .Dq no | ||
1092 | as this method is considered experimental. | ||
1082 | .El | 1093 | .El |
1083 | .Sh PATTERNS | 1094 | .Sh PATTERNS |
1084 | A | 1095 | A |
diff --git a/sshconnect2.c b/sshconnect2.c index 7d0c5e825..a762eec3b 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* $OpenBSD: sshconnect2.c,v 1.169 2008/11/01 04:50:08 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect2.c,v 1.170 2008/11/04 08:22:13 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2008 Damien Miller. All rights reserved. | ||
4 | * | 5 | * |
5 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions | 7 | * modification, are permitted provided that the following conditions |
@@ -67,6 +68,7 @@ | |||
67 | #include "msg.h" | 68 | #include "msg.h" |
68 | #include "pathnames.h" | 69 | #include "pathnames.h" |
69 | #include "uidswap.h" | 70 | #include "uidswap.h" |
71 | #include "jpake.h" | ||
70 | 72 | ||
71 | #ifdef GSSAPI | 73 | #ifdef GSSAPI |
72 | #include "ssh-gss.h" | 74 | #include "ssh-gss.h" |
@@ -201,6 +203,7 @@ struct Authctxt { | |||
201 | struct Authmethod { | 203 | struct Authmethod { |
202 | char *name; /* string to compare against server's list */ | 204 | char *name; /* string to compare against server's list */ |
203 | int (*userauth)(Authctxt *authctxt); | 205 | int (*userauth)(Authctxt *authctxt); |
206 | void (*cleanup)(Authctxt *authctxt); | ||
204 | int *enabled; /* flag in option struct that enables method */ | 207 | int *enabled; /* flag in option struct that enables method */ |
205 | int *batch_flag; /* flag in option struct that disables method */ | 208 | int *batch_flag; /* flag in option struct that disables method */ |
206 | }; | 209 | }; |
@@ -212,12 +215,18 @@ void input_userauth_error(int, u_int32_t, void *); | |||
212 | void input_userauth_info_req(int, u_int32_t, void *); | 215 | void input_userauth_info_req(int, u_int32_t, void *); |
213 | void input_userauth_pk_ok(int, u_int32_t, void *); | 216 | void input_userauth_pk_ok(int, u_int32_t, void *); |
214 | void input_userauth_passwd_changereq(int, u_int32_t, void *); | 217 | void input_userauth_passwd_changereq(int, u_int32_t, void *); |
218 | void input_userauth_jpake_server_step1(int, u_int32_t, void *); | ||
219 | void input_userauth_jpake_server_step2(int, u_int32_t, void *); | ||
220 | void input_userauth_jpake_server_confirm(int, u_int32_t, void *); | ||
215 | 221 | ||
216 | int userauth_none(Authctxt *); | 222 | int userauth_none(Authctxt *); |
217 | int userauth_pubkey(Authctxt *); | 223 | int userauth_pubkey(Authctxt *); |
218 | int userauth_passwd(Authctxt *); | 224 | int userauth_passwd(Authctxt *); |
219 | int userauth_kbdint(Authctxt *); | 225 | int userauth_kbdint(Authctxt *); |
220 | int userauth_hostbased(Authctxt *); | 226 | int userauth_hostbased(Authctxt *); |
227 | int userauth_jpake(Authctxt *); | ||
228 | |||
229 | void userauth_jpake_cleanup(Authctxt *); | ||
221 | 230 | ||
222 | #ifdef GSSAPI | 231 | #ifdef GSSAPI |
223 | int userauth_gssapi(Authctxt *authctxt); | 232 | int userauth_gssapi(Authctxt *authctxt); |
@@ -243,30 +252,43 @@ Authmethod authmethods[] = { | |||
243 | #ifdef GSSAPI | 252 | #ifdef GSSAPI |
244 | {"gssapi-with-mic", | 253 | {"gssapi-with-mic", |
245 | userauth_gssapi, | 254 | userauth_gssapi, |
255 | NULL, | ||
246 | &options.gss_authentication, | 256 | &options.gss_authentication, |
247 | NULL}, | 257 | NULL}, |
248 | #endif | 258 | #endif |
249 | {"hostbased", | 259 | {"hostbased", |
250 | userauth_hostbased, | 260 | userauth_hostbased, |
261 | NULL, | ||
251 | &options.hostbased_authentication, | 262 | &options.hostbased_authentication, |
252 | NULL}, | 263 | NULL}, |
253 | {"publickey", | 264 | {"publickey", |
254 | userauth_pubkey, | 265 | userauth_pubkey, |
266 | NULL, | ||
255 | &options.pubkey_authentication, | 267 | &options.pubkey_authentication, |
256 | NULL}, | 268 | NULL}, |
269 | #ifdef JPAKE | ||
270 | {"jpake-01@openssh.com", | ||
271 | userauth_jpake, | ||
272 | userauth_jpake_cleanup, | ||
273 | &options.zero_knowledge_password_authentication, | ||
274 | &options.batch_mode}, | ||
275 | #endif | ||
257 | {"keyboard-interactive", | 276 | {"keyboard-interactive", |
258 | userauth_kbdint, | 277 | userauth_kbdint, |
278 | NULL, | ||
259 | &options.kbd_interactive_authentication, | 279 | &options.kbd_interactive_authentication, |
260 | &options.batch_mode}, | 280 | &options.batch_mode}, |
261 | {"password", | 281 | {"password", |
262 | userauth_passwd, | 282 | userauth_passwd, |
283 | NULL, | ||
263 | &options.password_authentication, | 284 | &options.password_authentication, |
264 | &options.batch_mode}, | 285 | &options.batch_mode}, |
265 | {"none", | 286 | {"none", |
266 | userauth_none, | 287 | userauth_none, |
267 | NULL, | 288 | NULL, |
289 | NULL, | ||
268 | NULL}, | 290 | NULL}, |
269 | {NULL, NULL, NULL, NULL} | 291 | {NULL, NULL, NULL, NULL, NULL} |
270 | }; | 292 | }; |
271 | 293 | ||
272 | void | 294 | void |
@@ -334,6 +356,9 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host, | |||
334 | void | 356 | void |
335 | userauth(Authctxt *authctxt, char *authlist) | 357 | userauth(Authctxt *authctxt, char *authlist) |
336 | { | 358 | { |
359 | if (authctxt->method != NULL && authctxt->method->cleanup != NULL) | ||
360 | authctxt->method->cleanup(authctxt); | ||
361 | |||
337 | if (authctxt->methoddata) { | 362 | if (authctxt->methoddata) { |
338 | xfree(authctxt->methoddata); | 363 | xfree(authctxt->methoddata); |
339 | authctxt->methoddata = NULL; | 364 | authctxt->methoddata = NULL; |
@@ -851,6 +876,209 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) | |||
851 | &input_userauth_passwd_changereq); | 876 | &input_userauth_passwd_changereq); |
852 | } | 877 | } |
853 | 878 | ||
879 | #ifdef JPAKE | ||
880 | static char * | ||
881 | pw_encrypt(const char *password, const char *crypt_scheme, const char *salt) | ||
882 | { | ||
883 | /* OpenBSD crypt(3) handles all of these */ | ||
884 | if (strcmp(crypt_scheme, "crypt") == 0 || | ||
885 | strcmp(crypt_scheme, "bcrypt") == 0 || | ||
886 | strcmp(crypt_scheme, "md5crypt") == 0 || | ||
887 | strcmp(crypt_scheme, "crypt-extended") == 0) | ||
888 | return xstrdup(crypt(password, salt)); | ||
889 | error("%s: unsupported password encryption scheme \"%.100s\"", | ||
890 | __func__, crypt_scheme); | ||
891 | return NULL; | ||
892 | } | ||
893 | |||
894 | static BIGNUM * | ||
895 | jpake_password_to_secret(Authctxt *authctxt, const char *crypt_scheme, | ||
896 | const char *salt) | ||
897 | { | ||
898 | char prompt[256], *password, *crypted; | ||
899 | u_char *secret; | ||
900 | u_int secret_len; | ||
901 | BIGNUM *ret; | ||
902 | |||
903 | snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password (JPAKE): ", | ||
904 | authctxt->server_user, authctxt->host); | ||
905 | password = read_passphrase(prompt, 0); | ||
906 | |||
907 | if ((crypted = pw_encrypt(password, crypt_scheme, salt)) == NULL) { | ||
908 | logit("Disabling %s authentication", authctxt->method->name); | ||
909 | authctxt->method->enabled = NULL; | ||
910 | /* Continue with an empty password to fail gracefully */ | ||
911 | crypted = xstrdup(""); | ||
912 | } | ||
913 | |||
914 | #ifdef JPAKE_DEBUG | ||
915 | debug3("%s: salt = %s", __func__, salt); | ||
916 | debug3("%s: scheme = %s", __func__, crypt_scheme); | ||
917 | debug3("%s: crypted = %s", __func__, crypted); | ||
918 | #endif | ||
919 | |||
920 | if (hash_buffer(crypted, strlen(crypted), EVP_sha256(), | ||
921 | &secret, &secret_len) != 0) | ||
922 | fatal("%s: hash_buffer", __func__); | ||
923 | |||
924 | bzero(password, strlen(password)); | ||
925 | bzero(crypted, strlen(crypted)); | ||
926 | xfree(password); | ||
927 | xfree(crypted); | ||
928 | |||
929 | if ((ret = BN_bin2bn(secret, secret_len, NULL)) == NULL) | ||
930 | fatal("%s: BN_bin2bn (secret)", __func__); | ||
931 | bzero(secret, secret_len); | ||
932 | xfree(secret); | ||
933 | |||
934 | return ret; | ||
935 | } | ||
936 | |||
937 | /* ARGSUSED */ | ||
938 | void | ||
939 | input_userauth_jpake_server_step1(int type, u_int32_t seq, void *ctxt) | ||
940 | { | ||
941 | Authctxt *authctxt = ctxt; | ||
942 | struct jpake_ctx *pctx = authctxt->methoddata; | ||
943 | u_char *x3_proof, *x4_proof, *x2_s_proof; | ||
944 | u_int x3_proof_len, x4_proof_len, x2_s_proof_len; | ||
945 | char *crypt_scheme, *salt; | ||
946 | |||
947 | /* Disable this message */ | ||
948 | dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, NULL); | ||
949 | |||
950 | if ((pctx->g_x3 = BN_new()) == NULL || | ||
951 | (pctx->g_x4 = BN_new()) == NULL) | ||
952 | fatal("%s: BN_new", __func__); | ||
953 | |||
954 | /* Fetch step 1 values */ | ||
955 | crypt_scheme = packet_get_string(NULL); | ||
956 | salt = packet_get_string(NULL); | ||
957 | pctx->server_id = packet_get_string(&pctx->server_id_len); | ||
958 | packet_get_bignum2(pctx->g_x3); | ||
959 | packet_get_bignum2(pctx->g_x4); | ||
960 | x3_proof = packet_get_string(&x3_proof_len); | ||
961 | x4_proof = packet_get_string(&x4_proof_len); | ||
962 | packet_check_eom(); | ||
963 | |||
964 | JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__)); | ||
965 | |||
966 | /* Obtain password and derive secret */ | ||
967 | pctx->s = jpake_password_to_secret(authctxt, crypt_scheme, salt); | ||
968 | bzero(crypt_scheme, strlen(crypt_scheme)); | ||
969 | bzero(salt, strlen(salt)); | ||
970 | xfree(crypt_scheme); | ||
971 | xfree(salt); | ||
972 | JPAKE_DEBUG_BN((pctx->s, "%s: s = ", __func__)); | ||
973 | |||
974 | /* Calculate step 2 values */ | ||
975 | jpake_step2(pctx->grp, pctx->s, pctx->g_x1, | ||
976 | pctx->g_x3, pctx->g_x4, pctx->x2, | ||
977 | pctx->server_id, pctx->server_id_len, | ||
978 | pctx->client_id, pctx->client_id_len, | ||
979 | x3_proof, x3_proof_len, | ||
980 | x4_proof, x4_proof_len, | ||
981 | &pctx->a, | ||
982 | &x2_s_proof, &x2_s_proof_len); | ||
983 | |||
984 | bzero(x3_proof, x3_proof_len); | ||
985 | bzero(x4_proof, x4_proof_len); | ||
986 | xfree(x3_proof); | ||
987 | xfree(x4_proof); | ||
988 | |||
989 | JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__)); | ||
990 | |||
991 | /* Send values for step 2 */ | ||
992 | packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2); | ||
993 | packet_put_bignum2(pctx->a); | ||
994 | packet_put_string(x2_s_proof, x2_s_proof_len); | ||
995 | packet_send(); | ||
996 | |||
997 | bzero(x2_s_proof, x2_s_proof_len); | ||
998 | xfree(x2_s_proof); | ||
999 | |||
1000 | /* Expect step 2 packet from peer */ | ||
1001 | dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, | ||
1002 | input_userauth_jpake_server_step2); | ||
1003 | } | ||
1004 | |||
1005 | /* ARGSUSED */ | ||
1006 | void | ||
1007 | input_userauth_jpake_server_step2(int type, u_int32_t seq, void *ctxt) | ||
1008 | { | ||
1009 | Authctxt *authctxt = ctxt; | ||
1010 | struct jpake_ctx *pctx = authctxt->methoddata; | ||
1011 | u_char *x4_s_proof; | ||
1012 | u_int x4_s_proof_len; | ||
1013 | |||
1014 | /* Disable this message */ | ||
1015 | dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, NULL); | ||
1016 | |||
1017 | if ((pctx->b = BN_new()) == NULL) | ||
1018 | fatal("%s: BN_new", __func__); | ||
1019 | |||
1020 | /* Fetch step 2 values */ | ||
1021 | packet_get_bignum2(pctx->b); | ||
1022 | x4_s_proof = packet_get_string(&x4_s_proof_len); | ||
1023 | packet_check_eom(); | ||
1024 | |||
1025 | JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__)); | ||
1026 | |||
1027 | /* Derive shared key and calculate confirmation hash */ | ||
1028 | jpake_key_confirm(pctx->grp, pctx->s, pctx->b, | ||
1029 | pctx->x2, pctx->g_x1, pctx->g_x2, pctx->g_x3, pctx->g_x4, | ||
1030 | pctx->client_id, pctx->client_id_len, | ||
1031 | pctx->server_id, pctx->server_id_len, | ||
1032 | session_id2, session_id2_len, | ||
1033 | x4_s_proof, x4_s_proof_len, | ||
1034 | &pctx->k, | ||
1035 | &pctx->h_k_cid_sessid, &pctx->h_k_cid_sessid_len); | ||
1036 | |||
1037 | bzero(x4_s_proof, x4_s_proof_len); | ||
1038 | xfree(x4_s_proof); | ||
1039 | |||
1040 | JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__)); | ||
1041 | |||
1042 | /* Send key confirmation proof */ | ||
1043 | packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM); | ||
1044 | packet_put_string(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len); | ||
1045 | packet_send(); | ||
1046 | |||
1047 | /* Expect confirmation from peer */ | ||
1048 | dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, | ||
1049 | input_userauth_jpake_server_confirm); | ||
1050 | } | ||
1051 | |||
1052 | /* ARGSUSED */ | ||
1053 | void | ||
1054 | input_userauth_jpake_server_confirm(int type, u_int32_t seq, void *ctxt) | ||
1055 | { | ||
1056 | Authctxt *authctxt = ctxt; | ||
1057 | struct jpake_ctx *pctx = authctxt->methoddata; | ||
1058 | |||
1059 | /* Disable this message */ | ||
1060 | dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, NULL); | ||
1061 | |||
1062 | pctx->h_k_sid_sessid = packet_get_string(&pctx->h_k_sid_sessid_len); | ||
1063 | packet_check_eom(); | ||
1064 | |||
1065 | JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__)); | ||
1066 | |||
1067 | /* Verify expected confirmation hash */ | ||
1068 | if (jpake_check_confirm(pctx->k, | ||
1069 | pctx->server_id, pctx->server_id_len, | ||
1070 | session_id2, session_id2_len, | ||
1071 | pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len) == 1) | ||
1072 | debug("%s: %s success", __func__, authctxt->method->name); | ||
1073 | else { | ||
1074 | debug("%s: confirmation mismatch", __func__); | ||
1075 | /* XXX stash this so if auth succeeds then we can warn/kill */ | ||
1076 | } | ||
1077 | |||
1078 | userauth_jpake_cleanup(authctxt); | ||
1079 | } | ||
1080 | #endif /* JPAKE */ | ||
1081 | |||
854 | static int | 1082 | static int |
855 | identity_sign(Identity *id, u_char **sigp, u_int *lenp, | 1083 | identity_sign(Identity *id, u_char **sigp, u_int *lenp, |
856 | u_char *data, u_int datalen) | 1084 | u_char *data, u_int datalen) |
@@ -1425,6 +1653,76 @@ userauth_hostbased(Authctxt *authctxt) | |||
1425 | return 1; | 1653 | return 1; |
1426 | } | 1654 | } |
1427 | 1655 | ||
1656 | #ifdef JPAKE | ||
1657 | int | ||
1658 | userauth_jpake(Authctxt *authctxt) | ||
1659 | { | ||
1660 | struct jpake_ctx *pctx; | ||
1661 | u_char *x1_proof, *x2_proof; | ||
1662 | u_int x1_proof_len, x2_proof_len; | ||
1663 | static int attempt = 0; /* XXX share with userauth_password's? */ | ||
1664 | |||
1665 | if (attempt++ >= options.number_of_password_prompts) | ||
1666 | return 0; | ||
1667 | if (attempt != 1) | ||
1668 | error("Permission denied, please try again."); | ||
1669 | |||
1670 | if (authctxt->methoddata != NULL) | ||
1671 | fatal("%s: authctxt->methoddata already set (%p)", | ||
1672 | __func__, authctxt->methoddata); | ||
1673 | |||
1674 | authctxt->methoddata = pctx = jpake_new(); | ||
1675 | |||
1676 | /* | ||
1677 | * Send request immediately, to get the protocol going while | ||
1678 | * we do the initial computations. | ||
1679 | */ | ||
1680 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
1681 | packet_put_cstring(authctxt->server_user); | ||
1682 | packet_put_cstring(authctxt->service); | ||
1683 | packet_put_cstring(authctxt->method->name); | ||
1684 | packet_send(); | ||
1685 | packet_write_wait(); | ||
1686 | |||
1687 | jpake_step1(pctx->grp, | ||
1688 | &pctx->client_id, &pctx->client_id_len, | ||
1689 | &pctx->x1, &pctx->x2, &pctx->g_x1, &pctx->g_x2, | ||
1690 | &x1_proof, &x1_proof_len, | ||
1691 | &x2_proof, &x2_proof_len); | ||
1692 | |||
1693 | JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__)); | ||
1694 | |||
1695 | packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1); | ||
1696 | packet_put_string(pctx->client_id, pctx->client_id_len); | ||
1697 | packet_put_bignum2(pctx->g_x1); | ||
1698 | packet_put_bignum2(pctx->g_x2); | ||
1699 | packet_put_string(x1_proof, x1_proof_len); | ||
1700 | packet_put_string(x2_proof, x2_proof_len); | ||
1701 | packet_send(); | ||
1702 | |||
1703 | bzero(x1_proof, x1_proof_len); | ||
1704 | bzero(x2_proof, x2_proof_len); | ||
1705 | xfree(x1_proof); | ||
1706 | xfree(x2_proof); | ||
1707 | |||
1708 | /* Expect step 1 packet from peer */ | ||
1709 | dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, | ||
1710 | input_userauth_jpake_server_step1); | ||
1711 | |||
1712 | return 1; | ||
1713 | } | ||
1714 | |||
1715 | void | ||
1716 | userauth_jpake_cleanup(Authctxt *authctxt) | ||
1717 | { | ||
1718 | debug3("%s: clean up", __func__); | ||
1719 | if (authctxt->methoddata != NULL) { | ||
1720 | jpake_free(authctxt->methoddata); | ||
1721 | authctxt->methoddata = NULL; | ||
1722 | } | ||
1723 | } | ||
1724 | #endif /* JPAKE */ | ||
1725 | |||
1428 | /* find auth method */ | 1726 | /* find auth method */ |
1429 | 1727 | ||
1430 | /* | 1728 | /* |
@@ -1526,3 +1824,4 @@ authmethods_get(void) | |||
1526 | buffer_free(&b); | 1824 | buffer_free(&b); |
1527 | return list; | 1825 | return list; |
1528 | } | 1826 | } |
1827 | |||
diff --git a/sshd_config.5 b/sshd_config.5 index 06fe5fd38..a4a4be6e5 100644 --- a/sshd_config.5 +++ b/sshd_config.5 | |||
@@ -34,8 +34,8 @@ | |||
34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | .\" | 36 | .\" |
37 | .\" $OpenBSD: sshd_config.5,v 1.97 2008/10/09 03:50:54 djm Exp $ | 37 | .\" $OpenBSD: sshd_config.5,v 1.98 2008/11/04 08:22:13 djm Exp $ |
38 | .Dd $Mdocdate: October 9 2008 $ | 38 | .Dd $Mdocdate: November 4 2008 $ |
39 | .Dt SSHD_CONFIG 5 | 39 | .Dt SSHD_CONFIG 5 |
40 | .Os | 40 | .Os |
41 | .Sh NAME | 41 | .Sh NAME |
@@ -612,8 +612,9 @@ Available keywords are | |||
612 | .Cm RSAAuthentication , | 612 | .Cm RSAAuthentication , |
613 | .Cm X11DisplayOffset , | 613 | .Cm X11DisplayOffset , |
614 | .Cm X11Forwarding , | 614 | .Cm X11Forwarding , |
615 | .Cm X11UseLocalHost , | ||
615 | and | 616 | and |
616 | .Cm X11UseLocalHost . | 617 | .Cm ZeroKnowledgePasswordAuthentication . |
617 | .It Cm MaxAuthTries | 618 | .It Cm MaxAuthTries |
618 | Specifies the maximum number of authentication attempts permitted per | 619 | Specifies the maximum number of authentication attempts permitted per |
619 | connection. | 620 | connection. |
@@ -1004,6 +1005,17 @@ Specifies the full pathname of the | |||
1004 | program. | 1005 | program. |
1005 | The default is | 1006 | The default is |
1006 | .Pa /usr/X11R6/bin/xauth . | 1007 | .Pa /usr/X11R6/bin/xauth . |
1008 | .It Cm ZeroKnowledgePasswordAuthentication | ||
1009 | Specifies whether to use zero knowledge password authentication. | ||
1010 | This authentication method avoids exposure of password to untrusted | ||
1011 | hosts. | ||
1012 | The argument to this keyword must be | ||
1013 | .Dq yes | ||
1014 | or | ||
1015 | .Dq no . | ||
1016 | The default is currently | ||
1017 | .Dq no | ||
1018 | as this method is considered experimental. | ||
1007 | .El | 1019 | .El |
1008 | .Sh TIME FORMATS | 1020 | .Sh TIME FORMATS |
1009 | .Xr sshd 8 | 1021 | .Xr sshd 8 |