server.c 18.5 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 60 61 62 63 64 65 66 67 68
/* Socket woraround */
#ifdef linux
#ifndef SHUT_RD

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

#define SHUT_RD RCV_SHUTDOWN
#define SHUT_WR SEND_SHUTDOWN
#define SHUT_RD_WR (RCV_SHUTDOWN | SEND_SHUTDOWN)
#endif /* !SHUT_RD */
#endif /* linux */

Niels Möller's avatar
Niels Möller committed
69 70 71 72
/* For debug */
#include <signal.h>
#include <unistd.h>

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
#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)))
*/
     
#if 0
98 99 100 101 102 103 104 105 106 107 108 109 110 111
struct server_callback
{
  struct fd_callback super;
  struct io_backend *backend;

  struct signer *secret;        /* secret key */
  struct lsh_string *host_key;  /* public key */
  UINT32 block_size;
  char *id_comment;

  struct randomness *random;
  struct make_kexinit *init;
  struct packet_handler *kexinit_handler;
};
112
#endif
113

Niels Möller's avatar
Niels Möller committed
114
static int server_initiate(struct fd_callback **c,
Niels Möller's avatar
Niels Möller committed
115 116
			   int fd)
{
Niels Möller's avatar
Niels Möller committed
117 118
  struct server_callback *closure = (struct server_callback *) *c;
  
119 120 121
  struct ssh_connection *connection
    = make_ssh_connection(closure->kexinit_handler);

Niels Möller's avatar
Niels Möller committed
122 123
  int res;
  
124
  verbose("server_initiate()\n");
Niels Möller's avatar
Niels Möller committed
125 126 127 128 129 130 131 132

  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
133 134
  
  connection->server_version
Niels Möller's avatar
Niels Möller committed
135
    = ssh_format("SSH-%lz-%lz %lz",
Niels Möller's avatar
Niels Möller committed
136 137 138
		 PROTOCOL_VERSION,
		 SOFTWARE_SERVER_VERSION,
		 closure->id_comment);
Niels Möller's avatar
Niels Möller committed
139

Niels Möller's avatar
Niels Möller committed
140 141
  res = A_WRITE(connection->raw,
		 ssh_format("%lS\r\n", connection->server_version));
142
  if (LSH_CLOSEDP(res))
Niels Möller's avatar
Niels Möller committed
143 144
    return res;

145 146 147
  return res | initiate_keyexchange(connection, CONNECTION_SERVER,
				    MAKE_KEXINIT(closure->init),
				    NULL);
Niels Möller's avatar
Niels Möller committed
148 149
}

150 151 152 153 154 155 156 157 158
/* CLASS:
   (class
     (name server_line_handler)
     (super line_handler)
     (vars
       (connection object ssh_connection)))
*/

#if 0
Niels Möller's avatar
Niels Möller committed
159 160 161 162 163
struct server_line_handler
{
  struct line_handler super;
  struct ssh_connection *connection;
};
164
#endif
Niels Möller's avatar
Niels Möller committed
165

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

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

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

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

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

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

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

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

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

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

251
static int server_die(struct close_callback *closure, int reason)
Niels Möller's avatar
Niels Möller committed
252
{
253 254 255 256
  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
257 258 259
  return 0;  /* Ignored */
}

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

  c->f = server_die;

  return c;
}

269
/* Session */
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
/* 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)))
*/

#if 0
288 289 290 291 292 293 294 295 296
struct server_session
{
  struct ssh_channel super;

  /* User information */
  struct unix_user *user;

  /* Non-zero if a shell or command has been started. */
  int running;
Niels Möller's avatar
Niels Möller committed
297 298

  /* Child process's stdio */
Niels Möller's avatar
Niels Möller committed
299
  struct io_fd *in;
Niels Möller's avatar
Niels Möller committed
300 301
  struct io_fd *out;
  struct io_fd *err;
302
};
303
#endif
304

Niels Möller's avatar
Niels Möller committed
305 306 307 308
/* Recieve channel data */
static int do_recieve(struct ssh_channel *c,
		      int type, struct lsh_string *data)
{
309
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
310 311 312 313
  
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
314
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
315 316 317 318 319 320 321 322 323 324 325 326
    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)
{
327
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
328

Niels Möller's avatar
Niels Möller committed
329 330 331 332 333 334 335
  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
336 337 338 339
  
  return LSH_OK | LSH_GOON;
}

340 341 342 343
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
344
  NEW(server_session, self);
345 346 347 348 349 350 351 352 353 354 355 356 357

  init_channel(&self->super);

  self->super.max_window = max_window;
  self->super.rec_window_size = max_window;

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

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

366 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)))
*/

#if 0
376 377 378 379 380 381 382
struct open_session
{
  struct channel_open super;

  struct unix_user *user;
  struct alist *session_requests;
};
383
#endif
384 385 386 387 388 389 390 391 392

#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)
{
393
  CAST(open_session, closure, c);
394 395 396 397 398 399
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
400 401
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
402 403 404 405 406
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
407
  NEW(open_session, closure);
408 409 410 411 412 413 414 415

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

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
/* 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.
       ))
*/

#if 0
432 433 434 435 436 437 438 439 440 441 442 443
struct server_connection_service
{
  struct unix_service super;

  struct alist *global_requests;

  /* Requests specific to session channels */
  struct alist *session_requests; 

  /* FIXME: Doesn't support any channel types but "session".
   * This must be fixed to support for "direct-tcpip" channels. */
};
444
#endif
445 446 447 448 449

/* Start an authenticated ssh-connection service */
static struct ssh_service *do_login(struct unix_service *c,
				    struct unix_user *user)
{
450
  CAST(server_connection_service, closure, c);
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465

  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)
{
466
  NEW(server_connection_service, closure);
467 468 469 470 471 472 473 474

  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
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
				      int core, int signal)
{
  struct lsh_string *msg = ssh_format("Process killed by %z.", strsignal(signal));
  
  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);
}
496 497 498 499 500 501 502 503 504 505

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

#if 0
Niels Möller's avatar
Niels Möller committed
506 507 508 509 510 511
struct exit_shell
{
  struct exit_callback super;

  struct server_session *session;
};
512
#endif
Niels Möller's avatar
Niels Möller committed
513 514 515 516

static void do_exit_shell(struct exit_callback *c, int signaled,
			  int core, int value)
{
517
  CAST(exit_shell, closure, c);
Niels Möller's avatar
Niels Möller committed
518 519 520
  struct server_session *session = closure->session;
  struct ssh_channel *channel = &session->super;
  
521
  CHECK_TYPE(server_session, session);
522 523 524

  /* FIXME: Should we explicitly mark these files for closing?
   * The io-backend should notice EOF anyway. */
525
  close_fd(&session->in->super, 0);
526
#if 0
Niels Möller's avatar
Niels Möller committed
527 528
  close_fd(session->out);
  close_fd(session->err);
529
#endif
Niels Möller's avatar
Niels Möller committed
530 531

#if 0
Niels Möller's avatar
Niels Möller committed
532 533 534 535 536 537 538 539 540 541
  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
542
#endif
Niels Möller's avatar
Niels Möller committed
543

544
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
545
  
Niels Möller's avatar
Niels Möller committed
546 547 548 549 550 551
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
			signaled
			? format_exit_signal(channel, core, value)
			: format_exit(channel, value));
Niels Möller's avatar
Niels Möller committed
552 553 554 555
#if 0
      if (!LSH_CLOSEDP(res))
	res |= channel_close(channel);
#endif
Niels Möller's avatar
Niels Möller committed
556 557
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
558 559

      (void) res;
Niels Möller's avatar
Niels Möller committed
560 561 562 563 564 565
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
566
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
567 568 569 570 571 572 573

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

  return &self->super;
}

574 575 576 577 578 579 580 581 582 583
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

#if 0
584 585 586 587 588
struct shell_request
{
  struct channel_request super;

  struct io_backend *backend;
Niels Möller's avatar
Niels Möller committed
589
  struct reap *reap;
590
};
591
#endif
592 593 594 595 596 597

/* 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
598 599 600 601 602 603 604
  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]);

605
  if(shutdown(fds[0], SHUT_WR) < 0)
Niels Möller's avatar
Niels Möller committed
606 607 608 609
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
610
  if (shutdown(fds[1], SHUT_RD) < 0)
Niels Möller's avatar
Niels Möller committed
611
    {
612
      werror("shutdown(%d, REC) failed: %s\n", fds[0], strerror(errno));
Niels Möller's avatar
Niels Möller committed
613 614
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
615
  
Niels Möller's avatar
Niels Möller committed
616
  return 1;
617 618 619 620
}

static char *make_env_pair(char *name, struct lsh_string *value)
{
621
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
622 623 624 625
}

static char *make_env_pair_c(char *name, char *value)
{
626
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
627 628 629 630 631 632 633
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
			  int want_reply,
			  struct simple_buffer *args)
{
634
  CAST(shell_request, closure, c);
635 636
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
637 638 639
  int in[2];
  int out[2];
  int err[2];
640

641
  CHECK_TYPE(server_session, session);
642 643 644 645 646 647 648 649

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

Niels Möller's avatar
Niels Möller committed
653
  if (make_pipe(in))
654
    {
Niels Möller's avatar
Niels Möller committed
655
      if (make_pipe(out))
656
	{
Niels Möller's avatar
Niels Möller committed
657
	  if (make_pipe(err))
658 659 660 661 662 663 664 665 666 667
	    {
	      pid_t child;
	      
	      switch(child = fork())
		{
		case -1:
		  werror("fork() failed: %s\n", strerror(errno));
		  /* Close and return channel_failure */
		  break; 
		case 0:
668
		  { /* Child */
669 670 671 672 673 674
		    char *shell = session->user->shell->data;
#define MAX_ENV 7
		    char *env[MAX_ENV];
		    char *tz = getenv("TZ");
		    int i = 0;

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
		    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");
		    
702 703 704 705 706 707 708 709 710 711 712 713 714
		    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);
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
#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]);

756
#if 1
Niels Möller's avatar
Niels Möller committed
757 758 759 760 761 762 763 764 765 766 767
		    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
768 769 770 771 772 773 774 775 776 777 778 779 780 781
		    /* 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));
782

Niels Möller's avatar
Niels Möller committed
783
		      _exit(EXIT_FAILURE);
784
		    }
785 786 787 788
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
789 790 791 792 793

		  debug("Parent process\n");

		  REAP(closure->reap, child, make_exit_shell(session));
		  
794
		  /* Close the child's fd:s */
Niels Möller's avatar
Niels Möller committed
795 796
		  close(in[0]);
		  close(out[1]);
Niels Möller's avatar
Niels Möller committed
797
		  close(err[1]);
798

Niels Möller's avatar
Niels Möller committed
799
		  session->in
Niels Möller's avatar
Niels Möller committed
800
		    = io_write(closure->backend, in[1],
Niels Möller's avatar
Niels Möller committed
801 802
				SSH_MAX_PACKET,
				/* FIXME: Use a proper close callback */
Niels Möller's avatar
Niels Möller committed
803
				make_channel_close(channel));
Niels Möller's avatar
Niels Möller committed
804 805 806 807 808 809 810 811 812 813 814 815
		  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;
		  
816 817 818 819 820 821 822
		  session->running = 1;
		  return want_reply
		    ? A_WRITE(channel->write,
			      format_channel_success(channel->channel_number))
		    : LSH_OK | LSH_GOON;
		  
		}
Niels Möller's avatar
Niels Möller committed
823 824
	      close(err[0]);
	      close(err[1]);
825
	    }
Niels Möller's avatar
Niels Möller committed
826 827
	  close(out[0]);
	  close(out[1]);
828
	}
Niels Möller's avatar
Niels Möller committed
829 830
      close(in[0]);
      close(in[1]);
831 832 833 834 835 836 837
    }
 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
838 839
struct channel_request *make_shell_handler(struct io_backend *backend,
					   struct reap *reap)
840
{
841
  NEW(shell_request, closure);
842 843

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
844
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
845
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
846
  
847 848 849
  return &closure->super;
}