lshd.c 11.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 "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"
Niels Möller's avatar
Niels Möller committed
36
#include "password.h"
37
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
38
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
39
#include "server.h"
40
#include "server_keyexchange.h"
41
#include "sexp.h"
Niels Möller's avatar
Niels Möller committed
42
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
43
#include "userauth.h"
44
45
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
46
#include "compress.h"
47
#include "server_pty.h"
48

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

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

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

72
73
#define OPT_SSH1_FALLBACK -2

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

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

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

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

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
121
  if (! (e && sexp_check_type(e, "dsa", &i)))
122
    {
Niels Möller's avatar
Niels Möller committed
123
      werror("lshd: Unknown key type (only dsa is supported)\n");
124
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
      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);
157
	  struct signer *private;
158
159
160
	  	  
	  s = ssh_format("%n", x);
	  
161
162
163
164
	  private = MAKE_SIGNER(make_dsa_algorithm(closure->random),
				public->length, public->data,
				s->length, s->data);
	  assert(private);
165
	  lsh_string_free(s);
166
167
168
169
170
171
172
173
174
175
176
177
	  
	  /* 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
	  
178
179
180
181
182
183
	  verbose("lshd: Using (public) hostkey:\n"
		  "  p=%hn\n"
		  "  q=%hn\n"
		  "  g=%hn\n"
		  "  y=%hn\n",
		  p, q, g, y);
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
		  
	  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,
202
			 struct alist *keys,
203
204
205
206
207
			 struct randomness *r)
{
  int fd = open(name, O_RDONLY);
  if (fd < 0)
    {
208
      werror("lshd: Could not open %z (errno = %i): %z\n",
209
210
211
212
213
214
215
216
217
	     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
218

219
      handler->random = r;
220
      handler->keys = keys;
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
      
      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;
    }
}
237
238
239
240
241
242
243
244
245
246
247
248

/* GABA:
   (expr
     (name make_server_listen)
     (globals
       (log "&io_log_peer_command.super.super"))
     (params
       (listen object command)
       (handshake object command) )
     (expr (lambda (port) (handshake (log (listen port))))))
*/

Niels Möller's avatar
Niels Möller committed
249
250
int main(int argc, char **argv)
{
251
  char *host = NULL;  /* Interface to bind */
Niels Möller's avatar
Niels Möller committed
252
  char *port = "ssh";
Niels Möller's avatar
Niels Möller committed
253
  /* TODO: this should probably use sysconfdir */  
254
255
  char *hostkey = "/etc/lsh_host_key";

256
257
258
259
#if WITH_SSH1_FALLBACK
  char *sshd1 = NULL;
#endif
  
260
  struct alist *keys;
261
  
Niels Möller's avatar
Niels Möller committed
262
263
  int option;

264
265
266
  int preferred_crypto = 0;
  int preferred_compression = 0;
  int preferred_mac = 0;
267

268
  struct address_info *local;
269

Niels Möller's avatar
Niels Möller committed
270
271
  struct reap *reaper;
  
272
273
274
275
  struct randomness *r;
  struct diffie_hellman_method *dh;
  struct keyexchange_algorithm *kex;
  struct alist *algorithms;
Niels Möller's avatar
Niels Möller committed
276
  struct make_kexinit *make_kexinit;
277
  struct packet_handler *kexinit_handler;
278
279
280

  NEW(io_backend, backend);

Niels Möller's avatar
Niels Möller committed
281
282
283
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
284
285
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
286
287
288

  r = make_reasonably_random();
  dh = make_dh1(r);
Niels Möller's avatar
Niels Möller committed
289
  
290
  algorithms = many_algorithms(1,
Niels Möller's avatar
Niels Möller committed
291
			       ATOM_SSH_DSS, make_dsa_algorithm(r),
292
293
			       -1);

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

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

373
  /* Read the hostkey */
374
375
  keys = make_alist(0, -1);
  if (!read_host_key(hostkey, keys, r))
376
    {
Niels Möller's avatar
Niels Möller committed
377
      werror("lshd: Could not read hostkey.\n");
378
379
      return EXIT_FAILURE;
    }
380
381
382
  /* 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. */
383
384
385
386
387
388
389
390
391

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

#if 0
392
393
  if (!get_inaddr(&local, host, port, "tcp"))
    {
Niels Möller's avatar
Niels Möller committed
394
      werror("lshd: No such host or service.\n");
395
      return EXIT_FAILURE;
396
    }
397
398
#endif
  
Niels Möller's avatar
Niels Möller committed
399
#if 0
400
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
401
402
403
404
405
406
407
408
409
410
411
412
  {
    int option = LOG_PID | LOG_CONS;
    if (foreground_flag)
      {
	option |= LOG_PERROR;
      }
    openlog("lshd", option, LOG_DAEMON);
    syslog_flag = 1;
  }
#endif /* HAVE_SYSLOG */
#endif
 
413
  init_backend(backend);
Niels Möller's avatar
Niels Möller committed
414
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
415

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

418
419
420
421
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
422
423
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
424
425
426
427
428
429
430
431
432
433
434
			  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));
435
#if 0
436
437
438
  kexinit_handler = make_kexinit_handler
    (CONNECTION_SERVER,
     make_kexinit, algorithms,
Niels Möller's avatar
Niels Möller committed
439
440
441
442
     make_meta_service
     (make_alist
      (1, ATOM_SSH_USERAUTH,
       make_userauth_service
443
       (make_int_list(1, ATOM_PASSWORD, -1),
444
	make_alist(1, ATOM_PASSWORD,
Niels Möller's avatar
Niels Möller committed
445
446
447
		   make_unix_userauth
		   (make_alist(1,
			       ATOM_SSH_CONNECTION,
448
			       make_server_connection_service
Niels Möller's avatar
Niels Möller committed
449
			       (make_alist(0, -1),
450
451
452
453
454
				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),
455
456
					   -1),
				backend),
457
458
459
			       -1)),
		   -1)),
       -1)));
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
#endif
  
  {
    struct lsh_object *o = make_server_listen
      (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 */
			      NULL
			      ));
    
    CAST_SUBTYPE(command, server_listen, o);
    
Niels Möller's avatar
Niels Möller committed
479
    int res = COMMAND_CALL(server_listen, local, NULL);
480
481
482
483
484
485
486
487
488
489
490
    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
491
  if (!io_listen(backend, &local, 
Niels Möller's avatar
Niels Möller committed
492
493
		 make_server_callback(backend,
				      "lsh - a free ssh",
494
#if WITH_SSH1_FALLBACK
Niels Möller's avatar
Niels Möller committed
495
				      sshd1 ? make_ssh1_fallback (sshd1) :
496
#endif /* WITH_SSH1_FALLBACK */
Niels Möller's avatar
Niels Möller committed
497
498
499
500
				      NULL,
				      SSH_MAX_PACKET,
				      r, make_kexinit,
				      kexinit_handler)))
Niels Möller's avatar
Niels Möller committed
501
    {
Niels Möller's avatar
Niels Möller committed
502
503
      werror("lshd: listen() failed: (errno = %i): %z\n",
	     errno, strerror(errno));
Niels Möller's avatar
Niels Möller committed
504
505
      return 1;
    }
506
#endif
Niels Möller's avatar
Niels Möller committed
507
  
508
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
509
510
511

  return 0;
}