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
46
47
#include "server_password.h"
#include "server_pty.h"
#include "server_publickey.h"
#include "server_session.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
58
59
60
61
62
63
64
65
66
67
68
69
70
#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>
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

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

80
81
#define OPT_SSH1_FALLBACK -2

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

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

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

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

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

  e = SEXP_GET(i);
Niels Möller's avatar
Niels Möller committed
133
  if (! (e && sexp_check_type(e, "dsa", &i)))
134
    {
Niels Möller's avatar
Niels Möller committed
135
      werror("lshd: Unknown key type (only dsa is supported)\n");
136
      return;
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
166
    }

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

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

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

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

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

      KILL(handler);
      
      return 1;
    }
}
236

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

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

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

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

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

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

301
  struct address_info *local;
302

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

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

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

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

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

409
  /* Read the hostkey */
410
411
  keys = make_alist(0, -1);
  if (!read_host_key(hostkey, keys, r))
412
    {
Niels Möller's avatar
Niels Möller committed
413
      werror("lshd: Could not read hostkey.\n");
414
415
      return EXIT_FAILURE;
    }
416
417
418
  /* 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. */
419
420
421
422
423
424
425
426

  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
427
#if 0
428
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
429
430
431
432
433
434
435
436
437
438
439
440
  {
    int option = LOG_PID | LOG_CONS;
    if (foreground_flag)
      {
	option |= LOG_PERROR;
      }
    openlog("lshd", option, LOG_DAEMON);
    syslog_flag = 1;
  }
#endif /* HAVE_SYSLOG */
#endif
 
441
  init_backend(backend);
Niels Möller's avatar
Niels Möller committed
442
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
443

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

446
447
448
449
450
451
452
453
454
455
456
457
458
459
  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);

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

  make_kexinit
    = make_simple_kexinit(r,
464
465
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
466
467
468
469
470
471
472
473
474
475
476
			  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));
477
478
  
  {
479
480
    /* Commands to be invoked on the connection */
    struct object_list *connection_hooks;
481
482
483

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

  return 0;
}