blob: ea92fa048553239a46f723ae00079af9674a3c40 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
Damien Miller95def091999-11-25 00:26:21 +11002 *
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 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100015
16#include "includes.h"
Damien Miller98c7ad62000-03-09 21:27:49 +110017RCSID("$OpenBSD: hostfile.c,v 1.13 2000/02/18 10:20:20 markus Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100018
19#include "packet.h"
20#include "ssh.h"
21
Damien Miller5428f641999-11-25 11:54:57 +110022/*
Damien Miller98c7ad62000-03-09 21:27:49 +110023 * Reads a multiple-precision integer in decimal from the buffer, and advances
Damien Miller5428f641999-11-25 11:54:57 +110024 * 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 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100029
30int
Damien Miller95def091999-11-25 00:26:21 +110031auth_rsa_read_bignum(char **cpp, BIGNUM * value)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100032{
Damien Miller95def091999-11-25 00:26:21 +110033 char *cp = *cpp;
Damien Miller98c7ad62000-03-09 21:27:49 +110034 int old;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100035
Damien Miller95def091999-11-25 00:26:21 +110036 /* Skip any leading whitespace. */
Damien Miller5428f641999-11-25 11:54:57 +110037 for (; *cp == ' ' || *cp == '\t'; cp++)
38 ;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100039
Damien Miller98c7ad62000-03-09 21:27:49 +110040 /* Check that it begins with a decimal digit. */
Damien Miller95def091999-11-25 00:26:21 +110041 if (*cp < '0' || *cp > '9')
42 return 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100043
Damien Miller95def091999-11-25 00:26:21 +110044 /* Save starting position. */
45 *cpp = cp;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100046
Damien Miller98c7ad62000-03-09 21:27:49 +110047 /* Move forward until all decimal digits skipped. */
Damien Miller5428f641999-11-25 11:54:57 +110048 for (; *cp >= '0' && *cp <= '9'; cp++)
49 ;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100050
Damien Miller95def091999-11-25 00:26:21 +110051 /* Save the old terminating character, and replace it by \0. */
52 old = *cp;
53 *cp = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100054
Damien Miller95def091999-11-25 00:26:21 +110055 /* Parse the number. */
56 if (BN_dec2bn(&value, *cpp) == 0)
57 return 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100058
Damien Miller95def091999-11-25 00:26:21 +110059 /* Restore old terminating character. */
60 *cp = old;
61
62 /* Move beyond the number and return success. */
63 *cpp = cp;
64 return 1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100065}
66
Damien Miller5428f641999-11-25 11:54:57 +110067/*
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 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100071
72int
Damien Miller95def091999-11-25 00:26:21 +110073auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100074{
Damien Miller95def091999-11-25 00:26:21 +110075 unsigned int bits;
76 char *cp;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100077
Damien Miller95def091999-11-25 00:26:21 +110078 /* Skip leading whitespace. */
Damien Miller5428f641999-11-25 11:54:57 +110079 for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
80 ;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100081
Damien Miller95def091999-11-25 00:26:21 +110082 /* 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';
Damien Millerd4a8b7e1999-10-27 13:42:43 +100087
Damien Miller95def091999-11-25 00:26:21 +110088 /* Get public exponent. */
89 if (!auth_rsa_read_bignum(&cp, e))
90 return 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100091
Damien Miller95def091999-11-25 00:26:21 +110092 /* Get public modulus. */
93 if (!auth_rsa_read_bignum(&cp, n))
94 return 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100095
Damien Miller95def091999-11-25 00:26:21 +110096 /* Skip trailing whitespace. */
Damien Miller5428f641999-11-25 11:54:57 +110097 for (; *cp == ' ' || *cp == '\t'; cp++)
98 ;
Damien Miller95def091999-11-25 00:26:21 +110099
100 /* Return results. */
101 *cpp = cp;
102 *bitsp = bits;
103 return 1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000104}
105
Damien Miller5428f641999-11-25 11:54:57 +1100106/*
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 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000112
113int
114match_hostname(const char *host, const char *pattern, unsigned int len)
115{
Damien Miller95def091999-11-25 00:26:21 +1100116 char sub[1024];
117 int negated;
118 int got_positive;
119 unsigned int i, subi;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000120
Damien Miller95def091999-11-25 00:26:21 +1100121 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
Damien Miller5428f641999-11-25 11:54:57 +1100130 /*
131 * Extract the subpattern up to a comma or end. Convert the
132 * subpattern to lowercase.
133 */
Damien Miller95def091999-11-25 00:26:21 +1100134 for (subi = 0;
Damien Miller5428f641999-11-25 11:54:57 +1100135 i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
Damien Miller95def091999-11-25 00:26:21 +1100136 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
Damien Miller5428f641999-11-25 11:54:57 +1100142 /* If the subpattern was terminated by a comma, skip the comma. */
Damien Miller95def091999-11-25 00:26:21 +1100143 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)
Damien Miller5428f641999-11-25 11:54:57 +1100152 return 0; /* Fail */
Damien Miller95def091999-11-25 00:26:21 +1100153 else
154 got_positive = 1;
155 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000156 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000157
Damien Miller5428f641999-11-25 11:54:57 +1100158 /*
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 */
Damien Miller95def091999-11-25 00:26:21 +1100162 return got_positive;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000163}
164
Damien Miller5428f641999-11-25 11:54:57 +1100165/*
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 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000171
172HostStatus
Damien Miller7e8e8201999-11-16 13:37:16 +1100173check_host_in_hostfile(const char *filename, const char *host,
Damien Miller95def091999-11-25 00:26:21 +1100174 BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000175{
Damien Miller95def091999-11-25 00:26:21 +1100176 FILE *f;
177 char line[8192];
178 int linenum = 0;
Damien Miller98c7ad62000-03-09 21:27:49 +1100179 unsigned int kbits, hostlen;
Damien Miller95def091999-11-25 00:26:21 +1100180 char *cp, *cp2;
181 HostStatus end_return;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000182
Damien Miller95def091999-11-25 00:26:21 +1100183 /* Open the file containing the list of known hosts. */
184 f = fopen(filename, "r");
185 if (!f)
186 return HOST_NEW;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000187
Damien Miller95def091999-11-25 00:26:21 +1100188 /* Cache the length of the host name. */
189 hostlen = strlen(host);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000190
Damien Miller5428f641999-11-25 11:54:57 +1100191 /*
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 */
Damien Miller95def091999-11-25 00:26:21 +1100196 end_return = HOST_NEW;
Damien Miller7e8e8201999-11-16 13:37:16 +1100197
Damien Miller95def091999-11-25 00:26:21 +1100198 /* Go trough the file. */
199 while (fgets(line, sizeof(line), f)) {
200 cp = line;
201 linenum++;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000202
Damien Miller5428f641999-11-25 11:54:57 +1100203 /* Skip any leading whitespace, comments and empty lines. */
204 for (; *cp == ' ' || *cp == '\t'; cp++)
205 ;
Damien Miller95def091999-11-25 00:26:21 +1100206 if (!*cp || *cp == '#' || *cp == '\n')
207 continue;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000208
Damien Miller95def091999-11-25 00:26:21 +1100209 /* Find the end of the host name portion. */
Damien Miller5428f641999-11-25 11:54:57 +1100210 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
211 ;
Damien Miller7e8e8201999-11-16 13:37:16 +1100212
Damien Miller95def091999-11-25 00:26:21 +1100213 /* 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
Damien Miller5428f641999-11-25 11:54:57 +1100220 /*
221 * Extract the key from the line. This will skip any leading
222 * whitespace. Ignore badly formatted lines.
223 */
Damien Miller95def091999-11-25 00:26:21 +1100224 if (!auth_rsa_read_key(&cp, &kbits, ke, kn))
225 continue;
226
227 if (kbits != BN_num_bits(kn)) {
Damien Milleraae6c611999-12-06 11:47:28 +1100228 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);
Damien Miller95def091999-11-25 00:26:21 +1100231 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 }
Damien Miller5428f641999-11-25 11:54:57 +1100240 /*
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 */
Damien Miller95def091999-11-25 00:26:21 +1100245 end_return = HOST_CHANGED;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000246 }
Damien Miller95def091999-11-25 00:26:21 +1100247 /* Clear variables and close the file. */
248 fclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000249
Damien Miller5428f641999-11-25 11:54:57 +1100250 /*
251 * Return either HOST_NEW or HOST_CHANGED, depending on whether we
252 * saw a different key for the host.
253 */
Damien Miller95def091999-11-25 00:26:21 +1100254 return end_return;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000255}
256
Damien Miller5428f641999-11-25 11:54:57 +1100257/*
258 * Appends an entry to the host file. Returns false if the entry could not
259 * be appended.
260 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000261
262int
263add_host_to_hostfile(const char *filename, const char *host,
Damien Miller95def091999-11-25 00:26:21 +1100264 BIGNUM * e, BIGNUM * n)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000265{
Damien Miller95def091999-11-25 00:26:21 +1100266 FILE *f;
267 char *buf;
268 unsigned int bits;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000269
Damien Miller95def091999-11-25 00:26:21 +1100270 /* Open the file for appending. */
271 f = fopen(filename, "a");
272 if (!f)
273 return 0;
Damien Miller7e8e8201999-11-16 13:37:16 +1100274
Damien Miller95def091999-11-25 00:26:21 +1100275 /* size of modulus 'n' */
276 bits = BN_num_bits(n);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000277
Damien Miller95def091999-11-25 00:26:21 +1100278 /* 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;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000300}