server.c 20.8 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
#include "server.c.x"

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

113 114
       (secret object signer) ; Secret key
       (host_key string)      ; Public key 
115

116 117
       (block_size simple UINT32)
       (id_comment simple "const char *")
118

119 120 121
       (random object randomness)
       (init object make_kexinit)
       (kexinit_handler object packet_handler)))
122
*/
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)
{
127
  CAST(server_callback, closure, *c);
Niels Möller's avatar
Niels Möller committed
128
  
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

  connection_init_io(connection,
		     io_read_write(closure->backend, fd,
				   make_server_read_line(connection),
				   closure->block_size,
140
				   make_server_close_handler(connection)),
Niels Möller's avatar
Niels Möller committed
141 142
		     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
				"received")),
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 254 255 256 257 258 259 260 261
/* CLASS:
   (class
     (name server_cleanup)
     (super close_callback)
     (vars
       (connection object ssh_connection)))
*/

static int server_die(struct close_callback *c, int reason)
Niels Möller's avatar
Niels Möller committed
262
{
263 264
  CAST(server_cleanup, closure, c);
  
265 266 267 268
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
    werror("Connection died.\n");

269 270
  KILL_RESOURCE_LIST(closure->connection->resources);
  
Niels Möller's avatar
Niels Möller committed
271 272 273
  return 0;  /* Ignored */
}

274
struct close_callback *make_server_close_handler(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
275
{
276
  NEW(server_cleanup, closure);
Niels Möller's avatar
Niels Möller committed
277

278 279
  closure->connection = c;  
  closure->super.f = server_die;
Niels Möller's avatar
Niels Möller committed
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
  return &closure->super;
}


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

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

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

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

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

  self->super.kill = do_kill_process;

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

327
/* Session */
328 329 330 331 332 333 334 335 336
/* 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. 
337 338 339 340 341
       ;; (running simple int)

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

342 343 344 345 346 347
       ; Child process's stdio 
       (in object io_fd)
       (out object io_fd)
       (err object io_fd)))
*/

348 349
/* Receive channel data */
static int do_receive(struct ssh_channel *c,
Niels Möller's avatar
Niels Möller committed
350 351
		      int type, struct lsh_string *data)
{
352
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
353 354

  /* FIXME: Examine the size of the write buffer, to decide if the
355
   * receive window should be adjusted. */
Niels Möller's avatar
Niels Möller committed
356 357 358
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
359
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
360 361 362 363 364 365 366 367 368 369 370 371
    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)
{
372
  CAST(server_session, session, c);
Niels Möller's avatar
Niels Möller committed
373

374 375 376 377
  assert(session->out->super.read);
  assert(session->out->handler);
  assert(session->err->super.read);
  assert(session->err->handler);
Niels Möller's avatar
Niels Möller committed
378
  
379 380
  session->out->super.want_read = 1;
  session->err->super.want_read = 1;
Niels Möller's avatar
Niels Möller committed
381 382 383 384
  
  return LSH_OK | LSH_GOON;
}

Niels Möller's avatar
Niels Möller committed
385 386
static int do_eof(struct ssh_channel *c)
{
387
  CAST(server_session, session, c);
Niels Möller's avatar
Niels Möller committed
388

389
  write_buffer_close(session->in->buffer);
Niels Möller's avatar
Niels Möller committed
390 391 392 393

  return LSH_OK | LSH_GOON;
}

394 395 396 397 398 399 400 401
static void do_close(struct ssh_channel *c)
{
  CAST(server_session, session, c);

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

402 403 404 405
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
406
  NEW(server_session, self);
407 408 409 410

  init_channel(&self->super);

  self->super.max_window = max_window;
411 412
  /* We don't want to receive any data before we have forked some
   * process to receive it. */
Niels Möller's avatar
Niels Möller committed
413
  self->super.rec_window_size = 0;
414 415 416 417 418

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

  self->super.request_types = request_types;
419 420 421

  self->super.close = do_close;
  
422 423
  self->user = user;

424
#if 0
425
  self->running = 0;
426 427 428
#endif
  self->process = NULL;
  
Niels Möller's avatar
Niels Möller committed
429 430 431
  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
432 433 434 435
  
  return &self->super;
}

436 437 438 439 440 441 442 443 444
/* CLASS:
   (class
     (name open_session)
     (super channel_open)
     (vars
       (user object unix_user)
       (session_requests object alist)))
*/

445 446
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

447 448 449 450 451 452 453
static struct ssh_channel *
do_open_session(struct channel_open *c,
		struct ssh_connection *connection UNUSED,
		struct simple_buffer *args,
		UINT32 *error,
		char **error_msg,
		struct lsh_string **data)
454
{
455
  CAST(open_session, closure, c);
456 457 458 459 460 461
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
462 463
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
464 465 466 467 468
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
469
  NEW(open_session, closure);
470 471 472 473 474 475 476 477

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

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
/* 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.
       ))
*/

493 494 495 496
/* Start an authenticated ssh-connection service */
static struct ssh_service *do_login(struct unix_service *c,
				    struct unix_user *user)
{
497
  CAST(server_connection_service, closure, c);
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512

  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)
{
513
  NEW(server_connection_service, closure);
514 515 516 517 518 519 520 521

  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
522 523 524
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
				      int core, int signal)
{
525 526
  struct lsh_string *msg = ssh_format("Process killed by %lz.\n",
				      strsignal(signal));
Niels Möller's avatar
Niels Möller committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
  
  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);
}
544 545 546 547 548 549 550 551 552

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

Niels Möller's avatar
Niels Möller committed
553 554 555
static void do_exit_shell(struct exit_callback *c, int signaled,
			  int core, int value)
{
556
  CAST(exit_shell, closure, c);
Niels Möller's avatar
Niels Möller committed
557 558 559
  struct server_session *session = closure->session;
  struct ssh_channel *channel = &session->super;
  
560
  CHECK_TYPE(server_session, session);
561

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
  if (! session->process->alive)
    {
      /* The process was killed by a the resource callback (most
       * likely because the connection died. Keep silent. */
      debug("do_exit_shell: Process already flagged as dead.\n");
      return;
    }
  
  /* No need to kill the process. */
  session->process->alive = 0;
  
  /* FIXME: Should we explicitly mark these files for closing? The
   * io-backend should notice EOF anyway. And the client should send
   * EOF when it receives news of the process's death, unless it
   * really wants to talk to any live children processes. */
#if 0
578
  close_fd(&session->in->super, 0);
579
#endif
580
#if 0
Niels Möller's avatar
Niels Möller committed
581 582
  close_fd(session->out);
  close_fd(session->err);
583
#endif
Niels Möller's avatar
Niels Möller committed
584 585

#if 0
Niels Möller's avatar
Niels Möller committed
586 587 588 589 590 591 592 593 594 595
  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
596
#endif
Niels Möller's avatar
Niels Möller committed
597

598
  /* We close when we have both sent and received eof. */
599
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
600
  
Niels Möller's avatar
Niels Möller committed
601 602 603
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
604 605 606 607 608
		    signaled
		    ? format_exit_signal(channel, core, value)
		    : format_exit(channel, value));

      if (!LSH_CLOSEDP(res)
609 610
	  && (channel->flags & CHANNEL_SENT_EOF)
	  && (channel->flags & CHANNEL_RECEIVED_EOF))
611 612 613 614 615
	{
	  /* We have sent EOF already, so initiate close */
	  res |= channel_close(channel);
	}

Niels Möller's avatar
Niels Möller committed
616 617
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
618 619

      (void) res;
Niels Möller's avatar
Niels Möller committed
620 621 622 623 624 625
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
626
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
627 628 629 630 631 632 633

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

  return &self->super;
}

634 635 636 637 638 639 640 641 642
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

643 644 645 646 647
/* 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
648 649 650 651 652 653 654
  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]);

655
  if (SHUTDOWN(fds[0], SHUT_WR) < 0)
Niels Möller's avatar
Niels Möller committed
656 657 658 659
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
660
  if (SHUTDOWN(fds[1], SHUT_RD) < 0)
Niels Möller's avatar
Niels Möller committed
661
    {
662
      werror("shutdown(%d, REC) failed: %s\n", fds[0], strerror(errno));
Niels Möller's avatar
Niels Möller committed
663 664
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
665
  
Niels Möller's avatar
Niels Möller committed
666
  return 1;
667 668
}

669
static char *make_env_pair(const char *name, struct lsh_string *value)
670
{
671
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
672 673
}

674
static char *make_env_pair_c(const char *name, char *value)
675
{
676
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
677 678 679 680
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
681
			  struct ssh_connection *connection,
682 683 684
			  int want_reply,
			  struct simple_buffer *args)
{
685
  CAST(shell_request, closure, c);
686 687
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
688 689 690
  int in[2];
  int out[2];
  int err[2];
691

692
  CHECK_TYPE(server_session, session);
693 694 695 696

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

697
  if (session->process)
698 699 700
    /* Already spawned a shell or command */
    goto fail;
  
Niels Möller's avatar
Niels Möller committed
701 702
  /* {in|out|err}[0] is for reading,
   * {in|out|err}[1] for writing. */
703

Niels Möller's avatar
Niels Möller committed
704
  if (make_pipe(in))
705
    {
Niels Möller's avatar
Niels Möller committed
706
      if (make_pipe(out))
707
	{
Niels Möller's avatar
Niels Möller committed
708
	  if (make_pipe(err))
709 710 711 712 713 714 715 716 717 718
	    {
	      pid_t child;
	      
	      switch(child = fork())
		{
		case -1:
		  werror("fork() failed: %s\n", strerror(errno));
		  /* Close and return channel_failure */
		  break; 
		case 0:
719
		  { /* Child */
720 721 722 723 724 725
		    char *shell = session->user->shell->data;
#define MAX_ENV 7
		    char *env[MAX_ENV];
		    char *tz = getenv("TZ");
		    int i = 0;

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
		    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");
		    
753 754 755 756 757 758 759 760 761 762 763 764 765
		    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);
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
#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]);

807
#if 1
Niels Möller's avatar
Niels Möller committed
808 809 810 811 812 813 814 815 816 817 818
		    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
819 820 821 822 823 824 825 826 827 828 829 830 831 832
		    /* 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));
833

Niels Möller's avatar
Niels Möller committed
834
		      _exit(EXIT_FAILURE);
835
		    }
836 837 838 839
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
840 841 842 843

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

Niels Möller's avatar
Niels Möller committed
849
		  session->in
Niels Möller's avatar
Niels Möller committed
850
		    = io_write(closure->backend, in[1],
851 852 853
			       SSH_MAX_PACKET,
			       /* FIXME: Use a proper close callback */
			       make_channel_close(channel));
Niels Möller's avatar
Niels Möller committed
854 855 856 857 858 859 860 861 862
		  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);

863
		  channel->receive = do_receive;
Niels Möller's avatar
Niels Möller committed
864
		  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
865
		  channel->eof = do_eof;
Niels Möller's avatar
Niels Möller committed
866
		  
867
#if 0
868
		  session->running = 1;
869 870 871 872 873 874 875 876 877 878 879 880 881 882
#endif
		  session->process
		    = make_process_resource(child, SIGHUP);

		  /* Make sure that the process and it's stdio is
		   * cleaned up if the connection dies. */
		  REMEMBER_RESOURCE
		    (connection->resources, session->process);
		  REMEMBER_RESOURCE
		    (connection->resources, &session->in->super.super);
		  REMEMBER_RESOURCE
		    (connection->resources, &session->out->super.super);
		  REMEMBER_RESOURCE
		    (connection->resources, &session->err->super.super);
Niels Möller's avatar
Niels Möller committed
883 884 885 886 887 888

		  return (want_reply
			  ? A_WRITE(channel->write,
				    format_channel_success(channel
							   ->channel_number))
			  : 0) | LSH_CHANNEL_READY_REC;
889
		}
Niels Möller's avatar
Niels Möller committed
890 891
	      close(err[0]);
	      close(err[1]);
892
	    }
Niels Möller's avatar
Niels Möller committed
893 894
	  close(out[0]);
	  close(out[1]);
895
	}
Niels Möller's avatar
Niels Möller committed
896 897
      close(in[0]);
      close(in[1]);
898 899 900 901 902 903 904
    }
 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
905 906
struct channel_request *make_shell_handler(struct io_backend *backend,
					   struct reap *reap)
907
{
908
  NEW(shell_request, closure);
909 910

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
911
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
912
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
913
  
914 915 916
  return &closure->super;
}