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
#include "crypto.h"
35
#include "daemon.h"
36
#include "format.h"
Niels Möller's avatar
Niels Möller committed
37
#include "io.h"
38
#include "io_commands.h"
39
#include "lookup_verifier.h"
40
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
41
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
42
#include "server.h"
43
#include "server_authorization.h"
44
#include "server_keyexchange.h"
45
46
#include "server_pty.h"
#include "server_session.h"
47
#include "server_userauth.h"
48
#include "sexp.h"
Balázs Scheidler's avatar
Balázs Scheidler committed
49
50
#include "sexp_commands.h"
#include "spki.h"
Niels Möller's avatar
Niels Möller committed
51
#include "ssh.h"
52
53
#include "tcpforward.h"
#include "tcpforward_commands.h"
54
#include "tcpforward_commands.h"
Niels Möller's avatar
Niels Möller committed
55
#include "userauth.h"
56
57
58
#include "werror.h"
#include "xalloc.h"

59
#include "lsh_argp.h"
60
61
62
63
64
65
66
67
68
69
70
71
72

#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>
73
#if HAVE_UNISTD_H
74
#include <unistd.h>
75
#endif
76

77
/* Block size for stdout and stderr buffers */
Niels Möller's avatar
Niels Möller committed
78
79
#define BLOCK_SIZE 32768

80
81
82

/* Option parsing */

83
#define OPT_NO 0x400
84
85
86
#define OPT_SSH1_FALLBACK 0x200
#define OPT_INTERFACE 0x201
#define OPT_TCPIP_FORWARD 0x202
87
88
89
90
91
92
#define OPT_NO_TCPIP_FORWARD (OPT_TCPIP_FORWARD | OPT_NO)
#define OPT_DAEMONIC 0x203
#define OPT_NO_DAEMONIC (OPT_DAEMONIC | OPT_NO)
#define OPT_PIDFILE 0x204
#define OPT_NO_PIDFILE (OPT_PIDFILE | OPT_NO)
#define OPT_CORE 0x207
93
94
95
96
97
98
99
100
101
102
103
104

/* GABA:
   (class
     (name lshd_options)
     (super algorithms_options)
     (vars
       (style . sexp_argp_state)
       (interface . "char *")
       (port . "char *")
       (hostkey . "char *")
       (local object address_info)
       (with_tcpip_forward . int)
105
106
107
108
109
110
       (sshd1 object ssh1_fallback)
       (daemonic . int)
       (corefile . int)
       (pid_file . "const char *")
       ; -1 means use pid file iff we're in daemonic mode
       (use_pid_file . int)))
111
112
*/

Niels Möller's avatar
Niels Möller committed
113
114
static struct lshd_options *
make_lshd_options(struct alist *algorithms)
115
{
Niels Möller's avatar
Niels Möller committed
116
  NEW(lshd_options, self);
117
118
119
120
121
122
123
124
125
126
127
128
129

  init_algorithms_options(&self->super, algorithms);
  
  self->style = SEXP_TRANSPORT;
  self->interface = NULL;
  self->port = "ssh";
  /* FIXME: this should perhaps use sysconfdir */  
  self->hostkey = "/etc/lsh_host_key";
  self->local = NULL;

  self->with_tcpip_forward = 1;
  
  self->sshd1 = NULL;
130
131
132
133
134
135
  self->daemonic = 0;

  /* FIXME: Make the default a configure time option? */
  self->pid_file = "/var/run/lshd.pid";
  self->use_pid_file = -1;
  self->corefile = 0;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  
  return self;
}

static const struct argp_option
main_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
  { "interface", OPT_INTERFACE, "interface", 0,
    "Listen on this network interface", 0 }, 
  { "port", 'p', "Port", 0, "Listen on this port.", 0 },
  { "host-key", 'h', "Key file", 0, "Location of the server's private key.", 0},

#if WITH_SSH1_FALLBACK
  { "ssh1-fallback", OPT_SSH1_FALLBACK, "File name", OPTION_ARG_OPTIONAL,
    "Location of the sshd1 program, for falling back to version 1 of the Secure Shell protocol.", 0 },
#endif /* WITH_SSH1_FALLBACK */

#if WITH_TCP_FORWARD
  { "tcp-forward", OPT_TCPIP_FORWARD, NULL, 0, "Enable tcpip forwarding (default).", 0 },
  { "no-tcp-forward", OPT_NO_TCPIP_FORWARD, NULL, 0, "Disable tcpip forwarding.", 0 },
#endif /* WITH_TCP_FORWARD */

159
160
  { NULL, 0, NULL, 0, "Daemonic behaviour", 0 },
  { "daemonic", OPT_DAEMONIC, NULL, 0, "Run in the background, redirect stdio to /dev/null, and chdir to /.", 0 },
161
  { "no-daemonic", OPT_NO_DAEMONIC, NULL, 0, "Run in the foreground, with messages to stderr (default).", 0 },
162
163
  { "pid-file", OPT_PIDFILE, "file name", 0, "Create a pid file. When running in daemonic mode, "
    "the default is /var/run/lshd.pid.", 0 },
164
  { "no-pid-file", OPT_NO_PIDFILE, NULL, 0, "Don't use any pid file. Default in non-daemonic mode.", 0 },
165
166
  { "enable-core", OPT_CORE, NULL, 0, "Dump core on fatal errors (disabled by default).", 0 },
    
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  { NULL, 0, NULL, 0, NULL, 0 }
};

static const struct argp_child
main_argp_children[] =
{
  { &sexp_input_argp, 0, "", 0 },
  { &algorithms_argp, 0, "", 0 },
  { &werror_argp, 0, "", 0 },
  { NULL, 0, NULL, 0}
};

static error_t
main_argp_parser(int key, char *arg, struct argp_state *state)
{
  CAST(lshd_options, self, state->input);
  
  switch(key)
    {
    default:
      return ARGP_ERR_UNKNOWN;
    case ARGP_KEY_INIT:
      state->child_inputs[0] = &self->style;
      state->child_inputs[1] = &self->super;
191
      state->child_inputs[2] = NULL;
192
193
194
195
196
197
198
199
200
201
202
      break;
    case ARGP_KEY_ARG:
      argp_error(state, "Spurious arguments.");
      break;
      
    case ARGP_KEY_END:
      self->local = make_address_info_c(self->interface, self->port);
      if (!self->local)
	argp_error(state, "Invalid interface, port or service, %s:%s'.",
		   self->interface ? self->interface : "ANY",
		   self->port);
203
204
205
      if (self->use_pid_file < 0)
	self->use_pid_file = self->daemonic;
      
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
      break;
      
    case 'p':
      self->port = arg;
      break;

    case 'h':
      self->hostkey = arg;
      break;

    case OPT_INTERFACE:
      self->interface = arg;
      break;
 
#if WITH_SSH1_FALLBACK
    case OPT_SSH1_FALLBACK:
      self->sshd1 = make_ssh1_fallback(arg ? arg : SSHD1);
      break;
#endif
Niels Möller's avatar
Niels Möller committed
225

226
227
228
229
230
231
232
    case OPT_TCPIP_FORWARD:
      self->with_tcpip_forward = 1;
      break;

    case OPT_NO_TCPIP_FORWARD:
      self->with_tcpip_forward = 0;
      break;
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

    case OPT_DAEMONIC:
      self->daemonic = 1;
      break;

    case OPT_NO_DAEMONIC:
      self->daemonic = 0;
      break;

    case OPT_PIDFILE:
      self->pid_file = arg;
      self->use_pid_file = 1;
      break;

    case OPT_NO_PIDFILE:
      self->use_pid_file = 0;
      break;

    case OPT_CORE:
      self->corefile = 1;
      break;
254
255
256
    }
  return 0;
}
Niels Möller's avatar
Niels Möller committed
257

Niels Möller's avatar
Niels Möller committed
258
259
260
261
262
263
static const struct argp
main_argp =
{ main_options, main_argp_parser, 
  NULL,
  "Server for the ssh-2 protocol.",
  main_argp_children,
264
  NULL, NULL
Niels Möller's avatar
Niels Möller committed
265
266
};

267
268
269
270
271
272
273
274
275
276
277
278
279
/* 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)))))))
*/

280
/* Invoked when the client requests the userauth service. */
281
282
/* GABA:
   (expr
283
284
     (name lshd_services)
     (params 
285
       (userauth object command))
286
287
288
289
290
     (expr
       (lambda (connection)
         ((userauth connection) connection))))
*/

291
/* Invoked when starting the ssh-connection service */
292
293
/* GABA:
   (expr
294
     (name lshd_connection_service)
295
     (globals
296
297
       (progn "&progn_command.super.super")
       (init "&connection_service.super"))
298
     (params
299
300
301
302
       (login object command)     
       (hooks object object_list))
     (expr
       (lambda (user connection)
303
304
305
306
         ((progn hooks) (login user
	                       ; We have to initialize the connection
			       ; before logging in.
	                       (init connection))))))
307
308
*/

Niels Möller's avatar
Niels Möller committed
309
310
int main(int argc, char **argv)
{
311
  struct lshd_options *options;
312
  
313
  struct alist *keys;
314
  
Niels Möller's avatar
Niels Möller committed
315
316
  struct reap *reaper;
  
317
318
319
320
  struct randomness *r;
  struct diffie_hellman_method *dh;
  struct keyexchange_algorithm *kex;
  struct alist *algorithms;
Niels Möller's avatar
Niels Möller committed
321
  struct make_kexinit *make_kexinit;
322
  struct alist *authorization_lookup;
Balázs Scheidler's avatar
Balázs Scheidler committed
323
  struct keypair *hostkey;
324
  
325
  /* FIXME: Why not allocate backend statically? */
326
  NEW(io_backend, backend);
327
  init_backend(backend);
328

Niels Möller's avatar
Niels Möller committed
329
330
331
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
332
333
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
334
335
336

  r = make_reasonably_random();
  dh = make_dh1(r);
Niels Möller's avatar
Niels Möller committed
337
  
338
  algorithms = many_algorithms(1,
Niels Möller's avatar
Niels Möller committed
339
			       ATOM_SSH_DSS, make_dsa_algorithm(r),
340
			       -1);
341
342
  options = make_lshd_options(algorithms);
  
Niels Möller's avatar
Niels Möller committed
343
  argp_parse(&main_argp, argc, argv, 0, NULL, options);
344

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  if (!options->corefile && !daemon_disable_core())
    {
      werror("Disabling of core dumps failed.\n");
      return EXIT_FAILURE;
    }
  
  if (options->daemonic)
    {
#if HAVE_SYSLOG
      set_error_syslog("lshd");
#else /* !HAVE_SYSLOG */
      werror("lshd: No syslog. Further messages will be directed to /dev/null.\n");
#endif /* !HAVE_SYSLOG */
    }

  if (options->daemonic)
    switch (daemon_init())
      {
      case 0:
	werror("lshd: Spawning into background failed.\n");
	return EXIT_FAILURE;
      case DAEMON_INETD:
	werror("lshd: spawning from inetd not yet supported.\n");
	return EXIT_FAILURE;
      case DAEMON_INIT:
      case DAEMON_NORMAL:
	break;
      default:
	fatal("Internal error\n");
      }
375
376
377
378
379
380
  
  if (options->use_pid_file && !daemon_pidfile(options->pid_file))
    {
      werror("lshd seems to be running already.\n");
      return EXIT_FAILURE;
    }
381
      
382
  /* Read the hostkey */
383
  keys = make_alist(0, -1);
384
385
386
  if (!(hostkey = read_spki_key_file(options->hostkey,
				     make_alist(1, ATOM_DSA, make_dsa_algorithm(r), -1),
				     &ignore_exception_handler)))
387
    {
Niels Möller's avatar
Niels Möller committed
388
      werror("lshd: Could not read hostkey.\n");
389
390
      return EXIT_FAILURE;
    }
Balázs Scheidler's avatar
Balázs Scheidler committed
391
  ALIST_SET(keys, hostkey->type, hostkey);
392
393

  /* FIXME: We should check that we have at least one host key.
394
395
   * We should also extract the host-key algorithms for which we have keys,
   * instead of hardcoding ssh-dss below. */
Niels Möller's avatar
Niels Möller committed
396
 
Niels Möller's avatar
Niels Möller committed
397
  reaper = make_reaper();
Niels Möller's avatar
Niels Möller committed
398

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

401
  authorization_lookup
Niels Möller's avatar
Niels Möller committed
402
    = make_alist(1,
403
		 ATOM_SSH_DSS, make_authorization_db(ssh_format("authorized_keys_sha1"),
404
						     /* make_dsa_algorithm(NULL), */
405
						     &sha1_algorithm),
406
407
408
409
		 
		 -1);

  
410
411
412
413
  ALIST_SET(algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1, kex);

  make_kexinit
    = make_simple_kexinit(r,
414
415
			  make_int_list(1, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
					-1),
416
			  make_int_list(1, ATOM_SSH_DSS, -1),
Niels Möller's avatar
Niels Möller committed
417
418
419
			  options->super.crypto_algorithms,
			  options->super.mac_algorithms,
			  options->super.compression_algorithms,
420
			  make_int_list(0, -1));
421
422
  
  {
423
424
    /* Commands to be invoked on the connection */
    struct object_list *connection_hooks;
425
426

#if WITH_TCP_FORWARD
427
    if (options->with_tcpip_forward)
428
429
      connection_hooks = make_object_list
	(3,
Niels Möller's avatar
Niels Möller committed
430
	 make_tcpip_forward_hook(backend),
431
432
433
434
	 make_install_fix_global_request_handler
	 (ATOM_CANCEL_TCPIP_FORWARD, &tcpip_cancel_forward),
	 make_direct_tcpip_hook(backend),
	 -1);
435
436
    else
#endif
437
      connection_hooks = make_object_list(0, -1);
438
439
440
441
442
443
444
445
446
    {
      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,
447
				options->sshd1),
448
449
450
451
	 make_offer_service
	 (make_alist
	  (1, ATOM_SSH_USERAUTH,
	   lshd_services(make_userauth_service
452
453
454
455
456
457
458
			 (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),
459
			  make_alist(1, ATOM_SSH_CONNECTION,
460
461
462
463
				     lshd_connection_service
				     (make_server_connection_service
				      (make_alist
				       (1
464
#if WITH_PTY_SUPPORT
465
					+1, ATOM_PTY_REQ, make_pty_handler()
466
#endif /* WITH_PTY_SUPPORT */
467
468
469
470
471
472
					, ATOM_SHELL,
					make_shell_handler(backend,
							   reaper),
					-1),
				       backend),
				      connection_hooks),
473
474
				     -1))),
	   -1)));
475
    
476
      CAST_SUBTYPE(command, server_listen, o);
477
    
Niels Möller's avatar
Niels Möller committed
478
      COMMAND_CALL(server_listen, options->local,
479
480
481
482
		   &discard_continuation,
		   make_report_exception_handler(EXC_IO, EXC_IO, "lshd: ",
						 &default_exception_handler,
						 HANDLER_CONTEXT));
483
    }
484
  }
Niels Möller's avatar
Niels Möller committed
485
  
486
  reaper_run(reaper, backend);
Niels Möller's avatar
Niels Möller committed
487
488
489

  return 0;
}