client.c 12.6 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
46
#include "version.h"
#include "werror.h"
#include "xalloc.h"
47
#include "compress.h"
Niels Möller's avatar
Niels Möller committed
48

49
50
#include <signal.h>

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

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

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

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

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

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

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

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

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

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

  return &closure->super;
}

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

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

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

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

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

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

218

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

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

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

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

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

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

Niels Möller's avatar
Niels Möller committed
261
262
263
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
264
265
266
267
268
269
	       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
270
{
271
272
  CAST(exit_handler, closure, c);

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

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

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

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

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

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

  self->super.handler = do_exit_status;

  self->exit_status = exit_status;

  return &self->super;
}

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

  self->super.handler = do_exit_signal;

  self->exit_status = exit_status;

  return &self->super;
}

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

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

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

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

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

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

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

386
387
388
389
  /* 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. */
390
  session->out->e
391
392
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stdout",
393
					session->out->e,
394
					HANDLER_CONTEXT);
395

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

  /* Set up the fd we read from. */
  channel->send = do_send;

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

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

415
  /* Make sure stdio is closed properly if the channel or connection dies */
416
417
418
  REMEMBER_RESOURCE(channel->resources, &session->in->super);
  REMEMBER_RESOURCE(channel->resources, &session->out->super);
  REMEMBER_RESOURCE(channel->resources, &session->err->super);
419
  
420
421
422
423
  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));
424

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

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

433

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

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

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

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

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

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

470
471
472
473
474
475
476
/* 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.
477
       (session object ssh_channel)))
478
479
480
481
482
*/

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

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

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

  return res;
}

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

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

513
514
515
516
517
518
519
520

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

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