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("$OpenBSD: hostfile.c,v 1.13 2000/02/18 10:20:20 markus Exp $"); 18 19 #include "packet.h" 20 #include "ssh.h" 21 22 /* 23 * Reads a multiple-precision integer in decimal 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 */ 29 30 int 31 auth_rsa_read_bignum(char **cpp, BIGNUM * value) 32 { 33 char *cp = *cpp; 34 int old; 35 36 /* Skip any leading whitespace. */ 37 for (; *cp == ' ' || *cp == '\t'; cp++) 38 ; 39 40 /* Check that it begins with a decimal digit. */ 41 if (*cp < '0' || *cp > '9') 42 return 0; 43 44 /* Save starting position. */ 45 *cpp = cp; 46 47 /* Move forward until all decimal digits skipped. */ 48 for (; *cp >= '0' && *cp <= '9'; cp++) 49 ; 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; 65 } 66 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 */ 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 /* 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 */ 112 113 int 114 match_hostname(const char *host, const char *pattern, unsigned int len) 115 { 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 130 /* 131 * Extract the subpattern up to a comma or end. Convert the 132 * subpattern to lowercase. 133 */ 134 for (subi = 0; 135 i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; 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 142 /* If the subpattern was terminated by a comma, skip the comma. */ 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) 152 return 0; /* Fail */ 153 else 154 got_positive = 1; 155 } 156 } 157 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 */ 162 return got_positive; 163 } 164 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 */ 171 172 HostStatus 173 check_host_in_hostfile(const char *filename, const char *host, 174 BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn) 175 { 176 FILE *f; 177 char line[8192]; 178 int linenum = 0; 179 unsigned int kbits, hostlen; 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 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 */ 196 end_return = HOST_NEW; 197 198 /* Go trough the file. */ 199 while (fgets(line, sizeof(line), f)) { 200 cp = line; 201 linenum++; 202 203 /* Skip any leading whitespace, comments and empty lines. */ 204 for (; *cp == ' ' || *cp == '\t'; cp++) 205 ; 206 if (!*cp || *cp == '#' || *cp == '\n') 207 continue; 208 209 /* Find the end of the host name portion. */ 210 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 211 ; 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 220 /* 221 * Extract the key from the line. This will skip any leading 222 * whitespace. Ignore badly formatted lines. 223 */ 224 if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) 225 continue; 226 227 if (kbits != BN_num_bits(kn)) { 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); 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 } 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 */ 245 end_return = HOST_CHANGED; 246 } 247 /* Clear variables and close the file. */ 248 fclose(f); 249 250 /* 251 * Return either HOST_NEW or HOST_CHANGED, depending on whether we 252 * saw a different key for the host. 253 */ 254 return end_return; 255 } 256 257 /* 258 * Appends an entry to the host file. Returns false if the entry could not 259 * be appended. 260 */ 261 262 int 263 add_host_to_hostfile(const char *filename, const char *host, 264 BIGNUM * e, BIGNUM * n) 265 { 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; 300 } 301