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 /* $OpenBSD: hostfile.c,v 1.45 2006/08/03 03:34:42 deraadt Exp $ */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include "includes.h" 43 44 #include <openssl/opensslconf.h> 45 #include <openssl/hmac.h> 46 #include <openssl/sha.h> 47 48 #include "packet.h" 49 #include "xmalloc.h" 50 #include "match.h" 51 #include "key.h" 52 #include "hostfile.h" 53 #include "log.h" 54 55 /* 56 * Format of a hashed hostname is <MAGIC><SALT>|<HASHED_HOSTNAME>. <MAGIC> is 57 * "|1|". As in non-hashed hostnames this whole string is then followed by a 58 * space, a key type and the key (which is out of scope of this function). 59 * 60 * Example what can be in 's': 61 * 62 * |1|t17NtsuXSLwP0H0eYdd8vJeNakM=|9XFVPh3jZUrfY6YCWn8Ua5eGZtA= 63 */ 64 static int 65 extract_salt(const char *s, u_int l, char *salt, size_t salt_len) 66 { 67 char *p; 68 u_char *b64salt; 69 u_int b64len; 70 int ret; 71 72 if (l < sizeof(HASH_MAGIC) - 1) { 73 debug2("extract_salt: string too short"); 74 return (-1); 75 } 76 if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { 77 debug2("extract_salt: invalid magic identifier"); 78 return (-1); 79 } 80 s += sizeof(HASH_MAGIC) - 1; 81 l -= sizeof(HASH_MAGIC) - 1; 82 if ((p = memchr(s, HASH_DELIM, l)) == NULL) { 83 debug2("extract_salt: missing salt termination character"); 84 return (-1); 85 } 86 87 b64len = p - s; 88 /* Sanity check */ 89 if (b64len == 0 || b64len > 1024) { 90 debug2("extract_salt: bad encoded salt length %u", b64len); 91 return (-1); 92 } 93 b64salt = xmalloc(1 + b64len); 94 memcpy(b64salt, s, b64len); 95 b64salt[b64len] = '\0'; 96 97 ret = __b64_pton(b64salt, (u_char *) salt, salt_len); 98 xfree(b64salt); 99 if (ret == -1) { 100 debug2("extract_salt: salt decode error"); 101 return (-1); 102 } 103 if (ret != SHA_DIGEST_LENGTH) { 104 debug2("extract_salt: expected salt len %d, got %d", 105 SHA_DIGEST_LENGTH, ret); 106 return (-1); 107 } 108 109 return (0); 110 } 111 112 char * 113 host_hash(const char *host, const char *name_from_hostfile, u_int src_len) 114 { 115 const EVP_MD *md = EVP_sha1(); 116 HMAC_CTX mac_ctx; 117 char salt[256], result[256], uu_salt[512], uu_result[512]; 118 static char encoded[1024]; 119 u_int i, len; 120 121 len = EVP_MD_size(md); 122 123 if (name_from_hostfile == NULL) { 124 /* Create new salt */ 125 for (i = 0; i < len; i++) 126 salt[i] = arc4random(); 127 } else { 128 /* Extract salt from known host entry */ 129 if (extract_salt(name_from_hostfile, src_len, salt, 130 sizeof(salt)) == -1) 131 return (NULL); 132 } 133 134 HMAC_Init(&mac_ctx, salt, len, md); 135 HMAC_Update(&mac_ctx, (u_char *) host, strlen(host)); 136 HMAC_Final(&mac_ctx, (u_char *) result, NULL); 137 HMAC_cleanup(&mac_ctx); 138 139 if (__b64_ntop((u_char *) salt, len, uu_salt, sizeof(uu_salt)) == -1 || 140 __b64_ntop((u_char *) result, len, uu_result, sizeof(uu_result)) == -1) 141 fatal("host_hash: __b64_ntop failed"); 142 143 snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt, 144 HASH_DELIM, uu_result); 145 146 return (encoded); 147 } 148 149 /* 150 * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the 151 * pointer over the key. Skips any whitespace at the beginning and at end. 152 */ 153 154 int 155 hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) 156 { 157 char *cp; 158 159 /* Skip leading whitespace. */ 160 for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) 161 ; 162 163 if (key_read(ret, &cp) != 1) 164 return 0; 165 166 /* Skip trailing whitespace. */ 167 for (; *cp == ' ' || *cp == '\t'; cp++) 168 ; 169 170 /* Return results. */ 171 *cpp = cp; 172 *bitsp = key_size(ret); 173 return 1; 174 } 175 176 static int 177 hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum) 178 { 179 if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) 180 return 1; 181 if (bits != BN_num_bits(key->rsa->n)) { 182 log("Warning: %s, line %d: keysize mismatch for host %s: " 183 "actual %d vs. announced %d.", 184 filename, linenum, host, BN_num_bits(key->rsa->n), bits); 185 log("Warning: replace %d with %d in %s, line %d.", 186 bits, BN_num_bits(key->rsa->n), filename, linenum); 187 } 188 return 1; 189 } 190 191 /* 192 * Checks whether the given host (which must be in all lowercase) is already 193 * in the list of our known hosts. Returns HOST_OK if the host is known and 194 * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED 195 * if the host is known but used to have a different host key. 196 * 197 * If no 'key' has been specified and a key of type 'keytype' is known 198 * for the specified host, then HOST_FOUND is returned. 199 */ 200 201 static HostStatus 202 check_host_in_hostfile_by_key_or_type(const char *filename, 203 const char *host, const Key *key, int keytype, Key *found, int *numret) 204 { 205 FILE *f; 206 char line[8192]; 207 int linenum = 0; 208 u_int kbits; 209 char *cp, *cp2, *hashed_host; 210 HostStatus end_return; 211 212 debug3("check_host_in_hostfile: filename %s", filename); 213 214 /* Open the file containing the list of known hosts. */ 215 f = fopen(filename, "r"); 216 if (!f) 217 return HOST_NEW; 218 219 /* 220 * Return value when the loop terminates. This is set to 221 * HOST_CHANGED if we have seen a different key for the host and have 222 * not found the proper one. 223 */ 224 end_return = HOST_NEW; 225 226 /* Go through the file. */ 227 while (fgets(line, sizeof(line), f)) { 228 cp = line; 229 linenum++; 230 231 /* Skip any leading whitespace, comments and empty lines. */ 232 for (; *cp == ' ' || *cp == '\t'; cp++) 233 ; 234 if (!*cp || *cp == '#' || *cp == '\n') 235 continue; 236 237 /* Find the end of the host name portion. */ 238 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 239 ; 240 241 /* Check if the host name matches. */ 242 if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) { 243 if (*cp != HASH_DELIM) 244 continue; 245 hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); 246 if (hashed_host == NULL) { 247 debug("Invalid hashed host line %d of %s", 248 linenum, filename); 249 continue; 250 } 251 if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) 252 continue; 253 } 254 255 /* Got a match. Skip host name. */ 256 cp = cp2; 257 258 /* 259 * Extract the key from the line. This will skip any leading 260 * whitespace. Ignore badly formatted lines. 261 */ 262 if (!hostfile_read_key(&cp, &kbits, found)) 263 continue; 264 265 if (numret != NULL) 266 *numret = linenum; 267 268 if (key == NULL) { 269 /* we found a key of the requested type */ 270 if (found->type == keytype) { 271 fclose(f); 272 return HOST_FOUND; 273 } 274 continue; 275 } 276 277 if (!hostfile_check_key(kbits, found, host, filename, linenum)) 278 continue; 279 280 /* Check if the current key is the same as the given key. */ 281 if (key_equal(key, found)) { 282 /* Ok, they match. */ 283 debug3("check_host_in_hostfile: match line %d", linenum); 284 fclose(f); 285 return HOST_OK; 286 } 287 /* 288 * They do not match. We will continue to go through the 289 * file; however, we note that we will not return that it is 290 * new. 291 */ 292 end_return = HOST_CHANGED; 293 } 294 /* Clear variables and close the file. */ 295 fclose(f); 296 297 /* 298 * Return either HOST_NEW or HOST_CHANGED, depending on whether we 299 * saw a different key for the host. 300 */ 301 return end_return; 302 } 303 304 HostStatus 305 check_host_in_hostfile(const char *filename, const char *host, const Key *key, 306 Key *found, int *numret) 307 { 308 if (key == NULL) 309 fatal("no key to look up"); 310 return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0, 311 found, numret)); 312 } 313 314 int 315 lookup_key_in_hostfile_by_type(const char *filename, const char *host, 316 int keytype, Key *found, int *numret) 317 { 318 return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, 319 keytype, found, numret) == HOST_FOUND); 320 } 321 322 /* 323 * Appends an entry to the host file. Returns false if the entry could not 324 * be appended. 325 */ 326 327 int 328 add_host_to_hostfile(const char *filename, const char *host, const Key *key, 329 int store_hash) 330 { 331 FILE *f; 332 int success = 0; 333 char *hashed_host = NULL; 334 335 if (key == NULL) 336 return 1; /* XXX ? */ 337 f = fopen(filename, "a"); 338 if (!f) 339 return 0; 340 341 if (store_hash) { 342 if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { 343 error("add_host_to_hostfile: host_hash failed"); 344 fclose(f); 345 return 0; 346 } 347 } 348 fprintf(f, "%s ", store_hash ? hashed_host : host); 349 350 if (key_write(key, f)) { 351 success = 1; 352 } else { 353 error("add_host_to_hostfile: saving key in %s failed", filename); 354 } 355 fprintf(f, "\n"); 356 fclose(f); 357 return success; 358 }