xref: /freebsd/crypto/openssh/hostfile.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
1 /*
2  *
3  * hostfile.c
4  *
5  * Author: Tatu Ylonen <ylo@cs.hut.fi>
6  *
7  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8  *                    All rights reserved
9  *
10  * Created: Thu Jun 29 07:10:56 1995 ylo
11  *
12  * Functions for manipulating the known hosts files.
13  *
14  * $FreeBSD$
15  */
16 
17 #include "includes.h"
18 RCSID("$OpenBSD: hostfile.c,v 1.14 2000/03/23 22:15:33 markus Exp $");
19 
20 #include "packet.h"
21 #include "match.h"
22 #include "ssh.h"
23 #include <openssl/rsa.h>
24 #include <openssl/dsa.h>
25 #include "key.h"
26 #include "hostfile.h"
27 
28 /*
29  * Parses an RSA (number of bits, e, n) or DSA key from a string.  Moves the
30  * pointer over the key.  Skips any whitespace at the beginning and at end.
31  */
32 
33 int
34 hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret)
35 {
36 	unsigned int bits;
37 	char *cp;
38 
39 	/* Skip leading whitespace. */
40 	for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
41 		;
42 
43 	/* Get number of bits. */
44 	if (*cp < '0' || *cp > '9')
45 		return 0;	/* Bad bit count... */
46 	for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
47 		bits = 10 * bits + *cp - '0';
48 
49 	if (!key_read(ret, bits, &cp))
50 		return 0;
51 
52 	/* Skip trailing whitespace. */
53 	for (; *cp == ' ' || *cp == '\t'; cp++)
54 		;
55 
56 	/* Return results. */
57 	*cpp = cp;
58 	*bitsp = bits;
59 	return 1;
60 }
61 
62 int
63 auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
64 {
65 	Key *k = key_new(KEY_RSA);
66 	int ret = hostfile_read_key(cpp, bitsp, k);
67 	BN_copy(e, k->rsa->e);
68 	BN_copy(n, k->rsa->n);
69 	key_free(k);
70 	return ret;
71 }
72 
73 int
74 hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum)
75 {
76 	if (key == NULL || key->type != KEY_RSA || key->rsa == NULL)
77 		return 1;
78 	if (bits != BN_num_bits(key->rsa->n)) {
79 		error("Warning: %s, line %d: keysize mismatch for host %s: "
80 		    "actual %d vs. announced %d.",
81 		    filename, linenum, host, BN_num_bits(key->rsa->n), bits);
82 		error("Warning: replace %d with %d in %s, line %d.",
83 		    bits, BN_num_bits(key->rsa->n), filename, linenum);
84 	}
85 	return 1;
86 }
87 
88 /*
89  * Checks whether the given host (which must be in all lowercase) is already
90  * in the list of our known hosts. Returns HOST_OK if the host is known and
91  * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED
92  * if the host is known but used to have a different host key.
93  */
94 
95 HostStatus
96 check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found)
97 {
98 	FILE *f;
99 	char line[8192];
100 	int linenum = 0;
101 	unsigned int kbits, hostlen;
102 	char *cp, *cp2;
103 	HostStatus end_return;
104 
105 	if (key == NULL)
106 		fatal("no key to look up");
107 	/* Open the file containing the list of known hosts. */
108 	f = fopen(filename, "r");
109 	if (!f)
110 		return HOST_NEW;
111 
112 	/* Cache the length of the host name. */
113 	hostlen = strlen(host);
114 
115 	/*
116 	 * Return value when the loop terminates.  This is set to
117 	 * HOST_CHANGED if we have seen a different key for the host and have
118 	 * not found the proper one.
119 	 */
120 	end_return = HOST_NEW;
121 
122 	/* Go trough the file. */
123 	while (fgets(line, sizeof(line), f)) {
124 		cp = line;
125 		linenum++;
126 
127 		/* Skip any leading whitespace, comments and empty lines. */
128 		for (; *cp == ' ' || *cp == '\t'; cp++)
129 			;
130 		if (!*cp || *cp == '#' || *cp == '\n')
131 			continue;
132 
133 		/* Find the end of the host name portion. */
134 		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
135 			;
136 
137 		/* Check if the host name matches. */
138 		if (!match_hostname(host, cp, (unsigned int) (cp2 - cp)))
139 			continue;
140 
141 		/* Got a match.  Skip host name. */
142 		cp = cp2;
143 
144 		/*
145 		 * Extract the key from the line.  This will skip any leading
146 		 * whitespace.  Ignore badly formatted lines.
147 		 */
148 		if (!hostfile_read_key(&cp, &kbits, found))
149 			continue;
150 		if (!hostfile_check_key(kbits, found, host, filename, linenum))
151 			continue;
152 
153 		/* Check if the current key is the same as the given key. */
154 		if (key_equal(key, found)) {
155 			/* Ok, they match. */
156 			fclose(f);
157 			return HOST_OK;
158 		}
159 		/*
160 		 * They do not match.  We will continue to go through the
161 		 * file; however, we note that we will not return that it is
162 		 * new.
163 		 */
164 		end_return = HOST_CHANGED;
165 	}
166 	/* Clear variables and close the file. */
167 	fclose(f);
168 
169 	/*
170 	 * Return either HOST_NEW or HOST_CHANGED, depending on whether we
171 	 * saw a different key for the host.
172 	 */
173 	return end_return;
174 }
175 
176 /*
177  * Appends an entry to the host file.  Returns false if the entry could not
178  * be appended.
179  */
180 
181 int
182 add_host_to_hostfile(const char *filename, const char *host, Key *key)
183 {
184 	FILE *f;
185 	int success = 0;
186 
187 	if (key == NULL)
188 		return 1;
189 
190 	/* Open the file for appending. */
191 	f = fopen(filename, "a");
192 	if (!f)
193 		return 0;
194 
195 	fprintf(f, "%s ", host);
196 	if (key_write(key, f)) {
197 		fprintf(f, "\n");
198 		success = 1;
199 	} else {
200 		error("add_host_to_hostfile: saving key failed");
201 	}
202 
203 	/* Close the file. */
204 	fclose(f);
205 	return success;
206 }
207