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
/* For debug */
#include <signal.h>
105
#if HAVE_UNISTD_H
Niels Möller's avatar
Niels Möller committed
106
#include <unistd.h>
107
#endif
Niels Möller's avatar
Niels Möller committed
108

109 110 111 112 113 114 115 116 117
#include "server.c.x"

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

118 119
       (secret object signer) ; Secret key
       (host_key string)      ; Public key 
120

121 122
       (block_size simple UINT32)
       (id_comment simple "const char *")
123

124 125 126
       (random object randomness)
       (init object make_kexinit)
       (kexinit_handler object packet_handler)))
127
*/
128

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

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

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

Niels Möller's avatar
Niels Möller committed
148 149
  
  connection->server_version
Niels Möller's avatar
Niels Möller committed
150
    = ssh_format("SSH-%lz-%lz %lz",
151
		 SERVER_PROTOCOL_VERSION,
Niels Möller's avatar
Niels Möller committed
152 153
		 SOFTWARE_SERVER_VERSION,
		 closure->id_comment);
154 155 156 157 158 159
#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
160 161
  res = A_WRITE(connection->raw,
		 ssh_format("%lS\r\n", connection->server_version));
162
#endif
163
  if (LSH_CLOSEDP(res))
Niels Möller's avatar
Niels Möller committed
164 165
    return res;

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

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

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

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

	  return new;
	}
218 219 220 221 222 223 224
      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
225
      else
226
#endif /* SSH1_FALLBACK */
Niels Möller's avatar
Niels Möller committed
227
	{
228
	  wwrite("Unsupported protocol version: ");
Niels Möller's avatar
Niels Möller committed
229
	  werror_safe(length, line);
230
	  wwrite("\n");
Niels Möller's avatar
Niels Möller committed
231

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

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

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

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

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

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

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

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

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

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

306 307
  closure->connection = c;  
  closure->super.f = server_die;
Niels Möller's avatar
Niels Möller committed
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 351 352
  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
353 354
}

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

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

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

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

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

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

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

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

419 420 421 422 423
  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
424 425
}

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

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

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

  init_channel(&self->super);

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

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

  self->super.request_types = request_types;
451 452 453

  self->super.close = do_close;
  
454 455
  self->user = user;

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

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

477 478
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return &self->super;
}

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

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

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

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

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

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

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

724
  CHECK_TYPE(server_session, session);
725 726 727 728

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

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

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

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

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

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

		    debug("Child: Setting up environment.\n");
		    
785 786 787 788 789 790 791 792 793 794 795 796 797
		    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);
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)
		      {
811
			wwrite("Can't dup stdin!\n");
812 813 814 815 816 817 818
			exit(EXIT_FAILURE);
		      }
		    close(in[0]);
		    close(in[1]);
		    
		    if (dup2(out[1], STDOUT_FILENO) < 0)
		      {
819
			wwrite("Can't dup stdout!\n");
820 821 822 823 824 825
			exit(EXIT_FAILURE);
		      }
		    close(out[0]);
		    close(out[1]);

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

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

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

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

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

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

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

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