server.c 21.4 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
/* server.c
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * $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
22 23 24 25 26
 */

#include "server.h"

#include "abstract_io.h"
27
#include "channel.h"
28
#include "connection.h"
Niels Möller's avatar
Niels Möller committed
29 30
#include "debug.h"
#include "format.h"
31 32 33
#include "keyexchange.h"
#include "read_line.h"
#include "read_packet.h"
Niels Möller's avatar
Niels Möller committed
34
#include "reaper.h"
35
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
36
#include "translate_signal.h"
Niels Möller's avatar
Niels Möller committed
37
#include "unpad.h"
38
#include "version.h"
Niels Möller's avatar
Niels Möller committed
39 40
#include "werror.h"
#include "xalloc.h"
41
#include "compress.h"
Niels Möller's avatar
Niels Möller committed
42

43 44 45 46 47 48 49
#include <assert.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>

50 51 52 53 54 55 56 57
/* Datafellows workaround.
 *
 * It seems that Datafellows' ssh2 client says it want to use protocol
 * version 1.99 in its greeting to the server. This behaviour is not
 * allowed by the specification. Define this to support it anyway. */

#define DATAFELLOWS_SSH2_GREETING_WORKAROUND

58 59 60 61 62
/* 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. */
63 64 65 66 67 68
#ifdef linux

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

69 70 71 72
#undef SHUT_RD
#undef SHUT_WR
#undef SHUT_RD_WR

73 74 75
#define SHUT_RD RCV_SHUTDOWN
#define SHUT_WR SEND_SHUTDOWN
#define SHUT_RD_WR (RCV_SHUTDOWN | SEND_SHUTDOWN)
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

#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
102

Niels Möller's avatar
Niels Möller committed
103 104 105 106
/* For debug */
#include <signal.h>
#include <unistd.h>

107 108 109 110 111 112 113 114 115
#include "server.c.x"

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

116 117
       (secret object signer) ; Secret key
       (host_key string)      ; Public key 
118

119 120
       (block_size simple UINT32)
       (id_comment simple "const char *")
121

122 123 124
       (random object randomness)
       (init object make_kexinit)
       (kexinit_handler object packet_handler)))
125
*/
126

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

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

  connection_init_io(connection,
		     io_read_write(closure->backend, fd,
				   make_server_read_line(connection),
				   closure->block_size,
143
				   make_server_close_handler(connection)),
Niels Möller's avatar
Niels Möller committed
144 145
		     closure->random);

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

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

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

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

Niels Möller's avatar
Niels Möller committed
171
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
172 173 174
				    UINT32 length,
				    UINT8 *line)
{
175
  CAST(server_line_handler, closure, *h);
Niels Möller's avatar
Niels Möller committed
176
  
Niels Möller's avatar
Niels Möller committed
177 178 179
  if ( (length >= 4) && !memcmp(line, "SSH-", 4))
    {
      /* Parse and remember format string */
180 181 182 183 184
      if ( ((length >= 8) && !memcmp(line + 4, "2.0-", 4))
#ifdef DATAFELLOWS_SSH2_GREETING_WORKAROUND
	   || ((length >= 9) && !memcmp(line + 4, "1.99-", 5))
#endif
	   )
Niels Möller's avatar
Niels Möller committed
185
	{
186 187 188 189 190 191 192 193 194 195
	  struct read_handler *new = 
	    make_read_packet(
	      make_packet_unpad(
	        make_packet_inflate(
	          make_packet_debug(&closure->connection->super, "received"),
	          closure->connection
	        )
	      ),
	      closure->connection
	    );
Niels Möller's avatar
Niels Möller committed
196 197
	  
	  closure->connection->client_version
Niels Möller's avatar
Niels Möller committed
198
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
199

Niels Möller's avatar
Niels Möller committed
200 201 202 203 204
	  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
205
	  /* FIXME: Cleanup properly. */
206
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
207 208 209 210 211

	  return new;
	}
      else
	{
212
	  wwrite("Unsupported protocol version: ");
Niels Möller's avatar
Niels Möller committed
213
	  werror_safe(length, line);
214
	  wwrite("\n");
Niels Möller's avatar
Niels Möller committed
215

Niels Möller's avatar
Niels Möller committed
216
	  /* FIXME: Clean up properly */
217
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
218 219
	  *h = 0;
		  
Niels Möller's avatar
Niels Möller committed
220 221 222 223 224 225 226 227 228 229 230 231
	  return 0;
	}
    }
  else
    {
      /* Display line */
      werror_safe(length, line);

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

Niels Möller's avatar
Niels Möller committed
233
struct read_handler *make_server_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
234
{
235
  NEW(server_line_handler, closure);
Niels Möller's avatar
Niels Möller committed
236

Niels Möller's avatar
Niels Möller committed
237
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
238
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
239 240 241 242
  
  return make_read_line(&closure->super);
}

243 244
struct fd_callback *
make_server_callback(struct io_backend *b,
245
		     const char *comment,
246
		     UINT32 block_size,
Niels Möller's avatar
Niels Möller committed
247 248
		     struct randomness *random,
		     struct make_kexinit *init,
249
		     struct packet_handler *kexinit_handler)
Niels Möller's avatar
Niels Möller committed
250
{
251
  NEW(server_callback, connected);
Niels Möller's avatar
Niels Möller committed
252 253 254 255 256

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

Niels Möller's avatar
Niels Möller committed
258 259
  connected->random = random;  
  connected->init = init;
260
  connected->kexinit_handler = kexinit_handler;
Niels Möller's avatar
Niels Möller committed
261 262 263 264
  
  return &connected->super;
}

265 266 267 268 269 270 271 272 273
/* 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
274
{
275 276
  CAST(server_cleanup, closure, c);
  
277 278
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
279
    wwrite("Connection died.\n");
280

281 282
  KILL_RESOURCE_LIST(closure->connection->resources);
  
Niels Möller's avatar
Niels Möller committed
283 284 285
  return 0;  /* Ignored */
}

286
struct close_callback *make_server_close_handler(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
287
{
288
  NEW(server_cleanup, closure);
Niels Möller's avatar
Niels Möller committed
289

290 291
  closure->connection = c;  
  closure->super.f = server_die;
Niels Möller's avatar
Niels Möller committed
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 325 326 327 328 329 330 331 332 333 334 335 336
  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
337 338
}

339
/* Session */
340 341 342 343 344 345 346 347 348
/* 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. 
349 350 351 352 353
       ;; (running simple int)

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

354 355 356 357 358 359
       ; Child process's stdio 
       (in object io_fd)
       (out object io_fd)
       (err object io_fd)))
*/

360 361
/* Receive channel data */
static int do_receive(struct ssh_channel *c,
Niels Möller's avatar
Niels Möller committed
362 363
		      int type, struct lsh_string *data)
{
364
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
365 366

  /* FIXME: Examine the size of the write buffer, to decide if the
367
   * receive window should be adjusted. */
Niels Möller's avatar
Niels Möller committed
368 369 370
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
371
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
372
    case CHANNEL_STDERR_DATA:
373
      wwrite("Ignoring unexpected stderr data.\n");
Niels Möller's avatar
Niels Möller committed
374 375 376 377 378 379 380 381 382 383
      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)
{
384
  CAST(server_session, session, c);
Niels Möller's avatar
Niels Möller committed
385

386 387 388 389
  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
390
  
391 392
  session->out->super.want_read = 1;
  session->err->super.want_read = 1;
Niels Möller's avatar
Niels Möller committed
393 394 395 396
  
  return LSH_OK | LSH_GOON;
}

397
static int do_eof(struct ssh_channel *channel)
Niels Möller's avatar
Niels Möller committed
398
{
399
  CAST(server_session, session, channel);
Niels Möller's avatar
Niels Möller committed
400

401
  write_buffer_close(session->in->buffer);
Niels Möller's avatar
Niels Möller committed
402

403 404 405 406 407
  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
408 409
}

410 411 412 413 414 415 416 417
static void do_close(struct ssh_channel *c)
{
  CAST(server_session, session, c);

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

418 419 420 421
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
422
  NEW(server_session, self);
423 424 425 426

  init_channel(&self->super);

  self->super.max_window = max_window;
427 428
  /* 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
429
  self->super.rec_window_size = 0;
430 431 432 433 434

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

  self->super.request_types = request_types;
435 436 437

  self->super.close = do_close;
  
438 439
  self->user = user;

440
#if 0
441
  self->running = 0;
442 443 444
#endif
  self->process = NULL;
  
Niels Möller's avatar
Niels Möller committed
445 446 447
  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
448 449 450 451
  
  return &self->super;
}

452 453 454 455 456 457 458 459 460
/* CLASS:
   (class
     (name open_session)
     (super channel_open)
     (vars
       (user object unix_user)
       (session_requests object alist)))
*/

461 462
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

463 464 465 466
static struct ssh_channel *
do_open_session(struct channel_open *c,
		struct ssh_connection *connection UNUSED,
		struct simple_buffer *args,
467 468 469
		UINT32 *error UNUSED,
		char **error_msg UNUSED,
		struct lsh_string **data UNUSED)
470
{
471
  CAST(open_session, closure, c);
472 473 474 475 476 477
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
478 479
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
480 481 482 483 484
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
485
  NEW(open_session, closure);
486 487 488 489 490 491 492 493

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

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
/* 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.
       ))
*/

509 510 511 512
/* Start an authenticated ssh-connection service */
static struct ssh_service *do_login(struct unix_service *c,
				    struct unix_user *user)
{
513
  CAST(server_connection_service, closure, c);
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528

  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)
{
529
  NEW(server_connection_service, closure);
530 531 532 533 534 535 536 537

  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
538 539 540
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
				      int core, int signal)
{
541 542
  struct lsh_string *msg = ssh_format("Process killed by %lz.\n",
				      strsignal(signal));
Niels Möller's avatar
Niels Möller committed
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
  
  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);
}
560 561 562 563 564 565 566 567 568

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

Niels Möller's avatar
Niels Möller committed
569 570 571
static void do_exit_shell(struct exit_callback *c, int signaled,
			  int core, int value)
{
572
  CAST(exit_shell, closure, c);
Niels Möller's avatar
Niels Möller committed
573 574 575
  struct server_session *session = closure->session;
  struct ssh_channel *channel = &session->super;
  
576
  CHECK_TYPE(server_session, session);
577

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
  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
594
  close_fd(&session->in->super, 0);
595
#endif
596
#if 0
Niels Möller's avatar
Niels Möller committed
597 598
  close_fd(session->out);
  close_fd(session->err);
599
#endif
Niels Möller's avatar
Niels Möller committed
600 601

#if 0
Niels Möller's avatar
Niels Möller committed
602 603 604 605 606 607 608 609 610 611
  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
612
#endif
Niels Möller's avatar
Niels Möller committed
613

614
  /* We close when we have both sent and received eof. */
615
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
616
  
Niels Möller's avatar
Niels Möller committed
617 618 619
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
620 621 622 623 624
		    signaled
		    ? format_exit_signal(channel, core, value)
		    : format_exit(channel, value));

      if (!LSH_CLOSEDP(res)
625 626
	  && (channel->flags & CHANNEL_SENT_EOF)
	  && (channel->flags & CHANNEL_RECEIVED_EOF))
627 628 629 630 631
	{
	  /* We have sent EOF already, so initiate close */
	  res |= channel_close(channel);
	}

Niels Möller's avatar
Niels Möller committed
632 633
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
634 635

      (void) res;
Niels Möller's avatar
Niels Möller committed
636 637 638 639 640 641
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
642
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
643 644 645 646 647 648 649

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

  return &self->super;
}

650 651 652 653 654 655 656 657 658
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

659 660 661 662 663
/* 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
664 665 666 667 668 669 670
  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]);

671
  if (SHUTDOWN(fds[0], SHUT_WR) < 0)
Niels Möller's avatar
Niels Möller committed
672 673 674 675
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
676
  if (SHUTDOWN(fds[1], SHUT_RD) < 0)
Niels Möller's avatar
Niels Möller committed
677
    {
678
      werror("shutdown(%d, REC) failed: %s\n", fds[0], strerror(errno));
Niels Möller's avatar
Niels Möller committed
679 680
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
681
  
Niels Möller's avatar
Niels Möller committed
682
  return 1;
683 684
}

685
static char *make_env_pair(const char *name, struct lsh_string *value)
686
{
687
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
688 689
}

690
static char *make_env_pair_c(const char *name, char *value)
691
{
692
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
693 694 695 696
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
697
			  struct ssh_connection *connection,
698 699 700
			  int want_reply,
			  struct simple_buffer *args)
{
701
  CAST(shell_request, closure, c);
702 703
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
704 705 706
  int in[2];
  int out[2];
  int err[2];
707

708
  CHECK_TYPE(server_session, session);
709 710 711 712

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

713
  if (session->process)
714 715 716
    /* Already spawned a shell or command */
    goto fail;
  
Niels Möller's avatar
Niels Möller committed
717 718
  /* {in|out|err}[0] is for reading,
   * {in|out|err}[1] for writing. */
719

Niels Möller's avatar
Niels Möller committed
720
  if (make_pipe(in))
721
    {
Niels Möller's avatar
Niels Möller committed
722
      if (make_pipe(out))
723
	{
Niels Möller's avatar
Niels Möller committed
724
	  if (make_pipe(err))
725 726 727 728 729 730 731 732 733 734
	    {
	      pid_t child;
	      
	      switch(child = fork())
		{
		case -1:
		  werror("fork() failed: %s\n", strerror(errno));
		  /* Close and return channel_failure */
		  break; 
		case 0:
735
		  { /* Child */
736 737 738 739 740 741
		    char *shell = session->user->shell->data;
#define MAX_ENV 7
		    char *env[MAX_ENV];
		    char *tz = getenv("TZ");
		    int i = 0;

742 743 744 745 746 747
		    int old_stderr;
		    
		    debug("do_spawn_shell: Child process\n");

		    if (!session->user->shell)
		      {
748
			wwrite("No login shell!\n");
749 750 751 752 753 754
			exit(EXIT_FAILURE);
		      }

		    if (getuid() != session->user->uid)
		      if (!change_uid(session->user))
			{
755
			  wwrite("Changing uid failed!\n");
756 757 758 759 760 761 762
			  exit(EXIT_FAILURE);
			}
		    
		    assert(getuid() == session->user->uid);
		    
		    if (!change_dir(session->user))
		      {
763
			wwrite("Could not change to home (or root) directory!\n");
764 765 766 767 768
			exit(EXIT_FAILURE);
		      }

		    debug("Child: Setting up environment.\n");
		    
769 770 771 772 773 774 775 776 777 778 779 780 781
		    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);
782 783 784 785 786 787 788 789 790 791 792 793 794
#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)
		      {
795
			wwrite("Can't dup stdin!\n");
796 797 798 799 800 801 802
			exit(EXIT_FAILURE);
		      }
		    close(in[0]);
		    close(in[1]);
		    
		    if (dup2(out[1], STDOUT_FILENO) < 0)
		      {
803
			wwrite("Can't dup stdout!\n");
804 805 806 807 808 809
			exit(EXIT_FAILURE);
		      }
		    close(out[0]);
		    close(out[1]);

		    if ((old_stderr = dup(STDERR_FILENO)) < 0)
810
		      wwrite("Couldn't save old file_no.\n");
811 812 813 814 815 816 817 818 819 820 821 822
		    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]);

823
#if 1
Niels Möller's avatar
Niels Möller committed
824 825 826 827 828 829 830 831 832 833 834
		    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
835 836 837 838 839 840 841 842 843 844 845 846 847 848
		    /* 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));
849

Niels Möller's avatar
Niels Möller committed
850
		      _exit(EXIT_FAILURE);
851
		    }
852 853 854 855
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
856 857 858 859

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

Niels Möller's avatar
Niels Möller committed
865
		  session->in
Niels Möller's avatar
Niels Möller committed
866
		    = io_write(closure->backend, in[1],
867 868 869
			       SSH_MAX_PACKET,
			       /* FIXME: Use a proper close callback */
			       make_channel_close(channel));
Niels Möller's avatar
Niels Möller committed
870 871 872 873 874 875 876 877 878
		  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);

879
		  channel->receive = do_receive;
Niels Möller's avatar
Niels Möller committed
880
		  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
881
		  channel->eof = do_eof;
Niels Möller's avatar
Niels Möller committed
882
		  
883
#if 0
884
		  session->running = 1;
885 886 887 888 889 890 891 892 893 894 895 896 897 898
#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
899 900 901 902 903 904

		  return (want_reply
			  ? A_WRITE(channel->write,
				    format_channel_success(channel
							   ->channel_number))
			  : 0) | LSH_CHANNEL_READY_REC;
905
		}
Niels Möller's avatar
Niels Möller committed
906 907
	      close(err[0]);
	      close(err[1]);
908
	    }
Niels Möller's avatar
Niels Möller committed
909 910
	  close(out[0]);
	  close(out[1]);
911
	}
Niels Möller's avatar
Niels Möller committed
912 913
      close(in[0]);
      close(in[1]);
914 915 916 917 918 919 920
    }
 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
921 922
struct channel_request *make_shell_handler(struct io_backend *backend,
					   struct reap *reap)
923
{
924
  NEW(shell_request, closure);
925 926

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
927
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
928
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
929
  
930 931 932
  return &closure->super;
}