diff options
author | Damien Miller <djm@mindrot.org> | 2000-10-14 16:23:11 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2000-10-14 16:23:11 +1100 |
commit | 874d77bb134a21a5cf625956b60173376a993ba8 (patch) | |
tree | 93dd73b2ff1fbf0ad5f3978a2c4e0d8438a0bf7c /sshconnect2.c | |
parent | 89d9796fbedef4eed6956a2c095c7cc25330c28d (diff) |
- (djm) Big OpenBSD sync:
- markus@cvs.openbsd.org 2000/09/30 10:27:44
[log.c]
allow loglevel debug
- markus@cvs.openbsd.org 2000/10/03 11:59:57
[packet.c]
hmac->mac
- markus@cvs.openbsd.org 2000/10/03 12:03:03
[auth-krb4.c auth-passwd.c auth-rh-rsa.c auth-rhosts.c auth-rsa.c auth1.c]
move fake-auth from auth1.c to individual auth methods, disables s/key in
debug-msg
- markus@cvs.openbsd.org 2000/10/03 12:16:48
ssh.c
do not resolve canonname, i have no idea why this was added oin ossh
- markus@cvs.openbsd.org 2000/10/09 15:30:44
ssh-keygen.1 ssh-keygen.c
-X now reads private ssh.com DSA keys, too.
- markus@cvs.openbsd.org 2000/10/09 15:32:34
auth-options.c
clear options on every call.
- markus@cvs.openbsd.org 2000/10/09 15:51:00
authfd.c authfd.h
interop with ssh-agent2, from <res@shore.net>
- markus@cvs.openbsd.org 2000/10/10 14:20:45
compat.c
use rexexp for version string matching
- provos@cvs.openbsd.org 2000/10/10 22:02:18
[kex.c kex.h myproposal.h ssh.h ssh2.h sshconnect2.c sshd.c dh.c dh.h]
First rough implementation of the diffie-hellman group exchange. The
client can ask the server for bigger groups to perform the diffie-hellman
in, thus increasing the attack complexity when using ciphers with longer
keys. University of Windsor provided network, T the company.
- markus@cvs.openbsd.org 2000/10/11 13:59:52
[auth-rsa.c auth2.c]
clear auth options unless auth sucessfull
- markus@cvs.openbsd.org 2000/10/11 14:00:27
[auth-options.h]
clear auth options unless auth sucessfull
- markus@cvs.openbsd.org 2000/10/11 14:03:27
[scp.1 scp.c]
support 'scp -o' with help from mouring@pconline.com
- markus@cvs.openbsd.org 2000/10/11 14:11:35
[dh.c]
Wall
- markus@cvs.openbsd.org 2000/10/11 14:14:40
[auth.h auth2.c readconf.c readconf.h readpass.c servconf.c servconf.h]
[ssh.h sshconnect2.c sshd_config auth2-skey.c cli.c cli.h]
add support for s/key (kbd-interactive) to ssh2, based on work by
mkiernan@avantgo.com and me
- markus@cvs.openbsd.org 2000/10/11 14:27:24
[auth.c auth1.c auth2.c authfile.c cipher.c cipher.h kex.c kex.h]
[myproposal.h packet.c readconf.c session.c ssh.c ssh.h sshconnect1.c]
[sshconnect2.c sshd.c]
new cipher framework
- markus@cvs.openbsd.org 2000/10/11 14:45:21
[cipher.c]
remove DES
- markus@cvs.openbsd.org 2000/10/12 03:59:20
[cipher.c cipher.h sshconnect1.c sshconnect2.c sshd.c]
enable DES in SSH-1 clients only
- markus@cvs.openbsd.org 2000/10/12 08:21:13
[kex.h packet.c]
remove unused
- markus@cvs.openbsd.org 2000/10/13 12:34:46
[sshd.c]
Kludge for F-Secure Macintosh < 1.0.2; appro@fy.chalmers.se
- markus@cvs.openbsd.org 2000/10/13 12:59:15
[cipher.c cipher.h myproposal.h rijndael.c rijndael.h]
rijndael/aes support
- markus@cvs.openbsd.org 2000/10/13 13:10:54
[sshd.8]
more info about -V
- markus@cvs.openbsd.org 2000/10/13 13:12:02
[myproposal.h]
prefer no compression
Diffstat (limited to 'sshconnect2.c')
-rw-r--r-- | sshconnect2.c | 461 |
1 files changed, 381 insertions, 80 deletions
diff --git a/sshconnect2.c b/sshconnect2.c index eee09a19c..ca459f62c 100644 --- a/sshconnect2.c +++ b/sshconnect2.c | |||
@@ -23,7 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include "includes.h" | 25 | #include "includes.h" |
26 | RCSID("$OpenBSD: sshconnect2.c,v 1.21 2000/09/27 21:41:34 markus Exp $"); | 26 | RCSID("$OpenBSD: sshconnect2.c,v 1.25 2000/10/12 09:59:19 markus Exp $"); |
27 | 27 | ||
28 | #include <openssl/bn.h> | 28 | #include <openssl/bn.h> |
29 | #include <openssl/rsa.h> | 29 | #include <openssl/rsa.h> |
@@ -37,7 +37,6 @@ RCSID("$OpenBSD: sshconnect2.c,v 1.21 2000/09/27 21:41:34 markus Exp $"); | |||
37 | #include "rsa.h" | 37 | #include "rsa.h" |
38 | #include "buffer.h" | 38 | #include "buffer.h" |
39 | #include "packet.h" | 39 | #include "packet.h" |
40 | #include "cipher.h" | ||
41 | #include "uidswap.h" | 40 | #include "uidswap.h" |
42 | #include "compat.h" | 41 | #include "compat.h" |
43 | #include "readconf.h" | 42 | #include "readconf.h" |
@@ -49,9 +48,13 @@ RCSID("$OpenBSD: sshconnect2.c,v 1.21 2000/09/27 21:41:34 markus Exp $"); | |||
49 | #include "dsa.h" | 48 | #include "dsa.h" |
50 | #include "sshconnect.h" | 49 | #include "sshconnect.h" |
51 | #include "authfile.h" | 50 | #include "authfile.h" |
51 | #include "cli.h" | ||
52 | #include "dispatch.h" | 52 | #include "dispatch.h" |
53 | #include "authfd.h" | 53 | #include "authfd.h" |
54 | 54 | ||
55 | void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); | ||
56 | void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); | ||
57 | |||
55 | /* import */ | 58 | /* import */ |
56 | extern char *client_version_string; | 59 | extern char *client_version_string; |
57 | extern char *server_version_string; | 60 | extern char *server_version_string; |
@@ -65,8 +68,90 @@ unsigned char *session_id2 = NULL; | |||
65 | int session_id2_len = 0; | 68 | int session_id2_len = 0; |
66 | 69 | ||
67 | void | 70 | void |
68 | ssh_kex_dh(Kex *kex, char *host, struct sockaddr *hostaddr, | 71 | ssh_kex2(char *host, struct sockaddr *hostaddr) |
69 | Buffer *client_kexinit, Buffer *server_kexinit) | 72 | { |
73 | int i, plen; | ||
74 | Kex *kex; | ||
75 | Buffer *client_kexinit, *server_kexinit; | ||
76 | char *sprop[PROPOSAL_MAX]; | ||
77 | |||
78 | if (options.ciphers == NULL) { | ||
79 | if (options.cipher == SSH_CIPHER_3DES) { | ||
80 | options.ciphers = "3des-cbc"; | ||
81 | } else if (options.cipher == SSH_CIPHER_BLOWFISH) { | ||
82 | options.ciphers = "blowfish-cbc"; | ||
83 | } else if (options.cipher == SSH_CIPHER_DES) { | ||
84 | fatal("cipher DES not supported for protocol version 2"); | ||
85 | } | ||
86 | } | ||
87 | if (options.ciphers != NULL) { | ||
88 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
89 | myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | ||
90 | } | ||
91 | if (options.compression) { | ||
92 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; | ||
93 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; | ||
94 | } else { | ||
95 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; | ||
96 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; | ||
97 | } | ||
98 | |||
99 | /* buffers with raw kexinit messages */ | ||
100 | server_kexinit = xmalloc(sizeof(*server_kexinit)); | ||
101 | buffer_init(server_kexinit); | ||
102 | client_kexinit = kex_init(myproposal); | ||
103 | |||
104 | /* algorithm negotiation */ | ||
105 | kex_exchange_kexinit(client_kexinit, server_kexinit, sprop); | ||
106 | kex = kex_choose_conf(myproposal, sprop, 0); | ||
107 | for (i = 0; i < PROPOSAL_MAX; i++) | ||
108 | xfree(sprop[i]); | ||
109 | |||
110 | /* server authentication and session key agreement */ | ||
111 | switch(kex->kex_type) { | ||
112 | case DH_GRP1_SHA1: | ||
113 | ssh_dh1_client(kex, host, hostaddr, | ||
114 | client_kexinit, server_kexinit); | ||
115 | break; | ||
116 | case DH_GEX_SHA1: | ||
117 | ssh_dhgex_client(kex, host, hostaddr, client_kexinit, | ||
118 | server_kexinit); | ||
119 | break; | ||
120 | default: | ||
121 | fatal("Unsupported key exchange %d", kex->kex_type); | ||
122 | } | ||
123 | |||
124 | buffer_free(client_kexinit); | ||
125 | buffer_free(server_kexinit); | ||
126 | xfree(client_kexinit); | ||
127 | xfree(server_kexinit); | ||
128 | |||
129 | debug("Wait SSH2_MSG_NEWKEYS."); | ||
130 | packet_read_expect(&plen, SSH2_MSG_NEWKEYS); | ||
131 | packet_done(); | ||
132 | debug("GOT SSH2_MSG_NEWKEYS."); | ||
133 | |||
134 | debug("send SSH2_MSG_NEWKEYS."); | ||
135 | packet_start(SSH2_MSG_NEWKEYS); | ||
136 | packet_send(); | ||
137 | packet_write_wait(); | ||
138 | debug("done: send SSH2_MSG_NEWKEYS."); | ||
139 | |||
140 | #ifdef DEBUG_KEXDH | ||
141 | /* send 1st encrypted/maced/compressed message */ | ||
142 | packet_start(SSH2_MSG_IGNORE); | ||
143 | packet_put_cstring("markus"); | ||
144 | packet_send(); | ||
145 | packet_write_wait(); | ||
146 | #endif | ||
147 | debug("done: KEX2."); | ||
148 | } | ||
149 | |||
150 | /* diffie-hellman-group1-sha1 */ | ||
151 | |||
152 | void | ||
153 | ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr, | ||
154 | Buffer *client_kexinit, Buffer *server_kexinit) | ||
70 | { | 155 | { |
71 | #ifdef DEBUG_KEXDH | 156 | #ifdef DEBUG_KEXDH |
72 | int i; | 157 | int i; |
@@ -116,7 +201,7 @@ ssh_kex_dh(Kex *kex, char *host, struct sockaddr *hostaddr, | |||
116 | fatal("cannot decode server_host_key_blob"); | 201 | fatal("cannot decode server_host_key_blob"); |
117 | 202 | ||
118 | check_host_key(host, hostaddr, server_host_key, | 203 | check_host_key(host, hostaddr, server_host_key, |
119 | options.user_hostfile2, options.system_hostfile2); | 204 | options.user_hostfile2, options.system_hostfile2); |
120 | 205 | ||
121 | /* DH paramter f, server public DH key */ | 206 | /* DH paramter f, server public DH key */ |
122 | dh_server_pub = BN_new(); | 207 | dh_server_pub = BN_new(); |
@@ -186,72 +271,175 @@ ssh_kex_dh(Kex *kex, char *host, struct sockaddr *hostaddr, | |||
186 | memcpy(session_id2, hash, session_id2_len); | 271 | memcpy(session_id2, hash, session_id2_len); |
187 | } | 272 | } |
188 | 273 | ||
274 | /* diffie-hellman-group-exchange-sha1 */ | ||
275 | |||
276 | /* | ||
277 | * Estimates the group order for a Diffie-Hellman group that has an | ||
278 | * attack complexity approximately the same as O(2**bits). Estimate | ||
279 | * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) | ||
280 | */ | ||
281 | |||
282 | int | ||
283 | dh_estimate(int bits) | ||
284 | { | ||
285 | |||
286 | if (bits < 64) | ||
287 | return (512); /* O(2**63) */ | ||
288 | if (bits < 128) | ||
289 | return (1024); /* O(2**86) */ | ||
290 | if (bits < 192) | ||
291 | return (2048); /* O(2**116) */ | ||
292 | return (4096); /* O(2**156) */ | ||
293 | } | ||
294 | |||
189 | void | 295 | void |
190 | ssh_kex2(char *host, struct sockaddr *hostaddr) | 296 | ssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr, |
297 | Buffer *client_kexinit, Buffer *server_kexinit) | ||
191 | { | 298 | { |
192 | int i, plen; | 299 | #ifdef DEBUG_KEXDH |
193 | Kex *kex; | 300 | int i; |
194 | Buffer *client_kexinit, *server_kexinit; | 301 | #endif |
195 | char *sprop[PROPOSAL_MAX]; | 302 | int plen, dlen; |
303 | unsigned int klen, kout; | ||
304 | char *signature = NULL; | ||
305 | unsigned int slen, nbits; | ||
306 | char *server_host_key_blob = NULL; | ||
307 | Key *server_host_key; | ||
308 | unsigned int sbloblen; | ||
309 | DH *dh; | ||
310 | BIGNUM *dh_server_pub = 0; | ||
311 | BIGNUM *shared_secret = 0; | ||
312 | BIGNUM *p = 0, *g = 0; | ||
313 | unsigned char *kbuf; | ||
314 | unsigned char *hash; | ||
196 | 315 | ||
197 | if (options.ciphers != NULL) { | 316 | nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8); |
198 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
199 | myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | ||
200 | } else if (options.cipher == SSH_CIPHER_3DES) { | ||
201 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
202 | myproposal[PROPOSAL_ENC_ALGS_STOC] = | ||
203 | (char *) cipher_name(SSH_CIPHER_3DES_CBC); | ||
204 | } else if (options.cipher == SSH_CIPHER_BLOWFISH) { | ||
205 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
206 | myproposal[PROPOSAL_ENC_ALGS_STOC] = | ||
207 | (char *) cipher_name(SSH_CIPHER_BLOWFISH_CBC); | ||
208 | } | ||
209 | if (options.compression) { | ||
210 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; | ||
211 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; | ||
212 | } else { | ||
213 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; | ||
214 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; | ||
215 | } | ||
216 | 317 | ||
217 | /* buffers with raw kexinit messages */ | 318 | debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST."); |
218 | server_kexinit = xmalloc(sizeof(*server_kexinit)); | 319 | packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); |
219 | buffer_init(server_kexinit); | 320 | packet_put_int(nbits); |
220 | client_kexinit = kex_init(myproposal); | 321 | packet_send(); |
322 | packet_write_wait(); | ||
221 | 323 | ||
222 | /* algorithm negotiation */ | 324 | #ifdef DEBUG_KEXDH |
223 | kex_exchange_kexinit(client_kexinit, server_kexinit, sprop); | 325 | fprintf(stderr, "\nnbits = %d", nbits); |
224 | kex = kex_choose_conf(myproposal, sprop, 0); | 326 | #endif |
225 | for (i = 0; i < PROPOSAL_MAX; i++) | ||
226 | xfree(sprop[i]); | ||
227 | 327 | ||
228 | /* server authentication and session key agreement */ | 328 | debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP."); |
229 | ssh_kex_dh(kex, host, hostaddr, client_kexinit, server_kexinit); | ||
230 | 329 | ||
231 | buffer_free(client_kexinit); | 330 | packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP); |
232 | buffer_free(server_kexinit); | ||
233 | xfree(client_kexinit); | ||
234 | xfree(server_kexinit); | ||
235 | 331 | ||
236 | debug("Wait SSH2_MSG_NEWKEYS."); | 332 | debug("Got SSH2_MSG_KEX_DH_GEX_GROUP."); |
237 | packet_read_expect(&plen, SSH2_MSG_NEWKEYS); | ||
238 | packet_done(); | ||
239 | debug("GOT SSH2_MSG_NEWKEYS."); | ||
240 | 333 | ||
241 | debug("send SSH2_MSG_NEWKEYS."); | 334 | if ((p = BN_new()) == NULL) |
242 | packet_start(SSH2_MSG_NEWKEYS); | 335 | fatal("BN_new"); |
243 | packet_send(); | 336 | packet_get_bignum2(p, &dlen); |
244 | packet_write_wait(); | 337 | if ((g = BN_new()) == NULL) |
245 | debug("done: send SSH2_MSG_NEWKEYS."); | 338 | fatal("BN_new"); |
339 | packet_get_bignum2(g, &dlen); | ||
340 | if ((dh = dh_new_group(g, p)) == NULL) | ||
341 | fatal("dh_new_group"); | ||
246 | 342 | ||
247 | #ifdef DEBUG_KEXDH | 343 | #ifdef DEBUG_KEXDH |
248 | /* send 1st encrypted/maced/compressed message */ | 344 | fprintf(stderr, "\np= "); |
249 | packet_start(SSH2_MSG_IGNORE); | 345 | BN_print_fp(stderr, dh->p); |
250 | packet_put_cstring("markus"); | 346 | fprintf(stderr, "\ng= "); |
347 | BN_print_fp(stderr, dh->g); | ||
348 | fprintf(stderr, "\npub= "); | ||
349 | BN_print_fp(stderr, dh->pub_key); | ||
350 | fprintf(stderr, "\n"); | ||
351 | DHparams_print_fp(stderr, dh); | ||
352 | #endif | ||
353 | |||
354 | debug("Sending SSH2_MSG_KEX_DH_GEX_INIT."); | ||
355 | /* generate and send 'e', client DH public key */ | ||
356 | packet_start(SSH2_MSG_KEX_DH_GEX_INIT); | ||
357 | packet_put_bignum2(dh->pub_key); | ||
251 | packet_send(); | 358 | packet_send(); |
252 | packet_write_wait(); | 359 | packet_write_wait(); |
360 | |||
361 | debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY."); | ||
362 | |||
363 | packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY); | ||
364 | |||
365 | debug("Got SSH2_MSG_KEXDH_REPLY."); | ||
366 | |||
367 | /* key, cert */ | ||
368 | server_host_key_blob = packet_get_string(&sbloblen); | ||
369 | server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); | ||
370 | if (server_host_key == NULL) | ||
371 | fatal("cannot decode server_host_key_blob"); | ||
372 | |||
373 | check_host_key(host, hostaddr, server_host_key, | ||
374 | options.user_hostfile2, options.system_hostfile2); | ||
375 | |||
376 | /* DH paramter f, server public DH key */ | ||
377 | dh_server_pub = BN_new(); | ||
378 | if (dh_server_pub == NULL) | ||
379 | fatal("dh_server_pub == NULL"); | ||
380 | packet_get_bignum2(dh_server_pub, &dlen); | ||
381 | |||
382 | #ifdef DEBUG_KEXDH | ||
383 | fprintf(stderr, "\ndh_server_pub= "); | ||
384 | BN_print_fp(stderr, dh_server_pub); | ||
385 | fprintf(stderr, "\n"); | ||
386 | debug("bits %d", BN_num_bits(dh_server_pub)); | ||
253 | #endif | 387 | #endif |
254 | debug("done: KEX2."); | 388 | |
389 | /* signed H */ | ||
390 | signature = packet_get_string(&slen); | ||
391 | packet_done(); | ||
392 | |||
393 | if (!dh_pub_is_valid(dh, dh_server_pub)) | ||
394 | packet_disconnect("bad server public DH value"); | ||
395 | |||
396 | klen = DH_size(dh); | ||
397 | kbuf = xmalloc(klen); | ||
398 | kout = DH_compute_key(kbuf, dh_server_pub, dh); | ||
399 | #ifdef DEBUG_KEXDH | ||
400 | debug("shared secret: len %d/%d", klen, kout); | ||
401 | fprintf(stderr, "shared secret == "); | ||
402 | for (i = 0; i< kout; i++) | ||
403 | fprintf(stderr, "%02x", (kbuf[i])&0xff); | ||
404 | fprintf(stderr, "\n"); | ||
405 | #endif | ||
406 | shared_secret = BN_new(); | ||
407 | |||
408 | BN_bin2bn(kbuf, kout, shared_secret); | ||
409 | memset(kbuf, 0, klen); | ||
410 | xfree(kbuf); | ||
411 | |||
412 | /* calc and verify H */ | ||
413 | hash = kex_hash_gex( | ||
414 | client_version_string, | ||
415 | server_version_string, | ||
416 | buffer_ptr(client_kexinit), buffer_len(client_kexinit), | ||
417 | buffer_ptr(server_kexinit), buffer_len(server_kexinit), | ||
418 | server_host_key_blob, sbloblen, | ||
419 | nbits, dh->p, dh->g, | ||
420 | dh->pub_key, | ||
421 | dh_server_pub, | ||
422 | shared_secret | ||
423 | ); | ||
424 | xfree(server_host_key_blob); | ||
425 | DH_free(dh); | ||
426 | #ifdef DEBUG_KEXDH | ||
427 | fprintf(stderr, "hash == "); | ||
428 | for (i = 0; i< 20; i++) | ||
429 | fprintf(stderr, "%02x", (hash[i])&0xff); | ||
430 | fprintf(stderr, "\n"); | ||
431 | #endif | ||
432 | if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) | ||
433 | fatal("dsa_verify failed for server_host_key"); | ||
434 | key_free(server_host_key); | ||
435 | |||
436 | kex_derive_keys(kex, hash, shared_secret); | ||
437 | packet_set_kex(kex); | ||
438 | |||
439 | /* save session id */ | ||
440 | session_id2_len = 20; | ||
441 | session_id2 = xmalloc(session_id2_len); | ||
442 | memcpy(session_id2, hash, session_id2_len); | ||
255 | } | 443 | } |
256 | 444 | ||
257 | /* | 445 | /* |
@@ -270,8 +458,8 @@ struct Authctxt { | |||
270 | const char *host; | 458 | const char *host; |
271 | const char *service; | 459 | const char *service; |
272 | AuthenticationConnection *agent; | 460 | AuthenticationConnection *agent; |
273 | int success; | ||
274 | Authmethod *method; | 461 | Authmethod *method; |
462 | int success; | ||
275 | }; | 463 | }; |
276 | struct Authmethod { | 464 | struct Authmethod { |
277 | char *name; /* string to compare against server's list */ | 465 | char *name; /* string to compare against server's list */ |
@@ -283,11 +471,16 @@ struct Authmethod { | |||
283 | void input_userauth_success(int type, int plen, void *ctxt); | 471 | void input_userauth_success(int type, int plen, void *ctxt); |
284 | void input_userauth_failure(int type, int plen, void *ctxt); | 472 | void input_userauth_failure(int type, int plen, void *ctxt); |
285 | void input_userauth_error(int type, int plen, void *ctxt); | 473 | void input_userauth_error(int type, int plen, void *ctxt); |
474 | void input_userauth_info_req(int type, int plen, void *ctxt); | ||
475 | |||
476 | int userauth_none(Authctxt *authctxt); | ||
286 | int userauth_pubkey(Authctxt *authctxt); | 477 | int userauth_pubkey(Authctxt *authctxt); |
287 | int userauth_passwd(Authctxt *authctxt); | 478 | int userauth_passwd(Authctxt *authctxt); |
479 | int userauth_kbdint(Authctxt *authctxt); | ||
288 | 480 | ||
289 | void authmethod_clear(); | 481 | void authmethod_clear(); |
290 | Authmethod *authmethod_get(char *auth_list); | 482 | Authmethod *authmethod_get(char *authlist); |
483 | Authmethod *authmethod_lookup(const char *name); | ||
291 | 484 | ||
292 | Authmethod authmethods[] = { | 485 | Authmethod authmethods[] = { |
293 | {"publickey", | 486 | {"publickey", |
@@ -298,6 +491,14 @@ Authmethod authmethods[] = { | |||
298 | userauth_passwd, | 491 | userauth_passwd, |
299 | &options.password_authentication, | 492 | &options.password_authentication, |
300 | &options.batch_mode}, | 493 | &options.batch_mode}, |
494 | {"keyboard-interactive", | ||
495 | userauth_kbdint, | ||
496 | &options.kbd_interactive_authentication, | ||
497 | &options.batch_mode}, | ||
498 | {"none", | ||
499 | userauth_none, | ||
500 | NULL, | ||
501 | NULL}, | ||
301 | {NULL, NULL, NULL, NULL} | 502 | {NULL, NULL, NULL, NULL} |
302 | }; | 503 | }; |
303 | 504 | ||
@@ -334,17 +535,13 @@ ssh_userauth2(const char *server_user, char *host) | |||
334 | authctxt.host = host; | 535 | authctxt.host = host; |
335 | authctxt.service = "ssh-connection"; /* service name */ | 536 | authctxt.service = "ssh-connection"; /* service name */ |
336 | authctxt.success = 0; | 537 | authctxt.success = 0; |
337 | authctxt.method = NULL; | 538 | authctxt.method = authmethod_lookup("none"); |
539 | if (authctxt.method == NULL) | ||
540 | fatal("ssh_userauth2: internal error: cannot send userauth none request"); | ||
541 | authmethod_clear(); | ||
338 | 542 | ||
339 | /* initial userauth request */ | 543 | /* initial userauth request */ |
340 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | 544 | userauth_none(&authctxt); |
341 | packet_put_cstring(authctxt.server_user); | ||
342 | packet_put_cstring(authctxt.service); | ||
343 | packet_put_cstring("none"); | ||
344 | packet_send(); | ||
345 | packet_write_wait(); | ||
346 | |||
347 | authmethod_clear(); | ||
348 | 545 | ||
349 | dispatch_init(&input_userauth_error); | 546 | dispatch_init(&input_userauth_error); |
350 | dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); | 547 | dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); |
@@ -354,7 +551,7 @@ ssh_userauth2(const char *server_user, char *host) | |||
354 | if (authctxt.agent != NULL) | 551 | if (authctxt.agent != NULL) |
355 | ssh_close_authentication_connection(authctxt.agent); | 552 | ssh_close_authentication_connection(authctxt.agent); |
356 | 553 | ||
357 | debug("ssh-userauth2 successfull"); | 554 | debug("ssh-userauth2 successfull: method %s", authctxt.method->name); |
358 | } | 555 | } |
359 | void | 556 | void |
360 | input_userauth_error(int type, int plen, void *ctxt) | 557 | input_userauth_error(int type, int plen, void *ctxt) |
@@ -376,12 +573,11 @@ input_userauth_failure(int type, int plen, void *ctxt) | |||
376 | Authctxt *authctxt = ctxt; | 573 | Authctxt *authctxt = ctxt; |
377 | char *authlist = NULL; | 574 | char *authlist = NULL; |
378 | int partial; | 575 | int partial; |
379 | int dlen; | ||
380 | 576 | ||
381 | if (authctxt == NULL) | 577 | if (authctxt == NULL) |
382 | fatal("input_userauth_failure: no authentication context"); | 578 | fatal("input_userauth_failure: no authentication context"); |
383 | 579 | ||
384 | authlist = packet_get_string(&dlen); | 580 | authlist = packet_get_string(NULL); |
385 | partial = packet_get_char(); | 581 | partial = packet_get_char(); |
386 | packet_done(); | 582 | packet_done(); |
387 | 583 | ||
@@ -390,12 +586,12 @@ input_userauth_failure(int type, int plen, void *ctxt) | |||
390 | debug("authentications that can continue: %s", authlist); | 586 | debug("authentications that can continue: %s", authlist); |
391 | 587 | ||
392 | for (;;) { | 588 | for (;;) { |
393 | /* try old method or get next method */ | ||
394 | method = authmethod_get(authlist); | 589 | method = authmethod_get(authlist); |
395 | if (method == NULL) | 590 | if (method == NULL) |
396 | fatal("Unable to find an authentication method"); | 591 | fatal("Unable to find an authentication method"); |
592 | authctxt->method = method; | ||
397 | if (method->userauth(authctxt) != 0) { | 593 | if (method->userauth(authctxt) != 0) { |
398 | debug2("we sent a packet, wait for reply"); | 594 | debug2("we sent a %s packet, wait for reply", method->name); |
399 | break; | 595 | break; |
400 | } else { | 596 | } else { |
401 | debug2("we did not send a packet, disable method"); | 597 | debug2("we did not send a packet, disable method"); |
@@ -406,6 +602,19 @@ input_userauth_failure(int type, int plen, void *ctxt) | |||
406 | } | 602 | } |
407 | 603 | ||
408 | int | 604 | int |
605 | userauth_none(Authctxt *authctxt) | ||
606 | { | ||
607 | /* initial userauth request */ | ||
608 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
609 | packet_put_cstring(authctxt->server_user); | ||
610 | packet_put_cstring(authctxt->service); | ||
611 | packet_put_cstring(authctxt->method->name); | ||
612 | packet_send(); | ||
613 | packet_write_wait(); | ||
614 | return 1; | ||
615 | } | ||
616 | |||
617 | int | ||
409 | userauth_passwd(Authctxt *authctxt) | 618 | userauth_passwd(Authctxt *authctxt) |
410 | { | 619 | { |
411 | static int attempt = 0; | 620 | static int attempt = 0; |
@@ -424,7 +633,7 @@ userauth_passwd(Authctxt *authctxt) | |||
424 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | 633 | packet_start(SSH2_MSG_USERAUTH_REQUEST); |
425 | packet_put_cstring(authctxt->server_user); | 634 | packet_put_cstring(authctxt->server_user); |
426 | packet_put_cstring(authctxt->service); | 635 | packet_put_cstring(authctxt->service); |
427 | packet_put_cstring("password"); | 636 | packet_put_cstring(authctxt->method->name); |
428 | packet_put_char(0); | 637 | packet_put_char(0); |
429 | packet_put_cstring(password); | 638 | packet_put_cstring(password); |
430 | memset(password, 0, strlen(password)); | 639 | memset(password, 0, strlen(password)); |
@@ -442,6 +651,7 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | |||
442 | int bloblen, slen; | 651 | int bloblen, slen; |
443 | int skip = 0; | 652 | int skip = 0; |
444 | int ret = -1; | 653 | int ret = -1; |
654 | int have_sig = 1; | ||
445 | 655 | ||
446 | dsa_make_key_blob(k, &blob, &bloblen); | 656 | dsa_make_key_blob(k, &blob, &bloblen); |
447 | 657 | ||
@@ -460,8 +670,8 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | |||
460 | datafellows & SSH_BUG_PUBKEYAUTH ? | 670 | datafellows & SSH_BUG_PUBKEYAUTH ? |
461 | "ssh-userauth" : | 671 | "ssh-userauth" : |
462 | authctxt->service); | 672 | authctxt->service); |
463 | buffer_put_cstring(&b, "publickey"); | 673 | buffer_put_cstring(&b, authctxt->method->name); |
464 | buffer_put_char(&b, 1); | 674 | buffer_put_char(&b, have_sig); |
465 | buffer_put_cstring(&b, KEX_DSS); | 675 | buffer_put_cstring(&b, KEX_DSS); |
466 | buffer_put_string(&b, blob, bloblen); | 676 | buffer_put_string(&b, blob, bloblen); |
467 | 677 | ||
@@ -481,8 +691,8 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) | |||
481 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | 691 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); |
482 | buffer_put_cstring(&b, authctxt->server_user); | 692 | buffer_put_cstring(&b, authctxt->server_user); |
483 | buffer_put_cstring(&b, authctxt->service); | 693 | buffer_put_cstring(&b, authctxt->service); |
484 | buffer_put_cstring(&b, "publickey"); | 694 | buffer_put_cstring(&b, authctxt->method->name); |
485 | buffer_put_char(&b, 1); | 695 | buffer_put_char(&b, have_sig); |
486 | buffer_put_cstring(&b, KEX_DSS); | 696 | buffer_put_cstring(&b, KEX_DSS); |
487 | buffer_put_string(&b, blob, bloblen); | 697 | buffer_put_string(&b, blob, bloblen); |
488 | } | 698 | } |
@@ -606,6 +816,92 @@ userauth_pubkey(Authctxt *authctxt) | |||
606 | return sent; | 816 | return sent; |
607 | } | 817 | } |
608 | 818 | ||
819 | /* | ||
820 | * Send userauth request message specifying keyboard-interactive method. | ||
821 | */ | ||
822 | int | ||
823 | userauth_kbdint(Authctxt *authctxt) | ||
824 | { | ||
825 | static int attempt = 0; | ||
826 | |||
827 | if (attempt++ >= options.number_of_password_prompts) | ||
828 | return 0; | ||
829 | |||
830 | debug2("userauth_kbdint"); | ||
831 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
832 | packet_put_cstring(authctxt->server_user); | ||
833 | packet_put_cstring(authctxt->service); | ||
834 | packet_put_cstring(authctxt->method->name); | ||
835 | packet_put_cstring(""); /* lang */ | ||
836 | packet_put_cstring(options.kbd_interactive_devices ? | ||
837 | options.kbd_interactive_devices : ""); | ||
838 | packet_send(); | ||
839 | packet_write_wait(); | ||
840 | |||
841 | dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); | ||
842 | return 1; | ||
843 | } | ||
844 | |||
845 | /* | ||
846 | * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send | ||
847 | * SSH2_MSG_USERAUTH_INFO_RESPONSE | ||
848 | */ | ||
849 | void | ||
850 | input_userauth_info_req(int type, int plen, void *ctxt) | ||
851 | { | ||
852 | Authctxt *authctxt = ctxt; | ||
853 | char *name = NULL; | ||
854 | char *inst = NULL; | ||
855 | char *lang = NULL; | ||
856 | char *prompt = NULL; | ||
857 | char *response = NULL; | ||
858 | unsigned int num_prompts, i; | ||
859 | int echo = 0; | ||
860 | |||
861 | debug2("input_userauth_info_req"); | ||
862 | |||
863 | if (authctxt == NULL) | ||
864 | fatal("input_userauth_info_req: no authentication context"); | ||
865 | |||
866 | name = packet_get_string(NULL); | ||
867 | inst = packet_get_string(NULL); | ||
868 | lang = packet_get_string(NULL); | ||
869 | |||
870 | if (strlen(name) > 0) | ||
871 | cli_mesg(name); | ||
872 | xfree(name); | ||
873 | |||
874 | if (strlen(inst) > 0) | ||
875 | cli_mesg(inst); | ||
876 | xfree(inst); | ||
877 | xfree(lang); /* unused */ | ||
878 | |||
879 | num_prompts = packet_get_int(); | ||
880 | /* | ||
881 | * Begin to build info response packet based on prompts requested. | ||
882 | * We commit to providing the correct number of responses, so if | ||
883 | * further on we run into a problem that prevents this, we have to | ||
884 | * be sure and clean this up and send a correct error response. | ||
885 | */ | ||
886 | packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); | ||
887 | packet_put_int(num_prompts); | ||
888 | |||
889 | for (i = 0; i < num_prompts; i++) { | ||
890 | prompt = packet_get_string(NULL); | ||
891 | echo = packet_get_char(); | ||
892 | |||
893 | response = cli_prompt(prompt, echo); | ||
894 | |||
895 | packet_put_cstring(response); | ||
896 | memset(response, 0, strlen(response)); | ||
897 | xfree(response); | ||
898 | xfree(prompt); | ||
899 | } | ||
900 | packet_done(); /* done with parsing incoming message. */ | ||
901 | |||
902 | packet_send(); | ||
903 | packet_write_wait(); | ||
904 | } | ||
609 | 905 | ||
610 | /* find auth method */ | 906 | /* find auth method */ |
611 | 907 | ||
@@ -692,6 +988,7 @@ authmethod_get(char *authlist) | |||
692 | 988 | ||
693 | if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) { | 989 | if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) { |
694 | /* start over if passed a different list */ | 990 | /* start over if passed a different list */ |
991 | debug3("start over, passed a different list"); | ||
695 | authmethod_clear(); | 992 | authmethod_clear(); |
696 | authlist_current = xstrdup(authlist); | 993 | authlist_current = xstrdup(authlist); |
697 | authlist_working = xstrdup(authlist); | 994 | authlist_working = xstrdup(authlist); |
@@ -706,16 +1003,20 @@ authmethod_get(char *authlist) | |||
706 | } | 1003 | } |
707 | 1004 | ||
708 | while (name != NULL) { | 1005 | while (name != NULL) { |
1006 | debug3("authmethod_lookup %s", name); | ||
709 | method = authmethod_lookup(name); | 1007 | method = authmethod_lookup(name); |
710 | if (method != NULL && authmethod_is_enabled(method)) | 1008 | if (method != NULL && authmethod_is_enabled(method)) { |
1009 | debug3("authmethod_is_enabled %s", name); | ||
711 | break; | 1010 | break; |
1011 | } | ||
712 | name = strtok_r(NULL, DELIM, &authlist_state); | 1012 | name = strtok_r(NULL, DELIM, &authlist_state); |
1013 | method = NULL; | ||
713 | } | 1014 | } |
714 | 1015 | ||
715 | if (authname_current != NULL) | 1016 | if (authname_current != NULL) |
716 | xfree(authname_current); | 1017 | xfree(authname_current); |
717 | 1018 | ||
718 | if (name != NULL) { | 1019 | if (method != NULL) { |
719 | debug("next auth method to try is %s", name); | 1020 | debug("next auth method to try is %s", name); |
720 | authname_current = xstrdup(name); | 1021 | authname_current = xstrdup(name); |
721 | return method; | 1022 | return method; |