hostfile.c (7bc8c308) hostfile.c (06c9be66)
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/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved
6 * Functions for manipulating the known hosts files.
7 *
8 * As far as I am concerned, the code I have written for this software
9 * can be used freely for any purpose. Any derived versions of this

--- 22 unchanged lines hidden (view full) ---

32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <sys/types.h>
2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved
6 * Functions for manipulating the known hosts files.
7 *
8 * As far as I am concerned, the code I have written for this software
9 * can be used freely for any purpose. Any derived versions of this

--- 22 unchanged lines hidden (view full) ---

32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <sys/types.h>
40#include <sys/stat.h>
40
41#include <netinet/in.h>
42
43#include <errno.h>
44#include <resolv.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <stdarg.h>
41
42#include <netinet/in.h>
43
44#include <errno.h>
45#include <resolv.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <stdarg.h>
50#include <unistd.h>
49
50#include "xmalloc.h"
51#include "match.h"
52#include "sshkey.h"
53#include "hostfile.h"
54#include "log.h"
55#include "misc.h"
56#include "ssherr.h"

--- 365 unchanged lines hidden (view full) ---

422int
423lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
424 const struct hostkey_entry **found)
425{
426 return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
427 found) == HOST_FOUND);
428}
429
51
52#include "xmalloc.h"
53#include "match.h"
54#include "sshkey.h"
55#include "hostfile.h"
56#include "log.h"
57#include "misc.h"
58#include "ssherr.h"

--- 365 unchanged lines hidden (view full) ---

424int
425lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
426 const struct hostkey_entry **found)
427{
428 return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
429 found) == HOST_FOUND);
430}
431
432static int
433write_host_entry(FILE *f, const char *host,
434 const struct sshkey *key, int store_hash)
435{
436 int r, success = 0;
437 char *hashed_host = NULL;
438
439 if (store_hash) {
440 if ((hashed_host = host_hash(host, NULL, 0)) == NULL) {
441 error("%s: host_hash failed", __func__);
442 return 0;
443 }
444 }
445 fprintf(f, "%s ", store_hash ? hashed_host : host);
446
447 if ((r = sshkey_write(key, f)) == 0)
448 success = 1;
449 else
450 error("%s: sshkey_write failed: %s", __func__, ssh_err(r));
451 fputc('\n', f);
452 return success;
453}
454
430/*
431 * Appends an entry to the host file. Returns false if the entry could not
432 * be appended.
433 */
434int
435add_host_to_hostfile(const char *filename, const char *host,
436 const struct sshkey *key, int store_hash)
437{
438 FILE *f;
455/*
456 * Appends an entry to the host file. Returns false if the entry could not
457 * be appended.
458 */
459int
460add_host_to_hostfile(const char *filename, const char *host,
461 const struct sshkey *key, int store_hash)
462{
463 FILE *f;
439 int r, success = 0;
440 char *hashed_host = NULL;
464 int success;
441
442 if (key == NULL)
443 return 1; /* XXX ? */
444 f = fopen(filename, "a");
445 if (!f)
446 return 0;
465
466 if (key == NULL)
467 return 1; /* XXX ? */
468 f = fopen(filename, "a");
469 if (!f)
470 return 0;
471 success = write_host_entry(f, host, key, store_hash);
472 fclose(f);
473 return success;
474}
447
475
448 if (store_hash) {
449 if ((hashed_host = host_hash(host, NULL, 0)) == NULL) {
450 error("%s: host_hash failed", __func__);
451 fclose(f);
476struct host_delete_ctx {
477 FILE *out;
478 int quiet;
479 const char *host;
480 int *skip_keys;
481 struct sshkey * const *keys;
482 size_t nkeys;
483};
484
485static int
486host_delete(struct hostkey_foreach_line *l, void *_ctx)
487{
488 struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx;
489 int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO;
490 size_t i;
491
492 if (l->status == HKF_STATUS_HOST_MATCHED) {
493 if (l->marker != MRK_NONE) {
494 /* Don't remove CA and revocation lines */
495 fprintf(ctx->out, "%s\n", l->line);
452 return 0;
453 }
496 return 0;
497 }
498
499 /* XXX might need a knob for this later */
500 /* Don't remove RSA1 keys */
501 if (l->key->type == KEY_RSA1) {
502 fprintf(ctx->out, "%s\n", l->line);
503 return 0;
504 }
505
506 /*
507 * If this line contains one of the keys that we will be
508 * adding later, then don't change it and mark the key for
509 * skipping.
510 */
511 for (i = 0; i < ctx->nkeys; i++) {
512 if (sshkey_equal(ctx->keys[i], l->key)) {
513 ctx->skip_keys[i] = 1;
514 fprintf(ctx->out, "%s\n", l->line);
515 debug3("%s: %s key already at %s:%ld", __func__,
516 sshkey_type(l->key), l->path, l->linenum);
517 return 0;
518 }
519 }
520
521 /*
522 * Hostname matches and has no CA/revoke marker, delete it
523 * by *not* writing the line to ctx->out.
524 */
525 do_log2(loglevel, "%s%s%s:%ld: Host %s removed",
526 ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "",
527 l->path, l->linenum, ctx->host);
528 return 0;
454 }
529 }
455 fprintf(f, "%s ", store_hash ? hashed_host : host);
530 /* Retain non-matching hosts and invalid lines when deleting */
531 if (l->status == HKF_STATUS_INVALID) {
532 do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry",
533 ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "",
534 l->path, l->linenum);
535 }
536 fprintf(ctx->out, "%s\n", l->line);
537 return 0;
538}
456
539
457 if ((r = sshkey_write(key, f)) != 0) {
458 error("%s: saving key in %s failed: %s",
459 __func__, filename, ssh_err(r));
460 } else
461 success = 1;
462 fputc('\n', f);
463 fclose(f);
464 return success;
540int
541hostfile_replace_entries(const char *filename, const char *host,
542 struct sshkey **keys, size_t nkeys, int store_hash, int quiet)
543{
544 int r, fd, oerrno = 0;
545 int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO;
546 struct host_delete_ctx ctx;
547 char *temp = NULL, *back = NULL;
548 mode_t omask;
549 size_t i;
550
551 memset(&ctx, 0, sizeof(ctx));
552 ctx.host = host;
553 ctx.quiet = quiet;
554 if ((ctx.skip_keys = calloc(nkeys, sizeof(*ctx.skip_keys))) == NULL)
555 return SSH_ERR_ALLOC_FAIL;
556 ctx.keys = keys;
557 ctx.nkeys = nkeys;
558
559 /*
560 * Prepare temporary file for in-place deletion.
561 */
562 if ((r = asprintf(&temp, "%s.XXXXXXXXXXX", filename)) < 0 ||
563 (r = asprintf(&back, "%s.old", filename)) < 0) {
564 r = SSH_ERR_ALLOC_FAIL;
565 goto fail;
566 }
567
568 omask = umask(077);
569 if ((fd = mkstemp(temp)) == -1) {
570 oerrno = errno;
571 error("%s: mkstemp: %s", __func__, strerror(oerrno));
572 r = SSH_ERR_SYSTEM_ERROR;
573 goto fail;
574 }
575 if ((ctx.out = fdopen(fd, "w")) == NULL) {
576 oerrno = errno;
577 close(fd);
578 error("%s: fdopen: %s", __func__, strerror(oerrno));
579 r = SSH_ERR_SYSTEM_ERROR;
580 goto fail;
581 }
582
583 /* Remove all entries for the specified host from the file */
584 if ((r = hostkeys_foreach(filename, host_delete, &ctx, host,
585 HKF_WANT_PARSE_KEY)) != 0) {
586 error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r));
587 goto fail;
588 }
589
590 /* Add the requested keys */
591 for (i = 0; i < nkeys; i++) {
592 if (ctx.skip_keys[i])
593 continue;
594 do_log2(loglevel, "%s%sadd %s key to %s",
595 quiet ? __func__ : "", quiet ? ": " : NULL,
596 sshkey_type(keys[i]), filename);
597 if (!write_host_entry(ctx.out, host, keys[i], store_hash)) {
598 r = SSH_ERR_INTERNAL_ERROR;
599 goto fail;
600 }
601 }
602 fclose(ctx.out);
603 ctx.out = NULL;
604
605 /* Backup the original file and replace it with the temporary */
606 if (unlink(back) == -1 && errno != ENOENT) {
607 oerrno = errno;
608 error("%s: unlink %.100s: %s", __func__, back, strerror(errno));
609 r = SSH_ERR_SYSTEM_ERROR;
610 goto fail;
611 }
612 if (link(filename, back) == -1) {
613 oerrno = errno;
614 error("%s: link %.100s to %.100s: %s", __func__, filename, back,
615 strerror(errno));
616 r = SSH_ERR_SYSTEM_ERROR;
617 goto fail;
618 }
619 if (rename(temp, filename) == -1) {
620 oerrno = errno;
621 error("%s: rename \"%s\" to \"%s\": %s", __func__,
622 temp, filename, strerror(errno));
623 r = SSH_ERR_SYSTEM_ERROR;
624 goto fail;
625 }
626 /* success */
627 r = 0;
628 fail:
629 if (temp != NULL && r != 0)
630 unlink(temp);
631 free(temp);
632 free(back);
633 if (ctx.out != NULL)
634 fclose(ctx.out);
635 free(ctx.skip_keys);
636 if (r == SSH_ERR_SYSTEM_ERROR)
637 errno = oerrno;
638 return r;
465}
466
467static int
468match_maybe_hashed(const char *host, const char *names, int *was_hashed)
469{
470 int hashed = *names == HASH_DELIM;
471 const char *hashed_host;
472 size_t nlen = strlen(names);

--- 133 unchanged lines hidden ---
639}
640
641static int
642match_maybe_hashed(const char *host, const char *names, int *was_hashed)
643{
644 int hashed = *names == HASH_DELIM;
645 const char *hashed_host;
646 size_t nlen = strlen(names);

--- 133 unchanged lines hidden ---