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