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 int)
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
77
78
79

  struct simple_buffer buffer;
  int msg_number;
  int name;

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
107
struct packet_handler *
make_accept_service_handler(int 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);
Niels Möller's avatar
Niels Möller committed
235
236
237
238
239
240
241
242
  int status;

  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);

Niels Möller's avatar
Niels Möller committed
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  int signal;
  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
#if 0
363
364
365
366
367
368
/* Used for the first callback, to register stdin as a resource. */
static void
do_send_first(struct ssh_channel *s,
	      struct ssh_connection *connection)
{
  CAST(client_session, self, s);
Niels Möller's avatar
Niels Möller committed
369

370
  REMEMBER_RESOURCE(channel->resources, &self->in->super.super);
371
  s->send = do_send;
Niels Möller's avatar
Niels Möller committed
372

373
  do_send(s, connection);
Niels Möller's avatar
Niels Möller committed
374
}
375
#endif
Niels Möller's avatar
Niels Möller committed
376

377
/* We have a remote shell */
Niels Möller's avatar
Niels Möller committed
378
379
380
381
382
static void
do_client_io(struct command *s UNUSED,
	     struct lsh_object *x,
	     struct command_continuation *c,
	     struct exception_handler *e UNUSED)
383

384
{
385
386
  CAST(client_session, session, x);
  struct ssh_channel *channel = &session->super;
387
  assert(x);
388

389
  /* Set up write fd:s. */
Niels Möller's avatar
Niels Möller committed
390
  
391
  channel->receive = do_receive;
392

393
394
395
396
397
398
399
  /* 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",
400
401
					session->out->super.e,
					HANDLER_CONTEXT);
402
403
404
405

  session->err->super.e
    = make_channel_io_exception_handler(channel,
					"lsh: I/O error on stderr",
406
407
					session->err->super.e,
					HANDLER_CONTEXT);
408
409
410
411
412
413

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

  session->in->super.read = make_channel_read_data(channel);
  
Niels Möller's avatar
Niels Möller committed
414
415
  session->in->super.close_callback
    = make_channel_read_close_callback(channel);
Niels Möller's avatar
Niels Möller committed
416

417
418
419
420
421
  /* 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);
  
422
423
424
425
  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));
426

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

432
433
434
struct command client_io =
{ STATIC_HEADER, do_client_io };

435

436
437
438
439
440
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
441
{
442
  NEW(client_session, self);
Niels Möller's avatar
Niels Möller committed
443

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

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

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

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

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

472
473
474
475
476
477
478
/* 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.
479
       (session object ssh_channel)))
480
481
482
483
484
485
486
487
488
*/

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;
489
490
491

  self->session->write = connection->write;
  
492
  *request = prepare_channel_open(connection, ATOM_SESSION,
493
494
495
496
				  self->session, "");
  if (!*request)
    return NULL;
  
497
  res = self->session;
498
499
500
501
502
503
504

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

  return res;
}

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

  return &self->super.super;
513
514
}

515
516
517
518
519
520
521
522

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

524
525
struct channel_request_command request_shell =
{ { STATIC_HEADER, do_channel_request_command }, do_format_shell_request };
526