lshd.c 12.3 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
35
#include "crypto.h"
#include "format.h"
Niels Möller's avatar
Niels Möller committed
36
#include "io.h"
37
#include "io_commands.h"
38
#include "lookup_verifier.h"
39
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
40
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
41
#include "server.h"
42
#include "server_authorization.h"
43
#include "server_keyexchange.h"
44
45
#include "server_pty.h"
#include "server_session.h"
46
#include "server_userauth.h"
47
#include "sexp.h"
Niels Möller's avatar
Niels Möller committed
48
#include "ssh.h"
49
50
#include "tcpforward.h"
#include "tcpforward_commands.h"
51
#include "tcpforward_commands.h"
Niels Möller's avatar
Niels Möller committed
52
#include "userauth.h"
53
54
55
#include "werror.h"
#include "xalloc.h"

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

#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>
70
#if HAVE_UNISTD_H
71
#include <unistd.h>
72
#endif
73

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

Niels Möller's avatar
Niels Möller committed
77
void usage(void) NORETURN;
Niels Möller's avatar
Niels Möller committed
78

79
80
#define OPT_SSH1_FALLBACK -2

Niels Möller's avatar
Niels Möller committed
81
void usage(void)
Niels Möller's avatar
Niels Möller committed
82
{
83
  werror("lshd [options]\n"
84
85
86
	 " -p,  --port=PORT\n"
	 " -h,  --hostkey=KEYFILE\n"
	 " -c,  --crypto=ALGORITHM\n"
87
	 " -z,  --compression[=ALGORITHM]\n"
88
89
	 "      --mac=ALGORITHM\n"
	 " -q,  --quiet\n"
90
91
92
#if WITH_TCP_FORWARD
	 "      --no-forward\n"
#endif
93
94
95
#if WITH_SSH1_FALLBACK
         "      --ssh1-fallback=SSHD\n"
#endif
96
97
	 " -v,  --verbose\n"
	 "      --debug\n");
Niels Möller's avatar
Niels Möller committed
98
99
100
  exit(1);
}

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

102
103
/* FIXME: We should have some more general functions for reading
 * private keys. */
104

105
/* GABA:
106
107
   (class
     (name read_key)
108
     (super command_continuation)
109
110
     (vars
       (random object randomness)
111
112
       ;; Maps hostkey algorithm to a keyinfo structure
       (keys object alist)))
113
114
*/

115
116
static void do_read_key(struct command_continuation *s,
			struct lsh_object *a)
117
{
118
119
120
  CAST(read_key, closure, s);
  CAST_SUBTYPE(sexp, private, a);
  
121
122
123
  struct sexp_iterator *i;
  struct sexp *e;
  mpz_t p, q, g, y, x;
Niels Möller's avatar
Niels Möller committed
124
  
125
126
127
  if (!sexp_check_type(private, "private-key", &i))
    {
      werror("lshd: Host key file does not contain a private key.");
128
      return;
129
130
131
    }

  e = SEXP_GET(i);
Niels Möller's avatar
Niels Möller committed
132
  if (! (e && sexp_check_type(e, "dsa", &i)))
133
    {
Niels Möller's avatar
Niels Möller committed
134
      werror("lshd: Unknown key type (only dsa is supported)\n");
135
      return;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
    }

  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);
166
	  struct signer *private;
167
168
169
	  	  
	  s = ssh_format("%n", x);
	  
170
171
172
173
	  private = MAKE_SIGNER(make_dsa_algorithm(closure->random),
				public->length, public->data,
				s->length, s->data);
	  assert(private);
174
	  lsh_string_free(s);
175
176
177
178
	  
	  /* FIXME: Check if we already have a key for this algorithm,
	   * and warn about multiple keys. */
	  ALIST_SET(closure->keys, ATOM_SSH_DSS,
179
		    make_keypair(public, private));
180

181
#if DATAFELLOWS_WORKAROUNDS
182
	  ALIST_SET(closure->keys, ATOM_SSH_DSS_KLUDGE,
183
		    make_keypair(public,
184
				      make_dsa_signer_kludge(private)));
185
#endif /* DATAFELLOWS_WORKAROUNDS */
186
	  
187
	  verbose("lshd: Using (public) hostkey:\n"
188
189
190
191
		  "  p=%xn\n"
		  "  q=%xn\n"
		  "  g=%xn\n"
		  "  y=%xn\n",
192
		  p, q, g, y);
193
194
195
196
197
198
199
200
201
202
203
204
	}
    }

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

static int read_host_key(const char *name,
205
			 struct alist *keys,
206
207
208
209
210
			 struct randomness *r)
{
  int fd = open(name, O_RDONLY);
  if (fd < 0)
    {
211
      werror("lshd: Could not open %z (errno = %i): %z\n",
212
	     name, errno, STRERROR(errno));
213
214
215
216
217
218
219
      return 0;
    }
  else
    {
      int res;
      
      NEW(read_key, handler);
220
      handler->super.c = do_read_key;
Niels Möller's avatar
Niels Möller committed
221

222
      handler->random = r;
223
      handler->keys = keys;
224
      
225
226
227
      res = blocking_read(fd,
			  make_read_sexp(SEXP_TRANSPORT, 1,
					 &handler->super, &ignore_exception_handler));
228
229
230
231
232
233
234
      close(fd);

      KILL(handler);
      
      return 1;
    }
}
235

236
237
238
239
240
241
242
243
244
245
246
247
248
/* 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)))))))
*/

249
/* Invoked when the client requests the userauth service. */
250
251
/* GABA:
   (expr
252
253
     (name lshd_services)
     (params 
254
       (userauth object command))
255
256
257
258
259
     (expr
       (lambda (connection)
         ((userauth connection) connection))))
*/

260
/* Invoked when starting the ssh-connection service */
261
262
/* GABA:
   (expr
263
     (name lshd_connection_service)
264
     (globals
265
266
       (progn "&progn_command.super.super")
       (init "&connection_service.super"))
267
     (params
268
269
270
271
       (login object command)     
       (hooks object object_list))
     (expr
       (lambda (user connection)
272
273
274
275
         ((progn hooks) (login user
	                       ; We have to initialize the connection
			       ; before logging in.
	                       (init connection))))))
276
277
*/

Niels Möller's avatar
Niels Möller committed
278
279
int main(int argc, char **argv)
{
280
  char *host = NULL;  /* Interface to bind */
Niels Möller's avatar
Niels Möller committed
281
  char *port = "ssh";
Niels Möller's avatar
Niels Möller committed
282
  /* TODO: this should probably use sysconfdir */  
283
284
  char *hostkey = "/etc/lsh_host_key";

285
286
287
#if WITH_SSH1_FALLBACK
  char *sshd1 = NULL;
#endif
288
289
290
#if WITH_TCP_FORWARD
  int forward_flag = 1;
#endif
291
  
292
  struct alist *keys;
293
  
Niels Möller's avatar
Niels Möller committed
294
295
  int option;

296
297
298
  int preferred_crypto = 0;
  int preferred_compression = 0;
  int preferred_mac = 0;
299

300
  struct address_info *local;
301

Niels Möller's avatar
Niels Möller committed
302
303
  struct reap *reaper;
  
304
305
306
307
  struct randomness *r;
  struct diffie_hellman_method *dh;
  struct keyexchange_algorithm *kex;
  struct alist *algorithms;
Niels Möller's avatar
Niels Möller committed
308
  struct make_kexinit *make_kexinit;
309
310
  struct alist *authorization_lookup;
  
311
312
  NEW(io_backend, backend);

Niels Möller's avatar
Niels Möller committed
313
314
315
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
316
317
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
318
319
320

  r = make_reasonably_random();
  dh = make_dh1(r);
Niels Möller's avatar
Niels Möller committed
321
  
322
  algorithms = many_algorithms(1,
Niels Möller's avatar
Niels Möller committed
323
			       ATOM_SSH_DSS, make_dsa_algorithm(r),
324
325
			       -1);

326
  for (;;)
327
    {
328
      struct option options[] =
Niels Möller's avatar
Niels Möller committed
329
      {
330
331
332
333
334
335
336
	{ "verbose", no_argument, NULL, 'v' },
	{ "quiet", no_argument, NULL, 'q' },
	{ "debug", no_argument, &debug_flag, 1},
	{ "port", required_argument, NULL, 'p' },
	{ "crypto", required_argument, NULL, 'c' },
	{ "compression", optional_argument, NULL, 'z'},
	{ "mac", required_argument, NULL, 'm' },
337
	{ "hostkey", required_argument, NULL, 'h' },
338
339
340
#if WITH_TCP_FORWARD
	{ "no-forward", no_argument, &forward_flag, 0 },
#endif
341
342
343
#if WITH_SSH1_FALLBACK
	{ "ssh1-fallback", optional_argument, NULL, OPT_SSH1_FALLBACK},
#endif
344
	{ NULL, 0, NULL, 0 }
345
346
      };
      
347
      option = getopt_long(argc, argv, "c:h:p:qvz::", options, NULL);
348
349
350
351
352
353
      switch(option)
	{
	case -1:
	  goto options_done;
	case 0:
	  break;
354
355
356
357
358
#if WITH_SSH1_FALLBACK
	case OPT_SSH1_FALLBACK:
	  sshd1 = optarg ? optarg : SSHD1;
	  break;
#endif
359
360
361
362
363
364
365
366
367
	case 'p':
	  port = optarg;
	  break;
	case 'q':
	  quiet_flag = 1;
	  break;
	case 'v':
	  verbose_flag = 1;
	  break;
368
369
370
	case 'h':
	  hostkey = optarg;
	  break;
371
372
373
374
	case 'c':
	  preferred_crypto = lookup_crypto(algorithms, optarg);
	  if (!preferred_crypto)
	    {
375
	      werror("lsh: Unknown crypto algorithm '%z'.\n", optarg);
376
377
378
379
380
381
382
383
384
385
	      exit(1);
	    }
	  break;
	case 'z':
	  if (!optarg)
	    optarg = "zlib";
	
	  preferred_compression = lookup_compression(algorithms, optarg);
	  if (!preferred_compression)
	    {
386
	      werror("lsh: Unknown compression algorithm '%z'.\n", optarg);
387
388
389
390
391
392
393
	      exit(1);
	    }
	  break;
	case 'm':
	  preferred_mac = lookup_mac(algorithms, optarg);
	  if (!preferred_mac)
	    {
394
	      werror("lsh: Unknown message authentication algorithm '%z'.\n",
395
396
397
398
399
400
401
402
403
		      optarg);
	      exit(1);
	    }
	    
	case '?':
	  usage();
	}
    }
 options_done:
Niels Möller's avatar
Niels Möller committed
404
405
406
407

  if ( (argc - optind) != 0)
    usage();

408
  /* Read the hostkey */
409
410
  keys = make_alist(0, -1);
  if (!read_host_key(hostkey, keys, r))
411
    {
Niels Möller's avatar
Niels Möller committed
412
      werror("lshd: Could not read hostkey.\n");
413
414
      return EXIT_FAILURE;
    }
415
416
417
  /* FIXME: We should check that we have at aleast one host key.
   * We should also extract the host-key algorithms for which we have keys,
   * instead of hardcoding ssh-dss below. */
418
419
420
421
422
423
424
425

  local = make_address_info_c(host, port);
  if (!local)
    {
      werror("lshd: Invalid port or service\n");
      exit (EXIT_FAILURE);
    }
  
Niels Möller's avatar
Niels Möller committed
426
#if 0
427
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
428
429
430
431
432
433
434
435
436
437
438
439
  {
    int option = LOG_PID | LOG_CONS;
    if (foreground_flag)
      {
	option |= LOG_PERROR;
      }
    openlog("lshd", option, LOG_DAEMON);
    syslog_flag = 1;
  }
#endif /* HAVE_SYSLOG */
#endif
 
440
  init_backend(backend);
Niels Möller's avatar
Niels Möller committed
441
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
442

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

445
446
447
448
449
450
451
452
453
454
455
456
457
458
  authorization_lookup
    = make_alist(1
#if DATAFELLOWS_WORKAROUNDS
		 +1,
		 ATOM_SSH_DSS_KLUDGE, make_authorization_db(make_dsa_kludge_algorithm(NULL),
							    &md5_algorithm)
#endif
				    
		 ,ATOM_SSH_DSS, make_authorization_db(make_dsa_algorithm(NULL),
						      &md5_algorithm),
		 
		 -1);

  
459
460
461
462
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
463
464
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
465
466
467
468
469
470
471
472
473
474
475
			  make_int_list(1, ATOM_SSH_DSS, -1),
			  (preferred_crypto
			   ? make_int_list(1, preferred_crypto, -1)
			   : default_crypto_algorithms()),
			  (preferred_mac
			   ? make_int_list(1, preferred_mac, -1)
			   : default_mac_algorithms()),
			  (preferred_compression
			   ? make_int_list(1, preferred_compression, -1)
			   : default_compression_algorithms()),
			  make_int_list(0, -1));
476
477
  
  {
478
479
    /* Commands to be invoked on the connection */
    struct object_list *connection_hooks;
480
481
482

#if WITH_TCP_FORWARD
    if (forward_flag)
483
484
      connection_hooks = make_object_list
	(3,
Niels Möller's avatar
Niels Möller committed
485
	 make_tcpip_forward_hook(backend),
486
487
488
489
	 make_install_fix_global_request_handler
	 (ATOM_CANCEL_TCPIP_FORWARD, &tcpip_cancel_forward),
	 make_direct_tcpip_hook(backend),
	 -1);
490
491
    else
#endif
492
      connection_hooks = make_object_list(0, -1);
493
494
495
496
497
498
499
500
501
    {
      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,
502
#if WITH_SSH1_FALLBACK
503
				sshd1 ? make_ssh1_fallback (sshd1) :
504
#endif /* WITH_SSH1_FALLBACK */
505
506
507
508
509
				NULL),
	 make_offer_service
	 (make_alist
	  (1, ATOM_SSH_USERAUTH,
	   lshd_services(make_userauth_service
510
511
512
513
514
515
516
			 (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),
517
			  make_alist(1, ATOM_SSH_CONNECTION,
518
519
520
521
				     lshd_connection_service
				     (make_server_connection_service
				      (make_alist
				       (1
522
#if WITH_PTY_SUPPORT
523
					+1, ATOM_PTY_REQ, make_pty_handler()
524
#endif /* WITH_PTY_SUPPORT */
525
526
527
528
529
530
					, ATOM_SHELL,
					make_shell_handler(backend,
							   reaper),
					-1),
				       backend),
				      connection_hooks),
531
532
				     -1))),
	   -1)));
533
    
534
      CAST_SUBTYPE(command, server_listen, o);
535
    
Niels Möller's avatar
Niels Möller committed
536
537
      COMMAND_CALL(server_listen, local,
		   &discard_continuation, &default_exception_handler);
538
    }
539
  }
Niels Möller's avatar
Niels Möller committed
540
  
541
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
542
543
544

  return 0;
}