Annotation of src/usr.bin/ssh/hostfile.c, Revision 1.12
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.12 ! markus 17: RCSID("$OpenBSD: hostfile.c,v 1.11 2000/01/04 00:07:59 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;
1.12 ! markus 182: unsigned int kbits, hostlen;
1.7 markus 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: /* Go trough the file. */
202: while (fgets(line, sizeof(line), f)) {
203: cp = line;
204: linenum++;
205:
1.9 markus 206: /* Skip any leading whitespace, comments and empty lines. */
207: for (; *cp == ' ' || *cp == '\t'; cp++)
208: ;
1.7 markus 209: if (!*cp || *cp == '#' || *cp == '\n')
210: continue;
211:
212: /* Find the end of the host name portion. */
1.9 markus 213: for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
214: ;
1.7 markus 215:
216: /* Check if the host name matches. */
217: if (!match_hostname(host, cp, (unsigned int) (cp2 - cp)))
218: continue;
219:
220: /* Got a match. Skip host name. */
221: cp = cp2;
222:
1.9 markus 223: /*
224: * Extract the key from the line. This will skip any leading
225: * whitespace. Ignore badly formatted lines.
226: */
1.7 markus 227: if (!auth_rsa_read_key(&cp, &kbits, ke, kn))
228: continue;
229:
230: if (kbits != BN_num_bits(kn)) {
1.10 markus 231: error("Warning: %s, line %d: keysize mismatch for host %s: "
232: "actual %d vs. announced %d.",
233: filename, linenum, host, BN_num_bits(kn), kbits);
1.7 markus 234: error("Warning: replace %d with %d in %s, line %d.",
235: kbits, BN_num_bits(kn), filename, linenum);
236: }
237: /* Check if the current key is the same as the given key. */
238: if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) {
239: /* Ok, they match. */
240: fclose(f);
241: return HOST_OK;
242: }
1.9 markus 243: /*
244: * They do not match. We will continue to go through the
245: * file; however, we note that we will not return that it is
246: * new.
247: */
1.7 markus 248: end_return = HOST_CHANGED;
1.1 deraadt 249: }
1.7 markus 250: /* Clear variables and close the file. */
251: fclose(f);
252:
1.9 markus 253: /*
254: * Return either HOST_NEW or HOST_CHANGED, depending on whether we
255: * saw a different key for the host.
256: */
1.7 markus 257: return end_return;
1.1 deraadt 258: }
259:
1.9 markus 260: /*
261: * Appends an entry to the host file. Returns false if the entry could not
262: * be appended.
263: */
1.1 deraadt 264:
1.2 provos 265: int
266: add_host_to_hostfile(const char *filename, const char *host,
1.7 markus 267: BIGNUM * e, BIGNUM * n)
1.1 deraadt 268: {
1.7 markus 269: FILE *f;
270: char *buf;
271: unsigned int bits;
272:
273: /* Open the file for appending. */
274: f = fopen(filename, "a");
275: if (!f)
276: return 0;
277:
278: /* size of modulus 'n' */
279: bits = BN_num_bits(n);
280:
281: /* Print the host name and key to the file. */
282: fprintf(f, "%s %u ", host, bits);
283: buf = BN_bn2dec(e);
284: if (buf == NULL) {
285: error("add_host_to_hostfile: BN_bn2dec(e) failed");
286: fclose(f);
287: return 0;
288: }
289: fprintf(f, "%s ", buf);
290: free(buf);
291: buf = BN_bn2dec(n);
292: if (buf == NULL) {
293: error("add_host_to_hostfile: BN_bn2dec(n) failed");
294: fclose(f);
295: return 0;
296: }
297: fprintf(f, "%s\n", buf);
298: free(buf);
299:
300: /* Close the file. */
301: fclose(f);
302: return 1;
1.1 deraadt 303: }