lshd.c 10.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
23
24
/* 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
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
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
32
#include "crypto.h"
#include "format.h"
Niels Möller's avatar
Niels Möller committed
33
#include "io.h"
Niels Möller's avatar
Niels Möller committed
34
#include "password.h"
35
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
36
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
37
#include "server.h"
38
#include "server_keyexchange.h"
39
#include "sexp.h"
Niels Möller's avatar
Niels Möller committed
40
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
41
#include "userauth.h"
42
43
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
44
#include "compress.h"
45
#include "server_pty.h"
46

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

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

Niels Möller's avatar
Niels Möller committed
68
void usage(void) NORETURN;
Niels Möller's avatar
Niels Möller committed
69

70
71
#define OPT_SSH1_FALLBACK -2

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

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

90
91
/* FIXME: We should have some more general functions for reading
 * private keys. */
92
93
94
95
96
97
98

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

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

217
      handler->random = r;
218
      handler->keys = keys;
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
      
      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;
    }
}
  
Niels Möller's avatar
Niels Möller committed
236
237
int main(int argc, char **argv)
{
238
  char *host = NULL;  /* Interface to bind */
Niels Möller's avatar
Niels Möller committed
239
  char *port = "ssh";
Niels Möller's avatar
Niels Möller committed
240
  /* TODO: this should probably use sysconfdir */  
241
242
  char *hostkey = "/etc/lsh_host_key";

243
244
245
246
#if WITH_SSH1_FALLBACK
  char *sshd1 = NULL;
#endif
  
247
  struct alist *keys;
248
  
Niels Möller's avatar
Niels Möller committed
249
250
  int option;

251
252
253
  int preferred_crypto = 0;
  int preferred_compression = 0;
  int preferred_mac = 0;
254

255
  struct sockaddr_in local;
256

Niels Möller's avatar
Niels Möller committed
257
258
  struct reap *reaper;
  
259
260
261
262
  struct randomness *r;
  struct diffie_hellman_method *dh;
  struct keyexchange_algorithm *kex;
  struct alist *algorithms;
Niels Möller's avatar
Niels Möller committed
263
  struct make_kexinit *make_kexinit;
264
  struct packet_handler *kexinit_handler;
265
266
267

  NEW(io_backend, backend);

Niels Möller's avatar
Niels Möller committed
268
269
270
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
271
272
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
273
274
275

  r = make_reasonably_random();
  dh = make_dh1(r);
Niels Möller's avatar
Niels Möller committed
276
  
277
  algorithms = many_algorithms(1,
Niels Möller's avatar
Niels Möller committed
278
			       ATOM_SSH_DSS, make_dsa_algorithm(r),
279
280
			       -1);

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

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

360
  /* Read the hostkey */
361
362
  keys = make_alist(0, -1);
  if (!read_host_key(hostkey, keys, r))
363
    {
Niels Möller's avatar
Niels Möller committed
364
      werror("lshd: Could not read hostkey.\n");
365
366
      return EXIT_FAILURE;
    }
367
368
369
370
  /* 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. */
  
371
372
  if (!get_inaddr(&local, host, port, "tcp"))
    {
Niels Möller's avatar
Niels Möller committed
373
      werror("lshd: No such host or service.\n");
374
      return EXIT_FAILURE;
375
376
    }

Niels Möller's avatar
Niels Möller committed
377
#if 0
378
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
379
380
381
382
383
384
385
386
387
388
389
390
  {
    int option = LOG_PID | LOG_CONS;
    if (foreground_flag)
      {
	option |= LOG_PERROR;
      }
    openlog("lshd", option, LOG_DAEMON);
    syslog_flag = 1;
  }
#endif /* HAVE_SYSLOG */
#endif
 
391
  init_backend(backend);
Niels Möller's avatar
Niels Möller committed
392
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
393

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

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, -1),
			  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));
412
413
414
415

  kexinit_handler = make_kexinit_handler
    (CONNECTION_SERVER,
     make_kexinit, algorithms,
Niels Möller's avatar
Niels Möller committed
416
417
418
419
     make_meta_service
     (make_alist
      (1, ATOM_SSH_USERAUTH,
       make_userauth_service
420
       (make_int_list(1, ATOM_PASSWORD, -1),
421
	make_alist(1, ATOM_PASSWORD,
Niels Möller's avatar
Niels Möller committed
422
423
424
		   make_unix_userauth
		   (make_alist(1,
			       ATOM_SSH_CONNECTION,
425
			       make_server_connection_service
Niels Möller's avatar
Niels Möller committed
426
			       (make_alist(0, -1),
427
428
429
430
431
				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),
432
433
					   -1),
				backend),
434
435
436
			       -1)),
		   -1)),
       -1)));
Niels Möller's avatar
Niels Möller committed
437
     
438
  if (!io_listen(backend, &local, 
Niels Möller's avatar
Niels Möller committed
439
440
		 make_server_callback(backend,
				      "lsh - a free ssh",
441
#if WITH_SSH1_FALLBACK
Niels Möller's avatar
Niels Möller committed
442
				      sshd1 ? make_ssh1_fallback (sshd1) :
443
#endif /* WITH_SSH1_FALLBACK */
Niels Möller's avatar
Niels Möller committed
444
445
446
447
				      NULL,
				      SSH_MAX_PACKET,
				      r, make_kexinit,
				      kexinit_handler)))
Niels Möller's avatar
Niels Möller committed
448
    {
Niels Möller's avatar
Niels Möller committed
449
450
      werror("lshd: listen() failed: (errno = %i): %z\n",
	     errno, strerror(errno));
Niels Möller's avatar
Niels Möller committed
451
452
      return 1;
    }
Niels Möller's avatar
Niels Möller committed
453
  
454
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
455
456
457

  return 0;
}