lshd.c 10.9 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
	     name, errno, STRERROR(errno));
211
212
213
214
215
216
217
218
      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

  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
404
#if 0
405
#if HAVE_SYSLOG
Niels Möller's avatar
Niels Möller committed
406
407
408
409
410
411
412
413
414
415
416
417
  {
    int option = LOG_PID | LOG_CONS;
    if (foreground_flag)
      {
	option |= LOG_PERROR;
      }
    openlog("lshd", option, LOG_DAEMON);
    syslog_flag = 1;
  }
#endif /* HAVE_SYSLOG */
#endif
 
418
  init_backend(backend);
Niels Möller's avatar
Niels Möller committed
419
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
420

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

423
424
425
426
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
427
428
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
429
430
431
432
433
434
435
436
437
438
439
			  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));
440
441
  
  {
442
    struct lsh_object *o = lshd_listen
443
444
445
446
447
448
449
450
451
452
      (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 */
453
454
455
456
457
458
459
460
461
462
463
			      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),
464
465
466
467
468
469
470
471
472
				    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),
				     -1),
473
474
475
				    backend),
				   -1))),
	 -1)));
476
477
478
    
    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
    if (res)
      {
	if (res & LSH_COMMAND_FAILED)
	    werror("lshd: Failed to bind port. (errno = %d) %z\n",
484
		   errno, STRERROR(errno));
485
486
487
488
489
	else
	  werror("lshd: Unexpected failure from listen: %d\n", res);
	return EXIT_FAILURE;
      }
  }
Niels Möller's avatar
Niels Möller committed
490
  
491
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
492
493
494

  return 0;
}