client.c 13.2 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
 */

#include "client.h"
25

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

45
46
#include <signal.h>

Niels Möller's avatar
Niels Möller committed
47
#include <string.h>
48
#include <assert.h>
Niels Möller's avatar
Niels Möller committed
49

50
51
#include "client.c.x"

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

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

  struct simple_buffer buffer;
73
74
  unsigned msg_number;
  UINT32 name;
75

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

102
struct packet_handler *
103
make_accept_service_handler(UINT32 service,
104
105
			    struct command_continuation *c,
			    struct exception_handler *e)
106
{
107
  NEW(accept_service_handler, closure);
108

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

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

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

142
struct command *make_request_service(int service)
143
{
144
  NEW(request_service, closure);
145

146
  closure->super.call = do_request_service;
147
148
149
150
151
  closure->service = service;

  return &closure->super;
}

152
/* ;; GABA:
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
   (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
170
/* Initiate and manage a session */
171
/* GABA:
172
173
174
175
176
   (class
     (name client_session)
     (super ssh_channel)
     (vars
       ; Exec or shell request. 
177
178
179
180
181
       ;(final_request simple int)
       ;(args string)

       ; List of requests
       (requests object request_info)
182
183
  
       ; To access stdio
184
185
186
       (in object lsh_fd)
       (out object lsh_fd)
       (err object lsh_fd)
187
188
189
190
191

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

192
/* Callback used when the server sends us eof */
Niels Möller's avatar
Niels Möller committed
193
194
static void
do_client_session_eof(struct ssh_channel *c)
Niels Möller's avatar
Niels Möller committed
195
{
196
  CAST(client_session, session, c);
Niels Möller's avatar
Niels Möller committed
197
  
198
  close_fd(session->in, 0);
Niels Möller's avatar
Niels Möller committed
199
200
}  

Niels Möller's avatar
Niels Möller committed
201
202
static void
do_client_session_close(struct ssh_channel *c)
203
{
Niels Möller's avatar
Niels Möller committed
204
205
206
207
  static const struct exception finish_exception
    = STATIC_EXCEPTION(EXC_FINISH_PENDING, "Session closed.");

  EXCEPTION_RAISE(c->e, &finish_exception);
208
209
}

210

211
/* GABA:
212
213
214
215
216
217
218
   (class
     (name exit_handler)
     (super channel_request)
     (vars
       (exit_status simple "int *")))
*/

Niels Möller's avatar
Niels Möller committed
219
220
221
static void
do_exit_status(struct channel_request *c,
	       struct ssh_channel *channel,
222
223
	       struct ssh_connection *connection UNUSED,
	       UINT32 type UNUSED,
Niels Möller's avatar
Niels Möller committed
224
	       int want_reply,
225
226
227
	       struct simple_buffer *args,
	       struct command_continuation *s,
	       struct exception_handler *e)
Niels Möller's avatar
Niels Möller committed
228
{
229
  CAST(exit_handler, closure, c);
230
  UINT32 status;
Niels Möller's avatar
Niels Möller committed
231
232
233
234
235
236
237

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

238
239
      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
240

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

245
      channel_eof(channel);
246
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
247
    }
Niels Möller's avatar
Niels Möller committed
248
249
  else
    /* Invalid request */
250
    PROTOCOL_ERROR(e, "Invalid exit-status message");
Niels Möller's avatar
Niels Möller committed
251
252
}

Niels Möller's avatar
Niels Möller committed
253
254
255
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
256
257
258
259
260
261
	       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
262
{
263
264
  CAST(exit_handler, closure, c);

265
  UINT32 signal;
Niels Möller's avatar
Niels Möller committed
266
267
  int core;

Niels Möller's avatar
Niels Möller committed
268
  const UINT8 *msg;
Niels Möller's avatar
Niels Möller committed
269
270
  UINT32 length;

Niels Möller's avatar
Niels Möller committed
271
  const UINT8 *language;
Niels Möller's avatar
Niels Möller committed
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  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);

288
289
      werror("%us", length, msg);
      werror("Remote process was killed by %z.%z\n",
290
	     signal ? STRSIGNAL(signal) : "an unknown signal",
291
	     core ? "(core dumped remotely)\n": "");
Niels Möller's avatar
Niels Möller committed
292

293
294
      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
295

296
297
298
299
      /* 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. */

300
      channel_eof(channel);
301
      COMMAND_RETURN(s, NULL);
Niels Möller's avatar
Niels Möller committed
302
    }
Niels Möller's avatar
Niels Möller committed
303
304
  else
    /* Invalid request */
305
    PROTOCOL_ERROR(e, "Invalid exit-signal message");
Niels Möller's avatar
Niels Möller committed
306
307
308
309
}

struct channel_request *make_handle_exit_status(int *exit_status)
{
310
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
311
312
313
314
315
316
317
318
319
320

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

struct channel_request *make_handle_exit_signal(int *exit_status)
{
321
  NEW(exit_handler, self);
Niels Möller's avatar
Niels Möller committed
322
323
324
325
326
327
328
329

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

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

Niels Möller's avatar
Niels Möller committed
350
/* We may send more data */
351
static void
Niels Möller's avatar
Niels Möller committed
352
353
do_send_adjust(struct ssh_channel *s,
	       UINT32 i UNUSED)
Niels Möller's avatar
Niels Möller committed
354
{
355
356
  CAST(client_session, self, s);

357
  assert(self->in->read);
358

359
  self->in->want_read = 1;
360
361
}

362
/* We have a remote shell */
Niels Möller's avatar
Niels Möller committed
363
364
365
366
367
static void
do_client_io(struct command *s UNUSED,
	     struct lsh_object *x,
	     struct command_continuation *c,
	     struct exception_handler *e UNUSED)
368

369
{
370
371
  CAST(client_session, session, x);
  struct ssh_channel *channel = &session->super;
372
  assert(x);
373

374
  /* Set up write fd:s. */
Niels Möller's avatar
Niels Möller committed
375
  
376
  channel->receive = do_receive;
377

378
379
380
381
  /* 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. */
382
  session->out->e
383
384
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stdout",
385
					session->out->e,
386
					HANDLER_CONTEXT);
387

388
  session->err->e
389
390
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stderr",
391
					session->err->e,
392
					HANDLER_CONTEXT);
393
394

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

397
  session->in->read = make_channel_read_data(channel);
Balázs Scheidler's avatar
Balázs Scheidler committed
398
399

  /* FIXME: Perhaps there is some way to arrange that channel.c calls
Niels Möller's avatar
Niels Möller committed
400
   * the CHANNEL_SEND_ADJUST method instead? */
Balázs Scheidler's avatar
Balázs Scheidler committed
401
  if (session->super.send_window_size)
402
    session->in->want_read = 1;
403
  
404
  session->in->close_callback
Niels Möller's avatar
Niels Möller committed
405
    = make_channel_read_close_callback(channel);
Niels Möller's avatar
Niels Möller committed
406

407
  /* Make sure stdio is closed properly if the channel or connection dies */
408
409
410
  REMEMBER_RESOURCE(channel->resources, &session->in->super);
  REMEMBER_RESOURCE(channel->resources, &session->out->super);
  REMEMBER_RESOURCE(channel->resources, &session->err->super);
411
  
412
413
414
415
  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));
416

417
  channel->eof = do_client_session_eof;
418
      
419
  COMMAND_RETURN(c, channel);
420
421
}

422
423
424
struct command client_io =
{ STATIC_HEADER, do_client_io };

425

426
427
428
429
430
431
struct ssh_channel *
make_client_session(struct lsh_fd *in,
		    struct lsh_fd *out,
		    struct lsh_fd *err,
		    UINT32 initial_window,
		    int *exit_status)
Niels Möller's avatar
Niels Möller committed
432
{
433
  NEW(client_session, self);
Niels Möller's avatar
Niels Möller committed
434

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

437
438
439
440
  /* Makes sure the pending_close bit is set whenever this session
   * dies, no matter when or how. */
  self->super.close = do_client_session_close;
  
441
  self->super.rec_window_size = initial_window;
Niels Möller's avatar
Niels Möller committed
442
443

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

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

Niels Möller's avatar
Niels Möller committed
448
  /* self->expect_close = 0; */
Niels Möller's avatar
Niels Möller committed
449
450
451
452
  self->in = in;
  self->out = out;
  self->err = err;

453
  /* Flow control */
Niels Möller's avatar
Niels Möller committed
454
455
  out->write_buffer->report = &self->super.super;
  err->write_buffer->report = &self->super.super;
456
  
Niels Möller's avatar
Niels Möller committed
457
  self->exit_status = exit_status;
Niels Möller's avatar
Niels Möller committed
458
  
Niels Möller's avatar
Niels Möller committed
459
460
461
  return &self->super;
}

462
463
464
465
466
467
468
/* 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.
469
       (session object ssh_channel)))
470
471
472
473
474
*/

static struct ssh_channel *
new_session(struct channel_open_command *s,
	    struct ssh_connection *connection,
475
	    UINT32 local_channel_number,
476
477
478
479
	    struct lsh_string **request)
{
  CAST(session_open_command, self, s);
  struct ssh_channel *res;
480
481
482

  self->session->write = connection->write;
  
483
484
485
  *request = format_channel_open(ATOM_SESSION,
				 local_channel_number,
				 self->session, "");
486
  
487
  res = self->session;
488
489
490
491
492
493
494

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

  return res;
}

495
struct command *make_open_session_command(struct ssh_channel *session)
496
497
498
499
500
{
  NEW(session_open_command, self);
  self->super.super.call = do_channel_open_command;
  self->super.new_channel = new_session;
  self->session = session;
501
502

  return &self->super.super;
503
504
}

505
506
507
508
509
510
511
512

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, "");
}
513

514
515
struct channel_request_command request_shell =
{ { STATIC_HEADER, do_channel_request_command }, do_format_shell_request };
516

517
518
519
520
521
522
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

/* 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;
}