blob: 460d614f04928c677157ee29203a4e8e48e3a5ff [file] [log] [blame]
Damien Millereba71ba2000-04-29 23:57:08 +10001/*
2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
Damien Millereba71ba2000-04-29 23:57:08 +100012 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
Damien Millera0ff4662001-03-30 10:49:35 +100026RCSID("$OpenBSD: sshconnect2.c,v 1.60 2001/03/29 21:06:21 stevesk Exp $");
Damien Millereba71ba2000-04-29 23:57:08 +100027
28#include <openssl/bn.h>
Damien Millereba71ba2000-04-29 23:57:08 +100029#include <openssl/md5.h>
30#include <openssl/dh.h>
31#include <openssl/hmac.h>
32
33#include "ssh.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000034#include "ssh2.h"
Damien Millereba71ba2000-04-29 23:57:08 +100035#include "xmalloc.h"
36#include "rsa.h"
37#include "buffer.h"
38#include "packet.h"
Damien Millereba71ba2000-04-29 23:57:08 +100039#include "uidswap.h"
40#include "compat.h"
Damien Millereba71ba2000-04-29 23:57:08 +100041#include "bufaux.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000042#include "cipher.h"
Damien Millereba71ba2000-04-29 23:57:08 +100043#include "kex.h"
44#include "myproposal.h"
45#include "key.h"
Damien Millereba71ba2000-04-29 23:57:08 +100046#include "sshconnect.h"
47#include "authfile.h"
Damien Miller874d77b2000-10-14 16:23:11 +110048#include "cli.h"
Ben Lindstromdf221392001-03-29 00:36:16 +000049#include "dh.h"
Damien Miller62cee002000-09-23 17:15:56 +110050#include "dispatch.h"
Damien Millerad833b32000-08-23 10:46:23 +100051#include "authfd.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000052#include "log.h"
53#include "readconf.h"
54#include "readpass.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000055#include "match.h"
Damien Millereba71ba2000-04-29 23:57:08 +100056
Damien Miller874d77b2000-10-14 16:23:11 +110057void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
58void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
59
Damien Millereba71ba2000-04-29 23:57:08 +100060/* import */
61extern char *client_version_string;
62extern char *server_version_string;
63extern Options options;
64
65/*
66 * SSH2 key exchange
67 */
68
Ben Lindstrom46c16222000-12-22 01:43:59 +000069u_char *session_id2 = NULL;
Damien Millereba71ba2000-04-29 23:57:08 +100070int session_id2_len = 0;
71
72void
Damien Miller874d77b2000-10-14 16:23:11 +110073ssh_kex2(char *host, struct sockaddr *hostaddr)
74{
75 int i, plen;
76 Kex *kex;
77 Buffer *client_kexinit, *server_kexinit;
78 char *sprop[PROPOSAL_MAX];
79
Damien Millere39cacc2000-11-29 12:18:44 +110080 if (options.ciphers == (char *)-1) {
81 log("No valid ciphers for protocol version 2 given, using defaults.");
82 options.ciphers = NULL;
Damien Miller874d77b2000-10-14 16:23:11 +110083 }
84 if (options.ciphers != NULL) {
85 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
86 myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
87 }
Damien Millera0ff4662001-03-30 10:49:35 +100088 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
89 compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
90 myproposal[PROPOSAL_ENC_ALGS_STOC] =
91 compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
Damien Miller874d77b2000-10-14 16:23:11 +110092 if (options.compression) {
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000093 myproposal[PROPOSAL_COMP_ALGS_CTOS] =
Damien Miller874d77b2000-10-14 16:23:11 +110094 myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
95 } else {
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000096 myproposal[PROPOSAL_COMP_ALGS_CTOS] =
Damien Miller874d77b2000-10-14 16:23:11 +110097 myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
98 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000099 if (options.macs != NULL) {
100 myproposal[PROPOSAL_MAC_ALGS_CTOS] =
101 myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
102 }
Damien Miller874d77b2000-10-14 16:23:11 +1100103
104 /* buffers with raw kexinit messages */
105 server_kexinit = xmalloc(sizeof(*server_kexinit));
106 buffer_init(server_kexinit);
107 client_kexinit = kex_init(myproposal);
108
109 /* algorithm negotiation */
110 kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
111 kex = kex_choose_conf(myproposal, sprop, 0);
112 for (i = 0; i < PROPOSAL_MAX; i++)
113 xfree(sprop[i]);
114
115 /* server authentication and session key agreement */
116 switch(kex->kex_type) {
117 case DH_GRP1_SHA1:
118 ssh_dh1_client(kex, host, hostaddr,
119 client_kexinit, server_kexinit);
120 break;
121 case DH_GEX_SHA1:
122 ssh_dhgex_client(kex, host, hostaddr, client_kexinit,
123 server_kexinit);
124 break;
125 default:
126 fatal("Unsupported key exchange %d", kex->kex_type);
127 }
128
129 buffer_free(client_kexinit);
130 buffer_free(server_kexinit);
131 xfree(client_kexinit);
132 xfree(server_kexinit);
133
134 debug("Wait SSH2_MSG_NEWKEYS.");
135 packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
136 packet_done();
137 debug("GOT SSH2_MSG_NEWKEYS.");
138
139 debug("send SSH2_MSG_NEWKEYS.");
140 packet_start(SSH2_MSG_NEWKEYS);
141 packet_send();
142 packet_write_wait();
143 debug("done: send SSH2_MSG_NEWKEYS.");
144
145#ifdef DEBUG_KEXDH
146 /* send 1st encrypted/maced/compressed message */
147 packet_start(SSH2_MSG_IGNORE);
148 packet_put_cstring("markus");
149 packet_send();
150 packet_write_wait();
151#endif
152 debug("done: KEX2.");
153}
154
155/* diffie-hellman-group1-sha1 */
156
157void
Kevin Stevesef4eea92001-02-05 12:42:17 +0000158ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr,
Damien Miller874d77b2000-10-14 16:23:11 +1100159 Buffer *client_kexinit, Buffer *server_kexinit)
Damien Millereba71ba2000-04-29 23:57:08 +1000160{
Damien Miller62cee002000-09-23 17:15:56 +1100161#ifdef DEBUG_KEXDH
162 int i;
163#endif
Damien Millerb1715dc2000-05-30 13:44:51 +1000164 int plen, dlen;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000165 u_int klen, kout;
Damien Millereba71ba2000-04-29 23:57:08 +1000166 char *signature = NULL;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000167 u_int slen;
Damien Millereba71ba2000-04-29 23:57:08 +1000168 char *server_host_key_blob = NULL;
169 Key *server_host_key;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000170 u_int sbloblen;
Damien Millereba71ba2000-04-29 23:57:08 +1000171 DH *dh;
172 BIGNUM *dh_server_pub = 0;
173 BIGNUM *shared_secret = 0;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000174 u_char *kbuf;
175 u_char *hash;
Damien Millereba71ba2000-04-29 23:57:08 +1000176
Damien Millereba71ba2000-04-29 23:57:08 +1000177 debug("Sending SSH2_MSG_KEXDH_INIT.");
Damien Millereba71ba2000-04-29 23:57:08 +1000178 /* generate and send 'e', client DH public key */
179 dh = dh_new_group1();
Ben Lindstrom4c4f05e2001-03-06 01:09:20 +0000180 dh_gen_key(dh, kex->we_need * 8);
Damien Millereba71ba2000-04-29 23:57:08 +1000181 packet_start(SSH2_MSG_KEXDH_INIT);
182 packet_put_bignum2(dh->pub_key);
183 packet_send();
184 packet_write_wait();
185
186#ifdef DEBUG_KEXDH
187 fprintf(stderr, "\np= ");
Damien Miller62cee002000-09-23 17:15:56 +1100188 BN_print_fp(stderr, dh->p);
Damien Millereba71ba2000-04-29 23:57:08 +1000189 fprintf(stderr, "\ng= ");
Damien Miller62cee002000-09-23 17:15:56 +1100190 BN_print_fp(stderr, dh->g);
Damien Millereba71ba2000-04-29 23:57:08 +1000191 fprintf(stderr, "\npub= ");
Damien Miller62cee002000-09-23 17:15:56 +1100192 BN_print_fp(stderr, dh->pub_key);
Damien Millereba71ba2000-04-29 23:57:08 +1000193 fprintf(stderr, "\n");
194 DHparams_print_fp(stderr, dh);
195#endif
196
197 debug("Wait SSH2_MSG_KEXDH_REPLY.");
198
Damien Millerb1715dc2000-05-30 13:44:51 +1000199 packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY);
Damien Millereba71ba2000-04-29 23:57:08 +1000200
201 debug("Got SSH2_MSG_KEXDH_REPLY.");
202
203 /* key, cert */
204 server_host_key_blob = packet_get_string(&sbloblen);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100205 server_host_key = key_from_blob(server_host_key_blob, sbloblen);
Damien Millereba71ba2000-04-29 23:57:08 +1000206 if (server_host_key == NULL)
207 fatal("cannot decode server_host_key_blob");
208
209 check_host_key(host, hostaddr, server_host_key,
Damien Miller874d77b2000-10-14 16:23:11 +1100210 options.user_hostfile2, options.system_hostfile2);
Damien Millereba71ba2000-04-29 23:57:08 +1000211
212 /* DH paramter f, server public DH key */
213 dh_server_pub = BN_new();
214 if (dh_server_pub == NULL)
215 fatal("dh_server_pub == NULL");
216 packet_get_bignum2(dh_server_pub, &dlen);
217
218#ifdef DEBUG_KEXDH
219 fprintf(stderr, "\ndh_server_pub= ");
Damien Miller62cee002000-09-23 17:15:56 +1100220 BN_print_fp(stderr, dh_server_pub);
Damien Millereba71ba2000-04-29 23:57:08 +1000221 fprintf(stderr, "\n");
222 debug("bits %d", BN_num_bits(dh_server_pub));
223#endif
224
225 /* signed H */
226 signature = packet_get_string(&slen);
227 packet_done();
228
229 if (!dh_pub_is_valid(dh, dh_server_pub))
230 packet_disconnect("bad server public DH value");
231
232 klen = DH_size(dh);
233 kbuf = xmalloc(klen);
234 kout = DH_compute_key(kbuf, dh_server_pub, dh);
235#ifdef DEBUG_KEXDH
236 debug("shared secret: len %d/%d", klen, kout);
237 fprintf(stderr, "shared secret == ");
238 for (i = 0; i< kout; i++)
239 fprintf(stderr, "%02x", (kbuf[i])&0xff);
240 fprintf(stderr, "\n");
241#endif
242 shared_secret = BN_new();
243
244 BN_bin2bn(kbuf, kout, shared_secret);
245 memset(kbuf, 0, klen);
246 xfree(kbuf);
247
248 /* calc and verify H */
249 hash = kex_hash(
250 client_version_string,
251 server_version_string,
252 buffer_ptr(client_kexinit), buffer_len(client_kexinit),
253 buffer_ptr(server_kexinit), buffer_len(server_kexinit),
254 server_host_key_blob, sbloblen,
255 dh->pub_key,
256 dh_server_pub,
257 shared_secret
258 );
259 xfree(server_host_key_blob);
Damien Millerb1715dc2000-05-30 13:44:51 +1000260 DH_free(dh);
Ben Lindstromb1985f72001-01-23 00:19:15 +0000261 BN_free(dh_server_pub);
Damien Millereba71ba2000-04-29 23:57:08 +1000262#ifdef DEBUG_KEXDH
263 fprintf(stderr, "hash == ");
264 for (i = 0; i< 20; i++)
265 fprintf(stderr, "%02x", (hash[i])&0xff);
266 fprintf(stderr, "\n");
267#endif
Ben Lindstrom46c16222000-12-22 01:43:59 +0000268 if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
Damien Miller0bc1bd82000-11-13 22:57:25 +1100269 fatal("key_verify failed for server_host_key");
Damien Millereba71ba2000-04-29 23:57:08 +1000270 key_free(server_host_key);
Ben Lindstromb1985f72001-01-23 00:19:15 +0000271 xfree(signature);
Damien Millereba71ba2000-04-29 23:57:08 +1000272
273 kex_derive_keys(kex, hash, shared_secret);
Ben Lindstromb1985f72001-01-23 00:19:15 +0000274 BN_clear_free(shared_secret);
Damien Millereba71ba2000-04-29 23:57:08 +1000275 packet_set_kex(kex);
276
Damien Millereba71ba2000-04-29 23:57:08 +1000277 /* save session id */
278 session_id2_len = 20;
279 session_id2 = xmalloc(session_id2_len);
280 memcpy(session_id2, hash, session_id2_len);
Damien Millerb1715dc2000-05-30 13:44:51 +1000281}
282
Damien Miller874d77b2000-10-14 16:23:11 +1100283/* diffie-hellman-group-exchange-sha1 */
284
285/*
286 * Estimates the group order for a Diffie-Hellman group that has an
287 * attack complexity approximately the same as O(2**bits). Estimate
288 * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3)))
289 */
290
291int
292dh_estimate(int bits)
Damien Millerb1715dc2000-05-30 13:44:51 +1000293{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000294
Damien Miller874d77b2000-10-14 16:23:11 +1100295 if (bits < 64)
296 return (512); /* O(2**63) */
297 if (bits < 128)
298 return (1024); /* O(2**86) */
299 if (bits < 192)
300 return (2048); /* O(2**116) */
301 return (4096); /* O(2**156) */
302}
Damien Millerb1715dc2000-05-30 13:44:51 +1000303
Damien Miller874d77b2000-10-14 16:23:11 +1100304void
305ssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr,
306 Buffer *client_kexinit, Buffer *server_kexinit)
307{
308#ifdef DEBUG_KEXDH
309 int i;
310#endif
311 int plen, dlen;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000312 u_int klen, kout;
Damien Miller874d77b2000-10-14 16:23:11 +1100313 char *signature = NULL;
Ben Lindstromdf221392001-03-29 00:36:16 +0000314 u_int slen, nbits, min, max;
Damien Miller874d77b2000-10-14 16:23:11 +1100315 char *server_host_key_blob = NULL;
316 Key *server_host_key;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000317 u_int sbloblen;
Damien Miller874d77b2000-10-14 16:23:11 +1100318 DH *dh;
319 BIGNUM *dh_server_pub = 0;
320 BIGNUM *shared_secret = 0;
321 BIGNUM *p = 0, *g = 0;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000322 u_char *kbuf;
323 u_char *hash;
Damien Millerb1715dc2000-05-30 13:44:51 +1000324
Ben Lindstrom4c4f05e2001-03-06 01:09:20 +0000325 nbits = dh_estimate(kex->we_need * 8);
Damien Millerb1715dc2000-05-30 13:44:51 +1000326
Ben Lindstromdf221392001-03-29 00:36:16 +0000327 if (datafellows & SSH_OLD_DHGEX) {
328 debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST_OLD.");
329
330 /* Old GEX request */
331 packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
332 packet_put_int(nbits);
333 min = DH_GRP_MIN;
334 max = DH_GRP_MAX;
335 } else {
336 debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
337
338 /* New GEX request */
339 min = DH_GRP_MIN;
Damien Miller653ae112001-03-30 10:49:05 +1000340 max = DH_GRP_MAX;
Ben Lindstromdf221392001-03-29 00:36:16 +0000341
342 packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
343 packet_put_int(min);
344 packet_put_int(nbits);
345 packet_put_int(max);
346 }
Damien Millereba71ba2000-04-29 23:57:08 +1000347 packet_send();
348 packet_write_wait();
Damien Millereba71ba2000-04-29 23:57:08 +1000349
350#ifdef DEBUG_KEXDH
Ben Lindstromdf221392001-03-29 00:36:16 +0000351 fprintf(stderr, "\nmin = %d, nbits = %d, max = %d", min, nbits, max);
Damien Miller874d77b2000-10-14 16:23:11 +1100352#endif
353
354 debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP.");
355
356 packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP);
357
358 debug("Got SSH2_MSG_KEX_DH_GEX_GROUP.");
359
360 if ((p = BN_new()) == NULL)
361 fatal("BN_new");
362 packet_get_bignum2(p, &dlen);
363 if ((g = BN_new()) == NULL)
364 fatal("BN_new");
365 packet_get_bignum2(g, &dlen);
Ben Lindstromdf221392001-03-29 00:36:16 +0000366
367 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
368 fatal("DH_GEX group out of range: %d !< %d !< %d",
369 min, BN_num_bits(p), max);
370
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000371 dh = dh_new_group(g, p);
Damien Miller874d77b2000-10-14 16:23:11 +1100372
Ben Lindstrom4c4f05e2001-03-06 01:09:20 +0000373 dh_gen_key(dh, kex->we_need * 8);
Kevin Steves6b875862000-12-15 23:31:01 +0000374
Damien Miller874d77b2000-10-14 16:23:11 +1100375#ifdef DEBUG_KEXDH
376 fprintf(stderr, "\np= ");
377 BN_print_fp(stderr, dh->p);
378 fprintf(stderr, "\ng= ");
379 BN_print_fp(stderr, dh->g);
380 fprintf(stderr, "\npub= ");
381 BN_print_fp(stderr, dh->pub_key);
382 fprintf(stderr, "\n");
383 DHparams_print_fp(stderr, dh);
384#endif
385
386 debug("Sending SSH2_MSG_KEX_DH_GEX_INIT.");
387 /* generate and send 'e', client DH public key */
388 packet_start(SSH2_MSG_KEX_DH_GEX_INIT);
389 packet_put_bignum2(dh->pub_key);
Damien Millereba71ba2000-04-29 23:57:08 +1000390 packet_send();
391 packet_write_wait();
Damien Miller874d77b2000-10-14 16:23:11 +1100392
393 debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY.");
394
395 packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY);
396
397 debug("Got SSH2_MSG_KEXDH_REPLY.");
398
399 /* key, cert */
400 server_host_key_blob = packet_get_string(&sbloblen);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100401 server_host_key = key_from_blob(server_host_key_blob, sbloblen);
Damien Miller874d77b2000-10-14 16:23:11 +1100402 if (server_host_key == NULL)
403 fatal("cannot decode server_host_key_blob");
404
405 check_host_key(host, hostaddr, server_host_key,
406 options.user_hostfile2, options.system_hostfile2);
407
408 /* DH paramter f, server public DH key */
409 dh_server_pub = BN_new();
410 if (dh_server_pub == NULL)
411 fatal("dh_server_pub == NULL");
412 packet_get_bignum2(dh_server_pub, &dlen);
413
414#ifdef DEBUG_KEXDH
415 fprintf(stderr, "\ndh_server_pub= ");
416 BN_print_fp(stderr, dh_server_pub);
417 fprintf(stderr, "\n");
418 debug("bits %d", BN_num_bits(dh_server_pub));
Damien Millereba71ba2000-04-29 23:57:08 +1000419#endif
Damien Miller874d77b2000-10-14 16:23:11 +1100420
421 /* signed H */
422 signature = packet_get_string(&slen);
423 packet_done();
424
425 if (!dh_pub_is_valid(dh, dh_server_pub))
426 packet_disconnect("bad server public DH value");
427
428 klen = DH_size(dh);
429 kbuf = xmalloc(klen);
430 kout = DH_compute_key(kbuf, dh_server_pub, dh);
431#ifdef DEBUG_KEXDH
432 debug("shared secret: len %d/%d", klen, kout);
433 fprintf(stderr, "shared secret == ");
434 for (i = 0; i< kout; i++)
435 fprintf(stderr, "%02x", (kbuf[i])&0xff);
436 fprintf(stderr, "\n");
437#endif
438 shared_secret = BN_new();
439
440 BN_bin2bn(kbuf, kout, shared_secret);
441 memset(kbuf, 0, klen);
442 xfree(kbuf);
443
Damien Miller2557bfc2001-03-30 10:47:14 +1000444 if (datafellows & SSH_OLD_DHGEX) {
445 /* These values are not included in the hash */
446 min = -1;
447 max = -1;
448 }
449
Damien Miller874d77b2000-10-14 16:23:11 +1100450 /* calc and verify H */
451 hash = kex_hash_gex(
452 client_version_string,
453 server_version_string,
454 buffer_ptr(client_kexinit), buffer_len(client_kexinit),
455 buffer_ptr(server_kexinit), buffer_len(server_kexinit),
456 server_host_key_blob, sbloblen,
Damien Miller2557bfc2001-03-30 10:47:14 +1000457 min, nbits, max,
458 dh->p, dh->g,
Damien Miller874d77b2000-10-14 16:23:11 +1100459 dh->pub_key,
460 dh_server_pub,
461 shared_secret
462 );
463 xfree(server_host_key_blob);
464 DH_free(dh);
Ben Lindstromb1985f72001-01-23 00:19:15 +0000465 BN_free(dh_server_pub);
Damien Miller874d77b2000-10-14 16:23:11 +1100466#ifdef DEBUG_KEXDH
467 fprintf(stderr, "hash == ");
468 for (i = 0; i< 20; i++)
469 fprintf(stderr, "%02x", (hash[i])&0xff);
470 fprintf(stderr, "\n");
471#endif
Ben Lindstrom46c16222000-12-22 01:43:59 +0000472 if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
Damien Miller0bc1bd82000-11-13 22:57:25 +1100473 fatal("key_verify failed for server_host_key");
Damien Miller874d77b2000-10-14 16:23:11 +1100474 key_free(server_host_key);
Ben Lindstromb1985f72001-01-23 00:19:15 +0000475 xfree(signature);
Damien Miller874d77b2000-10-14 16:23:11 +1100476
477 kex_derive_keys(kex, hash, shared_secret);
Ben Lindstromb1985f72001-01-23 00:19:15 +0000478 BN_clear_free(shared_secret);
Damien Miller874d77b2000-10-14 16:23:11 +1100479 packet_set_kex(kex);
480
481 /* save session id */
482 session_id2_len = 20;
483 session_id2 = xmalloc(session_id2_len);
484 memcpy(session_id2, hash, session_id2_len);
Damien Millereba71ba2000-04-29 23:57:08 +1000485}
Damien Millerb1715dc2000-05-30 13:44:51 +1000486
Damien Millereba71ba2000-04-29 23:57:08 +1000487/*
488 * Authenticate user
489 */
Damien Miller62cee002000-09-23 17:15:56 +1100490
491typedef struct Authctxt Authctxt;
492typedef struct Authmethod Authmethod;
493
494typedef int sign_cb_fn(
495 Authctxt *authctxt, Key *key,
Ben Lindstrom46c16222000-12-22 01:43:59 +0000496 u_char **sigp, int *lenp, u_char *data, int datalen);
Damien Miller62cee002000-09-23 17:15:56 +1100497
498struct Authctxt {
499 const char *server_user;
500 const char *host;
501 const char *service;
502 AuthenticationConnection *agent;
Damien Miller62cee002000-09-23 17:15:56 +1100503 Authmethod *method;
Damien Miller874d77b2000-10-14 16:23:11 +1100504 int success;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000505 char *authlist;
506 Key *last_key;
507 sign_cb_fn *last_key_sign;
508 int last_key_hint;
Damien Miller62cee002000-09-23 17:15:56 +1100509};
510struct Authmethod {
511 char *name; /* string to compare against server's list */
512 int (*userauth)(Authctxt *authctxt);
513 int *enabled; /* flag in option struct that enables method */
514 int *batch_flag; /* flag in option struct that disables method */
515};
516
517void input_userauth_success(int type, int plen, void *ctxt);
518void input_userauth_failure(int type, int plen, void *ctxt);
Ben Lindstromd26dcf32001-01-06 15:18:16 +0000519void input_userauth_banner(int type, int plen, void *ctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100520void input_userauth_error(int type, int plen, void *ctxt);
Damien Miller874d77b2000-10-14 16:23:11 +1100521void input_userauth_info_req(int type, int plen, void *ctxt);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000522void input_userauth_pk_ok(int type, int plen, void *ctxt);
Damien Miller874d77b2000-10-14 16:23:11 +1100523
524int userauth_none(Authctxt *authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100525int userauth_pubkey(Authctxt *authctxt);
526int userauth_passwd(Authctxt *authctxt);
Damien Miller874d77b2000-10-14 16:23:11 +1100527int userauth_kbdint(Authctxt *authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100528
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000529void userauth(Authctxt *authctxt, char *authlist);
530
531int
532sign_and_send_pubkey(Authctxt *authctxt, Key *k,
533 sign_cb_fn *sign_callback);
534void clear_auth_state(Authctxt *authctxt);
535
Damien Miller874d77b2000-10-14 16:23:11 +1100536Authmethod *authmethod_get(char *authlist);
537Authmethod *authmethod_lookup(const char *name);
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000538char *authmethods_get(void);
Damien Miller62cee002000-09-23 17:15:56 +1100539
540Authmethod authmethods[] = {
541 {"publickey",
542 userauth_pubkey,
Damien Miller0bc1bd82000-11-13 22:57:25 +1100543 &options.pubkey_authentication,
Damien Miller62cee002000-09-23 17:15:56 +1100544 NULL},
545 {"password",
546 userauth_passwd,
547 &options.password_authentication,
548 &options.batch_mode},
Damien Miller874d77b2000-10-14 16:23:11 +1100549 {"keyboard-interactive",
550 userauth_kbdint,
551 &options.kbd_interactive_authentication,
552 &options.batch_mode},
553 {"none",
554 userauth_none,
555 NULL,
556 NULL},
Damien Miller62cee002000-09-23 17:15:56 +1100557 {NULL, NULL, NULL, NULL}
558};
559
560void
561ssh_userauth2(const char *server_user, char *host)
562{
563 Authctxt authctxt;
564 int type;
565 int plen;
566
Ben Lindstrom95fb2dd2001-01-23 03:12:10 +0000567 if (options.challenge_reponse_authentication)
568 options.kbd_interactive_authentication = 1;
569
Damien Miller62cee002000-09-23 17:15:56 +1100570 debug("send SSH2_MSG_SERVICE_REQUEST");
571 packet_start(SSH2_MSG_SERVICE_REQUEST);
572 packet_put_cstring("ssh-userauth");
573 packet_send();
574 packet_write_wait();
575 type = packet_read(&plen);
576 if (type != SSH2_MSG_SERVICE_ACCEPT) {
577 fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type);
578 }
579 if (packet_remaining() > 0) {
580 char *reply = packet_get_string(&plen);
581 debug("service_accept: %s", reply);
582 xfree(reply);
Damien Miller62cee002000-09-23 17:15:56 +1100583 } else {
584 debug("buggy server: service_accept w/o service");
585 }
586 packet_done();
587 debug("got SSH2_MSG_SERVICE_ACCEPT");
588
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000589 if (options.preferred_authentications == NULL)
590 options.preferred_authentications = authmethods_get();
591
Damien Miller62cee002000-09-23 17:15:56 +1100592 /* setup authentication context */
593 authctxt.agent = ssh_get_authentication_connection();
594 authctxt.server_user = server_user;
595 authctxt.host = host;
596 authctxt.service = "ssh-connection"; /* service name */
597 authctxt.success = 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100598 authctxt.method = authmethod_lookup("none");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000599 authctxt.authlist = NULL;
Damien Miller874d77b2000-10-14 16:23:11 +1100600 if (authctxt.method == NULL)
601 fatal("ssh_userauth2: internal error: cannot send userauth none request");
Damien Miller62cee002000-09-23 17:15:56 +1100602
603 /* initial userauth request */
Damien Miller874d77b2000-10-14 16:23:11 +1100604 userauth_none(&authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100605
606 dispatch_init(&input_userauth_error);
607 dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
608 dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
Ben Lindstromd26dcf32001-01-06 15:18:16 +0000609 dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
Damien Miller62cee002000-09-23 17:15:56 +1100610 dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */
611
612 if (authctxt.agent != NULL)
613 ssh_close_authentication_connection(authctxt.agent);
614
Ben Lindstrom4dccfa52000-12-28 16:40:05 +0000615 debug("ssh-userauth2 successful: method %s", authctxt.method->name);
Damien Miller62cee002000-09-23 17:15:56 +1100616}
617void
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000618userauth(Authctxt *authctxt, char *authlist)
619{
620 if (authlist == NULL) {
621 authlist = authctxt->authlist;
622 } else {
623 if (authctxt->authlist)
624 xfree(authctxt->authlist);
625 authctxt->authlist = authlist;
626 }
627 for (;;) {
628 Authmethod *method = authmethod_get(authlist);
629 if (method == NULL)
630 fatal("Permission denied (%s).", authlist);
631 authctxt->method = method;
632 if (method->userauth(authctxt) != 0) {
633 debug2("we sent a %s packet, wait for reply", method->name);
634 break;
635 } else {
636 debug2("we did not send a packet, disable method");
637 method->enabled = NULL;
638 }
639 }
640}
641void
Damien Miller62cee002000-09-23 17:15:56 +1100642input_userauth_error(int type, int plen, void *ctxt)
643{
Ben Lindstromd26dcf32001-01-06 15:18:16 +0000644 fatal("input_userauth_error: bad message during authentication: "
645 "type %d", type);
646}
647void
648input_userauth_banner(int type, int plen, void *ctxt)
649{
650 char *msg, *lang;
651 debug3("input_userauth_banner");
652 msg = packet_get_string(NULL);
653 lang = packet_get_string(NULL);
654 fprintf(stderr, "%s", msg);
655 xfree(msg);
656 xfree(lang);
Damien Miller62cee002000-09-23 17:15:56 +1100657}
658void
659input_userauth_success(int type, int plen, void *ctxt)
660{
661 Authctxt *authctxt = ctxt;
662 if (authctxt == NULL)
663 fatal("input_userauth_success: no authentication context");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000664 if (authctxt->authlist)
665 xfree(authctxt->authlist);
666 clear_auth_state(authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100667 authctxt->success = 1; /* break out */
668}
669void
670input_userauth_failure(int type, int plen, void *ctxt)
671{
Damien Miller62cee002000-09-23 17:15:56 +1100672 Authctxt *authctxt = ctxt;
673 char *authlist = NULL;
674 int partial;
Damien Miller62cee002000-09-23 17:15:56 +1100675
676 if (authctxt == NULL)
677 fatal("input_userauth_failure: no authentication context");
678
Damien Miller874d77b2000-10-14 16:23:11 +1100679 authlist = packet_get_string(NULL);
Damien Miller62cee002000-09-23 17:15:56 +1100680 partial = packet_get_char();
681 packet_done();
682
683 if (partial != 0)
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000684 log("Authenticated with partial success.");
Damien Miller62cee002000-09-23 17:15:56 +1100685 debug("authentications that can continue: %s", authlist);
686
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000687 clear_auth_state(authctxt);
688 userauth(authctxt, authlist);
689}
690void
691input_userauth_pk_ok(int type, int plen, void *ctxt)
692{
693 Authctxt *authctxt = ctxt;
694 Key *key = NULL;
695 Buffer b;
696 int alen, blen, pktype, sent = 0;
Ben Lindstromcfccef92001-03-13 04:57:58 +0000697 char *pkalg, *pkblob, *fp;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000698
699 if (authctxt == NULL)
700 fatal("input_userauth_pk_ok: no authentication context");
701 if (datafellows & SSH_BUG_PKOK) {
702 /* this is similar to SSH_BUG_PKAUTH */
703 debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
704 pkblob = packet_get_string(&blen);
705 buffer_init(&b);
706 buffer_append(&b, pkblob, blen);
707 pkalg = buffer_get_string(&b, &alen);
708 buffer_free(&b);
709 } else {
710 pkalg = packet_get_string(&alen);
711 pkblob = packet_get_string(&blen);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000712 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000713 packet_done();
714
715 debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d",
716 pkalg, blen, authctxt->last_key, authctxt->last_key_hint);
717
718 do {
719 if (authctxt->last_key == NULL ||
720 authctxt->last_key_sign == NULL) {
721 debug("no last key or no sign cb");
722 break;
723 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000724 if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) {
725 debug("unknown pkalg %s", pkalg);
726 break;
727 }
728 if ((key = key_from_blob(pkblob, blen)) == NULL) {
729 debug("no key from blob. pkalg %s", pkalg);
730 break;
731 }
Ben Lindstromcfccef92001-03-13 04:57:58 +0000732 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
733 debug2("input_userauth_pk_ok: fp %s", fp);
734 xfree(fp);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000735 if (!key_equal(key, authctxt->last_key)) {
736 debug("key != last_key");
737 break;
738 }
739 sent = sign_and_send_pubkey(authctxt, key,
740 authctxt->last_key_sign);
741 } while(0);
742
743 if (key != NULL)
744 key_free(key);
745 xfree(pkalg);
746 xfree(pkblob);
747
748 /* unregister */
749 clear_auth_state(authctxt);
750 dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL);
751
752 /* try another method if we did not send a packet*/
753 if (sent == 0)
754 userauth(authctxt, NULL);
755
Damien Miller62cee002000-09-23 17:15:56 +1100756}
757
Damien Millereba71ba2000-04-29 23:57:08 +1000758int
Damien Miller874d77b2000-10-14 16:23:11 +1100759userauth_none(Authctxt *authctxt)
760{
761 /* initial userauth request */
762 packet_start(SSH2_MSG_USERAUTH_REQUEST);
763 packet_put_cstring(authctxt->server_user);
764 packet_put_cstring(authctxt->service);
765 packet_put_cstring(authctxt->method->name);
766 packet_send();
Damien Miller874d77b2000-10-14 16:23:11 +1100767 return 1;
768}
769
770int
Damien Miller62cee002000-09-23 17:15:56 +1100771userauth_passwd(Authctxt *authctxt)
Damien Millereba71ba2000-04-29 23:57:08 +1000772{
Damien Millere247cc42000-05-07 12:03:14 +1000773 static int attempt = 0;
Damien Millereba71ba2000-04-29 23:57:08 +1000774 char prompt[80];
775 char *password;
776
Damien Millerd3a18572000-06-07 19:55:44 +1000777 if (attempt++ >= options.number_of_password_prompts)
Damien Millere247cc42000-05-07 12:03:14 +1000778 return 0;
779
Damien Millerd3a18572000-06-07 19:55:44 +1000780 if(attempt != 1)
781 error("Permission denied, please try again.");
782
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000783 snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
Damien Miller62cee002000-09-23 17:15:56 +1100784 authctxt->server_user, authctxt->host);
Damien Millereba71ba2000-04-29 23:57:08 +1000785 password = read_passphrase(prompt, 0);
786 packet_start(SSH2_MSG_USERAUTH_REQUEST);
Damien Miller62cee002000-09-23 17:15:56 +1100787 packet_put_cstring(authctxt->server_user);
788 packet_put_cstring(authctxt->service);
Damien Miller874d77b2000-10-14 16:23:11 +1100789 packet_put_cstring(authctxt->method->name);
Damien Millereba71ba2000-04-29 23:57:08 +1000790 packet_put_char(0);
Ben Lindstrom5699c5f2001-03-05 06:17:49 +0000791 packet_put_cstring(password);
Damien Millereba71ba2000-04-29 23:57:08 +1000792 memset(password, 0, strlen(password));
793 xfree(password);
Ben Lindstrom5699c5f2001-03-05 06:17:49 +0000794 packet_inject_ignore(64);
Damien Millereba71ba2000-04-29 23:57:08 +1000795 packet_send();
Damien Millereba71ba2000-04-29 23:57:08 +1000796 return 1;
797}
798
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000799void
800clear_auth_state(Authctxt *authctxt)
801{
802 /* XXX clear authentication state */
803 if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) {
804 debug3("clear_auth_state: key_free %p", authctxt->last_key);
805 key_free(authctxt->last_key);
806 }
807 authctxt->last_key = NULL;
808 authctxt->last_key_hint = -2;
809 authctxt->last_key_sign = NULL;
810}
811
Damien Millerad833b32000-08-23 10:46:23 +1000812int
Damien Miller62cee002000-09-23 17:15:56 +1100813sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback)
Damien Millereba71ba2000-04-29 23:57:08 +1000814{
815 Buffer b;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000816 u_char *blob, *signature;
Damien Millereba71ba2000-04-29 23:57:08 +1000817 int bloblen, slen;
Damien Miller6536c7d2000-06-22 21:32:31 +1000818 int skip = 0;
Damien Millerad833b32000-08-23 10:46:23 +1000819 int ret = -1;
Damien Miller874d77b2000-10-14 16:23:11 +1100820 int have_sig = 1;
Damien Millereba71ba2000-04-29 23:57:08 +1000821
Ben Lindstromd121f612000-12-03 17:00:47 +0000822 debug3("sign_and_send_pubkey");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000823
Damien Miller0bc1bd82000-11-13 22:57:25 +1100824 if (key_to_blob(k, &blob, &bloblen) == 0) {
825 /* we cannot handle this key */
Ben Lindstromd121f612000-12-03 17:00:47 +0000826 debug3("sign_and_send_pubkey: cannot handle key");
Damien Miller0bc1bd82000-11-13 22:57:25 +1100827 return 0;
828 }
Damien Millereba71ba2000-04-29 23:57:08 +1000829 /* data to be signed */
830 buffer_init(&b);
Damien Miller50a41ed2000-10-16 12:14:42 +1100831 if (datafellows & SSH_OLD_SESSIONID) {
Damien Miller6536c7d2000-06-22 21:32:31 +1000832 buffer_append(&b, session_id2, session_id2_len);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000833 skip = session_id2_len;
Damien Miller50a41ed2000-10-16 12:14:42 +1100834 } else {
835 buffer_put_string(&b, session_id2, session_id2_len);
836 skip = buffer_len(&b);
Damien Miller6536c7d2000-06-22 21:32:31 +1000837 }
Damien Millereba71ba2000-04-29 23:57:08 +1000838 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
Damien Miller62cee002000-09-23 17:15:56 +1100839 buffer_put_cstring(&b, authctxt->server_user);
Damien Miller30c3d422000-05-09 11:02:59 +1000840 buffer_put_cstring(&b,
Ben Lindstromd121f612000-12-03 17:00:47 +0000841 datafellows & SSH_BUG_PKSERVICE ?
Damien Miller30c3d422000-05-09 11:02:59 +1000842 "ssh-userauth" :
Damien Miller62cee002000-09-23 17:15:56 +1100843 authctxt->service);
Ben Lindstromd121f612000-12-03 17:00:47 +0000844 if (datafellows & SSH_BUG_PKAUTH) {
845 buffer_put_char(&b, have_sig);
846 } else {
847 buffer_put_cstring(&b, authctxt->method->name);
848 buffer_put_char(&b, have_sig);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000849 buffer_put_cstring(&b, key_ssh_name(k));
Ben Lindstromd121f612000-12-03 17:00:47 +0000850 }
Damien Millereba71ba2000-04-29 23:57:08 +1000851 buffer_put_string(&b, blob, bloblen);
Damien Millereba71ba2000-04-29 23:57:08 +1000852
853 /* generate signature */
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000854 ret = (*sign_callback)(authctxt, k, &signature, &slen,
855 buffer_ptr(&b), buffer_len(&b));
Damien Millerad833b32000-08-23 10:46:23 +1000856 if (ret == -1) {
857 xfree(blob);
858 buffer_free(&b);
859 return 0;
860 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100861#ifdef DEBUG_PK
Damien Millereba71ba2000-04-29 23:57:08 +1000862 buffer_dump(&b);
863#endif
Ben Lindstromd121f612000-12-03 17:00:47 +0000864 if (datafellows & SSH_BUG_PKSERVICE) {
Damien Miller30c3d422000-05-09 11:02:59 +1000865 buffer_clear(&b);
866 buffer_append(&b, session_id2, session_id2_len);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000867 skip = session_id2_len;
Damien Miller30c3d422000-05-09 11:02:59 +1000868 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
Damien Miller62cee002000-09-23 17:15:56 +1100869 buffer_put_cstring(&b, authctxt->server_user);
870 buffer_put_cstring(&b, authctxt->service);
Damien Miller874d77b2000-10-14 16:23:11 +1100871 buffer_put_cstring(&b, authctxt->method->name);
872 buffer_put_char(&b, have_sig);
Ben Lindstromd121f612000-12-03 17:00:47 +0000873 if (!(datafellows & SSH_BUG_PKAUTH))
Kevin Stevesef4eea92001-02-05 12:42:17 +0000874 buffer_put_cstring(&b, key_ssh_name(k));
Damien Miller30c3d422000-05-09 11:02:59 +1000875 buffer_put_string(&b, blob, bloblen);
876 }
877 xfree(blob);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000878
Damien Millereba71ba2000-04-29 23:57:08 +1000879 /* append signature */
880 buffer_put_string(&b, signature, slen);
881 xfree(signature);
882
883 /* skip session id and packet type */
Damien Miller6536c7d2000-06-22 21:32:31 +1000884 if (buffer_len(&b) < skip + 1)
Damien Miller62cee002000-09-23 17:15:56 +1100885 fatal("userauth_pubkey: internal error");
Damien Miller6536c7d2000-06-22 21:32:31 +1000886 buffer_consume(&b, skip + 1);
Damien Millereba71ba2000-04-29 23:57:08 +1000887
888 /* put remaining data from buffer into packet */
889 packet_start(SSH2_MSG_USERAUTH_REQUEST);
890 packet_put_raw(buffer_ptr(&b), buffer_len(&b));
891 buffer_free(&b);
Damien Millereba71ba2000-04-29 23:57:08 +1000892 packet_send();
Damien Millerad833b32000-08-23 10:46:23 +1000893
894 return 1;
Damien Miller994cf142000-07-21 10:19:44 +1000895}
896
897int
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000898send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback,
899 int hint)
Damien Miller994cf142000-07-21 10:19:44 +1000900{
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000901 u_char *blob;
902 int bloblen, have_sig = 0;
Damien Miller994cf142000-07-21 10:19:44 +1000903
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000904 debug3("send_pubkey_test");
905
906 if (key_to_blob(k, &blob, &bloblen) == 0) {
907 /* we cannot handle this key */
908 debug3("send_pubkey_test: cannot handle key");
Damien Miller994cf142000-07-21 10:19:44 +1000909 return 0;
910 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000911 /* register callback for USERAUTH_PK_OK message */
912 authctxt->last_key_sign = sign_callback;
913 authctxt->last_key_hint = hint;
914 authctxt->last_key = k;
915 dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
Damien Miller994cf142000-07-21 10:19:44 +1000916
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000917 packet_start(SSH2_MSG_USERAUTH_REQUEST);
918 packet_put_cstring(authctxt->server_user);
919 packet_put_cstring(authctxt->service);
920 packet_put_cstring(authctxt->method->name);
921 packet_put_char(have_sig);
922 if (!(datafellows & SSH_BUG_PKAUTH))
923 packet_put_cstring(key_ssh_name(k));
924 packet_put_string(blob, bloblen);
925 xfree(blob);
926 packet_send();
927 return 1;
928}
929
930Key *
931load_identity_file(char *filename)
932{
933 Key *private;
934 char prompt[300], *passphrase;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000935 int quit, i;
Ben Lindstrom329782e2001-03-10 17:08:59 +0000936 struct stat st;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000937
Ben Lindstrom329782e2001-03-10 17:08:59 +0000938 if (stat(filename, &st) < 0) {
939 debug3("no such identity: %s", filename);
940 return NULL;
941 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000942 private = key_load_private_type(KEY_UNSPEC, filename, "", NULL);
943 if (private == NULL) {
944 if (options.batch_mode)
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000945 return NULL;
Damien Miller994cf142000-07-21 10:19:44 +1000946 snprintf(prompt, sizeof prompt,
Damien Miller0bc1bd82000-11-13 22:57:25 +1100947 "Enter passphrase for key '%.100s': ", filename);
Damien Miller62cee002000-09-23 17:15:56 +1100948 for (i = 0; i < options.number_of_password_prompts; i++) {
949 passphrase = read_passphrase(prompt, 0);
950 if (strcmp(passphrase, "") != 0) {
Ben Lindstromd0fca422001-03-26 13:44:06 +0000951 private = key_load_private_type(KEY_UNSPEC, filename,
952 passphrase, NULL);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000953 quit = 0;
Damien Miller62cee002000-09-23 17:15:56 +1100954 } else {
955 debug2("no passphrase given, try next key");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000956 quit = 1;
Damien Miller62cee002000-09-23 17:15:56 +1100957 }
958 memset(passphrase, 0, strlen(passphrase));
959 xfree(passphrase);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000960 if (private != NULL || quit)
Damien Miller62cee002000-09-23 17:15:56 +1100961 break;
962 debug2("bad passphrase given, try again...");
963 }
Damien Miller994cf142000-07-21 10:19:44 +1000964 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000965 return private;
966}
967
968int
969identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
970 u_char *data, int datalen)
971{
972 Key *private;
973 int idx, ret;
974
975 idx = authctxt->last_key_hint;
976 if (idx < 0)
977 return -1;
978 private = load_identity_file(options.identity_files[idx]);
979 if (private == NULL)
980 return -1;
981 ret = key_sign(private, sigp, lenp, data, datalen);
982 key_free(private);
Damien Millerad833b32000-08-23 10:46:23 +1000983 return ret;
984}
985
Ben Lindstrom46c16222000-12-22 01:43:59 +0000986int agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
987 u_char *data, int datalen)
Damien Millerad833b32000-08-23 10:46:23 +1000988{
Damien Miller62cee002000-09-23 17:15:56 +1100989 return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen);
Damien Millerad833b32000-08-23 10:46:23 +1000990}
991
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000992int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
993 u_char *data, int datalen)
994{
995 return key_sign(key, sigp, lenp, data, datalen);
996}
997
Damien Millerad833b32000-08-23 10:46:23 +1000998int
Damien Miller62cee002000-09-23 17:15:56 +1100999userauth_pubkey_agent(Authctxt *authctxt)
Damien Millerad833b32000-08-23 10:46:23 +10001000{
1001 static int called = 0;
Damien Miller0bc1bd82000-11-13 22:57:25 +11001002 int ret = 0;
Damien Millerad833b32000-08-23 10:46:23 +10001003 char *comment;
1004 Key *k;
Damien Millerad833b32000-08-23 10:46:23 +10001005
1006 if (called == 0) {
Damien Miller0bc1bd82000-11-13 22:57:25 +11001007 if (ssh_get_num_identities(authctxt->agent, 2) == 0)
1008 debug2("userauth_pubkey_agent: no keys at all");
Damien Miller62cee002000-09-23 17:15:56 +11001009 called = 1;
Damien Millerad833b32000-08-23 10:46:23 +10001010 }
Damien Miller0bc1bd82000-11-13 22:57:25 +11001011 k = ssh_get_next_identity(authctxt->agent, &comment, 2);
Damien Miller62cee002000-09-23 17:15:56 +11001012 if (k == NULL) {
Damien Miller0bc1bd82000-11-13 22:57:25 +11001013 debug2("userauth_pubkey_agent: no more keys");
1014 } else {
Ben Lindstrom266dfdf2001-03-09 00:12:22 +00001015 debug("userauth_pubkey_agent: testing agent key %s", comment);
Damien Miller0bc1bd82000-11-13 22:57:25 +11001016 xfree(comment);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +00001017 ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1);
1018 if (ret == 0)
1019 key_free(k);
Damien Miller62cee002000-09-23 17:15:56 +11001020 }
Damien Miller0bc1bd82000-11-13 22:57:25 +11001021 if (ret == 0)
1022 debug2("userauth_pubkey_agent: no message sent");
Damien Millerad833b32000-08-23 10:46:23 +10001023 return ret;
Damien Millereba71ba2000-04-29 23:57:08 +10001024}
1025
Damien Miller62cee002000-09-23 17:15:56 +11001026int
1027userauth_pubkey(Authctxt *authctxt)
Damien Millereba71ba2000-04-29 23:57:08 +10001028{
Damien Miller62cee002000-09-23 17:15:56 +11001029 static int idx = 0;
1030 int sent = 0;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +00001031 Key *key;
1032 char *filename;
Damien Millereba71ba2000-04-29 23:57:08 +10001033
Damien Miller0bc1bd82000-11-13 22:57:25 +11001034 if (authctxt->agent != NULL) {
1035 do {
1036 sent = userauth_pubkey_agent(authctxt);
1037 } while(!sent && authctxt->agent->howmany > 0);
1038 }
1039 while (!sent && idx < options.num_identity_files) {
Ben Lindstrom266dfdf2001-03-09 00:12:22 +00001040 key = options.identity_keys[idx];
1041 filename = options.identity_files[idx];
1042 if (key == NULL) {
1043 debug("try privkey: %s", filename);
1044 key = load_identity_file(filename);
1045 if (key != NULL) {
1046 sent = sign_and_send_pubkey(authctxt, key,
1047 key_sign_cb);
1048 key_free(key);
1049 }
1050 } else if (key->type != KEY_RSA1) {
1051 debug("try pubkey: %s", filename);
1052 sent = send_pubkey_test(authctxt, key,
1053 identity_sign_cb, idx);
1054 }
Damien Miller0bc1bd82000-11-13 22:57:25 +11001055 idx++;
1056 }
Damien Miller62cee002000-09-23 17:15:56 +11001057 return sent;
1058}
Damien Millereba71ba2000-04-29 23:57:08 +10001059
Damien Miller874d77b2000-10-14 16:23:11 +11001060/*
1061 * Send userauth request message specifying keyboard-interactive method.
1062 */
1063int
1064userauth_kbdint(Authctxt *authctxt)
1065{
1066 static int attempt = 0;
1067
1068 if (attempt++ >= options.number_of_password_prompts)
1069 return 0;
1070
1071 debug2("userauth_kbdint");
1072 packet_start(SSH2_MSG_USERAUTH_REQUEST);
1073 packet_put_cstring(authctxt->server_user);
1074 packet_put_cstring(authctxt->service);
1075 packet_put_cstring(authctxt->method->name);
1076 packet_put_cstring(""); /* lang */
1077 packet_put_cstring(options.kbd_interactive_devices ?
1078 options.kbd_interactive_devices : "");
1079 packet_send();
Damien Miller874d77b2000-10-14 16:23:11 +11001080
1081 dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
1082 return 1;
1083}
1084
1085/*
Ben Lindstrom03df5bd2001-02-10 22:16:41 +00001086 * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
Damien Miller874d77b2000-10-14 16:23:11 +11001087 */
1088void
1089input_userauth_info_req(int type, int plen, void *ctxt)
1090{
1091 Authctxt *authctxt = ctxt;
Ben Lindstrom03df5bd2001-02-10 22:16:41 +00001092 char *name, *inst, *lang, *prompt, *response;
Ben Lindstrom46c16222000-12-22 01:43:59 +00001093 u_int num_prompts, i;
Damien Miller874d77b2000-10-14 16:23:11 +11001094 int echo = 0;
1095
1096 debug2("input_userauth_info_req");
1097
1098 if (authctxt == NULL)
1099 fatal("input_userauth_info_req: no authentication context");
1100
1101 name = packet_get_string(NULL);
1102 inst = packet_get_string(NULL);
1103 lang = packet_get_string(NULL);
Damien Miller874d77b2000-10-14 16:23:11 +11001104 if (strlen(name) > 0)
1105 cli_mesg(name);
Damien Miller874d77b2000-10-14 16:23:11 +11001106 if (strlen(inst) > 0)
1107 cli_mesg(inst);
Ben Lindstrom03df5bd2001-02-10 22:16:41 +00001108 xfree(name);
Damien Miller874d77b2000-10-14 16:23:11 +11001109 xfree(inst);
Ben Lindstrom03df5bd2001-02-10 22:16:41 +00001110 xfree(lang);
Damien Miller874d77b2000-10-14 16:23:11 +11001111
1112 num_prompts = packet_get_int();
1113 /*
1114 * Begin to build info response packet based on prompts requested.
1115 * We commit to providing the correct number of responses, so if
1116 * further on we run into a problem that prevents this, we have to
1117 * be sure and clean this up and send a correct error response.
1118 */
1119 packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
1120 packet_put_int(num_prompts);
1121
1122 for (i = 0; i < num_prompts; i++) {
1123 prompt = packet_get_string(NULL);
1124 echo = packet_get_char();
1125
1126 response = cli_prompt(prompt, echo);
1127
Ben Lindstrom5699c5f2001-03-05 06:17:49 +00001128 packet_put_cstring(response);
Damien Miller874d77b2000-10-14 16:23:11 +11001129 memset(response, 0, strlen(response));
1130 xfree(response);
1131 xfree(prompt);
1132 }
1133 packet_done(); /* done with parsing incoming message. */
1134
Ben Lindstrom5699c5f2001-03-05 06:17:49 +00001135 packet_inject_ignore(64);
Damien Miller874d77b2000-10-14 16:23:11 +11001136 packet_send();
Damien Miller874d77b2000-10-14 16:23:11 +11001137}
Damien Miller62cee002000-09-23 17:15:56 +11001138
1139/* find auth method */
1140
Damien Miller62cee002000-09-23 17:15:56 +11001141/*
1142 * given auth method name, if configurable options permit this method fill
1143 * in auth_ident field and return true, otherwise return false.
1144 */
1145int
1146authmethod_is_enabled(Authmethod *method)
1147{
1148 if (method == NULL)
1149 return 0;
1150 /* return false if options indicate this method is disabled */
1151 if (method->enabled == NULL || *method->enabled == 0)
1152 return 0;
1153 /* return false if batch mode is enabled but method needs interactive mode */
1154 if (method->batch_flag != NULL && *method->batch_flag != 0)
1155 return 0;
1156 return 1;
1157}
1158
1159Authmethod *
1160authmethod_lookup(const char *name)
1161{
1162 Authmethod *method = NULL;
1163 if (name != NULL)
1164 for (method = authmethods; method->name != NULL; method++)
1165 if (strcmp(name, method->name) == 0)
1166 return method;
1167 debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
1168 return NULL;
1169}
1170
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001171/* XXX internal state */
1172static Authmethod *current = NULL;
1173static char *supported = NULL;
1174static char *preferred = NULL;
Damien Miller62cee002000-09-23 17:15:56 +11001175/*
1176 * Given the authentication method list sent by the server, return the
1177 * next method we should try. If the server initially sends a nil list,
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001178 * use a built-in default list.
Kevin Stevesef4eea92001-02-05 12:42:17 +00001179 */
Damien Miller62cee002000-09-23 17:15:56 +11001180Authmethod *
1181authmethod_get(char *authlist)
1182{
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001183
1184 char *name = NULL;
1185 int next;
Kevin Stevesef4eea92001-02-05 12:42:17 +00001186
Damien Miller62cee002000-09-23 17:15:56 +11001187 /* Use a suitable default if we're passed a nil list. */
1188 if (authlist == NULL || strlen(authlist) == 0)
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001189 authlist = options.preferred_authentications;
Damien Miller62cee002000-09-23 17:15:56 +11001190
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001191 if (supported == NULL || strcmp(authlist, supported) != 0) {
1192 debug3("start over, passed a different list %s", authlist);
1193 if (supported != NULL)
1194 xfree(supported);
1195 supported = xstrdup(authlist);
1196 preferred = options.preferred_authentications;
1197 debug3("preferred %s", preferred);
1198 current = NULL;
1199 } else if (current != NULL && authmethod_is_enabled(current))
1200 return current;
Damien Millereba71ba2000-04-29 23:57:08 +10001201
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001202 for (;;) {
1203 if ((name = match_list(preferred, supported, &next)) == NULL) {
1204 debug("no more auth methods to try");
1205 current = NULL;
1206 return NULL;
Damien Miller874d77b2000-10-14 16:23:11 +11001207 }
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001208 preferred += next;
1209 debug3("authmethod_lookup %s", name);
1210 debug3("remaining preferred: %s", preferred);
1211 if ((current = authmethod_lookup(name)) != NULL &&
1212 authmethod_is_enabled(current)) {
1213 debug3("authmethod_is_enabled %s", name);
1214 debug("next auth method to try is %s", name);
1215 return current;
1216 }
Damien Millereba71ba2000-04-29 23:57:08 +10001217 }
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001218}
Damien Miller62cee002000-09-23 17:15:56 +11001219
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001220
1221#define DELIM ","
1222char *
1223authmethods_get(void)
1224{
1225 Authmethod *method = NULL;
1226 char buf[1024];
1227
1228 buf[0] = '\0';
1229 for (method = authmethods; method->name != NULL; method++) {
1230 if (authmethod_is_enabled(method)) {
1231 if (buf[0] != '\0')
1232 strlcat(buf, DELIM, sizeof buf);
1233 strlcat(buf, method->name, sizeof buf);
1234 }
Damien Miller62cee002000-09-23 17:15:56 +11001235 }
Ben Lindstromb9be60a2001-03-11 01:49:19 +00001236 return xstrdup(buf);
Damien Millereba71ba2000-04-29 23:57:08 +10001237}