server.c 18.1 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
256
257
258
  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
259
260
261
  return 0;  /* Ignored */
}

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

  c->f = server_die;

  return c;
}

271
/* Session */
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/* 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
289
290
291
292
/* Recieve channel data */
static int do_recieve(struct ssh_channel *c,
		      int type, struct lsh_string *data)
{
293
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
294
295
296

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

Niels Möller's avatar
Niels Möller committed
315
316
317
318
319
320
321
  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
322
323
324
325
  
  return LSH_OK | LSH_GOON;
}

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

  write_buffer_close(closure->in->buffer);

  return LSH_OK | LSH_GOON;
}

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

  init_channel(&self->super);

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

  /* 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
355
356
357
358

  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
359
360
361
362
  
  return &self->super;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

      (void) res;
Niels Möller's avatar
Niels Möller committed
528
529
530
531
532
533
      return;
    }
}

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

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

  return &self->super;
}

542
543
544
545
546
547
548
549
550
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

551
552
553
554
555
/* 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
556
557
558
559
560
561
562
  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]);

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

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

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

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
			  int want_reply,
			  struct simple_buffer *args)
{
592
  CAST(shell_request, closure, c);
593
594
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
595
596
597
  int in[2];
  int out[2];
  int err[2];
598

599
  CHECK_TYPE(server_session, session);
600
601
602
603
604
605
606
607

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

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

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
		    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");
		    
660
661
662
663
664
665
666
667
668
669
670
671
672
		    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);
673
674
675
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
#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]);

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

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

		  debug("Parent process\n");

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

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

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

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