lshd.c 32.1 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* lshd.c
 *
3
 * Main server program.
4
 *
5
 */
Niels Möller's avatar
Niels Möller committed
6

7
8
/* lsh, an implementation of the ssh protocol
 *
9
 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Niels Mller
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#if HAVE_CONFIG_H
#include "config.h"
#endif

#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>
#if TIME_WITH_SYS_TIME && HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#if HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

50
#include "algorithms.h"
51
52
#include "alist.h"
#include "atoms.h"
53
#include "channel.h"
54
#include "channel_commands.h"
55
#include "charset.h"
56
#include "compress.h"
57
#include "connection_commands.h"
58
#include "crypto.h"
59
#include "daemon.h"
60
#include "environ.h"
61
#include "format.h"
62
#include "handshake.h"
Niels Möller's avatar
Niels Möller committed
63
#include "io.h"
64
#include "io_commands.h"
65
#include "lookup_verifier.h"
66
#include "randomness.h"
Niels Möller's avatar
Niels Möller committed
67
#include "reaper.h"
Niels Möller's avatar
Niels Möller committed
68
#include "server.h"
69
#include "server_authorization.h"
70
#include "server_keyexchange.h"
71
72
#include "server_pty.h"
#include "server_session.h"
Niels Möller's avatar
Niels Möller committed
73
#include "spki.h"
74
#include "srp.h"
Niels Möller's avatar
Niels Möller committed
75
#include "ssh.h"
76
#include "tcpforward.h"
77
#include "server_userauth.h"
78
#include "version.h"
79
80
81
#include "werror.h"
#include "xalloc.h"

82
#include "lsh_argp.h"
83

84
85
86
87
/* Forward declarations */
static struct install_info install_session_handler;
#define INSTALL_SESSION_HANDLER (&install_session_handler.super.super.super)

88
89
#include "lshd.c.x"

90

91
92
/* Option parsing */

93
94
95
96
97
const char *argp_program_version
= "lshd-" VERSION ", secsh protocol version " SERVER_PROTOCOL_VERSION;

const char *argp_program_bug_address = BUG_ADDRESS;

98
#define OPT_NO 0x400
99
#define OPT_INTERFACE 0x201
100

101
#define OPT_TCPIP_FORWARD 0x202
102
#define OPT_NO_TCPIP_FORWARD (OPT_TCPIP_FORWARD | OPT_NO)
103
104
#define OPT_PTY 0x203
#define OPT_NO_PTY (OPT_PTY | OPT_NO)
105
106
#define OPT_SUBSYSTEMS 0x204
#define OPT_NO_SUBSYSTEMS (OPT_SUBSYSTEMS | OPT_NO)
107

108
#define OPT_DAEMONIC 0x205
109
#define OPT_NO_DAEMONIC (OPT_DAEMONIC | OPT_NO)
110
#define OPT_PIDFILE 0x206
111
112
#define OPT_NO_PIDFILE (OPT_PIDFILE | OPT_NO)
#define OPT_CORE 0x207
113
114
#define OPT_SYSLOG 0x208
#define OPT_NO_SYSLOG (OPT_SYSLOG | OPT_NO)
115
116
#define OPT_X11_FORWARD 0x209
#define OPT_NO_X11_FORWARD (OPT_X11_FORWARD |OPT_NO)
117

118
119
120
121
122
123
#define OPT_SRP 0x210
#define OPT_NO_SRP (OPT_SRP | OPT_NO)
#define OPT_DH 0x211
#define OPT_NO_DH (OPT_DH | OPT_NO)

#define OPT_PUBLICKEY 0x220
124
#define OPT_NO_PUBLICKEY (OPT_PUBLICKEY | OPT_NO)
125
#define OPT_PASSWORD 0x221
126
127
#define OPT_NO_PASSWORD (OPT_PASSWORD | OPT_NO)

128
#define OPT_ROOT_LOGIN 0x222
129
130
#define OPT_NO_ROOT_LOGIN (OPT_ROOT_LOGIN | OPT_NO)

131
132
133
#define OPT_KERBEROS_PASSWD 0x223
#define OPT_NO_KERBEROS_PASSWD (OPT_KERBEROS_PASSWD | OPT_NO)

134
135
#define OPT_PASSWORD_HELPER 0x224

136
137
#define OPT_LOGIN_SHELL 0x225

138
139
140
141
142
#define OPT_TCPWRAPPERS 0x226
#define OPT_NO_TCPWRAPPERS 0x227

#define OPT_TCPWRAP_GOAWAY_MSG 0x228

143
144
145
146
147
148
#define OPT_LOGIN_AUTH_MODE 0x230
#define OPT_NO_LOGIN_AUTH_MODE (OPT_LOGIN_AUTH_MODE | OPT_NO)
#define OPT_LOGIN_AUTH_USER 0x231

#define OPT_BANNER_FILE 0x232

149
150
151
152
153
/* GABA:
   (class
     (name lshd_options)
     (super algorithms_options)
     (vars
154
       (reaper object reaper)
155
       (random object randomness)
156
       
157
       (signature_algorithms object alist)
158
       ;; Addresses to bind
159
       (local struct addr_queue)
160
161
       (port . "char *")
       (hostkey . "char *")
162
163
       (tcp_wrapper_name . "char *")
       (tcp_wrapper_message . "char *")
164

165
166
167
168
169
       (with_srp_keyexchange . int)
       (with_dh_keyexchange . int)

       ;; (kexinit object make_kexinit)
       (kex_algorithms object int_list)
170
171

       (with_loginauthmode . int)
172
173
       (with_publickey . int)
       (with_password . int)
174
       (allow_root . int)
175
       (pw_helper . "const char *")
176
       (login_shell . "const char *")
177
       ;; (loginauthmode_user . "const char *")
178
179

       (banner_file . "const char *")
180
       
181
       (with_tcpip_forward . int)
182
       (with_x11_forward . int)
183
       (with_pty . int)
184
       (subsystems . "const char **")
185
       
186
187
188
       (userauth_methods object int_list)
       (userauth_algorithms object alist)
       
189
       (daemonic . int)
190
       (no_syslog . int)
191
192
193
       (corefile . int)
       (pid_file . "const char *")
       ; -1 means use pid file iff we're in daemonic mode
194
       (use_pid_file . int)))
195
196
*/

197

Niels Möller's avatar
Niels Möller committed
198
static struct lshd_options *
199
make_lshd_options(void)
200
{
Niels Möller's avatar
Niels Möller committed
201
  NEW(lshd_options, self);
202

203
  init_algorithms_options(&self->super, all_symmetric_algorithms());
204

205
  self->reaper = make_reaper();
206
  self->random = make_system_random();
207

208
209
  /* OK to initialize with NULL */
  self->signature_algorithms = all_signature_algorithms(self->random);
210

211
  addr_queue_init(&self->local);
212
  
213
214
215
216
  /* Default behaviour is to lookup the "ssh" service, and fall back
   * to port 22 if that fails. */
  self->port = NULL;
  
217
218
  /* FIXME: this should perhaps use sysconfdir */  
  self->hostkey = "/etc/lsh_host_key";
219
  
220
221
222
223
224
  self->with_dh_keyexchange = 1;
  self->with_srp_keyexchange = 0;

  self->kex_algorithms = NULL;
  
225
  self->with_loginauthmode = 0;
226
227
  self->with_publickey = 1;
  self->with_password = 1;
228
  self->with_tcpip_forward = 1;
229
230
  /* Enabled by default. */
  self->with_x11_forward = 1;
231
  self->with_pty = 1;
232
  self->subsystems = NULL;
233
234

#if 0
235
  self->loginauthmode_user = NULL;
236
#endif
237
238
  self->banner_file = NULL;

239
240
241
  self->tcp_wrapper_name = "lshd";
  self->tcp_wrapper_message = NULL;

242
  self->allow_root = 0;
243
  self->pw_helper = NULL;
244
  self->login_shell = NULL;
245
  
246
247
  self->userauth_methods = NULL;
  self->userauth_algorithms = NULL;
248
  
249
  self->daemonic = 0;
250
251
  self->no_syslog = 0;
  
252
253
254
255
  /* FIXME: Make the default a configure time option? */
  self->pid_file = "/var/run/lshd.pid";
  self->use_pid_file = -1;
  self->corefile = 0;
256

257
258
259
  return self;
}

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/* GABA:
   (class
     (name pid_file_resource)
     (super resource)
     (vars
       (file . "const char *")))
*/

static void
do_kill_pid_file(struct resource *s)
{
  CAST(pid_file_resource, self, s);
  if (self->super.alive)
    {
      self->super.alive = 0;
      if (unlink(self->file) < 0)
276
	werror("Unlinking pidfile failed %e\n", errno);
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
    }
}

static struct resource *
make_pid_file_resource(const char *file)
{
  NEW(pid_file_resource, self);
  init_resource(&self->super, do_kill_pid_file);
  self->file = file;

  return &self->super;
}

/* GABA:
   (class
     (name sighup_close_callback)
     (super lsh_callback)
     (vars
295
       (resource object resource)))
296
297
298
299
300
301
302
303
304
*/

static void
do_sighup_close_callback(struct lsh_callback *s)
{
  CAST(sighup_close_callback, self, s);
  unsigned nfiles;
  
  werror("SIGHUP received.\n");
305
  KILL_RESOURCE(self->resource);
306
307
308
309
310
311
312
313
314
  
  nfiles = io_nfiles();

  if (nfiles)
    werror("Waiting for active connections to terminate, "
	   "%i files still open.\n", nfiles);
}

static struct lsh_callback *
315
make_sighup_close_callback(struct resource *resource)
316
317
318
{
  NEW(sighup_close_callback, self);
  self->super.f = do_sighup_close_callback;
319
  self->resource = resource;
320
321
322
323

  return &self->super;
}

324
325
326
327
328
static const struct argp_option
main_options[] =
{
  /* Name, key, arg-name, flags, doc, group */
  { "interface", OPT_INTERFACE, "interface", 0,
329
    "Listen on this network interface.", 0 }, 
330
331
  { "port", 'p', "Port", 0, "Listen on this port.", 0 },
  { "host-key", 'h', "Key file", 0, "Location of the server's private key.", 0},
332
333
  { "banner-file", OPT_BANNER_FILE, "File name", 0, "Banner file to send before " "handshake.", 9 },

334
335
336
337
338
339
340
341
#if WITH_TCPWRAPPERS
  { NULL, 0, NULL, 0, "Connection filtering:", 0 },
  { "tcpwrappers", OPT_TCPWRAPPERS, "name", 0, "Set service name for tcp wrappers (default lshd)", 0 },
  { "no-tcpwrappers", OPT_NO_TCPWRAPPERS, NULL, 0, "Disable wrappers", 0 },
  { "tcpwrappers-msg", OPT_TCPWRAP_GOAWAY_MSG, "'Message'", 0, "Message sent to clients " 
    "who aren't allowed to connect. A newline will be added.", 0 },
#endif /* WITH_TCPWRAPPERS */

342
  { NULL, 0, NULL, 0, "Keyexchange options:", 0 },
343
344
345
346
347
348
349
350
#if WITH_SRP
  { "srp-keyexchange", OPT_SRP, NULL, 0, "Enable experimental SRP support.", 0 },
  { "no-srp-keyexchange", OPT_NO_SRP, NULL, 0, "Disable experimental SRP support (default).", 0 },
#endif /* WITH_SRP */

  { "dh-keyexchange", OPT_DH, NULL, 0, "Enable DH support (default).", 0 },
  { "no-dh-keyexchange", OPT_NO_DH, NULL, 0, "Disable DH support.", 0 },
  
351
  { NULL, 0, NULL, 0, "User authentication options:", 0 },
352

353
354
  { "login-auth-mode", OPT_LOGIN_AUTH_MODE, NULL, 0, 
    "Enable a telnet like mode (accept none-authentication and launch the" 
355
    "login-shell, making it responsible for authenticating the user).", 0 },
356
357
358
359

  { "no-login-auth-mode", OPT_NO_LOGIN_AUTH_MODE, NULL, 0, 
    "Disable login-auth-mode (default).", 0 },

360
#if 0
361
362
363
  { "login-auth-mode-user", OPT_LOGIN_AUTH_USER, "username", 0,
    "Run login-program as this user, defaults to the user "
    "who started lshd.", 0 },
364
365
#endif
  
366
367
368
369
370
371
372
373
374
  { "password", OPT_PASSWORD, NULL, 0,
    "Enable password user authentication (default).", 0},
  { "no-password", OPT_NO_PASSWORD, NULL, 0,
    "Disable password user authentication.", 0},

  { "publickey", OPT_PUBLICKEY, NULL, 0,
    "Enable publickey user authentication (default).", 0},
  { "no-publickey", OPT_NO_PUBLICKEY, NULL, 0,
    "Disable publickey user authentication.", 0},
375
376
377
378
379

  { "root-login", OPT_ROOT_LOGIN, NULL, 0,
    "Allow root to login.", 0 },
  { "no-root-login", OPT_NO_ROOT_LOGIN, NULL, 0,
    "Don't allow root to login (default).", 0 },
380

381
382
383
384
  { "login-shell", OPT_LOGIN_SHELL, "Program", 0,
    "Use this program as the login shell for all users. "
    "(Experimental)", 0 },
  
385
386
  { "kerberos-passwords", OPT_KERBEROS_PASSWD, NULL, 0,
    "Recognize kerberos passwords, using the helper program "
387
    "\"" PATH_KERBEROS_HELPER "\". This option is experimental.", 0 },
388
  { "no-kerberos-passwords", OPT_NO_KERBEROS_PASSWD, NULL, 0,
Niels Möller's avatar
Niels Möller committed
389
    "Don't recognize kerberos passwords (default behaviour).", 0 },
390

391
392
  { "password-helper", OPT_PASSWORD_HELPER, "Program", 0,
    "Use the named helper program for password verification. "
393
    "(Experimental).", 0 },
394

395
  { NULL, 0, NULL, 0, "Offered services:", 0 },
396

397
398
399
400
#if WITH_PTY_SUPPORT
  { "pty-support", OPT_PTY, NULL, 0, "Enable pty allocation (default).", 0 },
  { "no-pty-support", OPT_NO_PTY, NULL, 0, "Disable pty allocation.", 0 },
#endif /* WITH_PTY_SUPPORT */
401
402
403
404
405
406
407
408
#if WITH_TCP_FORWARD
  { "tcpip-forward", OPT_TCPIP_FORWARD, NULL, 0,
    "Enable tcpip forwarding (default).", 0 },
  { "no-tcpip-forward", OPT_NO_TCPIP_FORWARD, NULL, 0,
    "Disable tcpip forwarding.", 0 },
#endif /* WITH_TCP_FORWARD */
#if WITH_X11_FORWARD
  { "x11-forward", OPT_X11_FORWARD, NULL, 0,
409
    "Enable x11 forwarding (default).", 0 },
410
  { "no-x11-forward", OPT_NO_X11_FORWARD, NULL, 0,
411
    "Disable x11 forwarding.", 0 },
412
413
#endif /* WITH_X11_FORWARD */
  
414
415
416
  { "subsystems", OPT_SUBSYSTEMS, "List of subsystem names and programs", 0,
    "For example `sftp=/usr/sbin/sftp-server,foosystem=/usr/bin/foo' "
    "(experimental).", 0},
417
  
418
419
  { 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 },
420
  { "no-daemonic", OPT_NO_DAEMONIC, NULL, 0, "Run in the foreground, with messages to stderr (default).", 0 },
421
422
  { "pid-file", OPT_PIDFILE, "file name", 0, "Create a pid file. When running in daemonic mode, "
    "the default is /var/run/lshd.pid.", 0 },
423
  { "no-pid-file", OPT_NO_PIDFILE, NULL, 0, "Don't use any pid file. Default in non-daemonic mode.", 0 },
424
  { "enable-core", OPT_CORE, NULL, 0, "Dump core on fatal errors (disabled by default).", 0 },
425
426
  { "no-syslog", OPT_NO_SYSLOG, NULL, 0, "Don't use syslog (by default, syslog is used "
    "when running in daemonic mode).", 0 },
427
428
429
430
431
432
433
434
435
436
437
  { NULL, 0, NULL, 0, NULL, 0 }
};

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

438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
/* NOTE: Modifies the argument string. */
static const char **
parse_subsystem_list(char *arg)
{
  const char **subsystems;
  char *separator;
  unsigned length;
  unsigned i;
  
  /* First count the number of elements. */
  for (length = 1, i = 0; arg[i]; i++)
    if (arg[i] == ',')
      length++;

  subsystems = lsh_space_alloc((length * 2 + 1) * sizeof(*subsystems));

  for (i = 0; ; i++)
    {
      subsystems[2*i] = arg;

      separator = strchr(arg, '=');

      if (!separator)
	goto fail;

      *separator = '\0';

      subsystems[2*i+1] = arg = separator + 1;
      
      separator = strchr(arg, ',');

      if (i == (length - 1))
	break;
      
      if (!separator)
	goto fail;

      *separator = '\0';
      arg = separator + 1;
    }
  if (separator)
    {
    fail:
      lsh_space_free(subsystems);
      return NULL;
    }
  return subsystems;
}

487
488
489
/* NOTE: On success, modifies interface destructively. */
static int
parse_interface(char *interface, const char **host, const char **port)
490
{
491
  *port = NULL;
492
493
494
495
  
  if (interface[0] == '[')
    {
      /* A literal address */
496
      char *end;
497
498
499
500
      interface++;
      
      end = strchr(interface, ']');
      if (!end)
501
	return 0;
502
503
504
505

      switch (end[1])
	{
	case ':':
506
	  *port = end + 2;
507
508
509
510
	  break;
	case 0:
	  break;
	default:
511
	  return 0;
512
	}
513
514
515
516

      *host = interface;
      *end = 0;
      return 1;
517
518
519
    }
  else
    {
520
      char *end = strchr(interface, ':');
521
522
      if (end)
	{
523
524
	  *port = end + 1;
	  *end = 0;
525
	}
526
527
      *host = interface;
      return 1;
528
529
530
    }
}

531
532
533
534
535
536
537
538
539
540
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:
Niels Möller's avatar
Niels Möller committed
541
542
      state->child_inputs[0] = &self->super;
      state->child_inputs[1] = NULL;
543
544
      break;
    case ARGP_KEY_END:
545
      {
546
	struct user_db *user_db = NULL;
547
	
548
549
	if (!self->random)
	  argp_failure( state, EXIT_FAILURE, 0,  "No randomness generator available.");
550
	
551
       	if (self->with_password || self->with_publickey || self->with_srp_keyexchange)
552
	  user_db = make_unix_user_db(self->reaper,
553
554
				      self->pw_helper, self->login_shell,
				      self->allow_root);
555
	  
556
557
558
559
	if (self->with_dh_keyexchange || self->with_srp_keyexchange)
	  {
	    int i = 0;
	    self->kex_algorithms 
560
	      = alloc_int_list(3 * self->with_dh_keyexchange + self->with_srp_keyexchange);
561
562
563
	    
	    if (self->with_dh_keyexchange)
	      {
564
565
566
		struct keyexchange_algorithm *dh2
		  = make_dh_server(make_dh14(self->random));
		
567
568
569
		LIST(self->kex_algorithms)[i++] = ATOM_DIFFIE_HELLMAN_GROUP14_SHA1;
		ALIST_SET(self->super.algorithms,
			  ATOM_DIFFIE_HELLMAN_GROUP14_SHA1,
570
571
572
573
574
575
576
			  &dh2->super);

		/* Alternative name for the same thing */
		LIST(self->kex_algorithms)[i++] = ATOM_DIFFIE_HELLMAN_GROUP2_SHA1;
		ALIST_SET(self->super.algorithms,
			  ATOM_DIFFIE_HELLMAN_GROUP2_SHA1,
			  &dh2->super);
577

578
579
580
		LIST(self->kex_algorithms)[i++] = ATOM_DIFFIE_HELLMAN_GROUP1_SHA1;
		ALIST_SET(self->super.algorithms,
			  ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
581
			  &make_dh_server(make_dh1(self->random))
582
			  ->super);
583
584
585
586
	      }
#if WITH_SRP	    
	    if (self->with_srp_keyexchange)
	      {
587
		assert(user_db);
588
		LIST(self->kex_algorithms)[i++] = ATOM_SRP_RING1_SHA1_LOCAL;
589
		ALIST_SET(self->super.algorithms,
590
			  ATOM_SRP_RING1_SHA1_LOCAL,
591
			  &make_srp_server(make_srp1(self->random),
592
					   user_db)
593
			  ->super);
594
595
596
597
598
599
	      }
#endif /* WITH_SRP */
	  }
	else
	  argp_error(state, "All keyexchange algorithms disabled.");

600
	if (addr_queue_is_empty(&self->local))
601
602
603
	  {
	    /* Default interface */

604
605
606
607
	    if (!(self->port
		  ? io_resolv_address(NULL, self->port, 0, &self->local)
		  : io_resolv_address(NULL, "ssh", 22, &self->local)))
		
608
609
610
	      argp_failure(state, EXIT_FAILURE, 0,
			   "Strange. Could not resolve the ANY address.");
	  }
611
	assert(!addr_queue_is_empty(&self->local));
612
	
613
614
615
	if (self->use_pid_file < 0)
	  self->use_pid_file = self->daemonic;

616
617
	self->userauth_algorithms = make_alist(0, -1);

618
619
620
621
622
623
624
625
626
627
628
	if (self->with_password || self->with_publickey)
	  {
	    int i = 0;
	    
	    self->userauth_methods
	      = alloc_int_list(self->with_password + self->with_publickey);
	    
	    if (self->with_password)
	      {
		LIST(self->userauth_methods)[i++] = ATOM_PASSWORD;
		ALIST_SET(self->userauth_algorithms,
629
			  ATOM_PASSWORD,
630
			  &make_userauth_password(user_db)->super);
631
632
633
	      }
	    if (self->with_publickey)
	      {
634
635
636
		/* FIXME: Doesn't use spki */
		struct lookup_verifier *key_db
		  = make_authorization_db(ssh_format("authorized_keys_sha1"),
637
					  &crypto_sha1_algorithm);
638
		
639
640
641
		LIST(self->userauth_methods)[i++] = ATOM_PUBLICKEY;
		ALIST_SET(self->userauth_algorithms,
			  ATOM_PUBLICKEY,
642
			  &make_userauth_publickey
643
644
645
646
			  (user_db,
			   make_alist(2,
				      ATOM_SSH_DSS, key_db,
				      ATOM_SSH_RSA, key_db,
647
648
				      -1))
			  ->super);
649
650
	      }
	  }
651
652


653
654
655
        if (self->with_srp_keyexchange)
          ALIST_SET(self->userauth_algorithms,
                    ATOM_NONE,
656
657
658
		    /* username must match the name from the key
		       exchange */		    
                    &server_userauth_none_preauth.super);
659
660
	else if (self->with_loginauthmode)
	  {
661
	    const char *name;
662
	    
663
	    name = getenv(ENV_LOGNAME);
664
665
666
	    if (!name)
	      argp_failure(state, EXIT_FAILURE, 0,
			   "$LOGNAME not set in the environment.\n");
667
668
669

	    ALIST_SET(self->userauth_algorithms,
		      ATOM_NONE,
670
671
672
673
674
675
		      &make_userauth_none_permit
		      (make_unix_user_self(make_string(name),
					   self->reaper,
					   /* Make home dir configurable? */
					   "/",
					   self->login_shell))->super);
676
677
	  }

678
        if (!self->userauth_algorithms->size)
679
	  argp_error(state, "All user authentication methods disabled.");
680

681
682
	break;
      }
683
    case 'p':
684
685
      /* FIXME: Interpret multiple -p:s as a request to listen on
       * several ports. */
686
687
688
689
690
691
692
693
      self->port = arg;
      break;

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

    case OPT_INTERFACE:
694
      {
695
696
	const char *host;
	const char *port;
697

698
699
700
	/* On success, modifies arg destructively. */
	if (!parse_interface(arg, &host, &port))
	  argp_error(state, "Invalid interface, port or service: %s.", arg);
701

702
703
704
705
706
707
708
709
710
	if (!port)
	  port = self->port;
	
	if (!(port
	      ? io_resolv_address(host, port, 0, &self->local)
	      : io_resolv_address(host, "ssh", 22, &self->local)))
	  argp_failure(state, EXIT_FAILURE, 0,
		       "Address %s:%s could not be resolved.\n",
		       host, port ? port : "ssh");
711
712
      }
	
713
      break;
714

715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
    case OPT_SRP:
      self->with_srp_keyexchange = 1;
      break;

    case OPT_NO_SRP:
      self->with_srp_keyexchange = 0;
      break;
      
    case OPT_DH:
      self->with_dh_keyexchange = 1;
      break;

    case OPT_NO_DH:
      self->with_dh_keyexchange = 0;
      break;
      
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
    case OPT_PASSWORD:
      self->with_password = 1;
      break;
      
    case OPT_NO_PASSWORD:
      self->with_password = 0;
      break;

    case OPT_PUBLICKEY:
      self->with_publickey = 1;
      break;
      
    case OPT_NO_PUBLICKEY:
      self->with_publickey = 0;
      break;
746

747
748
749
750
751
752
753
754
    case OPT_LOGIN_AUTH_MODE:
      self->with_loginauthmode = 1;
      break;

    case OPT_NO_LOGIN_AUTH_MODE:
      self->with_loginauthmode = 0;
      break;

755
#if 0
756
757
758
    case OPT_LOGIN_AUTH_USER:
      self->loginauthmode_user = arg;
      break;
759
760
#endif
      
761
762
763
764
    case OPT_BANNER_FILE:
      self->banner_file = arg;
      break;

765
766
767
    case OPT_ROOT_LOGIN:
      self->allow_root = 1;
      break;
768
769

    case OPT_KERBEROS_PASSWD:
770
      self->pw_helper = PATH_KERBEROS_HELPER;
771
772
773
774
775
      break;

    case OPT_NO_KERBEROS_PASSWD:
      self->pw_helper = NULL;
      break;
776
777
778
779

    case OPT_PASSWORD_HELPER:
      self->pw_helper = arg;
      break;
780
781
782
783

    case OPT_LOGIN_SHELL:
      self->login_shell = arg;
      break;
784
      
785
#if WITH_TCP_FORWARD
786
787
788
789
790
791
792
    case OPT_TCPIP_FORWARD:
      self->with_tcpip_forward = 1;
      break;

    case OPT_NO_TCPIP_FORWARD:
      self->with_tcpip_forward = 0;
      break;
793
#endif /* WITH_TCP_FORWARD */
794
795
796
797
798
799
800
801
#if WITH_X11_FORWARD
    case OPT_X11_FORWARD:
      self->with_x11_forward = 1;
      break;
    case OPT_NO_X11_FORWARD:
      self->with_x11_forward = 0;
      break;
#endif /* WITH_X11_FORWARD */
802
803
804
805
806
807
808
809
810
      
#if WITH_PTY_SUPPORT
    case OPT_PTY:
      self->with_pty = 1;
      break;
    case OPT_NO_PTY:
      self->with_pty = 0;
      break;
#endif /* WITH_PTY_SUPPORT */
811

812
813
814
815
816
817
818
819
820
821
822
823
824
825
#if WITH_TCPWRAPPERS
    case OPT_TCPWRAPPERS:
      self->tcp_wrapper_name = arg; /* Name given */
      break;
    case OPT_NO_TCPWRAPPERS:
      self->tcp_wrapper_name = NULL; /* Disable by giving name NULL */
      break;
      
    case OPT_TCPWRAP_GOAWAY_MSG:
      self->tcp_wrapper_message = arg;
      break;

#endif /* WITH_TCPWRAPPERS */

826
827
828
829
830
831
832
833
834
835
    case OPT_SUBSYSTEMS:
      self->subsystems = parse_subsystem_list(arg);
      if (!self->subsystems)
	argp_error(state, "Invalid subsystem list.");
      break;

    case OPT_NO_SUBSYSTEMS:
      self->subsystems = NULL;
      break;
      
836
837
838
    case OPT_DAEMONIC:
      self->daemonic = 1;
      break;
839
      
840
841
842
843
    case OPT_NO_DAEMONIC:
      self->daemonic = 0;
      break;

844
845
846
847
    case OPT_NO_SYSLOG:
      self->no_syslog = 1;
      break;
      
848
849
850
851
852
853
854
855
856
857
858
859
    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;
860
861
862
    }
  return 0;
}
Niels Möller's avatar
Niels Möller committed
863

Niels Möller's avatar
Niels Möller committed
864
865
866
867
868
869
static const struct argp
main_argp =
{ main_options, main_argp_parser, 
  NULL,
  "Server for the ssh-2 protocol.",
  main_argp_children,
870
  NULL, NULL
Niels Möller's avatar
Niels Möller committed
871
872
};

873

874
875
876
static void
do_terminate_callback(struct lsh_callback *s UNUSED)
{
877
  io_final();
878
879
880
881
882
883

  /* If we're using GCOV, just call exit(). That way, profiling info
   * is written properly when the process is terminated. */
#if !WITH_GCOV
  kill(getpid(), SIGKILL);
#endif
884
885
886
  exit(0);
}

887
static struct lsh_callback
888
sigterm_handler = { STATIC_HEADER, do_terminate_callback };
889
890

static void
891
install_signal_handlers(struct resource *resource)
892
{
893
894
  io_signal_handler(SIGTERM, &sigterm_handler);
  io_signal_handler(SIGHUP,
895
896
897
		    make_sighup_close_callback(resource));
}

898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
static void
do_exc_lshd_handler(struct exception_handler *self,
		    const struct exception *e)
{
  if (e->type & EXC_IO)
    {
      CAST_SUBTYPE(io_exception, exc, e);
      
      werror("%z, (errno = %i)\n", e->msg, exc->error);
    }
  else
    EXCEPTION_RAISE(self->parent, e);
}

static struct exception_handler lshd_exception_handler
= STATIC_EXCEPTION_HANDLER(do_exc_lshd_handler, &default_exception_handler);
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930

/* Invoked when starting the ssh-connection service */
/* GABA:
   (expr
     (name lshd_connection_service)
     (params
       (hooks object object_list))
     (expr
       (lambda (connection)
         ((progn hooks)
	    ; We have to initialize the connection
	    ; before adding handlers.
	    (init_connection_service
	      ; Disconnect if connection->user is NULL
	      (connection_require_userauth connection)))))))
*/

931
932
933
934
935
936
937
static struct command *
make_lshd_connection_service(struct lshd_options *options)
{
  /* Commands to be invoked on the connection */
  /* FIXME: Use a queue instead. */
  struct object_list *connection_hooks;
  struct command *session_setup;
938
939
  struct alist *supported_channel_requests;

940
  /* Supported channel requests */
941
942
943
944
945
946


  supported_channel_requests = make_alist(2,
					  ATOM_SHELL, &shell_request_handler,
					  ATOM_EXEC, &exec_request_handler,
					  -1);
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
    
#if WITH_PTY_SUPPORT
  if (options->with_pty)
    {
      ALIST_SET(supported_channel_requests,
		ATOM_PTY_REQ, &pty_request_handler.super);
      ALIST_SET(supported_channel_requests,
		ATOM_WINDOW_CHANGE, &window_change_request_handler.super);
    }
#endif /* WITH_PTY_SUPPORT */

#if WITH_X11_FORWARD
  if (options->with_x11_forward)
    ALIST_SET(supported_channel_requests,
	      ATOM_X11_REQ, &x11_req_handler.super);
#endif /* WITH_X11_FORWARD */
  
  if (options->subsystems)
    ALIST_SET(supported_channel_requests,
	      ATOM_SUBSYSTEM,
	      &make_subsystem_handler(options->subsystems)->super);
  
  session_setup = make_install_fix_channel_open_handler
    (ATOM_SESSION, make_open_session(supported_channel_requests));
  
#if WITH_TCP_FORWARD
  if (options->with_tcpip_forward)
    connection_hooks = make_object_list
      (4,
       session_setup,
       make_tcpip_forward_hook(),
       make_install_fix_global_request_handler
       (ATOM_CANCEL_TCPIP_FORWARD, &tcpip_cancel_forward),
       make_direct_tcpip_hook(),
       -1);
  else
#endif
    connection_hooks
985
      = make_object_list (1, session_setup, -1); 
986
987
988
989
990
991
992
993

  {
    CAST_SUBTYPE(command, connection_service,
		 lshd_connection_service(connection_hooks));
    return connection_service;
  }
}

994
995
996
/* Command to install session handler */
static struct install_info install_session_handler =
STATIC_INSTALL_OPEN_HANDLER(ATOM_SESSION);
997

998
999
1000
1001
1002
/* Invoked when starting the ssh-connection service */ 
/* GABA: 
   (expr 
     (name lshd_login_service) 
     (params
1003
       (handler object channel_open))
1004
1005
     (expr 
       (lambda (connection) 
1006
1007
1008
1009
1010
1011
1012
1013
         (install_session_handler
            ; We have to initialize the connection 
	    ; before adding handlers.
	    (init_connection_service
	      ; The fix user object is installed by the
	      ; userauth "none" handler.
	      (connection_require_userauth connection))
	    handler))))
1014
*/ 
1015
  
1016
1017
1018
1019
static struct command *
make_lshd_login_service(struct lshd_options *options)
{
  struct alist *supported_channel_requests;
1020
  
1021
1022
1023
#if WITH_PTY_SUPPORT
  if (options->with_pty)
    {
1024
1025
1026
1027
1028
1029
      supported_channel_requests
	= make_alist(3,
		     ATOM_SHELL, &shell_request_handler,
		     ATOM_PTY_REQ, &pty_request_handler.super,
		     ATOM_WINDOW_CHANGE, &window_change_request_handler.super,
		     -1);
1030
    }
1031
  else
1032
#endif /* WITH_PTY_SUPPORT */
1033
1034
1035
1036
    supported_channel_requests
      = make_alist(1,
		   ATOM_SHELL, &shell_request_handler,
		   -1);
1037
1038
1039

  {
    CAST_SUBTYPE(command, connection_service,
1040
1041
		 lshd_login_service(make_open_session
				    (supported_channel_requests)));
1042
1043
1044
1045
1046
    return connection_service;
  }
}


1047
1048
1049
1050
1051
1052
1053
1054
/* GABA:
   (expr
     (name lshd_listen_callback)
     (params
       (handshake object handshake_info)
       (kexinit object make_kexinit)
       (keys object alist)
       (logger object command)
1055
       (services object command))
1056
1057
1058
1059
     (expr (lambda (lv)
    	      (services (connection_handshake
	                   handshake
			   kexinit
1060
			   keys
1061
1062
1063
			   (logger lv))))))
*/

1064
1065
1066
1067
1068
1069
1070
1071
static struct io_callback *
make_lshd_listen_callback(struct lshd_options *options,
			  struct alist *keys,
			  struct command *service)
{
  struct int_list *hostkey_algorithms;
  struct make_kexinit *kexinit;
  struct command *logger;
1072
  struct lsh_string *banner_text = NULL;
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
  
  /* Include only hostkey algorithms that we have keys for. */
  hostkey_algorithms
    = filter_algorithms(keys,
			options->super.hostkey_algorithms);
  
  if (!hostkey_algorithms)
    {
      werror("No hostkey algorithms advertised.\n");
      hostkey_algorithms = make_int_list(1, ATOM_NONE, -1);
    }
  
  kexinit = make_simple_kexinit(options->random,
				options->kex_algorithms,
				hostkey_algorithms,
				options->super.crypto_algorithms,
				options->super.mac_algorithms,
				options->super.compression_algorithms,
				make_int_list(0, -1));

1093
#if WITH_TCPWRAPPERS
1094
1095
1096
1097
1098
1099
  if (options->tcp_wrapper_name)
    logger = make_tcp_wrapper
      (make_string(options->tcp_wrapper_name),
       make_string(options->tcp_wrapper_message
		   ? options->tcp_wrapper_message : ""));
  else
1100
#endif /* WITH_TCPWRAPPERS */
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
    {
      logger = &io_log_peer_command;

    }

  if (options->banner_file) /* Banner? */
    {

      int fd = open(options->banner_file, 0);
      
      if (fd >= 0)
	{
	  banner_text = io_read_file_raw(fd, 1024);
	  close(fd);
	}
    }



1120
1121
1122
  {
    CAST_SUBTYPE(command, server_callback,
		 lshd_listen_callback
1123
		   (make_handshake_info(CONNECTION_SERVER,
1124
					SOFTWARE_SLOGAN,
1125
1126
1127
1128
					NULL,
					SSH_MAX_PACKET,
					options->random,
					options->super.algorithms,
1129
				        banner_text),
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
		  kexinit,
		  keys,
		  logger,
		  make_offer_service
		  (make_alist
		   (1,
		    ATOM_SSH_USERAUTH,
		    make_userauth_service(options->userauth_methods,
					  options->userauth_algorithms,
					  make_alist(1, ATOM_SSH_CONNECTION,
						     service,-1)),
		    -1))));

1143
    /* There should be no exceptions raised on the fd:s we listen on */
1144
    return make_listen_callback(server_callback, &lshd_exception_handler);
1145
  }
1146
}
1147

1148
1149
int
main(int argc, char **argv)
Niels Möller's avatar
Niels Möller committed
1150
{
1151
  struct lshd_options *options;
1152

1153
1154
1155
1156
1157
1158
1159
1160
1161
  /* Resources that should be killed when SIGHUP is received,
   * or when the program exits. */
  struct resource_list *resources = make_resource_list();

  /* Hostkeys */
  struct alist *keys = make_alist(0, -1);

  struct resource *fds;
  
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
#if HAVE_SETRLIMIT && HAVE_SYS_RESOURCE_H
  /* Try to increase max number of open files, ignore any error */

  struct rlimit r;

  r.rlim_max = RLIM_INFINITY;
  r.rlim_cur = RLIM_INFINITY;

  setrlimit(RLIMIT_NOFILE, &r);
#endif

1173
1174
1175
1176
  /* Not strictly needed for gc, but makes sure the
   * resource list is killed properly by gc_final. */
  gc_global(&resources->super);
  
1177
  io_init();
1178
  
Niels Möller's avatar
Niels Möller committed
1179
1180
1181
  /* For filtering messages. Could perhaps also be used when converting
   * strings to and from UTF8. */
  setlocale(LC_CTYPE, "");
1182

1183
1184
  /* FIXME: Choose character set depending on the locale */
  set_local_charset(CHARSET_LATIN1);
1185

1186
  install_signal_handlers(&resources->super);
1187

1188
  options = make_lshd_options();
1189

Niels Möller's avatar
Niels Möller committed
1190
  trace("Parsing options...\n");
Niels Möller's avatar
Niels Möller committed
1191
  argp_parse(&main_argp, argc, argv, 0, NULL, options);
Niels Möller's avatar
Niels Möller committed
1192
  trace("Parsing options... done\n");  
1193

1194
1195
1196
1197
1198
1199
1200
1201
1202
  if (options->daemonic && !options->no_syslog)
    {
#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 */
    }
  
1203
1204
1205
1206
1207
  if (!options->corefile && !daemon_disable_core())
    {
      werror("Disabling of core dumps failed.\n");
      return EXIT_FAILURE;
    }
1208

1209
1210
1211
1212
1213
  if (!options->random) 
    {
      werror("Failed to initialize randomness generator.\n");
      return EXIT_FAILURE;
    }
1214
1215
1216
1217

  if (!read_host_key(options->hostkey, options->signature_algorithms, keys))
    return EXIT_FAILURE;

1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
  if (options->with_loginauthmode) 
    fds = io_listen_list(&options->local, 
			 make_lshd_listen_callback 
			 (options, keys, 
			  make_lshd_login_service(options)), 
			 &lshd_exception_handler);           
  else 
    fds = io_listen_list(&options->local, 
			 make_lshd_listen_callback 
			 (options, keys, 
			  make_lshd_connection_service(options)), 
			 &lshd_exception_handler); 
  
1231
1232
1233
1234
1235
1236
1237
  if (!fds)
    {
      werror("Could not bind any address.\n");
      return EXIT_FAILURE;
    }

  remember_resource(resources, fds);
1238
  
1239
  if (options->daemonic)
1240
    {
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
      if (options->no_syslog)
        {
          /* Just put process into the background. --no-syslog is an
           * inappropriate name */
          switch (fork())
            {
            case 0:
              /* Child */
              /* FIXME: Should we create a new process group, close our tty
               * and stdio, etc? */
              trace("forked into background. New pid: %i.\n", getpid());
              break;
              
            case -1:
              /* Error */
1256
              werror("background_process: fork failed %e\n", errno);
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
              break;
              
            default:
              /* Parent */
              _exit(EXIT_SUCCESS);
            }
        }
      else
        {
          switch (daemon_init())
            {
            case 0:
              werror("lshd: Spawning into background failed.\n");