Home | History | Annotate | Line # | Download | only in dist
hostfile.c revision 1.4.8.1
      1  1.4.8.1       tls /*	$NetBSD: hostfile.c,v 1.4.8.1 2014/08/19 23:45:24 tls Exp $	*/
      2  1.4.8.1       tls /* $OpenBSD: hostfile.c,v 1.52 2013/07/12 00:19:58 djm Exp $ */
      3      1.1  christos /*
      4      1.1  christos  * Author: Tatu Ylonen <ylo (at) cs.hut.fi>
      5      1.1  christos  * Copyright (c) 1995 Tatu Ylonen <ylo (at) cs.hut.fi>, Espoo, Finland
      6      1.1  christos  *                    All rights reserved
      7      1.1  christos  * Functions for manipulating the known hosts files.
      8      1.1  christos  *
      9      1.1  christos  * As far as I am concerned, the code I have written for this software
     10      1.1  christos  * can be used freely for any purpose.  Any derived versions of this
     11      1.1  christos  * software must be clearly marked as such, and if the derived work is
     12      1.1  christos  * incompatible with the protocol description in the RFC file, it must be
     13      1.1  christos  * called by a name other than "ssh" or "Secure Shell".
     14      1.1  christos  *
     15      1.1  christos  *
     16      1.1  christos  * Copyright (c) 1999, 2000 Markus Friedl.  All rights reserved.
     17      1.1  christos  * Copyright (c) 1999 Niels Provos.  All rights reserved.
     18      1.1  christos  *
     19      1.1  christos  * Redistribution and use in source and binary forms, with or without
     20      1.1  christos  * modification, are permitted provided that the following conditions
     21      1.1  christos  * are met:
     22      1.1  christos  * 1. Redistributions of source code must retain the above copyright
     23      1.1  christos  *    notice, this list of conditions and the following disclaimer.
     24      1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     25      1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     26      1.1  christos  *    documentation and/or other materials provided with the distribution.
     27      1.1  christos  *
     28      1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     29      1.1  christos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     30      1.1  christos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     31      1.1  christos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     32      1.1  christos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     33      1.1  christos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     34      1.1  christos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     35      1.1  christos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     36      1.1  christos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     37      1.1  christos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     38      1.1  christos  */
     39      1.1  christos 
     40      1.2  christos #include "includes.h"
     41  1.4.8.1       tls __RCSID("$NetBSD: hostfile.c,v 1.4.8.1 2014/08/19 23:45:24 tls Exp $");
     42      1.1  christos #include <sys/types.h>
     43      1.1  christos 
     44      1.1  christos #include <netinet/in.h>
     45      1.1  christos 
     46      1.1  christos #include <openssl/hmac.h>
     47      1.1  christos #include <openssl/sha.h>
     48      1.1  christos 
     49      1.1  christos #include <resolv.h>
     50      1.1  christos #include <stdio.h>
     51      1.1  christos #include <stdlib.h>
     52      1.1  christos #include <string.h>
     53      1.1  christos 
     54      1.1  christos #include "xmalloc.h"
     55      1.1  christos #include "match.h"
     56      1.1  christos #include "key.h"
     57      1.1  christos #include "hostfile.h"
     58      1.1  christos #include "log.h"
     59      1.4  christos #include "misc.h"
     60      1.4  christos 
     61      1.4  christos struct hostkeys {
     62      1.4  christos 	struct hostkey_entry *entries;
     63      1.4  christos 	u_int num_entries;
     64      1.4  christos };
     65      1.1  christos 
     66      1.1  christos static int
     67  1.4.8.1       tls extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len)
     68      1.1  christos {
     69      1.1  christos 	char *p, *b64salt;
     70      1.1  christos 	u_int b64len;
     71      1.1  christos 	int ret;
     72      1.1  christos 
     73      1.1  christos 	if (l < sizeof(HASH_MAGIC) - 1) {
     74      1.1  christos 		debug2("extract_salt: string too short");
     75      1.1  christos 		return (-1);
     76      1.1  christos 	}
     77      1.1  christos 	if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) {
     78      1.1  christos 		debug2("extract_salt: invalid magic identifier");
     79      1.1  christos 		return (-1);
     80      1.1  christos 	}
     81      1.1  christos 	s += sizeof(HASH_MAGIC) - 1;
     82      1.1  christos 	l -= sizeof(HASH_MAGIC) - 1;
     83      1.1  christos 	if ((p = memchr(s, HASH_DELIM, l)) == NULL) {
     84      1.1  christos 		debug2("extract_salt: missing salt termination character");
     85      1.1  christos 		return (-1);
     86      1.1  christos 	}
     87      1.1  christos 
     88      1.1  christos 	b64len = p - s;
     89      1.1  christos 	/* Sanity check */
     90      1.1  christos 	if (b64len == 0 || b64len > 1024) {
     91      1.1  christos 		debug2("extract_salt: bad encoded salt length %u", b64len);
     92      1.1  christos 		return (-1);
     93      1.1  christos 	}
     94      1.1  christos 	b64salt = xmalloc(1 + b64len);
     95      1.1  christos 	memcpy(b64salt, s, b64len);
     96      1.1  christos 	b64salt[b64len] = '\0';
     97      1.1  christos 
     98      1.1  christos 	ret = __b64_pton(b64salt, salt, salt_len);
     99  1.4.8.1       tls 	free(b64salt);
    100      1.1  christos 	if (ret == -1) {
    101      1.1  christos 		debug2("extract_salt: salt decode error");
    102      1.1  christos 		return (-1);
    103      1.1  christos 	}
    104      1.1  christos 	if (ret != SHA_DIGEST_LENGTH) {
    105      1.1  christos 		debug2("extract_salt: expected salt len %d, got %d",
    106      1.1  christos 		    SHA_DIGEST_LENGTH, ret);
    107      1.1  christos 		return (-1);
    108      1.1  christos 	}
    109      1.1  christos 
    110      1.1  christos 	return (0);
    111      1.1  christos }
    112      1.1  christos 
    113      1.1  christos char *
    114      1.1  christos host_hash(const char *host, const char *name_from_hostfile, u_int src_len)
    115      1.1  christos {
    116      1.1  christos 	const EVP_MD *md = EVP_sha1();
    117      1.1  christos 	HMAC_CTX mac_ctx;
    118  1.4.8.1       tls 	u_char salt[256], result[256];
    119  1.4.8.1       tls 	char uu_salt[512], uu_result[512];
    120      1.1  christos 	static char encoded[1024];
    121      1.1  christos 	u_int i, len;
    122      1.1  christos 
    123      1.1  christos 	len = EVP_MD_size(md);
    124      1.1  christos 
    125      1.1  christos 	if (name_from_hostfile == NULL) {
    126      1.1  christos 		/* Create new salt */
    127      1.1  christos 		for (i = 0; i < len; i++)
    128      1.1  christos 			salt[i] = arc4random();
    129      1.1  christos 	} else {
    130      1.1  christos 		/* Extract salt from known host entry */
    131      1.1  christos 		if (extract_salt(name_from_hostfile, src_len, salt,
    132      1.1  christos 		    sizeof(salt)) == -1)
    133      1.1  christos 			return (NULL);
    134      1.1  christos 	}
    135      1.1  christos 
    136      1.1  christos 	HMAC_Init(&mac_ctx, salt, len, md);
    137  1.4.8.1       tls 	HMAC_Update(&mac_ctx, __UNCONST(host), strlen(host));
    138      1.1  christos 	HMAC_Final(&mac_ctx, result, NULL);
    139      1.1  christos 	HMAC_cleanup(&mac_ctx);
    140      1.1  christos 
    141      1.1  christos 	if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 ||
    142      1.1  christos 	    __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1)
    143      1.1  christos 		fatal("host_hash: __b64_ntop failed");
    144      1.1  christos 
    145      1.1  christos 	snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt,
    146      1.1  christos 	    HASH_DELIM, uu_result);
    147      1.1  christos 
    148      1.1  christos 	return (encoded);
    149      1.1  christos }
    150      1.1  christos 
    151      1.1  christos /*
    152      1.1  christos  * Parses an RSA (number of bits, e, n) or DSA key from a string.  Moves the
    153      1.1  christos  * pointer over the key.  Skips any whitespace at the beginning and at end.
    154      1.1  christos  */
    155      1.1  christos 
    156      1.1  christos int
    157  1.4.8.1       tls hostfile_read_key(char **cpp, int *bitsp, Key *ret)
    158      1.1  christos {
    159      1.1  christos 	char *cp;
    160      1.1  christos 
    161      1.1  christos 	/* Skip leading whitespace. */
    162      1.1  christos 	for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
    163      1.1  christos 		;
    164      1.1  christos 
    165      1.1  christos 	if (key_read(ret, &cp) != 1)
    166      1.1  christos 		return 0;
    167      1.1  christos 
    168      1.1  christos 	/* Skip trailing whitespace. */
    169      1.1  christos 	for (; *cp == ' ' || *cp == '\t'; cp++)
    170      1.1  christos 		;
    171      1.1  christos 
    172      1.1  christos 	/* Return results. */
    173      1.1  christos 	*cpp = cp;
    174  1.4.8.1       tls 	if (bitsp != NULL) {
    175  1.4.8.1       tls 		if ((*bitsp = key_size(ret)) <= 0)
    176  1.4.8.1       tls 			return 0;
    177  1.4.8.1       tls 	}
    178      1.1  christos 	return 1;
    179      1.1  christos }
    180      1.1  christos 
    181      1.1  christos static int
    182      1.4  christos hostfile_check_key(int bits, const Key *key, const char *host,
    183      1.4  christos     const char *filename, u_long linenum)
    184      1.1  christos {
    185      1.1  christos 	if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL)
    186      1.1  christos 		return 1;
    187      1.1  christos 	if (bits != BN_num_bits(key->rsa->n)) {
    188      1.4  christos 		logit("Warning: %s, line %lu: keysize mismatch for host %s: "
    189      1.1  christos 		    "actual %d vs. announced %d.",
    190      1.1  christos 		    filename, linenum, host, BN_num_bits(key->rsa->n), bits);
    191      1.4  christos 		logit("Warning: replace %d with %d in %s, line %lu.",
    192      1.1  christos 		    bits, BN_num_bits(key->rsa->n), filename, linenum);
    193      1.1  christos 	}
    194      1.1  christos 	return 1;
    195      1.1  christos }
    196      1.1  christos 
    197      1.4  christos static HostkeyMarker
    198      1.3      adam check_markers(char **cpp)
    199      1.3      adam {
    200      1.3      adam 	char marker[32], *sp, *cp = *cpp;
    201      1.3      adam 	int ret = MRK_NONE;
    202      1.3      adam 
    203      1.3      adam 	while (*cp == '@') {
    204      1.3      adam 		/* Only one marker is allowed */
    205      1.3      adam 		if (ret != MRK_NONE)
    206      1.3      adam 			return MRK_ERROR;
    207      1.3      adam 		/* Markers are terminated by whitespace */
    208      1.3      adam 		if ((sp = strchr(cp, ' ')) == NULL &&
    209      1.3      adam 		    (sp = strchr(cp, '\t')) == NULL)
    210      1.3      adam 			return MRK_ERROR;
    211      1.3      adam 		/* Extract marker for comparison */
    212      1.3      adam 		if (sp <= cp + 1 || sp >= cp + sizeof(marker))
    213      1.3      adam 			return MRK_ERROR;
    214      1.3      adam 		memcpy(marker, cp, sp - cp);
    215      1.3      adam 		marker[sp - cp] = '\0';
    216      1.3      adam 		if (strcmp(marker, CA_MARKER) == 0)
    217      1.3      adam 			ret = MRK_CA;
    218      1.3      adam 		else if (strcmp(marker, REVOKE_MARKER) == 0)
    219      1.3      adam 			ret = MRK_REVOKE;
    220      1.3      adam 		else
    221      1.3      adam 			return MRK_ERROR;
    222      1.3      adam 
    223      1.3      adam 		/* Skip past marker and any whitespace that follows it */
    224      1.3      adam 		cp = sp;
    225      1.3      adam 		for (; *cp == ' ' || *cp == '\t'; cp++)
    226      1.3      adam 			;
    227      1.3      adam 	}
    228      1.3      adam 	*cpp = cp;
    229      1.3      adam 	return ret;
    230      1.3      adam }
    231      1.3      adam 
    232      1.4  christos struct hostkeys *
    233      1.4  christos init_hostkeys(void)
    234      1.4  christos {
    235      1.4  christos 	struct hostkeys *ret = xcalloc(1, sizeof(*ret));
    236      1.4  christos 
    237      1.4  christos 	ret->entries = NULL;
    238      1.4  christos 	return ret;
    239      1.4  christos }
    240      1.1  christos 
    241      1.4  christos void
    242      1.4  christos load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
    243      1.1  christos {
    244      1.1  christos 	FILE *f;
    245      1.1  christos 	char line[8192];
    246      1.4  christos 	u_long linenum = 0, num_loaded = 0;
    247      1.1  christos 	char *cp, *cp2, *hashed_host;
    248      1.4  christos 	HostkeyMarker marker;
    249      1.4  christos 	Key *key;
    250      1.4  christos 	int kbits;
    251      1.4  christos 
    252      1.4  christos 	if ((f = fopen(path, "r")) == NULL)
    253      1.4  christos 		return;
    254      1.4  christos 	debug3("%s: loading entries for host \"%.100s\" from file \"%s\"",
    255      1.4  christos 	    __func__, host, path);
    256      1.4  christos 	while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
    257      1.1  christos 		cp = line;
    258      1.1  christos 
    259      1.1  christos 		/* Skip any leading whitespace, comments and empty lines. */
    260      1.1  christos 		for (; *cp == ' ' || *cp == '\t'; cp++)
    261      1.1  christos 			;
    262      1.1  christos 		if (!*cp || *cp == '#' || *cp == '\n')
    263      1.1  christos 			continue;
    264      1.1  christos 
    265      1.4  christos 		if ((marker = check_markers(&cp)) == MRK_ERROR) {
    266      1.4  christos 			verbose("%s: invalid marker at %s:%lu",
    267      1.4  christos 			    __func__, path, linenum);
    268      1.3      adam 			continue;
    269      1.4  christos 		}
    270      1.3      adam 
    271      1.1  christos 		/* Find the end of the host name portion. */
    272      1.1  christos 		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
    273      1.1  christos 			;
    274      1.1  christos 
    275      1.1  christos 		/* Check if the host name matches. */
    276      1.1  christos 		if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) {
    277      1.1  christos 			if (*cp != HASH_DELIM)
    278      1.1  christos 				continue;
    279      1.1  christos 			hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
    280      1.1  christos 			if (hashed_host == NULL) {
    281      1.4  christos 				debug("Invalid hashed host line %lu of %s",
    282      1.4  christos 				    linenum, path);
    283      1.1  christos 				continue;
    284      1.1  christos 			}
    285      1.1  christos 			if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
    286      1.1  christos 				continue;
    287      1.1  christos 		}
    288      1.1  christos 
    289      1.1  christos 		/* Got a match.  Skip host name. */
    290      1.1  christos 		cp = cp2;
    291      1.1  christos 
    292      1.1  christos 		/*
    293      1.1  christos 		 * Extract the key from the line.  This will skip any leading
    294      1.1  christos 		 * whitespace.  Ignore badly formatted lines.
    295      1.1  christos 		 */
    296      1.4  christos 		key = key_new(KEY_UNSPEC);
    297      1.4  christos 		if (!hostfile_read_key(&cp, &kbits, key)) {
    298      1.4  christos 			key_free(key);
    299      1.4  christos 			key = key_new(KEY_RSA1);
    300      1.4  christos 			if (!hostfile_read_key(&cp, &kbits, key)) {
    301      1.4  christos 				key_free(key);
    302      1.4  christos 				continue;
    303      1.4  christos 			}
    304      1.4  christos 		}
    305      1.4  christos 		if (!hostfile_check_key(kbits, key, host, path, linenum))
    306      1.1  christos 			continue;
    307      1.1  christos 
    308      1.4  christos 		debug3("%s: found %skey type %s in file %s:%lu", __func__,
    309      1.4  christos 		    marker == MRK_NONE ? "" :
    310      1.4  christos 		    (marker == MRK_CA ? "ca " : "revoked "),
    311      1.4  christos 		    key_type(key), path, linenum);
    312      1.4  christos 		hostkeys->entries = xrealloc(hostkeys->entries,
    313      1.4  christos 		    hostkeys->num_entries + 1, sizeof(*hostkeys->entries));
    314      1.4  christos 		hostkeys->entries[hostkeys->num_entries].host = xstrdup(host);
    315      1.4  christos 		hostkeys->entries[hostkeys->num_entries].file = xstrdup(path);
    316      1.4  christos 		hostkeys->entries[hostkeys->num_entries].line = linenum;
    317      1.4  christos 		hostkeys->entries[hostkeys->num_entries].key = key;
    318      1.4  christos 		hostkeys->entries[hostkeys->num_entries].marker = marker;
    319      1.4  christos 		hostkeys->num_entries++;
    320      1.4  christos 		num_loaded++;
    321      1.4  christos 	}
    322      1.4  christos 	debug3("%s: loaded %lu keys", __func__, num_loaded);
    323      1.4  christos 	fclose(f);
    324      1.4  christos 	return;
    325      1.4  christos }
    326      1.4  christos 
    327      1.4  christos void
    328      1.4  christos free_hostkeys(struct hostkeys *hostkeys)
    329      1.4  christos {
    330      1.4  christos 	u_int i;
    331      1.1  christos 
    332      1.4  christos 	for (i = 0; i < hostkeys->num_entries; i++) {
    333  1.4.8.1       tls 		free(hostkeys->entries[i].host);
    334  1.4.8.1       tls 		free(hostkeys->entries[i].file);
    335      1.4  christos 		key_free(hostkeys->entries[i].key);
    336      1.4  christos 		bzero(hostkeys->entries + i, sizeof(*hostkeys->entries));
    337      1.4  christos 	}
    338  1.4.8.1       tls 	free(hostkeys->entries);
    339  1.4.8.1       tls 	bzero(hostkeys, sizeof(*hostkeys));
    340  1.4.8.1       tls 	free(hostkeys);
    341      1.4  christos }
    342      1.4  christos 
    343      1.4  christos static int
    344      1.4  christos check_key_not_revoked(struct hostkeys *hostkeys, Key *k)
    345      1.4  christos {
    346      1.4  christos 	int is_cert = key_is_cert(k);
    347      1.4  christos 	u_int i;
    348      1.4  christos 
    349      1.4  christos 	for (i = 0; i < hostkeys->num_entries; i++) {
    350      1.4  christos 		if (hostkeys->entries[i].marker != MRK_REVOKE)
    351      1.1  christos 			continue;
    352      1.4  christos 		if (key_equal_public(k, hostkeys->entries[i].key))
    353      1.4  christos 			return -1;
    354      1.4  christos 		if (is_cert &&
    355      1.4  christos 		    key_equal_public(k->cert->signature_key,
    356      1.4  christos 		    hostkeys->entries[i].key))
    357      1.4  christos 			return -1;
    358      1.4  christos 	}
    359      1.4  christos 	return 0;
    360      1.4  christos }
    361      1.4  christos 
    362      1.4  christos /*
    363      1.4  christos  * Match keys against a specified key, or look one up by key type.
    364      1.4  christos  *
    365      1.4  christos  * If looking for a keytype (key == NULL) and one is found then return
    366      1.4  christos  * HOST_FOUND, otherwise HOST_NEW.
    367      1.4  christos  *
    368      1.4  christos  * If looking for a key (key != NULL):
    369      1.4  christos  *  1. If the key is a cert and a matching CA is found, return HOST_OK
    370      1.4  christos  *  2. If the key is not a cert and a matching key is found, return HOST_OK
    371      1.4  christos  *  3. If no key matches but a key with a different type is found, then
    372      1.4  christos  *     return HOST_CHANGED
    373      1.4  christos  *  4. If no matching keys are found, then return HOST_NEW.
    374      1.4  christos  *
    375      1.4  christos  * Finally, check any found key is not revoked.
    376      1.4  christos  */
    377      1.4  christos static HostStatus
    378      1.4  christos check_hostkeys_by_key_or_type(struct hostkeys *hostkeys,
    379      1.4  christos     Key *k, int keytype, const struct hostkey_entry **found)
    380      1.4  christos {
    381      1.4  christos 	u_int i;
    382      1.4  christos 	HostStatus end_return = HOST_NEW;
    383      1.4  christos 	int want_cert = key_is_cert(k);
    384      1.4  christos 	HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE;
    385      1.4  christos 	int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2;
    386      1.4  christos 
    387      1.4  christos 	if (found != NULL)
    388      1.4  christos 		*found = NULL;
    389      1.1  christos 
    390      1.4  christos 	for (i = 0; i < hostkeys->num_entries; i++) {
    391      1.4  christos 		if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1)
    392      1.4  christos 			continue;
    393      1.4  christos 		if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1)
    394      1.1  christos 			continue;
    395      1.4  christos 		if (hostkeys->entries[i].marker != want_marker)
    396      1.4  christos 			continue;
    397      1.4  christos 		if (k == NULL) {
    398      1.4  christos 			if (hostkeys->entries[i].key->type != keytype)
    399      1.4  christos 				continue;
    400      1.4  christos 			end_return = HOST_FOUND;
    401      1.4  christos 			if (found != NULL)
    402      1.4  christos 				*found = hostkeys->entries + i;
    403      1.4  christos 			k = hostkeys->entries[i].key;
    404      1.4  christos 			break;
    405      1.4  christos 		}
    406      1.4  christos 		if (want_cert) {
    407      1.4  christos 			if (key_equal_public(k->cert->signature_key,
    408      1.4  christos 			    hostkeys->entries[i].key)) {
    409      1.4  christos 				/* A matching CA exists */
    410      1.4  christos 				end_return = HOST_OK;
    411      1.4  christos 				if (found != NULL)
    412      1.4  christos 					*found = hostkeys->entries + i;
    413      1.4  christos 				break;
    414      1.3      adam 			}
    415      1.4  christos 		} else {
    416      1.4  christos 			if (key_equal(k, hostkeys->entries[i].key)) {
    417      1.4  christos 				end_return = HOST_OK;
    418      1.4  christos 				if (found != NULL)
    419      1.4  christos 					*found = hostkeys->entries + i;
    420      1.4  christos 				break;
    421      1.3      adam 			}
    422      1.4  christos 			/* A non-maching key exists */
    423      1.4  christos 			end_return = HOST_CHANGED;
    424      1.4  christos 			if (found != NULL)
    425      1.4  christos 				*found = hostkeys->entries + i;
    426      1.3      adam 		}
    427      1.1  christos 	}
    428      1.4  christos 	if (check_key_not_revoked(hostkeys, k) != 0) {
    429      1.4  christos 		end_return = HOST_REVOKED;
    430      1.4  christos 		if (found != NULL)
    431      1.4  christos 			*found = NULL;
    432      1.4  christos 	}
    433      1.1  christos 	return end_return;
    434      1.1  christos }
    435      1.4  christos 
    436      1.1  christos HostStatus
    437      1.4  christos check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key,
    438      1.4  christos     const struct hostkey_entry **found)
    439      1.1  christos {
    440      1.1  christos 	if (key == NULL)
    441      1.1  christos 		fatal("no key to look up");
    442      1.4  christos 	return check_hostkeys_by_key_or_type(hostkeys, key, 0, found);
    443      1.1  christos }
    444      1.1  christos 
    445      1.1  christos int
    446      1.4  christos lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
    447      1.4  christos     const struct hostkey_entry **found)
    448      1.1  christos {
    449      1.4  christos 	return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
    450      1.4  christos 	    found) == HOST_FOUND);
    451      1.1  christos }
    452      1.1  christos 
    453      1.1  christos /*
    454      1.1  christos  * Appends an entry to the host file.  Returns false if the entry could not
    455      1.1  christos  * be appended.
    456      1.1  christos  */
    457      1.1  christos 
    458      1.1  christos int
    459      1.1  christos add_host_to_hostfile(const char *filename, const char *host, const Key *key,
    460      1.1  christos     int store_hash)
    461      1.1  christos {
    462      1.1  christos 	FILE *f;
    463      1.1  christos 	int success = 0;
    464      1.1  christos 	char *hashed_host = NULL;
    465      1.1  christos 
    466      1.1  christos 	if (key == NULL)
    467      1.1  christos 		return 1;	/* XXX ? */
    468      1.1  christos 	f = fopen(filename, "a");
    469      1.1  christos 	if (!f)
    470      1.1  christos 		return 0;
    471      1.1  christos 
    472      1.1  christos 	if (store_hash) {
    473      1.1  christos 		if ((hashed_host = host_hash(host, NULL, 0)) == NULL) {
    474      1.1  christos 			error("add_host_to_hostfile: host_hash failed");
    475      1.1  christos 			fclose(f);
    476      1.1  christos 			return 0;
    477      1.1  christos 		}
    478      1.1  christos 	}
    479      1.1  christos 	fprintf(f, "%s ", store_hash ? hashed_host : host);
    480      1.1  christos 
    481      1.1  christos 	if (key_write(key, f)) {
    482      1.1  christos 		success = 1;
    483      1.1  christos 	} else {
    484      1.1  christos 		error("add_host_to_hostfile: saving key in %s failed", filename);
    485      1.1  christos 	}
    486      1.1  christos 	fprintf(f, "\n");
    487      1.1  christos 	fclose(f);
    488      1.1  christos 	return success;
    489      1.1  christos }
    490