client.c 11.2 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* client.c
 *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 *
 * $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
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Niels Möller's avatar
Niels Möller committed
24
25
 */

26
/* FIXME: Why include stdio? */
Niels Möller's avatar
Niels Möller committed
27
/* #include <stdio.h> */
Niels Möller's avatar
Niels Möller committed
28

Niels Möller's avatar
Niels Möller committed
29
#include "client.h"
30

Niels Möller's avatar
Niels Möller committed
31
#include "abstract_io.h"
32
#include "channel.h"
33
34
#include "connection.h"
#include "crypto.h"
Niels Möller's avatar
Niels Möller committed
35
#include "debug.h"
Niels Möller's avatar
Niels Möller committed
36
#include "encrypt.h"
37
#include "format.h"
Niels Möller's avatar
Niels Möller committed
38
#include "pad.h"
39
#include "parse.h"
40
41
#include "read_line.h"
#include "read_packet.h"
42
#include "service.h"
43
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
44
#include "unpad.h"
45
46
47
#include "version.h"
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
48

49
/* Handle connection and initial handshaking. */
50
51
52
53
54
55
56
57
58
59
60
61
struct client_callback
{
  struct fd_callback super;
  struct io_backend *backend;
  UINT32 block_size;
  char *id_comment;

  struct randomness *random;
  struct make_kexinit *init;
  struct packet_handler *kexinit_handler;
};

62
static int client_initiate(struct fd_callback **c,
Niels Möller's avatar
Niels Möller committed
63
			   int fd)
Niels Möller's avatar
Niels Möller committed
64
{
65
  struct client_callback *closure
66
    = (struct client_callback *) *c;
Niels Möller's avatar
Niels Möller committed
67

Niels Möller's avatar
Niels Möller committed
68
69
  int res;
  
Niels Möller's avatar
Niels Möller committed
70
  /* FIXME: Should pass a key exchange handler, not NULL! */
71
  struct ssh_connection *connection
Niels Möller's avatar
Niels Möller committed
72
    = make_ssh_connection(closure->kexinit_handler);
73
74
75

  connection_init_io(connection,
		     io_read_write(closure->backend, fd,
Niels Möller's avatar
Niels Möller committed
76
				   make_client_read_line(connection),
77
78
79
				   closure->block_size,
				   make_client_close_handler()),
		     closure->random);
Niels Möller's avatar
Niels Möller committed
80
  
Niels Möller's avatar
Niels Möller committed
81
  connection->client_version
82
    = ssh_format("SSH-%lz-%lz %lz",
Niels Möller's avatar
Niels Möller committed
83
84
85
		 PROTOCOL_VERSION,
		 SOFTWARE_CLIENT_VERSION,
		 closure->id_comment);
Niels Möller's avatar
Niels Möller committed
86
  
Niels Möller's avatar
Niels Möller committed
87
88
  res = A_WRITE(connection->raw,
		ssh_format("%lS\r\n", connection->client_version));
89
  if (LSH_CLOSEDP(res))
Niels Möller's avatar
Niels Möller committed
90
91
    return res;

92
93
94
  return res | initiate_keyexchange(connection, CONNECTION_CLIENT,
				    MAKE_KEXINIT(closure->init),
				    NULL);
Niels Möller's avatar
Niels Möller committed
95
96
}

Niels Möller's avatar
Niels Möller committed
97
struct client_line_handler
Niels Möller's avatar
Niels Möller committed
98
{
Niels Möller's avatar
Niels Möller committed
99
  struct line_handler super;
Niels Möller's avatar
Niels Möller committed
100
  struct ssh_connection *connection;
Niels Möller's avatar
Niels Möller committed
101
};
Niels Möller's avatar
Niels Möller committed
102

103
static struct read_handler *do_line(struct line_handler **h,
Niels Möller's avatar
Niels Möller committed
104
105
106
				    UINT32 length,
				    UINT8 *line)
{
107
108
  struct client_line_handler *closure
    = (struct client_line_handler *) *h;
109
110

  MDEBUG(closure);
111
  
Niels Möller's avatar
Niels Möller committed
112
  if ( (length >= 4) && !memcmp(line, "SSH-", 4))
Niels Möller's avatar
Niels Möller committed
113
114
    {
      /* Parse and remember format string */
Niels Möller's avatar
Niels Möller committed
115
116
      if ( ((length >= 8) && !memcmp(line + 4, "2.0-", 4))
	   || ((length >= 9) && !memcmp(line + 4, "1.99-", 5)))
Niels Möller's avatar
Niels Möller committed
117
	{
118
	  struct read_handler *new = make_read_packet
Niels Möller's avatar
Niels Möller committed
119
120
121
	    (make_packet_unpad
	     (make_packet_debug
	      (&closure->connection->super,
122
	       "")),
123
	     closure->connection);
Niels Möller's avatar
Niels Möller committed
124
	     
Niels Möller's avatar
Niels Möller committed
125
	  closure->connection->server_version
126
	    = ssh_format("%ls", length, line);
Niels Möller's avatar
Niels Möller committed
127

128
129
130
131
132
133
134
135
	  verbose("Client version: ");
	  verbose_safe(closure->connection->client_version->length,
		       closure->connection->client_version->data);
	  verbose("\nServer version: ");
	  verbose_safe(closure->connection->server_version->length,
		       closure->connection->server_version->data);
	  verbose("\n");
	  
Niels Möller's avatar
Niels Möller committed
136
	  /* FIXME: Cleanup properly. */
137
	  lsh_free(closure);
Niels Möller's avatar
Niels Möller committed
138
139

	  return new;
Niels Möller's avatar
Niels Möller committed
140
141
142
143
	}
      else
	{
	  werror("Unsupported protocol version: ");
Niels Möller's avatar
Niels Möller committed
144
	  werror_safe(length, line);
Niels Möller's avatar
Niels Möller committed
145
	  werror("\n");
Niels Möller's avatar
Niels Möller committed
146

147
	  /* FIXME: Clean up properly */
148
	  lsh_free(closure);
Niels Möller's avatar
Niels Möller committed
149
	  *h = NULL;
150
		  
Niels Möller's avatar
Niels Möller committed
151
152
153
154
155
156
	  return 0;
	}
    }
  else
    {
      /* Display line */
Niels Möller's avatar
Niels Möller committed
157
      werror_safe(length, line);
Niels Möller's avatar
Niels Möller committed
158
159

      /* Read next line */
Niels Möller's avatar
Niels Möller committed
160
      return 0;
Niels Möller's avatar
Niels Möller committed
161
162
163
    }
}

Niels Möller's avatar
Niels Möller committed
164
struct read_handler *make_client_read_line(struct ssh_connection *c)
Niels Möller's avatar
Niels Möller committed
165
{
Niels Möller's avatar
Niels Möller committed
166
  struct client_line_handler *closure;
167

Niels Möller's avatar
Niels Möller committed
168
  NEW(closure);
169

170
  closure->super.handler = do_line;
Niels Möller's avatar
Niels Möller committed
171
  closure->connection = c;
Niels Möller's avatar
Niels Möller committed
172
  
173
  return make_read_line(&closure->super);
Niels Möller's avatar
Niels Möller committed
174
175
}
  
Niels Möller's avatar
Niels Möller committed
176
177
178
179
180
181
182
183
struct fd_callback *
make_client_callback(struct io_backend *b,
		     char *comment,
		     UINT32 block_size,
		     struct randomness *random,
		     struct make_kexinit *init,
		     struct packet_handler *kexinit_handler)
  
Niels Möller's avatar
Niels Möller committed
184
{
Niels Möller's avatar
Niels Möller committed
185
186
187
  struct client_callback *connected;

  NEW(connected);
Niels Möller's avatar
Niels Möller committed
188

189
  connected->super.f = client_initiate;
Niels Möller's avatar
Niels Möller committed
190
191
  connected->backend = b;
  connected->block_size = block_size;
Niels Möller's avatar
Niels Möller committed
192
  connected->id_comment = comment;
Niels Möller's avatar
Niels Möller committed
193
194
195
196
197

  connected->random = random;
  connected->init = init;
  connected->kexinit_handler = kexinit_handler;

198
  return &connected->super;
Niels Möller's avatar
Niels Möller committed
199
}
Niels Möller's avatar
Niels Möller committed
200

201
static int client_close_die(struct close_callback *closure, int reason)
Niels Möller's avatar
Niels Möller committed
202
{
203
204
205
  verbose("Connection died, for reason %d.\n", reason);
  if (reason != CLOSE_EOF)
    werror("Connection died.\n");
Niels Möller's avatar
Niels Möller committed
206
207
208
  exit(1);
}

209
struct close_callback *make_client_close_handler(void)
Niels Möller's avatar
Niels Möller committed
210
{
Niels Möller's avatar
Niels Möller committed
211
212
213
  struct close_callback *c;

  NEW(c);
Niels Möller's avatar
Niels Möller committed
214

215
  c->f = client_close_die;
Niels Möller's avatar
Niels Möller committed
216
217
218

  return c;
}
219

Niels Möller's avatar
Niels Möller committed
220
/* Start a service that the servar has accepted (for instance ssh-userauth). */
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
struct accept_service_handler
{
  struct packet_handler super;

  int service_name;
  struct ssh_service *service;
};

static int do_accept_service(struct packet_handler *c,
			     struct ssh_connection *connection,
			     struct lsh_string *packet)
{
  struct accept_service_handler *closure = (struct accept_service_handler *) c;

  struct simple_buffer buffer;
  int msg_number;
  int name;

  MDEBUG(closure);

  if (parse_uint8(&buffer, &msg_number)
      && (msg_number == SSH_MSG_SERVICE_ACCEPT)
      && parse_atom(&buffer, &name)
      && parse_eod(&buffer)
      && (name == closure->service_name))
    {
      lsh_string_free(packet);
      connection->dispatch[SSH_MSG_SERVICE_ACCEPT] = connection->fail;
      
      return SERVICE_INIT(closure->service, connection);
    }

  lsh_string_free(packet);
  return LSH_FAIL | LSH_DIE;
}

Niels Möller's avatar
Niels Möller committed
257
258
struct packet_handler *make_accept_service_handler(int service_name,
						   struct ssh_service *service)
259
{
Niels Möller's avatar
Niels Möller committed
260
  struct accept_service_handler *closure;
261
262

  NEW(closure);
Niels Möller's avatar
Niels Möller committed
263
  closure->super.handler = do_accept_service;
264
265
266
267
268
269
  closure->service_name = service_name;
  closure->service = service;

  return &closure->super;
}

Niels Möller's avatar
Niels Möller committed
270
271
272
273
274
275
276
277
struct service_request
{
  struct ssh_service super;

  int service_name;
  struct ssh_service *service;
};

278
279
280
281
282
283
284
static int do_request_service(struct ssh_service *c,
			      struct ssh_connection *connection)
{
  struct service_request *closure = (struct service_request *) c;
  
  MDEBUG(c);

Niels Möller's avatar
Niels Möller committed
285
286
287
288
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT]
    = make_accept_service_handler(closure->service_name,
				  closure->service);
  
289
  return A_WRITE(connection->write, format_service_request(closure->service_name));
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
}

struct ssh_service *request_service(int service_name,
				    struct ssh_service *service)
{
  struct service_request *closure;

  NEW(closure);
  closure->super.init = do_request_service;
  closure->service_name = service_name;
  closure->service = service;

  return &closure->super;
}

Niels Möller's avatar
Niels Möller committed
305
/* Initiate and manage a session */
306
307
struct session
{
308
  struct ssh_channel super;
309
310

  UINT32 max_window;
Niels Möller's avatar
Niels Möller committed
311
312
313
314

  /* Exec or shell request. */
  int final_request;
  struct lsh_string *args;
315
316
  
  /* To access stdio */
317
318
319
  struct io_fd *in;
  struct abstract_write *out;
  struct abstract_write *err;
320
321
};

Niels Möller's avatar
Niels Möller committed
322
static int client_session_die(struct ssh_channel *c)
323
324
325
326
327
{
  struct session *closure = (struct session *) c;
  
  MDEBUG(closure);

Niels Möller's avatar
Niels Möller committed
328
329
330
  /* FIXME: Don't die this hard. */
  if ( (closure->super.flags & (CHANNEL_SENT_CLOSE | CHANNEL_RECIEVED_CLOSE))
       ==  (CHANNEL_SENT_CLOSE | CHANNEL_RECIEVED_CLOSE))
331
332
333
334
335
    exit(EXIT_SUCCESS);

  exit(EXIT_FAILURE);
}

Niels Möller's avatar
Niels Möller committed
336
/* Recieve channel data */
Niels Möller's avatar
Niels Möller committed
337
static int do_recieve(struct ssh_channel *c,
338
339
340
341
342
343
344
		      int type, struct lsh_string *data)
{
  struct session *closure = (struct session *) c;
  int res = 0;
  
  MDEBUG(closure);

Niels Möller's avatar
Niels Möller committed
345
  if (closure->super.rec_window_size < closure->max_window / 2)
346
    {
Niels Möller's avatar
Niels Möller committed
347
      res = A_WRITE(closure->super.write, prepare_window_adjust
348
349
		    (&closure->super,
		     closure->max_window - closure->super.rec_window_size));
350
351
352
353
354
355
356
      if (LSH_CLOSEDP(res))
	return res;
    }
  
  switch(type)
    {
    case CHANNEL_DATA:
357
      return A_WRITE(closure->out, data);
358
    case CHANNEL_STDERR_DATA:
359
      return A_WRITE(closure->err, data);
360
361
362
363
    default:
      fatal("Internal error!\n");
    }
}
Niels Möller's avatar
Niels Möller committed
364

Niels Möller's avatar
Niels Möller committed
365
366
367
368
369
370
371
372
373
374
375
376
/* We may send more data */
static int do_send(struct ssh_channel *c)
{
  struct session *closure = (struct session *) c;

  MDEBUG(closure);

  closure->in->on_hold = 0;

  return LSH_OK | LSH_GOON;
}

377
/* We have a remote shell */
Niels Möller's avatar
Niels Möller committed
378
static int do_io(struct ssh_channel *c)
379
380
381
382
{
  struct session *closure = (struct session *) c;

  MDEBUG(closure);
Niels Möller's avatar
Niels Möller committed
383
  
384
  closure->super.recieve = do_recieve;
Niels Möller's avatar
Niels Möller committed
385
386
387
388
  closure->in->handler = make_channel_read_data(&closure->super);
  closure->super.send = do_send;
  
  return LSH_OK | LSH_GOON;
389
390
391
}

/* We have opened a channel of type "session" */
Niels Möller's avatar
Niels Möller committed
392
static int do_open_confirm(struct ssh_channel *c)
393
394
{
  struct session *closure = (struct session *) c;
Niels Möller's avatar
Niels Möller committed
395
396
  struct lsh_string *args;
  
397
398
399
400
401
  MDEBUG(closure);

  closure->super.open_confirm = NULL;
  closure->super.open_failure = NULL;

Niels Möller's avatar
Niels Möller committed
402
  closure->super.channel_success = do_io;
403
404
  closure->super.channel_failure = client_session_die;

Niels Möller's avatar
Niels Möller committed
405
406
407
408
409
410
  args = closure->args;
  closure->args = NULL; /* for gc */

  return A_WRITE(closure->super.write,
		 format_channel_request(closure->final_request, c, 1,
					"%lfS", args));
411
412
}

Niels Möller's avatar
Niels Möller committed
413
414
415
416
417
418
static struct ssh_channel *make_session(struct io_fd *in,
					struct abstract_write *out,
					struct abstract_write *err,
					UINT32 max_window,
					int final_request,
					struct lsh_string *args)
Niels Möller's avatar
Niels Möller committed
419
420
421
422
423
{
  struct session *self;

  NEW(self);

Niels Möller's avatar
Niels Möller committed
424
425
426
427
428
429
430
  init_channel(&self->super);

  self->max_window = max_window;
  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
431
  
Niels Möller's avatar
Niels Möller committed
432
  /* self->expect_close = 0; */
Niels Möller's avatar
Niels Möller committed
433
434
435
436
  self->in = in;
  self->out = out;
  self->err = err;

Niels Möller's avatar
Niels Möller committed
437
438
439
  self->final_request = final_request;
  self->args = args;
  
Niels Möller's avatar
Niels Möller committed
440
441
442
  return &self->super;
}

443
444
445
446
struct client_startup
{
  struct connection_startup super;

Niels Möller's avatar
Niels Möller committed
447
448
449
450
451
452
453
454
455
456
457
  struct ssh_channel *session;
#if 0
  /* Exec or shell request. */
  int final_request;
  struct lsh_string *args;
  
  /* To access stdio */
  struct io_fd *in;
  struct abstract_write *out;
  struct abstract_write *err;
#endif
458
459
};

460
static int do_client_startup(struct connection_startup *c,
Niels Möller's avatar
Niels Möller committed
461
462
			     struct channel_table *table,
			     struct abstract_write *write)
463
464
{
  struct client_startup *closure = (struct client_startup *) c;
Niels Möller's avatar
Niels Möller committed
465
  
466
  MDEBUG(closure);
467

Niels Möller's avatar
Niels Möller committed
468
  closure->session->write = write;
469
  
Niels Möller's avatar
Niels Möller committed
470
471
472
473
474
  closure->session->open_confirm = do_open_confirm;
  closure->session->open_failure = client_session_die;

  return A_WRITE(write, prepare_channel_open(table, ATOM_SESSION,
					     closure->session, ""));
475
476
}

Niels Möller's avatar
Niels Möller committed
477
478
#define WINDOW_SIZE (SSH_MAX_PACKET << 3)

479
/* Request opening a session. */
Niels Möller's avatar
Niels Möller committed
480
481
482
483
484
struct connection_startup *make_client_startup(struct io_fd *in,
					       struct abstract_write *out,
					       struct abstract_write *err,
					       int final_request,
					       struct lsh_string *args)
485
{
486
  struct client_startup *closure;
Niels Möller's avatar
Niels Möller committed
487
  
488
489
  NEW(closure);
  closure->super.start = do_client_startup;
Niels Möller's avatar
Niels Möller committed
490
491
492
  closure->session = make_session(in, out, err,
				  WINDOW_SIZE,
				  final_request, args);
493

494
495
496
  return &closure->super;
}