]>
Commit | Line | Data |
---|---|---|
8efc0c15 | 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("$Id$"); | |
18 | ||
19 | #include "packet.h" | |
20 | #include "ssh.h" | |
21 | ||
22 | /* Reads a multiple-precision integer in hex from the buffer, and advances the | |
23 | pointer. The integer must already be initialized. This function is | |
24 | permitted to modify the buffer. This leaves *cpp to point just beyond | |
25 | the last processed (and maybe modified) character. Note that this may | |
26 | modify the buffer containing the number. */ | |
27 | ||
28 | int | |
29 | auth_rsa_read_bignum(char **cpp, BIGNUM *value) | |
30 | { | |
31 | char *cp = *cpp; | |
32 | int len, old; | |
33 | ||
34 | /* Skip any leading whitespace. */ | |
35 | for (; *cp == ' ' || *cp == '\t'; cp++) | |
36 | ; | |
37 | ||
38 | /* Check that it begins with a hex digit. */ | |
39 | if (*cp < '0' || *cp > '9') | |
40 | return 0; | |
41 | ||
42 | /* Save starting position. */ | |
43 | *cpp = cp; | |
44 | ||
45 | /* Move forward until all hex digits skipped. */ | |
46 | for (; *cp >= '0' && *cp <= '9'; cp++) | |
47 | ; | |
48 | ||
49 | /* Compute the length of the hex number. */ | |
50 | len = cp - *cpp; | |
51 | ||
52 | /* Save the old terminating character, and replace it by \0. */ | |
53 | old = *cp; | |
54 | *cp = 0; | |
55 | ||
56 | ||
57 | /* Parse the number. */ | |
58 | if (BN_dec2bn(&value, *cpp) == 0) | |
59 | return 0; | |
60 | ||
61 | /* Restore old terminating character. */ | |
62 | *cp = old; | |
63 | ||
64 | /* Move beyond the number and return success. */ | |
65 | *cpp = cp; | |
66 | return 1; | |
67 | } | |
68 | ||
69 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer | |
70 | over the key. Skips any whitespace at the beginning and at end. */ | |
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 | /* Tries to match the host name (which must be in all lowercase) against the | |
107 | comma-separated sequence of subpatterns (each possibly preceded by ! to | |
108 | indicate negation). Returns true if there is a positive match; zero | |
109 | otherwise. */ | |
110 | ||
111 | int | |
112 | match_hostname(const char *host, const char *pattern, unsigned int len) | |
113 | { | |
114 | char sub[1024]; | |
115 | int negated; | |
116 | int got_positive; | |
117 | unsigned int i, subi; | |
118 | ||
119 | got_positive = 0; | |
120 | for (i = 0; i < len;) | |
121 | { | |
122 | /* Check if the subpattern is negated. */ | |
123 | if (pattern[i] == '!') | |
124 | { | |
125 | negated = 1; | |
126 | i++; | |
127 | } | |
128 | else | |
129 | negated = 0; | |
130 | ||
131 | /* Extract the subpattern up to a comma or end. Convert the subpattern | |
132 | to lowercase. */ | |
133 | for (subi = 0; | |
134 | i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; | |
135 | subi++, i++) | |
136 | sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; | |
137 | /* If subpattern too long, return failure (no match). */ | |
138 | if (subi >= sizeof(sub) - 1) | |
139 | return 0; | |
140 | ||
141 | /* If the subpattern was terminated by a comma, skip the comma. */ | |
142 | if (i < len && pattern[i] == ',') | |
143 | i++; | |
144 | ||
145 | /* Null-terminate the subpattern. */ | |
146 | sub[subi] = '\0'; | |
147 | ||
148 | /* Try to match the subpattern against the host name. */ | |
149 | if (match_pattern(host, sub)) { | |
150 | if (negated) | |
151 | return 0; /* Fail if host matches any negated subpattern. */ | |
152 | else | |
153 | got_positive = 1; | |
154 | } | |
155 | } | |
156 | ||
157 | /* Return success if got a positive match. If there was a negative match, | |
158 | we have already returned zero and never get here. */ | |
159 | return got_positive; | |
160 | } | |
161 | ||
162 | /* Checks whether the given host (which must be in all lowercase) is | |
163 | already in the list of our known hosts. | |
164 | Returns HOST_OK if the host is known and has the specified key, | |
165 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known | |
166 | but used to have a different host key. */ | |
167 | ||
168 | HostStatus | |
169 | check_host_in_hostfile(const char *filename, | |
170 | const char *host, unsigned int bits, | |
171 | BIGNUM *e, BIGNUM *n, | |
172 | BIGNUM *ke, BIGNUM *kn) | |
173 | { | |
174 | FILE *f; | |
175 | char line[8192]; | |
176 | unsigned int kbits, hostlen; | |
177 | char *cp, *cp2; | |
178 | HostStatus end_return; | |
179 | struct stat st; | |
180 | ||
181 | /* Open the file containing the list of known hosts. */ | |
182 | f = fopen(filename, "r"); | |
183 | if (!f) | |
184 | { | |
185 | if (stat(filename, &st) >= 0) | |
186 | { | |
187 | packet_send_debug("Could not open %.900s for reading.", filename); | |
188 | packet_send_debug("If your home directory is on an NFS volume, it may need to be world-readable."); | |
189 | } | |
190 | return HOST_NEW; | |
191 | } | |
192 | ||
193 | /* Cache the length of the host name. */ | |
194 | hostlen = strlen(host); | |
195 | ||
196 | /* Return value when the loop terminates. This is set to HOST_CHANGED if | |
197 | we have seen a different key for the host and have not found the proper | |
198 | one. */ | |
199 | end_return = HOST_NEW; | |
200 | ||
201 | /* Go trough the file. */ | |
202 | while (fgets(line, sizeof(line), f)) | |
203 | { | |
204 | cp = line; | |
205 | ||
206 | /* Skip any leading whitespace. */ | |
207 | for (; *cp == ' ' || *cp == '\t'; cp++) | |
208 | ; | |
209 | ||
210 | /* Ignore comment lines and empty lines. */ | |
211 | if (!*cp || *cp == '#' || *cp == '\n') | |
212 | continue; | |
213 | ||
214 | /* Find the end of the host name portion. */ | |
215 | for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) | |
216 | ; | |
217 | ||
218 | /* Check if the host name matches. */ | |
219 | if (!match_hostname(host, cp, (unsigned int)(cp2 - cp))) | |
220 | continue; | |
221 | ||
222 | /* Got a match. Skip host name. */ | |
223 | cp = cp2; | |
224 | ||
225 | /* Extract the key from the line. This will skip any leading | |
226 | whitespace. Ignore badly formatted lines. */ | |
227 | if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) | |
228 | continue; | |
229 | ||
230 | /* Check if the current key is the same as the previous one. */ | |
231 | if (kbits == bits && BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) | |
232 | { | |
233 | /* Ok, they match. */ | |
234 | fclose(f); | |
235 | return HOST_OK; | |
236 | } | |
237 | ||
238 | /* They do not match. We will continue to go through the file; however, | |
239 | we note that we will not return that it is new. */ | |
240 | end_return = HOST_CHANGED; | |
241 | } | |
242 | /* Clear variables and close the file. */ | |
243 | fclose(f); | |
244 | ||
245 | /* Return either HOST_NEW or HOST_CHANGED, depending on whether we saw a | |
246 | different key for the host. */ | |
247 | return end_return; | |
248 | } | |
249 | ||
250 | /* Appends an entry to the host file. Returns false if the entry | |
251 | could not be appended. */ | |
252 | ||
253 | int | |
254 | add_host_to_hostfile(const char *filename, const char *host, | |
255 | unsigned int bits, BIGNUM *e, BIGNUM *n) | |
256 | { | |
257 | FILE *f; | |
258 | char *buf; | |
259 | ||
260 | /* Open the file for appending. */ | |
261 | f = fopen(filename, "a"); | |
262 | if (!f) | |
263 | return 0; | |
264 | ||
265 | /* Print the host name and key to the file. */ | |
266 | fprintf(f, "%s %u ", host, bits); | |
267 | buf = BN_bn2dec(e); | |
268 | assert(buf != NULL); | |
269 | fprintf(f, "%s ", buf); | |
270 | free (buf); | |
271 | buf = BN_bn2dec(n); | |
272 | assert(buf != NULL); | |
273 | fprintf(f, "%s\n", buf); | |
274 | free (buf); | |
275 | ||
276 | /* Close the file. */ | |
277 | fclose(f); | |
278 | return 1; | |
279 | } |