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
#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
359
360
static void
do_send(struct ssh_channel *s,
	struct ssh_connection *c 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
402
403

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

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

  /* FIXME: Perhaps there is some way to arrange that channel.c calls
   * the CHANNEL_SEND method instead? */
  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