summaryrefslogtreecommitdiff
path: root/hostfile.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2015-01-26 03:04:45 +0000
committerDamien Miller <djm@mindrot.org>2015-01-27 00:00:57 +1100
commit8d4f87258f31cb6def9b3b55b6a7321d84728ff2 (patch)
treec98e66c1c0824f0b0e312d7b44d8eeac46265362 /hostfile.c
parent60b1825262b1f1e24fc72050b907189c92daf18e (diff)
upstream commit
Host key rotation support. Add a hostkeys@openssh.com protocol extension (global request) for a server to inform a client of all its available host key after authentication has completed. The client may record the keys in known_hosts, allowing it to upgrade to better host key algorithms and a server to gracefully rotate its keys. The client side of this is controlled by a UpdateHostkeys config option (default on). ok markus@
Diffstat (limited to 'hostfile.c')
-rw-r--r--hostfile.c206
1 files changed, 190 insertions, 16 deletions
diff --git a/hostfile.c b/hostfile.c
index ccb2af920..9de1b383b 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.c,v 1.61 2015/01/18 21:48:09 djm Exp $ */ 1/* $OpenBSD: hostfile.c,v 1.62 2015/01/26 03:04:45 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -39,6 +39,7 @@
39#include "includes.h" 39#include "includes.h"
40 40
41#include <sys/types.h> 41#include <sys/types.h>
42#include <sys/stat.h>
42 43
43#include <netinet/in.h> 44#include <netinet/in.h>
44 45
@@ -49,6 +50,7 @@
49#include <stdlib.h> 50#include <stdlib.h>
50#include <string.h> 51#include <string.h>
51#include <stdarg.h> 52#include <stdarg.h>
53#include <unistd.h>
52 54
53#include "xmalloc.h" 55#include "xmalloc.h"
54#include "match.h" 56#include "match.h"
@@ -430,6 +432,29 @@ lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
430 found) == HOST_FOUND); 432 found) == HOST_FOUND);
431} 433}
432 434
435static int
436write_host_entry(FILE *f, const char *host,
437 const struct sshkey *key, int store_hash)
438{
439 int r, success = 0;
440 char *hashed_host = NULL;
441
442 if (store_hash) {
443 if ((hashed_host = host_hash(host, NULL, 0)) == NULL) {
444 error("%s: host_hash failed", __func__);
445 return 0;
446 }
447 }
448 fprintf(f, "%s ", store_hash ? hashed_host : host);
449
450 if ((r = sshkey_write(key, f)) == 0)
451 success = 1;
452 else
453 error("%s: sshkey_write failed: %s", __func__, ssh_err(r));
454 fputc('\n', f);
455 return success;
456}
457
433/* 458/*
434 * Appends an entry to the host file. Returns false if the entry could not 459 * Appends an entry to the host file. Returns false if the entry could not
435 * be appended. 460 * be appended.
@@ -439,32 +464,181 @@ add_host_to_hostfile(const char *filename, const char *host,
439 const struct sshkey *key, int store_hash) 464 const struct sshkey *key, int store_hash)
440{ 465{
441 FILE *f; 466 FILE *f;
442 int r, success = 0; 467 int success;
443 char *hashed_host = NULL;
444 468
445 if (key == NULL) 469 if (key == NULL)
446 return 1; /* XXX ? */ 470 return 1; /* XXX ? */
447 f = fopen(filename, "a"); 471 f = fopen(filename, "a");
448 if (!f) 472 if (!f)
449 return 0; 473 return 0;
474 success = write_host_entry(f, host, key, store_hash);
475 fclose(f);
476 return success;
477}
450 478
451 if (store_hash) { 479struct host_delete_ctx {
452 if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { 480 FILE *out;
453 error("%s: host_hash failed", __func__); 481 int quiet;
454 fclose(f); 482 const char *host;
483 int *skip_keys;
484 struct sshkey * const *keys;
485 size_t nkeys;
486};
487
488static int
489host_delete(struct hostkey_foreach_line *l, void *_ctx)
490{
491 struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx;
492 int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO;
493 size_t i;
494
495 if (l->status == HKF_STATUS_HOST_MATCHED) {
496 if (l->marker != MRK_NONE) {
497 /* Don't remove CA and revocation lines */
498 fprintf(ctx->out, "%s\n", l->line);
499 return 0;
500 }
501
502 /* XXX might need a knob for this later */
503 /* Don't remove RSA1 keys */
504 if (l->key->type == KEY_RSA1) {
505 fprintf(ctx->out, "%s\n", l->line);
455 return 0; 506 return 0;
456 } 507 }
508
509 /*
510 * If this line contains one of the keys that we will be
511 * adding later, then don't change it and mark the key for
512 * skipping.
513 */
514 for (i = 0; i < ctx->nkeys; i++) {
515 if (sshkey_equal(ctx->keys[i], l->key)) {
516 ctx->skip_keys[i] = 1;
517 fprintf(ctx->out, "%s\n", l->line);
518 debug3("%s: %s key already at %s:%ld", __func__,
519 sshkey_type(l->key), l->path, l->linenum);
520 return 0;
521 }
522 }
523
524 /*
525 * Hostname matches and has no CA/revoke marker, delete it
526 * by *not* writing the line to ctx->out.
527 */
528 do_log2(loglevel, "%s%s%s:%ld: Host %s removed",
529 ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "",
530 l->path, l->linenum, ctx->host);
531 return 0;
457 } 532 }
458 fprintf(f, "%s ", store_hash ? hashed_host : host); 533 /* Retain non-matching hosts and invalid lines when deleting */
534 if (l->status == HKF_STATUS_INVALID) {
535 do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry",
536 ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "",
537 l->path, l->linenum);
538 }
539 fprintf(ctx->out, "%s\n", l->line);
540 return 0;
541}
459 542
460 if ((r = sshkey_write(key, f)) != 0) { 543int
461 error("%s: saving key in %s failed: %s", 544hostfile_replace_entries(const char *filename, const char *host,
462 __func__, filename, ssh_err(r)); 545 struct sshkey **keys, size_t nkeys, int store_hash, int quiet)
463 } else 546{
464 success = 1; 547 int r, fd, oerrno = 0;
465 fputc('\n', f); 548 int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO;
466 fclose(f); 549 struct host_delete_ctx ctx;
467 return success; 550 char *temp = NULL, *back = NULL;
551 mode_t omask;
552 size_t i;
553
554 memset(&ctx, 0, sizeof(ctx));
555 ctx.host = host;
556 ctx.quiet = quiet;
557 if ((ctx.skip_keys = calloc(nkeys, sizeof(*ctx.skip_keys))) == NULL)
558 return SSH_ERR_ALLOC_FAIL;
559 ctx.keys = keys;
560 ctx.nkeys = nkeys;
561
562 /*
563 * Prepare temporary file for in-place deletion.
564 */
565 if ((r = asprintf(&temp, "%s.XXXXXXXXXXX", filename)) < 0 ||
566 (r = asprintf(&back, "%s.old", filename)) < 0) {
567 r = SSH_ERR_ALLOC_FAIL;
568 goto fail;
569 }
570
571 omask = umask(077);
572 if ((fd = mkstemp(temp)) == -1) {
573 oerrno = errno;
574 error("%s: mkstemp: %s", __func__, strerror(oerrno));
575 r = SSH_ERR_SYSTEM_ERROR;
576 goto fail;
577 }
578 if ((ctx.out = fdopen(fd, "w")) == NULL) {
579 oerrno = errno;
580 close(fd);
581 error("%s: fdopen: %s", __func__, strerror(oerrno));
582 r = SSH_ERR_SYSTEM_ERROR;
583 goto fail;
584 }
585
586 /* Remove all entries for the specified host from the file */
587 if ((r = hostkeys_foreach(filename, host_delete, &ctx, host,
588 HKF_WANT_PARSE_KEY)) != 0) {
589 error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r));
590 goto fail;
591 }
592
593 /* Add the requested keys */
594 for (i = 0; i < nkeys; i++) {
595 if (ctx.skip_keys[i])
596 continue;
597 do_log2(loglevel, "%s%sadd %s key to %s",
598 quiet ? __func__ : "", quiet ? ": " : NULL,
599 sshkey_type(keys[i]), filename);
600 if (!write_host_entry(ctx.out, host, keys[i], store_hash)) {
601 r = SSH_ERR_INTERNAL_ERROR;
602 goto fail;
603 }
604 }
605 fclose(ctx.out);
606 ctx.out = NULL;
607
608 /* Backup the original file and replace it with the temporary */
609 if (unlink(back) == -1 && errno != ENOENT) {
610 oerrno = errno;
611 error("%s: unlink %.100s: %s", __func__, back, strerror(errno));
612 r = SSH_ERR_SYSTEM_ERROR;
613 goto fail;
614 }
615 if (link(filename, back) == -1) {
616 oerrno = errno;
617 error("%s: link %.100s to %.100s: %s", __func__, filename, back,
618 strerror(errno));
619 r = SSH_ERR_SYSTEM_ERROR;
620 goto fail;
621 }
622 if (rename(temp, filename) == -1) {
623 oerrno = errno;
624 error("%s: rename \"%s\" to \"%s\": %s", __func__,
625 temp, filename, strerror(errno));
626 r = SSH_ERR_SYSTEM_ERROR;
627 goto fail;
628 }
629 /* success */
630 r = 0;
631 fail:
632 if (temp != NULL && r != 0)
633 unlink(temp);
634 free(temp);
635 free(back);
636 if (ctx.out != NULL)
637 fclose(ctx.out);
638 free(ctx.skip_keys);
639 if (r == SSH_ERR_SYSTEM_ERROR)
640 errno = oerrno;
641 return r;
468} 642}
469 643
470static int 644static int