read_packet.c 7.53 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
  UINT8 *crypt_pos;

  /* Must point to an area large enough to hold a mac */
60 61
  struct lsh_string *recieved_mac; 
  
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

81 82 83 84 85
/* For efficiency, allow reading several packets at a time. Butin
 * order not to starve other channels, return when this much data has
 * been read. */
#define QUANTUM 1024

Niels Möller's avatar
Niels Möller committed
86 87
static int do_read_packet(struct read_handler **h,
			  struct abstract_read *read)
88
{
89
  struct read_packet *closure = (struct read_packet *) *h;
90
  int total = 0;
Niels Möller's avatar
Niels Möller committed
91
  
92
  MDEBUG(closure);
Niels Möller's avatar
Niels Möller committed
93

94 95
  while (total < QUANTUM)
    switch(closure->state)
Niels Möller's avatar
Niels Möller committed
96
      {
97 98 99 100
      case WAIT_START:
	{
	  UINT32 block_size = closure->connection->rec_crypto
	    ? closure->connection->rec_crypto->block_size : 8;
Niels Möller's avatar
Niels Möller committed
101

102 103 104 105 106 107
	  closure->buffer = lsh_string_realloc(closure->buffer,
					       block_size);
	  if (closure->connection->rec_mac)
	    closure->recieved_mac = lsh_string_realloc
	      (closure->recieved_mac,
	       closure->connection->rec_mac->hash_size);
108

109
	  closure->pos = 0;
110

111 112 113 114 115 116 117 118 119
	  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;
Niels Möller's avatar
Niels Möller committed
120

121 122 123 124
	  left = block_size - closure->pos;
	    
	  n = A_READ(read, left, closure->buffer->data + closure->pos);
	  switch(n)
Niels Möller's avatar
Niels Möller committed
125
	    {
126 127 128 129 130 131 132
	    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;
Niels Möller's avatar
Niels Möller committed
133
	    }
134 135 136 137 138 139 140
	  closure->pos += n;
	  total += n;
	  
	  /* Read a complete block? */
	  if (n == left)
	    {
	      UINT32 length;
141

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
	      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)
		{
		  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;
		}

	      if ( (length < 12)
		   || (length < (block_size - 4))
		   || ( (length + 4) % block_size))
		{
		  werror("read_packet: Bad packet length %d\n",
			 length);
		  return LSH_FAIL | LSH_DIE;
		}

	      /* Process this block before the length field is lost. */
	      if (closure->connection->rec_mac)
		{
		  UINT8 s[4];
		  WRITE_UINT32(s, closure->sequence_number);
		    
		  HASH_UPDATE(closure->connection->rec_mac, 4, s);
		  HASH_UPDATE(closure->connection->rec_mac,
			      closure->buffer->length,
			      closure->buffer->data);
		}
177

178
	      /* Allocate full packet */
179
	      {
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
		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;
195
	      }
196 197 198 199 200 201 202
	      /* Fall through */
	    }
	  else
	    /* Try reading some more */
	    break;
	}
      case WAIT_CONTENTS:
Niels Möller's avatar
Niels Möller committed
203
	{
204 205
	  UINT32 left = closure->buffer->length - closure->pos;
	  int n = A_READ(read, left, closure->buffer->data + closure->pos);
206

Niels Möller's avatar
Niels Möller committed
207 208 209 210 211 212 213 214 215
	  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;
216
	    }
Niels Möller's avatar
Niels Möller committed
217
	  closure->pos += n;
218
	  total += n;
Niels Möller's avatar
Niels Möller committed
219

220 221 222 223 224
	  /* Read a complete packet? */
	  if (n == left)
	    {
	      UINT32 left
		= ( (closure->buffer->length + closure->buffer->data)
225
		    - closure->crypt_pos );
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	      if (closure->connection->rec_crypto)
		CRYPT(closure->connection->rec_crypto,
		      left,
		      closure->crypt_pos,
		      closure->crypt_pos);		      
	      if (closure->connection->rec_mac)
		HASH_UPDATE(closure->connection->rec_mac,
			    left,
			    closure->crypt_pos);
	      closure->state = WAIT_MAC;
	      closure->pos = 0;
	      /* Fall through */
	    }
	  else
	    /* Try reading some more */
Niels Möller's avatar
Niels Möller committed
241
	    break;
242
	}
243 244 245 246 247 248 249 250 251
      case WAIT_MAC:
	if (closure->connection->rec_mac)
	  {
	    UINT32 left = (closure->connection->rec_mac->mac_size
			   - closure->pos);
	    UINT8 *mac;
	    
	    int n = A_READ(read, left,
			   closure->recieved_mac->data + closure->pos);
Niels Möller's avatar
Niels Möller committed
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
	    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;
	    total += n;
	    
	    /* Read complete mac? */
	    if (n == left)
	      {
		mac = alloca(closure->connection->rec_mac->hash_size);
		HASH_DIGEST(closure->connection->rec_mac, mac);
	    
		if (!memcmp(mac,
			    closure->recieved_mac,
			    closure->connection->rec_mac->hash_size))
		  /* FIXME: Free resources */
		  return LSH_FAIL | LSH_DIE;

		closure->pos += n;
	      }
	    else
	      /* Try reading more */
	      break;
	  }
	/* MAC was ok, send packet on */
	{
	  struct lsh_string *packet = closure->buffer;
	  int res;
	  
	  closure->buffer = NULL;
	  closure->state = WAIT_START;
	  
	  res = A_WRITE(closure->handler, packet);
293
	  if (LSH_ACTIONP(res))
294 295 296 297 298
	    return res;
	  break;
	}
      default:
	fatal("Internal error\n");
Niels Möller's avatar
Niels Möller committed
299
      }
300
  return LSH_OK | LSH_GOON;
301
}
Niels Möller's avatar
Niels Möller committed
302 303

struct read_handler *make_read_packet(struct abstract_write *handler,
304
				      struct ssh_connection *connection)
Niels Möller's avatar
Niels Möller committed
305 306 307
{
  struct read_packet *closure = xalloc(sizeof(struct read_packet));

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

310
  closure->connection = connection;
311
  closure->handler = handler;
312

313
  closure->state = WAIT_START;
Niels Möller's avatar
Niels Möller committed
314 315 316 317 318 319
  closure->sequence_number = 0;

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

320
  closure->recieved_mac = NULL;
321
  
Niels Möller's avatar
Niels Möller committed
322 323
  return &closure->super;
}