lshd.c 12 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 "charset.h"
31
#include "connection_commands.h"
32
33
#include "crypto.h"
#include "format.h"
Niels Möller's avatar
Niels Möller committed
34
#include "io.h"
35
#include "io_commands.h"
36
37
#include "server_password.h"
#include "server_session.h"
38
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
39
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
40
#include "server.h"
41
#include "server_keyexchange.h"
42
#include "sexp.h"
Niels Möller's avatar
Niels Möller committed
43
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
44
#include "userauth.h"
45
46
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
47
#include "compress.h"
48
#include "server_pty.h"
49

50
51
52
53
54
55
56
57
58
59
60
61
62
63
#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>
64
#if HAVE_UNISTD_H
65
#include <unistd.h>
66
#endif
67

68
/* Block size for stdout and stderr buffers */
Niels Möller's avatar
Niels Möller committed
69
70
#define BLOCK_SIZE 32768

Niels Möller's avatar
Niels Möller committed
71
void usage(void) NORETURN;
Niels Möller's avatar
Niels Möller committed
72

73
74
#define OPT_SSH1_FALLBACK -2

Niels Möller's avatar
Niels Möller committed
75
void usage(void)
Niels Möller's avatar
Niels Möller committed
76
{
77
  werror("lshd [options]\n"
78
79
80
	 " -p,  --port=PORT\n"
	 " -h,  --hostkey=KEYFILE\n"
	 " -c,  --crypto=ALGORITHM\n"
81
	 " -z,  --compression[=ALGORITHM]\n"
82
83
	 "      --mac=ALGORITHM\n"
	 " -q,  --quiet\n"
84
85
86
#if WITH_SSH1_FALLBACK
         "      --ssh1-fallback=SSHD\n"
#endif
87
88
	 " -v,  --verbose\n"
	 "      --debug\n");
Niels Möller's avatar
Niels Möller committed
89
90
91
  exit(1);
}

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

93
94
/* FIXME: We should have some more general functions for reading
 * private keys. */
95

96
/* GABA:
97
98
99
100
101
   (class
     (name read_key)
     (super sexp_handler)
     (vars
       (random object randomness)
102
103
       ;; Maps hostkey algorithm to a keyinfo structure
       (keys object alist)))
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
*/

static int do_read_key(struct sexp_handler *h, struct sexp *private)
{
  CAST(read_key, closure, h);
  struct sexp_iterator *i;
  struct sexp *e;
  mpz_t p, q, g, y, x;

  int res;
  
  if (!sexp_check_type(private, "private-key", &i))
    {
      werror("lshd: Host key file does not contain a private key.");
      return LSH_FAIL | LSH_DIE;
    }

  e = SEXP_GET(i);
Niels Möller's avatar
Niels Möller committed
122
  if (! (e && sexp_check_type(e, "dsa", &i)))
123
    {
Niels Möller's avatar
Niels Möller committed
124
      werror("lshd: Unknown key type (only dsa is supported)\n");
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
      return LSH_FAIL | LSH_DIE;
    }

  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);

	  res = LSH_FAIL | LSH_DIE;
	}
      else
	{
	  struct lsh_string *public
	    = ssh_format("%a%n%n%n%n", ATOM_SSH_DSS, p, q, g, y);
158
	  struct signer *private;
159
160
161
	  	  
	  s = ssh_format("%n", x);
	  
162
163
164
165
	  private = MAKE_SIGNER(make_dsa_algorithm(closure->random),
				public->length, public->data,
				s->length, s->data);
	  assert(private);
166
	  lsh_string_free(s);
167
168
169
170
171
172
173
174
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,
		    make_keypair_info(public, private));

#if DATAFELLOWS_SSH2_SSH_DSA_KLUDGE
	  ALIST_SET(closure->keys, ATOM_SSH_DSS_KLUDGE,
		    make_keypair_info(public,
				      make_dsa_signer_kludge(private)));
#endif
	  
179
180
181
182
183
184
	  verbose("lshd: Using (public) hostkey:\n"
		  "  p=%hn\n"
		  "  q=%hn\n"
		  "  g=%hn\n"
		  "  y=%hn\n",
		  p, q, g, y);
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
		  
	  res = LSH_OK | LSH_CLOSE;
	}
    }
  else
    res = LSH_FAIL | LSH_DIE;

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

  return res;
}

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

220
      handler->random = r;
221
      handler->keys = keys;
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
      
      res = blocking_read(fd, make_read_sexp(&handler->super,
					     2000, SEXP_TRANSPORT, 0));
      close(fd);

      KILL(handler);
      
      if (LSH_FAILUREP(res))
	{
	  werror("lshd: Invalid host key.\n");
	  return 0;
	}

      return 1;
    }
}
238

239
/* Invoked when the client requests the userauth service. */
240
241
/* GABA:
   (expr
242
243
244
245
246
247
248
249
250
251
252
     (name lshd_services)
     (params 
       (userauth object command) )
     (expr
       (lambda (connection)
         ((userauth connection) connection))))
*/

/* GABA:
   (expr
     (name lshd_listen)
253
254
255
256
     (globals
       (log "&io_log_peer_command.super.super"))
     (params
       (listen object command)
257
258
259
260
       (handshake object command)
       (services object command) )
     (expr (lambda (port)
             (services (handshake (log (listen port)))))))
261
262
*/

Niels Möller's avatar
Niels Möller committed
263
264
int main(int argc, char **argv)
{
265
  char *host = NULL;  /* Interface to bind */
Niels Möller's avatar
Niels Möller committed
266
  char *port = "ssh";
Niels Möller's avatar
Niels Möller committed
267
  /* TODO: this should probably use sysconfdir */  
268
269
  char *hostkey = "/etc/lsh_host_key";

270
271
272
273
#if WITH_SSH1_FALLBACK
  char *sshd1 = NULL;
#endif
  
274
  struct alist *keys;
275
  
Niels Möller's avatar
Niels Möller committed
276
277
  int option;

278
279
280
  int preferred_crypto = 0;
  int preferred_compression = 0;
  int preferred_mac = 0;
281

282
  struct address_info *local;
283

Niels Möller's avatar
Niels Möller committed
284
285
  struct reap *reaper;
  
286
287
288
289
  struct randomness *r;
  struct diffie_hellman_method *dh;
  struct keyexchange_algorithm *kex;
  struct alist *algorithms;
Niels Möller's avatar
Niels Möller committed
290
  struct make_kexinit *make_kexinit;
291
292
293

  NEW(io_backend, backend);

Niels Möller's avatar
Niels Möller committed
294
295
296
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
297
298
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
299
300
301

  r = make_reasonably_random();
  dh = make_dh1(r);
Niels Möller's avatar
Niels Möller committed
302
  
303
  algorithms = many_algorithms(1,
Niels Möller's avatar
Niels Möller committed
304
			       ATOM_SSH_DSS, make_dsa_algorithm(r),
305
306
			       -1);

307
  for (;;)
308
309
    {
      static struct option options[] =
Niels Möller's avatar
Niels Möller committed
310
      {
311
312
313
314
315
316
317
	{ "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' },
318
	{ "hostkey", required_argument, NULL, 'h' },
319
320
321
#if WITH_SSH1_FALLBACK
	{ "ssh1-fallback", optional_argument, NULL, OPT_SSH1_FALLBACK},
#endif
322
323
324
	{ NULL }
      };
      
325
      option = getopt_long(argc, argv, "c:h:p:qvz::", options, NULL);
326
327
328
329
330
331
      switch(option)
	{
	case -1:
	  goto options_done;
	case 0:
	  break;
332
333
334
335
336
#if WITH_SSH1_FALLBACK
	case OPT_SSH1_FALLBACK:
	  sshd1 = optarg ? optarg : SSHD1;
	  break;
#endif
337
338
339
340
341
342
343
344
345
	case 'p':
	  port = optarg;
	  break;
	case 'q':
	  quiet_flag = 1;
	  break;
	case 'v':
	  verbose_flag = 1;
	  break;
346
347
348
	case 'h':
	  hostkey = optarg;
	  break;
349
350
351
352
	case 'c':
	  preferred_crypto = lookup_crypto(algorithms, optarg);
	  if (!preferred_crypto)
	    {
353
	      werror("lsh: Unknown crypto algorithm '%z'.\n", optarg);
354
355
356
357
358
359
360
361
362
363
	      exit(1);
	    }
	  break;
	case 'z':
	  if (!optarg)
	    optarg = "zlib";
	
	  preferred_compression = lookup_compression(algorithms, optarg);
	  if (!preferred_compression)
	    {
364
	      werror("lsh: Unknown compression algorithm '%z'.\n", optarg);
365
366
367
368
369
370
371
	      exit(1);
	    }
	  break;
	case 'm':
	  preferred_mac = lookup_mac(algorithms, optarg);
	  if (!preferred_mac)
	    {
372
	      werror("lsh: Unknown message authentication algorithm '%z'.\n",
373
374
375
376
377
378
379
380
381
		      optarg);
	      exit(1);
	    }
	    
	case '?':
	  usage();
	}
    }
 options_done:
Niels Möller's avatar
Niels Möller committed
382
383
384
385

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

386
  /* Read the hostkey */
387
388
  keys = make_alist(0, -1);
  if (!read_host_key(hostkey, keys, r))
389
    {
Niels Möller's avatar
Niels Möller committed
390
      werror("lshd: Could not read hostkey.\n");
391
392
      return EXIT_FAILURE;
    }
393
394
395
  /* 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. */
396
397
398
399
400
401
402
403
404

  local = make_address_info_c(host, port);
  if (!local)
    {
      werror("lshd: Invalid port or service\n");
      exit (EXIT_FAILURE);
    }

#if 0
405
406
  if (!get_inaddr(&local, host, port, "tcp"))
    {
Niels Möller's avatar
Niels Möller committed
407
      werror("lshd: No such host or service.\n");
408
      return EXIT_FAILURE;
409
    }
410
411
#endif
  
Niels Möller's avatar
Niels Möller committed
412
#if 0
413
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
414
415
416
417
418
419
420
421
422
423
424
425
  {
    int option = LOG_PID | LOG_CONS;
    if (foreground_flag)
      {
	option |= LOG_PERROR;
      }
    openlog("lshd", option, LOG_DAEMON);
    syslog_flag = 1;
  }
#endif /* HAVE_SYSLOG */
#endif
 
426
  init_backend(backend);
Niels Möller's avatar
Niels Möller committed
427
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
428

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

431
432
433
434
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
435
436
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
437
438
439
440
441
442
443
444
445
446
447
			  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));
448
#if 0
449
450
451
  kexinit_handler = make_kexinit_handler
    (CONNECTION_SERVER,
     make_kexinit, algorithms,
Niels Möller's avatar
Niels Möller committed
452
453
454
455
     make_meta_service
     (make_alist
      (1, ATOM_SSH_USERAUTH,
       make_userauth_service
456
       (make_int_list(1, ATOM_PASSWORD, -1),
457
	make_alist(1, ATOM_PASSWORD,
Niels Möller's avatar
Niels Möller committed
458
459
460
		   make_unix_userauth
		   (make_alist(1,
			       ATOM_SSH_CONNECTION,
461
			       make_server_connection_service
Niels Möller's avatar
Niels Möller committed
462
			       (make_alist(0, -1),
463
464
465
466
467
				make_alist(1
#if WITH_PTY_SUPPORT
					   +1, ATOM_PTY_REQ, make_pty_handler()
#endif /* WITH_PTY_SUPPORT */
					   , ATOM_SHELL, make_shell_handler(backend, reaper),
468
469
					   -1),
				backend),
470
471
472
			       -1)),
		   -1)),
       -1)));
473
474
475
#endif
  
  {
476
    struct lsh_object *o = lshd_listen
477
478
479
480
481
482
483
484
485
486
      (make_simple_listen(backend, NULL),
       make_handshake_command(CONNECTION_SERVER,
			      "lsh - a free ssh",
			      SSH_MAX_PACKET,
			      r,
			      algorithms,
			      make_kexinit,
#if WITH_SSH1_FALLBACK
			      sshd1 ? make_ssh1_fallback (sshd1) :
#endif /* WITH_SSH1_FALLBACK */
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
			      NULL),
       make_offer_service
       (make_alist
	(1, ATOM_SSH_USERAUTH,
	 lshd_services(make_userauth_service
		       (make_int_list(1, ATOM_PASSWORD, -1),
			make_alist(1, ATOM_PASSWORD,
				   &unix_userauth.super, -1),
			make_alist(1, ATOM_SSH_CONNECTION,
				   make_server_connection_service
				   (make_alist(0, -1),
				    make_alist(1,
					       ATOM_SHELL,
					       make_shell_handler(backend,
								  reaper),
					       -1),
				    backend),
				   -1))),
	 -1)));
506
507
508
    
    CAST_SUBTYPE(command, server_listen, o);
    
Niels Möller's avatar
Niels Möller committed
509
    int res = COMMAND_CALL(server_listen, local, NULL);
510
511
512
513
514
515
516
517
518
519
520
    if (res)
      {
	if (res & LSH_COMMAND_FAILED)
	    werror("lshd: Failed to bind port. (errno = %d) %z\n",
		   errno, strerror(errno));
	else
	  werror("lshd: Unexpected failure from listen: %d\n", res);
	return EXIT_FAILURE;
      }
  }
#if 0
521
  if (!io_listen(backend, &local, 
Niels Möller's avatar
Niels Möller committed
522
523
		 make_server_callback(backend,
				      "lsh - a free ssh",
524
#if WITH_SSH1_FALLBACK
Niels Möller's avatar
Niels Möller committed
525
				      sshd1 ? make_ssh1_fallback (sshd1) :
526
#endif /* WITH_SSH1_FALLBACK */
Niels Möller's avatar
Niels Möller committed
527
528
529
530
				      NULL,
				      SSH_MAX_PACKET,
				      r, make_kexinit,
				      kexinit_handler)))
Niels Möller's avatar
Niels Möller committed
531
    {
Niels Möller's avatar
Niels Möller committed
532
533
      werror("lshd: listen() failed: (errno = %i): %z\n",
	     errno, strerror(errno));
Niels Möller's avatar
Niels Möller committed
534
535
      return 1;
    }
536
#endif
Niels Möller's avatar
Niels Möller committed
537
  
538
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
539
540
541

  return 0;
}