client.c 14.9 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* client.c
 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 *
 * $Id$ */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Niels Möller's avatar
Niels Möller committed
24
25
 */

26
/* FIXME: Why include stdio? */
Niels Möller's avatar
Niels Möller committed
27
/* #include <stdio.h> */
Niels Möller's avatar
Niels Möller committed
28

Niels Möller's avatar
Niels Möller committed
29
#include "client.h"
30

Niels Möller's avatar
Niels Möller committed
31
#include "abstract_io.h"
32
#include "channel.h"
33
34
#include "connection.h"
#include "crypto.h"
Niels Möller's avatar
Niels Möller committed
35
#include "debug.h"
Niels Möller's avatar
Niels Möller committed
36
#include "encrypt.h"
37
#include "format.h"
Niels Möller's avatar
Niels Möller committed
38
#include "pad.h"
39
#include "parse.h"
40
41
#include "read_line.h"
#include "read_packet.h"
42
#include "service.h"
43
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
44
#include "translate_signal.h"
Niels Möller's avatar
Niels Möller committed
45
#include "unpad.h"
46
47
48
#include "version.h"
#include "werror.h"
#include "xalloc.h"
49
#include "compress.h"
Niels Möller's avatar
Niels Möller committed
50

Niels Möller's avatar
Niels Möller committed
51
#include <string.h>
52
#include <assert.h>
Niels Möller's avatar
Niels Möller committed
53

54
55
#include "client.c.x"

56
/* Handle connection and initial handshaking. */
57
58
59
60
61
62
63
/* CLASS:
   (class
     (name client_callback)
     (super fd_callback)
     (vars
       (backend object io_backend)
       (block_size simple UINT32)
64
       (id_comment simple "const char *")
65
66
67
68
69
       (random object randomness)
       (init object make_kexinit)
       (kexinit_handler object packet_handler)))
*/

70
static int client_initiate(struct fd_callback **c,
Niels Möller's avatar
Niels Möller committed
71
			   int fd)
Niels Möller's avatar
Niels Möller committed
72
{
73
  struct client_callback *closure
74
    = (struct client_callback *) *c;
Niels Möller's avatar
Niels Möller committed
75

Niels Möller's avatar
Niels Möller committed
76
77
  int res;
  
78
  struct ssh_connection *connection
Niels Möller's avatar
Niels Möller committed
79
    = make_ssh_connection(closure->kexinit_handler);
80
81
82

  connection_init_io(connection,
		     io_read_write(closure->backend, fd,
Niels Möller's avatar
Niels Möller committed
83
				   make_client_read_line(connection),
84
85
86
				   closure->block_size,
				   make_client_close_handler()),
		     closure->random);
Niels Möller's avatar
Niels Möller committed
87
  
Niels Möller's avatar
Niels Möller committed
88
  connection->client_version
89
    = ssh_format("SSH-%lz-%lz %lz",
Niels Möller's avatar
Niels Möller committed
90
91
92
		 PROTOCOL_VERSION,
		 SOFTWARE_CLIENT_VERSION,
		 closure->id_comment);
Niels Möller's avatar
Niels Möller committed
93
  
Niels Möller's avatar
Niels Möller committed
94
95
  res = A_WRITE(connection->raw,
		ssh_format("%lS\r\n", connection->client_version));
96
  if (LSH_CLOSEDP(res))
Niels Möller's avatar
Niels Möller committed
97
98
    return res;

99
100
101
  return res | initiate_keyexchange(connection, CONNECTION_CLIENT,
				    MAKE_KEXINIT(closure->init),
				    NULL);
Niels Möller's avatar
Niels Möller committed
102
103
}

104
105
106
107
108
109
110
111
/* CLASS:
   (class
     (name client_line_handler)
     (super line_handler)
     (vars
       (connection object ssh_connection)))
*/

112
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
113
114
115
				    UINT32 length,
				    UINT8 *line)
{
116
  CAST(client_line_handler, closure, *h);
117

Niels Möller's avatar
Niels Möller committed
118
  if ( (length >= 4) && !memcmp(line, "SSH-", 4))
Niels Möller's avatar
Niels Möller committed
119
120
    {
      /* Parse and remember format string */
Niels Möller's avatar
Niels Möller committed
121
122
      if ( ((length >= 8) && !memcmp(line + 4, "2.0-", 4))
	   || ((length >= 9) && !memcmp(line + 4, "1.99-", 5)))
Niels Möller's avatar
Niels Möller committed
123
	{
124
125
126
127
128
129
130
131
132
133
134
	  struct read_handler *new = 
	    make_read_packet(
	      make_packet_unpad(
	        make_packet_inflate(
	          make_packet_debug(&closure->connection->super, ""),
	          closure->connection
		  )
		),
	      closure->connection
	      );
	  
Niels Möller's avatar
Niels Möller committed
135
	  closure->connection->server_version
136
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
137

138
139
140
141
142
143
144
145
	  verbose("Client version: ");
	  verbose_safe(closure->connection->client_version->length,
		       closure->connection->client_version->data);
	  verbose("\nServer version: ");
	  verbose_safe(closure->connection->server_version->length,
		       closure->connection->server_version->data);
	  verbose("\n");
	  
Niels Möller's avatar
Niels Möller committed
146
	  /* FIXME: Cleanup properly. */
147
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
148
149

	  return new;
Niels Möller's avatar
Niels Möller committed
150
151
152
	}
      else
	{
153
	  wwrite("Unsupported protocol version: ");
Niels Möller's avatar
Niels Möller committed
154
	  werror_safe(length, line);
155
	  wwrite("\n");
Niels Möller's avatar
Niels Möller committed
156

157
	  /* FIXME: Clean up properly */
158
	  KILL(closure);
Niels Möller's avatar
Niels Möller committed
159
	  *h = NULL;
160
		  
Niels Möller's avatar
Niels Möller committed
161
162
163
164
165
166
	  return 0;
	}
    }
  else
    {
      /* Display line */
Niels Möller's avatar
Niels Möller committed
167
      werror_safe(length, line);
Niels Möller's avatar
Niels Möller committed
168
169

      /* Read next line */
Niels Möller's avatar
Niels Möller committed
170
      return 0;
Niels Möller's avatar
Niels Möller committed
171
172
173
    }
}

Niels Möller's avatar
Niels Möller committed
174
struct read_handler *make_client_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
175
{
176
  NEW(client_line_handler, closure);
177

178
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
179
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
180
  
181
  return make_read_line(&closure->super);
Niels Möller's avatar
Niels Möller committed
182
183
}
  
Niels Möller's avatar
Niels Möller committed
184
185
struct fd_callback *
make_client_callback(struct io_backend *b,
186
		     const char *comment,
Niels Möller's avatar
Niels Möller committed
187
188
189
190
191
		     UINT32 block_size,
		     struct randomness *random,
		     struct make_kexinit *init,
		     struct packet_handler *kexinit_handler)
  
Niels Möller's avatar
Niels Möller committed
192
{
193
  NEW(client_callback, connected);
Niels Möller's avatar
Niels Möller committed
194

195
  connected->super.f = client_initiate;
Niels Möller's avatar
Niels Möller committed
196
197
  connected->backend = b;
  connected->block_size = block_size;
Niels Möller's avatar
Niels Möller committed
198
  connected->id_comment = comment;
Niels Möller's avatar
Niels Möller committed
199
200
201
202
203

  connected->random = random;
  connected->init = init;
  connected->kexinit_handler = kexinit_handler;

204
  return &connected->super;
Niels Möller's avatar
Niels Möller committed
205
}
Niels Möller's avatar
Niels Möller committed
206

207
208
static int client_close_die(struct close_callback *closure UNUSED,
			    int reason)
Niels Möller's avatar
Niels Möller committed
209
{
210
211
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
212
    wwrite("Connection died.\n");
213
214
215

  /* FIXME: Return value is not used. */
  return 4711;
Niels Möller's avatar
Niels Möller committed
216
217
}

218
struct close_callback *make_client_close_handler(void)
Niels Möller's avatar
Niels Möller committed
219
{
220
  NEW(close_callback, c);
Niels Möller's avatar
Niels Möller committed
221

222
  c->f = client_close_die;
Niels Möller's avatar
Niels Möller committed
223
224
225

  return c;
}
226

227
/* Start a service that the server has accepted (for instance ssh-userauth). */
228
229
230
231
232
233
234
235
236
/* CLASS:
   (class
     (name accept_service_handler)
     (super packet_handler)
     (vars
       (service_name simple int)
       (service object ssh_service)))
*/

237
238
239
240
static int do_accept_service(struct packet_handler *c,
			     struct ssh_connection *connection,
			     struct lsh_string *packet)
{
241
  CAST(accept_service_handler, closure, c);
242
243
244
245
246

  struct simple_buffer buffer;
  int msg_number;
  int name;

247
248
  simple_buffer_init(&buffer, packet->length, packet->data);
  
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_SERVICE_ACCEPT)
      && parse_atom(&buffer, &name)
      && parse_eod(&buffer)
      && (name == closure->service_name))
    {
      lsh_string_free(packet);
      connection->dispatch[SSH_MSG_SERVICE_ACCEPT] = connection->fail;
      
      return SERVICE_INIT(closure->service, connection);
    }

  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

Niels Möller's avatar
Niels Möller committed
265
266
struct packet_handler *make_accept_service_handler(int service_name,
						   struct ssh_service *service)
267
{
268
  NEW(accept_service_handler, closure);
269

Niels Möller's avatar
Niels Möller committed
270
  closure->super.handler = do_accept_service;
271
272
273
274
275
276
  closure->service_name = service_name;
  closure->service = service;

  return &closure->super;
}

277
278
279
280
281
282
283
284
285
/* CLASS:
   (class
     (name service_request)
     (super ssh_service)
     (vars
       (service_name simple int)
       (service object ssh_service)))
*/

286
287
288
static int do_request_service(struct ssh_service *c,
			      struct ssh_connection *connection)
{
289
  CAST(service_request, closure, c);
290
  
Niels Möller's avatar
Niels Möller committed
291
292
293
294
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT]
    = make_accept_service_handler(closure->service_name,
				  closure->service);
  
295
  return A_WRITE(connection->write, format_service_request(closure->service_name));
296
297
298
299
300
}

struct ssh_service *request_service(int service_name,
				    struct ssh_service *service)
{
301
  NEW(service_request, closure);
302
303
304
305
306
307
308
309

  closure->super.init = do_request_service;
  closure->service_name = service_name;
  closure->service = service;

  return &closure->super;
}

Niels Möller's avatar
Niels Möller committed
310
/* Initiate and manage a session */
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/* CLASS:
   (class
     (name client_session)
     (super ssh_channel)
     (vars
       ; Exec or shell request. 
       (final_request simple int)
       (args string)
  
       ; To access stdio
       (in object io_fd)
       (out object io_fd)
       (err object io_fd)

       ; Where to save the exit code.
       (exit_status simple "int *")))
*/

329
/* Callback used when the server sends us eof */
Niels Möller's avatar
Niels Möller committed
330
331
static int close_client_session(struct ssh_channel *c)
{
332
  CAST(client_session, session, c);
Niels Möller's avatar
Niels Möller committed
333
  
334
335
336
337
338
  close_fd(&session->in->super, 0);
#if 0
  close_fd(&session->out->super, 0);
  close_fd(&session->err->super, 0);
#endif
Niels Möller's avatar
Niels Möller committed
339
340
341
342
  
  return LSH_OK | LSH_CHANNEL_PENDING_CLOSE;
}  

Niels Möller's avatar
Niels Möller committed
343
static int client_session_die(struct ssh_channel *c)
344
{
345
  CAST(client_session, closure, c);
346
  
Niels Möller's avatar
Niels Möller committed
347
  /* FIXME: Don't die this hard. */
348
349
  if ( (closure->super.flags & (CHANNEL_SENT_CLOSE | CHANNEL_RECEIVED_CLOSE))
       ==  (CHANNEL_SENT_CLOSE | CHANNEL_RECEIVED_CLOSE))
350
351
352
353
354
    exit(EXIT_SUCCESS);

  exit(EXIT_FAILURE);
}

355
356
357
358
359
360
361
362
/* CLASS:
   (class
     (name exit_handler)
     (super channel_request)
     (vars
       (exit_status simple "int *")))
*/

Niels Möller's avatar
Niels Möller committed
363
364
static int do_exit_status(struct channel_request *c,
			  struct ssh_channel *channel,
365
			  struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
366
367
368
			  int want_reply,
			  struct simple_buffer *args)
{
369
  CAST(exit_handler, closure, c);
Niels Möller's avatar
Niels Möller committed
370
371
372
373
374
375
376
377
  int status;

  if (!want_reply
      && parse_uint32(args, &status)
      && parse_eod(args))
    {
      *closure->exit_status = status;

378
379
      ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);
      ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);
Niels Möller's avatar
Niels Möller committed
380

381
382
383
384
      /* Sent EOF, if we haven't done that already. */
      /* FIXME: Make this behaviour configurable, there may be some
       * child process alive that we could talk to. */

385
      if (!(channel->flags & CHANNEL_SENT_EOF))
386
387
	return channel_eof(channel);
      
388
      return LSH_OK | LSH_GOON;
Niels Möller's avatar
Niels Möller committed
389
390
391
392
393
394
395
396
    }
  
  /* Invalid request */
  return LSH_FAIL | LSH_DIE;
}

static int do_exit_signal(struct channel_request *c,
			  struct ssh_channel *channel,
397
			  struct ssh_connection *connection UNUSED,
Niels Möller's avatar
Niels Möller committed
398
399
400
			  int want_reply,
			  struct simple_buffer *args)
{
401
402
  CAST(exit_handler, closure, c);

Niels Möller's avatar
Niels Möller committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
  int signal;
  int core;

  UINT8 *msg;
  UINT32 length;

  UINT8 *language;
  UINT32 language_length;
  
  if (!want_reply
      && parse_uint32(args, &signal)
      && parse_boolean(args, &core)
      && parse_string(args, &length, &msg)
      && parse_string(args, &language_length, &language)
      && parse_eod(args))
    {
      /* FIXME: What exit status should be returned when the remote
       * process dies violently? */

      *closure->exit_status = 7;

      signal = signal_network_to_local(signal);

      werror_utf8(length, msg);
      werror("Remote process was killed by %s.\n",
	     signal ? strsignal(signal) : "an unknown signal");
      if (core)
430
	wwrite("(core dumped remotely)\n");
Niels Möller's avatar
Niels Möller committed
431

432
433
      ALIST_SET(channel->request_types, ATOM_EXIT_STATUS, NULL);
      ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL, NULL);
Niels Möller's avatar
Niels Möller committed
434

435
436
437
438
439
440
441
442
443
      /* Sent EOF, if we haven't done that already. */
      /* FIXME: Make this behaviour configurable, there may be some
       * child process alive that we could talk to. */

      if (!(channel->flags & CHANNEL_SENT_EOF))
	return channel_eof(channel);

      return LSH_OK | LSH_GOON;
#if 0
Niels Möller's avatar
Niels Möller committed
444
      return close_client_session(channel);
445
#endif
Niels Möller's avatar
Niels Möller committed
446
447
448
449
450
451
452
453
    }
  
  /* Invalid request */
  return LSH_FAIL | LSH_DIE;
}

struct channel_request *make_handle_exit_status(int *exit_status)
{
454
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
455
456
457
458
459
460
461
462
463
464

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

struct channel_request *make_handle_exit_signal(int *exit_status)
{
465
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
466
467
468
469
470
471
472
473

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

474
475
/* Receive channel data */
static int do_receive(struct ssh_channel *c,
476
477
		      int type, struct lsh_string *data)
{
478
  CAST(client_session, closure, c);
479
480
481
482
  
  switch(type)
    {
    case CHANNEL_DATA:
483
      return A_WRITE(&closure->out->buffer->super, data);
484
    case CHANNEL_STDERR_DATA:
485
      return A_WRITE(&closure->err->buffer->super, data);
486
487
488
489
    default:
      fatal("Internal error!\n");
    }
}
Niels Möller's avatar
Niels Möller committed
490

Niels Möller's avatar
Niels Möller committed
491
492
493
/* We may send more data */
static int do_send(struct ssh_channel *c)
{
494
  CAST(client_session, closure, c);
Niels Möller's avatar
Niels Möller committed
495

496
497
498
  assert(closure->in->super.read);
  assert(closure->in->handler);
  closure->in->super.want_read = 1;
Niels Möller's avatar
Niels Möller committed
499
500
501
502

  return LSH_OK | LSH_GOON;
}

503
/* We have a remote shell */
504
static int do_io(struct ssh_channel *channel)
505
{
506
  CAST(client_session, closure, channel);
507
  
508
  channel->receive = do_receive;
509
  
510
511
  closure->out->super.close_callback
    = closure->err->super.close_callback = make_channel_close(channel);
Niels Möller's avatar
Niels Möller committed
512
  
Niels Möller's avatar
Niels Möller committed
513
  closure->in->handler = make_channel_read_data(&closure->super);
514
  channel->send = do_send;
Niels Möller's avatar
Niels Möller committed
515
516
517
518
519

  ALIST_SET(channel->request_types, ATOM_EXIT_STATUS,
	    make_handle_exit_status(closure->exit_status));
  ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL,
	    make_handle_exit_signal(closure->exit_status));
520
521
522

  channel->eof = close_client_session;

523
  return LSH_OK | LSH_CHANNEL_READY_SEND;
524
525
526
}

/* We have opened a channel of type "session" */
Niels Möller's avatar
Niels Möller committed
527
static int do_open_confirm(struct ssh_channel *c)
528
{
529
  CAST(client_session, closure, c);
Niels Möller's avatar
Niels Möller committed
530
531
  struct lsh_string *args;
  
532
533
534
  closure->super.open_confirm = NULL;
  closure->super.open_failure = NULL;

Niels Möller's avatar
Niels Möller committed
535
  closure->super.channel_success = do_io;
536
537
  closure->super.channel_failure = client_session_die;

Niels Möller's avatar
Niels Möller committed
538
539
540
541
542
543
  args = closure->args;
  closure->args = NULL; /* for gc */

  return A_WRITE(closure->super.write,
		 format_channel_request(closure->final_request, c, 1,
					"%lfS", args));
544
545
}

546
static struct ssh_channel *make_client_session(struct io_fd *in,
547
548
					       struct io_fd *out,
					       struct io_fd *err,
549
550
					       UINT32 max_window,
					       int final_request,
Niels Möller's avatar
Niels Möller committed
551
552
					       struct lsh_string *args,
					       int *exit_status)
Niels Möller's avatar
Niels Möller committed
553
{
554
  NEW(client_session, self);
Niels Möller's avatar
Niels Möller committed
555

Niels Möller's avatar
Niels Möller committed
556
557
  init_channel(&self->super);

558
  self->super.max_window = max_window;
Niels Möller's avatar
Niels Möller committed
559
560
561
562
  self->super.rec_window_size = max_window;

  /* FIXME: Make maximum packet size configurable */
  self->super.rec_max_packet = SSH_MAX_PACKET;
Niels Möller's avatar
Niels Möller committed
563
564

  self->super.request_types = make_alist(0, -1);
565

Niels Möller's avatar
Niels Möller committed
566
  /* self->expect_close = 0; */
Niels Möller's avatar
Niels Möller committed
567
568
569
570
  self->in = in;
  self->out = out;
  self->err = err;

Niels Möller's avatar
Niels Möller committed
571
572
  self->final_request = final_request;
  self->args = args;
Niels Möller's avatar
Niels Möller committed
573
574

  self->exit_status = exit_status;
Niels Möller's avatar
Niels Möller committed
575
  
Niels Möller's avatar
Niels Möller committed
576
577
578
  return &self->super;
}

579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
/* CLASS:
   (class
     (name client_startup)
     (super connection_startup)
     (vars
       (session object ssh_channel)
       
       ; Exec or shell request. 
       ;; (final_request simple int)
       ;; (args string)
  
       ; To access stdio 
       ;; (in object io_fd)
       ;; (out object io_fd)
       ;; (err object io_fd)
       ))
*/

597
static int do_client_startup(struct connection_startup *c,
Niels Möller's avatar
Niels Möller committed
598
599
			     struct channel_table *table,
			     struct abstract_write *write)
600
{
601
  CAST(client_startup, closure, c);
602
  struct lsh_string *s;
Niels Möller's avatar
Niels Möller committed
603
604
  
  closure->session->write = write;
605
  
Niels Möller's avatar
Niels Möller committed
606
607
608
  closure->session->open_confirm = do_open_confirm;
  closure->session->open_failure = client_session_die;

609
610
611
612
613
614
  s = prepare_channel_open(table, ATOM_SESSION,
			   closure->session, "");
  if (!s)
    fatal("Couldn't allocate a channel number!\n");

  return A_WRITE(write, s);
615
616
}

Niels Möller's avatar
Niels Möller committed
617
618
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

619
/* Request opening a session. */
Niels Möller's avatar
Niels Möller committed
620
struct connection_startup *make_client_startup(struct io_fd *in,
621
622
					       struct io_fd *out,
					       struct io_fd *err,
Niels Möller's avatar
Niels Möller committed
623
					       int final_request,
Niels Möller's avatar
Niels Möller committed
624
625
					       struct lsh_string *args,
					       int *exit_status)
626
{
627
  NEW(client_startup, closure);
Niels Möller's avatar
Niels Möller committed
628
  
629
  closure->super.start = do_client_startup;
630
631
  closure->session = make_client_session(in, out, err,
					 WINDOW_SIZE,
Niels Möller's avatar
Niels Möller committed
632
633
					 final_request, args,
					 exit_status);
634

635
636
637
  return &closure->super;
}