lsh.c 24.5 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
3
/* lsh.c
 *
 * client main program
4
5
6
7
8
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
9
 * Copyright (C) 1998, 1999, 2000 Niels Mller
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen's avatar
J.H.M. Dassen committed
23
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
24
25
 */

26
#include "algorithms.h"
Niels Möller's avatar
Niels Möller committed
27
28
#include "alist.h"
#include "atoms.h"
29
#include "channel.h"
30
#include "charset.h"
Niels Möller's avatar
Niels Möller committed
31
#include "client.h"
Niels Möller's avatar
Niels Möller committed
32
#include "client_keyexchange.h"
33
#include "client_userauth.h"
34
35
#include "compress.h"
#include "connection_commands.h"
Niels Möller's avatar
Niels Möller committed
36
#include "crypto.h"
37
#include "dsa.h"
Niels Möller's avatar
Niels Möller committed
38
#include "format.h"
39
#include "interact.h"
Niels Möller's avatar
Niels Möller committed
40
#include "io.h"
41
#include "io_commands.h"
42
43
#include "gateway.h"
#include "gateway_commands.h"
44
#include "handshake.h"
45
#include "lookup_verifier.h"
Niels Möller's avatar
Niels Möller committed
46
#include "randomness.h"
47
#include "rsa.h"
48
#include "sexp.h"
49
#include "spki_commands.h"
50
#include "srp.h" 
51
#include "ssh.h"
52
#include "tcpforward_commands.h"
53
#include "version.h"
Niels Möller's avatar
Niels Möller committed
54
#include "werror.h"
Niels Möller's avatar
Niels Möller committed
55
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
56

57
#include <assert.h>
58
59
60
61
62
63
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

64
65
66
67
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

68
#include "lsh_argp.h"
69

70
/* Forward declarations */
71
struct command_2 lsh_verifier_command;
72
73
#define OPTIONS2VERIFIER (&lsh_verifier_command.super.super)

74
struct command_2 lsh_login_command;
75
76
77
#define LSH_LOGIN (&lsh_login_command.super.super)

static struct command options2known_hosts;
78
#define OPTIONS2KNOWN_HOSTS (&options2known_hosts.super)
79

80
81
struct command options2service;
#define OPTIONS2SERVICE (&options2service.super)
82

83
84
static struct command options2identities;
#define OPTIONS2IDENTITIES (&options2identities.super)
85
86
87
88
89
90

static struct request_service request_userauth_service =
STATIC_REQUEST_SERVICE(ATOM_SSH_USERAUTH);
#define REQUEST_USERAUTH_SERVICE (&request_userauth_service.super.super)


91
92
#include "lsh.c.x"

93
/* Block size for stdout and stderr buffers */
Niels Möller's avatar
Niels Möller committed
94
95
#define BLOCK_SIZE 32768

96
97
98
99
/* Window size for the session channel
 *
 * NOTE: Large windows seem to trig a bug in sshd2. */
#define WINDOW_SIZE 10000
100

101
102
103
/* GABA:
   (class
     (name lsh_options)
104
     (super client_options)
105
     (vars
106
       (algorithms object algorithms_options)
Niels Möller's avatar
Niels Möller committed
107

108
       (random object randomness_with_poll)
109

110
111
112
113
       (signature_algorithms object alist)
       (home . "const char *")
       
       (identity . "char *")
114
115
116
117
       (with_publickey . int)

       (with_srp_keyexchange . int)
       (with_dh_keyexchange . int)
118

119
120
121
       ; Command to invoke to start ssh-connection service)
       (service object command)
       
122
123
124
       ;; (kexinit object make_kexinit)
       (kex_algorithms object int_list)
       
125
126
127
128
129
130
       (sloppy . int)
       (capture . "const char *")
       (capture_file object abstract_write)

       (known_hosts . "const char *")
       
131
       (start_gateway . int)))
132
133
134
135
*/


static struct lsh_options *
136
make_options(struct io_backend *backend,
137
138
139
140
	     struct exception_handler *handler,
	     int *exit_code)
{
  NEW(lsh_options, self);
141
  init_client_options(&self->super, backend, handler, exit_code);
142

143
144
145
146
  self->algorithms
    = make_algorithms_options(all_symmetric_algorithms());

  self->random = make_default_random(NULL, handler);
147
  
148
149
  self->home = getenv("HOME");
  
150
  self->signature_algorithms = all_signature_algorithms(&self->random->super);
151

152
153
154
155
156
  self->sloppy = 0;
  self->capture = NULL;
  self->capture_file = NULL;

  self->known_hosts = NULL;
157
  
158
  self->start_gateway = 0;
159

160
161
162
  self->with_publickey = 1;

  self->with_srp_keyexchange = 0;
163
164
165
166

  /* By default, enable only one of dh and srp. */
  self->with_dh_keyexchange = -1;
  
167
  self->kex_algorithms = NULL;
168
169
170
171
  
  return self;
}

Niels Möller's avatar
Niels Möller committed
172

173
174
/* Request ssh-userauth or ssh-connection service, as appropriate,
 * and pass the options as a first argument. */
175
176
177
178
179
DEFINE_COMMAND(options2service)
     (struct command *s UNUSED,
      struct lsh_object *a,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
180
181
{
  CAST(lsh_options, options, a);
182
  COMMAND_RETURN(c, options->service);
183
184
}

Niels Möller's avatar
Niels Möller committed
185
186
187
188
189
190
191
192
193
194
195
196
/* Open hostkey database. By default, "~/.lsh/known_hosts". */

static void
do_options2known_hosts(struct command *ignored UNUSED,
		       struct lsh_object *a,
		       struct command_continuation *c,
		       struct exception_handler *e)
{
  CAST(lsh_options, options, a);
  
  struct lsh_string *tmp = NULL;
  const char *s = NULL;
Niels Möller's avatar
Niels Möller committed
197
  struct lsh_fd *f;
Niels Möller's avatar
Niels Möller committed
198
199
200
201
202
  
  if (options->known_hosts)
    s = options->known_hosts;
  else 
    {
203
204
      tmp = ssh_format("%lz/.lsh/known_hosts", options->home);
      s = lsh_get_cstring(tmp);
Niels Möller's avatar
Niels Möller committed
205
206
    }
  
207
  f = io_read_file(options->super.backend, s, e);
Niels Möller's avatar
Niels Möller committed
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

  if (!f)
    {
      werror("Failed to open '%z' (errno = %i): %z.\n",
	     s, errno, STRERROR(errno));
      COMMAND_RETURN(c, make_spki_context(options->signature_algorithms));
    }
  else
    {
      CAST_SUBTYPE(command, read,
		   make_spki_read_acls(options->signature_algorithms));
      COMMAND_CALL(read, f, c, e);
    }
  lsh_string_free(tmp);
}

static struct command options2known_hosts =
STATIC_COMMAND(do_options2known_hosts);

/* Read user's private keys. By default, "~/.lsh/identity". */
static void
do_options2identities(struct command *ignored UNUSED,
		      struct lsh_object *a,
		      struct command_continuation *c,
		      struct exception_handler *e)
{
  CAST(lsh_options, options, a);
  
  struct lsh_string *tmp = NULL;
  const char *s = NULL;
Niels Möller's avatar
Niels Möller committed
238
  struct lsh_fd *f = NULL;
Niels Möller's avatar
Niels Möller committed
239

240
241
  trace("do_options2identities\n");
  
242
  if (!options->with_publickey)
Niels Möller's avatar
Niels Möller committed
243
244
245
246
247
248
249
250
    {
      COMMAND_RETURN(c, make_object_list(0, -1));
      return;
    }
  if (options->identity)
    s = options->identity;
  else 
    {
251
252
      tmp = ssh_format("%lz/.lsh/identity", options->home);
      s = lsh_get_cstring(tmp);
Niels Möller's avatar
Niels Möller committed
253
254
    }
  
255
  f = io_read_file(options->super.backend, s, e);
Niels Möller's avatar
Niels Möller committed
256
257
258
259
260
261
262
263
  
  if (!f)
    {
      werror("Failed to open '%z' (errno = %i): %z.\n",
	     s, errno, STRERROR(errno));
      COMMAND_RETURN(c, make_object_list(0, -1));
    }
  else
264
265
266
267
268
269
270
    {
      struct command *command
	= make_spki_read_userkeys(options->algorithms->algorithms,
				  options->signature_algorithms,
				  options->super.tty);
      COMMAND_CALL(command, f, c, e);
    }
Niels Möller's avatar
Niels Möller committed
271
272
273
274
275
276
277
  lsh_string_free(tmp);
}

static struct command options2identities =
STATIC_COMMAND(do_options2identities);


278
/* Maps a host key to a (trusted) verifier object. */
Niels Möller's avatar
Niels Möller committed
279

280
/* GABA:
281
   (class
282
     (name lsh_host_db)
283
284
     (super lookup_verifier)
     (vars
285
       (db object spki_context)
286
       (tty object interact)
287
288
289
290
291
292
       (access object sexp)
       (host object address_info)
       ; Allow unauthorized keys
       (sloppy . int)
       ; If non-null, append an ACL for the received key to this file.
       (file object abstract_write)
293
294
       ; For fingerprinting
       (hash object hash_algorithm)))
295
296
*/

297
static struct verifier *
298
do_lsh_lookup(struct lookup_verifier *c,
Niels Möller's avatar
Niels Möller committed
299
	      int method,
300
	      struct lsh_user *keyholder UNUSED,
Niels Möller's avatar
Niels Möller committed
301
	      struct lsh_string *key)
Niels Möller's avatar
Niels Möller committed
302
{
303
  CAST(lsh_host_db, self, c);
304
  struct spki_subject *subject;
Niels Möller's avatar
Niels Möller committed
305

306
307
308
309
  switch (method)
    {
    case ATOM_SSH_DSS:
      {
310
	struct verifier *v = make_ssh_dss_verifier(key->length, key->data);
311
312
	if (!v)
	  {
313
	    werror("do_lsh_lookup: Invalid ssh-dss key.\n");
314
315
316
	    return NULL;
	  }
	subject = SPKI_LOOKUP(self->db,
317
318
			      spki_make_public_key(v),
			      v);
319
320
321
322
	assert(subject);
	assert(subject->verifier);
	break;
      }
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    case ATOM_SSH_RSA:
      {
	struct verifier *v = make_ssh_rsa_verifier(key->length, key->data);
	if (!v)
	  {
	    werror("do_lsh_lookup: Invalid ssh-rsa key.\n");
	    return NULL;
	  }
	subject = SPKI_LOOKUP(self->db,
			      spki_make_public_key(v),
			      v);
	assert(subject);
	assert(subject->verifier);
	break;
      }
      
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
#if 0
    case ATOM_RSA_PKCS1_SHA1_LOCAL:
      {
	struct rsa_verifier *v = make_ssh_rsa_verifier(key->length, key->data);
	if (!v)
	  {
	    werror("do_lsh_lookup: Invalid ssh-rsa key.\n");
	    return NULL;
	  }
	subject = SPKI_LOOKUP(self->db,
			      rsa_to_spki_public_key(&v->public),
			      &v->super);
	assert(subject);
	assert(subject->verifier);
	break;
      }
355
      
356
    case ATOM_SPKI:
357
358
359
360
#endif
      /* It doesn't matter here which flavour of SPKI is used. */
    case ATOM_SPKI_SIGN_RSA:
    case ATOM_SPKI_SIGN_DSS:
361
      {
362
	struct sexp *e = string_to_sexp(SEXP_CANONICAL, key, 0);
363
364
	if (!e)
	  {
365
	    werror("do_lsh_lookup: Invalid spki s-expression.\n");
366
367
368
369
370
371
	    return NULL;
	  }
	  
	subject = SPKI_LOOKUP(self->db, e, NULL);
	if (!subject)
	  {
372
	    werror("do_lsh_lookup: Invalid spki key.\n");
373
374
375
376
	    return NULL;
	  }
	if (!subject->verifier)
	  {
377
	    werror("do_lsh_lookup: Valid SPKI subject, but no key available.\n");
378
379
380
381
382
	    return NULL;
	  }
	break;
      }
    default:
383
      werror("do_lsh_lookup: Unknown key type. Should not happen!\n");
384
385
386
387
      return NULL;
    }

  assert(subject->key);
388
  
389
390
391
392
  /* Check authorization */

  if (SPKI_AUTHORIZE(self->db, subject, self->access))
    {
393
      verbose("SPKI host authorization successful!\n");
394
395
    }
  else
396
    {
397
398
      struct sexp *acl;
      
399
      verbose("SPKI authorization failed.\n");
400
      if (!self->sloppy)
401
	{
402
	  werror("Server's hostkey is not trusted. Disconnecting.\n");
403
404
	  return NULL;
	}
405
      
406
407
      /* Ok, let's see if we want to use this untrusted key. */
      if (!quiet_flag)
408
	{
409
410
411
412
413
414
	  /* Display fingerprint */
	  struct lsh_string *fingerprint
	    = hash_string(self->hash,
			  sexp_format(subject->key, SEXP_CANONICAL, 0),
			  1);
			  
415
416
417
418
419
420
	  if (!INTERACT_YES_OR_NO
	      (self->tty,
	       ssh_format("Received unauthenticated key for host %lS\n"
			  "Fingerprint: %lfxS\n"
			  "Do you trust this key? (y/n) ",
			  self->host->ip, fingerprint), 0, 1))
421
	    return NULL;
422
	}
423
424
425
426
427
428
429
430
431
432
433
434

      acl = sexp_l(2, sexp_a(ATOM_ACL),
		   sexp_l(3, sexp_a(ATOM_ENTRY),
			  subject->key,
			  sexp_l(2, sexp_a(ATOM_TAG),
				 self->access,
				 -1),
			  -1),
		   -1);

      /* Remember this key. We don't want to ask again for key re-exchange */
      spki_add_acl(self->db, acl);
435
      
Niels Möller's avatar
Niels Möller committed
436
      /* Write an ACL to disk. */
437
      if (self->file)
438
	{
Niels Möller's avatar
Niels Möller committed
439
440
	  A_WRITE(self->file, ssh_format("\n; ACL for host %lS\n", self->host->ip));
	  A_WRITE(self->file,
441
		  sexp_format(acl, SEXP_TRANSPORT, 0));
Niels Möller's avatar
Niels Möller committed
442
	  A_WRITE(self->file, ssh_format("\n"));
443
444
445
	}
    }
  
446
  return subject->verifier;
Niels Möller's avatar
Niels Möller committed
447
448
}

449
static struct lookup_verifier *
450
make_lsh_host_db(struct spki_context *db,
451
		 struct interact *tty,
Niels Möller's avatar
Niels Möller committed
452
453
454
		 struct address_info *host,
		 int sloppy,
		 struct abstract_write *file)
Niels Möller's avatar
Niels Möller committed
455
{
456
  NEW(lsh_host_db, res);
457

458
  res->super.lookup = do_lsh_lookup;
459
  res->db = db;
460
  res->tty = tty;
461
462
463
464
  res->access = make_ssh_hostkey_tag(host);
  res->host = host;
  res->sloppy = sloppy;
  res->file = file;
465
  res->hash = &sha1_algorithm;
Niels Möller's avatar
Niels Möller committed
466
467
468
469

  return &res->super;
}

470
471
472
473
474
475
476
477
478
/* Takes options and an spki_context as argument and returns a
 * lookup_verifier */

DEFINE_COMMAND2(lsh_verifier_command)
     (struct command_2 *s UNUSED,
      struct lsh_object *a1,
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
479
{
480
481
  CAST(lsh_options, options, a1);
  CAST_SUBTYPE(spki_context, db, a2);
482
  COMMAND_RETURN(c, make_lsh_host_db(db,
483
484
485
486
				     options->super.tty,
				     options->super.remote,
				     options->sloppy,
				     options->capture_file));
487
488
489
}


490
491
492
493
494
495
496
/* (login options public-keys connection) */
DEFINE_COMMAND2(lsh_login_command)
     (struct command_2 *s UNUSED,
      struct lsh_object *a1,
      struct lsh_object *a2,
      struct command_continuation *c,
      struct exception_handler *e UNUSED)
497
{
498
499
  CAST(lsh_options, options, a1);
  CAST_SUBTYPE(object_list, keys, a2);
500

501
  struct client_userauth_method *password
502
    = make_client_password_auth(options->super.tty);
503
504
505
506

  /* FIXME: Perhaps we should try "none" only when using SRP. */
  struct client_userauth_method *none
    = make_client_none_auth();
507
  
508
  COMMAND_RETURN(c,
509
		 make_client_userauth
510
		 (ssh_format("%lz", options->super.user),
511
512
513
		  ATOM_SSH_CONNECTION,
		  (LIST_LENGTH(keys)
		   ? make_object_list
514
515
		   (3,
                    none,
516
517
518
		    make_client_publickey_auth(keys),
		    password,
		    -1)
519
		   : make_object_list(2, none, password, -1))));
520
521
522
}


523
/* NOTE: options2identities can block for reading, so it must not be
524
525
526
527
 * invoked directly. */

/* Requests the ssh-userauth service, log in, and request connection
 * service. */
528
529
530
531
532
533
534
/* GABA:
   (expr
     (name make_lsh_userauth)
     (params
       (options object lsh_options))
     (expr
       (lambda (connection)
535
536
         (lsh_login options
	   
537
538
	   ; The prog1 delay is needed because options2identities
	   ; may not return immediately.
539
540
	   (options2identities (prog1 options connection))

541
	   ; Request the userauth service
542
543
	   (request_userauth_service connection))))) */

Niels Möller's avatar
Niels Möller committed
544
545
/* GABA:
   (expr
546
     (name make_lsh_connect)
547
548
     (params
       (connect object command)
549
       (handshake object handshake_info)
550
       (init object make_kexinit))
551
     (expr (lambda (options)
552
               ; What to do with the service
553
	       ((progn (options2actions options))
554
555
556
557
558
	         ; Initialize service
		 (init_connection_service
		   ; Either requests ssh-connection service,
		   ; or requests and uses the ssh-userauth service.
		   (options2service options
Niels Möller's avatar
Niels Möller committed
559
		     ; Start the ssh transport protocol
560
	             (connection_handshake
561
		       handshake init
562
563
		       (options2verifier options
				         (options2known_hosts options))
Niels Möller's avatar
Niels Möller committed
564
 		       ; Connect using tcp
565
		       (connect (options2remote options)))))))))
566
567
*/

568
569
570

/* Option parsing */

571
572
573
574
575
const char *argp_program_version
= "lsh-" VERSION ", secsh protocol version " CLIENT_PROTOCOL_VERSION;

const char *argp_program_bug_address = BUG_ADDRESS;

576
577
#define ARG_NOT 0x400

578
#define OPT_PUBLICKEY 0x201
579

580
581
582
583
#define OPT_SLOPPY 0x202
#define OPT_STRICT 0x203
#define OPT_CAPTURE 0x204

Niels Möller's avatar
Niels Möller committed
584
585
#define OPT_HOST_DB 0x205

586
587
588
#define OPT_DH 0x206
#define OPT_SRP 0x207

589
590
591
592
#define OPT_STDIN 0x210
#define OPT_STDOUT 0x211
#define OPT_STDERR 0x212

593
594
#define OPT_FORK_STDIO 0x213

595
596
597
598
static const struct argp_option
main_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
Balázs Scheidler's avatar
Balázs Scheidler committed
599
  { "identity", 'i',  "Identity key", 0, "Use this key to authenticate.", 0 },
600
601
602
  { "publickey", OPT_PUBLICKEY, NULL, 0,
    "Try publickey user authentication (default).", 0 },
  { "no-publickey", OPT_PUBLICKEY | ARG_NOT, NULL, 0,
603
    "Don't try publickey user authentication.", 0 },
Niels Möller's avatar
Niels Möller committed
604
  { "host-db", OPT_HOST_DB, "Filename", 0, "By default, ~/.lsh/known_hosts", 0},
605
606
607
608
609
610
611
  { "sloppy-host-authentication", OPT_SLOPPY, NULL, 0,
    "Allow untrusted hostkeys.", 0 },
  { "strict-host-authentication", OPT_STRICT, NULL, 0,
    "Never, never, ever trust an unknown hostkey. (default)", 0 },
  { "capture-to", OPT_CAPTURE, "File", 0,
    "When a new hostkey is received, append an ACL expressing trust in the key. "
    "In sloppy mode, the default is ~/.lsh/captured_keys.", 0 },
612
613
614
615
616
#if WITH_SRP
  { "srp-keyexchange", OPT_SRP, NULL, 0, "Enable experimental SRP support.", 0 },
  { "no-srp-keyexchange", OPT_SRP | ARG_NOT, NULL, 0, "Disable experimental SRP support (default).", 0 },
#endif /* WITH_SRP */

617
618
619
  { "dh-keyexchange", OPT_DH, NULL, 0,
    "Enable DH support (default, unless SRP is being used).", 0 },

620
621
  { "no-dh-keyexchange", OPT_DH | ARG_NOT, NULL, 0, "Disable DH support.", 0 },

622
#if 0
623
624
625
626
  { "userauth", OPT_USERAUTH, NULL, 0,
    "Request the ssh-userauth service (default, unless SRP is being used).", 0 },
  { "no-userauth", OPT_USERAUTH | ARG_NOT, NULL, 0,
    "Request the ssh-userauth service (default if SRP is used).", 0 },
627
628
#endif
  
629
630
  /* ACtions */
  { "forward-remote-port", 'R', "remote-port:target-host:target-port",
631
    0, "", CLIENT_ARGP_ACTION_GROUP },
632
  { "gateway", 'G', NULL, 0, "Setup a local gateway", 0 },
633
634
635
636
637
638
639
  { NULL, 0, NULL, 0, NULL, 0 }
};


static const struct argp_child
main_argp_children[] =
{
640
  { &client_argp, 0, "", 0 },
641
  { &algorithms_argp, 0, "", 0 },
642
643
644
645
  { &werror_argp, 0, "", 0 },
  { NULL, 0, NULL, 0}
};

646
647
#define CASE_ARG(opt, attr, none)		\
  case opt:					\
648
    if (self->super.not)			\
649
      {						\
650
        self->super.not = 0;			\
651
652
653
654
655
656
657
658
659
						\
      case opt | ARG_NOT:			\
        self->attr = none;			\
        break;					\
      }						\
      						\
    self->attr = arg;				\
    break

660
661
#define CASE_FLAG(opt, flag)			\
  case opt:					\
662
    if (self->super.not)			\
663
      {						\
664
        self->super.not = 0;			\
665
666
667
668
669
670
						\
      case opt | ARG_NOT:			\
        self->flag = 0;				\
        break;					\
      }						\
      						\
671
672
    self->flag = 1;				\
    break
673

674
675
676
677
678
679
680
681
682
683
static error_t
main_argp_parser(int key, char *arg, struct argp_state *state)
{
  CAST(lsh_options, self, state->input);
  
  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;
    case ARGP_KEY_INIT:
684
      state->child_inputs[0] = &self->super;
685
686
      state->child_inputs[1] = self->algorithms;
      state->child_inputs[2] = NULL;
687
      break;
688
      
689
    case ARGP_KEY_END:
690
691
692
693
694
      if (!self->home)
	{
	  argp_error(state, "No home directory. Please set HOME in the environment.");
	  break;
	}
695

696
697
698
699
700
701
702
703
704
705
706
707
708
      if (self->with_dh_keyexchange < 0)
	self->with_dh_keyexchange = !self->with_srp_keyexchange;
      
      if (self->with_dh_keyexchange || self->with_srp_keyexchange)
	{
	  int i = 0;
	  self->kex_algorithms 
	    = alloc_int_list(self->with_dh_keyexchange + self->with_srp_keyexchange);
	    
#if WITH_SRP	    
	  if (self->with_srp_keyexchange)
	    {
	      LIST(self->kex_algorithms)[i++] = ATOM_SRP_RING1_SHA1_LOCAL;
709
	      ALIST_SET(self->algorithms->algorithms,
710
			ATOM_SRP_RING1_SHA1_LOCAL,
711
712
713
714
			&make_srp_client(make_srp1(&self->random->super),
					 self->super.tty,
					 ssh_format("%lz", self->super.user))
			->super);
715
716
717
718
719
	    }
#endif /* WITH_SRP */
	  if (self->with_dh_keyexchange)
	    {
	      LIST(self->kex_algorithms)[i++] = ATOM_DIFFIE_HELLMAN_GROUP1_SHA1;
720
	      ALIST_SET(self->algorithms->algorithms,
721
			ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
722
723
			&make_dh_client(make_dh1(&self->random->super))
			->super);
724
725
726
727
728
	    }
	}
      else
	argp_error(state, "All keyexchange algorithms disabled.");

729
      {
730
731
        CAST_SUBTYPE(command, o, make_lsh_userauth(self));
        self->service = o;
732
733
734
735
736
737
738
739
740
      }
	
      {
	struct lsh_string *tmp = NULL;
	const char *s = NULL;
	  
	if (self->capture)
	  s = self->capture;
	else if (self->sloppy)
741
	  {
742
743
	    tmp = ssh_format("%lz/.lsh/captured_keys", self->home);
	    s = lsh_get_cstring(tmp);
744
	  }
745
	if (s)
746
	  {
747
	    struct lsh_fd *f
748
	      = io_write_file(self->super.backend, s,
749
750
751
752
753
754
755
756
757
758
			      O_CREAT | O_APPEND | O_WRONLY,
			      0600, 500, NULL,
			      make_report_exception_handler
			      (make_report_exception_info(EXC_IO, EXC_IO,
							  "Writing new ACL: "),
			       &default_exception_handler,
			       HANDLER_CONTEXT));
	    if (f)
	      self->capture_file = &f->write_buffer->super;
	    else
759
	      {
760
761
		werror("Failed to open '%z' (errno = %i): %z.\n",
		       s, errno, STRERROR(errno));
762
763
	      }
	  }
764
765
	lsh_string_free(tmp);
      }
766

767
768
769
      /* We can't add the gateway action immediately when the -G
       * option is encountered, as we need the name and port of the
       * remote machine (self->super.remote) first. */
770
      if (self->start_gateway)
771
	{
772
	  struct local_info *gateway;
773
	  if (!self->super.local_user)
774
	    {
775
776
777
	      argp_error(state, "You have to set LOGNAME in the environment, "
			 " if you want to use the gateway feature.");
	      break;
778
	    }
779
780
781
	  gateway = make_gateway_address(self->super.local_user,
					 self->super.user,
					 self->super.remote);
782

783
	  if (!gateway)
784
	    {
785
786
	      argp_error(state, "Local or remote user name, or the target host name, are too "
			 "strange for the gateway socket name construction.");
787
	      break;
788
	    }
789
	      
790
791
792
	  client_add_action(&self->super,
			    make_gateway_setup
			    (make_listen_local(self->super.backend, gateway)));
793
	}
794

795
796
797
798
799
800
      if (object_queue_is_empty(&self->super.actions))
	{
	  argp_error(state, "No actions given.");
	  break;
	}
      
801
802
803
      /* Start background poll */
      RANDOM_POLL_BACKGROUND(self->random->poller);
      
804
      break;
805
      
Balázs Scheidler's avatar
Balázs Scheidler committed
806
    case 'i':
807
      self->identity = arg;
808
      break;
Niels Möller's avatar
Niels Möller committed
809

810
    CASE_FLAG(OPT_PUBLICKEY, with_publickey);
811

Niels Möller's avatar
Niels Möller committed
812
    case OPT_HOST_DB:
813
      self->known_hosts = arg;
Niels Möller's avatar
Niels Möller committed
814
815
      break;
      
816
817
818
819
820
821
822
823
824
825
826
    case OPT_SLOPPY:
      self->sloppy = 1;
      break;

    case OPT_STRICT:
      self->sloppy = 0;
      break;

    case OPT_CAPTURE:
      self->capture = arg;
      break;
827

828
829
    CASE_FLAG(OPT_DH, with_dh_keyexchange);
    CASE_FLAG(OPT_SRP, with_srp_keyexchange);
830

831
832
833
834
835
    case 'R':
      {
	UINT32 listen_port;
	struct address_info *target;

836
	if (!client_parse_forward_arg(arg, &listen_port, &target))
837
838
	  argp_error(state, "Invalid forward specification '%s'.", arg);

839
840
841
842
843
844
845
	client_add_action(&self->super, make_forward_remote_port
			  (self->super.backend,
			   make_address_info((self->super.with_remote_peers
					      ? ssh_format("%lz", "0.0.0.0")
					      : ssh_format("%lz", "127.0.0.1")),
					     listen_port),
			   target));
846
847

	self->super.remote_forward = 1;
848
849
	break;
      }      
850
851
      
    CASE_FLAG('G', start_gateway);
852
    }
853

854
855
856
857
858
859
860
861
862
863
864
865
866
867
  return 0;
}

static const struct argp
main_argp =
{ main_options, main_argp_parser, 
  ( "host\n"
    "host command ..."), 
  ( "Connects to a remote machine\v"
    "Connects to the remote machine, and then performs one or more actions, "
    "i.e. command execution, various forwarding services. The default "
    "action is to start a remote interactive shell or execute a given "
    "command on the remote machine." ),
  main_argp_children,
868
  NULL, NULL
869
870
};

Niels Möller's avatar
Niels Möller committed
871
872
873
874
875
876
877
878
/* GABA:
   (class
     (name lsh_default_handler)
     (super exception_handler)
     (vars
       (status . "int *")))
*/

Niels Möller's avatar
Niels Möller committed
879
880
881
static void
do_lsh_default_handler(struct exception_handler *s,
		       const struct exception *e)
Niels Möller's avatar
Niels Möller committed
882
883
884
{
  CAST(lsh_default_handler, self, s);

885
  if (e->type & EXC_IO)
Niels Möller's avatar
Niels Möller committed
886
    {
887
      CAST_SUBTYPE(io_exception, exc, e);
Niels Möller's avatar
Niels Möller committed
888
889
      *self->status = EXIT_FAILURE;
      
890
      werror("%z, (errno = %i)\n", e->msg, exc->error);
Niels Möller's avatar
Niels Möller committed
891
    }
892
893
894
895
  else
    switch(e->type)
      {
      case EXC_RESOLVE:
896
      case EXC_USERAUTH:
897
898
      case EXC_SEXP_SYNTAX:
      case EXC_SPKI_TYPE:
899
      case EXC_GLOBAL_REQUEST:
900
      case EXC_CHANNEL_REQUEST:
901
      case EXC_CHANNEL_OPEN:
Niels Möller's avatar
Niels Möller committed
902

903
	werror("%z\n", e->msg);
904
905
906
907
908
909
	*self->status = EXIT_FAILURE;
	break;
      default:
	*self->status = EXIT_FAILURE;
	EXCEPTION_RAISE(self->super.parent, e);
      }
Niels Möller's avatar
Niels Möller committed
910
911
912
}

static struct exception_handler *
913
914
make_lsh_default_handler(int *status, struct exception_handler *parent,
			 const char *context)
Niels Möller's avatar
Niels Möller committed
915
916
917
{
  NEW(lsh_default_handler, self);
  self->super.parent = parent;
Niels Möller's avatar
Niels Möller committed
918
  self->super.raise = do_lsh_default_handler;
919
920
  self->super.context = context;
  
Niels Möller's avatar
Niels Möller committed
921
922
923
924
925
  self->status = status;

  return &self->super;
}

926

Niels Möller's avatar
Niels Möller committed
927
928
int main(int argc, char **argv)
{
929
  struct lsh_options *options;
930
931
932

  /* Default exit code if something goes wrong. */
  int lsh_exit_code = 17;
933

Niels Möller's avatar
Niels Möller committed
934
  struct exception_handler *handler
935
936
    = make_lsh_default_handler(&lsh_exit_code, &default_exception_handler,
			       HANDLER_CONTEXT);
Niels Möller's avatar
Niels Möller committed
937

938
  struct io_backend *backend = make_io_backend();
939
  
Niels Möller's avatar
Niels Möller committed
940
941
942
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
943

944
945
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
946

947
  options = make_options(backend, handler, &lsh_exit_code);
948
949

  argp_parse(&main_argp, argc, argv, ARGP_IN_ORDER, NULL, options);
950
  
951
952
  {
    struct lsh_object *o =
Niels Möller's avatar
Niels Möller committed
953
954
955
      make_lsh_connect(
	make_simple_connect(backend, NULL),
	make_handshake_info(CONNECTION_CLIENT,
956
			    "lsh - a free ssh", NULL,
Niels Möller's avatar
Niels Möller committed
957
			    SSH_MAX_PACKET,
958
			    &options->random->super,
959
			    options->algorithms->algorithms,
Niels Möller's avatar
Niels Möller committed
960
			    NULL),
961
	make_simple_kexinit(&options->random->super,
962
			    options->kex_algorithms,
963
964
965
966
967
			    options->algorithms->hostkey_algorithms,
			    options->algorithms->crypto_algorithms,
			    options->algorithms->mac_algorithms,
			    options->algorithms->compression_algorithms,
			    make_int_list(0, -1)));
968
    
969
    CAST_SUBTYPE(command, lsh_connect, o);
970

971
    COMMAND_CALL(lsh_connect, options, &discard_continuation,
972
		 handler);
Niels Möller's avatar
Niels Möller committed
973
	
974
  } 
975

976
977
978
979
980
981
#if 0
  /* All commands using stdout have dup:ed stdout by now. We close it,
   * because if stdout is a pipe, we want the reader to know whether
   * or not anybody is still using it. */
  close(STDOUT_FILENO);
  if (open("/dev/null", O_WRONLY) != STDOUT_FILENO)
982
    werror("Strange: Final redirect of stdout to /dev/null failed.\n");
983
984
#endif
  
985
  io_run(backend);
986
987
988

  /* Close all files and other resources associated with the backend. */
  io_final(backend);
989
  
990
991
  gc_final();

992
993
  /* FIXME: Perhaps we have to reset the stdio file descriptors to
   * blocking mode? */
Niels Möller's avatar
Niels Möller committed
994
  return lsh_exit_code;
Niels Möller's avatar
Niels Möller committed
995
}