server.c 18.4 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* server.c
 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 *
 * $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
24
25
26
27
28
 */

#include "server.h"

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

Niels Möller's avatar
Niels Möller committed
44
45
46
47
#ifndef _GNU_SOURCE
#warning _GNU_SOURCE undefined
#endif

48
49
50
51
52
53
54
#include <assert.h>
#include <string.h>
#include <errno.h>

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

55
56
57
58
59
/* 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. */
60
61
62
63
64
65
#ifdef linux

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

66
67
68
69
#undef SHUT_RD
#undef SHUT_WR
#undef SHUT_RD_WR

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

#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
99

Niels Möller's avatar
Niels Möller committed
100
101
102
103
/* For debug */
#include <signal.h>
#include <unistd.h>

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

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

        (secret object signer) ; Secret key
	(host_key string)      ; Public key 

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

	(random object randomness)
	(init object make_kexinit)
	(kexinit_handler object packet_handler)))
*/
123

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

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

  connection_init_io(connection,
		     io_read_write(closure->backend, fd,
				   make_server_read_line(connection),
				   closure->block_size,
				   make_server_close_handler()),
		     closure->random);

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

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

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

160
161
162
163
164
165
166
167
/* CLASS:
   (class
     (name server_line_handler)
     (super line_handler)
     (vars
       (connection object ssh_connection)))
*/

Niels Möller's avatar
Niels Möller committed
168
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
169
170
171
				    UINT32 length,
				    UINT8 *line)
{
172
  CAST(server_line_handler, closure, *h);
Niels Möller's avatar
Niels Möller committed
173
  
Niels Möller's avatar
Niels Möller committed
174
175
176
177
178
  if ( (length >= 4) && !memcmp(line, "SSH-", 4))
    {
      /* Parse and remember format string */
      if ((length >= 8) && !memcmp(line + 4, "2.0-", 4))
	{
Niels Möller's avatar
Niels Möller committed
179
180
181
	  struct read_handler *new = make_read_packet
	    (make_packet_unpad
	     (make_packet_debug(&closure->connection->super,
182
				"recieved")),
Niels Möller's avatar
Niels Möller committed
183
	     closure->connection);
Niels Möller's avatar
Niels Möller committed
184
185
	  
	  closure->connection->client_version
Niels Möller's avatar
Niels Möller committed
186
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
187

Niels Möller's avatar
Niels Möller committed
188
189
190
191
192
	  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
193
	  /* FIXME: Cleanup properly. */
194
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
195
196
197
198
199
200
201
202
203

	  return new;
	}
      else
	{
	  werror("Unsupported protocol version: ");
	  werror_safe(length, line);
	  werror("\n");

Niels Möller's avatar
Niels Möller committed
204
	  /* FIXME: Clean up properly */
205
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
206
207
	  *h = 0;
		  
Niels Möller's avatar
Niels Möller committed
208
209
210
211
212
213
214
215
216
217
218
219
	  return 0;
	}
    }
  else
    {
      /* Display line */
      werror_safe(length, line);

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

Niels Möller's avatar
Niels Möller committed
221
struct read_handler *make_server_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
222
{
223
  NEW(server_line_handler, closure);
Niels Möller's avatar
Niels Möller committed
224

Niels Möller's avatar
Niels Möller committed
225
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
226
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
227
228
229
230
  
  return make_read_line(&closure->super);
}

231
232
struct fd_callback *
make_server_callback(struct io_backend *b,
233
		     const char *comment,
234
		     UINT32 block_size,
Niels Möller's avatar
Niels Möller committed
235
236
		     struct randomness *random,
		     struct make_kexinit *init,
237
		     struct packet_handler *kexinit_handler)
Niels Möller's avatar
Niels Möller committed
238
{
239
  NEW(server_callback, connected);
Niels Möller's avatar
Niels Möller committed
240
241
242
243
244

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

Niels Möller's avatar
Niels Möller committed
246
247
  connected->random = random;  
  connected->init = init;
248
  connected->kexinit_handler = kexinit_handler;
Niels Möller's avatar
Niels Möller committed
249
250
251
252
  
  return &connected->super;
}

253
static int server_die(struct close_callback *closure, int reason)
Niels Möller's avatar
Niels Möller committed
254
{
255
  /* FIXME: If any processes are running, they should be killed now. */
256
257
258
259
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
    werror("Connection died.\n");

Niels Möller's avatar
Niels Möller committed
260
261
262
  return 0;  /* Ignored */
}

263
struct close_callback *make_server_close_handler(void)
Niels Möller's avatar
Niels Möller committed
264
{
265
  NEW(close_callback, c);
Niels Möller's avatar
Niels Möller committed
266
267
268
269
270
271

  c->f = server_die;

  return c;
}

272
/* Session */
273
274
275
276
277
278
279
280
281
282
/* 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. 
       (running simple int)
283
284
       ; FIXME: We need the pid as well, to be able to kill
       ; the process if the channel or connection is closed.
285
286
287
288
289
290
       ; Child process's stdio 
       (in object io_fd)
       (out object io_fd)
       (err object io_fd)))
*/

Niels Möller's avatar
Niels Möller committed
291
292
293
294
/* Recieve channel data */
static int do_recieve(struct ssh_channel *c,
		      int type, struct lsh_string *data)
{
295
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
296
297
298

  /* FIXME: Examine the size of the write buffer, to decide if the
   * recieve window should be adjusted. */
Niels Möller's avatar
Niels Möller committed
299
300
301
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
302
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
303
304
305
306
307
308
309
310
311
312
313
314
    case CHANNEL_STDERR_DATA:
      werror("Ignoring unexpected stderr data.\n");
      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)
{
315
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
316

Niels Möller's avatar
Niels Möller committed
317
318
319
320
321
322
323
  assert(closure->out->super.read);
  assert(closure->out->handler);
  assert(closure->err->super.read);
  assert(closure->err->handler);
  
  closure->out->super.want_read = 1;
  closure->err->super.want_read = 1;
Niels Möller's avatar
Niels Möller committed
324
325
326
327
  
  return LSH_OK | LSH_GOON;
}

Niels Möller's avatar
Niels Möller committed
328
329
330
331
332
333
334
335
336
static int do_eof(struct ssh_channel *c)
{
  CAST(server_session, closure, c);

  write_buffer_close(closure->in->buffer);

  return LSH_OK | LSH_GOON;
}

337
338
339
340
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
341
  NEW(server_session, self);
342
343
344
345

  init_channel(&self->super);

  self->super.max_window = max_window;
Niels Möller's avatar
Niels Möller committed
346
347
348
  /* We don't want to recieve any data before we have forked some
   * process to recieve it. */
  self->super.rec_window_size = 0;
349
350
351
352
353
354
355
356

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

  self->super.request_types = request_types;
  self->user = user;

  self->running = 0;
Niels Möller's avatar
Niels Möller committed
357
358
359
360

  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
361
362
363
364
  
  return &self->super;
}

365
366
367
368
369
370
371
372
373
/* CLASS:
   (class
     (name open_session)
     (super channel_open)
     (vars
       (user object unix_user)
       (session_requests object alist)))
*/

374
375
376
377
378
379
380
381
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

static struct ssh_channel *do_open_session(struct channel_open *c,
					   struct simple_buffer *args,
					   UINT32 *error,
					   char **error_msg,
					   struct lsh_string **data)
{
382
  CAST(open_session, closure, c);
383
384
385
386
387
388
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
389
390
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
391
392
393
394
395
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
396
  NEW(open_session, closure);
397
398
399
400
401
402
403
404

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

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
/* 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.
       ))
*/

420
421
422
423
/* Start an authenticated ssh-connection service */
static struct ssh_service *do_login(struct unix_service *c,
				    struct unix_user *user)
{
424
  CAST(server_connection_service, closure, c);
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439

  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)
{
440
  NEW(server_connection_service, closure);
441
442
443
444
445
446
447
448

  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
449
450
451
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
				      int core, int signal)
{
452
453
  struct lsh_string *msg = ssh_format("Process killed by %lz.\n",
				      strsignal(signal));
Niels Möller's avatar
Niels Möller committed
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  
  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);
}
471
472
473
474
475
476
477
478
479

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

Niels Möller's avatar
Niels Möller committed
480
481
482
static void do_exit_shell(struct exit_callback *c, int signaled,
			  int core, int value)
{
483
  CAST(exit_shell, closure, c);
Niels Möller's avatar
Niels Möller committed
484
485
486
  struct server_session *session = closure->session;
  struct ssh_channel *channel = &session->super;
  
487
  CHECK_TYPE(server_session, session);
488
489
490

  /* FIXME: Should we explicitly mark these files for closing?
   * The io-backend should notice EOF anyway. */
491
  close_fd(&session->in->super, 0);
492
#if 0
Niels Möller's avatar
Niels Möller committed
493
494
  close_fd(session->out);
  close_fd(session->err);
495
#endif
Niels Möller's avatar
Niels Möller committed
496
497

#if 0
Niels Möller's avatar
Niels Möller committed
498
499
500
501
502
503
504
505
506
507
  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
508
#endif
Niels Möller's avatar
Niels Möller committed
509

510
  /* FIXME: We shouldn't close until we have both sent and recieved eof. */
511
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
512
  
Niels Möller's avatar
Niels Möller committed
513
514
515
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
516
517
518
519
520
521
522
523
524
525
526
		    signaled
		    ? format_exit_signal(channel, core, value)
		    : format_exit(channel, value));

      if (!LSH_CLOSEDP(res)
	  && (channel->flags & CHANNEL_SENT_EOF))
	{
	  /* We have sent EOF already, so initiate close */
	  res |= channel_close(channel);
	}

Niels Möller's avatar
Niels Möller committed
527
528
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
529
530

      (void) res;
Niels Möller's avatar
Niels Möller committed
531
532
533
534
535
536
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
537
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
538
539
540
541
542
543
544

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

  return &self->super;
}

545
546
547
548
549
550
551
552
553
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

554
555
556
557
558
/* 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
559
560
561
562
563
564
565
  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]);

566
  if (SHUTDOWN(fds[0], SHUT_WR) < 0)
Niels Möller's avatar
Niels Möller committed
567
568
569
570
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
571
  if (SHUTDOWN(fds[1], SHUT_RD) < 0)
Niels Möller's avatar
Niels Möller committed
572
    {
573
      werror("shutdown(%d, REC) failed: %s\n", fds[0], strerror(errno));
Niels Möller's avatar
Niels Möller committed
574
575
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
576
  
Niels Möller's avatar
Niels Möller committed
577
  return 1;
578
579
}

580
static char *make_env_pair(const char *name, struct lsh_string *value)
581
{
582
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
583
584
}

585
static char *make_env_pair_c(const char *name, char *value)
586
{
587
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
588
589
590
591
592
593
594
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
			  int want_reply,
			  struct simple_buffer *args)
{
595
  CAST(shell_request, closure, c);
596
597
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
598
599
600
  int in[2];
  int out[2];
  int err[2];
601

602
  CHECK_TYPE(server_session, session);
603
604
605
606
607
608
609
610

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

  if (session->running)
    /* Already spawned a shell or command */
    goto fail;
  
Niels Möller's avatar
Niels Möller committed
611
612
  /* {in|out|err}[0] is for reading,
   * {in|out|err}[1] for writing. */
613

Niels Möller's avatar
Niels Möller committed
614
  if (make_pipe(in))
615
    {
Niels Möller's avatar
Niels Möller committed
616
      if (make_pipe(out))
617
	{
Niels Möller's avatar
Niels Möller committed
618
	  if (make_pipe(err))
619
620
621
622
623
624
625
626
627
628
	    {
	      pid_t child;
	      
	      switch(child = fork())
		{
		case -1:
		  werror("fork() failed: %s\n", strerror(errno));
		  /* Close and return channel_failure */
		  break; 
		case 0:
629
		  { /* Child */
630
631
632
633
634
635
		    char *shell = session->user->shell->data;
#define MAX_ENV 7
		    char *env[MAX_ENV];
		    char *tz = getenv("TZ");
		    int i = 0;

636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
		    int old_stderr;
		    
		    debug("do_spawn_shell: Child process\n");

		    if (!session->user->shell)
		      {
			werror("No login shell!\n");
			exit(EXIT_FAILURE);
		      }

		    if (getuid() != session->user->uid)
		      if (!change_uid(session->user))
			{
			  werror("Changing uid failed!\n");
			  exit(EXIT_FAILURE);
			}
		    
		    assert(getuid() == session->user->uid);
		    
		    if (!change_dir(session->user))
		      {
			werror("Could not change to home (or root) directory!\n");
			exit(EXIT_FAILURE);
		      }

		    debug("Child: Setting up environment.\n");
		    
663
664
665
666
667
668
669
670
671
672
673
674
675
		    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);
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
#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)
		      {
			werror("Can't dup stdin!\n");
			exit(EXIT_FAILURE);
		      }
		    close(in[0]);
		    close(in[1]);
		    
		    if (dup2(out[1], STDOUT_FILENO) < 0)
		      {
			werror("Can't dup stdout!\n");
			exit(EXIT_FAILURE);
		      }
		    close(out[0]);
		    close(out[1]);

		    if ((old_stderr = dup(STDERR_FILENO)) < 0)
		      werror("Couldn't safe old file_no.\n");
		    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]);

717
#if 1
Niels Möller's avatar
Niels Möller committed
718
719
720
721
722
723
724
725
726
727
728
		    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
729
730
731
732
733
734
735
736
737
738
739
740
741
742
		    /* 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));
743

Niels Möller's avatar
Niels Möller committed
744
		      _exit(EXIT_FAILURE);
745
		    }
746
747
748
749
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
750
751
752
753
754

		  debug("Parent process\n");

		  REAP(closure->reap, child, make_exit_shell(session));
		  
755
		  /* Close the child's fd:s */
Niels Möller's avatar
Niels Möller committed
756
757
		  close(in[0]);
		  close(out[1]);
Niels Möller's avatar
Niels Möller committed
758
		  close(err[1]);
759

Niels Möller's avatar
Niels Möller committed
760
		  session->in
Niels Möller's avatar
Niels Möller committed
761
		    = io_write(closure->backend, in[1],
Niels Möller's avatar
Niels Möller committed
762
763
				SSH_MAX_PACKET,
				/* FIXME: Use a proper close callback */
Niels Möller's avatar
Niels Möller committed
764
				make_channel_close(channel));
Niels Möller's avatar
Niels Möller committed
765
766
767
768
769
770
771
772
773
774
775
		  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);

		  channel->recieve = do_recieve;
		  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
776
		  channel->eof = do_eof;
Niels Möller's avatar
Niels Möller committed
777
		  
778
		  session->running = 1;
Niels Möller's avatar
Niels Möller committed
779
780
781
782
783
784

		  return (want_reply
			  ? A_WRITE(channel->write,
				    format_channel_success(channel
							   ->channel_number))
			  : 0) | LSH_CHANNEL_READY_REC;
785
		}
Niels Möller's avatar
Niels Möller committed
786
787
	      close(err[0]);
	      close(err[1]);
788
	    }
Niels Möller's avatar
Niels Möller committed
789
790
	  close(out[0]);
	  close(out[1]);
791
	}
Niels Möller's avatar
Niels Möller committed
792
793
      close(in[0]);
      close(in[1]);
794
795
796
797
798
799
800
    }
 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
801
802
struct channel_request *make_shell_handler(struct io_backend *backend,
					   struct reap *reap)
803
{
804
  NEW(shell_request, closure);
805
806

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
807
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
808
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
809
  
810
811
812
  return &closure->super;
}