read_packet.c 7.05 KB
Newer Older
1
2
/* read_packet.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.
24
25
 */

26
#include <assert.h>
Niels Möller's avatar
Niels Möller committed
27
28
29
#include <errno.h>
#include <string.h>

30
#include "read_packet.h"
31
32

#include "crypto.h"
Niels Möller's avatar
Niels Möller committed
33
34
#include "format.h"
#include "io.h"
35
36
#include "werror.h"
#include "xalloc.h"
37

38
39
40
41
#define WAIT_START 0
#define WAIT_HEADER 1
#define WAIT_CONTENTS 2
#define WAIT_MAC 3
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
struct read_packet
{
  struct read_handler super; /* Super type */

  int state;
  
  UINT32 sequence_number; /* Attached to read packets */
  
  /* Buffer partial headers and packets. */
  UINT32 pos;

  /* FIXME: This buffer should hold one block, and must be reallocated
   * when the crypto algorithms is changed. */
  struct lsh_string *buffer;
57
58
59
60
  UINT8 *crypt_pos;

  /* Must point to an area large enough to hold a mac */
  struct lsh_string *computed_mac; 
61
62
63
64
65

  struct abstract_write *handler;
  struct ssh_connection *connection;
};

66
67
68
69
70
71
72
73
74
75
76
77
78
79
static struct lsh_string *
lsh_string_realloc(struct lsh_string *s, UINT32 length)
{
  if (!s)
    return lsh_string_alloc(length);

  if (s->length < length)
    {
      lsh_string_free(s);
      return lsh_string_alloc(length);
    }
  else
    return s;
}
Niels Möller's avatar
Niels Möller committed
80

Niels Möller's avatar
Niels Möller committed
81
82
static int do_read_packet(struct read_handler **h,
			  struct abstract_read *read)
83
{
84
  struct read_packet *closure = (struct read_packet *) *h;
Niels Möller's avatar
Niels Möller committed
85

Niels Möller's avatar
Niels Möller committed
86
87
  MDEBUG(closure);
  
Niels Möller's avatar
Niels Möller committed
88
  switch(closure->state)
89
    {
Niels Möller's avatar
Niels Möller committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    case WAIT_START:
      {
	UINT32 block_size = closure->connection->rec_crypto
	  ? closure->connection->rec_crypto->block_size : 8;

	closure->buffer = lsh_string_realloc(closure->buffer,
					     block_size);
	closure->pos = 0;

	closure->state = WAIT_HEADER;
	/* FALL THROUGH */
      }
    case WAIT_HEADER:
      {
	UINT32 block_size = closure->connection->rec_crypto
	  ? closure->connection->rec_crypto->block_size : 8;
	UINT32 left;
	int n;

	left = block_size - closure->pos;
	    
	n = A_READ(read, left, closure->buffer->data + closure->pos);
	switch(n)
113
	  {
Niels Möller's avatar
Niels Möller committed
114
115
116
117
118
119
120
	  case 0:
	    return LSH_OK | LSH_GOON;
	  case A_FAIL:
	    return LSH_FAIL | LSH_DIE;
	  case A_EOF:
	    /* FIXME: Free associated resources! */
	    return LSH_OK | LSH_CLOSE;
121
	  }
Niels Möller's avatar
Niels Möller committed
122
123
124
125
	closure->pos += n;

	/* Read a complete block? */
	if (n == left)
126
	  {
Niels Möller's avatar
Niels Möller committed
127
	    UINT32 length;
128

Niels Möller's avatar
Niels Möller committed
129
130
131
132
133
134
135
136
	    if (closure->connection->rec_crypto)
	      CRYPT(closure->connection->rec_crypto,
		    block_size,
		    closure->buffer->data,
		    closure->buffer->data);
		
	    length = READ_UINT32(closure->buffer->data);
	    if (length > closure->connection->rec_max_packet)
137
	      {
Niels Möller's avatar
Niels Möller committed
138
139
140
141
		werror("read_packet: Recieving too large packet.\n"
		       "  %d octets, limit is %d\n",
		       length, closure->connection->rec_max_packet);
		return LSH_FAIL | LSH_DIE;
142
143
	      }

Niels Möller's avatar
Niels Möller committed
144
145
146
	    if ( (length < 12)
		 || (length < (block_size - 4))
		 || ( (length + 4) % block_size))
147
	      {
Niels Möller's avatar
Niels Möller committed
148
149
150
151
		werror("read_packet: Bad packet length %d\n",
		       length);
		return LSH_FAIL | LSH_DIE;
	      }
152

Niels Möller's avatar
Niels Möller committed
153
154
155
156
157
	    /* Process this block before the length field is lost. */
	    if (closure->connection->rec_mac)
	      {
		UINT8 s[4];
		WRITE_UINT32(s, closure->sequence_number);
158
		    
Niels Möller's avatar
Niels Möller committed
159
160
161
162
		HASH_UPDATE(closure->connection->rec_mac, 4, s);
		HASH_UPDATE(closure->connection->rec_mac,
			    closure->buffer->length,
			    closure->buffer->data);
163
	      }
Niels Möller's avatar
Niels Möller committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

	    /* Allocate full packet */
	    {
	      int done = block_size - 4;
		  
	      closure->buffer
		= ssh_format("%ls%lr",
			     done,
			     closure->buffer->data + 4,
			     length - done,
			     &closure->crypt_pos);

	      /* FIXME: Is this needed anywhere? */
	      closure->buffer->sequence_number
		= closure->sequence_number++;

	      closure->pos = done;
	      closure->state = WAIT_CONTENTS;
	    }
	    /* Fall through */
184
	  }
Niels Möller's avatar
Niels Möller committed
185
186
187
188
189
190
191
192
	else
	  /* Try reading some more */
	  break;
      }
    case WAIT_CONTENTS:
      {
	UINT32 left = closure->buffer->length - closure->pos;
	int n = A_READ(read, left, closure->buffer->data + closure->pos);
193

Niels Möller's avatar
Niels Möller committed
194
195
196
197
198
199
200
201
202
203
204
	switch(n)
	  {
	  case 0:
	    RETURN LSH_OK | LSH_GOON;
	  case A_FAIL:
	    /* Fall through */
	  case A_EOF:
	    /* FIXME: Free associated resources! */
	    return LSH_FAIL | LSH_DIE;
	  }
	closure->pos += n;
205

Niels Möller's avatar
Niels Möller committed
206
207
208
209
210
211
212
213
214
215
216
217
	/* Read a complete packet? */
	if (n == left)
	  {
	    assert(left == ((closure->buffer->length
			     + closure->buffer->data)
			    - closure->crypt_pos));
	    if (closure->connection->rec_crypto)
	      CRYPT(closure->connection->rec_crypto,
		    left,
		    closure->crypt_pos,
		    closure->crypt_pos);		      
	    if (closure->connection->rec_mac)
218
	      {
Niels Möller's avatar
Niels Möller committed
219
220
221
222
223
224
225
226
227
		closure->computed_mac = lsh_string_realloc
		  (closure->computed_mac,
		   closure->connection->rec_mac->hash_size);

		HASH_UPDATE(closure->connection->rec_mac,
			    left,
			    closure->crypt_pos);
		HASH_DIGEST(closure->connection->rec_mac,
			    closure->computed_mac->data);
228
	      }
Niels Möller's avatar
Niels Möller committed
229
230
231
	    closure->state = WAIT_MAC;
	    closure->pos = 0;
	    /* Fall through */
232
	  }
Niels Möller's avatar
Niels Möller committed
233
234
235
236
237
238
239
240
241
242
	else
	  /* Try reading some more */
	  break;
      }
    case WAIT_MAC:
      if (closure->connection->rec_mac)
	{
	  UINT32 left = (closure->connection->rec_mac->mac_size
			 - closure->pos);
	  UINT8 *mac = alloca(left);
243

Niels Möller's avatar
Niels Möller committed
244
	  int n = A_READ(read, left, mac);
245

Niels Möller's avatar
Niels Möller committed
246
247
248
249
250
251
252
253
254
	  switch(n)
	    {
	    case 0:
	      return LSH_OK | LSH_GOON;
	    case A_FAIL:
	      /* Fall through */
	    case A_EOF:
	      /* FIXME: Free associated resources! */
	      return LSH_FAIL | LSH_DIE;
255
	    }
Niels Möller's avatar
Niels Möller committed
256
257
258
259
260
261
262
263

	  /* FIXME: Don't fail until the entire MAC has been read.
	   * Otherwise we will leak information about partially
	   * correct MAC:s. */
	  if (!memcmp(mac,
		      closure->computed_mac + closure->pos,
		      n))
	    /* FIXME: Free resources */
264
	    return 0;
Niels Möller's avatar
Niels Möller committed
265
266
267
268
269
270

	  closure->pos += n;

	  if (n < left)
	    /* Try reading more */
	    break;
271
	}
Niels Möller's avatar
Niels Möller committed
272
273
274
275
276
277
278
279
280
281
      /* MAC was ok, send packet on */
      {
	struct lsh_string *packet = closure->buffer;
	closure->buffer = NULL;
	closure->state = WAIT_START;

	return A_WRITE(closure->handler, packet);
      }
    default:
      fatal("Internal error\n");
282
    }
Niels Möller's avatar
Niels Möller committed
283
  fatal("Internal error\n");
284
}
Niels Möller's avatar
Niels Möller committed
285
286

struct read_handler *make_read_packet(struct abstract_write *handler,
287
				      struct ssh_connection *connection)
Niels Möller's avatar
Niels Möller committed
288
289
290
{
  struct read_packet *closure = xalloc(sizeof(struct read_packet));

291
  closure->super.handler = do_read_packet;
Niels Möller's avatar
Niels Möller committed
292

293
  closure->connection = connection;
294
  closure->handler = handler;
295

296
  closure->state = WAIT_START;
Niels Möller's avatar
Niels Möller committed
297
298
299
300
301
302
  closure->sequence_number = 0;

  /* closure->pos = 0; */
  closure->buffer = NULL;
  /* closure->crypt_pos = 0; */

303
304
  closure->computed_mac = NULL;
  
Niels Möller's avatar
Niels Möller committed
305
306
  return &closure->super;
}