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