server.c 18.2 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
117
118
119
120
121
122
123
124
125
126
#define CLASS_DEFINE
#include "server.h.x"
#undef CLASS_DEFINE

#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)
	(id_comment simple "char *")

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

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

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

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

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

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

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

Niels Möller's avatar
Niels Möller committed
172
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
173
174
175
				    UINT32 length,
				    UINT8 *line)
{
176
  CAST(server_line_handler, closure, *h);
Niels Möller's avatar
Niels Möller committed
177
  
Niels Möller's avatar
Niels Möller committed
178
179
180
181
182
  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
183
184
185
	  struct read_handler *new = make_read_packet
	    (make_packet_unpad
	     (make_packet_debug(&closure->connection->super,
186
				"recieved")),
Niels Möller's avatar
Niels Möller committed
187
	     closure->connection);
Niels Möller's avatar
Niels Möller committed
188
189
	  
	  closure->connection->client_version
Niels Möller's avatar
Niels Möller committed
190
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
191

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

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

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

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

Niels Möller's avatar
Niels Möller committed
225
struct read_handler *make_server_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
226
{
227
  NEW(server_line_handler, closure);
Niels Möller's avatar
Niels Möller committed
228

Niels Möller's avatar
Niels Möller committed
229
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
230
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
231
232
233
234
  
  return make_read_line(&closure->super);
}

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

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

Niels Möller's avatar
Niels Möller committed
250
251
  connected->random = random;  
  connected->init = init;
252
  connected->kexinit_handler = kexinit_handler;
Niels Möller's avatar
Niels Möller committed
253
254
255
256
  
  return &connected->super;
}

257
static int server_die(struct close_callback *closure, int reason)
Niels Möller's avatar
Niels Möller committed
258
{
259
260
261
262
  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
263
264
265
  return 0;  /* Ignored */
}

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

  c->f = server_die;

  return c;
}

275
/* Session */
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/* 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)

       ; 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
293
294
295
296
/* Recieve channel data */
static int do_recieve(struct ssh_channel *c,
		      int type, struct lsh_string *data)
{
297
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
298
299
300

  /* 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
301
302
303
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
304
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
305
306
307
308
309
310
311
312
313
314
315
316
    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)
{
317
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
318

Niels Möller's avatar
Niels Möller committed
319
320
321
322
323
324
325
  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
326
327
328
329
  
  return LSH_OK | LSH_GOON;
}

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

  write_buffer_close(closure->in->buffer);

  return LSH_OK | LSH_GOON;
}

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

  init_channel(&self->super);

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

  /* 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
359
360
361
362

  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
363
364
365
366
  
  return &self->super;
}

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

376
377
378
379
380
381
382
383
#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)
{
384
  CAST(open_session, closure, c);
385
386
387
388
389
390
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
391
392
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
393
394
395
396
397
}

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

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

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

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

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

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

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

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

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

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

512
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
513
  
Niels Möller's avatar
Niels Möller committed
514
515
516
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
517
518
519
520
521
522
523
524
525
526
527
		    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
528
529
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
530
531

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

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

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

  return &self->super;
}

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

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

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

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

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

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

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

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

  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
612
613
  /* {in|out|err}[0] is for reading,
   * {in|out|err}[1] for writing. */
614

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

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
663
		    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");
		    
664
665
666
667
668
669
670
671
672
673
674
675
676
		    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);
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
717
#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]);

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

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

		  debug("Parent process\n");

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

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

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

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