server.c 21.4 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
/* server.c
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * $Id$ */

/* 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
22
23
24
25
26
 */

#include "server.h"

#include "abstract_io.h"
27
#include "channel.h"
28
#include "connection.h"
Niels Möller's avatar
Niels Möller committed
29
30
#include "debug.h"
#include "format.h"
31
32
33
#include "keyexchange.h"
#include "read_line.h"
#include "read_packet.h"
Niels Möller's avatar
Niels Möller committed
34
#include "reaper.h"
35
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
36
#include "translate_signal.h"
Niels Möller's avatar
Niels Möller committed
37
#include "unpad.h"
38
#include "version.h"
Niels Möller's avatar
Niels Möller committed
39
40
#include "werror.h"
#include "xalloc.h"
41
#include "compress.h"
Niels Möller's avatar
Niels Möller committed
42

43
44
45
46
47
48
49
#include <assert.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>

50
51
52
53
54
55
56
57
/* Datafellows workaround.
 *
 * It seems that Datafellows' ssh2 client says it want to use protocol
 * version 1.99 in its greeting to the server. This behaviour is not
 * allowed by the specification. Define this to support it anyway. */

#define DATAFELLOWS_SSH2_GREETING_WORKAROUND

58
59
60
61
62
/* Socket workround */
#ifndef SHUTDOWN_WORKS_WITH_UNIX_SOCKETS

/* There's an how++ missing in the af_unix shutdown implementation of
 * some linux versions. Try an ugly workaround. */
63
64
65
66
67
68
#ifdef linux

/* From src/linux/include/net/sock.h */
#define RCV_SHUTDOWN	1
#define SEND_SHUTDOWN	2

69
70
71
72
#undef SHUT_RD
#undef SHUT_WR
#undef SHUT_RD_WR

73
74
75
#define SHUT_RD RCV_SHUTDOWN
#define SHUT_WR SEND_SHUTDOWN
#define SHUT_RD_WR (RCV_SHUTDOWN | SEND_SHUTDOWN)
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

#else /* !linux */

/* Don't know how to work around the broken shutdown(). So disable it
 * completely. */

#define SHUTDOWN(fd, how) 0

#endif /* !linux */
#endif /* !SHUTDOWN_WORKS_WITH_UNIX_SOCKETS */

#ifndef SHUTDOWN
#define SHUTDOWN(fd, how) (shutdown((fd), (how)))
#endif

#ifndef SHUT_RD
#define SHUT_RD 0
#endif

#ifndef SHUT_WR
#define SHUT_WR 1
#endif

#ifndef SHUT_RD_WR
#define SHUT_RD_WR 2
#endif
102

Niels Möller's avatar
Niels Möller committed
103
104
105
106
/* For debug */
#include <signal.h>
#include <unistd.h>

107
108
109
110
111
112
113
114
115
#include "server.c.x"

/* CLASS:
   (class
     (name server_callback)
     (super fd_callback)
     (vars
       (backend object io_backend)

116
117
       (secret object signer) ; Secret key
       (host_key string)      ; Public key 
118

119
120
       (block_size simple UINT32)
       (id_comment simple "const char *")
121

122
123
124
       (random object randomness)
       (init object make_kexinit)
       (kexinit_handler object packet_handler)))
125
*/
126

Niels Möller's avatar
Niels Möller committed
127
static int server_initiate(struct fd_callback **c,
Niels Möller's avatar
Niels Möller committed
128
129
			   int fd)
{
130
  CAST(server_callback, closure, *c);
Niels Möller's avatar
Niels Möller committed
131
  
132
133
134
  struct ssh_connection *connection
    = make_ssh_connection(closure->kexinit_handler);

Niels Möller's avatar
Niels Möller committed
135
136
  int res;
  
137
  verbose("server_initiate()\n");
Niels Möller's avatar
Niels Möller committed
138
139
140
141
142

  connection_init_io(connection,
		     io_read_write(closure->backend, fd,
				   make_server_read_line(connection),
				   closure->block_size,
143
				   make_server_close_handler(connection)),
Niels Möller's avatar
Niels Möller committed
144
145
		     closure->random);

Niels Möller's avatar
Niels Möller committed
146
147
  
  connection->server_version
Niels Möller's avatar
Niels Möller committed
148
    = ssh_format("SSH-%lz-%lz %lz",
Niels Möller's avatar
Niels Möller committed
149
150
151
		 PROTOCOL_VERSION,
		 SOFTWARE_SERVER_VERSION,
		 closure->id_comment);
Niels Möller's avatar
Niels Möller committed
152

Niels Möller's avatar
Niels Möller committed
153
154
  res = A_WRITE(connection->raw,
		 ssh_format("%lS\r\n", connection->server_version));
155
  if (LSH_CLOSEDP(res))
Niels Möller's avatar
Niels Möller committed
156
157
    return res;

158
159
160
  return res | initiate_keyexchange(connection, CONNECTION_SERVER,
				    MAKE_KEXINIT(closure->init),
				    NULL);
Niels Möller's avatar
Niels Möller committed
161
162
}

163
164
165
166
167
168
169
170
/* CLASS:
   (class
     (name server_line_handler)
     (super line_handler)
     (vars
       (connection object ssh_connection)))
*/

Niels Möller's avatar
Niels Möller committed
171
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
172
173
174
				    UINT32 length,
				    UINT8 *line)
{
175
  CAST(server_line_handler, closure, *h);
Niels Möller's avatar
Niels Möller committed
176
  
Niels Möller's avatar
Niels Möller committed
177
178
179
  if ( (length >= 4) && !memcmp(line, "SSH-", 4))
    {
      /* Parse and remember format string */
180
181
182
183
184
      if ( ((length >= 8) && !memcmp(line + 4, "2.0-", 4))
#ifdef DATAFELLOWS_SSH2_GREETING_WORKAROUND
	   || ((length >= 9) && !memcmp(line + 4, "1.99-", 5))
#endif
	   )
Niels Möller's avatar
Niels Möller committed
185
	{
186
187
188
189
190
191
192
193
194
195
	  struct read_handler *new = 
	    make_read_packet(
	      make_packet_unpad(
	        make_packet_inflate(
	          make_packet_debug(&closure->connection->super, "received"),
	          closure->connection
	        )
	      ),
	      closure->connection
	    );
Niels Möller's avatar
Niels Möller committed
196
197
	  
	  closure->connection->client_version
Niels Möller's avatar
Niels Möller committed
198
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
199

Niels Möller's avatar
Niels Möller committed
200
201
202
203
204
	  verbose("Client version: ");
	  verbose_safe(closure->connection->client_version->length,
		       closure->connection->client_version->data);
	  verbose("\n");
	  
Niels Möller's avatar
Niels Möller committed
205
	  /* FIXME: Cleanup properly. */
206
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
207
208
209
210
211

	  return new;
	}
      else
	{
212
	  wwrite("Unsupported protocol version: ");
Niels Möller's avatar
Niels Möller committed
213
	  werror_safe(length, line);
214
	  wwrite("\n");
Niels Möller's avatar
Niels Möller committed
215

Niels Möller's avatar
Niels Möller committed
216
	  /* FIXME: Clean up properly */
217
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
218
219
	  *h = 0;
		  
Niels Möller's avatar
Niels Möller committed
220
221
222
223
224
225
226
227
228
229
230
231
	  return 0;
	}
    }
  else
    {
      /* Display line */
      werror_safe(length, line);

      /* Read next line */
      return 0;
    }
}
Niels Möller's avatar
Niels Möller committed
232

Niels Möller's avatar
Niels Möller committed
233
struct read_handler *make_server_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
234
{
235
  NEW(server_line_handler, closure);
Niels Möller's avatar
Niels Möller committed
236

Niels Möller's avatar
Niels Möller committed
237
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
238
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
239
240
241
242
  
  return make_read_line(&closure->super);
}

243
244
struct fd_callback *
make_server_callback(struct io_backend *b,
245
		     const char *comment,
246
		     UINT32 block_size,
Niels Möller's avatar
Niels Möller committed
247
248
		     struct randomness *random,
		     struct make_kexinit *init,
249
		     struct packet_handler *kexinit_handler)
Niels Möller's avatar
Niels Möller committed
250
{
251
  NEW(server_callback, connected);
Niels Möller's avatar
Niels Möller committed
252
253
254
255
256

  connected->super.f = server_initiate;
  connected->backend = b;
  connected->block_size = block_size;
  connected->id_comment = comment;
257

Niels Möller's avatar
Niels Möller committed
258
259
  connected->random = random;  
  connected->init = init;
260
  connected->kexinit_handler = kexinit_handler;
Niels Möller's avatar
Niels Möller committed
261
262
263
264
  
  return &connected->super;
}

265
266
267
268
269
270
271
272
273
/* CLASS:
   (class
     (name server_cleanup)
     (super close_callback)
     (vars
       (connection object ssh_connection)))
*/

static int server_die(struct close_callback *c, int reason)
Niels Möller's avatar
Niels Möller committed
274
{
275
276
  CAST(server_cleanup, closure, c);
  
277
278
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
279
    wwrite("Connection died.\n");
280

281
282
  KILL_RESOURCE_LIST(closure->connection->resources);
  
Niels Möller's avatar
Niels Möller committed
283
284
285
  return 0;  /* Ignored */
}

286
struct close_callback *make_server_close_handler(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
287
{
288
  NEW(server_cleanup, closure);
Niels Möller's avatar
Niels Möller committed
289

290
291
  closure->connection = c;  
  closure->super.f = server_die;
Niels Möller's avatar
Niels Möller committed
292

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  return &closure->super;
}


/* CLASS:
   (class
     (name process_resource)
     (super resource)
     (vars
       (pid . pid_t)
       (signal . int)))
*/

static void do_kill_process(struct resource *r)
{
  CAST(process_resource, self, r);

  if (!self->super.alive)
    return;

  self->super.alive = 0;
  /* NOTE: This function only makes one attempt at killing the
   * process. An improvement would be to install a callout handler
   * which will kill -9 the process after a delay, if it hasn't died
   * voluntarily. */
  
  if (kill(self->pid, self->signal) < 0)
    {
      werror("do_kill_process: kill() failed (errno = %d): %s\n",
	     errno, strerror(errno));
    }
}
          
struct resource *make_process_resource(pid_t pid, int signal)
{
  NEW(process_resource, self);
  self->super.alive = 1;

  self->pid = pid;
  self->signal = signal;

  self->super.kill = do_kill_process;

  return &self->super;
Niels Möller's avatar
Niels Möller committed
337
338
}

339
/* Session */
340
341
342
343
344
345
346
347
348
/* CLASS:
   (class
     (name server_session)
     (super ssh_channel)
     (vars
       ; User information
       (user object unix_user)

       ; Non-zero if a shell or command has been started. 
349
350
351
352
353
       ;; (running simple int)

       ; Resource to kill when the channel is closed. 
       (process object resource)

354
355
356
357
358
359
       ; Child process's stdio 
       (in object io_fd)
       (out object io_fd)
       (err object io_fd)))
*/

360
361
/* Receive channel data */
static int do_receive(struct ssh_channel *c,
Niels Möller's avatar
Niels Möller committed
362
363
		      int type, struct lsh_string *data)
{
364
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
365
366

  /* FIXME: Examine the size of the write buffer, to decide if the
367
   * receive window should be adjusted. */
Niels Möller's avatar
Niels Möller committed
368
369
370
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
371
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
372
    case CHANNEL_STDERR_DATA:
373
      wwrite("Ignoring unexpected stderr data.\n");
Niels Möller's avatar
Niels Möller committed
374
375
376
377
378
379
380
381
382
383
      lsh_string_free(data);
      return LSH_OK | LSH_GOON;
    default:
      fatal("Internal error!\n");
    }
}

/* We may send more data */
static int do_send(struct ssh_channel *c)
{
384
  CAST(server_session, session, c);
Niels Möller's avatar
Niels Möller committed
385

386
387
388
389
  assert(session->out->super.read);
  assert(session->out->handler);
  assert(session->err->super.read);
  assert(session->err->handler);
Niels Möller's avatar
Niels Möller committed
390
  
391
392
  session->out->super.want_read = 1;
  session->err->super.want_read = 1;
Niels Möller's avatar
Niels Möller committed
393
394
395
396
  
  return LSH_OK | LSH_GOON;
}

397
static int do_eof(struct ssh_channel *channel)
Niels Möller's avatar
Niels Möller committed
398
{
399
  CAST(server_session, session, channel);
Niels Möller's avatar
Niels Möller committed
400

401
  write_buffer_close(session->in->buffer);
Niels Möller's avatar
Niels Möller committed
402

403
404
405
406
407
  if ( (channel->flags & CHANNEL_SENT_EOF)
       && (channel->flags & CHANNEL_CLOSE_AT_EOF))
    return channel_close(channel);
  else
    return LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
408
409
}

410
411
412
413
414
415
416
417
static void do_close(struct ssh_channel *c)
{
  CAST(server_session, session, c);

  if (session->process)
    KILL_RESOURCE(session->process);
}

418
419
420
421
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
422
  NEW(server_session, self);
423
424
425
426

  init_channel(&self->super);

  self->super.max_window = max_window;
427
428
  /* We don't want to receive any data before we have forked some
   * process to receive it. */
Niels Möller's avatar
Niels Möller committed
429
  self->super.rec_window_size = 0;
430
431
432
433
434

  /* FIXME: Make maximum packet size configurable. */
  self->super.rec_max_packet = SSH_MAX_PACKET;

  self->super.request_types = request_types;
435
436
437

  self->super.close = do_close;
  
438
439
  self->user = user;

440
#if 0
441
  self->running = 0;
442
443
444
#endif
  self->process = NULL;
  
Niels Möller's avatar
Niels Möller committed
445
446
447
  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
448
449
450
451
  
  return &self->super;
}

452
453
454
455
456
457
458
459
460
/* CLASS:
   (class
     (name open_session)
     (super channel_open)
     (vars
       (user object unix_user)
       (session_requests object alist)))
*/

461
462
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

463
464
465
466
static struct ssh_channel *
do_open_session(struct channel_open *c,
		struct ssh_connection *connection UNUSED,
		struct simple_buffer *args,
467
468
469
		UINT32 *error UNUSED,
		char **error_msg UNUSED,
		struct lsh_string **data UNUSED)
470
{
471
  CAST(open_session, closure, c);
472
473
474
475
476
477
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
478
479
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
480
481
482
483
484
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
485
  NEW(open_session, closure);
486
487
488
489
490
491
492
493

  closure->super.handler = do_open_session;
  closure->user = user;
  closure->session_requests = session_requests;
  
  return &closure->super;
}

494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/* CLASS:
   (class
     (name server_connection_service)
     (super unix_service)
     (vars
       (global_requests object alist)

       ; Requests specific to session channels 
       (session_requests object alist)

       ; FIXME: Doesn't support any channel types but "session". This
       ; must be fixed to support for "direct-tcpip" channels.
       ))
*/

509
510
511
512
/* Start an authenticated ssh-connection service */
static struct ssh_service *do_login(struct unix_service *c,
				    struct unix_user *user)
{
513
  CAST(server_connection_service, closure, c);
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528

  debug("server.c: do_login()\n");
  
  return
    make_connection_service(closure->global_requests,
			    make_alist(1, ATOM_SESSION,
				       make_open_session(user,
							 closure->session_requests),
				       -1),
			    NULL);
}

struct unix_service *make_server_session_service(struct alist *global_requests,
						 struct alist *session_requests)
{
529
  NEW(server_connection_service, closure);
530
531
532
533
534
535
536
537

  closure->super.login = do_login;
  closure->global_requests = global_requests;
  closure->session_requests = session_requests;
  
  return &closure->super;
}

Niels Möller's avatar
Niels Möller committed
538
539
540
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
				      int core, int signal)
{
541
542
  struct lsh_string *msg = ssh_format("Process killed by %lz.\n",
				      strsignal(signal));
Niels Möller's avatar
Niels Möller committed
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
  
  return format_channel_request(ATOM_EXIT_SIGNAL,
				channel,
				0,
				"%i%c%fS%z",
				signal_local_to_network(signal),
				core,
				msg, "");
}

struct lsh_string *format_exit(struct ssh_channel *channel, int value)
{
  return format_channel_request(ATOM_EXIT_STATUS,
				channel,
				0,
				"%i", value);
}
560
561
562
563
564
565
566
567
568

/* CLASS:
   (class
     (name exit_shell)
     (super exit_callback)
     (vars
       (session object server_session)))
*/

Niels Möller's avatar
Niels Möller committed
569
570
571
static void do_exit_shell(struct exit_callback *c, int signaled,
			  int core, int value)
{
572
  CAST(exit_shell, closure, c);
Niels Möller's avatar
Niels Möller committed
573
574
575
  struct server_session *session = closure->session;
  struct ssh_channel *channel = &session->super;
  
576
  CHECK_TYPE(server_session, session);
577

578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  if (! session->process->alive)
    {
      /* The process was killed by a the resource callback (most
       * likely because the connection died. Keep silent. */
      debug("do_exit_shell: Process already flagged as dead.\n");
      return;
    }
  
  /* No need to kill the process. */
  session->process->alive = 0;
  
  /* FIXME: Should we explicitly mark these files for closing? The
   * io-backend should notice EOF anyway. And the client should send
   * EOF when it receives news of the process's death, unless it
   * really wants to talk to any live children processes. */
#if 0
594
  close_fd(&session->in->super, 0);
595
#endif
596
#if 0
Niels Möller's avatar
Niels Möller committed
597
598
  close_fd(session->out);
  close_fd(session->err);
599
#endif
Niels Möller's avatar
Niels Möller committed
600
601

#if 0
Niels Möller's avatar
Niels Möller committed
602
603
604
605
606
607
608
609
610
611
  if (!(channel->flags & CHANNEL_SENT_EOF)
      /* Don't send eof if the process died violently. */
      && !signaled)
    {
      int res = channel_eof(channel);
      if (LSH_CLOSEDP(res))
	/* FIXME: Can we do anything better with the return code than
	 * ignore it? */
	return;
    }
Niels Möller's avatar
Niels Möller committed
612
#endif
Niels Möller's avatar
Niels Möller committed
613

614
  /* We close when we have both sent and received eof. */
615
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
616
  
Niels Möller's avatar
Niels Möller committed
617
618
619
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
620
621
622
623
624
		    signaled
		    ? format_exit_signal(channel, core, value)
		    : format_exit(channel, value));

      if (!LSH_CLOSEDP(res)
625
626
	  && (channel->flags & CHANNEL_SENT_EOF)
	  && (channel->flags & CHANNEL_RECEIVED_EOF))
627
628
629
630
631
	{
	  /* We have sent EOF already, so initiate close */
	  res |= channel_close(channel);
	}

Niels Möller's avatar
Niels Möller committed
632
633
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
634
635

      (void) res;
Niels Möller's avatar
Niels Möller committed
636
637
638
639
640
641
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
642
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
643
644
645
646
647
648
649

  self->super.exit = do_exit_shell;
  self->session = session;

  return &self->super;
}

650
651
652
653
654
655
656
657
658
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

659
660
661
662
663
/* Creates a one-way socket connection. Returns 1 on successm 0 on
 * failure. fds[0] is for reading, fds[1] for writing (like for the
 * pipe() system call). */
static int make_pipe(int *fds)
{
Niels Möller's avatar
Niels Möller committed
664
665
666
667
668
669
670
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
    {
      werror("socketpair() failed: %s\n", strerror(errno));
      return 0;
    }
  debug("Created socket pair. Using fd:s %d <-- %d\n", fds[0], fds[1]);

671
  if (SHUTDOWN(fds[0], SHUT_WR) < 0)
Niels Möller's avatar
Niels Möller committed
672
673
674
675
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
676
  if (SHUTDOWN(fds[1], SHUT_RD) < 0)
Niels Möller's avatar
Niels Möller committed
677
    {
678
      werror("shutdown(%d, REC) failed: %s\n", fds[0], strerror(errno));
Niels Möller's avatar
Niels Möller committed
679
680
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
681
  
Niels Möller's avatar
Niels Möller committed
682
  return 1;
683
684
}

685
static char *make_env_pair(const char *name, struct lsh_string *value)
686
{
687
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
688
689
}

690
static char *make_env_pair_c(const char *name, char *value)
691
{
692
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
693
694
695
696
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
697
			  struct ssh_connection *connection,
698
699
700
			  int want_reply,
			  struct simple_buffer *args)
{
701
  CAST(shell_request, closure, c);
702
703
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
704
705
706
  int in[2];
  int out[2];
  int err[2];
707

708
  CHECK_TYPE(server_session, session);
709
710
711
712

  if (!parse_eod(args))
    return LSH_FAIL | LSH_DIE;

713
  if (session->process)
714
715
716
    /* Already spawned a shell or command */
    goto fail;
  
Niels Möller's avatar
Niels Möller committed
717
718
  /* {in|out|err}[0] is for reading,
   * {in|out|err}[1] for writing. */
719

Niels Möller's avatar
Niels Möller committed
720
  if (make_pipe(in))
721
    {
Niels Möller's avatar
Niels Möller committed
722
      if (make_pipe(out))
723
	{
Niels Möller's avatar
Niels Möller committed
724
	  if (make_pipe(err))
725
726
727
728
729
730
731
732
733
734
	    {
	      pid_t child;
	      
	      switch(child = fork())
		{
		case -1:
		  werror("fork() failed: %s\n", strerror(errno));
		  /* Close and return channel_failure */
		  break; 
		case 0:
735
		  { /* Child */
736
737
738
739
740
741
		    char *shell = session->user->shell->data;
#define MAX_ENV 7
		    char *env[MAX_ENV];
		    char *tz = getenv("TZ");
		    int i = 0;

742
743
744
745
746
747
		    int old_stderr;
		    
		    debug("do_spawn_shell: Child process\n");

		    if (!session->user->shell)
		      {
748
			wwrite("No login shell!\n");
749
750
751
752
753
754
			exit(EXIT_FAILURE);
		      }

		    if (getuid() != session->user->uid)
		      if (!change_uid(session->user))
			{
755
			  wwrite("Changing uid failed!\n");
756
757
758
759
760
761
762
			  exit(EXIT_FAILURE);
			}
		    
		    assert(getuid() == session->user->uid);
		    
		    if (!change_dir(session->user))
		      {
763
			wwrite("Could not change to home (or root) directory!\n");
764
765
766
767
768
			exit(EXIT_FAILURE);
		      }

		    debug("Child: Setting up environment.\n");
		    
769
770
771
772
773
774
775
776
777
778
779
780
781
		    env[i++] = make_env_pair("LOGNAME", session->user->name);
		    env[i++] = make_env_pair("USER", session->user->name);
		    env[i++] = make_env_pair("SHELL", session->user->shell);
		    if (session->user->home)
		      env[i++] = make_env_pair("HOME", session->user->home);
		    if (tz)
		      env[i++] = make_env_pair_c("TZ", tz);

		    /* FIXME: The value of $PATH should not be hard-coded */
		    env[i++] = "PATH=/bin:/usr/bin";
		    env[i++] = NULL;
		    
		    assert(i <= MAX_ENV);
782
783
784
785
786
787
788
789
790
791
792
793
794
#undef MAX_ENV

		    debug("Child: Environment:\n");
		    for (i=0; env[i]; i++)
		      debug("Child:   '%s'\n", env[i]);
		    
		    /* Close all descriptors but those used for
		     * communicationg with parent. We rely on the
		     * close-on-exec flag for all fd:s handled by the
		     * backend. */
		    
		    if (dup2(in[0], STDIN_FILENO) < 0)
		      {
795
			wwrite("Can't dup stdin!\n");
796
797
798
799
800
801
802
			exit(EXIT_FAILURE);
		      }
		    close(in[0]);
		    close(in[1]);
		    
		    if (dup2(out[1], STDOUT_FILENO) < 0)
		      {
803
			wwrite("Can't dup stdout!\n");
804
805
806
807
808
809
			exit(EXIT_FAILURE);
		      }
		    close(out[0]);
		    close(out[1]);

		    if ((old_stderr = dup(STDERR_FILENO)) < 0)
810
		      wwrite("Couldn't save old file_no.\n");
811
812
813
814
815
816
817
818
819
820
821
822
		    io_set_close_on_exec(old_stderr);

		    debug("Child: Duping stderr (bye).\n");
		    
		    if (dup2(err[1], STDERR_FILENO) < 0)
		      {
			/* Can't write any message to stderr. */ 
			exit(EXIT_FAILURE);
		      }
		    close(err[0]);
		    close(err[1]);

823
#if 1
Niels Möller's avatar
Niels Möller committed
824
825
826
827
828
829
830
831
832
833
834
		    execle(shell, shell, NULL, env);
#else
#define GREETING "Hello world!\n"
		    if (write(STDOUT_FILENO, GREETING, strlen(GREETING)) < 0)
		      _exit(errno);
		    kill(getuid(), SIGSTOP);
		    if (write(STDOUT_FILENO, shell, strlen(shell)) < 0)
		      _exit(125);
		    _exit(126);
#undef GREETING
#endif
835
836
837
838
839
840
841
842
843
844
845
846
847
848
		    /* exec failed! */
		    {
		      int exec_errno = errno;

		      if (dup2(old_stderr, STDERR_FILENO) < 0)
			{
			  /* This is really bad... We can't restore stderr
			   * to report our problems. */
			  char msg[] = "child: execle() failed!\n";
			  write(old_stderr, msg, sizeof(msg));
			}
		      else
			debug("Child: execle() failed (errno = %d): %s\n",
			      exec_errno, strerror(exec_errno));
849

Niels Möller's avatar
Niels Möller committed
850
		      _exit(EXIT_FAILURE);
851
		    }
852
853
854
855
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
856
857
858
859

		  debug("Parent process\n");
		  REAP(closure->reap, child, make_exit_shell(session));
		  
860
		  /* Close the child's fd:s */
Niels Möller's avatar
Niels Möller committed
861
862
		  close(in[0]);
		  close(out[1]);
Niels Möller's avatar
Niels Möller committed
863
		  close(err[1]);
864

Niels Möller's avatar
Niels Möller committed
865
		  session->in
Niels Möller's avatar
Niels Möller committed
866
		    = io_write(closure->backend, in[1],
867
868
869
			       SSH_MAX_PACKET,
			       /* FIXME: Use a proper close callback */
			       make_channel_close(channel));
Niels Möller's avatar
Niels Möller committed
870
871
872
873
874
875
876
877
878
		  session->out
		    = io_read(closure->backend, out[0],
			      make_channel_read_data(channel),
			      NULL);
		  session->err
		    = io_read(closure->backend, err[0],
			      make_channel_read_stderr(channel),
			      NULL);

879
		  channel->receive = do_receive;
Niels Möller's avatar
Niels Möller committed
880
		  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
881
		  channel->eof = do_eof;
Niels Möller's avatar
Niels Möller committed
882
		  
883
#if 0
884
		  session->running = 1;
885
886
887
888
889
890
891
892
893
894
895
896
897
898
#endif
		  session->process
		    = make_process_resource(child, SIGHUP);

		  /* Make sure that the process and it's stdio is
		   * cleaned up if the connection dies. */
		  REMEMBER_RESOURCE
		    (connection->resources, session->process);
		  REMEMBER_RESOURCE
		    (connection->resources, &session->in->super.super);
		  REMEMBER_RESOURCE
		    (connection->resources, &session->out->super.super);
		  REMEMBER_RESOURCE
		    (connection->resources, &session->err->super.super);
Niels Möller's avatar
Niels Möller committed
899
900
901
902
903
904

		  return (want_reply
			  ? A_WRITE(channel->write,
				    format_channel_success(channel
							   ->channel_number))
			  : 0) | LSH_CHANNEL_READY_REC;
905
		}
Niels Möller's avatar
Niels Möller committed
906
907
	      close(err[0]);
	      close(err[1]);
908
	    }
Niels Möller's avatar
Niels Möller committed
909
910
	  close(out[0]);
	  close(out[1]);
911
	}
Niels Möller's avatar
Niels Möller committed
912
913
      close(in[0]);
      close(in[1]);
914
915
916
917
918
919
920
    }
 fail:
  return want_reply
    ? A_WRITE(channel->write, format_channel_failure(channel->channel_number))
    : LSH_OK | LSH_GOON;
}

Niels Möller's avatar
Niels Möller committed
921
922
struct channel_request *make_shell_handler(struct io_backend *backend,
					   struct reap *reap)
923
{
924
  NEW(shell_request, closure);
925
926

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
927
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
928
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
929
  
930
931
932
  return &closure->super;
}