lsh-transport.c 34.6 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1 2 3 4 5 6 7 8
/* lsh-transport.c
 *
 * Client program responsible for the transport protocol and
 * user authnetication.
 */

/* lsh, an implementation of the ssh protocol
 *
Niels Möller's avatar
Niels Möller committed
9
 * Copyright (C) 1998, 1999, 2000, 2005, Niels Möller
Niels Möller's avatar
Niels Möller committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * 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
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>
#include <errno.h>
32 33 34 35 36
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
Niels Möller's avatar
Niels Möller committed
37
#include <sys/stat.h>
38
#include <fcntl.h>
39 40 41
#include <netdb.h>

#include "algorithms.h"
42
#include "charset.h"
43 44 45 46 47 48 49
#include "crypto.h"
#include "environ.h"
#include "format.h"
#include "interact.h"
#include "io.h"
#include "keyexchange.h"
#include "lsh_string.h"
50
#include "parse.h"
51 52
#include "randomness.h"
#include "resource.h"
53
#include "service.h"
54
#include "spki.h"
55
#include "ssh.h"
56
#include "transport_forward.h"
57 58 59 60
#include "version.h"
#include "werror.h"
#include "xalloc.h"

61 62 63 64 65 66 67 68 69 70 71 72
enum lsh_transport_state
{
  /* Initial handshake and key exchange */
  STATE_HANDSHAKE,
  /* We have sent our service request, waiting for reply. */
  STATE_SERVICE_REQUEST,
  /* We're trying to log in */
  STATE_USERAUTH,
  /* We're forwarding packets for the service */
  STATE_SERVICE_FORWARD,
};

73 74
#include "lsh-transport.c.x"

75 76 77 78
static int
lsh_transport_packet_handler(struct transport_connection *connection,
			     uint32_t seqno, uint32_t length, const uint8_t *packet);

79 80 81 82 83 84 85 86 87 88
static int
try_password_auth(struct lsh_transport_connection *self);

static int
try_keyboard_interactive_auth(struct lsh_transport_connection *self);

static void
send_userauth_info_response(struct lsh_transport_connection *self,
			    struct simple_buffer *buffer);

89 90 91 92 93 94
static void
start_userauth(struct lsh_transport_connection *self);

static void
start_service(struct lsh_transport_connection *self);

95 96 97 98
struct lsh_transport_lookup_verifier;

static struct lsh_transport_lookup_verifier *
make_lsh_transport_lookup_verifier(struct lsh_transport_config *config);
Niels Möller's avatar
Niels Möller committed
99 100 101

/* GABA:
   (class
102
     (name lsh_transport_config)
103
     (super transport_context)
104
     (vars
105
       (algorithms object algorithms_options)
106 107
       (werror_config object werror_config)

Niels Möller's avatar
Niels Möller committed
108
       (sloppy . int)
109 110 111 112 113 114 115
       (capture_file . "const char *")
       (capture_fd . int)

       (signature_algorithms object alist)
       (host_acls . "const char *")
       (host_db object lsh_transport_lookup_verifier)

116
       (home . "const char *")       
Niels Möller's avatar
Niels Möller committed
117 118 119
       (port . "const char *")
       (target . "const char *")

120
       (userauth . int)
121 122
       (user . "const char *")
       (identity . "const char *")
123 124
       (keypair object keypair)
       
125 126 127
       ; The service we ask for in the SERVICE_REQUEST
       (requested_service . "const char *")
       ; The service we ultimately want to start
128
       (service . "const char *")))
Niels Möller's avatar
Niels Möller committed
129 130
*/

131
static struct lsh_transport_config *
Niels Möller's avatar
Niels Möller committed
132
make_lsh_transport_config(void)
Niels Möller's avatar
Niels Möller committed
133
{
134
  NEW(lsh_transport_config, self);
135
  init_transport_context (&self->super, 0);
Niels Möller's avatar
Niels Möller committed
136

137
  self->home = getenv(ENV_HOME);
Niels Möller's avatar
Niels Möller committed
138 139 140 141 142
  if (!self->home)
    {
      werror("No home directory. Please set HOME in the environment.");
      return NULL;
    }
143

144 145
  self->algorithms = make_algorithms_options(self->super.algorithms);

146 147
  self->werror_config = make_werror_config();

148
  self->signature_algorithms = all_signature_algorithms();
149 150

  self->host_db = make_lsh_transport_lookup_verifier(self);
151 152

  ALIST_SET(self->super.algorithms, ATOM_DIFFIE_HELLMAN_GROUP14_SHA1,
Niels Möller's avatar
Niels Möller committed
153
	    &make_client_dh_exchange(make_dh_group14(&nettle_sha1),
154
				     &self->host_db->super)->super);
155
  ALIST_SET(self->super.algorithms, ATOM_DIFFIE_HELLMAN_GROUP1_SHA1,
Niels Möller's avatar
Niels Möller committed
156
	    &make_client_dh_exchange(make_dh_group1(&nettle_sha1),
157
				     &self->host_db->super)->super);
158
  
Niels Möller's avatar
Niels Möller committed
159 160
  self->sloppy = 0;
  self->capture_file = NULL;
161 162
  self->capture_fd = -1;
  self->host_acls = NULL;
Niels Möller's avatar
Niels Möller committed
163 164 165 166

  self->port = "22";
  self->target = NULL;

167
  self->userauth = 1;
Niels Möller's avatar
Niels Möller committed
168 169
  USER_NAME_FROM_ENV(self->user);
  self->identity = NULL;
170
  self->keypair = NULL;
Niels Möller's avatar
Niels Möller committed
171

172 173
  self->service = "ssh-connection";
  
Niels Möller's avatar
Niels Möller committed
174 175 176
  return self;
}

177 178 179
/* GABA:
   (class
     (name lsh_transport_connection)
180
     (super transport_forward)
181
     (vars
182 183 184 185 186 187
       (state . "enum lsh_transport_state")
       ;; For password authentication
       (config object lsh_transport_config)
       (tried_empty_password . unsigned)
       ;; For keyboard-interactive authentication
       (expect_info_request . int)))
188 189 190
*/

static void
191 192 193 194 195
kill_lsh_transport_connection(struct resource *s UNUSED)
{
  exit(EXIT_SUCCESS);
}

196
static void
197 198
lsh_transport_event_handler(struct transport_connection *connection,
			    enum transport_event event)
199
{
200
  CAST(lsh_transport_connection, self, connection);
201 202
  CAST(lsh_transport_config, config, connection->ctx);

203
  switch (event)
204
    {
205 206
    case TRANSPORT_EVENT_START_APPLICATION:
    case TRANSPORT_EVENT_STOP_APPLICATION:
207 208 209
    case TRANSPORT_EVENT_CLOSE:
    case TRANSPORT_EVENT_PUSH:
      /* Do nothing */
210
      break;
211

212
    case TRANSPORT_EVENT_KEYEXCHANGE_COMPLETE:
213 214 215
      assert(self->state == STATE_HANDSHAKE);

      transport_send_packet(
Niels Möller's avatar
Niels Möller committed
216
	connection, TRANSPORT_WRITE_FLAG_PUSH,
217 218 219 220
	ssh_format("%c%z", SSH_MSG_SERVICE_REQUEST,
		   config->requested_service));

      self->state = STATE_SERVICE_REQUEST;
221 222
      connection->packet_handler = lsh_transport_packet_handler;
      break;
223
    }
224
 }
225

226 227 228 229 230
static void
lsh_transport_service_packet_handler(struct transport_forward *self,
				     uint32_t length, const uint8_t *data)
{
  assert(length > 0);
231 232
  debug("lsh_transport_service_packet_handler: %xs\n",
	length, data);
233 234 235 236 237 238 239 240 241 242 243 244
  if (data[0] == SSH_LSH_RANDOM_REQUEST)
    {
      struct simple_buffer buffer;
      uint32_t random_length;
      simple_buffer_init(&buffer, length-1, data+1);

      if (parse_uint32(&buffer, &random_length)
	  && parse_eod(&buffer))
	{
	  struct lsh_string *response;
	  uint32_t pos;

245 246
	  if (random_length > RANDOM_REQUEST_MAX)
	    random_length = RANDOM_REQUEST_MAX;
247

248
	  response = ssh_format("%c%r", SSH_LSH_RANDOM_REPLY, random_length, &pos);
249
	  lsh_string_write_random(response, pos, random_length);
250 251 252 253 254 255 256 257 258 259 260 261 262

	  /* Note: Bogus sequence number. */
	  transport_forward_service_packet(self, 0, STRING_LD(response));
	  lsh_string_free(response);
	}
      else
	transport_disconnect(&self->super, SSH_DISCONNECT_BY_APPLICATION,
			     "Received invalid packet from service layer.");
    }
  else
    transport_forward_packet(self, length, data);  
}

263 264 265 266
static struct lsh_transport_connection *
make_lsh_transport_connection(struct lsh_transport_config *config, int fd)
{
  NEW(lsh_transport_connection, self);
267 268
  /* FIXME: Packet handler that implements SSH_LSH_RANDOM_REQUEST,
     needed for X forwarding. */
269 270
  init_transport_forward(&self->super, kill_lsh_transport_connection,
			 &config->super, fd, fd,
271 272
			 lsh_transport_event_handler,
			 lsh_transport_service_packet_handler);
273 274

  self->state = STATE_HANDSHAKE;
275 276 277
  self->config = config;
  self->tried_empty_password = 0;
  self->expect_info_request = 0;
278 279 280
  return self;
}

281

282
static void
283 284
lsh_transport_line_handler(struct transport_connection *connection,
			   uint32_t length, const uint8_t *line)
285
{
286
  if (length < 4 || 0 != memcmp(line, "SSH-", 4))
287
    {
288 289
      /* A banner line */
      werror("%ps\n", length, line);
290 291
      return;
    }
292 293 294 295
  verbose("Server version string: %ps\n", length, line);

  /* Line must start with "SSH-2.0-". */
  if (length < 8 || 0 != memcmp(line, "SSH-2.0-", 4))
296
    {
297 298
      transport_disconnect(connection, 0, "Bad version string.");
      return;
299
    }
300 301 302
  
  connection->kex.version[1] = ssh_format("%ls", length, line);
  connection->line_handler = NULL;
303 304
}

305
/* Handles decrypted packets. Replaced after userauth is complete. */
306 307 308
static int
lsh_transport_packet_handler(struct transport_connection *connection,
			     uint32_t seqno UNUSED, uint32_t length, const uint8_t *packet)
309
{
310
  CAST(lsh_transport_connection, self, connection);
311
  CAST(lsh_transport_config, config, connection->ctx);
312
  
313
  uint8_t msg;
314

315
  debug("Received packet: %xs\n", length, packet);
316
  assert(length > 0);
317

318
  msg = packet[0];
319 320 321 322

  switch(self->state)
    {
    case STATE_HANDSHAKE:
323
      fatal("Internal error.\n");
324 325 326 327 328 329 330 331 332 333
    case STATE_SERVICE_REQUEST:
      if (msg == SSH_MSG_SERVICE_ACCEPT)
	{
	  struct simple_buffer buffer;
	  uint32_t service_length;
	  const uint8_t *service;
	  
	  simple_buffer_init(&buffer, length-1, packet + 1);
	  if (parse_string(&buffer, &service_length, &service)
	      && parse_eod(&buffer)
334 335 336
	      && service_length == strlen(config->requested_service)
	      && 0 == memcmp(service, config->requested_service,
			     service_length))
337 338 339 340 341 342 343 344 345 346 347 348
	    {
	      if (config->userauth)
		{
		  self->state = STATE_USERAUTH;
		  start_userauth(self);
		}
	      else
		{
		  self->state = STATE_SERVICE_FORWARD;
		  start_service(self);
		}
	    }
349 350 351
	  else
	    transport_protocol_error(connection,
				     "Invalid SERVICE_ACCEPT message");
352 353 354 355 356 357 358
	}
      else
	transport_protocol_error(connection,
				 "Expected SERVICE_ACCEPT message");
      break;

    case STATE_USERAUTH:
359 360 361 362 363
      switch (msg)
	{
	case SSH_MSG_USERAUTH_SUCCESS:
	  if (length == 1)
	    {
364
	      verbose("Received USERAUTH_SUCCESS.\n");
365 366 367 368 369 370
	      self->state = STATE_SERVICE_FORWARD;
	      start_service(self);	      
	    }
	  else
	    transport_protocol_error(connection,
				     "Invalid USERAUTH_SUCCESS message");
371 372
	  break;

373
	case SSH_MSG_USERAUTH_BANNER:
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
	  {
	    struct simple_buffer buffer;
	    uint32_t msg_length;
	    const uint8_t *msg;
	    uint32_t language_length;
	    const uint8_t *language;
	    
	    simple_buffer_init(&buffer, length-1, packet + 1);
	    if (parse_string(&buffer, &msg_length, &msg)
		&& parse_string(&buffer, &language_length, &language)
		&& parse_eod(&buffer))
	      {
		/* Ignores the language tag */
		werror("%ups", msg_length, msg);
	      }
	    else
	      transport_protocol_error(connection,
				       "Invalid USERAUTH_BANNER message");
	    break;
	  }
394 395 396 397 398 399 400 401 402 403
	case SSH_MSG_USERAUTH_FAILURE:
	  {
	    struct simple_buffer buffer;
	    struct int_list *methods;
	    int partial;
	    simple_buffer_init(&buffer, length-1, packet + 1);
	    if ( (methods = parse_atom_list(&buffer, 17))
		 && parse_boolean(&buffer, &partial)
		 && parse_eod(&buffer))
	      {
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
		int pending = 0;

		self->expect_info_request = 0;

		if (partial)
		  /* Doesn't help us */
		  werror("Received SSH_MSH_USERAUTH_FAILURE "
			 "indicating partial success.\n");

		if (int_list_member (methods, ATOM_PASSWORD))
		  pending = try_password_auth(self);
		else if (int_list_member (methods, ATOM_KEYBOARD_INTERACTIVE))
		  pending = try_keyboard_interactive_auth(self);

		if (!pending)
		  transport_disconnect(connection,
				       SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
				       "No more auth methods available");
422 423 424 425
	      }
	    else
	      transport_protocol_error(connection,
				       "Invalid USERAUTH_FAILURE message");
426
	    break;
427
	  }
428 429 430 431 432 433 434 435 436 437 438 439
	case SSH_MSG_USERAUTH_INFO_REQUEST:
	  if (self->expect_info_request)
	    {
	      struct simple_buffer buffer;
	      simple_buffer_init(&buffer, length-1, packet + 1);
	      send_userauth_info_response(self, &buffer);
	    }
	  else
	    transport_protocol_error(connection,
				     "Unexpected USERAUTH_INFO_REQUEST");
	  break;

440
	default:
Niels Möller's avatar
Niels Möller committed
441
	  transport_send_packet(connection, TRANSPORT_WRITE_FLAG_PUSH,
442 443
				format_unimplemented(seqno));	  
	}
444 445 446 447
      break;
    case STATE_SERVICE_FORWARD:
      break;
    }
448

449
  return 1;
450 451
}

452
static void
453
start_userauth(struct lsh_transport_connection *self)
454
{
455
  CAST(lsh_transport_config, config, self->super.super.ctx);
456 457 458 459 460

  if (config->keypair)
    {
      struct keypair *key = config->keypair;
      
461
      /* Generates signature straight away. */
462 463 464 465 466 467 468 469 470 471 472 473

      /* The part of the request we sign */
      struct lsh_string *request
	= ssh_format("%c%z%z%a%c%a%S",
		     SSH_MSG_USERAUTH_REQUEST,
		     config->user,
		     config->service,
		     ATOM_PUBLICKEY,
		     1,
		     key->type, key->public);

      struct lsh_string *signed_data
474
	= ssh_format("%S%lS", self->super.super.session_id, request);
475

476 477 478 479 480 481 482 483 484 485 486
      struct lsh_string *signature = SIGN(key->private, key->type,
					  lsh_string_length(signed_data),
					  lsh_string_data(signed_data));
      lsh_string_free(signed_data);

      if (!signature)
	{
	  werror("Signing using the private key failed, RSA key too small!\n");
	  lsh_string_free (request);
	  goto no_pubkey;
	}
487 488
      request =	ssh_format("%flS%fS", 
			   request, 
489
			   signature);
490

491 492
      verbose("Requesting authentication of user `%z' using the `publickey' method.\n",
	      config->user);
493

Niels Möller's avatar
Niels Möller committed
494
      transport_send_packet(&self->super.super, TRANSPORT_WRITE_FLAG_PUSH,
495 496 497 498
			    request);
    }
  else
    {
499
    no_pubkey:
500
      /* Find out whether or not password authentication is supported. */
Niels Möller's avatar
Niels Möller committed
501
      transport_send_packet(&self->super.super, TRANSPORT_WRITE_FLAG_PUSH,
502 503 504 505 506 507
			    ssh_format("%c%z%z%a",
				       SSH_MSG_USERAUTH_REQUEST,
				       config->user,
				       config->service,
				       ATOM_NONE));
    }
508 509
}

510 511 512 513 514 515
#define MAX_PASSWORD 100

static int
try_password_auth(struct lsh_transport_connection *self)
{
  struct lsh_string *password
516
    = interact_read_password(ssh_format("Password for %lz: ",
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
					self->config->user));
  if (!password)
    return 0;

  /* Password empty? */
  if (!lsh_string_length(password))
    {
      /* NOTE: At least on some systems, the getpass function
       * sets the tty to raw mode, disabling ^C, ^D and the like.
       *
       * To be a little friendlier, we stop asking if the user
       * gives us the empty password twice.
       */
      if (self->tried_empty_password++)
	{
	  lsh_string_free (password);
	  return 0;
	}
    }

537 538
  random_add(RANDOM_SOURCE_SECRET, STRING_LD(password));

539 540
  verbose("Requesting authentication of user `%z' using the `password' method.\n",
	  self->config->user);
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556

  transport_send_packet(&self->super.super, TRANSPORT_WRITE_FLAG_PUSH,
			ssh_format("%c%z%z%a%c%fS",
				   SSH_MSG_USERAUTH_REQUEST,
				   self->config->user,
				   self->config->service,
				   ATOM_PASSWORD, 0, password));
  return 1;
}

#define KBDINTERACT_MAX_PROMPTS 17
#define KBDINTERACT_MAX_LENGTH 200

static int
try_keyboard_interactive_auth(struct lsh_transport_connection *self)
{
557 558 559
  verbose("Requesting authentication of user `%z' using the `keyboard-interactive' method.\n",
	  self->config->user);

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
  transport_send_packet(&self->super.super, TRANSPORT_WRITE_FLAG_PUSH,
			ssh_format("%c%z%z%a%i%i",
				   SSH_MSG_USERAUTH_REQUEST,
				   self->config->user,
				   self->config->service,
				   /* Empty language tag and submethods */
				   ATOM_KEYBOARD_INTERACTIVE, 0, 0));

  self->expect_info_request = 1;
  return 1;
  
}

static struct lsh_string *
format_userauth_info_response(struct interact_dialog *dialog)
{
  uint32_t length;
  unsigned i;
  struct lsh_string *msg;
  uint32_t p;

  /* We need to format a message containing a variable number of
     strings. */

  /* First convert to utf8 */
  for (i = 0; i < dialog->nprompt; i++)
    dialog->response[i] = local_to_utf8(dialog->response[i], 1);
  
  for (i = length = 0; i < dialog->nprompt; i++)
    length += lsh_string_length(dialog->response[i]);

  msg = ssh_format("%c%i%lr", SSH_MSG_USERAUTH_INFO_RESPONSE, dialog->nprompt,
		   length + 4 * dialog->nprompt, &p);

  for (i = 0; i < dialog->nprompt; i++)
    {
      struct lsh_string *r = dialog->response[i];
      uint32_t rlength = lsh_string_length(r);
      lsh_string_write_uint32(msg, p, rlength);
      p += 4;
      lsh_string_write(msg, p, rlength, lsh_string_data(r));
      p += rlength;
    }
  assert (p == lsh_string_length(msg));
  
  return msg;
}

static void
send_userauth_info_response(struct lsh_transport_connection *self,
			    struct simple_buffer *buffer)
{
  const uint8_t *name;
  uint32_t name_length;

  const uint8_t *instruction;
  uint32_t instruction_length;
  /* Deprecated and ignored */
  const uint8_t *language;
  uint32_t language_length;

  /* Typed as "int" in the spec. Hope that means uint32_t? */  
  uint32_t nprompt; 
  
  if (parse_string(buffer, &name_length, &name)
      && parse_string(buffer, &instruction_length, &instruction)
      && parse_string(buffer, &language_length, &language)
      && parse_uint32(buffer, &nprompt))
    {
      struct interact_dialog *dialog;
      unsigned i;
      
      if (nprompt > KBDINTERACT_MAX_PROMPTS
	  || name_length > KBDINTERACT_MAX_LENGTH
	  || instruction_length > 10*KBDINTERACT_MAX_LENGTH)
	{
	too_large:
	  transport_disconnect(&self->super.super,
			       SSH_DISCONNECT_BY_APPLICATION,
			       "Dialog too large.");

	  return;
	}

      dialog = make_interact_dialog(nprompt);

      for (i = 0; i < nprompt; i++)
	{
	  const uint8_t *prompt;
	  uint32_t prompt_length;
	  struct lsh_string *s;

	  if (! (parse_string(buffer, &prompt_length, &prompt)
		 && parse_boolean(buffer, &dialog->echo[i])))
	    {
	      KILL(dialog);
	      goto error;
	    }

	  if (prompt_length > KBDINTERACT_MAX_LENGTH)
	    {
	      KILL(dialog);
	      goto too_large;
	    }
	  s = low_utf8_to_local(prompt_length, prompt,
				utf8_replace | utf8_paranoid);
	  if (!s)
	    goto error;
	  
	  dialog->prompt[i] = s;
	}
      
      dialog->instruction
	= low_utf8_to_local(instruction_length, instruction,
			    utf8_replace | utf8_paranoid);
      
      if (!dialog->instruction)
	goto error;

      if (name_length > 0)
	{
	  /* Prepend to instruction */
	  struct lsh_string *s;
      
	  s = low_utf8_to_local(name_length, name,
				utf8_replace | utf8_paranoid);
	  if (!s)
	    goto error;

	  dialog->instruction = ssh_format("%lfS\n\n%lfS\n",
					   s, dialog->instruction);
	}
      else
	dialog->instruction = ssh_format("%lfS\n", dialog->instruction);

695
      if (!interact_dialog(dialog))
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
	{
	  transport_disconnect(&self->super.super,
			       SSH_DISCONNECT_AUTH_CANCELLED_BY_USER,
			       "Cancelled");

	  return;
	}
      
      transport_send_packet(&self->super.super, TRANSPORT_WRITE_FLAG_PUSH,
			    format_userauth_info_response(dialog));
    }
  else
    {
    error:
      transport_protocol_error(&self->super.super,
			       "Invalid USERAUTH_INFO_REQUEST");
    }
}

715 716 717
static void
start_service(struct lsh_transport_connection *self UNUSED)
{
718 719 720
  static const char hello[LSH_HELLO_LINE_LENGTH]
    = "LSH " STRINGIZE(LSH_HELLO_VERSION) " OK lsh-transport";

721 722 723 724 725
  /* Setting stdio fd:s to non-blocking mode is unfriendly in a
     general purpose program, that may share stdin and stdout with
     other processes. But we expect to get our own exclusive pipe when
     we are started by lsh. */

726 727 728
  /* Write hello message */
  if (!write_raw (STDOUT_FILENO, sizeof(hello), hello))
    {
729
      werror ("Writing local hello message failed: %e.\n", errno);
730 731 732
      exit (EXIT_FAILURE);
    }

733 734 735 736 737 738
  /* FIXME: We can probably get by with only stdout non-blocking. */
  io_set_nonblocking(STDIN_FILENO);
  io_set_nonblocking(STDOUT_FILENO);
  
  /* Replaces event_handler and packet_handler. */  
  transport_forward_setup(&self->super, STDIN_FILENO, STDOUT_FILENO);
739 740
}

Niels Möller's avatar
Niels Möller committed
741
static int
742
lsh_connect(struct lsh_transport_config *config)
Niels Möller's avatar
Niels Möller committed
743
{
744 745
  struct lsh_transport_connection *connection;

Niels Möller's avatar
Niels Möller committed
746 747 748 749 750
  struct addrinfo hints;
  struct addrinfo *list;
  struct addrinfo *p;
  int err;
  int s = -1;
751
  
Niels Möller's avatar
Niels Möller committed
752 753 754 755
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

756
  verbose("Connecting to %z:%z....\n", config->target, config->port);
757
  
758
  err = getaddrinfo(config->target, config->port, &hints, &list);
Niels Möller's avatar
Niels Möller committed
759 760 761
  if (err)
    {
      werror("Could not resolv address `%z', port %z: %z\n",
762
	     config->target, config->port, gai_strerror(err));
Niels Möller's avatar
Niels Möller committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
      return 0;
    }

  for (p = list; p; p = p->ai_next)
    {
      s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
      if (s < 0)
	continue;

      if (connect(s, p->ai_addr, p->ai_addrlen) == 0)
	break;

      if (p->ai_next)
	werror("Connection failed, trying next address.\n");
      else
	werror("Connection failed.\n");
      close(s);
      s = -1;
    }
  
  freeaddrinfo(list);

785 786
  if (s < 0)
    return 0;
787 788 789

  verbose("... connected.\n");

Niels Möller's avatar
Niels Möller committed
790
  /* We keep the socket in blocking mode */
791
  connection = make_lsh_transport_connection(config, s);
792
  gc_global(&connection->super.super.super);
793

794
  transport_handshake(&connection->super.super,
795
		      make_string(CLIENT_VERSION_LINE),
796
		      lsh_transport_line_handler);
797 798

  return 1;
Niels Möller's avatar
Niels Möller committed
799 800
}

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
/* For now, supports only a single key */
static struct keypair *
read_user_key(struct lsh_transport_config *config)
{
  struct lsh_string *tmp = NULL;
  struct lsh_string *contents;
  int fd;
  int algorithm_name;
  
  struct signer *s;
  struct verifier *v;

  trace("read_user_key\n");
  
  if (!config->userauth)
    return NULL;

  if (config->identity)
    {
      fd = open(config->identity, O_RDONLY);
      if (fd < 0)
822
	verbose("Failed to open `%z' for reading: %e.\n",
823 824 825 826 827 828 829
		config->identity, errno);
    }
  else
    {
      tmp = ssh_format("%lz/.lsh/identity", config->home);
      fd = open(lsh_get_cstring(tmp), O_RDONLY);
      if (fd < 0)
830
	werror("Failed to open `%S' for reading: %e.\n",
831 832 833 834 835 836 837 838 839 840 841
		tmp, errno);
      lsh_string_free(tmp);
    }

  if (fd < 0)
    return NULL;

  contents = io_read_file_raw(fd, 2000);

  if (!contents)
    {
842
      werror("Failed to read private key file: %e.\n", errno);
843 844 845 846 847 848 849 850 851
      close(fd);

      return NULL;
    }

  close(fd);

  /* FIXME: We should read the public key somehow, and decrypt the
     private key only if it is needed. */
852 853
  /* FIXME: Mix in the passphrase using
     random_add(RANDOM_SOURCE_SECRET, ...) */
854 855 856 857 858 859 860 861
  contents
    = spki_pkcs5_decrypt(alist_select_l(config->super.algorithms,
					2, ATOM_HMAC_SHA1, ATOM_HMAC_MD5, -1),
			 alist_select_l(config->super.algorithms,
					4, ATOM_3DES_CBC, ATOM_BLOWFISH_CBC,
					ATOM_TWOFISH_CBC, ATOM_AES256_CBC, -1),
			 contents);
  if (!contents)
862
    {
863 864
      werror("Decrypting private key failed.\n");
      return NULL;;
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
    }
  
  s = spki_make_signer(config->signature_algorithms, contents,
		       &algorithm_name);

  lsh_string_free(contents);
  
  if (!s)
    {
      werror("Invalid private key.\n");
      return NULL;
    }
  
  v = SIGNER_GET_VERIFIER(s);
  assert(v);

  /* FIXME: SPKI support */

  /* Test key here? */  
  switch (algorithm_name)
    {	  
    case ATOM_DSA:
      return make_keypair(ATOM_SSH_DSS,
			  PUBLIC_KEY(v), s);
889 890 891 892
    case ATOM_DSA_SHA256:
      return make_keypair(ATOM_SSH_DSA_SHA256_LOCAL,
			  PUBLIC_KEY(v), s);
      
893 894 895 896 897 898 899 900 901 902 903 904 905
    case ATOM_RSA_PKCS1:
    case ATOM_RSA_PKCS1_SHA1:
      return make_keypair(ATOM_SSH_RSA,
			  PUBLIC_KEY(v), s);
    case ATOM_RSA_PKCS1_MD5:
      werror("Key type rsa-pkcs1-md5 not supported.\n");
      return NULL;

    default:
      fatal("Internal error!\n");
    }
}

906 907 908 909 910 911 912
/* Maps a host key to a (trusted) verifier object. */

/* GABA:
   (class
     (name lsh_transport_lookup_verifier)
     (super lookup_verifier)
     (vars
913 914 915 916
       (config object lsh_transport_config)
       (db object spki_context)
       (access string)
       ; For fingerprinting
Niels Möller's avatar
Niels Möller committed
917
       (hash . "const struct nettle_hash *")))
918 919 920
*/

static struct verifier *
921 922 923
lsh_transport_lookup_verifier(struct lookup_verifier *s,
			      int hostkey_algorithm,
			      uint32_t key_length, const uint8_t *key)
924
{
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
  CAST(lsh_transport_lookup_verifier, self, s);
  struct spki_principal *subject;

  switch (hostkey_algorithm)
    {
    case ATOM_SSH_DSS:
      {	
	struct lsh_string *spki_key;
	struct verifier *v = make_ssh_dss_verifier(key_length, key);

	if (!v)
	  {
	    werror("do_lsh_lookup: Invalid ssh-dss key.\n");
	    return NULL;
	  }

	spki_key = PUBLIC_SPKI_KEY(v, 0);

943
	subject = spki_lookup_key(self->db, STRING_LD(spki_key), v);
944 945 946 947 948 949
	assert(subject);
	assert(subject->verifier);

	lsh_string_free(spki_key);
	break;
      }
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971

    case ATOM_SSH_DSA_SHA256_LOCAL:
      {
	struct lsh_string *spki_key;
	struct verifier *v = make_ssh_dsa_sha256_verifier(key_length, key);

	if (!v)
	  {
	    werror("do_lsh_lookup: Invalid ssh-dsa-sha256 key.\n");
	    return NULL;
	  }

	spki_key = PUBLIC_SPKI_KEY(v, 0);

	subject = spki_lookup_key(self->db, STRING_LD(spki_key), v);
	assert(subject);
	assert(subject->verifier);

	lsh_string_free(spki_key);
	break;
      }

972 973 974 975 976 977 978 979 980 981 982 983
    case ATOM_SSH_RSA:
      {
	struct lsh_string *spki_key;
	struct verifier *v = make_ssh_rsa_verifier(key_length, key);

	if (!v)
	  {
	    werror("do_lsh_lookup: Invalid ssh-rsa key.\n");
	    return NULL;
	  }

	spki_key = PUBLIC_SPKI_KEY(v, 0);
984
	subject = spki_lookup_key(self->db, STRING_LD(spki_key), v);
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
	assert(subject);
	assert(subject->verifier);

	lsh_string_free(spki_key);
	break;
      }
      
      /* It doesn't matter here which flavour of SPKI is used. */
    case ATOM_SPKI_SIGN_RSA:
    case ATOM_SPKI_SIGN_DSS:
      {
	subject = spki_lookup(self->db, key_length, key, NULL);
	if (!subject)
	  {
	    werror("do_lsh_lookup: Invalid spki key.\n");
	    return NULL;
	  }
	if (!subject->verifier)
	  {
	    werror("do_lsh_lookup: Valid SPKI subject, but no key available.\n");
	    return NULL;
	  }
	break;
      }
    default:
      werror("do_lsh_lookup: Unknown key type. Should not happen!\n");
      return NULL;
    }

  assert(subject->key);
  
  /* Check authorization */

  if (spki_authorize(self->db, subject, time(NULL), self->access))
    {
      verbose("SPKI host authorization successful!\n");
    }
  else
    {
      struct lsh_string *acl;
1025 1026
      struct lsh_string *fingerprint;
      struct lsh_string *babble;
1027 1028 1029 1030 1031 1032 1033
      
      verbose("SPKI authorization failed.\n");
      if (!self->config->sloppy)
	{
	  werror("Server's hostkey is not trusted. Disconnecting.\n");
	  return NULL;
	}
1034 1035

      fingerprint = 
1036 1037
	    lsh_string_colonize( 
				ssh_format( "%lfxS", 
Niels Möller's avatar
Niels Möller committed
1038
					    hash_string_l(&nettle_md5,
1039 1040 1041 1042 1043 1044
							  key_length, key)
					    ), 
				2, 
				1  
				);

1045
      babble = 
1046
	    lsh_string_bubblebabble( 
Niels Möller's avatar
Niels Möller committed
1047
				    hash_string_l(&nettle_sha1,
1048 1049 1050
						  key_length, key),
				    1 
				    );
1051 1052 1053

      /* Ok, let's see if we want to use this untrusted key. Display
	 fingerprint. */
1054
      if (!werror_quiet_p() && !interact_yes_or_no(
1055
	       ssh_format("Received unauthenticated key for host %lz\n"
1056
			  "Key details:\n"
1057 1058
			  "Bubble Babble: %lS\n"
			  "Fingerprint:   %lS\n"
1059
			  "Do you trust this key? (y/n) ",
1060 1061 1062 1063 1064
			  self->config->target, babble, fingerprint), 0))
	{
	  lsh_string_free(fingerprint);
	  lsh_string_free(babble);
	  return NULL;
1065 1066 1067 1068 1069 1070 1071 1072 1073
	}

      acl = lsh_string_format_sexp(0, "(acl(entry(subject%l)%l))",
				   subject->key_length, subject->key,
				   STRING_LD(self->access));
      
      /* FIXME: Seems awkward to pick the acl apart again. */
      
      /* Remember this key. We don't want to ask again for key re-exchange */
1074
      spki_add_acls(self->db, STRING_LD(acl));
1075

1076 1077
      /* FIXME: When --host-db-update is given explicitly together
	 with -q, *always* append the new acl to the given file? */
1078 1079 1080
      if (self->config->capture_fd >= 0
	  && interact_yes_or_no(ssh_format("Remember key and trust it in the future? (y/n) "),
				0))
1081
	{
1082
	  /* Write an ACL to disk. */
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
	  time_t now = time(NULL);
	  const char *sexp_conv;
	  const char *args[] = { "sexp-conv", "-s", "advanced", "--lock", NULL };

	  struct lsh_string *entry
	    = ssh_format("\n; ACL for host %lz\n"
			 "; Date: %lz\n"
			 "; Fingerprint: %lS\n"
			 "; Bubble-babble: %lS\n"
			 "%lS\n",
			 self->config->target, ctime(&now), fingerprint, babble, acl);
	  
	  GET_FILE_ENV(sexp_conv, SEXP_CONV);
1096

1097 1098
	  if (!lsh_popen_write(sexp_conv, args, self->config->capture_fd, STRING_LD(entry)))
	    werror("Writing acl entry failed.\n");
1099

1100
	  lsh_string_free(entry);
1101
	}
1102 1103 1104
      lsh_string_free(fingerprint);
      lsh_string_free(babble);
      lsh_string_free(acl);
1105
    } 
1106
  return subject->verifier;
1107 1108
}

1109 1110
static struct lsh_transport_lookup_verifier *
make_lsh_transport_lookup_verifier(struct lsh_transport_config *config)
1111 1112 1113 1114
{
  NEW(lsh_transport_lookup_verifier, self);
  self->super.lookup = lsh_transport_lookup_verifier;
  self->config = config;
1115 1116
  self->db = NULL;
  self->access = NULL;
Niels Möller's avatar
Niels Möller committed
1117
  self->hash = &nettle_sha1;
1118
  
1119
  return self;
1120 1121
}

1122 1123 1124
/* Initialize the spki database and the access tag. Called after
   options parsing. */
static void
1125 1126
read_host_acls(struct lsh_transport_lookup_verifier *self,
	       const char *file)
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
{
  struct lsh_string *contents;
  int fd;
  const char *sexp_conv;
  const char *args[] = { "sexp-conv", "-s", "canonical", NULL };

  assert(self->config->target);
  
  self->access = make_ssh_hostkey_tag(self->config->target);
  self->db = make_spki_context(self->config->signature_algorithms);
  
1138 1139
  fd = open(file, O_RDONLY);
  if (fd < 0)
1140
    {
1141
      if (errno == ENOENT)
1142
	{
1143
	  verbose("Failed to open `%z' for reading: %e.\n", file, errno);
1144 1145 1146 1147
	  if (!self->config->host_acls)
	    {
	      struct stat sbuf;
	      struct lsh_string *known_hosts;
1148

1149
	      known_hosts = ssh_format("%lz/.lsh/known_hosts", self->config->home);
1150

1151 1152 1153 1154 1155 1156 1157 1158
	      if (stat(lsh_get_cstring(known_hosts), &sbuf) == 0)
		{
		  werror("You have an old known-hosts file `%S'.\n"
			 "To work with lsh-2.0 and alter, run the lsh-upgrade script,\n"
			 "which will convert that to a new host-acls file.\n",
			 known_hosts);
		}
	      lsh_string_free(known_hosts);
1159 1160
	    }
	}
1161
      else
1162
	werror("Failed to open `%z' for reading: %e.\n",
1163 1164
	       file, errno);
      return;
1165 1166
    }

1167
  GET_FILE_ENV(sexp_conv, SEXP_CONV);
1168 1169 1170 1171 1172
  
  contents = lsh_popen_read(sexp_conv, args, fd, 5000);
  
  if (!contents)
    {
1173
      werror("Failed to read host-acls file `%z': %e.\n",
1174
	     file, errno);
1175 1176 1177 1178 1179 1180
      close(fd);
      return;
    }

  close(fd);

1181 1182 1183
  /* Ignores any error */
  spki_add_acls(self->db, STRING_LD(contents));

1184 1185
  lsh_string_free(contents);
}
1186

Niels Möller's avatar
Niels Möller committed
1187 1188 1189
/* Option parsing */

const char *argp_program_version