hostfile.c revision 76262
157429Smarkm/*
257429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
357429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
457429Smarkm *                    All rights reserved
565674Skris * Functions for manipulating the known hosts files.
660576Skris *
765674Skris * As far as I am concerned, the code I have written for this software
865674Skris * can be used freely for any purpose.  Any derived versions of this
965674Skris * software must be clearly marked as such, and if the derived work is
1065674Skris * incompatible with the protocol description in the RFC file, it must be
1165674Skris * called by a name other than "ssh" or "Secure Shell".
1260576Skris *
1360576Skris *
1465674Skris * Copyright (c) 1999,2000 Markus Friedl.  All rights reserved.
1565674Skris * Copyright (c) 1999 Niels Provos.  All rights reserved.
1665674Skris *
1765674Skris * Redistribution and use in source and binary forms, with or without
1865674Skris * modification, are permitted provided that the following conditions
1965674Skris * are met:
2065674Skris * 1. Redistributions of source code must retain the above copyright
2165674Skris *    notice, this list of conditions and the following disclaimer.
2265674Skris * 2. Redistributions in binary form must reproduce the above copyright
2365674Skris *    notice, this list of conditions and the following disclaimer in the
2465674Skris *    documentation and/or other materials provided with the distribution.
2565674Skris *
2665674Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2765674Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2865674Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2965674Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3065674Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3165674Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3265674Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3365674Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3465674Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3565674Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3657429Smarkm */
3757429Smarkm
3857429Smarkm#include "includes.h"
3976262SgreenRCSID("$OpenBSD: hostfile.c,v 1.26 2001/04/12 19:15:24 markus Exp $");
4065674SkrisRCSID("$FreeBSD: head/crypto/openssh/hostfile.c 76262 2001-05-04 04:14:23Z green $");
4157429Smarkm
4257429Smarkm#include "packet.h"
4358582Skris#include "match.h"
4458582Skris#include "key.h"
4558582Skris#include "hostfile.h"
4676262Sgreen#include "log.h"
4757429Smarkm
4857429Smarkm/*
4958582Skris * Parses an RSA (number of bits, e, n) or DSA key from a string.  Moves the
5058582Skris * pointer over the key.  Skips any whitespace at the beginning and at end.
5157429Smarkm */
5257429Smarkm
5357429Smarkmint
5476262Sgreenhostfile_read_key(char **cpp, u_int *bitsp, Key *ret)
5557429Smarkm{
5657429Smarkm	char *cp;
5757429Smarkm
5857429Smarkm	/* Skip leading whitespace. */
5957429Smarkm	for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
6057429Smarkm		;
6157429Smarkm
6276262Sgreen	if (key_read(ret, &cp) != 1)
6357429Smarkm		return 0;
6457429Smarkm
6557429Smarkm	/* Skip trailing whitespace. */
6657429Smarkm	for (; *cp == ' ' || *cp == '\t'; cp++)
6757429Smarkm		;
6857429Smarkm
6957429Smarkm	/* Return results. */
7057429Smarkm	*cpp = cp;
7176262Sgreen	*bitsp = key_size(ret);
7257429Smarkm	return 1;
7357429Smarkm}
7457429Smarkm
7558582Skrisint
7676262Sgreenauth_rsa_read_key(char **cpp, u_int *bitsp, BIGNUM * e, BIGNUM * n)
7758582Skris{
7876262Sgreen	Key *k = key_new(KEY_RSA1);
7958582Skris	int ret = hostfile_read_key(cpp, bitsp, k);
8058582Skris	BN_copy(e, k->rsa->e);
8158582Skris	BN_copy(n, k->rsa->n);
8258582Skris	key_free(k);
8358582Skris	return ret;
8458582Skris}
8557429Smarkm
8657429Smarkmint
8758582Skrishostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum)
8857429Smarkm{
8976262Sgreen	if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL)
9058582Skris		return 1;
9158582Skris	if (bits != BN_num_bits(key->rsa->n)) {
9260576Skris		log("Warning: %s, line %d: keysize mismatch for host %s: "
9358582Skris		    "actual %d vs. announced %d.",
9458582Skris		    filename, linenum, host, BN_num_bits(key->rsa->n), bits);
9560576Skris		log("Warning: replace %d with %d in %s, line %d.",
9658582Skris		    bits, BN_num_bits(key->rsa->n), filename, linenum);
9757429Smarkm	}
9858582Skris	return 1;
9957429Smarkm}
10057429Smarkm
10157429Smarkm/*
10257429Smarkm * Checks whether the given host (which must be in all lowercase) is already
10357429Smarkm * in the list of our known hosts. Returns HOST_OK if the host is known and
10457429Smarkm * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED
10557429Smarkm * if the host is known but used to have a different host key.
10657429Smarkm */
10757429Smarkm
10857429SmarkmHostStatus
10976262Sgreencheck_host_in_hostfile(const char *filename, const char *host, Key *key,
11076262Sgreen    Key *found, int *numret)
11157429Smarkm{
11257429Smarkm	FILE *f;
11357429Smarkm	char line[8192];
11457429Smarkm	int linenum = 0;
11576262Sgreen	u_int kbits;
11657429Smarkm	char *cp, *cp2;
11757429Smarkm	HostStatus end_return;
11857429Smarkm
11976262Sgreen	debug3("check_host_in_hostfile: filename %s", filename);
12058582Skris	if (key == NULL)
12158582Skris		fatal("no key to look up");
12257429Smarkm	/* Open the file containing the list of known hosts. */
12357429Smarkm	f = fopen(filename, "r");
12457429Smarkm	if (!f)
12557429Smarkm		return HOST_NEW;
12657429Smarkm
12757429Smarkm	/*
12857429Smarkm	 * Return value when the loop terminates.  This is set to
12957429Smarkm	 * HOST_CHANGED if we have seen a different key for the host and have
13057429Smarkm	 * not found the proper one.
13157429Smarkm	 */
13257429Smarkm	end_return = HOST_NEW;
13357429Smarkm
13476262Sgreen	/* Go through the file. */
13557429Smarkm	while (fgets(line, sizeof(line), f)) {
13657429Smarkm		cp = line;
13757429Smarkm		linenum++;
13857429Smarkm
13957429Smarkm		/* Skip any leading whitespace, comments and empty lines. */
14057429Smarkm		for (; *cp == ' ' || *cp == '\t'; cp++)
14157429Smarkm			;
14257429Smarkm		if (!*cp || *cp == '#' || *cp == '\n')
14357429Smarkm			continue;
14457429Smarkm
14557429Smarkm		/* Find the end of the host name portion. */
14657429Smarkm		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
14757429Smarkm			;
14857429Smarkm
14957429Smarkm		/* Check if the host name matches. */
15076262Sgreen		if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1)
15157429Smarkm			continue;
15257429Smarkm
15357429Smarkm		/* Got a match.  Skip host name. */
15457429Smarkm		cp = cp2;
15557429Smarkm
15657429Smarkm		/*
15757429Smarkm		 * Extract the key from the line.  This will skip any leading
15857429Smarkm		 * whitespace.  Ignore badly formatted lines.
15957429Smarkm		 */
16058582Skris		if (!hostfile_read_key(&cp, &kbits, found))
16157429Smarkm			continue;
16258582Skris		if (!hostfile_check_key(kbits, found, host, filename, linenum))
16358582Skris			continue;
16457429Smarkm
16576262Sgreen		if (numret != NULL)
16676262Sgreen			*numret = linenum;
16776262Sgreen
16857429Smarkm		/* Check if the current key is the same as the given key. */
16958582Skris		if (key_equal(key, found)) {
17057429Smarkm			/* Ok, they match. */
17176262Sgreen			debug3("check_host_in_hostfile: match line %d", linenum);
17257429Smarkm			fclose(f);
17357429Smarkm			return HOST_OK;
17457429Smarkm		}
17557429Smarkm		/*
17657429Smarkm		 * They do not match.  We will continue to go through the
17757429Smarkm		 * file; however, we note that we will not return that it is
17857429Smarkm		 * new.
17957429Smarkm		 */
18057429Smarkm		end_return = HOST_CHANGED;
18157429Smarkm	}
18257429Smarkm	/* Clear variables and close the file. */
18357429Smarkm	fclose(f);
18457429Smarkm
18557429Smarkm	/*
18657429Smarkm	 * Return either HOST_NEW or HOST_CHANGED, depending on whether we
18757429Smarkm	 * saw a different key for the host.
18857429Smarkm	 */
18957429Smarkm	return end_return;
19057429Smarkm}
19157429Smarkm
19257429Smarkm/*
19357429Smarkm * Appends an entry to the host file.  Returns false if the entry could not
19457429Smarkm * be appended.
19557429Smarkm */
19657429Smarkm
19757429Smarkmint
19858582Skrisadd_host_to_hostfile(const char *filename, const char *host, Key *key)
19957429Smarkm{
20057429Smarkm	FILE *f;
20158582Skris	int success = 0;
20258582Skris	if (key == NULL)
20360576Skris		return 1;	/* XXX ? */
20457429Smarkm	f = fopen(filename, "a");
20557429Smarkm	if (!f)
20657429Smarkm		return 0;
20758582Skris	fprintf(f, "%s ", host);
20858582Skris	if (key_write(key, f)) {
20958582Skris		success = 1;
21058582Skris	} else {
21160576Skris		error("add_host_to_hostfile: saving key in %s failed", filename);
21257429Smarkm	}
21360576Skris	fprintf(f, "\n");
21457429Smarkm	fclose(f);
21558582Skris	return success;
21657429Smarkm}
217