server.c 20.9 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;
}

385
static int do_eof(struct ssh_channel *channel)
Niels Möller's avatar
Niels Möller committed
386
{
387
  CAST(server_session, session, channel);
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 394 395
  if ( (channel->flags & CHANNEL_SENT_EOF)
       && (channel->flags & CHANNEL_CLOSE_AT_EOF))
    return channel_close(channel);
  else
    return LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
396 397
}

398 399 400 401 402 403 404 405
static void do_close(struct ssh_channel *c)
{
  CAST(server_session, session, c);

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

406 407 408 409
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
410
  NEW(server_session, self);
411 412 413 414

  init_channel(&self->super);

  self->super.max_window = max_window;
415 416
  /* 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
417
  self->super.rec_window_size = 0;
418 419 420 421 422

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

  self->super.request_types = request_types;
423 424 425

  self->super.close = do_close;
  
426 427
  self->user = user;

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

440 441 442 443 444 445 446 447 448
/* CLASS:
   (class
     (name open_session)
     (super channel_open)
     (vars
       (user object unix_user)
       (session_requests object alist)))
*/

449 450
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

451 452 453 454 455 456 457
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)
458
{
459
  CAST(open_session, closure, c);
460 461 462 463 464 465
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
466 467
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
468 469 470 471 472
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
473
  NEW(open_session, closure);
474 475 476 477 478 479 480 481

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

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
/* 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.
       ))
*/

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

  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)
{
517
  NEW(server_connection_service, closure);
518 519 520 521 522 523 524 525

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

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

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

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
  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
582
  close_fd(&session->in->super, 0);
583
#endif
584
#if 0
Niels Möller's avatar
Niels Möller committed
585 586
  close_fd(session->out);
  close_fd(session->err);
587
#endif
Niels Möller's avatar
Niels Möller committed
588 589

#if 0
Niels Möller's avatar
Niels Möller committed
590 591 592 593 594 595 596 597 598 599
  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
600
#endif
Niels Möller's avatar
Niels Möller committed
601

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

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

Niels Möller's avatar
Niels Möller committed
620 621
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
622 623

      (void) res;
Niels Möller's avatar
Niels Möller committed
624 625 626 627 628 629
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
630
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
631 632 633 634 635 636 637

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

  return &self->super;
}

638 639 640 641 642 643 644 645 646
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

647 648 649 650 651
/* 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
652 653 654 655 656 657 658
  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]);

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

673
static char *make_env_pair(const char *name, struct lsh_string *value)
674
{
675
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
676 677
}

678
static char *make_env_pair_c(const char *name, char *value)
679
{
680
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
681 682 683 684
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
685
			  struct ssh_connection *connection,
686 687 688
			  int want_reply,
			  struct simple_buffer *args)
{
689
  CAST(shell_request, closure, c);
690 691
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
692 693 694
  int in[2];
  int out[2];
  int err[2];
695

696
  CHECK_TYPE(server_session, session);
697 698 699 700

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

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

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

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 756
		    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");
		    
757 758 759 760 761 762 763 764 765 766 767 768 769
		    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);
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 807 808 809 810
#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]);

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

Niels Möller's avatar
Niels Möller committed
838
		      _exit(EXIT_FAILURE);
839
		    }
840 841 842 843
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
844 845 846 847

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

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

867
		  channel->receive = do_receive;
Niels Möller's avatar
Niels Möller committed
868
		  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
869
		  channel->eof = do_eof;
Niels Möller's avatar
Niels Möller committed
870
		  
871
#if 0
872
		  session->running = 1;
873 874 875 876 877 878 879 880 881 882 883 884 885 886
#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
887 888 889 890 891 892

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

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
915
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
916
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
917
  
918 919 920
  return &closure->super;
}