lshd.c 15.1 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
3
/* lshd.c
 *
 * main server program.
4
5
 *
 * $Id$ */
Niels Möller's avatar
Niels Möller committed
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 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
Niels Möller's avatar
Niels Möller committed
23
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
 */
Niels Möller's avatar
Niels Möller committed
25

26
#include "algorithms.h"
27
28
#include "alist.h"
#include "atoms.h"
29
#include "channel.h"
30
#include "channel_commands.h"
31
#include "charset.h"
32
#include "compress.h"
33
#include "connection_commands.h"
34
#include "crypto.h"
35
#include "daemon.h"
36
#include "format.h"
Niels Möller's avatar
Niels Möller committed
37
#include "io.h"
38
#include "io_commands.h"
39
#include "lookup_verifier.h"
40
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
41
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
42
#include "server.h"
43
#include "server_authorization.h"
44
#include "server_keyexchange.h"
45
46
#include "server_pty.h"
#include "server_session.h"
47
#include "server_userauth.h"
48
#include "sexp.h"
Niels Möller's avatar
Niels Möller committed
49
#include "ssh.h"
50
51
#include "tcpforward.h"
#include "tcpforward_commands.h"
52
#include "tcpforward_commands.h"
Niels Möller's avatar
Niels Möller committed
53
#include "userauth.h"
54
55
56
#include "werror.h"
#include "xalloc.h"

57
#include "lsh_argp.h"
58
59
60
61
62
63
64
65
66
67
68
69
70

#include "lshd.c.x"

#include <assert.h>

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
71
#if HAVE_UNISTD_H
72
#include <unistd.h>
73
#endif
74

75
/* Block size for stdout and stderr buffers */
Niels Möller's avatar
Niels Möller committed
76
77
#define BLOCK_SIZE 32768

78
79
80

/* Option parsing */

81
#define OPT_NO 0x400
82
83
84
#define OPT_SSH1_FALLBACK 0x200
#define OPT_INTERFACE 0x201
#define OPT_TCPIP_FORWARD 0x202
85
86
87
88
89
90
#define OPT_NO_TCPIP_FORWARD (OPT_TCPIP_FORWARD | OPT_NO)
#define OPT_DAEMONIC 0x203
#define OPT_NO_DAEMONIC (OPT_DAEMONIC | OPT_NO)
#define OPT_PIDFILE 0x204
#define OPT_NO_PIDFILE (OPT_PIDFILE | OPT_NO)
#define OPT_CORE 0x207
91
92
93
94
95
96
97
98
99
100
101
102

/* GABA:
   (class
     (name lshd_options)
     (super algorithms_options)
     (vars
       (style . sexp_argp_state)
       (interface . "char *")
       (port . "char *")
       (hostkey . "char *")
       (local object address_info)
       (with_tcpip_forward . int)
103
104
105
106
107
108
       (sshd1 object ssh1_fallback)
       (daemonic . int)
       (corefile . int)
       (pid_file . "const char *")
       ; -1 means use pid file iff we're in daemonic mode
       (use_pid_file . int)))
109
110
*/

Niels Möller's avatar
Niels Möller committed
111
112
static struct lshd_options *
make_lshd_options(struct alist *algorithms)
113
{
Niels Möller's avatar
Niels Möller committed
114
  NEW(lshd_options, self);
115
116
117
118
119
120
121
122
123
124
125
126
127

  init_algorithms_options(&self->super, algorithms);
  
  self->style = SEXP_TRANSPORT;
  self->interface = NULL;
  self->port = "ssh";
  /* FIXME: this should perhaps use sysconfdir */  
  self->hostkey = "/etc/lsh_host_key";
  self->local = NULL;

  self->with_tcpip_forward = 1;
  
  self->sshd1 = NULL;
128
129
130
131
132
133
  self->daemonic = 0;

  /* FIXME: Make the default a configure time option? */
  self->pid_file = "/var/run/lshd.pid";
  self->use_pid_file = -1;
  self->corefile = 0;
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  
  return self;
}

static const struct argp_option
main_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
  { "interface", OPT_INTERFACE, "interface", 0,
    "Listen on this network interface", 0 }, 
  { "port", 'p', "Port", 0, "Listen on this port.", 0 },
  { "host-key", 'h', "Key file", 0, "Location of the server's private key.", 0},

#if WITH_SSH1_FALLBACK
  { "ssh1-fallback", OPT_SSH1_FALLBACK, "File name", OPTION_ARG_OPTIONAL,
    "Location of the sshd1 program, for falling back to version 1 of the Secure Shell protocol.", 0 },
#endif /* WITH_SSH1_FALLBACK */

#if WITH_TCP_FORWARD
  { "tcp-forward", OPT_TCPIP_FORWARD, NULL, 0, "Enable tcpip forwarding (default).", 0 },
  { "no-tcp-forward", OPT_NO_TCPIP_FORWARD, NULL, 0, "Disable tcpip forwarding.", 0 },
#endif /* WITH_TCP_FORWARD */

157
158
  { NULL, 0, NULL, 0, "Daemonic behaviour", 0 },
  { "daemonic", OPT_DAEMONIC, NULL, 0, "Run in the background, redirect stdio to /dev/null, and chdir to /.", 0 },
159
  { "no-daemonic", OPT_NO_DAEMONIC, NULL, 0, "Run in the foreground, with messages to stderr (default).", 0 },
160
161
  { "pid-file", OPT_PIDFILE, "file name", 0, "Create a pid file. When running in daemonic mode, "
    "the default is /var/run/lshd.pid.", 0 },
162
  { "no-pid-file", OPT_NO_PIDFILE, NULL, 0, "Don't use any pid file. Default in non-daemonic mode.", 0 },
163
164
  { "enable-core", OPT_CORE, NULL, 0, "Dump core on fatal errors (disabled by default).", 0 },
    
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  { NULL, 0, NULL, 0, NULL, 0 }
};

static const struct argp_child
main_argp_children[] =
{
  { &sexp_input_argp, 0, "", 0 },
  { &algorithms_argp, 0, "", 0 },
  { &werror_argp, 0, "", 0 },
  { NULL, 0, NULL, 0}
};

static error_t
main_argp_parser(int key, char *arg, struct argp_state *state)
{
  CAST(lshd_options, self, state->input);
  
  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;
    case ARGP_KEY_INIT:
      state->child_inputs[0] = &self->style;
      state->child_inputs[1] = &self->super;
189
      state->child_inputs[2] = NULL;
190
191
192
193
194
195
196
197
198
199
200
      break;
    case ARGP_KEY_ARG:
      argp_error(state, "Spurious arguments.");
      break;
      
    case ARGP_KEY_END:
      self->local = make_address_info_c(self->interface, self->port);
      if (!self->local)
	argp_error(state, "Invalid interface, port or service, %s:%s'.",
		   self->interface ? self->interface : "ANY",
		   self->port);
201
202
203
      if (self->use_pid_file < 0)
	self->use_pid_file = self->daemonic;
      
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
      break;
      
    case 'p':
      self->port = arg;
      break;

    case 'h':
      self->hostkey = arg;
      break;

    case OPT_INTERFACE:
      self->interface = arg;
      break;
 
#if WITH_SSH1_FALLBACK
    case OPT_SSH1_FALLBACK:
      self->sshd1 = make_ssh1_fallback(arg ? arg : SSHD1);
      break;
#endif
Niels Möller's avatar
Niels Möller committed
223

224
225
226
227
228
229
230
    case OPT_TCPIP_FORWARD:
      self->with_tcpip_forward = 1;
      break;

    case OPT_NO_TCPIP_FORWARD:
      self->with_tcpip_forward = 0;
      break;
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

    case OPT_DAEMONIC:
      self->daemonic = 1;
      break;

    case OPT_NO_DAEMONIC:
      self->daemonic = 0;
      break;

    case OPT_PIDFILE:
      self->pid_file = arg;
      self->use_pid_file = 1;
      break;

    case OPT_NO_PIDFILE:
      self->use_pid_file = 0;
      break;

    case OPT_CORE:
      self->corefile = 1;
      break;
252
253
254
    }
  return 0;
}
Niels Möller's avatar
Niels Möller committed
255

Niels Möller's avatar
Niels Möller committed
256
257
258
259
260
261
static const struct argp
main_argp =
{ main_options, main_argp_parser, 
  NULL,
  "Server for the ssh-2 protocol.",
  main_argp_children,
262
  NULL, NULL
Niels Möller's avatar
Niels Möller committed
263
264
};

265
266
/* FIXME: We should have some more general functions for reading
 * private keys. */
267

268
/* GABA:
269
270
   (class
     (name read_key)
271
     (super command_continuation)
272
273
     (vars
       (random object randomness)
274
275
       ;; Maps hostkey algorithm to a keyinfo structure
       (keys object alist)))
276
277
*/

278
279
static void do_read_key(struct command_continuation *s,
			struct lsh_object *a)
280
{
281
282
283
  CAST(read_key, closure, s);
  CAST_SUBTYPE(sexp, private, a);
  
284
285
286
  struct sexp_iterator *i;
  struct sexp *e;
  mpz_t p, q, g, y, x;
Niels Möller's avatar
Niels Möller committed
287
  
288
289
290
  if (!sexp_check_type(private, "private-key", &i))
    {
      werror("lshd: Host key file does not contain a private key.");
291
      return;
292
293
294
    }

  e = SEXP_GET(i);
Niels Möller's avatar
Niels Möller committed
295
  if (! (e && sexp_check_type(e, "dsa", &i)))
296
    {
Niels Möller's avatar
Niels Möller committed
297
      werror("lshd: Unknown key type (only dsa is supported)\n");
298
      return;
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
    }

  mpz_init(p);
  mpz_init(q);
  mpz_init(g);
  mpz_init(y);
  mpz_init(x);

  if (sexp_get_un(i, "p", p)
      && sexp_get_un(i, "q", q)
      && sexp_get_un(i, "g", g)
      && sexp_get_un(i, "y", y)
      && sexp_get_un(i, "x", x)
      && !SEXP_GET(i))
    {
      /* Test key */
      mpz_t tmp;
      struct lsh_string *s;
      
      mpz_init_set(tmp, g);
      mpz_powm(tmp, tmp, x, p);
      if (mpz_cmp(tmp, y))
	{
	  werror("lshd: Host key doesn't work.\n");
	  mpz_clear(tmp);
	}
      else
	{
	  struct lsh_string *public
	    = ssh_format("%a%n%n%n%n", ATOM_SSH_DSS, p, q, g, y);
329
	  struct signer *private;
330
331
332
	  	  
	  s = ssh_format("%n", x);
	  
333
334
335
336
	  private = MAKE_SIGNER(make_dsa_algorithm(closure->random),
				public->length, public->data,
				s->length, s->data);
	  assert(private);
337
	  lsh_string_free(s);
338
339
340
341
	  
	  /* FIXME: Check if we already have a key for this algorithm,
	   * and warn about multiple keys. */
	  ALIST_SET(closure->keys, ATOM_SSH_DSS,
342
		    make_keypair(public, private));
343

344
#if DATAFELLOWS_WORKAROUNDS
345
	  ALIST_SET(closure->keys, ATOM_SSH_DSS_KLUDGE,
346
		    make_keypair(public,
347
				      make_dsa_signer_kludge(private)));
348
#endif /* DATAFELLOWS_WORKAROUNDS */
349
	  
350
351
352
353
354
355
	  debug("lshd: Using (public) hostkey:\n"
		"  p=%xn\n"
		"  q=%xn\n"
		"  g=%xn\n"
		"  y=%xn\n",
		p, q, g, y);
356
357
358
359
360
361
362
363
364
365
366
367
	}
    }

  /* Cleanup */
  mpz_clear(p);
  mpz_clear(q);
  mpz_clear(g);
  mpz_clear(y);
  mpz_clear(x);
}

static int read_host_key(const char *name,
368
			 struct alist *keys,
369
370
371
372
373
			 struct randomness *r)
{
  int fd = open(name, O_RDONLY);
  if (fd < 0)
    {
374
      werror("lshd: Could not open %z (errno = %i): %z\n",
375
	     name, errno, STRERROR(errno));
376
377
378
379
380
381
382
      return 0;
    }
  else
    {
      int res;
      
      NEW(read_key, handler);
383
      handler->super.c = do_read_key;
Niels Möller's avatar
Niels Möller committed
384

385
      handler->random = r;
386
      handler->keys = keys;
387
      
388
389
390
      res = blocking_read(fd,
			  make_read_sexp(SEXP_TRANSPORT, 1,
					 &handler->super, &ignore_exception_handler));
391
392
393
394
395
396
397
      close(fd);

      KILL(handler);
      
      return 1;
    }
}
398

399
400
401
402
403
404
405
406
407
408
409
410
411
/* GABA:
   (expr
     (name lshd_listen)
     (globals
       (log "&io_log_peer_command.super.super"))
     (params
       (listen object command)
       (handshake object command)
       (services object command) )
     (expr (lambda (port)
             (services (handshake (log (listen port)))))))
*/

412
/* Invoked when the client requests the userauth service. */
413
414
/* GABA:
   (expr
415
416
     (name lshd_services)
     (params 
417
       (userauth object command))
418
419
420
421
422
     (expr
       (lambda (connection)
         ((userauth connection) connection))))
*/

423
/* Invoked when starting the ssh-connection service */
424
425
/* GABA:
   (expr
426
     (name lshd_connection_service)
427
     (globals
428
429
       (progn "&progn_command.super.super")
       (init "&connection_service.super"))
430
     (params
431
432
433
434
       (login object command)     
       (hooks object object_list))
     (expr
       (lambda (user connection)
435
436
437
438
         ((progn hooks) (login user
	                       ; We have to initialize the connection
			       ; before logging in.
	                       (init connection))))))
439
440
*/

Niels Möller's avatar
Niels Möller committed
441
442
int main(int argc, char **argv)
{
443
  struct lshd_options *options;
444
  
445
  struct alist *keys;
446
  
Niels Möller's avatar
Niels Möller committed
447
448
  struct reap *reaper;
  
449
450
451
452
  struct randomness *r;
  struct diffie_hellman_method *dh;
  struct keyexchange_algorithm *kex;
  struct alist *algorithms;
Niels Möller's avatar
Niels Möller committed
453
  struct make_kexinit *make_kexinit;
454
455
  struct alist *authorization_lookup;
  
456
  /* FIXME: Why not allocate backend statically? */
457
  NEW(io_backend, backend);
458
  init_backend(backend);
459

Niels Möller's avatar
Niels Möller committed
460
461
462
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
463
464
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
465
466
467

  r = make_reasonably_random();
  dh = make_dh1(r);
Niels Möller's avatar
Niels Möller committed
468
  
469
  algorithms = many_algorithms(1,
Niels Möller's avatar
Niels Möller committed
470
			       ATOM_SSH_DSS, make_dsa_algorithm(r),
471
			       -1);
472
473
  options = make_lshd_options(algorithms);
  
Niels Möller's avatar
Niels Möller committed
474
  argp_parse(&main_argp, argc, argv, 0, NULL, options);
475

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  if (!options->corefile && !daemon_disable_core())
    {
      werror("Disabling of core dumps failed.\n");
      return EXIT_FAILURE;
    }
  
  if (options->daemonic)
    {
#if HAVE_SYSLOG
      set_error_syslog("lshd");
#else /* !HAVE_SYSLOG */
      werror("lshd: No syslog. Further messages will be directed to /dev/null.\n");
#endif /* !HAVE_SYSLOG */
    }

  if (options->daemonic)
    switch (daemon_init())
      {
      case 0:
	werror("lshd: Spawning into background failed.\n");
	return EXIT_FAILURE;
      case DAEMON_INETD:
	werror("lshd: spawning from inetd not yet supported.\n");
	return EXIT_FAILURE;
      case DAEMON_INIT:
      case DAEMON_NORMAL:
	break;
      default:
	fatal("Internal error\n");
      }
506
507
508
509
510
511
  
  if (options->use_pid_file && !daemon_pidfile(options->pid_file))
    {
      werror("lshd seems to be running already.\n");
      return EXIT_FAILURE;
    }
512
      
513
  /* Read the hostkey */
514
  keys = make_alist(0, -1);
515
  if (!read_host_key(options->hostkey, keys, r))
516
    {
Niels Möller's avatar
Niels Möller committed
517
      werror("lshd: Could not read hostkey.\n");
518
519
      return EXIT_FAILURE;
    }
520
521

  /* FIXME: We should check that we have at least one host key.
522
523
   * We should also extract the host-key algorithms for which we have keys,
   * instead of hardcoding ssh-dss below. */
Niels Möller's avatar
Niels Möller committed
524
 
Niels Möller's avatar
Niels Möller committed
525
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
526

527
  kex = make_dh_server(dh, keys);
Niels Möller's avatar
Niels Möller committed
528

529
530
531
532
  authorization_lookup
    = make_alist(1
#if DATAFELLOWS_WORKAROUNDS
		 +1,
533
534
		 ATOM_SSH_DSS_KLUDGE, make_authorization_db(ssh_format("keys_md5"), 
							    make_dsa_kludge_algorithm(NULL),
535
536
537
							    &md5_algorithm)
#endif
				    
538
539
		 ,ATOM_SSH_DSS, make_authorization_db(ssh_format("keys_md5"),
						      make_dsa_algorithm(NULL),
540
541
542
543
544
						      &md5_algorithm),
		 
		 -1);

  
545
546
547
548
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
549
550
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
551
			  make_int_list(1, ATOM_SSH_DSS, -1),
Niels Möller's avatar
Niels Möller committed
552
553
554
			  options->super.crypto_algorithms,
			  options->super.mac_algorithms,
			  options->super.compression_algorithms,
555
			  make_int_list(0, -1));
556
557
  
  {
558
559
    /* Commands to be invoked on the connection */
    struct object_list *connection_hooks;
560
561

#if WITH_TCP_FORWARD
562
    if (options->with_tcpip_forward)
563
564
      connection_hooks = make_object_list
	(3,
Niels Möller's avatar
Niels Möller committed
565
	 make_tcpip_forward_hook(backend),
566
567
568
569
	 make_install_fix_global_request_handler
	 (ATOM_CANCEL_TCPIP_FORWARD, &tcpip_cancel_forward),
	 make_direct_tcpip_hook(backend),
	 -1);
570
571
    else
#endif
572
      connection_hooks = make_object_list(0, -1);
573
574
575
576
577
578
579
580
581
    {
      struct lsh_object *o = lshd_listen
	(make_simple_listen(backend, NULL),
	 make_handshake_command(CONNECTION_SERVER,
				"lsh - a free ssh",
				SSH_MAX_PACKET,
				r,
				algorithms,
				make_kexinit,
582
				options->sshd1),
583
584
585
586
	 make_offer_service
	 (make_alist
	  (1, ATOM_SSH_USERAUTH,
	   lshd_services(make_userauth_service
587
588
589
590
591
592
593
			 (make_int_list(2,
					ATOM_PASSWORD,
					ATOM_PUBLICKEY, -1),
			  make_alist(2,
				     ATOM_PASSWORD, &unix_userauth.super,
				     ATOM_PUBLICKEY, make_userauth_publickey(authorization_lookup),
				     -1),
594
			  make_alist(1, ATOM_SSH_CONNECTION,
595
596
597
598
				     lshd_connection_service
				     (make_server_connection_service
				      (make_alist
				       (1
599
#if WITH_PTY_SUPPORT
600
					+1, ATOM_PTY_REQ, make_pty_handler()
601
#endif /* WITH_PTY_SUPPORT */
602
603
604
605
606
607
					, ATOM_SHELL,
					make_shell_handler(backend,
							   reaper),
					-1),
				       backend),
				      connection_hooks),
608
609
				     -1))),
	   -1)));
610
    
611
      CAST_SUBTYPE(command, server_listen, o);
612
    
Niels Möller's avatar
Niels Möller committed
613
      COMMAND_CALL(server_listen, options->local,
614
615
616
617
		   &discard_continuation,
		   make_report_exception_handler(EXC_IO, EXC_IO, "lshd: ",
						 &default_exception_handler,
						 HANDLER_CONTEXT));
618
    }
619
  }
Niels Möller's avatar
Niels Möller committed
620
  
621
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
622
623
624

  return 0;
}