lsh.c 28.9 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"
Niels Möller's avatar
Niels Möller committed
37
#include "format.h"
38
#include "interact.h"
Niels Möller's avatar
Niels Möller committed
39
#include "io.h"
40
#include "io_commands.h"
41
42
#include "gateway.h"
#include "gateway_commands.h"
43
#include "handshake.h"
44
#include "lookup_verifier.h"
45
#include "publickey_crypto.h"
Niels Möller's avatar
Niels Möller committed
46
#include "randomness.h"
47
#include "sexp.h"
Niels Möller's avatar
Niels Möller committed
48
#include "spki.h"
49
#include "srp.h" 
50
#include "ssh.h"
51
#include "tcpforward_commands.h"
52
#include "version.h"
Niels Möller's avatar
Niels Möller committed
53
#include "werror.h"
Niels Möller's avatar
Niels Möller committed
54
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
55

56
57
#include "nettle/sexp.h"

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

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

69
#include "lsh_argp.h"
70

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

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

78
79
struct command options2service;
#define OPTIONS2SERVICE (&options2service.super)
80

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

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


89
90
#include "lsh.c.x"

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

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

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

106
107
108
109
       (signature_algorithms object alist)
       (home . "const char *")
       
       (identity . "char *")
110
111
112
113
       (with_publickey . int)

       (with_srp_keyexchange . int)
       (with_dh_keyexchange . int)
114

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

       (known_hosts . "const char *")
       
127
       (start_gateway . int)))
128
129
130
131
*/


static struct lsh_options *
132
make_options(struct exception_handler *handler,
133
134
135
	     int *exit_code)
{
  NEW(lsh_options, self);
136
137
138
  const char *home = getenv("HOME");
  struct randomness *r = make_user_random(home);

139
  init_client_options(&self->super, 
140
		      r,
141
		      handler, exit_code);
142

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

146
147
  /* OK to init with NULL */  
  self->signature_algorithms = all_signature_algorithms(r);
148

149
150
151
152
153
  self->sloppy = 0;
  self->capture = NULL;
  self->capture_file = NULL;

  self->known_hosts = NULL;
154
  
155
  self->start_gateway = 0;
156

157
158
159
  self->with_publickey = 1;

  self->with_srp_keyexchange = 0;
160
161
162
163

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

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

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

Niels Möller's avatar
Niels Möller committed
182
183
/* Open hostkey database. By default, "~/.lsh/known_hosts". */

184
185
static struct spki_context *
read_known_hosts(struct lsh_options *options)
Niels Möller's avatar
Niels Möller committed
186
187
{
  struct lsh_string *tmp = NULL;
188
  const char *s;
189
190
  struct lsh_string *contents;
  int fd;
191
  struct sexp_iterator i;
192
  struct spki_context *context;
193
194
195
  const char *sexp_conv = getenv("SEXP_CONV");
  const char *args[] = { "sexp-conv", "-s", "canonical", NULL };
  
196
  context = make_spki_context(options->signature_algorithms);
Niels Möller's avatar
Niels Möller committed
197
198
  
  if (options->known_hosts)
199
200
201
202
    {
      s = options->known_hosts;
      fd = open(s, O_RDONLY);
    }
203
  else
Niels Möller's avatar
Niels Möller committed
204
    {
205
      tmp = ssh_format("%lz/.lsh/host-acls", options->home);
206
      s = lsh_get_cstring(tmp);
207
208
209
210
211
212
213
214
215
      fd = open(s, O_RDONLY);
      
      if (fd < 0)
	{
	  lsh_string_free(tmp);
	  tmp = ssh_format("%lz/.lsh/known_hosts", options->home);
	  s = lsh_get_cstring(tmp);
	  fd = open(lsh_get_cstring(tmp), O_RDONLY);
	}
Niels Möller's avatar
Niels Möller committed
216
217
    }

218
  if (fd < 0)
Niels Möller's avatar
Niels Möller committed
219
    {
220
      werror("Failed to open `%z' for reading %e\n", s, errno);
221
      lsh_string_free(tmp);
222
      return context;
Niels Möller's avatar
Niels Möller committed
223
    }
224
225
226

  lsh_string_free(tmp);

227
228
229
230
  if (!sexp_conv)
    sexp_conv = PREFIX "/bin/sexp-conv";
  
  contents = lsh_popen_read(sexp_conv, args, fd, 5000);
231
232
233
  
  if (!contents)
  {
234
    werror("Failed to read known_hosts file %e\n", errno);
235
236
237
238
239
240
    close(fd);
    return context;
  }

  close(fd);

241
242
243
  /* We could use transport syntax instead. That would have the
   * advantage that we can read and process one entry at a time. */
  if (!sexp_iterator_first(&i, contents->length, contents->data))
244
245
246
247
248
249
250
251
252
253
254
    werror("read_known_hosts: S-expression syntax error.\n");
    
  else
    while (i.type != SEXP_END)
      {
	if (!spki_add_acl(context, &i))
	  {
	    werror("read_known_hosts: Invalid ACL.\n");
	    break;
	  }
      }
255
256
  lsh_string_free(contents);
  return context;
Niels Möller's avatar
Niels Möller committed
257
258
}

259
260
261
/* For now, supports only a single key */
static struct object_list *
read_user_keys(struct lsh_options *options)
Niels Möller's avatar
Niels Möller committed
262
263
{
  struct lsh_string *tmp = NULL;
264
265
266
267
268
269
270
271
  struct lsh_string *contents;
  const char *name = NULL;
  int fd;
  int algorithm_name;
  
  struct signer *s;
  struct verifier *v;
  struct lsh_string *spki_public;
Niels Möller's avatar
Niels Möller committed
272

273
  trace("read-user_keys\n");
274
  
275
  if (!options->with_publickey)
276
277
    return make_object_list(0, -1);

Niels Möller's avatar
Niels Möller committed
278
  if (options->identity)
279
    name = options->identity;
Niels Möller's avatar
Niels Möller committed
280
281
  else 
    {
282
      tmp = ssh_format("%lz/.lsh/identity", options->home);
283
      name = lsh_get_cstring(tmp);
Niels Möller's avatar
Niels Möller committed
284
    }
285
286
287

  fd = open(name, O_RDONLY);
  if (fd < 0)
Niels Möller's avatar
Niels Möller committed
288
    {
289
      werror("Failed to open `%z' for reading %e\n", name, errno);
290
291
292
      lsh_string_free(tmp);

      return make_object_list(0, -1);
Niels Möller's avatar
Niels Möller committed
293
    }
294
295
296
297
298
299

  lsh_string_free(tmp);

  contents = io_read_file_raw(fd, 2000);

  if (!contents)
300
    {
301
      werror("Failed to read private key file %e\n", errno);
302
303
304
      close(fd);

      return make_object_list(0, -1);
305
    }
306
307
308
309
310
311
312
313
314
315
316

  close(fd);

  if (options->super.tty)
    {
      struct alist *mac = make_alist(0, -1);
      struct alist *crypto = make_alist(0, -1);

      alist_select_l(mac, options->algorithms->algorithms,
		     2, ATOM_HMAC_SHA1, ATOM_HMAC_MD5, -1);
      alist_select_l(crypto, options->algorithms->algorithms,
317
318
		     4, ATOM_3DES_CBC, ATOM_BLOWFISH_CBC,
		     ATOM_TWOFISH_CBC, ATOM_AES256_CBC, -1);
319
      
320
321
322
323
      contents = spki_pkcs5_decrypt(mac, crypto,
				    options->super.tty,
				    contents);
      if (!contents)
324
325
326
327
328
329
        {
          werror("Decrypting private key failed.\n");
          return make_object_list(0, -1);
        }
    }
  
330
331
332
333
334
  s = spki_make_signer(options->signature_algorithms, contents,
		       &algorithm_name);

  lsh_string_free(contents);
  
335
336
337
338
339
340
341
342
343
  if (!s)
    {
      werror("Invalid private key.\n");
      return make_object_list(0, -1);
    }
  
  v = SIGNER_GET_VERIFIER(s);
  assert(v);
  
344
  spki_public = PUBLIC_SPKI_KEY(v, 0);
345
346
347
348
349
350
351
352
353
354
355
356
357
358

  /* Test key here? */  
  switch (algorithm_name)
    {	  
    case ATOM_DSA:
      return make_object_list(2,
                              make_keypair(ATOM_SSH_DSS,
                                           PUBLIC_KEY(v),
                                           s),
                              make_keypair(ATOM_SPKI_SIGN_DSS,
                                           spki_public, s),
                              -1);
      break;

359
    case ATOM_RSA_PKCS1:
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    case ATOM_RSA_PKCS1_SHA1:
      return make_object_list(2, 
                              make_keypair(ATOM_SSH_RSA,
                                           PUBLIC_KEY(v),
                                           s),
                              make_keypair(ATOM_SPKI_SIGN_RSA,
                                           spki_public, s),
                              -1);

    case ATOM_RSA_PKCS1_MD5:
      return make_object_list(1,
                              make_keypair(ATOM_SPKI_SIGN_RSA,
                                           spki_public, s),
                              -1);

    default:
      fatal("Internal error!\n");
    }
}

Niels Möller's avatar
Niels Möller committed
380
381
/* FIXME: Call read_user_key directly from main instead. */

382
383
384
385
386
387
388
389
390
391
/* 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 UNUSED)
{
  CAST(lsh_options, options, a);

  COMMAND_RETURN(c, read_user_keys(options));
Niels Möller's avatar
Niels Möller committed
392
393
394
395
396
397
}

static struct command options2identities =
STATIC_COMMAND(do_options2identities);


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

400
/* GABA:
401
   (class
402
     (name lsh_host_db)
403
404
     (super lookup_verifier)
     (vars
405
       (db object spki_context)
406
       (tty object interact)
407
       (access string)
408
409
410
411
412
       (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)
413
       ; For fingerprinting
414
       (hash const object hash_algorithm)))
415
416
*/

417
static struct verifier *
418
do_lsh_lookup(struct lookup_verifier *c,
Niels Möller's avatar
Niels Möller committed
419
	      int method,
420
	      struct lsh_user *keyholder UNUSED,
Niels Möller's avatar
Niels Möller committed
421
	      struct lsh_string *key)
Niels Möller's avatar
Niels Möller committed
422
{
423
  CAST(lsh_host_db, self, c);
424
  struct spki_principal *subject;
Niels Möller's avatar
Niels Möller committed
425

426
427
428
  switch (method)
    {
    case ATOM_SSH_DSS:
429
430
      {	
	struct lsh_string *spki_key;
431
	struct verifier *v = make_ssh_dss_verifier(key->length, key->data);
432

433
434
	if (!v)
	  {
435
	    werror("do_lsh_lookup: Invalid ssh-dss key.\n");
436
437
	    return NULL;
	  }
438
439
440

	/* FIXME: It seems like a waste to pick apart the sexp again */
	spki_key = PUBLIC_SPKI_KEY(v, 0);
441
442

	subject = spki_lookup(self->db, spki_key->length, spki_key->data, v);
443
444
	assert(subject);
	assert(subject->verifier);
445
446

	lsh_string_free(spki_key);
447
448
	break;
      }
449
450
    case ATOM_SSH_RSA:
      {
451
	struct lsh_string *spki_key;
452
	struct verifier *v = make_ssh_rsa_verifier(key->length, key->data);
453

454
455
456
457
458
	if (!v)
	  {
	    werror("do_lsh_lookup: Invalid ssh-rsa key.\n");
	    return NULL;
	  }
459
460
461

	/* FIXME: It seems like a waste to pick apart the sexp again */
	spki_key = PUBLIC_SPKI_KEY(v, 0);
462
	subject = spki_lookup(self->db, spki_key->length, spki_key->data, v);
463
464
	assert(subject);
	assert(subject->verifier);
465
466

	lsh_string_free(spki_key);
467
468
469
	break;
      }
      
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
#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;
      }
486
      
487
    case ATOM_SPKI:
488
489
490
491
#endif
      /* It doesn't matter here which flavour of SPKI is used. */
    case ATOM_SPKI_SIGN_RSA:
    case ATOM_SPKI_SIGN_DSS:
492
      {
493
	subject = spki_lookup(self->db, key->length, key->data, NULL);
494
495
	if (!subject)
	  {
496
	    werror("do_lsh_lookup: Invalid spki key.\n");
497
498
499
500
	    return NULL;
	  }
	if (!subject->verifier)
	  {
501
	    werror("do_lsh_lookup: Valid SPKI subject, but no key available.\n");
502
503
504
505
506
	    return NULL;
	  }
	break;
      }
    default:
507
      werror("do_lsh_lookup: Unknown key type. Should not happen!\n");
508
509
510
511
      return NULL;
    }

  assert(subject->key);
512
  
513
514
  /* Check authorization */

515
  if (spki_authorize(self->db, subject, time(NULL), self->access))
516
    {
517
      verbose("SPKI host authorization successful!\n");
518
519
    }
  else
520
    {
521
522
      struct lsh_string *acl;
      struct sexp_iterator i;
523
      
524
      verbose("SPKI authorization failed.\n");
525
      if (!self->sloppy)
526
	{
527
	  werror("Server's hostkey is not trusted. Disconnecting.\n");
528
529
	  return NULL;
	}
530
      
531
532
      /* Ok, let's see if we want to use this untrusted key. */
      if (!quiet_flag)
533
	{
534
	  /* Display fingerprint */
535
536
	  /* FIXME: Rewrite to use libspki subject */
#if 0
537
	  struct lsh_string *spki_fingerprint = 
538
	    hash_string(self->hash, subject->key, 0);
539
540
#endif
	  
541
542
543
544
545
546
547
548
549
550
551
	  struct lsh_string *fingerprint = 
	    lsh_string_colonize( 
				ssh_format( "%lfxS", 
					    hash_string(&crypto_md5_algorithm,
							key,
							0)
					    ), 
				2, 
				1  
				);

552
553
554
555
556
557
558
559
	  struct lsh_string *babble = 
	    lsh_string_bubblebabble( 
				    hash_string(&crypto_sha1_algorithm,
						key,
						0),
				    1 
				    );
	  
560
561
562
	  if (!INTERACT_YES_OR_NO
	      (self->tty,
	       ssh_format("Received unauthenticated key for host %lS\n"
563
564
565
			  "Key details:\n"
			  "Bubble Babble: %lfS\n"
			  "Fingerprint:   %lfS\n"
566
			  /* "SPKI SHA1:     %lfxS\n" */
567
			  "Do you trust this key? (y/n) ",
568
			  self->host->ip, babble, fingerprint /* , spki_fingerprint */), 0, 1))
569
	    return NULL;
570
	}
571

572
      acl = lsh_sexp_format(0, "(%0s(%0s%l(%0s%s)))",
573
			    "acl", "entry",
574
			    subject->key_length, subject->key,
575
			    "tag", self->access->length, self->access->data);
576
      
577
578
579
580
581
582
583
      /* FIXME: Seems awkward to pick the acl apart again. */
      if (!sexp_iterator_first(&i, acl->length, acl->data))
	fatal("Internal error.\n");
      
      /* Remember this key. We don't want to ask again for key re-exchange */
      spki_add_acl(self->db, &i);

Niels Möller's avatar
Niels Möller committed
584
      /* Write an ACL to disk. */
585
      if (self->file)
586
	{
Niels Möller's avatar
Niels Möller committed
587
588
	  A_WRITE(self->file, ssh_format("\n; ACL for host %lS\n", self->host->ip));
	  A_WRITE(self->file,
589
590
591
		  lsh_sexp_format(1, "%l", acl->length, acl->data));
	  lsh_string_free(acl);
	  
Niels Möller's avatar
Niels Möller committed
592
	  A_WRITE(self->file, ssh_format("\n"));
593
594
595
	}
    }
  
596
  return subject->verifier;
Niels Möller's avatar
Niels Möller committed
597
598
}

599
static struct lookup_verifier *
600
make_lsh_host_db(struct spki_context *db,
601
		 struct interact *tty,
Niels Möller's avatar
Niels Möller committed
602
603
604
		 struct address_info *host,
		 int sloppy,
		 struct abstract_write *file)
Niels Möller's avatar
Niels Möller committed
605
{
606
  NEW(lsh_host_db, res);
607

608
  res->super.lookup = do_lsh_lookup;
609
  res->db = db;
610
  res->tty = tty;
611
612
613
614
  res->access = make_ssh_hostkey_tag(host);
  res->host = host;
  res->sloppy = sloppy;
  res->file = file;
615
  res->hash = &crypto_sha1_algorithm;
Niels Möller's avatar
Niels Möller committed
616
617
618
619

  return &res->super;
}

620
621
622
623
624
625
626
627
628
/* 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)
629
{
630
631
  CAST(lsh_options, options, a1);
  CAST_SUBTYPE(spki_context, db, a2);
632
  COMMAND_RETURN(c, make_lsh_host_db(db,
633
634
635
636
				     options->super.tty,
				     options->super.remote,
				     options->sloppy,
				     options->capture_file));
637
638
639
}


640
641
642
643
644
645
646
/* (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)
647
{
648
649
  CAST(lsh_options, options, a1);
  CAST_SUBTYPE(object_list, keys, a2);
650

651
  struct client_userauth_method *password
652
    = make_client_password_auth(options->super.tty);
653
654
655
656

  /* FIXME: Perhaps we should try "none" only when using SRP. */
  struct client_userauth_method *none
    = make_client_none_auth();
657
  
658
  COMMAND_RETURN(c,
659
		 make_client_userauth
660
		 (ssh_format("%lz", options->super.user),
661
662
663
		  ATOM_SSH_CONNECTION,
		  (LIST_LENGTH(keys)
		   ? make_object_list
664
665
		   (3,
                    none,
666
667
668
		    make_client_publickey_auth(keys),
		    password,
		    -1)
669
		   : make_object_list(2, none, password, -1))));
670
671
672
}


673
/* NOTE: options2identities can block for reading, so it must not be
674
675
676
677
 * invoked directly. */

/* Requests the ssh-userauth service, log in, and request connection
 * service. */
678
679
680
681
682
683
684
/* GABA:
   (expr
     (name make_lsh_userauth)
     (params
       (options object lsh_options))
     (expr
       (lambda (connection)
685
686
         (lsh_login options
	   
687
688
	   ; The prog1 delay is needed because options2identities
	   ; may not return immediately.
689
690
	   (options2identities (prog1 options connection))

691
	   ; Request the userauth service
692
693
	   (request_userauth_service connection))))) */

Niels Möller's avatar
Niels Möller committed
694
695
/* GABA:
   (expr
696
     (name make_lsh_connect)
697
     (params
698
       (handshake object handshake_info)
699
700
       (init object make_kexinit)
       (db object lookup_verifier))
701
     (expr (lambda (options)
702
               ; What to do with the service
703
	       ((progn (options2actions options))
704
705
706
707
708
	         ; 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
709
		     ; Start the ssh transport protocol
710
	             (connection_handshake
711
		       handshake init
712
		       db
Niels Möller's avatar
Niels Möller committed
713
 		       ; Connect using tcp
Niels Möller's avatar
Niels Möller committed
714
		       (connect_simple (options2remote options)))))))))
715
716
*/

717
718
719

/* Option parsing */

720
721
722
723
724
const char *argp_program_version
= "lsh-" VERSION ", secsh protocol version " CLIENT_PROTOCOL_VERSION;

const char *argp_program_bug_address = BUG_ADDRESS;

725
726
#define ARG_NOT 0x400

727
#define OPT_PUBLICKEY 0x201
728

729
730
731
732
#define OPT_SLOPPY 0x202
#define OPT_STRICT 0x203
#define OPT_CAPTURE 0x204

Niels Möller's avatar
Niels Möller committed
733
734
#define OPT_HOST_DB 0x205

735
736
737
#define OPT_DH 0x206
#define OPT_SRP 0x207

738
739
740
741
#define OPT_STDIN 0x210
#define OPT_STDOUT 0x211
#define OPT_STDERR 0x212

742
743
#define OPT_FORK_STDIO 0x213

744
745
746
747
static const struct argp_option
main_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
Balázs Scheidler's avatar
Balázs Scheidler committed
748
  { "identity", 'i',  "Identity key", 0, "Use this key to authenticate.", 0 },
749
750
751
  { "publickey", OPT_PUBLICKEY, NULL, 0,
    "Try publickey user authentication (default).", 0 },
  { "no-publickey", OPT_PUBLICKEY | ARG_NOT, NULL, 0,
752
    "Don't try publickey user authentication.", 0 },
Niels Möller's avatar
Niels Möller committed
753
  { "host-db", OPT_HOST_DB, "Filename", 0, "By default, ~/.lsh/known_hosts", 0},
754
755
756
757
758
759
760
  { "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 },
761
762
763
764
765
#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 */

766
767
768
  { "dh-keyexchange", OPT_DH, NULL, 0,
    "Enable DH support (default, unless SRP is being used).", 0 },

769
  { "no-dh-keyexchange", OPT_DH | ARG_NOT, NULL, 0, "Disable DH support.", 0 },
770
  
Niels Möller's avatar
Niels Möller committed
771
  /* Actions */
772
  { "forward-remote-port", 'R', "remote-port:target-host:target-port",
773
    0, "", CLIENT_ARGP_ACTION_GROUP },
774
  { "gateway", 'G', NULL, 0, "Setup a local gateway", 0 },
775

776
#if WITH_X11_FORWARD
Niels Möller's avatar
Niels Möller committed
777
778
779
780
  /* FIXME: Perhaps this should be moved from lsh.c to client.c? It
   * doesn't work with lshg. Or perhaps that can be fixed?
   * About the same problem applies to -R. */
  
781
782
783
  { "x11-forward", 'x', NULL, 0, "Enable X11 forwarding.", CLIENT_ARGP_MODIFIER_GROUP },
  { "no-x11-forward", 'x' | ARG_NOT, NULL, 0,
    "Disable X11 forwarding (default).", 0 },
784
#endif
785
  
786
787
788
789
790
791
792
  { NULL, 0, NULL, 0, NULL, 0 }
};


static const struct argp_child
main_argp_children[] =
{
793
  { &client_argp, 0, "", 0 },
794
  { &algorithms_argp, 0, "", 0 },
795
796
797
798
  { &werror_argp, 0, "", 0 },
  { NULL, 0, NULL, 0}
};

799
800
#define CASE_ARG(opt, attr, none)		\
  case opt:					\
801
    if (self->super.not)			\
802
      {						\
803
        self->super.not = 0;			\
804
805
806
807
808
809
810
811
812
						\
      case opt | ARG_NOT:			\
        self->attr = none;			\
        break;					\
      }						\
      						\
    self->attr = arg;				\
    break

813
814
#define CASE_FLAG(opt, flag)			\
  case opt:					\
815
    if (self->super.not)			\
816
      {						\
817
        self->super.not = 0;			\
818
819
820
821
822
823
						\
      case opt | ARG_NOT:			\
        self->flag = 0;				\
        break;					\
      }						\
      						\
824
825
    self->flag = 1;				\
    break
826

827
828
829
830
831
832
833
834
835
836
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:
837
      state->child_inputs[0] = &self->super;
838
839
      state->child_inputs[1] = self->algorithms;
      state->child_inputs[2] = NULL;
840
      break;
841
      
842
    case ARGP_KEY_END:
843
844
845
      if (self->super.inhibit_actions)
	break;

846
847
848
849
850
      if (!self->home)
	{
	  argp_error(state, "No home directory. Please set HOME in the environment.");
	  break;
	}
851

852
853
854
      if (!self->super.random)
	argp_failure( state, EXIT_FAILURE, 0,  "No randomness generator available.");

855
856
857
858
859
860
861
862
863
864
865
866
867
      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;
868
	      ALIST_SET(self->algorithms->algorithms,
869
			ATOM_SRP_RING1_SHA1_LOCAL,
870
			&make_srp_client(make_srp1(self->super.random),
871
872
873
					 self->super.tty,
					 ssh_format("%lz", self->super.user))
			->super);
874
875
876
877
878
	    }
#endif /* WITH_SRP */
	  if (self->with_dh_keyexchange)
	    {
	      LIST(self->kex_algorithms)[i++] = ATOM_DIFFIE_HELLMAN_GROUP1_SHA1;
879
	      ALIST_SET(self->algorithms->algorithms,
880
			ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
881
			&make_dh_client(make_dh1(self->super.random))
882
			->super);
883
884
885
886
887
	    }
	}
      else
	argp_error(state, "All keyexchange algorithms disabled.");

888
      {
889
890
        CAST_SUBTYPE(command, o, make_lsh_userauth(self));
        self->service = o;
891
892
893
894
895
896
897
898
899
      }
	
      {
	struct lsh_string *tmp = NULL;
	const char *s = NULL;
	  
	if (self->capture)
	  s = self->capture;
	else if (self->sloppy)
900
	  {
901
902
	    tmp = ssh_format("%lz/.lsh/captured_keys", self->home);
	    s = lsh_get_cstring(tmp);
903
	  }
904
	if (s)
905
	  {
906
907
908
909
	    static const struct report_exception_info report =
	      STATIC_REPORT_EXCEPTION_INFO(EXC_IO, EXC_IO,
					   "Writing new ACL: ");
	    
910
	    struct lsh_fd *f
911
	      = io_write_file(s,
912
913
914
			      O_CREAT | O_APPEND | O_WRONLY,
			      0600, 500, NULL,
			      make_report_exception_handler
915
			      (&report,
916
917
918
919
920
			       &default_exception_handler,
			       HANDLER_CONTEXT));
	    if (f)
	      self->capture_file = &f->write_buffer->super;
	    else
921
	      {
922
		werror("Failed to open '%z' %e.\n", s, errno);
923
924
	      }
	  }
925
926
	lsh_string_free(tmp);
      }
927

928
929
930
      /* 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. */
931
      if (self->start_gateway)
932
	{
933
	  struct local_info *gateway;
934
	  if (!self->super.local_user)
935
	    {
936
937
938
	      argp_error(state, "You have to set LOGNAME in the environment, "
			 " if you want to use the gateway feature.");
	      break;
939
	    }
940
941
942
	  gateway = make_gateway_address(self->super.local_user,
					 self->super.user,
					 self->super.remote);
943

944
	  if (!gateway)
945
	    {
946
947
	      argp_error(state, "Local or remote user name, or the target host name, are too "
			 "strange for the gateway socket name construction.");
948
	      break;
949
	    }
950
	      
951
	  client_add_action(&self->super,
952
			    make_gateway_setup(gateway));
953
	}
954

955
956
957
958
959
      if (object_queue_is_empty(&self->super.actions))
	{
	  argp_error(state, "No actions given.");
	  break;
	}
960

961
      break;
962
      
Balázs Scheidler's avatar
Balázs Scheidler committed
963
    case 'i':
964
      self->identity = arg;
965
      break;
Niels Möller's avatar
Niels Möller committed
966

967
    CASE_FLAG(OPT_PUBLICKEY, with_publickey);
968

Niels Möller's avatar
Niels Möller committed
969
    case OPT_HOST_DB:
970
      self->known_hosts = arg;
Niels Möller's avatar
Niels Möller committed
971
972
      break;
      
973
974
975
976
977
978
979
980
981
982
983
    case OPT_SLOPPY:
      self->sloppy = 1;
      break;

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

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

985
986
    CASE_FLAG(OPT_DH, with_dh_keyexchange);
    CASE_FLAG(OPT_SRP, with_srp_keyexchange);
987

988
989
990
991
992
    case 'R':
      {
	UINT32 listen_port;
	struct address_info *target;

993
	if (!client_parse_forward_arg(arg, &listen_port, &target))
994
995
	  argp_error(state, "Invalid forward specification '%s'.", arg);

996
	client_add_action(&self->super, make_forward_remote_port
997
			  (make_address_info((self->super.with_remote_peers
998
999
1000
1001
					      ? ssh_format("%lz", "0.0.0.0")
					      : ssh_format("%lz", "127.0.0.1")),
					     listen_port),
			   target));
1002
1003

	self->super.remote_forward = 1;
1004
1005
	break;
      }      
1006
1007
      
    CASE_FLAG('G', start_gateway);
1008
#if WITH_X11_FORWARD
1009
    CASE_FLAG('x', super.with_x11);
1010
#e