client.c 13.3 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
/* client.c
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * $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
J.H.M. Dassen's avatar
J.H.M. Dassen committed
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Niels Möller's avatar
Niels Möller committed
22
23
 */

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

Niels Möller's avatar
Niels Möller committed
27
#include "client.h"
28

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

48
49
#include <signal.h>

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

53
54
#include "client.c.x"

55
56
/* Start a service that the server has accepted (for instance
 * ssh-userauth). */
57
/* GABA:
58
59
60
61
   (class
     (name accept_service_handler)
     (super packet_handler)
     (vars
62
       (service simple UINT32)
63
64
65
       (c object command_continuation)
       ;; Do we really need the exception handler here?
       (e object exception_handler)))
66
67
*/

Niels Möller's avatar
Niels Möller committed
68
69
70
71
static void
do_accept_service(struct packet_handler *c,
		  struct ssh_connection *connection,
		  struct lsh_string *packet)
72
{
73
  CAST(accept_service_handler, closure, c);
74
75

  struct simple_buffer buffer;
76
77
  unsigned msg_number;
  UINT32 name;
78

79
80
  simple_buffer_init(&buffer, packet->length, packet->data);
  
81
82
  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_SERVICE_ACCEPT)
83
84
85
86
87
88
89
90
91
      && (
#if DATAFELLOWS_WORKAROUNDS
	  (connection->peer_flags & PEER_SERVICE_ACCEPT_KLUDGE)
#else
	  0
#endif
	  || (parse_atom(&buffer, &name)
	      && (name == closure->service)))
      && parse_eod(&buffer))
92
93
94
95
    {
      lsh_string_free(packet);
      connection->dispatch[SSH_MSG_SERVICE_ACCEPT] = connection->fail;
      
Niels Möller's avatar
Niels Möller committed
96
      COMMAND_RETURN(closure->c, connection);
97
    }
Niels Möller's avatar
Niels Möller committed
98
  else
Niels Möller's avatar
Niels Möller committed
99
100
    {
      lsh_string_free(packet);
101
      PROTOCOL_ERROR(closure->e, "Invalid SSH_MSG_SERVICE_ACCEPT message");
Niels Möller's avatar
Niels Möller committed
102
    }
103
104
}

105
struct packet_handler *
106
make_accept_service_handler(UINT32 service,
107
108
			    struct command_continuation *c,
			    struct exception_handler *e)
109
{
110
  NEW(accept_service_handler, closure);
111

Niels Möller's avatar
Niels Möller committed
112
  closure->super.handler = do_accept_service;
113
  closure->service = service;
114
  closure->c = c;
115
116
  closure->e = e;
  
117
118
119
  return &closure->super;
}

120
/* GABA:
121
   (class
122
123
     (name request_service)
     (super command)
124
     (vars
125
126
       (service simple int)))
       ;; (service object ssh_service)))
127
128
*/

Niels Möller's avatar
Niels Möller committed
129
130
131
132
133
static void
do_request_service(struct command *s,
		   struct lsh_object *x,
		   struct command_continuation *c,
		   struct exception_handler *e)
134
{
135
136
  CAST(request_service, self, s);
  CAST(ssh_connection, connection, x);
137
  
Niels Möller's avatar
Niels Möller committed
138
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT]
139
    = make_accept_service_handler(self->service, c, e);
Niels Möller's avatar
Niels Möller committed
140
  
Niels Möller's avatar
Niels Möller committed
141
142
  C_WRITE(connection,
	  format_service_request(self->service));
143
144
}

145
struct command *make_request_service(int service)
146
{
147
  NEW(request_service, closure);
148

149
  closure->super.call = do_request_service;
150
151
152
153
154
  closure->service = service;

  return &closure->super;
}

155
/* ;; GABA:
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
   (class
     (name request_info)
     (vars
       ; Next request
       (next object request_info)
       (want_reply . int)
       ; If true, close the channel if the request fails
       (essential . int)

       (format method "struct lsh_string *" "struct ssh_channel *c")
       ; Called with a success/fail indication
       (result method int "struct ssh_channel *c" int)))
*/

#define REQUEST_FORMAT(r, c) ((r)->format((r), (c)))
#define REQUEST_RESULT(r, c, i) ((r)->result((r), (c), (i)))

Niels Möller's avatar
Niels Möller committed
173
/* Initiate and manage a session */
174
/* GABA:
175
176
177
178
179
   (class
     (name client_session)
     (super ssh_channel)
     (vars
       ; Exec or shell request. 
180
181
182
183
184
       ;(final_request simple int)
       ;(args string)

       ; List of requests
       (requests object request_info)
185
186
  
       ; To access stdio
187
188
189
       (in object lsh_fd)
       (out object lsh_fd)
       (err object lsh_fd)
190
191
192
193
194

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

195
/* Callback used when the server sends us eof */
Niels Möller's avatar
Niels Möller committed
196
197
static void
do_client_session_eof(struct ssh_channel *c)
Niels Möller's avatar
Niels Möller committed
198
{
199
  CAST(client_session, session, c);
Niels Möller's avatar
Niels Möller committed
200
  
201
  close_fd(session->in, 0);
202
#if 0
203
204
  close_fd(session->out, 0);
  close_fd(session->err, 0);
205
#endif
Niels Möller's avatar
Niels Möller committed
206
207
}  

Niels Möller's avatar
Niels Möller committed
208
209
static void
do_client_session_close(struct ssh_channel *c)
210
{
Niels Möller's avatar
Niels Möller committed
211
212
213
214
  static const struct exception finish_exception
    = STATIC_EXCEPTION(EXC_FINISH_PENDING, "Session closed.");

  EXCEPTION_RAISE(c->e, &finish_exception);
215
216
}

217

218
/* GABA:
219
220
221
222
223
224
225
   (class
     (name exit_handler)
     (super channel_request)
     (vars
       (exit_status simple "int *")))
*/

Niels Möller's avatar
Niels Möller committed
226
227
228
static void
do_exit_status(struct channel_request *c,
	       struct ssh_channel *channel,
229
230
	       struct ssh_connection *connection UNUSED,
	       UINT32 type UNUSED,
Niels Möller's avatar
Niels Möller committed
231
	       int want_reply,
232
233
234
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
235
{
236
  CAST(exit_handler, closure, c);
237
  UINT32 status;
Niels Möller's avatar
Niels Möller committed
238
239
240
241
242
243
244

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

245
246
      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
247

Niels Möller's avatar
Niels Möller committed
248
      /* Send EOF, if we haven't done that already. */
249
250
251
      /* FIXME: Make this behaviour configurable, there may be some
       * child process alive that we could talk to. */

252
      channel_eof(channel);
253
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
254
    }
Niels Möller's avatar
Niels Möller committed
255
256
  else
    /* Invalid request */
257
    PROTOCOL_ERROR(e, "Invalid exit-status message");
Niels Möller's avatar
Niels Möller committed
258
259
}

Niels Möller's avatar
Niels Möller committed
260
261
262
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
263
264
265
266
267
268
	       struct ssh_connection *connection UNUSED,
	       UINT32 type UNUSED,
 	       int want_reply,
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
269
{
270
271
  CAST(exit_handler, closure, c);

272
  UINT32 signal;
Niels Möller's avatar
Niels Möller committed
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  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);

295
296
      werror("%us", length, msg);
      werror("Remote process was killed by %z.%z\n",
297
	     signal ? STRSIGNAL(signal) : "an unknown signal",
298
	     core ? "(core dumped remotely)\n": "");
Niels Möller's avatar
Niels Möller committed
299

300
301
      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
302

303
304
305
306
      /* 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. */

307
      channel_eof(channel);
308
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
309
    }
Niels Möller's avatar
Niels Möller committed
310
311
  else
    /* Invalid request */
312
    PROTOCOL_ERROR(e, "Invalid exit-signal message");
Niels Möller's avatar
Niels Möller committed
313
314
315
316
}

struct channel_request *make_handle_exit_status(int *exit_status)
{
317
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
318
319
320
321
322
323
324
325
326
327

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

struct channel_request *make_handle_exit_signal(int *exit_status)
{
328
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
329
330
331
332
333
334
335
336

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

337
/* Receive channel data */
Niels Möller's avatar
Niels Möller committed
338
339
340
static void
do_receive(struct ssh_channel *c,
	   int type, struct lsh_string *data)
341
{
342
  CAST(client_session, closure, c);
343
344
345
346
  
  switch(type)
    {
    case CHANNEL_DATA:
347
      A_WRITE(&closure->out->write_buffer->super, data);
Niels Möller's avatar
Niels Möller committed
348
      break;
349
    case CHANNEL_STDERR_DATA:
350
      A_WRITE(&closure->err->write_buffer->super, data);
Niels Möller's avatar
Niels Möller committed
351
      break;
352
353
354
355
    default:
      fatal("Internal error!\n");
    }
}
Niels Möller's avatar
Niels Möller committed
356

Niels Möller's avatar
Niels Möller committed
357
/* We may send more data */
358
static void
Niels Möller's avatar
Niels Möller committed
359
360
do_send_adjust(struct ssh_channel *s,
	       UINT32 i UNUSED)
Niels Möller's avatar
Niels Möller committed
361
{
362
363
  CAST(client_session, self, s);

364
  assert(self->in->read);
365

366
  self->in->want_read = 1;
367
368
}

369
/* We have a remote shell */
Niels Möller's avatar
Niels Möller committed
370
371
372
373
374
static void
do_client_io(struct command *s UNUSED,
	     struct lsh_object *x,
	     struct command_continuation *c,
	     struct exception_handler *e UNUSED)
375

376
{
377
378
  CAST(client_session, session, x);
  struct ssh_channel *channel = &session->super;
379
  assert(x);
380

381
  /* Set up write fd:s. */
Niels Möller's avatar
Niels Möller committed
382
  
383
  channel->receive = do_receive;
384

385
386
387
388
  /* FIXME: It seems a little kludgy to modify
   * exception handlers here; it would be better to create the
   * fd-objects at a point where the right exception handlers can be
   * installed from the start. */
389
  session->out->e
390
391
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stdout",
392
					session->out->e,
393
					HANDLER_CONTEXT);
394

395
  session->err->e
396
397
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stderr",
398
					session->err->e,
399
					HANDLER_CONTEXT);
400
401

  /* Set up the fd we read from. */
Niels Möller's avatar
Niels Möller committed
402
  channel->send_adjust = do_send_adjust;
403

404
  session->in->read = make_channel_read_data(channel);
Balázs Scheidler's avatar
Balázs Scheidler committed
405
406

  /* FIXME: Perhaps there is some way to arrange that channel.c calls
Niels Möller's avatar
Niels Möller committed
407
   * the CHANNEL_SEND_ADJUST method instead? */
Balázs Scheidler's avatar
Balázs Scheidler committed
408
  if (session->super.send_window_size)
409
    session->in->want_read = 1;
410
  
411
  session->in->close_callback
Niels Möller's avatar
Niels Möller committed
412
    = make_channel_read_close_callback(channel);
Niels Möller's avatar
Niels Möller committed
413

414
  /* Make sure stdio is closed properly if the channel or connection dies */
415
416
417
  REMEMBER_RESOURCE(channel->resources, &session->in->super);
  REMEMBER_RESOURCE(channel->resources, &session->out->super);
  REMEMBER_RESOURCE(channel->resources, &session->err->super);
418
  
419
420
421
422
  ALIST_SET(channel->request_types, ATOM_EXIT_STATUS,
	    make_handle_exit_status(session->exit_status));
  ALIST_SET(channel->request_types, ATOM_EXIT_SIGNAL,
	    make_handle_exit_signal(session->exit_status));
423

424
  channel->eof = do_client_session_eof;
425
      
426
  COMMAND_RETURN(c, channel);
427
428
}

429
430
431
struct command client_io =
{ STATIC_HEADER, do_client_io };

432

433
434
435
struct ssh_channel *make_client_session(struct lsh_fd *in,
					struct lsh_fd *out,
					struct lsh_fd *err,
436
437
					UINT32 max_window,
					int *exit_status)
Niels Möller's avatar
Niels Möller committed
438
{
439
  NEW(client_session, self);
Niels Möller's avatar
Niels Möller committed
440

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

443
444
445
446
  /* Makes sure the pending_close bit is set whenever this session
   * dies, no matter when or how. */
  self->super.close = do_client_session_close;
  
447
  self->super.max_window = max_window;
Niels Möller's avatar
Niels Möller committed
448
449
450
451
  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
452
453

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

Niels Möller's avatar
Niels Möller committed
455
  /* self->expect_close = 0; */
Niels Möller's avatar
Niels Möller committed
456
457
458
459
  self->in = in;
  self->out = out;
  self->err = err;

460
  /* Flow control */
Niels Möller's avatar
Niels Möller committed
461
462
  out->write_buffer->report = &self->super.super;
  err->write_buffer->report = &self->super.super;
463
  
Niels Möller's avatar
Niels Möller committed
464
  self->exit_status = exit_status;
Niels Möller's avatar
Niels Möller committed
465
  
Niels Möller's avatar
Niels Möller committed
466
467
468
  return &self->super;
}

469
470
471
472
473
474
475
/* GABA:
   (class
     (name session_open_command)
     (super channel_open_command)
     (vars
       ; This command can only be executed once,
       ; so we can allocate the session object in advance.
476
       (session object ssh_channel)))
477
478
479
480
481
*/

static struct ssh_channel *
new_session(struct channel_open_command *s,
	    struct ssh_connection *connection,
482
	    UINT32 local_channel_number,
483
484
485
486
	    struct lsh_string **request)
{
  CAST(session_open_command, self, s);
  struct ssh_channel *res;
487
488
489

  self->session->write = connection->write;
  
490
491
492
  *request = format_channel_open(ATOM_SESSION,
				 local_channel_number,
				 self->session, "");
493
  
494
  res = self->session;
495
496
497
498
499
500
501

  /* Make sure this command can not be invoked again */
  self->session = NULL;

  return res;
}

502
struct command *make_open_session_command(struct ssh_channel *session)
503
504
505
506
507
{
  NEW(session_open_command, self);
  self->super.super.call = do_channel_open_command;
  self->super.new_channel = new_session;
  self->session = session;
508
509

  return &self->super.super;
510
511
}

512
513
514
515
516
517
518
519

static struct lsh_string *
do_format_shell_request(struct channel_request_command *s UNUSED,
			struct ssh_channel *channel,
			struct command_continuation **c)
{
  return format_channel_request(ATOM_SHELL, channel, !!*c, "");
}
520

521
522
struct channel_request_command request_shell =
{ { STATIC_HEADER, do_channel_request_command }, do_format_shell_request };
523

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556

/* GABA:
   (class
     (name exec_request)
     (super channel_request_command)
     (vars
       (command string)))
*/

static struct lsh_string *
do_format_exec_request(struct channel_request_command *s,
		       struct ssh_channel *channel,
		       struct command_continuation **c)
{
  CAST(exec_request, self, s);

  verbose("lsh: Requesting remote exec.\n");

  return format_channel_request(ATOM_EXEC, channel,
				!!*c, "%S", self->command);
}

struct command *
make_exec_request(struct lsh_string *command)
{
  NEW(exec_request, req);

  req->super.format_request = do_format_exec_request;
  req->super.super.call = do_channel_request_command;
  req->command = command;

  return &req->super.super;
}