lsh.c 35.4 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
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * 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_pty.h"
34
#include "client_userauth.h"
35
36
#include "compress.h"
#include "connection_commands.h"
Niels Möller's avatar
Niels Möller committed
37
#include "crypto.h"
38
#include "dsa.h"
Niels Möller's avatar
Niels Möller committed
39
#include "format.h"
40
#include "interact.h"
Niels Möller's avatar
Niels Möller committed
41
#include "io.h"
42
#include "io_commands.h"
43
#include "lookup_verifier.h"
Niels Möller's avatar
Niels Möller committed
44
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
45
#include "service.h"
46
#include "sexp.h"
47
#include "spki_commands.h"
48
#include "srp.h" 
49
#include "ssh.h"
50
#include "tcpforward_commands.h"
51
#include "tty.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
#include <assert.h>
57
58
59
60
61
62
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

63
#if HAVE_UNISTD_H
64
#include <unistd.h>
65
#endif
66

67
68
69
70
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

71
#include "lsh_argp.h"
72

73
/* Forward declarations */
74
75
76
77
78
79
80
81
82
83
struct command_simple options2remote;
#define OPTIONS2REMOTE (&options2remote.super.super)

struct command_simple lsh_verifier_command;
#define OPTIONS2VERIFIER (&lsh_verifier_command.super.super)

struct command_simple lsh_login_command;
#define LSH_LOGIN (&lsh_login_command.super.super)

static struct command options2known_hosts;
84
#define OPTIONS2KNOWN_HOSTS (&options2known_hosts.super)
85

86
87
88
struct command_simple options2service;
#define OPTIONS2SERVICE (&options2service.super.super)

89
90
static struct command options2identities;
#define OPTIONS2IDENTITIES (&options2identities.super)
91
92
93
94
95
96
97
98
99

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

static struct request_service request_connection_service =
STATIC_REQUEST_SERVICE(ATOM_SSH_CONNECTION);
#define REQUEST_CONNECTION_SERVICE (&request_connection_service.super.super)

100
101
#include "lsh.c.x"

102
/* Block size for stdout and stderr buffers */
Niels Möller's avatar
Niels Möller committed
103
104
#define BLOCK_SIZE 32768

105
106
107
108
/* Window size for the session channel
 *
 * NOTE: Large windows seem to trig a bug in sshd2. */
#define WINDOW_SIZE 10000
109

110
111
112
113
114
115
/* GABA:
   (class
     (name lsh_options)
     (super algorithms_options)
     (vars
       (backend object io_backend)
Niels Möller's avatar
Niels Möller committed
116

117
118
       (random object randomness)

119
120
121
122
123
124
125
126
127
128
129
130
131
132
       (signature_algorithms object alist)
       (home . "const char *")
       
       ; For i/o exceptions 
       (handler object exception_handler)

       (exit_code . "int *")
       
       (not . int)
       (port . "char *")
       (remote object address_info)

       (user . "char *")
       (identity . "char *")
133
134
135
136
       (with_publickey . int)

       (with_srp_keyexchange . int)
       (with_dh_keyexchange . int)
137

138
139
140
141
142
143
       ; Ask for the userauth service
       (with_userauth . int)

       ; Command to invoke to start ssh-connection service)
       (service object command)
       
144
145
146
       ;; (kexinit object make_kexinit)
       (kex_algorithms object int_list)
       
147
148
149
150
151
152
153
154
155
156
       (sloppy . int)
       (capture . "const char *")
       (capture_file object abstract_write)

       (known_hosts . "const char *")
       
       ; -1 means default behaviour
       (with_pty . int)

       (with_remote_peers . int)
157
158

       ; Session modifiers
Niels Möller's avatar
Niels Möller committed
159
160
161
       (stdin_file . "const char *")
       (stdout_file . "const char *")
       (stderr_file . "const char *")
162
163
164
       ; True if the process's stdin or pty (respectively) has been used. 
       (used_stdin . int)
       (used_pty . int)
165
166
167
168
169
170
171
172
       
       (start_shell . int)
       (remote_forward . int)
       (actions struct object_queue)))
*/


static struct lsh_options *
173
make_options(struct io_backend *backend,
174
175
176
177
178
	     struct exception_handler *handler,
	     int *exit_code)
{
  NEW(lsh_options, self);

179
  init_algorithms_options(&self->super, all_symmetric_algorithms());
180
181
  
  self->backend = backend;
182
183
  self->random = make_reasonably_random();
  
184
185
  self->home = getenv("HOME");
  
186
  self->signature_algorithms = all_signature_algorithms(self->random);
187
188
189
190
191
192
193
  
  self->handler = handler;
  self->exit_code = exit_code;
  
  self->not = 0;
  self->remote = NULL;
  self->user = getenv("LOGNAME");
194
195
196
197

  /* Default behaviour is to lookup the "ssh" service, and fall back
   * to port 22 if that fails. */
  self->port = NULL; 
198
199
200
201
202
203
204

  self->sloppy = 0;
  self->capture = NULL;
  self->capture_file = NULL;

  self->known_hosts = NULL;
  /* self->known_hosts_file = NULL; */
205

Niels Möller's avatar
Niels Möller committed
206
207
208
  self->stdin_file = NULL;
  self->stdout_file = NULL;
  self->stderr_file = NULL;
209
210
  self->used_stdin = 0;
    
211
212
213
214
215
  self->with_pty = -1;
  self->start_shell = 1;
  self->with_remote_peers = 0;
  object_queue_init(&self->actions);

216
217
218
  self->with_publickey = 1;

  self->with_srp_keyexchange = 0;
219
220
221

  /* By default, enable only one of dh and srp. */
  self->with_dh_keyexchange = -1;
222
223

  self->with_userauth = -1;
224
  
225
  self->kex_algorithms = NULL;
226
227
228
229
  
  return self;
}

Niels Möller's avatar
Niels Möller committed
230
231
232
233
234
235
236
237

/* Host to connect to */
COMMAND_SIMPLE(options2remote)
{
  CAST(lsh_options, options, a);
  return &options->remote->super;
}

238
239
/* Request ssh-userauth or ssh-connection service, as appropriate,
 * and pass the options as a first argument. */
240
241
242
243
244
245
COMMAND_SIMPLE(options2service)
{
  CAST(lsh_options, options, a);
  return &options->service->super;
}

Niels Möller's avatar
Niels Möller committed
246
247
248
249
250
251
252
253
254
255
256
257
/* 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
258
  struct lsh_fd *f;
Niels Möller's avatar
Niels Möller committed
259
260
261
262
263
  
  if (options->known_hosts)
    s = options->known_hosts;
  else 
    {
264
      tmp = ssh_cformat("%lz/.lsh/known_hosts", options->home);
Niels Möller's avatar
Niels Möller committed
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
      s = tmp->data;
    }
  
  f = io_read_file(options->backend, s, e);

  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
299
  struct lsh_fd *f = NULL;
Niels Möller's avatar
Niels Möller committed
300

301
302
  trace("do_options2identities\n");
  
303
  if (!options->with_publickey)
Niels Möller's avatar
Niels Möller committed
304
305
306
307
308
309
310
311
    {
      COMMAND_RETURN(c, make_object_list(0, -1));
      return;
    }
  if (options->identity)
    s = options->identity;
  else 
    {
312
      tmp = ssh_cformat("%lz/.lsh/identity", options->home);
Niels Möller's avatar
Niels Möller committed
313
314
315
316
317
318
319
320
321
322
323
324
      s = tmp->data;
    }
  
  f = io_read_file(options->backend, s, e);
  
  if (!f)
    {
      werror("Failed to open '%z' (errno = %i): %z.\n",
	     s, errno, STRERROR(errno));
      COMMAND_RETURN(c, make_object_list(0, -1));
    }
  else
325
326
    COMMAND_CALL(make_spki_read_userkeys(options->signature_algorithms),
		 f, c, e);
Niels Möller's avatar
Niels Möller committed
327
328
329
330
331
332
333
  
  lsh_string_free(tmp);
}

static struct command options2identities =
STATIC_COMMAND(do_options2identities);

334
335
336
337
338
339
340
341
/* GABA:
   (class
     (name options_command)
     (super command)
     (vars
       (options object lsh_options)))
*/

Niels Möller's avatar
Niels Möller committed
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
static struct command *
make_options_command(struct lsh_options *options,
		     void (*call)(struct command *s,
				  struct lsh_object *a,
				  struct command_continuation *c,
				  struct exception_handler *e))
{
  NEW(options_command, self);
  self->super.call = call;
  self->options = options;

  return &self->super;
}

/* Maps a host key to a (trusted) verifier object.
 *
 * NOTE: Handles only ssh-dss keys. */
Niels Möller's avatar
Niels Möller committed
359

360
/* GABA:
361
   (class
362
     (name lsh_host_db)
363
364
     (super lookup_verifier)
     (vars
365
366
367
368
369
370
371
       (db object spki_context)
       (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)
372
       (hash object hash_algorithm) ; For fingerprinting
373
374
       ;; (algorithm object signature_algorithm)
       ))
375
376
*/

377
static struct verifier *
378
do_lsh_lookup(struct lookup_verifier *c,
Niels Möller's avatar
Niels Möller committed
379
	      int method,
380
	      struct lsh_user *keyholder UNUSED,
Niels Möller's avatar
Niels Möller committed
381
	      struct lsh_string *key)
Niels Möller's avatar
Niels Möller committed
382
{
383
  CAST(lsh_host_db, self, c);
384
  struct spki_subject *subject;
Niels Möller's avatar
Niels Möller committed
385

386
387
388
389
  switch (method)
    {
    case ATOM_SSH_DSS:
      {
390
	struct verifier *v = make_ssh_dss_verifier(key->length, key->data);
391
392
	if (!v)
	  {
393
	    werror("do_lsh_lookup: Invalid ssh-dss key.\n");
394
395
396
	    return NULL;
	  }
	subject = SPKI_LOOKUP(self->db,
397
398
			      spki_make_public_key(v),
			      v);
399
400
401
402
	assert(subject);
	assert(subject->verifier);
	break;
      }
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#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;
      }
#endif
420
421
    case ATOM_SPKI:
      {
422
	struct sexp *e = string_to_sexp(SEXP_CANONICAL, key, 0);
423
424
	if (!e)
	  {
425
	    werror("do_lsh_lookup: Invalid spki s-expression.\n");
426
427
428
429
430
431
	    return NULL;
	  }
	  
	subject = SPKI_LOOKUP(self->db, e, NULL);
	if (!subject)
	  {
432
	    werror("do_lsh_lookup: Invalid spki key.\n");
433
434
435
436
	    return NULL;
	  }
	if (!subject->verifier)
	  {
437
	    werror("do_lsh_lookup: Valid SPKI subject, but no key available.\n");
438
439
440
441
442
	    return NULL;
	  }
	break;
      }
    default:
443
      werror("do_lsh_lookup: Unknown key type. Should not happen!\n");
444
445
446
447
      return NULL;
    }

  assert(subject->key);
448
  
449
450
451
452
  /* Check authorization */

  if (SPKI_AUTHORIZE(self->db, subject, self->access))
    {
453
      verbose("SPKI host authorization successful!\n");
454
455
    }
  else
456
    {
457
      verbose("SPKI authorization failed.\n");
458
      if (!self->sloppy)
459
460
461
462
	{
	  werror("lsh: Server's hostkey is not trusted. Disconnecting.\n");
	  return NULL;
	}
463
      
464
465
      /* Ok, let's see if we want to use this untrusted key. */
      if (!quiet_flag)
466
	{
467
468
469
470
471
472
	  /* Display fingerprint */
	  struct lsh_string *fingerprint
	    = hash_string(self->hash,
			  sexp_format(subject->key, SEXP_CANONICAL, 0),
			  1);
			  
473
	  if (!yes_or_no(ssh_format("Received unauthenticated key for host %lS\n"
474
475
476
477
				    "Fingerprint: %lfxS\n"
				    "Do you trust this key? (y/n) ",
				    self->host->ip, fingerprint), 0, 1))
	    return NULL;
478
479
	}
      
Niels Möller's avatar
Niels Möller committed
480
      /* Write an ACL to disk. */
481
      if (self->file)
482
	{
Niels Möller's avatar
Niels Möller committed
483
484
485
486
487
488
489
490
491
492
493
494
	  A_WRITE(self->file, ssh_format("\n; ACL for host %lS\n", self->host->ip));
	  A_WRITE(self->file,
		  sexp_format(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),
			      SEXP_TRANSPORT, 0));
	  A_WRITE(self->file, ssh_format("\n"));
495
496
497
	}
    }
  
498
  return subject->verifier;
Niels Möller's avatar
Niels Möller committed
499
500
}

501
static struct lookup_verifier *
502
make_lsh_host_db(struct spki_context *db,
Niels Möller's avatar
Niels Möller committed
503
504
505
		 struct address_info *host,
		 int sloppy,
		 struct abstract_write *file)
Niels Möller's avatar
Niels Möller committed
506
{
507
  NEW(lsh_host_db, res);
508

509
  res->super.lookup = do_lsh_lookup;
510
511
512
513
514
  res->db = db;
  res->access = make_ssh_hostkey_tag(host);
  res->host = host;
  res->sloppy = sloppy;
  res->file = file;
515
  res->hash = &sha1_algorithm;
Niels Möller's avatar
Niels Möller committed
516
517
518
519

  return &res->super;
}

Niels Möller's avatar
Niels Möller committed
520
/* Takes an spki_context as argument and returns a lookup_verifier */
521
522
523
524
525
526
527
528
529
static void
do_lsh_verifier(struct command *s,
		struct lsh_object *a,
		struct command_continuation *c,
		struct exception_handler *e UNUSED)
{
  CAST(options_command, self, s);
  CAST_SUBTYPE(spki_context, db, a);
  COMMAND_RETURN(c, make_lsh_host_db(db,
Niels Möller's avatar
Niels Möller committed
530
531
532
				     self->options->remote,
				     self->options->sloppy,
				     self->options->capture_file));
533
534
}

Niels Möller's avatar
Niels Möller committed
535
536
/* Takes an options object as argument and returns a lookup_verifier */

537
538
539
540
COMMAND_SIMPLE(lsh_verifier_command)
{
  CAST(lsh_options, options, a);

Niels Möller's avatar
Niels Möller committed
541
542
543
  return
    & make_options_command(options,
			   do_lsh_verifier)->super;
544
545
}

Niels Möller's avatar
Niels Möller committed
546
/* list-of-public-keys -> login-command */
547
548
549
550
551
static void
do_lsh_login(struct command *s,
	     struct lsh_object *a,
	     struct command_continuation *c,
	     struct exception_handler *e UNUSED)
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
{
  CAST(options_command, self, s);
  CAST_SUBTYPE(object_list, keys, a);

  COMMAND_RETURN(c,
		 make_client_userauth(ssh_format("%lz", self->options->user),
				      ATOM_SSH_CONNECTION,
				      (LIST_LENGTH(keys)
				       ? make_object_list
				         (2, 
					  make_client_publickey_auth(keys),
					  make_client_password_auth(), 
					  -1)
				       : make_object_list(1, make_client_password_auth(), 
							  -1))));
}

Niels Möller's avatar
Niels Möller committed
569
/* (login options public-keys connection) */
570
571
572
573
COMMAND_SIMPLE(lsh_login_command)
{
  CAST(lsh_options, options, a);

Niels Möller's avatar
Niels Möller committed
574
  return
575
576
    &make_options_command(options,
			  do_lsh_login)->super;
577
578
}

579
580
581
582
583
/* NOTE: options2identities a command_simple, so it must not be
 * invoked directly. */

/* Requests the ssh-userauth service, log in, and request connection
 * service. */
584
585
586
587
588
589
590
/* GABA:
   (expr
     (name make_lsh_userauth)
     (params
       (options object lsh_options))
     (expr
       (lambda (connection)
591
592
593
594
595
596
         (lsh_login options
	   
	   ; The prog1 delay is needed because options2identities is
	   ; not a command_simple.
	   (options2identities (prog1 options connection))

597
	   ; Request the userauth service
598
599
	   (request_userauth_service connection))))) */

Niels Möller's avatar
Niels Möller committed
600
601
/* GABA:
   (expr
602
     (name make_lsh_connect)
603
604
     (params
       (connect object command)
605
       (handshake object handshake_info)
606
       (init object make_kexinit)
Niels Möller's avatar
Niels Möller committed
607
       (requests object object_list))
608
     (expr (lambda (options)
609
610
611
612
613
614
615
               ; What to do with the service
	       ((progn requests)
	         ; 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
616
		     ; Start the ssh transport protocol
617
	             (connection_handshake
618
		       handshake init
619
620
		       (options2verifier options
				         (options2known_hosts options))
Niels Möller's avatar
Niels Möller committed
621
 		       ; Connect using tcp
622
		       (connect (options2remote options)))))))))
623
624
625
626
627
628
629
630
631
*/

/* GABA:
   (expr
     (name make_start_session)
     (params
       (open_session object command)
       (requests object object_list))
     (expr (lambda (connection)
Niels Möller's avatar
Niels Möller committed
632
633
634
       ((progn requests)
         ; Create a "session" channel
         (open_session connection)))))
Niels Möller's avatar
Niels Möller committed
635
636
*/

637
/* Requests a shell or command, and connects the channel to our stdio. */
Niels Möller's avatar
Niels Möller committed
638
639
/* GABA:
   (expr
640
641
642
     (name lsh_start_session)
     (params
       (request object command))
Niels Möller's avatar
Niels Möller committed
643
644
     (expr
       (lambda (session)
645
          (client_start_io (request session)))))
Niels Möller's avatar
Niels Möller committed
646
647
*/

648
649
650
651
652
653
654
static struct command *
make_lsh_start_session(struct command *request)
{
  CAST_SUBTYPE(command, r, lsh_start_session(request));
  return r;
}

Niels Möller's avatar
Niels Möller committed
655
656
657
658
659
/* Parse the argument for -R and -L */
static int
parse_forward_arg(char *arg,
		  UINT32 *listen_port,
		  struct address_info **target)
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
{
  char *first;
  char *second;
  char *end;
  long port;
  
  first = strchr(arg, ':');
  if (!first)
    return 0;

  second = strchr(first + 1, ':');
  if (!second || (second == first + 1))
    return 0;

  if (strchr(second + 1, ':'))
    return 0;

  port = strtol(arg, &end, 0);
  if ( (end == arg)  || (end != first)
       || (port < 0) || (port > 0xffff) )
    return 0;

  *listen_port = port;

  port = strtol(second + 1, &end, 0);
  if ( (end == second + 1) || (*end != '\0')
       || (port < 0) || (port > 0xffff) )
    return 0;

  *target = make_address_info(ssh_format("%ls", second - first - 1, first + 1), port);
  
  return 1;
}
Niels Möller's avatar
Niels Möller committed
693

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

695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
/* FIXME: Resetting the tty should really be done by the corresponding
 * channel. */

#if WITH_PTY_SUPPORT
/* Global variable, because we use atexit() rather than on_exit() */
struct tty_state
{
  struct termios mode;
  int fd;
} old_tty;

static void reset_tty(void)
{
  tty_setattr(old_tty.fd, &old_tty.mode);
}

static int remember_tty(int fd)
{
  old_tty.fd = fd;
  return (tty_getattr(fd, &old_tty.mode)
	  && !atexit(reset_tty));
}
#endif /* WITH_PTY_SUPPORT */


/* Option parsing */

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

const char *argp_program_bug_address = BUG_ADDRESS;

727
728
#define ARG_NOT 0x400

729
#define OPT_PUBLICKEY 0x201
730

731
732
733
734
#define OPT_SLOPPY 0x202
#define OPT_STRICT 0x203
#define OPT_CAPTURE 0x204

Niels Möller's avatar
Niels Möller committed
735
736
#define OPT_HOST_DB 0x205

737
738
#define OPT_DH 0x206
#define OPT_SRP 0x207
739
#define OPT_USERAUTH 0x208
740

741
742
743
744
#define OPT_STDIN 0x210
#define OPT_STDOUT 0x211
#define OPT_STDERR 0x212

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

769
770
771
  { "dh-keyexchange", OPT_DH, NULL, 0,
    "Enable DH support (default, unless SRP is being used).", 0 },

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

774
775
776
777
778
  { "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 },
  
779
780
781
  { NULL, 0, NULL, 0, "Actions:", 0 },
  { "forward-local-port", 'L', "local-port:target-host:target-port", 0, "", 0 },
  { "forward-remote-port", 'R', "remote-port:target-host:target-port", 0, "", 0 },
Niels Möller's avatar
Niels Möller committed
782
  { "nop", 'N', NULL, 0, "No operation (suppresses the default action, "
783
    "which is to spawn a remote shell)", 0 },
784
785
  { "execute", 'E', "command", 0, "Execute a command on the remote machine", 0 },
  { "shell", 'S', "command", 0, "Spawn a remote shell", 0 },
786
787
  { NULL, 0, NULL, 0, "Modifiers that apply to port forwarding:", 0 },
  { "remote-peers", 'g', NULL, 0, "Allow remote access to forwarded ports", 0 },
788
  { "no-remote-peers", 'g' | ARG_NOT, NULL, 0, 
789
    "Disallow remote access to forwarded ports (default).", 0 },
790

791
  { NULL, 0, NULL, 0, "Modifiers that apply to remote execution:", 0 },
792
793
794
795
796
797
798
799
  { "stdin", OPT_STDIN, "Filename", 0, "Redirect stdin", 0},
  { "no-stdin", OPT_STDIN | ARG_NOT, NULL, 0, "Redirect stdin from /dev/null", 0}, 
  { "stdout", OPT_STDOUT, "Filename", 0, "Redirect stdout", 0},
  { "no-stdout", OPT_STDOUT | ARG_NOT, NULL, 0, "Redirect stdout to /dev/null", 0}, 
  { "stderr", OPT_STDERR, "Filename", 0, "Redirect stderr", 0},
  { "no-stderr", OPT_STDERR | ARG_NOT, NULL, 0, "Redirect stderr to /dev/null", 0}, 
  
#if WITH_PTY_SUPPORT
800
801
  { "pty", 't', NULL, 0, "Request a remote pty (default).", 0 },
  { "no-pty", 't' | ARG_NOT, NULL, 0, "Don't request a remote pty.", 0 },
802
#endif /* WITH_PTY_SUPPORT */
803
804
  { NULL, 0, NULL, 0, "Universal not:", 0 },
  { "no", 'n', NULL, 0, "Inverts the effect of the next modifier", 0 },
805
806
807
808
809
810
811
  { NULL, 0, NULL, 0, NULL, 0 }
};


static const struct argp_child
main_argp_children[] =
{
812
  { &algorithms_argp, 0, "", 0 },
813
814
815
816
  { &werror_argp, 0, "", 0 },
  { NULL, 0, NULL, 0}
};

817
818
819
820
821
822
823
824
825
826
827
/* Create a session object. stdout and stderr are shared (although
 * with independent lsh_fd objects). stdin can be used by only one
 * session (until something "session-control"/"job-control" is added).
 * */
static struct ssh_channel *
make_lsh_session(struct lsh_options *self)
{
  int in;
  int out;
  int err;

Niels Möller's avatar
Niels Möller committed
828
829
  if (self->stdin_file)
    in = open(self->stdin_file, O_RDONLY);
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
  else
    {
      if (self->used_stdin)
	in = open("/dev/null", O_RDONLY);
      else
	{
	  in = dup(STDIN_FILENO);
	  self->used_stdin = 1;
	}
    }
    
  if (in < 0)
    {
      werror("lsh: Can't dup/open stdin (errno = %i): %z!\n",
	     errno, strerror(errno));
      return NULL;
    }

Niels Möller's avatar
Niels Möller committed
848
849
  out = (self->stdout_file
	 ? open(self->stdout_file, O_WRONLY | O_CREAT, 0666)
850
851
852
853
854
855
856
857
858
	 : dup(STDOUT_FILENO));
  if (out < 0)
    {
      werror("lsh: Can't dup/open stdout (errno = %i): %z!\n",
	     errno, strerror(errno));
      close(in);
      return NULL;
    }

Niels Möller's avatar
Niels Möller committed
859
860
  if (self->stderr_file)
    err = open(self->stderr_file, O_WRONLY | O_CREAT, 0666);
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
  else
    {
      err = dup(STDERR_FILENO);
      set_error_stream(STDERR_FILENO, 1);
    }

  if (err < 0) 
    {
      werror("lsh: Can't dup/open stderr!\n");
      close(in);
      close(out);
      return NULL;
    }

  /* Clear options */
Niels Möller's avatar
Niels Möller committed
876
  self->stdin_file = self->stdout_file = self->stderr_file = NULL;
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
  
  return make_client_session
    (io_read(make_lsh_fd(self->backend, in, self->handler),
	     NULL, NULL),
     io_write(make_lsh_fd(self->backend, out, self->handler),
	      BLOCK_SIZE, NULL),
     io_write(make_lsh_fd(self->backend, err, self->handler),
	      BLOCK_SIZE, NULL),
     WINDOW_SIZE,
     self->exit_code);
}

/* Create an interactive session */
static struct command *
lsh_shell_session(struct lsh_options *self)
{
  struct command *get_pty = NULL;
  struct command *get_shell;
  
  struct object_list *session_requests;
  struct ssh_channel *session = make_lsh_session(self);

  if (!session)
    return NULL;
  
#if WITH_PTY_SUPPORT
  if (self->with_pty && !self->used_pty)
    {
      self->used_pty = 1;
      
      if (tty_fd < 0)
	{
	  werror("lsh: No tty available.\n");
	}
      else
	{
	  if (! (remember_tty(tty_fd)
		 && (get_pty = make_pty_request(tty_fd))))
	    {
	      werror("lsh: Can't use tty (probably getattr or atexit() failed.\n");
	    }
	}
    }

  get_shell = make_lsh_start_session(&request_shell.super);
  
  /* FIXME: We need a non-varargs constructor for lists. */
  if (get_pty)
    session_requests
      = make_object_list(2,
			 /* Ignore EXC_CHANNEL_REQUEST for the pty allocation call. */
			 make_catch_apply
			 (make_catch_handler_info(EXC_ALL, EXC_CHANNEL_REQUEST,
						  0, NULL),
			  get_pty),
			 get_shell, -1);
  else
#endif /* WITH_PTY_SUPPORT */
    session_requests = make_object_list(1, get_shell, -1);

  {
    CAST_SUBTYPE(command, r,
		 make_start_session
		 (make_open_session_command(session), session_requests));
    return r;
  }
}

/* Create a session executing a command line */
static struct command *
lsh_command_session(struct lsh_options *self,
		    struct lsh_string *command)
{
  struct ssh_channel *session = make_lsh_session(self);

  if (session)
    {
      CAST_SUBTYPE(command, r,
		   make_start_session
		   (make_open_session_command(session),
		    make_object_list
		    (1, make_lsh_start_session(make_exec_request(command)),
		     -1)));
      return r;
    }
  
  return NULL;
}

static struct command *
lsh_add_action(struct lsh_options *self,
	       struct command *action)
{
  if (action)
    object_queue_add_tail(&self->actions, &action->super);

  return action;
}

/* NOTE: Some of the original quoting is lost here. */
static struct lsh_string *
rebuild_command_line(unsigned argc, char **argv)
{
  unsigned length;
  unsigned i;
  unsigned pos;
  struct lsh_string *r;
  unsigned *alengths = alloca(sizeof(unsigned) * argc);
  
  assert (argc);
  length = argc - 1; /* Number of separating spaces. */

  for (i = 0; i<argc; i++)
    {
      alengths[i] = strlen(argv[i]);
      length += alengths[i];
    }

  r = lsh_string_alloc(length);
  memcpy(r->data, argv[0], alengths[0]);
  pos = alengths[0];
  for (i = 1; i<argc; i++)
    {
      r->data[pos++] = ' ';