connection.c 12 KB
Newer Older
1
2
3
4
/* connection.c
 *
 */

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 1998 Niels Möller
 *
 * 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
Niels Möller's avatar
Niels Möller committed
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23
 */

24
#include "connection.h"
25

26
#include "compress.h"
27
#include "debug.h"
28
#include "disconnect.h"
29
#include "encrypt.h"
Niels Möller's avatar
Niels Möller committed
30
#include "exception.h"
31
#include "format.h"
Niels Möller's avatar
Niels Möller committed
32
#include "io.h"
33
#include "keyexchange.h"
34
#include "pad.h"
35
36
37
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"
38

39
40
#include <assert.h>

41
#define GABA_DEFINE
42
#include "connection.h.x"
43
#undef GABA_DEFINE
44

Niels Möller's avatar
Niels Möller committed
45
46
#include "connection.c.x"

47
const char *packet_types[0x100] =
48
49
50
#include "packet_types.h"
;

Niels Möller's avatar
Niels Möller committed
51
static void
52
53
connection_handle_packet(struct ssh_connection *closure,
			 struct lsh_string *packet)
54
{
55
  UINT8 msg;
Niels Möller's avatar
Niels Möller committed
56

57
  assert(!closure->paused);
58
  
59
60
  if (!packet->length)
    {
61
      werror("connection.c: Received empty packet!\n");
62
      PROTOCOL_ERROR(closure->e, "Received empty packet.");
63
      lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
64
      return;
65
    }
66

67
68
69
70
  if (packet->length > closure->rec_max_packet)
    {
      werror("connection.c: Packet too large!\n");
      PROTOCOL_ERROR(closure->e, "Packet too large");
71
      lsh_string_free(packet);
72
73
74
      return;
    }
    
75
  msg = packet->data[0];
76

Niels Möller's avatar
Niels Möller committed
77
  debug("handle_connection: Received packet of type %i (%z)\n",
78
	msg, packet_types[msg]);
Niels Möller's avatar
Niels Möller committed
79
  
80
  switch(closure->kex_state)
81
    {
82
83
84
    case KEX_STATE_INIT:
      if (msg == SSH_MSG_NEWKEYS)
	{
85
	  werror("Unexpected NEWKEYS message!\n");
86
	  PROTOCOL_ERROR(closure->e, "Unexpected NEWKEYS message!");
87
	  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
88
	  return;
89
90
91
	}
      break;
    case KEX_STATE_IGNORE:
92
      debug("handle_connection: Ignoring packet %i\n", msg);
93

Niels Möller's avatar
Niels Möller committed
94
      /* It's conceivable with key exchange methods for which one
95
96
97
98
       * wants to switch to the NEWKEYS state immediately. But for
       * now, we always switch to the IN_PROGRESS state, to wait for a
       * KEXDH_INIT or KEXDH_REPLY message. */
      closure->kex_state = KEX_STATE_IN_PROGRESS;
99
      lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
100
      return;
101
102
103
104
105

    case KEX_STATE_IN_PROGRESS:
      if ( (msg == SSH_MSG_NEWKEYS)
	   || (msg == SSH_MSG_KEXINIT))
	{
106
	  werror("Unexpected KEXINIT or NEWKEYS message!\n");
107
	  PROTOCOL_ERROR(closure->e, "Unexpected KEXINIT or NEWKEYS message!");
108
	  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
109
	  return;
110
111
112
	}
      break;
    case KEX_STATE_NEWKEYS:
113
114
115
116
      if (! ((msg == SSH_MSG_NEWKEYS)
	     || (msg == SSH_MSG_DISCONNECT)
	     || (msg == SSH_MSG_IGNORE)
	     || (msg == SSH_MSG_DEBUG)))
117
	{
118
	  werror("Expected NEWKEYS message, but received message %i!\n",
119
		 msg);
120
	  PROTOCOL_ERROR(closure->e, "Expected NEWKEYS message");
121
	  lsh_string_free(packet);
Niels Möller's avatar
Niels Möller committed
122
	  return;
123
124
125
126
	}
      break;
    default:
      fatal("handle_connection: Internal error.\n");
127
    }
128

Niels Möller's avatar
Niels Möller committed
129
  HANDLE_PACKET(closure->dispatch[msg], closure, packet);
130
  lsh_string_free(packet);
131
132
}

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
static void
connection_handle_pending(struct ssh_connection *self)
{
  while (!self->paused && !string_queue_is_empty(&self->pending))
    connection_handle_packet(self, string_queue_remove_head(&self->pending));
}

/* Deal with pausing of the connection. */
static void
do_handle_connection(struct abstract_write *w,
		     struct lsh_string *packet)
{
  CAST(ssh_connection, self, w);

  if (self->paused)
    string_queue_add_tail(&self->pending, packet);

  else if (string_queue_is_empty(&self->pending))
    connection_handle_packet(self, packet);

  else
    {
      string_queue_add_tail(&self->pending, packet);
      connection_handle_pending(self);
    }
}

160
DEFINE_PACKET_HANDLER(, connection_ignore_handler,
161
                      connection UNUSED, packet UNUSED)
162
163
164
{
}

165
DEFINE_PACKET_HANDLER(, connection_fail_handler, connection, packet UNUSED)
166
{
167
  PROTOCOL_ERROR(connection->e, NULL);
168
}
169

170
DEFINE_PACKET_HANDLER(, connection_unimplemented_handler, connection, packet)
171
{
Niels Möller's avatar
Niels Möller committed
172
173
174
175
176
177
178
  werror("Received packet of unimplemented type %i.\n",
	 packet->data[0]);

  C_WRITE(connection,
	  ssh_format("%c%i",
		     SSH_MSG_UNIMPLEMENTED,
		     packet->sequence_number));
179
}
180

181
182
183
184
185
DEFINE_PACKET_HANDLER(, connection_forward_handler, connection, packet)
{
  assert(connection->chain);
  C_WRITE(connection->chain, packet);
}
186

187

Niels Möller's avatar
Niels Möller committed
188
189
/* GABA:
   (class
190
     (name exc_connection_handler)
Niels Möller's avatar
Niels Möller committed
191
     (super exception_handler)
Niels Möller's avatar
Niels Möller committed
192
     (vars
193
       (backend object io_backend)
Niels Möller's avatar
Niels Möller committed
194
       (connection object ssh_connection)))
Niels Möller's avatar
Niels Möller committed
195
196
197
*/

static void
198
199
do_exc_connection_handler(struct exception_handler *s,
			  const struct exception *e)
Niels Möller's avatar
Niels Möller committed
200
{
201
  CAST(exc_connection_handler, self, s);
Niels Möller's avatar
Niels Möller committed
202

Niels Möller's avatar
Niels Möller committed
203
204
205
206
207
  switch (e->type)
    {
    case EXC_PROTOCOL:
      {
	CAST_SUBTYPE(protocol_exception, exc, e);
208
209
210

        werror("Protocol error: %z\n", e->msg);
        
Niels Möller's avatar
Niels Möller committed
211
212
213
	if (exc->reason)
	  C_WRITE(self->connection, format_disconnect(exc->reason, exc->super.msg, ""));
	
Niels Möller's avatar
Niels Möller committed
214
	EXCEPTION_RAISE(self->super.parent, &finish_read_exception);
Niels Möller's avatar
Niels Möller committed
215
216
      }
      break;
217

218
219
220
221
222
223
224
225
226
227
228
229
230
    case EXC_PAUSE_CONNECTION:
      assert(!self->connection->paused);
      self->connection->paused = 1;
      EXCEPTION_RAISE(self->super.parent,
		      make_simple_exception(EXC_PAUSE_READ, "locking connection."));
      break;

    case EXC_PAUSE_START_CONNECTION:
      /* NOTE: Raising EXC_PAUSE_START_READ will not by itself make
       * the connection start processing pending packets. Not until
       * the peer sends us more data.
       *
       * So any code that raises EXC_PAUSE_START_CONNECTION should
231
       * also call connection_handle_pending at a safe place. We
232
233
234
235
236
237
238
239
240
241
242
       * can't call it here, as we may be in the middle of the
       * handling of a packet. Installing a callout would be best. */
      
      assert(self->connection->paused);
      EXCEPTION_RAISE(self->super.parent,
		      make_simple_exception(EXC_PAUSE_START_READ, "unlocking connection."));
      
      self->connection->paused = 0;

      break;
      
Niels Möller's avatar
Niels Möller committed
243
244
245
246
247
    default:
      EXCEPTION_RAISE(self->super.parent, e);
    }
}

248
249
static struct exception_handler *
make_exc_connection_handler(struct ssh_connection *connection,
250
251
			  struct exception_handler *parent,
			  const char *context)
Niels Möller's avatar
Niels Möller committed
252
{
253
  NEW(exc_connection_handler, self);
254

Niels Möller's avatar
Niels Möller committed
255
  self->super.parent = parent;
256
  self->super.raise = do_exc_connection_handler;
257
258
259
  self->super.context = context;
  
  self->connection = connection;
Niels Möller's avatar
Niels Möller committed
260
261
262
263

  return &self->super;
}

264
struct ssh_connection *
265
266
make_ssh_connection(UINT32 flags,
		    struct address_info *peer,
267
		    const char *debug_comment,
268
		    struct command_continuation *c,
269
		    struct exception_handler *e)
270
271
272
{
  int i;

273
  NEW(ssh_connection, connection);
Niels Möller's avatar
Niels Möller committed
274

275
  connection->flags = flags;
276
  connection->peer = peer;
Niels Möller's avatar
Niels Möller committed
277
  
278
  connection->debug_comment = debug_comment;
279
  connection->super.write = do_handle_connection;
280

281
  /* Exception handler that sends a proper disconnect message on protocol errors */
282
  connection->e = make_exc_connection_handler(connection, e, HANDLER_CONTEXT);
283

284
285
  connection->established = c;
  
286
  /* Initialize instance variables */
Niels Möller's avatar
Niels Möller committed
287

288
289
  connection->versions[CONNECTION_SERVER]
    = connection->versions[CONNECTION_CLIENT]
290
291
    = connection->session_id = NULL;

292
  connection->peer_flags = 0;
293
  connection->user = NULL;
294
  
295
296
  connection->resources = empty_resource_list();
  
297
  connection->rec_max_packet = SSH_MAX_PACKET;
Niels Möller's avatar
Niels Möller committed
298

299
300
301
302
303
  /* Initial encryption state */
  connection->send_crypto = connection->rec_crypto = NULL;
  connection->send_mac = connection->rec_mac = NULL;
  connection->send_compress = connection->rec_compress = NULL;
  
304
305
  connection->paused = 0;
  string_queue_init(&connection->pending);
306
  
307
308
  connection->kex_state = KEX_STATE_INIT;

Gordon Matzigkeit's avatar
Gordon Matzigkeit committed
309
310
  connection->kexinits[CONNECTION_CLIENT]
    = connection->kexinits[CONNECTION_SERVER] = NULL;
311

Gordon Matzigkeit's avatar
Gordon Matzigkeit committed
312
313
  connection->literal_kexinits[CONNECTION_CLIENT]
    = connection->literal_kexinits[CONNECTION_SERVER] = NULL;
314
315
  
  for (i = 0; i < 0x100; i++)
316
    connection->dispatch[i] = &connection_unimplemented_handler;
317

318
  connection->dispatch[0] = &connection_fail_handler;
319
  connection->dispatch[SSH_MSG_DISCONNECT] = &disconnect_handler;
320
321
322
323
324
325
326
  connection->dispatch[SSH_MSG_IGNORE] = &connection_ignore_handler;

  /* So far, all messages we send have to be supported. */ 
  connection->dispatch[SSH_MSG_UNIMPLEMENTED] = &connection_fail_handler;

  connection->dispatch[SSH_MSG_DEBUG] = &connection_debug_handler;

Niels Möller's avatar
Niels Möller committed
327
  /* Make all other known message types terminate the connection */
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353

  connection->dispatch[SSH_MSG_SERVICE_REQUEST] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_SERVICE_ACCEPT] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_NEWKEYS] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_KEXDH_INIT] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_KEXDH_REPLY] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_USERAUTH_REQUEST] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_USERAUTH_FAILURE] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_USERAUTH_SUCCESS] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_USERAUTH_BANNER] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_USERAUTH_PK_OK] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_USERAUTH_PASSWD_CHANGEREQ] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_GLOBAL_REQUEST] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_REQUEST_SUCCESS] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_REQUEST_FAILURE] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_OPEN] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_OPEN_CONFIRMATION] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_OPEN_FAILURE] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_WINDOW_ADJUST] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_DATA] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_EXTENDED_DATA] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_EOF] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_CLOSE] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_REQUEST] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_SUCCESS] = &connection_fail_handler;
  connection->dispatch[SSH_MSG_CHANNEL_FAILURE] = &connection_fail_handler;
354
355
356
  
  return connection;
}
357

Niels Möller's avatar
Niels Möller committed
358
359
360
361
void
connection_init_io(struct ssh_connection *connection,
		   struct abstract_write *raw,
		   struct randomness *r)
362
363
364
{
  /* Initialize i/o hooks */
  connection->raw = raw;
365
366
367
368
369
370
371
372
  connection->write =
    make_packet_debug(
      make_packet_deflate(
	make_packet_pad(
	  make_packet_encrypt(raw, connection), 
	  connection,
	  r),
	connection),
373
374
375
      (connection->debug_comment
       ? ssh_format("%lz sent", connection->debug_comment)
       : ssh_format("Sent")));
376
}
Niels Möller's avatar
Niels Möller committed
377

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

/* GABA:
   (class
     (name connection_close_handler)
     (super lsh_callback)
     (vars
       (connection object ssh_connection)))
*/

static void
connection_die(struct lsh_callback *c)
{
  CAST(connection_close_handler, closure, c);
  
  verbose("Connection died.\n");
  
  KILL_RESOURCE_LIST(closure->connection->resources);
}

struct lsh_callback *
make_connection_close_handler(struct ssh_connection *c)
{
  NEW(connection_close_handler, closure);

  closure->connection = c;  
  closure->super.f = connection_die;

  return &closure->super;
}


409
/* Serialization. */
410
411
412

void connection_lock(struct ssh_connection *self)
{
413
414
415
416
  const struct exception pause
    = STATIC_EXCEPTION(EXC_PAUSE_CONNECTION, "locking connection.");
    
  EXCEPTION_RAISE(self->e, &pause);
417
418
419
420
}

void connection_unlock(struct ssh_connection *self)
{
421
422
423
424
  const struct exception unpause
    = STATIC_EXCEPTION(EXC_PAUSE_START_CONNECTION, "unlocking connection.");
    
  EXCEPTION_RAISE(self->e, &unpause);
425
}