1 | /* |
---|
2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
---|
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
---|
4 | * All rights reserved |
---|
5 | * Functions for manipulating the known hosts files. |
---|
6 | * |
---|
7 | * As far as I am concerned, the code I have written for this software |
---|
8 | * can be used freely for any purpose. Any derived versions of this |
---|
9 | * software must be clearly marked as such, and if the derived work is |
---|
10 | * incompatible with the protocol description in the RFC file, it must be |
---|
11 | * called by a name other than "ssh" or "Secure Shell". |
---|
12 | * |
---|
13 | * |
---|
14 | * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. |
---|
15 | * Copyright (c) 1999 Niels Provos. All rights reserved. |
---|
16 | * |
---|
17 | * Redistribution and use in source and binary forms, with or without |
---|
18 | * modification, are permitted provided that the following conditions |
---|
19 | * are met: |
---|
20 | * 1. Redistributions of source code must retain the above copyright |
---|
21 | * notice, this list of conditions and the following disclaimer. |
---|
22 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
23 | * notice, this list of conditions and the following disclaimer in the |
---|
24 | * documentation and/or other materials provided with the distribution. |
---|
25 | * |
---|
26 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
---|
27 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
---|
28 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
---|
29 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
---|
30 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
---|
31 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
---|
32 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
---|
33 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
---|
34 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
---|
35 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
36 | */ |
---|
37 | |
---|
38 | #include "includes.h" |
---|
39 | RCSID("$OpenBSD: hostfile.c,v 1.30 2002/07/24 16:11:18 markus Exp $"); |
---|
40 | |
---|
41 | #include "packet.h" |
---|
42 | #include "match.h" |
---|
43 | #include "key.h" |
---|
44 | #include "hostfile.h" |
---|
45 | #include "log.h" |
---|
46 | |
---|
47 | /* |
---|
48 | * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the |
---|
49 | * pointer over the key. Skips any whitespace at the beginning and at end. |
---|
50 | */ |
---|
51 | |
---|
52 | int |
---|
53 | hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) |
---|
54 | { |
---|
55 | char *cp; |
---|
56 | |
---|
57 | /* Skip leading whitespace. */ |
---|
58 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) |
---|
59 | ; |
---|
60 | |
---|
61 | if (key_read(ret, &cp) != 1) |
---|
62 | return 0; |
---|
63 | |
---|
64 | /* Skip trailing whitespace. */ |
---|
65 | for (; *cp == ' ' || *cp == '\t'; cp++) |
---|
66 | ; |
---|
67 | |
---|
68 | /* Return results. */ |
---|
69 | *cpp = cp; |
---|
70 | *bitsp = key_size(ret); |
---|
71 | return 1; |
---|
72 | } |
---|
73 | |
---|
74 | static int |
---|
75 | hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum) |
---|
76 | { |
---|
77 | if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) |
---|
78 | return 1; |
---|
79 | if (bits != BN_num_bits(key->rsa->n)) { |
---|
80 | log("Warning: %s, line %d: keysize mismatch for host %s: " |
---|
81 | "actual %d vs. announced %d.", |
---|
82 | filename, linenum, host, BN_num_bits(key->rsa->n), bits); |
---|
83 | log("Warning: replace %d with %d in %s, line %d.", |
---|
84 | bits, BN_num_bits(key->rsa->n), filename, linenum); |
---|
85 | } |
---|
86 | return 1; |
---|
87 | } |
---|
88 | |
---|
89 | /* |
---|
90 | * Checks whether the given host (which must be in all lowercase) is already |
---|
91 | * in the list of our known hosts. Returns HOST_OK if the host is known and |
---|
92 | * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED |
---|
93 | * if the host is known but used to have a different host key. |
---|
94 | * |
---|
95 | * If no 'key' has been specified and a key of type 'keytype' is known |
---|
96 | * for the specified host, then HOST_FOUND is returned. |
---|
97 | */ |
---|
98 | |
---|
99 | static HostStatus |
---|
100 | check_host_in_hostfile_by_key_or_type(const char *filename, |
---|
101 | const char *host, Key *key, int keytype, Key *found, int *numret) |
---|
102 | { |
---|
103 | FILE *f; |
---|
104 | char line[8192]; |
---|
105 | int linenum = 0; |
---|
106 | u_int kbits; |
---|
107 | char *cp, *cp2; |
---|
108 | HostStatus end_return; |
---|
109 | |
---|
110 | debug3("check_host_in_hostfile: filename %s", filename); |
---|
111 | |
---|
112 | /* Open the file containing the list of known hosts. */ |
---|
113 | f = fopen(filename, "r"); |
---|
114 | if (!f) |
---|
115 | return HOST_NEW; |
---|
116 | |
---|
117 | /* |
---|
118 | * Return value when the loop terminates. This is set to |
---|
119 | * HOST_CHANGED if we have seen a different key for the host and have |
---|
120 | * not found the proper one. |
---|
121 | */ |
---|
122 | end_return = HOST_NEW; |
---|
123 | |
---|
124 | /* Go through the file. */ |
---|
125 | while (fgets(line, sizeof(line), f)) { |
---|
126 | cp = line; |
---|
127 | linenum++; |
---|
128 | |
---|
129 | /* Skip any leading whitespace, comments and empty lines. */ |
---|
130 | for (; *cp == ' ' || *cp == '\t'; cp++) |
---|
131 | ; |
---|
132 | if (!*cp || *cp == '#' || *cp == '\n') |
---|
133 | continue; |
---|
134 | |
---|
135 | /* Find the end of the host name portion. */ |
---|
136 | for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) |
---|
137 | ; |
---|
138 | |
---|
139 | /* Check if the host name matches. */ |
---|
140 | if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) |
---|
141 | continue; |
---|
142 | |
---|
143 | /* Got a match. Skip host name. */ |
---|
144 | cp = cp2; |
---|
145 | |
---|
146 | /* |
---|
147 | * Extract the key from the line. This will skip any leading |
---|
148 | * whitespace. Ignore badly formatted lines. |
---|
149 | */ |
---|
150 | if (!hostfile_read_key(&cp, &kbits, found)) |
---|
151 | continue; |
---|
152 | |
---|
153 | if (numret != NULL) |
---|
154 | *numret = linenum; |
---|
155 | |
---|
156 | if (key == NULL) { |
---|
157 | /* we found a key of the requested type */ |
---|
158 | if (found->type == keytype) |
---|
159 | return HOST_FOUND; |
---|
160 | continue; |
---|
161 | } |
---|
162 | |
---|
163 | if (!hostfile_check_key(kbits, found, host, filename, linenum)) |
---|
164 | continue; |
---|
165 | |
---|
166 | /* Check if the current key is the same as the given key. */ |
---|
167 | if (key_equal(key, found)) { |
---|
168 | /* Ok, they match. */ |
---|
169 | debug3("check_host_in_hostfile: match line %d", linenum); |
---|
170 | fclose(f); |
---|
171 | return HOST_OK; |
---|
172 | } |
---|
173 | /* |
---|
174 | * They do not match. We will continue to go through the |
---|
175 | * file; however, we note that we will not return that it is |
---|
176 | * new. |
---|
177 | */ |
---|
178 | end_return = HOST_CHANGED; |
---|
179 | } |
---|
180 | /* Clear variables and close the file. */ |
---|
181 | fclose(f); |
---|
182 | |
---|
183 | /* |
---|
184 | * Return either HOST_NEW or HOST_CHANGED, depending on whether we |
---|
185 | * saw a different key for the host. |
---|
186 | */ |
---|
187 | return end_return; |
---|
188 | } |
---|
189 | |
---|
190 | HostStatus |
---|
191 | check_host_in_hostfile(const char *filename, const char *host, Key *key, |
---|
192 | Key *found, int *numret) |
---|
193 | { |
---|
194 | if (key == NULL) |
---|
195 | fatal("no key to look up"); |
---|
196 | return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0, |
---|
197 | found, numret)); |
---|
198 | } |
---|
199 | |
---|
200 | int |
---|
201 | lookup_key_in_hostfile_by_type(const char *filename, const char *host, |
---|
202 | int keytype, Key *found, int *numret) |
---|
203 | { |
---|
204 | return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, |
---|
205 | keytype, found, numret) == HOST_FOUND); |
---|
206 | } |
---|
207 | |
---|
208 | /* |
---|
209 | * Appends an entry to the host file. Returns false if the entry could not |
---|
210 | * be appended. |
---|
211 | */ |
---|
212 | |
---|
213 | int |
---|
214 | add_host_to_hostfile(const char *filename, const char *host, Key *key) |
---|
215 | { |
---|
216 | FILE *f; |
---|
217 | int success = 0; |
---|
218 | if (key == NULL) |
---|
219 | return 1; /* XXX ? */ |
---|
220 | f = fopen(filename, "a"); |
---|
221 | if (!f) |
---|
222 | return 0; |
---|
223 | fprintf(f, "%s ", host); |
---|
224 | if (key_write(key, f)) { |
---|
225 | success = 1; |
---|
226 | } else { |
---|
227 | error("add_host_to_hostfile: saving key in %s failed", filename); |
---|
228 | } |
---|
229 | fprintf(f, "\n"); |
---|
230 | fclose(f); |
---|
231 | return success; |
---|
232 | } |
---|