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