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