xref: /freebsd/crypto/openssh/hostfile.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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  */
15 
16 #include "includes.h"
17 RCSID("$OpenBSD: hostfile.c,v 1.13 2000/02/18 10:20:20 markus Exp $");
18 
19 #include "packet.h"
20 #include "ssh.h"
21 
22 /*
23  * Reads a multiple-precision integer in decimal from the buffer, and advances
24  * the pointer.  The integer must already be initialized.  This function is
25  * permitted to modify the buffer.  This leaves *cpp to point just beyond the
26  * last processed (and maybe modified) character.  Note that this may modify
27  * the buffer containing the number.
28  */
29 
30 int
31 auth_rsa_read_bignum(char **cpp, BIGNUM * value)
32 {
33 	char *cp = *cpp;
34 	int old;
35 
36 	/* Skip any leading whitespace. */
37 	for (; *cp == ' ' || *cp == '\t'; cp++)
38 		;
39 
40 	/* Check that it begins with a decimal digit. */
41 	if (*cp < '0' || *cp > '9')
42 		return 0;
43 
44 	/* Save starting position. */
45 	*cpp = cp;
46 
47 	/* Move forward until all decimal digits skipped. */
48 	for (; *cp >= '0' && *cp <= '9'; cp++)
49 		;
50 
51 	/* Save the old terminating character, and replace it by \0. */
52 	old = *cp;
53 	*cp = 0;
54 
55 	/* Parse the number. */
56 	if (BN_dec2bn(&value, *cpp) == 0)
57 		return 0;
58 
59 	/* Restore old terminating character. */
60 	*cp = old;
61 
62 	/* Move beyond the number and return success. */
63 	*cpp = cp;
64 	return 1;
65 }
66 
67 /*
68  * Parses an RSA key (number of bits, e, n) from a string.  Moves the pointer
69  * over the key.  Skips any whitespace at the beginning and at end.
70  */
71 
72 int
73 auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
74 {
75 	unsigned int bits;
76 	char *cp;
77 
78 	/* Skip leading whitespace. */
79 	for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
80 		;
81 
82 	/* Get number of bits. */
83 	if (*cp < '0' || *cp > '9')
84 		return 0;	/* Bad bit count... */
85 	for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
86 		bits = 10 * bits + *cp - '0';
87 
88 	/* Get public exponent. */
89 	if (!auth_rsa_read_bignum(&cp, e))
90 		return 0;
91 
92 	/* Get public modulus. */
93 	if (!auth_rsa_read_bignum(&cp, n))
94 		return 0;
95 
96 	/* Skip trailing whitespace. */
97 	for (; *cp == ' ' || *cp == '\t'; cp++)
98 		;
99 
100 	/* Return results. */
101 	*cpp = cp;
102 	*bitsp = bits;
103 	return 1;
104 }
105 
106 /*
107  * Tries to match the host name (which must be in all lowercase) against the
108  * comma-separated sequence of subpatterns (each possibly preceded by ! to
109  * indicate negation).  Returns true if there is a positive match; zero
110  * otherwise.
111  */
112 
113 int
114 match_hostname(const char *host, const char *pattern, unsigned int len)
115 {
116 	char sub[1024];
117 	int negated;
118 	int got_positive;
119 	unsigned int i, subi;
120 
121 	got_positive = 0;
122 	for (i = 0; i < len;) {
123 		/* Check if the subpattern is negated. */
124 		if (pattern[i] == '!') {
125 			negated = 1;
126 			i++;
127 		} else
128 			negated = 0;
129 
130 		/*
131 		 * Extract the subpattern up to a comma or end.  Convert the
132 		 * subpattern to lowercase.
133 		 */
134 		for (subi = 0;
135 		     i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
136 		     subi++, i++)
137 			sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i];
138 		/* If subpattern too long, return failure (no match). */
139 		if (subi >= sizeof(sub) - 1)
140 			return 0;
141 
142 		/* If the subpattern was terminated by a comma, skip the comma. */
143 		if (i < len && pattern[i] == ',')
144 			i++;
145 
146 		/* Null-terminate the subpattern. */
147 		sub[subi] = '\0';
148 
149 		/* Try to match the subpattern against the host name. */
150 		if (match_pattern(host, sub)) {
151 			if (negated)
152 				return 0;	/* Fail */
153 			else
154 				got_positive = 1;
155 		}
156 	}
157 
158 	/*
159 	 * Return success if got a positive match.  If there was a negative
160 	 * match, we have already returned zero and never get here.
161 	 */
162 	return got_positive;
163 }
164 
165 /*
166  * Checks whether the given host (which must be in all lowercase) is already
167  * in the list of our known hosts. Returns HOST_OK if the host is known and
168  * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED
169  * if the host is known but used to have a different host key.
170  */
171 
172 HostStatus
173 check_host_in_hostfile(const char *filename, const char *host,
174 		       BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn)
175 {
176 	FILE *f;
177 	char line[8192];
178 	int linenum = 0;
179 	unsigned int kbits, hostlen;
180 	char *cp, *cp2;
181 	HostStatus end_return;
182 
183 	/* Open the file containing the list of known hosts. */
184 	f = fopen(filename, "r");
185 	if (!f)
186 		return HOST_NEW;
187 
188 	/* Cache the length of the host name. */
189 	hostlen = strlen(host);
190 
191 	/*
192 	 * Return value when the loop terminates.  This is set to
193 	 * HOST_CHANGED if we have seen a different key for the host and have
194 	 * not found the proper one.
195 	 */
196 	end_return = HOST_NEW;
197 
198 	/* Go trough the file. */
199 	while (fgets(line, sizeof(line), f)) {
200 		cp = line;
201 		linenum++;
202 
203 		/* Skip any leading whitespace, comments and empty lines. */
204 		for (; *cp == ' ' || *cp == '\t'; cp++)
205 			;
206 		if (!*cp || *cp == '#' || *cp == '\n')
207 			continue;
208 
209 		/* Find the end of the host name portion. */
210 		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
211 			;
212 
213 		/* Check if the host name matches. */
214 		if (!match_hostname(host, cp, (unsigned int) (cp2 - cp)))
215 			continue;
216 
217 		/* Got a match.  Skip host name. */
218 		cp = cp2;
219 
220 		/*
221 		 * Extract the key from the line.  This will skip any leading
222 		 * whitespace.  Ignore badly formatted lines.
223 		 */
224 		if (!auth_rsa_read_key(&cp, &kbits, ke, kn))
225 			continue;
226 
227 		if (kbits != BN_num_bits(kn)) {
228 			error("Warning: %s, line %d: keysize mismatch for host %s: "
229 			      "actual %d vs. announced %d.",
230 			      filename, linenum, host, BN_num_bits(kn), kbits);
231 			error("Warning: replace %d with %d in %s, line %d.",
232 			      kbits, BN_num_bits(kn), filename, linenum);
233 		}
234 		/* Check if the current key is the same as the given key. */
235 		if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) {
236 			/* Ok, they match. */
237 			fclose(f);
238 			return HOST_OK;
239 		}
240 		/*
241 		 * They do not match.  We will continue to go through the
242 		 * file; however, we note that we will not return that it is
243 		 * new.
244 		 */
245 		end_return = HOST_CHANGED;
246 	}
247 	/* Clear variables and close the file. */
248 	fclose(f);
249 
250 	/*
251 	 * Return either HOST_NEW or HOST_CHANGED, depending on whether we
252 	 * saw a different key for the host.
253 	 */
254 	return end_return;
255 }
256 
257 /*
258  * Appends an entry to the host file.  Returns false if the entry could not
259  * be appended.
260  */
261 
262 int
263 add_host_to_hostfile(const char *filename, const char *host,
264 		     BIGNUM * e, BIGNUM * n)
265 {
266 	FILE *f;
267 	char *buf;
268 	unsigned int bits;
269 
270 	/* Open the file for appending. */
271 	f = fopen(filename, "a");
272 	if (!f)
273 		return 0;
274 
275 	/* size of modulus 'n' */
276 	bits = BN_num_bits(n);
277 
278 	/* Print the host name and key to the file. */
279 	fprintf(f, "%s %u ", host, bits);
280 	buf = BN_bn2dec(e);
281 	if (buf == NULL) {
282 		error("add_host_to_hostfile: BN_bn2dec(e) failed");
283 		fclose(f);
284 		return 0;
285 	}
286 	fprintf(f, "%s ", buf);
287 	free(buf);
288 	buf = BN_bn2dec(n);
289 	if (buf == NULL) {
290 		error("add_host_to_hostfile: BN_bn2dec(n) failed");
291 		fclose(f);
292 		return 0;
293 	}
294 	fprintf(f, "%s\n", buf);
295 	free(buf);
296 
297 	/* Close the file. */
298 	fclose(f);
299 	return 1;
300 }
301