client.c 12.4 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
188
189
190
191
192
193
194
195
  
       ; 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 *")))
*/

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
203
204
205
206
  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
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
230
231
232
static void
do_exit_status(struct channel_request *c,
	       struct ssh_channel *channel,
	       struct ssh_connection *connection UNUSED,
	       int want_reply,
	       struct simple_buffer *args)
Niels Möller's avatar
Niels Möller committed
233
{
234
  CAST(exit_handler, closure, c);
235
  UINT32 status;
Niels Möller's avatar
Niels Möller committed
236
237
238
239
240
241
242

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

243
244
      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
245

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

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

Niels Möller's avatar
Niels Möller committed
257
258
259
260
261
262
static void
do_exit_signal(struct channel_request *c,
	       struct ssh_channel *channel,
	       struct ssh_connection *connection UNUSED,
	       int want_reply,
	       struct simple_buffer *args)
Niels Möller's avatar
Niels Möller committed
263
{
264
265
  CAST(exit_handler, closure, c);

266
  UINT32 signal;
Niels Möller's avatar
Niels Möller committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  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);

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

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

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

301
      channel_eof(channel);
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(channel->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
352
353
static void
do_send(struct ssh_channel *s,
	struct ssh_connection *c UNUSED)
Niels Möller's avatar
Niels Möller committed
354
{
355
356
357
358
359
360
361
  CAST(client_session, self, s);

  assert(self->in->super.read);

  self->in->super.want_read = 1;
}

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

  session->err->super.e
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stderr",
391
392
					session->err->super.e,
					HANDLER_CONTEXT);
393
394
395
396
397

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

  session->in->super.read = make_channel_read_data(channel);
Balázs Scheidler's avatar
Balázs Scheidler committed
398
399
400
401
402

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

407
408
409
410
411
  /* Make sure stdio is closed properly if the channel or connection dies */
  REMEMBER_RESOURCE(channel->resources, &session->in->super.super);
  REMEMBER_RESOURCE(channel->resources, &session->out->super.super);
  REMEMBER_RESOURCE(channel->resources, &session->err->super.super);
  
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
struct ssh_channel *make_client_session(struct io_fd *in,
					struct io_fd *out,
					struct io_fd *err,
					UINT32 max_window,
					int *exit_status)
Niels Möller's avatar
Niels Möller committed
431
{
432
  NEW(client_session, self);
Niels Möller's avatar
Niels Möller committed
433

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

436
437
438
439
  /* Makes sure the pending_close bit is set whenever this session
   * dies, no matter when or how. */
  self->super.close = do_client_session_close;
  
440
  self->super.max_window = max_window;
Niels Möller's avatar
Niels Möller committed
441
442
443
444
  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
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
475
476
477
478
*/

static struct ssh_channel *
new_session(struct channel_open_command *s,
	    struct ssh_connection *connection,
	    struct lsh_string **request)
{
  CAST(session_open_command, self, s);
  struct ssh_channel *res;
479
480
481

  self->session->write = connection->write;
  
482
  *request = prepare_channel_open(connection, ATOM_SESSION,
483
484
485
486
				  self->session, "");
  if (!*request)
    return NULL;
  
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