Home | History | Annotate | Line # | Download | only in dist
hostfile.c revision 1.1.1.2
      1      1.1  christos /*	$NetBSD: hostfile.c,v 1.1.1.2 2010/11/21 17:05:43 adam Exp $	*/
      2  1.1.1.2      adam /* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 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.1  christos #include <sys/types.h>
     41      1.1  christos 
     42      1.1  christos #include <netinet/in.h>
     43      1.1  christos 
     44      1.1  christos #include <openssl/hmac.h>
     45      1.1  christos #include <openssl/sha.h>
     46      1.1  christos 
     47      1.1  christos #include <resolv.h>
     48      1.1  christos #include <stdio.h>
     49      1.1  christos #include <stdlib.h>
     50      1.1  christos #include <string.h>
     51      1.1  christos 
     52      1.1  christos #include "xmalloc.h"
     53      1.1  christos #include "match.h"
     54      1.1  christos #include "key.h"
     55      1.1  christos #include "hostfile.h"
     56      1.1  christos #include "log.h"
     57      1.1  christos 
     58      1.1  christos static int
     59      1.1  christos extract_salt(const char *s, u_int l, char *salt, size_t salt_len)
     60      1.1  christos {
     61      1.1  christos 	char *p, *b64salt;
     62      1.1  christos 	u_int b64len;
     63      1.1  christos 	int ret;
     64      1.1  christos 
     65      1.1  christos 	if (l < sizeof(HASH_MAGIC) - 1) {
     66      1.1  christos 		debug2("extract_salt: string too short");
     67      1.1  christos 		return (-1);
     68      1.1  christos 	}
     69      1.1  christos 	if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) {
     70      1.1  christos 		debug2("extract_salt: invalid magic identifier");
     71      1.1  christos 		return (-1);
     72      1.1  christos 	}
     73      1.1  christos 	s += sizeof(HASH_MAGIC) - 1;
     74      1.1  christos 	l -= sizeof(HASH_MAGIC) - 1;
     75      1.1  christos 	if ((p = memchr(s, HASH_DELIM, l)) == NULL) {
     76      1.1  christos 		debug2("extract_salt: missing salt termination character");
     77      1.1  christos 		return (-1);
     78      1.1  christos 	}
     79      1.1  christos 
     80      1.1  christos 	b64len = p - s;
     81      1.1  christos 	/* Sanity check */
     82      1.1  christos 	if (b64len == 0 || b64len > 1024) {
     83      1.1  christos 		debug2("extract_salt: bad encoded salt length %u", b64len);
     84      1.1  christos 		return (-1);
     85      1.1  christos 	}
     86      1.1  christos 	b64salt = xmalloc(1 + b64len);
     87      1.1  christos 	memcpy(b64salt, s, b64len);
     88      1.1  christos 	b64salt[b64len] = '\0';
     89      1.1  christos 
     90      1.1  christos 	ret = __b64_pton(b64salt, salt, salt_len);
     91      1.1  christos 	xfree(b64salt);
     92      1.1  christos 	if (ret == -1) {
     93      1.1  christos 		debug2("extract_salt: salt decode error");
     94      1.1  christos 		return (-1);
     95      1.1  christos 	}
     96      1.1  christos 	if (ret != SHA_DIGEST_LENGTH) {
     97      1.1  christos 		debug2("extract_salt: expected salt len %d, got %d",
     98      1.1  christos 		    SHA_DIGEST_LENGTH, ret);
     99      1.1  christos 		return (-1);
    100      1.1  christos 	}
    101      1.1  christos 
    102      1.1  christos 	return (0);
    103      1.1  christos }
    104      1.1  christos 
    105      1.1  christos char *
    106      1.1  christos host_hash(const char *host, const char *name_from_hostfile, u_int src_len)
    107      1.1  christos {
    108      1.1  christos 	const EVP_MD *md = EVP_sha1();
    109      1.1  christos 	HMAC_CTX mac_ctx;
    110      1.1  christos 	char salt[256], result[256], uu_salt[512], uu_result[512];
    111      1.1  christos 	static char encoded[1024];
    112      1.1  christos 	u_int i, len;
    113      1.1  christos 
    114      1.1  christos 	len = EVP_MD_size(md);
    115      1.1  christos 
    116      1.1  christos 	if (name_from_hostfile == NULL) {
    117      1.1  christos 		/* Create new salt */
    118      1.1  christos 		for (i = 0; i < len; i++)
    119      1.1  christos 			salt[i] = arc4random();
    120      1.1  christos 	} else {
    121      1.1  christos 		/* Extract salt from known host entry */
    122      1.1  christos 		if (extract_salt(name_from_hostfile, src_len, salt,
    123      1.1  christos 		    sizeof(salt)) == -1)
    124      1.1  christos 			return (NULL);
    125      1.1  christos 	}
    126      1.1  christos 
    127      1.1  christos 	HMAC_Init(&mac_ctx, salt, len, md);
    128      1.1  christos 	HMAC_Update(&mac_ctx, host, strlen(host));
    129      1.1  christos 	HMAC_Final(&mac_ctx, result, NULL);
    130      1.1  christos 	HMAC_cleanup(&mac_ctx);
    131      1.1  christos 
    132      1.1  christos 	if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 ||
    133      1.1  christos 	    __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1)
    134      1.1  christos 		fatal("host_hash: __b64_ntop failed");
    135      1.1  christos 
    136      1.1  christos 	snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt,
    137      1.1  christos 	    HASH_DELIM, uu_result);
    138      1.1  christos 
    139      1.1  christos 	return (encoded);
    140      1.1  christos }
    141      1.1  christos 
    142      1.1  christos /*
    143      1.1  christos  * Parses an RSA (number of bits, e, n) or DSA key from a string.  Moves the
    144      1.1  christos  * pointer over the key.  Skips any whitespace at the beginning and at end.
    145      1.1  christos  */
    146      1.1  christos 
    147      1.1  christos int
    148      1.1  christos hostfile_read_key(char **cpp, u_int *bitsp, Key *ret)
    149      1.1  christos {
    150      1.1  christos 	char *cp;
    151      1.1  christos 
    152      1.1  christos 	/* Skip leading whitespace. */
    153      1.1  christos 	for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
    154      1.1  christos 		;
    155      1.1  christos 
    156      1.1  christos 	if (key_read(ret, &cp) != 1)
    157      1.1  christos 		return 0;
    158      1.1  christos 
    159      1.1  christos 	/* Skip trailing whitespace. */
    160      1.1  christos 	for (; *cp == ' ' || *cp == '\t'; cp++)
    161      1.1  christos 		;
    162      1.1  christos 
    163      1.1  christos 	/* Return results. */
    164      1.1  christos 	*cpp = cp;
    165      1.1  christos 	*bitsp = key_size(ret);
    166      1.1  christos 	return 1;
    167      1.1  christos }
    168      1.1  christos 
    169      1.1  christos static int
    170      1.1  christos hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum)
    171      1.1  christos {
    172      1.1  christos 	if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL)
    173      1.1  christos 		return 1;
    174      1.1  christos 	if (bits != BN_num_bits(key->rsa->n)) {
    175      1.1  christos 		logit("Warning: %s, line %d: keysize mismatch for host %s: "
    176      1.1  christos 		    "actual %d vs. announced %d.",
    177      1.1  christos 		    filename, linenum, host, BN_num_bits(key->rsa->n), bits);
    178      1.1  christos 		logit("Warning: replace %d with %d in %s, line %d.",
    179      1.1  christos 		    bits, BN_num_bits(key->rsa->n), filename, linenum);
    180      1.1  christos 	}
    181      1.1  christos 	return 1;
    182      1.1  christos }
    183      1.1  christos 
    184  1.1.1.2      adam static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA }
    185  1.1.1.2      adam check_markers(char **cpp)
    186  1.1.1.2      adam {
    187  1.1.1.2      adam 	char marker[32], *sp, *cp = *cpp;
    188  1.1.1.2      adam 	int ret = MRK_NONE;
    189  1.1.1.2      adam 
    190  1.1.1.2      adam 	while (*cp == '@') {
    191  1.1.1.2      adam 		/* Only one marker is allowed */
    192  1.1.1.2      adam 		if (ret != MRK_NONE)
    193  1.1.1.2      adam 			return MRK_ERROR;
    194  1.1.1.2      adam 		/* Markers are terminated by whitespace */
    195  1.1.1.2      adam 		if ((sp = strchr(cp, ' ')) == NULL &&
    196  1.1.1.2      adam 		    (sp = strchr(cp, '\t')) == NULL)
    197  1.1.1.2      adam 			return MRK_ERROR;
    198  1.1.1.2      adam 		/* Extract marker for comparison */
    199  1.1.1.2      adam 		if (sp <= cp + 1 || sp >= cp + sizeof(marker))
    200  1.1.1.2      adam 			return MRK_ERROR;
    201  1.1.1.2      adam 		memcpy(marker, cp, sp - cp);
    202  1.1.1.2      adam 		marker[sp - cp] = '\0';
    203  1.1.1.2      adam 		if (strcmp(marker, CA_MARKER) == 0)
    204  1.1.1.2      adam 			ret = MRK_CA;
    205  1.1.1.2      adam 		else if (strcmp(marker, REVOKE_MARKER) == 0)
    206  1.1.1.2      adam 			ret = MRK_REVOKE;
    207  1.1.1.2      adam 		else
    208  1.1.1.2      adam 			return MRK_ERROR;
    209  1.1.1.2      adam 
    210  1.1.1.2      adam 		/* Skip past marker and any whitespace that follows it */
    211  1.1.1.2      adam 		cp = sp;
    212  1.1.1.2      adam 		for (; *cp == ' ' || *cp == '\t'; cp++)
    213  1.1.1.2      adam 			;
    214  1.1.1.2      adam 	}
    215  1.1.1.2      adam 	*cpp = cp;
    216  1.1.1.2      adam 	return ret;
    217  1.1.1.2      adam }
    218  1.1.1.2      adam 
    219      1.1  christos /*
    220      1.1  christos  * Checks whether the given host (which must be in all lowercase) is already
    221      1.1  christos  * in the list of our known hosts. Returns HOST_OK if the host is known and
    222      1.1  christos  * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED
    223      1.1  christos  * if the host is known but used to have a different host key.
    224      1.1  christos  *
    225      1.1  christos  * If no 'key' has been specified and a key of type 'keytype' is known
    226      1.1  christos  * for the specified host, then HOST_FOUND is returned.
    227      1.1  christos  */
    228      1.1  christos 
    229      1.1  christos static HostStatus
    230      1.1  christos check_host_in_hostfile_by_key_or_type(const char *filename,
    231  1.1.1.2      adam     const char *host, const Key *key, int keytype, Key *found,
    232  1.1.1.2      adam     int want_revocation, int *numret)
    233      1.1  christos {
    234      1.1  christos 	FILE *f;
    235      1.1  christos 	char line[8192];
    236  1.1.1.2      adam 	int want, have, linenum = 0, want_cert = key_is_cert(key);
    237      1.1  christos 	u_int kbits;
    238      1.1  christos 	char *cp, *cp2, *hashed_host;
    239      1.1  christos 	HostStatus end_return;
    240      1.1  christos 
    241  1.1.1.2      adam 	debug3("check_host_in_hostfile: host %s filename %s", host, filename);
    242  1.1.1.2      adam 
    243  1.1.1.2      adam 	if (want_revocation && (key == NULL || keytype != 0 || found != NULL))
    244  1.1.1.2      adam 		fatal("%s: invalid arguments", __func__);
    245      1.1  christos 
    246      1.1  christos 	/* Open the file containing the list of known hosts. */
    247      1.1  christos 	f = fopen(filename, "r");
    248      1.1  christos 	if (!f)
    249      1.1  christos 		return HOST_NEW;
    250      1.1  christos 
    251      1.1  christos 	/*
    252      1.1  christos 	 * Return value when the loop terminates.  This is set to
    253      1.1  christos 	 * HOST_CHANGED if we have seen a different key for the host and have
    254      1.1  christos 	 * not found the proper one.
    255      1.1  christos 	 */
    256      1.1  christos 	end_return = HOST_NEW;
    257      1.1  christos 
    258      1.1  christos 	/* Go through the file. */
    259      1.1  christos 	while (fgets(line, sizeof(line), f)) {
    260      1.1  christos 		cp = line;
    261      1.1  christos 		linenum++;
    262      1.1  christos 
    263      1.1  christos 		/* Skip any leading whitespace, comments and empty lines. */
    264      1.1  christos 		for (; *cp == ' ' || *cp == '\t'; cp++)
    265      1.1  christos 			;
    266      1.1  christos 		if (!*cp || *cp == '#' || *cp == '\n')
    267      1.1  christos 			continue;
    268      1.1  christos 
    269  1.1.1.2      adam 		if (want_revocation)
    270  1.1.1.2      adam 			want = MRK_REVOKE;
    271  1.1.1.2      adam 		else if (want_cert)
    272  1.1.1.2      adam 			want = MRK_CA;
    273  1.1.1.2      adam 		else
    274  1.1.1.2      adam 			want = MRK_NONE;
    275  1.1.1.2      adam 
    276  1.1.1.2      adam 		if ((have = check_markers(&cp)) == MRK_ERROR) {
    277  1.1.1.2      adam 			verbose("%s: invalid marker at %s:%d",
    278  1.1.1.2      adam 			    __func__, filename, linenum);
    279  1.1.1.2      adam 			continue;
    280  1.1.1.2      adam 		} else if (want != have)
    281  1.1.1.2      adam 			continue;
    282  1.1.1.2      adam 
    283      1.1  christos 		/* Find the end of the host name portion. */
    284      1.1  christos 		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
    285      1.1  christos 			;
    286      1.1  christos 
    287      1.1  christos 		/* Check if the host name matches. */
    288      1.1  christos 		if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) {
    289      1.1  christos 			if (*cp != HASH_DELIM)
    290      1.1  christos 				continue;
    291      1.1  christos 			hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
    292      1.1  christos 			if (hashed_host == NULL) {
    293      1.1  christos 				debug("Invalid hashed host line %d of %s",
    294      1.1  christos 				    linenum, filename);
    295      1.1  christos 				continue;
    296      1.1  christos 			}
    297      1.1  christos 			if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
    298      1.1  christos 				continue;
    299      1.1  christos 		}
    300      1.1  christos 
    301      1.1  christos 		/* Got a match.  Skip host name. */
    302      1.1  christos 		cp = cp2;
    303      1.1  christos 
    304  1.1.1.2      adam 		if (want_revocation)
    305  1.1.1.2      adam 			found = key_new(KEY_UNSPEC);
    306  1.1.1.2      adam 
    307      1.1  christos 		/*
    308      1.1  christos 		 * Extract the key from the line.  This will skip any leading
    309      1.1  christos 		 * whitespace.  Ignore badly formatted lines.
    310      1.1  christos 		 */
    311      1.1  christos 		if (!hostfile_read_key(&cp, &kbits, found))
    312      1.1  christos 			continue;
    313      1.1  christos 
    314      1.1  christos 		if (numret != NULL)
    315      1.1  christos 			*numret = linenum;
    316      1.1  christos 
    317      1.1  christos 		if (key == NULL) {
    318      1.1  christos 			/* we found a key of the requested type */
    319      1.1  christos 			if (found->type == keytype) {
    320      1.1  christos 				fclose(f);
    321      1.1  christos 				return HOST_FOUND;
    322      1.1  christos 			}
    323      1.1  christos 			continue;
    324      1.1  christos 		}
    325      1.1  christos 
    326      1.1  christos 		if (!hostfile_check_key(kbits, found, host, filename, linenum))
    327      1.1  christos 			continue;
    328      1.1  christos 
    329  1.1.1.2      adam 		if (want_revocation) {
    330  1.1.1.2      adam 			if (key_is_cert(key) &&
    331  1.1.1.2      adam 			    key_equal_public(key->cert->signature_key, found)) {
    332  1.1.1.2      adam 				verbose("check_host_in_hostfile: revoked CA "
    333  1.1.1.2      adam 				    "line %d", linenum);
    334  1.1.1.2      adam 				key_free(found);
    335  1.1.1.2      adam 				return HOST_REVOKED;
    336  1.1.1.2      adam 			}
    337  1.1.1.2      adam 			if (key_equal_public(key, found)) {
    338  1.1.1.2      adam 				verbose("check_host_in_hostfile: revoked key "
    339  1.1.1.2      adam 				    "line %d", linenum);
    340  1.1.1.2      adam 				key_free(found);
    341  1.1.1.2      adam 				return HOST_REVOKED;
    342  1.1.1.2      adam 			}
    343  1.1.1.2      adam 			key_free(found);
    344  1.1.1.2      adam 			continue;
    345  1.1.1.2      adam 		}
    346  1.1.1.2      adam 
    347      1.1  christos 		/* Check if the current key is the same as the given key. */
    348  1.1.1.2      adam 		if (want_cert && key_equal(key->cert->signature_key, found)) {
    349  1.1.1.2      adam 			/* Found CA cert for key */
    350  1.1.1.2      adam 			debug3("check_host_in_hostfile: CA match line %d",
    351  1.1.1.2      adam 			    linenum);
    352  1.1.1.2      adam 			fclose(f);
    353  1.1.1.2      adam 			return HOST_OK;
    354  1.1.1.2      adam 		} else if (!want_cert && key_equal(key, found)) {
    355  1.1.1.2      adam 			/* Found identical key */
    356      1.1  christos 			debug3("check_host_in_hostfile: match line %d", linenum);
    357      1.1  christos 			fclose(f);
    358      1.1  christos 			return HOST_OK;
    359      1.1  christos 		}
    360      1.1  christos 		/*
    361      1.1  christos 		 * They do not match.  We will continue to go through the
    362      1.1  christos 		 * file; however, we note that we will not return that it is
    363      1.1  christos 		 * new.
    364      1.1  christos 		 */
    365      1.1  christos 		end_return = HOST_CHANGED;
    366      1.1  christos 	}
    367      1.1  christos 	/* Clear variables and close the file. */
    368      1.1  christos 	fclose(f);
    369      1.1  christos 
    370      1.1  christos 	/*
    371      1.1  christos 	 * Return either HOST_NEW or HOST_CHANGED, depending on whether we
    372      1.1  christos 	 * saw a different key for the host.
    373      1.1  christos 	 */
    374      1.1  christos 	return end_return;
    375      1.1  christos }
    376      1.1  christos 
    377      1.1  christos HostStatus
    378      1.1  christos check_host_in_hostfile(const char *filename, const char *host, const Key *key,
    379      1.1  christos     Key *found, int *numret)
    380      1.1  christos {
    381      1.1  christos 	if (key == NULL)
    382      1.1  christos 		fatal("no key to look up");
    383  1.1.1.2      adam 	if (check_host_in_hostfile_by_key_or_type(filename, host,
    384  1.1.1.2      adam 	    key, 0, NULL, 1, NULL) == HOST_REVOKED)
    385  1.1.1.2      adam 		return HOST_REVOKED;
    386  1.1.1.2      adam 	return check_host_in_hostfile_by_key_or_type(filename, host, key, 0,
    387  1.1.1.2      adam 	    found, 0, numret);
    388      1.1  christos }
    389      1.1  christos 
    390      1.1  christos int
    391      1.1  christos lookup_key_in_hostfile_by_type(const char *filename, const char *host,
    392      1.1  christos     int keytype, Key *found, int *numret)
    393      1.1  christos {
    394      1.1  christos 	return (check_host_in_hostfile_by_key_or_type(filename, host, NULL,
    395  1.1.1.2      adam 	    keytype, found, 0, numret) == HOST_FOUND);
    396      1.1  christos }
    397      1.1  christos 
    398      1.1  christos /*
    399      1.1  christos  * Appends an entry to the host file.  Returns false if the entry could not
    400      1.1  christos  * be appended.
    401      1.1  christos  */
    402      1.1  christos 
    403      1.1  christos int
    404      1.1  christos add_host_to_hostfile(const char *filename, const char *host, const Key *key,
    405      1.1  christos     int store_hash)
    406      1.1  christos {
    407      1.1  christos 	FILE *f;
    408      1.1  christos 	int success = 0;
    409      1.1  christos 	char *hashed_host = NULL;
    410      1.1  christos 
    411      1.1  christos 	if (key == NULL)
    412      1.1  christos 		return 1;	/* XXX ? */
    413      1.1  christos 	f = fopen(filename, "a");
    414      1.1  christos 	if (!f)
    415      1.1  christos 		return 0;
    416      1.1  christos 
    417      1.1  christos 	if (store_hash) {
    418      1.1  christos 		if ((hashed_host = host_hash(host, NULL, 0)) == NULL) {
    419      1.1  christos 			error("add_host_to_hostfile: host_hash failed");
    420      1.1  christos 			fclose(f);
    421      1.1  christos 			return 0;
    422      1.1  christos 		}
    423      1.1  christos 	}
    424      1.1  christos 	fprintf(f, "%s ", store_hash ? hashed_host : host);
    425      1.1  christos 
    426      1.1  christos 	if (key_write(key, f)) {
    427      1.1  christos 		success = 1;
    428      1.1  christos 	} else {
    429      1.1  christos 		error("add_host_to_hostfile: saving key in %s failed", filename);
    430      1.1  christos 	}
    431      1.1  christos 	fprintf(f, "\n");
    432      1.1  christos 	fclose(f);
    433      1.1  christos 	return success;
    434      1.1  christos }
    435