transport.c 21.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* transport.c
 *
 * Interface for the ssh transport protocol.
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 2005 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

Niels Möller's avatar
Niels Möller committed
25
26
27
28
29
30
31
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>

#include "format.h"
32
#include "io.h"
33
#include "lsh_string.h"
Niels Möller's avatar
Niels Möller committed
34
35
36
37
38
39
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"

#include "transport.h"

40
41
42
43
#define GABA_DEFINE
# include "transport.h.x"
#undef GABA_DEFINE

44
45
46
47
48
#include "transport.c.x"

/* Maximum time for keyexchange to complete */
#define TRANSPORT_TIMEOUT_KEYEXCHANGE (10 * 60)

49
50
51
/* FIXME: Make configurable. Different timeouts can be used by client
   and server, and artificially small timeouts are useful for
   testing. */
52
53
54
/* Session key lifetime */
#define TRANSPORT_TIMEOUT_REEXCHANGE (40 * 60)

55
/* Time to wait for write buffer to drain after disconnect */
56
#define TRANSPORT_TIMEOUT_CLOSE (5 * 60)
57

58
59
60
61
/* Forward declaration. */
static void *
oop_timer_retry(oop_source *oop, struct timeval tv, void *state);

62
63
64
void
init_transport_connection(struct transport_connection *self,
			  void (*kill)(struct resource *s),
Niels Möller's avatar
Niels Möller committed
65
			  struct transport_context *ctx,
66
			  int ssh_input, int ssh_output,
67
68
			  void (*event)(struct transport_connection *,
					enum transport_event event))
69
70
71
{
  init_resource(&self->super, kill);
  
Niels Möller's avatar
Niels Möller committed
72
  self->ctx = ctx;
73

Niels Möller's avatar
Niels Möller committed
74
  init_kexinit_state(&self->kex);
75
76
  self->session_id = NULL;
  self->keyexchange_handler = NULL;
Niels Möller's avatar
Niels Möller committed
77
78
  self->new_mac = NULL;
  self->new_crypto = NULL;
79
  self->new_inflate = NULL;
80
81

  self->expire = NULL;
Niels Möller's avatar
Niels Möller committed
82
  
83
  self->ssh_input = ssh_input;
Niels Möller's avatar
Niels Möller committed
84
  self->reader = make_transport_read_state();
85
  self->read_active = 0;
Niels Möller's avatar
Niels Möller committed
86
87
  
  /* One extra byte needed for inflate */
88
  self->read_packet = lsh_string_alloc(SSH_MAX_PACKET + 1);
89
90
  self->retry_length = 0;
  self->retry_seqno = 0;
Niels Möller's avatar
Niels Möller committed
91
92
  io_register_fd(ssh_input, "transport read fd");

93
  self->ssh_output = ssh_output;
Niels Möller's avatar
Niels Möller committed
94
  self->writer = make_transport_write_state();
95
  self->write_active = 0;
96
  self->write_margin = SSH_MAX_TRANSPORT_RESPONSE;
Niels Möller's avatar
Niels Möller committed
97
98
  if (ssh_output != ssh_input)
    io_register_fd(ssh_output, "transport write fd");
99

100
  self->closing = 0;
Niels Möller's avatar
Niels Möller committed
101
  self->event_handler = event;
102
103
}

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* GABA:
   (class
     (name transport_timeout)
     (super lsh_callback)
     (vars
       (connection object transport_connection)))
*/

static void
transport_timeout(struct transport_connection *connection,
		  unsigned seconds,
		  void (*callback)(struct lsh_callback *s))
{
  NEW(transport_timeout, self);
  self->super.f = callback;
  self->connection = connection;

  if (connection->expire)
    KILL_RESOURCE(connection->expire);

  connection->expire = io_callout(&self->super, seconds);
}

static void
transport_timeout_close(struct lsh_callback *s)
{
  CAST(transport_timeout, self, s);
  struct transport_connection *connection = self->connection;

133
  KILL_RESOURCE(&connection->super);
134
135
}

136
137
/* Intended to be called by the kill method in child class. */
void
138
transport_connection_kill(struct transport_connection *connection)
139
140
141
142
143
144
{
  if (connection->expire)
    {
      KILL_RESOURCE(connection->expire);
      connection->expire = NULL;
    }
145
146
147
148

  global_oop_source->cancel_time(global_oop_source,
				 OOP_TIME_NOW, oop_timer_retry, connection);

Niels Möller's avatar
Niels Möller committed
149
150
151
152
153
154
  io_close_fd(connection->ssh_input);

  if (connection->ssh_output != connection->ssh_input)
    io_close_fd(connection->ssh_output);
  
  connection->ssh_input = connection->ssh_output = -1;
155
156
157
158
159
}

/* We close the connection when we either have sent a DISCONNECT
   message (possible as the result of a protocol error), or when we
   have received a DISCONNECT message. In the first case, we want to
160
161
   let our write buffer for the ssh connection drain (so that our
   DISCONNECT message is delivered properly).
162
163
164
165

   In both cases, the application can't generate any more data. We
   generate a TRANSPORT_EVENT_CLOSE event tell it, and the return
   value tells us if the application is finished.
166
167
168
169
170

   When a DISCONNECT message is sent and received, we stop caring
   about delivering application data. It's the job of the connection
   layer's channel close logic, with CHANNEL_EOF and CHANNEL_CLOSE to
   decide when the connection can be disconnected.
171
172
*/

173
174
175
void
transport_close(struct transport_connection *connection, int flush)
{
Niels Möller's avatar
Niels Möller committed
176
177
  trace("transport_close\n");

178
  if (connection->super.alive && !connection->closing)
179
    {
180
      connection->event_handler(connection, TRANSPORT_EVENT_CLOSE);
181

182
183
184
185
186
      if (connection->expire)
	{
	  KILL_RESOURCE(connection->expire);
	  connection->expire = NULL;
	}
Niels Möller's avatar
Niels Möller committed
187
188
189

      if (connection->ssh_input != connection->ssh_output)
	io_close_fd (connection->ssh_input);
190
      
Niels Möller's avatar
Niels Möller committed
191
192
193
      connection->ssh_input = -1;

      if (flush && connection->write_active)
194
	{
Niels Möller's avatar
Niels Möller committed
195
	  /* Stay open for a while, to allow buffers to drain. */
196
197
	  connection->closing = 1;

Niels Möller's avatar
Niels Möller committed
198
199
200
201
	  transport_timeout(connection,
			    TRANSPORT_TIMEOUT_CLOSE,
			    transport_timeout_close);
	  trace("transport_close: Waiting for buffers to drain.\n");
202
	}
203
      else
204
205
206
207
208
	{	      
	  io_close_fd(connection->ssh_output);
	  connection->ssh_output = -1;
	  KILL_RESOURCE(&connection->super);
	}
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    }
}

void
transport_kexinit_handler(struct transport_connection *connection,
			  uint32_t length, const uint8_t *packet)
{
  int is_server = connection->ctx->is_server;
  const char *error;

  /* Have we sent a kexinit message already? */
  if (!connection->kex.kexinit[is_server])
    transport_send_kexinit(connection);
  
  error = handle_kexinit(&connection->kex, length, packet,
			 connection->ctx->algorithms,
			 is_server);

  if (error)
    {
      transport_disconnect(connection,
			   SSH_DISCONNECT_KEY_EXCHANGE_FAILED, error);
      return;
    }
  {
    CAST_SUBTYPE(keyexchange_algorithm, kex_algorithm,
		 LIST(connection->kex.algorithm_list)[KEX_KEY_EXCHANGE]);
    
    connection->keyexchange_handler
238
      = KEYEXCHANGE_INIT(kex_algorithm, connection);
239

240
241
242
243
    if (!connection->keyexchange_handler)
      transport_disconnect(connection,
			   SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
			   "Configuration error");
244
245
246
247
248
249
250
251
252
253
254
255
256
  }  
}

static void
transport_timeout_reexchange(struct lsh_callback *s)
{
  CAST(transport_timeout, self, s);
  struct transport_connection *connection = self->connection;

  verbose("Session key expired. Initiating key re-exchange.\n");
  transport_send_kexinit(connection);
}

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/* Returns 1 if processing of the packet is complete, or 0 if it
   should be retried later. */
static int
transport_process_packet(struct transport_connection *connection,
			 uint32_t seqno, uint32_t length, const struct lsh_string *packet)
{
  const uint8_t *data;
  uint8_t msg;

  if (length == 0)
    {
      transport_protocol_error(connection, "Received empty packet");
      return 1;
    }

  data = lsh_string_data(packet);
  msg = data[0];

275
276
  trace("transport_process_packet: %T (%i) message, length %i\n",
	msg, msg, length);
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  
  /* Messages of type IGNORE, DISCONNECT and DEBUG are always
     acceptable. */
  if (msg == SSH_MSG_IGNORE)
    {
      /* Do nothing */
    }
  else if (msg == SSH_MSG_DISCONNECT)
    {
      verbose("Received disconnect message.\n");
      transport_close(connection, 0);
    }
  else if (msg == SSH_MSG_DEBUG)
    {
      /* Ignore it. Perhaps it's best to pass it on to the
	 application? */
    }

  /* Otherwise, behaviour depends on the kex state */
  else switch (connection->kex.read_state)
    {
    default:
      abort();
    case KEX_STATE_IGNORE:
      connection->kex.read_state = KEX_STATE_IN_PROGRESS;
      break;
    case KEX_STATE_IN_PROGRESS:
      if (msg < SSH_FIRST_KEYEXCHANGE_SPECIFIC
	  || msg >= SSH_FIRST_USERAUTH_GENERIC)
	{
Niels Möller's avatar
Niels Möller committed
307
	  werror("Unexpected %T (%i) message during key exchange.\n", msg, msg);
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
	  transport_protocol_error(connection,
				   "Unexpected message during key exchange");
	}
      else
	connection->keyexchange_handler->handler(connection->keyexchange_handler,
						 connection, length, data);
      break;
    case KEX_STATE_NEWKEYS:
      if (msg != SSH_MSG_NEWKEYS)
	transport_protocol_error(connection, "NEWKEYS expected");
      else if (length != 1)
	transport_protocol_error(connection, "Invalid NEWKEYS message");
      else
	{
	  transport_read_new_keys(connection->reader,
				  connection->new_mac,
				  connection->new_crypto,
				  connection->new_inflate);
	  connection->new_mac = NULL;
	  connection->new_crypto = NULL;
	  connection->new_inflate = NULL;

	  reset_kexinit_state(&connection->kex);
	  transport_timeout(connection,
			    TRANSPORT_TIMEOUT_REEXCHANGE,
			    transport_timeout_reexchange);	      
	}
      break;

    case KEX_STATE_INIT:
      if (msg == SSH_MSG_KEXINIT)
	transport_kexinit_handler(connection, length, data);
	  
      /* Pass on everything except keyexchagne related messages. */
      else if ( (msg < SSH_FIRST_KEYEXCHANGE_GENERIC
		 || msg >= SSH_FIRST_USERAUTH_GENERIC)
		&& connection->packet_handler)
	return connection->packet_handler(connection, seqno, length, data);
      else
Niels Möller's avatar
Niels Möller committed
347
	transport_send_packet(connection, TRANSPORT_WRITE_FLAG_PUSH,
348
			      format_unimplemented(seqno));
349
350
351
352
353
      break;
    }
  return 1;
}

Niels Möller's avatar
Niels Möller committed
354
static void *
355
356
oop_read_ssh(oop_source *source UNUSED,
	     int fd, oop_event event, void *state)
357
{
358
  CAST_SUBTYPE(transport_connection, connection, (struct lsh_object *) state);
Niels Möller's avatar
Niels Möller committed
359
360
  int error;
  const char *error_msg;
361
  enum ssh_read_status status;
Niels Möller's avatar
Niels Möller committed
362
363

  assert(event == OOP_READ);
Niels Möller's avatar
Niels Möller committed
364
  assert(fd == connection->ssh_input);
365

366
367
  assert(!connection->retry_length);

Niels Möller's avatar
Niels Möller committed
368
369
370
371
372
  while (connection->line_handler && connection->ssh_input >= 0)
    {
      uint32_t length;
      const uint8_t *line;
  
373
374
      status = transport_read_line(connection->reader, fd, &error, &error_msg,
				   &length, &line);
Niels Möller's avatar
Niels Möller committed
375
      fd = -1;
376
377
      
      switch (status)
Niels Möller's avatar
Niels Möller committed
378
	{
379
380
381
	default:
	  return OOP_CONTINUE;

382
	case SSH_READ_IO_ERROR:
383
384
385
386
	  werror("Read error: %e\n", error);
	  transport_close(connection, 0);
	  break;

387
	case SSH_READ_PROTOCOL_ERROR:
388
389
390
	  transport_disconnect(connection, error, error_msg);
	  break;

391
	case SSH_READ_EOF:
Niels Möller's avatar
Niels Möller committed
392
393
	  werror("Unexpected EOF at start of line.\n");
	  transport_close(connection, 0);
394
395
	  break;

396
	case SSH_READ_COMPLETE:
397
398
	  connection->line_handler(connection, length, line);
	  break;
Niels Möller's avatar
Niels Möller committed
399
400
	}
    }
Niels Möller's avatar
Niels Möller committed
401
  while (connection->ssh_input >= 0)
Niels Möller's avatar
Niels Möller committed
402
403
404
405
    {
      uint32_t seqno;
      uint32_t length;
      
406
407
      status = transport_read_packet(connection->reader, fd, &error, &error_msg,
				     &seqno, &length, connection->read_packet);
Niels Möller's avatar
Niels Möller committed
408
      fd = -1;
409

410
      switch (status)
Niels Möller's avatar
Niels Möller committed
411
	{
412
	case SSH_READ_IO_ERROR:
413
414
	  werror("Read error: %e\n", error);
	  transport_close(connection, 0);
Niels Möller's avatar
Niels Möller committed
415
	  break;
416

417
	case SSH_READ_PROTOCOL_ERROR:
418
	  transport_disconnect(connection, error, error_msg);
Niels Möller's avatar
Niels Möller committed
419
	  break;
420

421
	case SSH_READ_EOF:
422
423
	  werror("Unexpected EOF at start of packet.\n");
	  transport_close(connection, 0);	  
Niels Möller's avatar
Niels Möller committed
424
	  break;
425

426
	case SSH_READ_PUSH:
427
428
	  connection->event_handler(connection, TRANSPORT_EVENT_PUSH);
	  /* Fall through */
429
	case SSH_READ_PENDING:
430
	  return OOP_CONTINUE;
431
	  
432
	case SSH_READ_COMPLETE:
433
434
435
436
437
438
439
	  if (!transport_process_packet(connection, seqno, length, connection->read_packet))
	    {
	      connection->retry_length = length;
	      connection->retry_seqno = seqno;
	      transport_stop_read(connection);
	      return OOP_CONTINUE;
	    }
Niels Möller's avatar
Niels Möller committed
440
441
442
443
	  break;
	}
    }
  return OOP_CONTINUE;
444
445
}

446
static void *
447
448
oop_timer_retry(oop_source *oop UNUSED,
		struct timeval tv UNUSED, void *state)
449
450
{
  CAST_SUBTYPE(transport_connection, connection, (struct lsh_object *) state);
451
452
453
454
455
456
457
  uint32_t length;
  uint32_t seqno;

  assert(connection->super.alive);
  
  length = connection->retry_length;
  seqno = connection->retry_seqno;
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
  
  assert(length);

  connection->retry_length = 0;
  connection->retry_seqno = 0;
  connection->read_active = 0;

  if (!connection->packet_handler(connection, seqno, length,
				  lsh_string_data(connection->read_packet)))
    {
      transport_disconnect(connection,
			   SSH_DISCONNECT_BY_APPLICATION,
			   "Application layer not responsive.");
      return OOP_CONTINUE;
    }

  /* Process any remaining buffered packets */
  while (connection->ssh_input >= 0)
    {
477
      enum ssh_read_status status;
478
479
480
481
482
483
484
485
      const char *error_msg;
      int error;

      status = transport_read_packet(connection->reader, -1, &error, &error_msg,
				     &seqno, &length, connection->read_packet);

      switch (status)
	{
486
	case SSH_READ_IO_ERROR:
487
488
489
490
	  werror("Read error: %e\n", error);
	  transport_close(connection, 0);
	  break;

491
	case SSH_READ_PROTOCOL_ERROR:
492
493
494
	  transport_disconnect(connection, error, error_msg);
	  break;

495
	case SSH_READ_EOF:
496
497
498
499
	  werror("Unexpected EOF at start of packet.\n");
	  transport_close(connection, 0);	  
	  break;

500
	case SSH_READ_PUSH:
501
502
	  connection->event_handler(connection, TRANSPORT_EVENT_PUSH);
	  /* Fall through */
503
	case SSH_READ_PENDING:
504
505
506
	  transport_start_read(connection);
	  return OOP_CONTINUE;
	  
507
	case SSH_READ_COMPLETE:
508
509
510
511
512
513
514
515
516
517
518
519
520
	  if (!transport_process_packet(connection, seqno, length, connection->read_packet))
	    {
	      connection->retry_length = length;
	      connection->retry_seqno = seqno;
	      /* Wait for application to wake us up again */
	      return OOP_CONTINUE;
	    }
	  break;
	}
    }
  return OOP_CONTINUE;
}

521
522
523
524
525
void
transport_start_read(struct transport_connection *connection)
{
  if (!connection->read_active)
    {
526
527
528
529
530
      connection->read_active = 1;

      if (connection->retry_length)
	/* Arrange to have the packet handler called from the main
	   event loop. */
531
532
533
	/* Note: To protect connection from gc, it is essential that
	   the timer is cancelled when the connection is killed.
	   FIXME: Use io_callout instead? */
Niels Möller's avatar
Niels Möller committed
534
535
	global_oop_source->on_time(global_oop_source,
				   OOP_TIME_NOW, oop_timer_retry, connection);
536
      else
Niels Möller's avatar
Niels Möller committed
537
538
	global_oop_source->on_fd(global_oop_source, connection->ssh_input,
				 OOP_READ, oop_read_ssh, connection);
539
540
541
542
543
544
545
    }
}

void
transport_stop_read(struct transport_connection *connection)
{
  connection->read_active = 0;
546
547
  global_oop_source->cancel_time(global_oop_source,
				 OOP_TIME_NOW, oop_timer_retry, connection);
Niels Möller's avatar
Niels Möller committed
548
549
  global_oop_source->cancel_fd(global_oop_source,
			       connection->ssh_input, OOP_READ);  
550
551
}

552
553
554
555
556
557
558
559
/* Returns 1 if the write buffer is close to full */ 
static int
transport_write_almost_full(struct transport_connection *connection)
{
  return (connection->writer->super.length + connection->write_margin
	  > lsh_string_length(connection->writer->super.buffer));
}

560
561
562
static void
transport_stop_write(struct transport_connection *connection);

Niels Möller's avatar
Niels Möller committed
563
static void *
564
565
oop_write_ssh(oop_source *source UNUSED,
	      int fd, oop_event event, void *state)
Niels Möller's avatar
Niels Möller committed
566
{
567
  CAST_SUBTYPE(transport_connection, connection, (struct lsh_object *) state);
Niels Möller's avatar
Niels Möller committed
568
  enum transport_write_status status;
Niels Möller's avatar
Niels Möller committed
569
570
571
572

  assert(event == OOP_WRITE);
  assert(fd == connection->ssh_output);

573
  status = transport_write_flush(connection->writer, fd);
574
  switch(status)
Niels Möller's avatar
Niels Möller committed
575
    {
576
    default:
Niels Möller's avatar
Niels Möller committed
577
    case TRANSPORT_WRITE_OVERFLOW:
578
      abort();
Niels Möller's avatar
Niels Möller committed
579
    case TRANSPORT_WRITE_PENDING:
Niels Möller's avatar
Niels Möller committed
580
581
      /* More to write */
      break;
Niels Möller's avatar
Niels Möller committed
582
    case TRANSPORT_WRITE_COMPLETE:
583
      transport_stop_write(connection);
Niels Möller's avatar
Niels Möller committed
584
      break;
585
      
Niels Möller's avatar
Niels Möller committed
586
    case TRANSPORT_WRITE_IO_ERROR:
Niels Möller's avatar
Niels Möller committed
587
588
589
590
591
592
593
594
595
596
      if (errno != EWOULDBLOCK)
	{
	  werror("Write failed: %e\n", errno);
	  transport_close(connection, 0);
	}
      break;
    }
  return OOP_CONTINUE;
}

597
598
static void
transport_start_write(struct transport_connection *connection)
Niels Möller's avatar
Niels Möller committed
599
{
600
  if (!connection->write_active)
Niels Möller's avatar
Niels Möller committed
601
    {
602
603
      connection->write_active = 1;
      
Niels Möller's avatar
Niels Möller committed
604
605
      global_oop_source->on_fd(global_oop_source, connection->ssh_output,
			       OOP_WRITE, oop_write_ssh, connection);
606
607
    }
  
608
609
  if (connection->kex.write_state == KEX_STATE_INIT
      && transport_write_almost_full(connection))
610
611
612
613
614
615
616
617
618
619
620
    connection->event_handler(connection,
			      TRANSPORT_EVENT_STOP_APPLICATION);
}

static void
transport_stop_write(struct transport_connection *connection)
{
  if (connection->write_active)
    {
      connection->write_active = 0;

Niels Möller's avatar
Niels Möller committed
621
622
      global_oop_source->cancel_fd(global_oop_source,
				   connection->ssh_output, OOP_WRITE);
623
      if (connection->closing)
Niels Möller's avatar
Niels Möller committed
624
	{
625
626
	  close(connection->ssh_output);
	  connection->ssh_output = -1;
627

628
629
630
	  connection->closing--;
	  if(!connection->closing)
	    KILL_RESOURCE(&connection->super);
631
	}
632
633
      else if (connection->kex.write_state == KEX_STATE_INIT
	       && !transport_write_almost_full(connection))
634
635
	connection->event_handler(connection,
				  TRANSPORT_EVENT_START_APPLICATION); 
Niels Möller's avatar
Niels Möller committed
636
637
638
    }
}

Niels Möller's avatar
Niels Möller committed
639
/* FIXME: Naming is unfortunate, with transport_write_packet vs
640
641
   transport_send_packet.

Niels Möller's avatar
Niels Möller committed
642
   FIXME: Use a length / const uint8_t * interface?
643
644
645
646
647

   FIXME: Return a result code saying if more data can be sent right
   away, or if the buffer is full.
*/

648
/* A NULL packets means to push out the buffered data. */
649
void
Niels Möller's avatar
Niels Möller committed
650
transport_send_packet(struct transport_connection *connection,
Niels Möller's avatar
Niels Möller committed
651
		      enum transport_write_flag flags,
Niels Möller's avatar
Niels Möller committed
652
		      struct lsh_string *packet)
653
{
654
  struct transport_write_state *writer;
Niels Möller's avatar
Niels Möller committed
655
  enum transport_write_status status;
656

657
658
659
  if (!connection->super.alive)
    {
      werror("connection_write_data: Connection is dead.\n");
Niels Möller's avatar
Niels Möller committed
660
      lsh_string_free(packet);
661
662
      return;
    }
663

664
  writer = connection->writer;  
665
  if (packet)
666
    status = transport_write_packet(writer, connection->ssh_output,
667
				    flags, packet);
668
  else
669
    status = transport_write_flush(writer, connection->ssh_output);
670
671
  switch (status)
    {
Niels Möller's avatar
Niels Möller committed
672
    case TRANSPORT_WRITE_OVERFLOW:
673
674
675
      werror("Remote peer not responsive. Disconnecting.\n");
      transport_close(connection, 0);
      break;
Niels Möller's avatar
Niels Möller committed
676
    case TRANSPORT_WRITE_IO_ERROR:
677
678
679
      werror("Write failed: %e\n", errno);
      transport_close(connection, 0);
      break;
Niels Möller's avatar
Niels Möller committed
680
    case TRANSPORT_WRITE_PENDING:
681
682
      transport_start_write(connection);
      break;
Niels Möller's avatar
Niels Möller committed
683
    case TRANSPORT_WRITE_COMPLETE:
684
685
686
      transport_stop_write(connection);
      break;
    }
687
688
689
690
691
692
}

void
transport_disconnect(struct transport_connection *connection,
		     int reason, const uint8_t *msg)
{
Niels Möller's avatar
Niels Möller committed
693
694
695
  if (msg)
    werror("Disconnecting: %z\n", msg);
  
696
  if (reason)
Niels Möller's avatar
Niels Möller committed
697
    transport_send_packet(connection, TRANSPORT_WRITE_FLAG_PUSH,
698
			  format_disconnect(reason, msg, ""));
699

Niels Möller's avatar
Niels Möller committed
700
  transport_close(connection, 1);
701
702
703
};

static void
704
transport_timeout_keyexchange(struct lsh_callback *s)
705
{
706
707
708
709
710
711
712
713
714
715
716
  CAST(transport_timeout, self, s);
  struct transport_connection *connection = self->connection;

  transport_disconnect(connection, SSH_DISCONNECT_BY_APPLICATION,
		       "Key exchange timeout");  
}

void
transport_send_kexinit(struct transport_connection *connection)
{
  int is_server = connection->ctx->is_server;
717
  struct lsh_string *s;
718
719
720
  struct kexinit *kex;

  connection->kex.write_state = 1;
721
722
723

  if (connection->session_id)
    /* This is a reexchange; no more data can be sent */
724
    connection->event_handler(connection, TRANSPORT_EVENT_STOP_APPLICATION);
725
  
726
  kex = connection->ctx->kexinit->make(connection->ctx->kexinit);
727
  connection->kex.kexinit[is_server] = kex;
728

729
730
  assert(kex->first_kex_packet_follows == !!kex->first_kex_packet);
  assert(connection->kex.read_state == KEX_STATE_INIT);
731
732
733
  
  s = format_kexinit(kex);
  connection->kex.literal_kexinit[is_server] = lsh_string_dup(s); 
Niels Möller's avatar
Niels Möller committed
734
  transport_send_packet(connection, TRANSPORT_WRITE_FLAG_PUSH, s);
735

736
737
738
739
740
741
742
  /* NOTE: This feature isn't fully implemented, as we won't tell
   * the selected key exchange method if the guess was "right". */
  if (kex->first_kex_packet_follows)
    {
      s = kex->first_kex_packet;
      kex->first_kex_packet = NULL;

Niels Möller's avatar
Niels Möller committed
743
      transport_send_packet(connection, TRANSPORT_WRITE_FLAG_PUSH, s);
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
    }

  transport_timeout(connection,
		    TRANSPORT_TIMEOUT_KEYEXCHANGE,
		    transport_timeout_keyexchange);
}

void
transport_keyexchange_finish(struct transport_connection *connection,
			     const struct hash_algorithm *H,
			     struct lsh_string *exchange_hash,
			     struct lsh_string *K)
{
  int first = !connection->session_id;

  if (first)
    connection->session_id = exchange_hash;

  if (!keyexchange_finish(connection, H, exchange_hash, K))
    {
      transport_disconnect(connection, SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
			   "Key exchange resulted in weak keys!");
      return;
    }

769
770
771
772
  assert(connection->kex.read_state == KEX_STATE_IN_PROGRESS);
  connection->kex.read_state = KEX_STATE_NEWKEYS;  
  connection->kex.write_state = 0;
  
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
  if (first)    
    connection->event_handler(connection,
			      TRANSPORT_EVENT_KEYEXCHANGE_COMPLETE);
  else
    {
      lsh_string_free(exchange_hash);
      connection->event_handler(connection,
				TRANSPORT_EVENT_START_APPLICATION);
    }
}

void
transport_handshake(struct transport_connection *connection,
		    struct lsh_string *version,
		    void (*line_handler)
		      (struct transport_connection *connection,
		       uint32_t length,
		       const uint8_t *line))
{
  int is_server = connection->ctx->is_server;
Niels Möller's avatar
Niels Möller committed
793
  enum transport_write_status status;
794
795
  
  connection->kex.version[is_server] = version;
796
797
798
  status = transport_write_line(connection->writer,
				connection->ssh_output,
				ssh_format("%lS\r\n", version));
799

800
  if (status < 0)
801
802
803
804
805
806
807
808
809
    {
      werror("Writing version string failed: %e\n", errno);
      transport_close(connection, 0);
    }

  transport_send_kexinit(connection);

  connection->line_handler = line_handler;

810
  transport_start_read(connection);
811
}