server.c 21.9 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",
149
		 SERVER_PROTOCOL_VERSION,
Niels Möller's avatar
Niels Möller committed
150 151
		 SOFTWARE_SERVER_VERSION,
		 closure->id_comment);
152 153 154 155 156 157
#ifdef SSH1_FALLBACK
  /* "In this mode the server SHOULD NOT send carriage return character (ascii
   * 13) after the version identification string." */
  res = A_WRITE(connection->raw,
		 ssh_format("%lS\n", connection->server_version));
#else
Niels Möller's avatar
Niels Möller committed
158 159
  res = A_WRITE(connection->raw,
		 ssh_format("%lS\r\n", connection->server_version));
160
#endif
161
  if (LSH_CLOSEDP(res))
Niels Möller's avatar
Niels Möller committed
162 163
    return res;

164 165 166
  return res | initiate_keyexchange(connection, CONNECTION_SERVER,
				    MAKE_KEXINIT(closure->init),
				    NULL);
Niels Möller's avatar
Niels Möller committed
167 168
}

169 170 171 172 173 174 175 176
/* CLASS:
   (class
     (name server_line_handler)
     (super line_handler)
     (vars
       (connection object ssh_connection)))
*/

Niels Möller's avatar
Niels Möller committed
177
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
178 179 180
				    UINT32 length,
				    UINT8 *line)
{
181
  CAST(server_line_handler, closure, *h);
Niels Möller's avatar
Niels Möller committed
182
  
Niels Möller's avatar
Niels Möller committed
183 184 185
  if ( (length >= 4) && !memcmp(line, "SSH-", 4))
    {
      /* Parse and remember format string */
186 187 188 189 190
      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
191
	{
192 193 194 195 196 197 198 199 200 201
	  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
202 203
	  
	  closure->connection->client_version
Niels Möller's avatar
Niels Möller committed
204
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
205

Niels Möller's avatar
Niels Möller committed
206 207 208 209 210
	  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
211
	  /* FIXME: Cleanup properly. */
212
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
213 214 215

	  return new;
	}
216 217 218 219 220 221 222
      else 
#ifdef SSH1_FALLBACK      
        if ( ((length >= 6) && !memcmp(line + 4, "1.", 2)) )
	{
	  /* TODO: fork a SSH1 server to handle this connection */
	  werror("Falling back to ssh1 not implemented.\n");
        }
Niels Möller's avatar
Niels Möller committed
223
      else
224
#endif /* SSH1_FALLBACK */
Niels Möller's avatar
Niels Möller committed
225
	{
226
	  wwrite("Unsupported protocol version: ");
Niels Möller's avatar
Niels Möller committed
227
	  werror_safe(length, line);
228
	  wwrite("\n");
Niels Möller's avatar
Niels Möller committed
229

Niels Möller's avatar
Niels Möller committed
230
	  /* FIXME: Clean up properly */
231
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
232 233
	  *h = 0;
		  
Niels Möller's avatar
Niels Möller committed
234 235 236 237 238 239 240 241 242 243 244 245
	  return 0;
	}
    }
  else
    {
      /* Display line */
      werror_safe(length, line);

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

Niels Möller's avatar
Niels Möller committed
247
struct read_handler *make_server_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
248
{
249
  NEW(server_line_handler, closure);
Niels Möller's avatar
Niels Möller committed
250

Niels Möller's avatar
Niels Möller committed
251
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
252
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
253 254 255 256
  
  return make_read_line(&closure->super);
}

257 258
struct fd_callback *
make_server_callback(struct io_backend *b,
259
		     const char *comment,
260
		     UINT32 block_size,
Niels Möller's avatar
Niels Möller committed
261 262
		     struct randomness *random,
		     struct make_kexinit *init,
263
		     struct packet_handler *kexinit_handler)
Niels Möller's avatar
Niels Möller committed
264
{
265
  NEW(server_callback, connected);
Niels Möller's avatar
Niels Möller committed
266 267 268 269 270

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

Niels Möller's avatar
Niels Möller committed
272 273
  connected->random = random;  
  connected->init = init;
274
  connected->kexinit_handler = kexinit_handler;
Niels Möller's avatar
Niels Möller committed
275 276 277 278
  
  return &connected->super;
}

279 280 281 282 283 284 285 286 287
/* 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
288
{
289 290
  CAST(server_cleanup, closure, c);
  
291 292
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
293
    wwrite("Connection died.\n");
294

295 296
  KILL_RESOURCE_LIST(closure->connection->resources);
  
Niels Möller's avatar
Niels Möller committed
297 298 299
  return 0;  /* Ignored */
}

300
struct close_callback *make_server_close_handler(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
301
{
302
  NEW(server_cleanup, closure);
Niels Möller's avatar
Niels Möller committed
303

304 305
  closure->connection = c;  
  closure->super.f = server_die;
Niels Möller's avatar
Niels Möller committed
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 337 338 339 340 341 342 343 344 345 346 347 348 349 350
  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
351 352
}

353
/* Session */
354 355 356 357 358 359 360 361 362
/* 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. 
363 364 365 366 367
       ;; (running simple int)

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

368 369 370 371 372 373
       ; Child process's stdio 
       (in object io_fd)
       (out object io_fd)
       (err object io_fd)))
*/

374 375
/* Receive channel data */
static int do_receive(struct ssh_channel *c,
Niels Möller's avatar
Niels Möller committed
376 377
		      int type, struct lsh_string *data)
{
378
  CAST(server_session, closure, c);
Niels Möller's avatar
Niels Möller committed
379 380

  /* FIXME: Examine the size of the write buffer, to decide if the
381
   * receive window should be adjusted. */
Niels Möller's avatar
Niels Möller committed
382 383 384
  switch(type)
    {
    case CHANNEL_DATA:
Niels Möller's avatar
Niels Möller committed
385
      return A_WRITE(&closure->in->buffer->super, data);
Niels Möller's avatar
Niels Möller committed
386
    case CHANNEL_STDERR_DATA:
387
      wwrite("Ignoring unexpected stderr data.\n");
Niels Möller's avatar
Niels Möller committed
388 389 390 391 392 393 394 395 396 397
      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)
{
398
  CAST(server_session, session, c);
Niels Möller's avatar
Niels Möller committed
399

400 401 402 403
  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
404
  
405 406
  session->out->super.want_read = 1;
  session->err->super.want_read = 1;
Niels Möller's avatar
Niels Möller committed
407 408 409 410
  
  return LSH_OK | LSH_GOON;
}

411
static int do_eof(struct ssh_channel *channel)
Niels Möller's avatar
Niels Möller committed
412
{
413
  CAST(server_session, session, channel);
Niels Möller's avatar
Niels Möller committed
414

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

417 418 419 420 421
  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
422 423
}

424 425 426 427 428 429 430 431
static void do_close(struct ssh_channel *c)
{
  CAST(server_session, session, c);

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

432 433 434 435
struct ssh_channel *make_server_session(struct unix_user *user,
					UINT32 max_window,
					struct alist *request_types)
{
436
  NEW(server_session, self);
437 438 439 440

  init_channel(&self->super);

  self->super.max_window = max_window;
441 442
  /* 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
443
  self->super.rec_window_size = 0;
444 445 446 447 448

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

  self->super.request_types = request_types;
449 450 451

  self->super.close = do_close;
  
452 453
  self->user = user;

454
#if 0
455
  self->running = 0;
456 457 458
#endif
  self->process = NULL;
  
Niels Möller's avatar
Niels Möller committed
459 460 461
  self->in = NULL;
  self->out = NULL;
  self->err = NULL;
462 463 464 465
  
  return &self->super;
}

466 467 468 469 470 471 472 473 474
/* CLASS:
   (class
     (name open_session)
     (super channel_open)
     (vars
       (user object unix_user)
       (session_requests object alist)))
*/

475 476
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

477 478 479 480
static struct ssh_channel *
do_open_session(struct channel_open *c,
		struct ssh_connection *connection UNUSED,
		struct simple_buffer *args,
481 482 483
		UINT32 *error UNUSED,
		char **error_msg UNUSED,
		struct lsh_string **data UNUSED)
484
{
485
  CAST(open_session, closure, c);
486 487 488 489 490 491
  
  debug("server.c: do_open_session()\n");
  
  if (!parse_eod(args))
    return 0;
  
Niels Möller's avatar
Niels Möller committed
492 493
  return make_server_session(closure->user, WINDOW_SIZE,
			     closure->session_requests);
494 495 496 497 498
}

struct channel_open *make_open_session(struct unix_user *user,
				       struct alist *session_requests)
{
499
  NEW(open_session, closure);
500 501 502 503 504 505 506 507

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

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
/* 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.
       ))
*/

523 524 525 526
/* Start an authenticated ssh-connection service */
static struct ssh_service *do_login(struct unix_service *c,
				    struct unix_user *user)
{
527
  CAST(server_connection_service, closure, c);
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542

  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)
{
543
  NEW(server_connection_service, closure);
544 545 546 547 548 549 550 551

  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
552 553 554
struct lsh_string *format_exit_signal(struct ssh_channel *channel,
				      int core, int signal)
{
555 556
  struct lsh_string *msg = ssh_format("Process killed by %lz.\n",
				      strsignal(signal));
Niels Möller's avatar
Niels Möller committed
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
  
  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);
}
574 575 576 577 578 579 580 581 582

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

Niels Möller's avatar
Niels Möller committed
583 584 585
static void do_exit_shell(struct exit_callback *c, int signaled,
			  int core, int value)
{
586
  CAST(exit_shell, closure, c);
Niels Möller's avatar
Niels Möller committed
587 588 589
  struct server_session *session = closure->session;
  struct ssh_channel *channel = &session->super;
  
590
  CHECK_TYPE(server_session, session);
591

592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
  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
608
  close_fd(&session->in->super, 0);
609
#endif
610
#if 0
Niels Möller's avatar
Niels Möller committed
611 612
  close_fd(session->out);
  close_fd(session->err);
613
#endif
Niels Möller's avatar
Niels Möller committed
614 615

#if 0
Niels Möller's avatar
Niels Möller committed
616 617 618 619 620 621 622 623 624 625
  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
626
#endif
Niels Möller's avatar
Niels Möller committed
627

628
  /* We close when we have both sent and received eof. */
629
  channel->flags |= CHANNEL_CLOSE_AT_EOF;
Niels Möller's avatar
Niels Möller committed
630
  
Niels Möller's avatar
Niels Möller committed
631 632 633
  if (!(channel->flags & CHANNEL_SENT_CLOSE))
    {
      int res = A_WRITE(channel->write,
634 635 636 637 638
		    signaled
		    ? format_exit_signal(channel, core, value)
		    : format_exit(channel, value));

      if (!LSH_CLOSEDP(res)
639 640
	  && (channel->flags & CHANNEL_SENT_EOF)
	  && (channel->flags & CHANNEL_RECEIVED_EOF))
641 642 643 644 645
	{
	  /* We have sent EOF already, so initiate close */
	  res |= channel_close(channel);
	}

Niels Möller's avatar
Niels Möller committed
646 647
      /* FIXME: Can we do anything better with the return code than
       * ignore it? */
Niels Möller's avatar
Niels Möller committed
648 649

      (void) res;
Niels Möller's avatar
Niels Möller committed
650 651 652 653 654 655
      return;
    }
}

static struct exit_callback *make_exit_shell(struct server_session *session)
{
656
  NEW(exit_shell, self);
Niels Möller's avatar
Niels Möller committed
657 658 659 660 661 662 663

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

  return &self->super;
}

664 665 666 667 668 669 670 671 672
/* CLASS:
   (class
     (name shell_request)
     (super channel_request)
     (vars
       (backend object io_backend)
       (reap object reap)))
*/

673 674 675 676 677
/* 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
678 679 680 681 682 683 684
  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]);

685
  if (SHUTDOWN(fds[0], SHUT_WR) < 0)
Niels Möller's avatar
Niels Möller committed
686 687 688 689
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
690
  if (SHUTDOWN(fds[1], SHUT_RD) < 0)
Niels Möller's avatar
Niels Möller committed
691
    {
692
      werror("shutdown(%d, REC) failed: %s\n", fds[0], strerror(errno));
Niels Möller's avatar
Niels Möller committed
693 694
      return 0;
    }
Niels Möller's avatar
Niels Möller committed
695
  
Niels Möller's avatar
Niels Möller committed
696
  return 1;
697 698
}

699
static char *make_env_pair(const char *name, struct lsh_string *value)
700
{
701
  return ssh_format("%lz=%lS%c", name, value, 0)->data;
702 703
}

704
static char *make_env_pair_c(const char *name, char *value)
705
{
706
  return ssh_format("%lz=%lz%c", name, value, 0)->data;
707 708 709 710
}

static int do_spawn_shell(struct channel_request *c,
			  struct ssh_channel *channel,
711
			  struct ssh_connection *connection,
712 713 714
			  int want_reply,
			  struct simple_buffer *args)
{
715
  CAST(shell_request, closure, c);
716 717
  struct server_session *session = (struct server_session *) channel;

Niels Möller's avatar
Niels Möller committed
718 719 720
  int in[2];
  int out[2];
  int err[2];
721

722
  CHECK_TYPE(server_session, session);
723 724 725 726

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

727
  if (session->process)
728 729 730
    /* Already spawned a shell or command */
    goto fail;
  
Niels Möller's avatar
Niels Möller committed
731 732
  /* {in|out|err}[0] is for reading,
   * {in|out|err}[1] for writing. */
733

Niels Möller's avatar
Niels Möller committed
734
  if (make_pipe(in))
735
    {
Niels Möller's avatar
Niels Möller committed
736
      if (make_pipe(out))
737
	{
Niels Möller's avatar
Niels Möller committed
738
	  if (make_pipe(err))
739 740 741 742 743 744 745 746 747 748
	    {
	      pid_t child;
	      
	      switch(child = fork())
		{
		case -1:
		  werror("fork() failed: %s\n", strerror(errno));
		  /* Close and return channel_failure */
		  break; 
		case 0:
749
		  { /* Child */
750 751 752 753 754 755
		    char *shell = session->user->shell->data;
#define MAX_ENV 7
		    char *env[MAX_ENV];
		    char *tz = getenv("TZ");
		    int i = 0;

756 757 758 759 760 761
		    int old_stderr;
		    
		    debug("do_spawn_shell: Child process\n");

		    if (!session->user->shell)
		      {
762
			wwrite("No login shell!\n");
763 764 765 766 767 768
			exit(EXIT_FAILURE);
		      }

		    if (getuid() != session->user->uid)
		      if (!change_uid(session->user))
			{
769
			  wwrite("Changing uid failed!\n");
770 771 772 773 774 775 776
			  exit(EXIT_FAILURE);
			}
		    
		    assert(getuid() == session->user->uid);
		    
		    if (!change_dir(session->user))
		      {
777
			wwrite("Could not change to home (or root) directory!\n");
778 779 780 781 782
			exit(EXIT_FAILURE);
		      }

		    debug("Child: Setting up environment.\n");
		    
783 784 785 786 787 788 789 790 791 792 793 794 795
		    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);
796 797 798 799 800 801 802 803 804 805 806 807 808
#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)
		      {
809
			wwrite("Can't dup stdin!\n");
810 811 812 813 814 815 816
			exit(EXIT_FAILURE);
		      }
		    close(in[0]);
		    close(in[1]);
		    
		    if (dup2(out[1], STDOUT_FILENO) < 0)
		      {
817
			wwrite("Can't dup stdout!\n");
818 819 820 821 822 823
			exit(EXIT_FAILURE);
		      }
		    close(out[0]);
		    close(out[1]);

		    if ((old_stderr = dup(STDERR_FILENO)) < 0)
824
		      wwrite("Couldn't save old file_no.\n");
825 826 827 828 829 830 831 832 833 834 835 836
		    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]);

837
#if 1
Niels Möller's avatar
Niels Möller committed
838 839 840 841 842 843 844 845 846 847 848
		    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
849 850 851 852 853 854 855 856 857 858 859 860 861 862
		    /* 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));
863

Niels Möller's avatar
Niels Möller committed
864
		      _exit(EXIT_FAILURE);
865
		    }
866 867 868 869
#undef MAX_ENV
		  }
		default:
		  /* Parent */
Niels Möller's avatar
Niels Möller committed
870 871 872 873

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

Niels Möller's avatar
Niels Möller committed
879
		  session->in
Niels Möller's avatar
Niels Möller committed
880
		    = io_write(closure->backend, in[1],
881 882 883
			       SSH_MAX_PACKET,
			       /* FIXME: Use a proper close callback */
			       make_channel_close(channel));
Niels Möller's avatar
Niels Möller committed
884 885 886 887 888 889 890 891 892
		  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);

893
		  channel->receive = do_receive;
Niels Möller's avatar
Niels Möller committed
894
		  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
895
		  channel->eof = do_eof;
Niels Möller's avatar
Niels Möller committed
896
		  
897
#if 0
898
		  session->running = 1;
899 900 901 902 903 904 905 906 907 908 909 910 911 912
#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
913 914 915 916 917 918

		  return (want_reply
			  ? A_WRITE(channel->write,
				    format_channel_success(channel
							   ->channel_number))
			  : 0) | LSH_CHANNEL_READY_REC;
919
		}
Niels Möller's avatar
Niels Möller committed
920 921
	      close(err[0]);
	      close(err[1]);
922
	    }
Niels Möller's avatar
Niels Möller committed
923 924
	  close(out[0]);
	  close(out[1]);
925
	}
Niels Möller's avatar
Niels Möller committed
926 927
      close(in[0]);
      close(in[1]);
928 929 930 931 932 933 934
    }
 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
935 936
struct channel_request *make_shell_handler(struct io_backend *backend,
					   struct reap *reap)
937
{
938
  NEW(shell_request, closure);
939 940

  closure->super.handler = do_spawn_shell;
Niels Möller's avatar
Niels Möller committed
941
  closure->backend = backend;
Niels Möller's avatar
Niels Möller committed
942
  closure->reap = reap;
Niels Möller's avatar
Niels Möller committed
943
  
944 945 946
  return &closure->super;
}