diff options
author | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
commit | d4a8b7e34dd619a4debf9a206c81db26d1402ea6 (patch) | |
tree | a47d770a2f790f40d18b0982d4e55fa7cfb1fa3b /hostfile.c |
Initial revision
Diffstat (limited to 'hostfile.c')
-rw-r--r-- | hostfile.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/hostfile.c b/hostfile.c new file mode 100644 index 000000000..ca0fe88a2 --- /dev/null +++ b/hostfile.c | |||
@@ -0,0 +1,279 @@ | |||
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: hostfile.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | ||
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 | } | ||