blob: 1c52231b953cfe249675d4e62803f94cb0834ded [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"
Ben Lindstromf28f6342001-04-04 02:03:04 +000026RCSID("$OpenBSD: sshconnect2.c,v 1.63 2001/04/04 00:06:54 markus 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 Millerad833b32000-08-23 10:46:23 +100050#include "authfd.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000051#include "log.h"
52#include "readconf.h"
53#include "readpass.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000054#include "match.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000055#include "dispatch.h"
Damien Miller874d77b2000-10-14 16:23:11 +110056
Damien Millereba71ba2000-04-29 23:57:08 +100057/* import */
58extern char *client_version_string;
59extern char *server_version_string;
60extern Options options;
61
62/*
63 * SSH2 key exchange
64 */
65
Ben Lindstrom46c16222000-12-22 01:43:59 +000066u_char *session_id2 = NULL;
Damien Millereba71ba2000-04-29 23:57:08 +100067int session_id2_len = 0;
68
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000069char *xxx_host;
70struct sockaddr *xxx_hostaddr;
71
Ben Lindstromf28f6342001-04-04 02:03:04 +000072Kex *xxx_kex = NULL;
73
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000074int
75check_host_key_callback(Key *hostkey)
76{
77 check_host_key(xxx_host, xxx_hostaddr, hostkey,
78 options.user_hostfile2, options.system_hostfile2);
79 return 0;
80}
81
Damien Millereba71ba2000-04-29 23:57:08 +100082void
Damien Miller874d77b2000-10-14 16:23:11 +110083ssh_kex2(char *host, struct sockaddr *hostaddr)
84{
Damien Miller874d77b2000-10-14 16:23:11 +110085 Kex *kex;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000086
87 xxx_host = host;
88 xxx_hostaddr = hostaddr;
Damien Miller874d77b2000-10-14 16:23:11 +110089
Damien Millere39cacc2000-11-29 12:18:44 +110090 if (options.ciphers == (char *)-1) {
91 log("No valid ciphers for protocol version 2 given, using defaults.");
92 options.ciphers = NULL;
Damien Miller874d77b2000-10-14 16:23:11 +110093 }
94 if (options.ciphers != NULL) {
95 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
96 myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
97 }
Damien Millera0ff4662001-03-30 10:49:35 +100098 myproposal[PROPOSAL_ENC_ALGS_CTOS] =
99 compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
100 myproposal[PROPOSAL_ENC_ALGS_STOC] =
101 compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
Damien Miller874d77b2000-10-14 16:23:11 +1100102 if (options.compression) {
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000103 myproposal[PROPOSAL_COMP_ALGS_CTOS] =
Damien Miller874d77b2000-10-14 16:23:11 +1100104 myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib";
105 } else {
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000106 myproposal[PROPOSAL_COMP_ALGS_CTOS] =
Damien Miller874d77b2000-10-14 16:23:11 +1100107 myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
108 }
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000109 if (options.macs != NULL) {
110 myproposal[PROPOSAL_MAC_ALGS_CTOS] =
111 myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
112 }
Damien Miller874d77b2000-10-14 16:23:11 +1100113
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000114 kex = kex_start(myproposal);
115 kex->client_version_string=client_version_string;
116 kex->server_version_string=server_version_string;
117 kex->check_host_key=&check_host_key_callback;
Damien Miller874d77b2000-10-14 16:23:11 +1100118
Ben Lindstromf28f6342001-04-04 02:03:04 +0000119 xxx_kex = kex;
120
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000121 /* start key exchange */
122 dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex);
Damien Miller874d77b2000-10-14 16:23:11 +1100123
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000124 session_id2 = kex->session_id;
125 session_id2_len = kex->session_id_len;
126
Damien Miller874d77b2000-10-14 16:23:11 +1100127#ifdef DEBUG_KEXDH
128 /* send 1st encrypted/maced/compressed message */
129 packet_start(SSH2_MSG_IGNORE);
130 packet_put_cstring("markus");
131 packet_send();
132 packet_write_wait();
133#endif
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000134 debug("done: ssh_kex2.");
Damien Millereba71ba2000-04-29 23:57:08 +1000135}
Damien Millerb1715dc2000-05-30 13:44:51 +1000136
Damien Millereba71ba2000-04-29 23:57:08 +1000137/*
138 * Authenticate user
139 */
Damien Miller62cee002000-09-23 17:15:56 +1100140
141typedef struct Authctxt Authctxt;
142typedef struct Authmethod Authmethod;
143
144typedef int sign_cb_fn(
145 Authctxt *authctxt, Key *key,
Ben Lindstrom46c16222000-12-22 01:43:59 +0000146 u_char **sigp, int *lenp, u_char *data, int datalen);
Damien Miller62cee002000-09-23 17:15:56 +1100147
148struct Authctxt {
149 const char *server_user;
150 const char *host;
151 const char *service;
152 AuthenticationConnection *agent;
Damien Miller62cee002000-09-23 17:15:56 +1100153 Authmethod *method;
Damien Miller874d77b2000-10-14 16:23:11 +1100154 int success;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000155 char *authlist;
156 Key *last_key;
157 sign_cb_fn *last_key_sign;
158 int last_key_hint;
Damien Miller62cee002000-09-23 17:15:56 +1100159};
160struct Authmethod {
161 char *name; /* string to compare against server's list */
162 int (*userauth)(Authctxt *authctxt);
163 int *enabled; /* flag in option struct that enables method */
164 int *batch_flag; /* flag in option struct that disables method */
165};
166
167void input_userauth_success(int type, int plen, void *ctxt);
168void input_userauth_failure(int type, int plen, void *ctxt);
Ben Lindstromd26dcf32001-01-06 15:18:16 +0000169void input_userauth_banner(int type, int plen, void *ctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100170void input_userauth_error(int type, int plen, void *ctxt);
Damien Miller874d77b2000-10-14 16:23:11 +1100171void input_userauth_info_req(int type, int plen, void *ctxt);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000172void input_userauth_pk_ok(int type, int plen, void *ctxt);
Damien Miller874d77b2000-10-14 16:23:11 +1100173
174int userauth_none(Authctxt *authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100175int userauth_pubkey(Authctxt *authctxt);
176int userauth_passwd(Authctxt *authctxt);
Damien Miller874d77b2000-10-14 16:23:11 +1100177int userauth_kbdint(Authctxt *authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100178
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000179void userauth(Authctxt *authctxt, char *authlist);
180
181int
182sign_and_send_pubkey(Authctxt *authctxt, Key *k,
183 sign_cb_fn *sign_callback);
184void clear_auth_state(Authctxt *authctxt);
185
Damien Miller874d77b2000-10-14 16:23:11 +1100186Authmethod *authmethod_get(char *authlist);
187Authmethod *authmethod_lookup(const char *name);
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000188char *authmethods_get(void);
Damien Miller62cee002000-09-23 17:15:56 +1100189
190Authmethod authmethods[] = {
191 {"publickey",
192 userauth_pubkey,
Damien Miller0bc1bd82000-11-13 22:57:25 +1100193 &options.pubkey_authentication,
Damien Miller62cee002000-09-23 17:15:56 +1100194 NULL},
195 {"password",
196 userauth_passwd,
197 &options.password_authentication,
198 &options.batch_mode},
Damien Miller874d77b2000-10-14 16:23:11 +1100199 {"keyboard-interactive",
200 userauth_kbdint,
201 &options.kbd_interactive_authentication,
202 &options.batch_mode},
203 {"none",
204 userauth_none,
205 NULL,
206 NULL},
Damien Miller62cee002000-09-23 17:15:56 +1100207 {NULL, NULL, NULL, NULL}
208};
209
210void
211ssh_userauth2(const char *server_user, char *host)
212{
213 Authctxt authctxt;
214 int type;
215 int plen;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000216 int i;
Damien Miller62cee002000-09-23 17:15:56 +1100217
Ben Lindstrom95fb2dd2001-01-23 03:12:10 +0000218 if (options.challenge_reponse_authentication)
219 options.kbd_interactive_authentication = 1;
220
Damien Miller62cee002000-09-23 17:15:56 +1100221 debug("send SSH2_MSG_SERVICE_REQUEST");
222 packet_start(SSH2_MSG_SERVICE_REQUEST);
223 packet_put_cstring("ssh-userauth");
224 packet_send();
225 packet_write_wait();
226 type = packet_read(&plen);
227 if (type != SSH2_MSG_SERVICE_ACCEPT) {
228 fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type);
229 }
230 if (packet_remaining() > 0) {
231 char *reply = packet_get_string(&plen);
232 debug("service_accept: %s", reply);
233 xfree(reply);
Damien Miller62cee002000-09-23 17:15:56 +1100234 } else {
235 debug("buggy server: service_accept w/o service");
236 }
237 packet_done();
238 debug("got SSH2_MSG_SERVICE_ACCEPT");
239
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000240 if (options.preferred_authentications == NULL)
241 options.preferred_authentications = authmethods_get();
242
Damien Miller62cee002000-09-23 17:15:56 +1100243 /* setup authentication context */
244 authctxt.agent = ssh_get_authentication_connection();
245 authctxt.server_user = server_user;
246 authctxt.host = host;
247 authctxt.service = "ssh-connection"; /* service name */
248 authctxt.success = 0;
Damien Miller874d77b2000-10-14 16:23:11 +1100249 authctxt.method = authmethod_lookup("none");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000250 authctxt.authlist = NULL;
Damien Miller874d77b2000-10-14 16:23:11 +1100251 if (authctxt.method == NULL)
252 fatal("ssh_userauth2: internal error: cannot send userauth none request");
Damien Miller62cee002000-09-23 17:15:56 +1100253
254 /* initial userauth request */
Damien Miller874d77b2000-10-14 16:23:11 +1100255 userauth_none(&authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100256
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000257 //dispatch_init(&input_userauth_error);
258 for (i = 50; i <= 254; i++) {
259 dispatch_set(i, &input_userauth_error);
260 }
Damien Miller62cee002000-09-23 17:15:56 +1100261 dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
262 dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
Ben Lindstromd26dcf32001-01-06 15:18:16 +0000263 dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
Damien Miller62cee002000-09-23 17:15:56 +1100264 dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */
265
266 if (authctxt.agent != NULL)
267 ssh_close_authentication_connection(authctxt.agent);
268
Ben Lindstrom4dccfa52000-12-28 16:40:05 +0000269 debug("ssh-userauth2 successful: method %s", authctxt.method->name);
Damien Miller62cee002000-09-23 17:15:56 +1100270}
271void
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000272userauth(Authctxt *authctxt, char *authlist)
273{
274 if (authlist == NULL) {
275 authlist = authctxt->authlist;
276 } else {
277 if (authctxt->authlist)
278 xfree(authctxt->authlist);
279 authctxt->authlist = authlist;
280 }
281 for (;;) {
282 Authmethod *method = authmethod_get(authlist);
283 if (method == NULL)
284 fatal("Permission denied (%s).", authlist);
285 authctxt->method = method;
286 if (method->userauth(authctxt) != 0) {
287 debug2("we sent a %s packet, wait for reply", method->name);
288 break;
289 } else {
290 debug2("we did not send a packet, disable method");
291 method->enabled = NULL;
292 }
293 }
294}
295void
Damien Miller62cee002000-09-23 17:15:56 +1100296input_userauth_error(int type, int plen, void *ctxt)
297{
Ben Lindstromd26dcf32001-01-06 15:18:16 +0000298 fatal("input_userauth_error: bad message during authentication: "
299 "type %d", type);
300}
301void
302input_userauth_banner(int type, int plen, void *ctxt)
303{
304 char *msg, *lang;
305 debug3("input_userauth_banner");
306 msg = packet_get_string(NULL);
307 lang = packet_get_string(NULL);
308 fprintf(stderr, "%s", msg);
309 xfree(msg);
310 xfree(lang);
Damien Miller62cee002000-09-23 17:15:56 +1100311}
312void
313input_userauth_success(int type, int plen, void *ctxt)
314{
315 Authctxt *authctxt = ctxt;
316 if (authctxt == NULL)
317 fatal("input_userauth_success: no authentication context");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000318 if (authctxt->authlist)
319 xfree(authctxt->authlist);
320 clear_auth_state(authctxt);
Damien Miller62cee002000-09-23 17:15:56 +1100321 authctxt->success = 1; /* break out */
322}
323void
324input_userauth_failure(int type, int plen, void *ctxt)
325{
Damien Miller62cee002000-09-23 17:15:56 +1100326 Authctxt *authctxt = ctxt;
327 char *authlist = NULL;
328 int partial;
Damien Miller62cee002000-09-23 17:15:56 +1100329
330 if (authctxt == NULL)
331 fatal("input_userauth_failure: no authentication context");
332
Damien Miller874d77b2000-10-14 16:23:11 +1100333 authlist = packet_get_string(NULL);
Damien Miller62cee002000-09-23 17:15:56 +1100334 partial = packet_get_char();
335 packet_done();
336
337 if (partial != 0)
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000338 log("Authenticated with partial success.");
Damien Miller62cee002000-09-23 17:15:56 +1100339 debug("authentications that can continue: %s", authlist);
340
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000341 clear_auth_state(authctxt);
342 userauth(authctxt, authlist);
343}
344void
345input_userauth_pk_ok(int type, int plen, void *ctxt)
346{
347 Authctxt *authctxt = ctxt;
348 Key *key = NULL;
349 Buffer b;
350 int alen, blen, pktype, sent = 0;
Ben Lindstromcfccef92001-03-13 04:57:58 +0000351 char *pkalg, *pkblob, *fp;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000352
353 if (authctxt == NULL)
354 fatal("input_userauth_pk_ok: no authentication context");
355 if (datafellows & SSH_BUG_PKOK) {
356 /* this is similar to SSH_BUG_PKAUTH */
357 debug2("input_userauth_pk_ok: SSH_BUG_PKOK");
358 pkblob = packet_get_string(&blen);
359 buffer_init(&b);
360 buffer_append(&b, pkblob, blen);
361 pkalg = buffer_get_string(&b, &alen);
362 buffer_free(&b);
363 } else {
364 pkalg = packet_get_string(&alen);
365 pkblob = packet_get_string(&blen);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000366 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000367 packet_done();
368
369 debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d",
370 pkalg, blen, authctxt->last_key, authctxt->last_key_hint);
371
372 do {
373 if (authctxt->last_key == NULL ||
374 authctxt->last_key_sign == NULL) {
375 debug("no last key or no sign cb");
376 break;
377 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000378 if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) {
379 debug("unknown pkalg %s", pkalg);
380 break;
381 }
382 if ((key = key_from_blob(pkblob, blen)) == NULL) {
383 debug("no key from blob. pkalg %s", pkalg);
384 break;
385 }
Ben Lindstromcfccef92001-03-13 04:57:58 +0000386 fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX);
387 debug2("input_userauth_pk_ok: fp %s", fp);
388 xfree(fp);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000389 if (!key_equal(key, authctxt->last_key)) {
390 debug("key != last_key");
391 break;
392 }
393 sent = sign_and_send_pubkey(authctxt, key,
394 authctxt->last_key_sign);
395 } while(0);
396
397 if (key != NULL)
398 key_free(key);
399 xfree(pkalg);
400 xfree(pkblob);
401
402 /* unregister */
403 clear_auth_state(authctxt);
404 dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL);
405
406 /* try another method if we did not send a packet*/
407 if (sent == 0)
408 userauth(authctxt, NULL);
409
Damien Miller62cee002000-09-23 17:15:56 +1100410}
411
Damien Millereba71ba2000-04-29 23:57:08 +1000412int
Damien Miller874d77b2000-10-14 16:23:11 +1100413userauth_none(Authctxt *authctxt)
414{
415 /* initial userauth request */
416 packet_start(SSH2_MSG_USERAUTH_REQUEST);
417 packet_put_cstring(authctxt->server_user);
418 packet_put_cstring(authctxt->service);
419 packet_put_cstring(authctxt->method->name);
420 packet_send();
Damien Miller874d77b2000-10-14 16:23:11 +1100421 return 1;
422}
423
424int
Damien Miller62cee002000-09-23 17:15:56 +1100425userauth_passwd(Authctxt *authctxt)
Damien Millereba71ba2000-04-29 23:57:08 +1000426{
Damien Millere247cc42000-05-07 12:03:14 +1000427 static int attempt = 0;
Damien Millereba71ba2000-04-29 23:57:08 +1000428 char prompt[80];
429 char *password;
430
Damien Millerd3a18572000-06-07 19:55:44 +1000431 if (attempt++ >= options.number_of_password_prompts)
Damien Millere247cc42000-05-07 12:03:14 +1000432 return 0;
433
Damien Millerd3a18572000-06-07 19:55:44 +1000434 if(attempt != 1)
435 error("Permission denied, please try again.");
436
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000437 snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
Damien Miller62cee002000-09-23 17:15:56 +1100438 authctxt->server_user, authctxt->host);
Damien Millereba71ba2000-04-29 23:57:08 +1000439 password = read_passphrase(prompt, 0);
440 packet_start(SSH2_MSG_USERAUTH_REQUEST);
Damien Miller62cee002000-09-23 17:15:56 +1100441 packet_put_cstring(authctxt->server_user);
442 packet_put_cstring(authctxt->service);
Damien Miller874d77b2000-10-14 16:23:11 +1100443 packet_put_cstring(authctxt->method->name);
Damien Millereba71ba2000-04-29 23:57:08 +1000444 packet_put_char(0);
Ben Lindstrom5699c5f2001-03-05 06:17:49 +0000445 packet_put_cstring(password);
Damien Millereba71ba2000-04-29 23:57:08 +1000446 memset(password, 0, strlen(password));
447 xfree(password);
Ben Lindstrom5699c5f2001-03-05 06:17:49 +0000448 packet_inject_ignore(64);
Damien Millereba71ba2000-04-29 23:57:08 +1000449 packet_send();
Damien Millereba71ba2000-04-29 23:57:08 +1000450 return 1;
451}
452
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000453void
454clear_auth_state(Authctxt *authctxt)
455{
456 /* XXX clear authentication state */
457 if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) {
458 debug3("clear_auth_state: key_free %p", authctxt->last_key);
459 key_free(authctxt->last_key);
460 }
461 authctxt->last_key = NULL;
462 authctxt->last_key_hint = -2;
463 authctxt->last_key_sign = NULL;
464}
465
Damien Millerad833b32000-08-23 10:46:23 +1000466int
Damien Miller62cee002000-09-23 17:15:56 +1100467sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback)
Damien Millereba71ba2000-04-29 23:57:08 +1000468{
469 Buffer b;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000470 u_char *blob, *signature;
Damien Millereba71ba2000-04-29 23:57:08 +1000471 int bloblen, slen;
Damien Miller6536c7d2000-06-22 21:32:31 +1000472 int skip = 0;
Damien Millerad833b32000-08-23 10:46:23 +1000473 int ret = -1;
Damien Miller874d77b2000-10-14 16:23:11 +1100474 int have_sig = 1;
Damien Millereba71ba2000-04-29 23:57:08 +1000475
Ben Lindstromd121f612000-12-03 17:00:47 +0000476 debug3("sign_and_send_pubkey");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000477
Damien Miller0bc1bd82000-11-13 22:57:25 +1100478 if (key_to_blob(k, &blob, &bloblen) == 0) {
479 /* we cannot handle this key */
Ben Lindstromd121f612000-12-03 17:00:47 +0000480 debug3("sign_and_send_pubkey: cannot handle key");
Damien Miller0bc1bd82000-11-13 22:57:25 +1100481 return 0;
482 }
Damien Millereba71ba2000-04-29 23:57:08 +1000483 /* data to be signed */
484 buffer_init(&b);
Damien Miller50a41ed2000-10-16 12:14:42 +1100485 if (datafellows & SSH_OLD_SESSIONID) {
Damien Miller6536c7d2000-06-22 21:32:31 +1000486 buffer_append(&b, session_id2, session_id2_len);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000487 skip = session_id2_len;
Damien Miller50a41ed2000-10-16 12:14:42 +1100488 } else {
489 buffer_put_string(&b, session_id2, session_id2_len);
490 skip = buffer_len(&b);
Damien Miller6536c7d2000-06-22 21:32:31 +1000491 }
Damien Millereba71ba2000-04-29 23:57:08 +1000492 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
Damien Miller62cee002000-09-23 17:15:56 +1100493 buffer_put_cstring(&b, authctxt->server_user);
Damien Miller30c3d422000-05-09 11:02:59 +1000494 buffer_put_cstring(&b,
Ben Lindstromd121f612000-12-03 17:00:47 +0000495 datafellows & SSH_BUG_PKSERVICE ?
Damien Miller30c3d422000-05-09 11:02:59 +1000496 "ssh-userauth" :
Damien Miller62cee002000-09-23 17:15:56 +1100497 authctxt->service);
Ben Lindstromd121f612000-12-03 17:00:47 +0000498 if (datafellows & SSH_BUG_PKAUTH) {
499 buffer_put_char(&b, have_sig);
500 } else {
501 buffer_put_cstring(&b, authctxt->method->name);
502 buffer_put_char(&b, have_sig);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000503 buffer_put_cstring(&b, key_ssh_name(k));
Ben Lindstromd121f612000-12-03 17:00:47 +0000504 }
Damien Millereba71ba2000-04-29 23:57:08 +1000505 buffer_put_string(&b, blob, bloblen);
Damien Millereba71ba2000-04-29 23:57:08 +1000506
507 /* generate signature */
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000508 ret = (*sign_callback)(authctxt, k, &signature, &slen,
509 buffer_ptr(&b), buffer_len(&b));
Damien Millerad833b32000-08-23 10:46:23 +1000510 if (ret == -1) {
511 xfree(blob);
512 buffer_free(&b);
513 return 0;
514 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100515#ifdef DEBUG_PK
Damien Millereba71ba2000-04-29 23:57:08 +1000516 buffer_dump(&b);
517#endif
Ben Lindstromd121f612000-12-03 17:00:47 +0000518 if (datafellows & SSH_BUG_PKSERVICE) {
Damien Miller30c3d422000-05-09 11:02:59 +1000519 buffer_clear(&b);
520 buffer_append(&b, session_id2, session_id2_len);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000521 skip = session_id2_len;
Damien Miller30c3d422000-05-09 11:02:59 +1000522 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
Damien Miller62cee002000-09-23 17:15:56 +1100523 buffer_put_cstring(&b, authctxt->server_user);
524 buffer_put_cstring(&b, authctxt->service);
Damien Miller874d77b2000-10-14 16:23:11 +1100525 buffer_put_cstring(&b, authctxt->method->name);
526 buffer_put_char(&b, have_sig);
Ben Lindstromd121f612000-12-03 17:00:47 +0000527 if (!(datafellows & SSH_BUG_PKAUTH))
Kevin Stevesef4eea92001-02-05 12:42:17 +0000528 buffer_put_cstring(&b, key_ssh_name(k));
Damien Miller30c3d422000-05-09 11:02:59 +1000529 buffer_put_string(&b, blob, bloblen);
530 }
531 xfree(blob);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000532
Damien Millereba71ba2000-04-29 23:57:08 +1000533 /* append signature */
534 buffer_put_string(&b, signature, slen);
535 xfree(signature);
536
537 /* skip session id and packet type */
Damien Miller6536c7d2000-06-22 21:32:31 +1000538 if (buffer_len(&b) < skip + 1)
Damien Miller62cee002000-09-23 17:15:56 +1100539 fatal("userauth_pubkey: internal error");
Damien Miller6536c7d2000-06-22 21:32:31 +1000540 buffer_consume(&b, skip + 1);
Damien Millereba71ba2000-04-29 23:57:08 +1000541
542 /* put remaining data from buffer into packet */
543 packet_start(SSH2_MSG_USERAUTH_REQUEST);
544 packet_put_raw(buffer_ptr(&b), buffer_len(&b));
545 buffer_free(&b);
Damien Millereba71ba2000-04-29 23:57:08 +1000546 packet_send();
Damien Millerad833b32000-08-23 10:46:23 +1000547
548 return 1;
Damien Miller994cf142000-07-21 10:19:44 +1000549}
550
551int
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000552send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback,
553 int hint)
Damien Miller994cf142000-07-21 10:19:44 +1000554{
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000555 u_char *blob;
556 int bloblen, have_sig = 0;
Damien Miller994cf142000-07-21 10:19:44 +1000557
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000558 debug3("send_pubkey_test");
559
560 if (key_to_blob(k, &blob, &bloblen) == 0) {
561 /* we cannot handle this key */
562 debug3("send_pubkey_test: cannot handle key");
Damien Miller994cf142000-07-21 10:19:44 +1000563 return 0;
564 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000565 /* register callback for USERAUTH_PK_OK message */
566 authctxt->last_key_sign = sign_callback;
567 authctxt->last_key_hint = hint;
568 authctxt->last_key = k;
569 dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
Damien Miller994cf142000-07-21 10:19:44 +1000570
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000571 packet_start(SSH2_MSG_USERAUTH_REQUEST);
572 packet_put_cstring(authctxt->server_user);
573 packet_put_cstring(authctxt->service);
574 packet_put_cstring(authctxt->method->name);
575 packet_put_char(have_sig);
576 if (!(datafellows & SSH_BUG_PKAUTH))
577 packet_put_cstring(key_ssh_name(k));
578 packet_put_string(blob, bloblen);
579 xfree(blob);
580 packet_send();
581 return 1;
582}
583
584Key *
585load_identity_file(char *filename)
586{
587 Key *private;
588 char prompt[300], *passphrase;
Ben Lindstromd0fca422001-03-26 13:44:06 +0000589 int quit, i;
Ben Lindstrom329782e2001-03-10 17:08:59 +0000590 struct stat st;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000591
Ben Lindstrom329782e2001-03-10 17:08:59 +0000592 if (stat(filename, &st) < 0) {
593 debug3("no such identity: %s", filename);
594 return NULL;
595 }
Ben Lindstromd0fca422001-03-26 13:44:06 +0000596 private = key_load_private_type(KEY_UNSPEC, filename, "", NULL);
597 if (private == NULL) {
598 if (options.batch_mode)
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000599 return NULL;
Damien Miller994cf142000-07-21 10:19:44 +1000600 snprintf(prompt, sizeof prompt,
Damien Miller0bc1bd82000-11-13 22:57:25 +1100601 "Enter passphrase for key '%.100s': ", filename);
Damien Miller62cee002000-09-23 17:15:56 +1100602 for (i = 0; i < options.number_of_password_prompts; i++) {
603 passphrase = read_passphrase(prompt, 0);
604 if (strcmp(passphrase, "") != 0) {
Ben Lindstromd0fca422001-03-26 13:44:06 +0000605 private = key_load_private_type(KEY_UNSPEC, filename,
606 passphrase, NULL);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000607 quit = 0;
Damien Miller62cee002000-09-23 17:15:56 +1100608 } else {
609 debug2("no passphrase given, try next key");
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000610 quit = 1;
Damien Miller62cee002000-09-23 17:15:56 +1100611 }
612 memset(passphrase, 0, strlen(passphrase));
613 xfree(passphrase);
Ben Lindstromd0fca422001-03-26 13:44:06 +0000614 if (private != NULL || quit)
Damien Miller62cee002000-09-23 17:15:56 +1100615 break;
616 debug2("bad passphrase given, try again...");
617 }
Damien Miller994cf142000-07-21 10:19:44 +1000618 }
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000619 return private;
620}
621
622int
623identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
624 u_char *data, int datalen)
625{
626 Key *private;
627 int idx, ret;
628
629 idx = authctxt->last_key_hint;
630 if (idx < 0)
631 return -1;
632 private = load_identity_file(options.identity_files[idx]);
633 if (private == NULL)
634 return -1;
635 ret = key_sign(private, sigp, lenp, data, datalen);
636 key_free(private);
Damien Millerad833b32000-08-23 10:46:23 +1000637 return ret;
638}
639
Ben Lindstrom46c16222000-12-22 01:43:59 +0000640int agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
641 u_char *data, int datalen)
Damien Millerad833b32000-08-23 10:46:23 +1000642{
Damien Miller62cee002000-09-23 17:15:56 +1100643 return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen);
Damien Millerad833b32000-08-23 10:46:23 +1000644}
645
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000646int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp,
647 u_char *data, int datalen)
648{
649 return key_sign(key, sigp, lenp, data, datalen);
650}
651
Damien Millerad833b32000-08-23 10:46:23 +1000652int
Damien Miller62cee002000-09-23 17:15:56 +1100653userauth_pubkey_agent(Authctxt *authctxt)
Damien Millerad833b32000-08-23 10:46:23 +1000654{
655 static int called = 0;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100656 int ret = 0;
Damien Millerad833b32000-08-23 10:46:23 +1000657 char *comment;
658 Key *k;
Damien Millerad833b32000-08-23 10:46:23 +1000659
660 if (called == 0) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100661 if (ssh_get_num_identities(authctxt->agent, 2) == 0)
662 debug2("userauth_pubkey_agent: no keys at all");
Damien Miller62cee002000-09-23 17:15:56 +1100663 called = 1;
Damien Millerad833b32000-08-23 10:46:23 +1000664 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100665 k = ssh_get_next_identity(authctxt->agent, &comment, 2);
Damien Miller62cee002000-09-23 17:15:56 +1100666 if (k == NULL) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100667 debug2("userauth_pubkey_agent: no more keys");
668 } else {
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000669 debug("userauth_pubkey_agent: testing agent key %s", comment);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100670 xfree(comment);
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000671 ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1);
672 if (ret == 0)
673 key_free(k);
Damien Miller62cee002000-09-23 17:15:56 +1100674 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100675 if (ret == 0)
676 debug2("userauth_pubkey_agent: no message sent");
Damien Millerad833b32000-08-23 10:46:23 +1000677 return ret;
Damien Millereba71ba2000-04-29 23:57:08 +1000678}
679
Damien Miller62cee002000-09-23 17:15:56 +1100680int
681userauth_pubkey(Authctxt *authctxt)
Damien Millereba71ba2000-04-29 23:57:08 +1000682{
Damien Miller62cee002000-09-23 17:15:56 +1100683 static int idx = 0;
684 int sent = 0;
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000685 Key *key;
686 char *filename;
Damien Millereba71ba2000-04-29 23:57:08 +1000687
Damien Miller0bc1bd82000-11-13 22:57:25 +1100688 if (authctxt->agent != NULL) {
689 do {
690 sent = userauth_pubkey_agent(authctxt);
691 } while(!sent && authctxt->agent->howmany > 0);
692 }
693 while (!sent && idx < options.num_identity_files) {
Ben Lindstrom266dfdf2001-03-09 00:12:22 +0000694 key = options.identity_keys[idx];
695 filename = options.identity_files[idx];
696 if (key == NULL) {
697 debug("try privkey: %s", filename);
698 key = load_identity_file(filename);
699 if (key != NULL) {
700 sent = sign_and_send_pubkey(authctxt, key,
701 key_sign_cb);
702 key_free(key);
703 }
704 } else if (key->type != KEY_RSA1) {
705 debug("try pubkey: %s", filename);
706 sent = send_pubkey_test(authctxt, key,
707 identity_sign_cb, idx);
708 }
Damien Miller0bc1bd82000-11-13 22:57:25 +1100709 idx++;
710 }
Damien Miller62cee002000-09-23 17:15:56 +1100711 return sent;
712}
Damien Millereba71ba2000-04-29 23:57:08 +1000713
Damien Miller874d77b2000-10-14 16:23:11 +1100714/*
715 * Send userauth request message specifying keyboard-interactive method.
716 */
717int
718userauth_kbdint(Authctxt *authctxt)
719{
720 static int attempt = 0;
721
722 if (attempt++ >= options.number_of_password_prompts)
723 return 0;
724
725 debug2("userauth_kbdint");
726 packet_start(SSH2_MSG_USERAUTH_REQUEST);
727 packet_put_cstring(authctxt->server_user);
728 packet_put_cstring(authctxt->service);
729 packet_put_cstring(authctxt->method->name);
730 packet_put_cstring(""); /* lang */
731 packet_put_cstring(options.kbd_interactive_devices ?
732 options.kbd_interactive_devices : "");
733 packet_send();
Damien Miller874d77b2000-10-14 16:23:11 +1100734
735 dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req);
736 return 1;
737}
738
739/*
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000740 * parse INFO_REQUEST, prompt user and send INFO_RESPONSE
Damien Miller874d77b2000-10-14 16:23:11 +1100741 */
742void
743input_userauth_info_req(int type, int plen, void *ctxt)
744{
745 Authctxt *authctxt = ctxt;
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000746 char *name, *inst, *lang, *prompt, *response;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000747 u_int num_prompts, i;
Damien Miller874d77b2000-10-14 16:23:11 +1100748 int echo = 0;
749
750 debug2("input_userauth_info_req");
751
752 if (authctxt == NULL)
753 fatal("input_userauth_info_req: no authentication context");
754
755 name = packet_get_string(NULL);
756 inst = packet_get_string(NULL);
757 lang = packet_get_string(NULL);
Damien Miller874d77b2000-10-14 16:23:11 +1100758 if (strlen(name) > 0)
759 cli_mesg(name);
Damien Miller874d77b2000-10-14 16:23:11 +1100760 if (strlen(inst) > 0)
761 cli_mesg(inst);
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000762 xfree(name);
Damien Miller874d77b2000-10-14 16:23:11 +1100763 xfree(inst);
Ben Lindstrom03df5bd2001-02-10 22:16:41 +0000764 xfree(lang);
Damien Miller874d77b2000-10-14 16:23:11 +1100765
766 num_prompts = packet_get_int();
767 /*
768 * Begin to build info response packet based on prompts requested.
769 * We commit to providing the correct number of responses, so if
770 * further on we run into a problem that prevents this, we have to
771 * be sure and clean this up and send a correct error response.
772 */
773 packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE);
774 packet_put_int(num_prompts);
775
776 for (i = 0; i < num_prompts; i++) {
777 prompt = packet_get_string(NULL);
778 echo = packet_get_char();
779
780 response = cli_prompt(prompt, echo);
781
Ben Lindstrom5699c5f2001-03-05 06:17:49 +0000782 packet_put_cstring(response);
Damien Miller874d77b2000-10-14 16:23:11 +1100783 memset(response, 0, strlen(response));
784 xfree(response);
785 xfree(prompt);
786 }
787 packet_done(); /* done with parsing incoming message. */
788
Ben Lindstrom5699c5f2001-03-05 06:17:49 +0000789 packet_inject_ignore(64);
Damien Miller874d77b2000-10-14 16:23:11 +1100790 packet_send();
Damien Miller874d77b2000-10-14 16:23:11 +1100791}
Damien Miller62cee002000-09-23 17:15:56 +1100792
793/* find auth method */
794
Damien Miller62cee002000-09-23 17:15:56 +1100795/*
796 * given auth method name, if configurable options permit this method fill
797 * in auth_ident field and return true, otherwise return false.
798 */
799int
800authmethod_is_enabled(Authmethod *method)
801{
802 if (method == NULL)
803 return 0;
804 /* return false if options indicate this method is disabled */
805 if (method->enabled == NULL || *method->enabled == 0)
806 return 0;
807 /* return false if batch mode is enabled but method needs interactive mode */
808 if (method->batch_flag != NULL && *method->batch_flag != 0)
809 return 0;
810 return 1;
811}
812
813Authmethod *
814authmethod_lookup(const char *name)
815{
816 Authmethod *method = NULL;
817 if (name != NULL)
818 for (method = authmethods; method->name != NULL; method++)
819 if (strcmp(name, method->name) == 0)
820 return method;
821 debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
822 return NULL;
823}
824
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000825/* XXX internal state */
826static Authmethod *current = NULL;
827static char *supported = NULL;
828static char *preferred = NULL;
Damien Miller62cee002000-09-23 17:15:56 +1100829/*
830 * Given the authentication method list sent by the server, return the
831 * next method we should try. If the server initially sends a nil list,
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000832 * use a built-in default list.
Kevin Stevesef4eea92001-02-05 12:42:17 +0000833 */
Damien Miller62cee002000-09-23 17:15:56 +1100834Authmethod *
835authmethod_get(char *authlist)
836{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000837
838 char *name = NULL;
839 int next;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000840
Damien Miller62cee002000-09-23 17:15:56 +1100841 /* Use a suitable default if we're passed a nil list. */
842 if (authlist == NULL || strlen(authlist) == 0)
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000843 authlist = options.preferred_authentications;
Damien Miller62cee002000-09-23 17:15:56 +1100844
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000845 if (supported == NULL || strcmp(authlist, supported) != 0) {
846 debug3("start over, passed a different list %s", authlist);
847 if (supported != NULL)
848 xfree(supported);
849 supported = xstrdup(authlist);
850 preferred = options.preferred_authentications;
851 debug3("preferred %s", preferred);
852 current = NULL;
853 } else if (current != NULL && authmethod_is_enabled(current))
854 return current;
Damien Millereba71ba2000-04-29 23:57:08 +1000855
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000856 for (;;) {
857 if ((name = match_list(preferred, supported, &next)) == NULL) {
858 debug("no more auth methods to try");
859 current = NULL;
860 return NULL;
Damien Miller874d77b2000-10-14 16:23:11 +1100861 }
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000862 preferred += next;
863 debug3("authmethod_lookup %s", name);
864 debug3("remaining preferred: %s", preferred);
865 if ((current = authmethod_lookup(name)) != NULL &&
866 authmethod_is_enabled(current)) {
867 debug3("authmethod_is_enabled %s", name);
868 debug("next auth method to try is %s", name);
869 return current;
870 }
Damien Millereba71ba2000-04-29 23:57:08 +1000871 }
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000872}
Damien Miller62cee002000-09-23 17:15:56 +1100873
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000874
875#define DELIM ","
876char *
877authmethods_get(void)
878{
879 Authmethod *method = NULL;
880 char buf[1024];
881
882 buf[0] = '\0';
883 for (method = authmethods; method->name != NULL; method++) {
884 if (authmethod_is_enabled(method)) {
885 if (buf[0] != '\0')
886 strlcat(buf, DELIM, sizeof buf);
887 strlcat(buf, method->name, sizeof buf);
888 }
Damien Miller62cee002000-09-23 17:15:56 +1100889 }
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000890 return xstrdup(buf);
Damien Millereba71ba2000-04-29 23:57:08 +1000891}