1 /* $OpenBSD: hostfile.c,v 1.46 2009/10/11 23:03:15 djm Exp $ */ 2 /* 3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5 * All rights reserved 6 * Functions for manipulating the known hosts files. 7 * 8 * As far as I am concerned, the code I have written for this software 9 * can be used freely for any purpose. Any derived versions of this 10 * software must be clearly marked as such, and if the derived work is 11 * incompatible with the protocol description in the RFC file, it must be 12 * called by a name other than "ssh" or "Secure Shell". 13 * 14 * 15 * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. 16 * Copyright (c) 1999 Niels Provos. All rights reserved. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/types.h> 40 41 #include <netinet/in.h> 42 43 #include <openssl/hmac.h> 44 #include <openssl/sha.h> 45 46 #include <resolv.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 51 #include "xmalloc.h" 52 #include "match.h" 53 #include "key.h" 54 #include "hostfile.h" 55 #include "log.h" 56 57 static int 58 extract_salt(const char *s, u_int l, char *salt, size_t salt_len) 59 { 60 char *p, *b64salt; 61 u_int b64len; 62 int ret; 63 64 if (l < sizeof(HASH_MAGIC) - 1) { 65 debug2("extract_salt: string too short"); 66 return (-1); 67 } 68 if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { 69 debug2("extract_salt: invalid magic identifier"); 70 return (-1); 71 } 72 s += sizeof(HASH_MAGIC) - 1; 73 l -= sizeof(HASH_MAGIC) - 1; 74 if ((p = memchr(s, HASH_DELIM, l)) == NULL) { 75 debug2("extract_salt: missing salt termination character"); 76 return (-1); 77 } 78 79 b64len = p - s; 80 /* Sanity check */ 81 if (b64len == 0 || b64len > 1024) { 82 debug2("extract_salt: bad encoded salt length %u", b64len); 83 return (-1); 84 } 85 b64salt = xmalloc(1 + b64len); 86 memcpy(b64salt, s, b64len); 87 b64salt[b64len] = '\0'; 88 89 ret = __b64_pton(b64salt, salt, salt_len); 90 xfree(b64salt); 91 if (ret == -1) { 92 debug2("extract_salt: salt decode error"); 93 return (-1); 94 } 95 if (ret != SHA_DIGEST_LENGTH) { 96 debug2("extract_salt: expected salt len %d, got %d", 97 SHA_DIGEST_LENGTH, ret); 98 return (-1); 99 } 100 101 return (0); 102 } 103 104 char * 105 host_hash(const char *host, const char *name_from_hostfile, u_int src_len) 106 { 107 const EVP_MD *md = EVP_sha1(); 108 HMAC_CTX mac_ctx; 109 char salt[256], result[256], uu_salt[512], uu_result[512]; 110 static char encoded[1024]; 111 u_int i, len; 112 113 len = EVP_MD_size(md); 114 115 if (name_from_hostfile == NULL) { 116 /* Create new salt */ 117 for (i = 0; i < len; i++) 118 salt[i] = arc4random(); 119 } else { 120 /* Extract salt from known host entry */ 121 if (extract_salt(name_from_hostfile, src_len, salt, 122 sizeof(salt)) == -1) 123 return (NULL); 124 } 125 126 HMAC_Init(&mac_ctx, salt, len, md); 127 HMAC_Update(&mac_ctx, host, strlen(host)); 128 HMAC_Final(&mac_ctx, result, NULL); 129 HMAC_cleanup(&mac_ctx); 130 131 if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 || 132 __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1) 133 fatal("host_hash: __b64_ntop failed"); 134 135 snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt, 136 HASH_DELIM, uu_result); 137 138 return (encoded); 139 } 140 141 /* 142 * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the 143 * pointer over the key. Skips any whitespace at the beginning and at end. 144 */ 145 146 int 147 hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) 148 { 149 char *cp; 150 151 /* Skip leading whitespace. */ 152 for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) 153 ; 154 155 if (key_read(ret, &cp) != 1) 156 return 0; 157 158 /* Skip trailing whitespace. */ 159 for (; *cp == ' ' || *cp == '\t'; cp++) 160 ; 161 162 /* Return results. */ 163 *cpp = cp; 164 *bitsp = key_size(ret); 165 return 1; 166 } 167 168 static int 169 hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum) 170 { 171 if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) 172 return 1; 173 if (bits != BN_num_bits(key->rsa->n)) { 174 logit("Warning: %s, line %d: keysize mismatch for host %s: " 175 "actual %d vs. announced %d.", 176 filename, linenum, host, BN_num_bits(key->rsa->n), bits); 177 logit("Warning: replace %d with %d in %s, line %d.", 178 bits, BN_num_bits(key->rsa->n), filename, linenum); 179 } 180 return 1; 181 } 182 183 /* 184 * Checks whether the given host (which must be in all lowercase) is already 185 * in the list of our known hosts. Returns HOST_OK if the host is known and 186 * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED 187 * if the host is known but used to have a different host key. 188 * 189 * If no 'key' has been specified and a key of type 'keytype' is known 190 * for the specified host, then HOST_FOUND is returned. 191 */ 192 193 static HostStatus 194 check_host_in_hostfile_by_key_or_type(const char *filename, 195 const char *host, const Key *key, int keytype, Key *found, int *numret) 196 { 197 FILE *f; 198 char line[8192]; 199 int linenum = 0; 200 u_int kbits; 201 char *cp, *cp2, *hashed_host; 202 HostStatus end_return; 203 204 debug3("check_host_in_hostfile: host %s filename %s", host, filename); 205 206 /* Open the file containing the list of known hosts. */ 207 f = fopen(filename, "r"); 208 if (!f) 209 return HOST_NEW; 210 211 /* 212 * Return value when the loop terminates. This is set to 213 * HOST_CHANGED if we have seen a different key for the host and have 214 * not found the proper one. 215 */ 216 end_return = HOST_NEW; 217 218 /* Go through the file. */ 219 while (fgets(line, sizeof(line), f)) { 220 cp = line; 221 linenum++; 222 223 /* Skip any leading whitespace, comments and empty lines. */ 224 for (; *cp == ' ' || *cp == '\t'; cp++) 225 ; 226 if (!*cp || *cp == '#' || *cp == '\n') 227 continue; 228 229 /* Find the end of the host name portion. */ 230 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 231 ; 232 233 /* Check if the host name matches. */ 234 if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) { 235 if (*cp != HASH_DELIM) 236 continue; 237 hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); 238 if (hashed_host == NULL) { 239 debug("Invalid hashed host line %d of %s", 240 linenum, filename); 241 continue; 242 } 243 if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) 244 continue; 245 } 246 247 /* Got a match. Skip host name. */ 248 cp = cp2; 249 250 /* 251 * Extract the key from the line. This will skip any leading 252 * whitespace. Ignore badly formatted lines. 253 */ 254 if (!hostfile_read_key(&cp, &kbits, found)) 255 continue; 256 257 if (numret != NULL) 258 *numret = linenum; 259 260 if (key == NULL) { 261 /* we found a key of the requested type */ 262 if (found->type == keytype) { 263 fclose(f); 264 return HOST_FOUND; 265 } 266 continue; 267 } 268 269 if (!hostfile_check_key(kbits, found, host, filename, linenum)) 270 continue; 271 272 /* Check if the current key is the same as the given key. */ 273 if (key_equal(key, found)) { 274 /* Ok, they match. */ 275 debug3("check_host_in_hostfile: match line %d", linenum); 276 fclose(f); 277 return HOST_OK; 278 } 279 /* 280 * They do not match. We will continue to go through the 281 * file; however, we note that we will not return that it is 282 * new. 283 */ 284 end_return = HOST_CHANGED; 285 } 286 /* Clear variables and close the file. */ 287 fclose(f); 288 289 /* 290 * Return either HOST_NEW or HOST_CHANGED, depending on whether we 291 * saw a different key for the host. 292 */ 293 return end_return; 294 } 295 296 HostStatus 297 check_host_in_hostfile(const char *filename, const char *host, const Key *key, 298 Key *found, int *numret) 299 { 300 if (key == NULL) 301 fatal("no key to look up"); 302 return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0, 303 found, numret)); 304 } 305 306 int 307 lookup_key_in_hostfile_by_type(const char *filename, const char *host, 308 int keytype, Key *found, int *numret) 309 { 310 return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, 311 keytype, found, numret) == HOST_FOUND); 312 } 313 314 /* 315 * Appends an entry to the host file. Returns false if the entry could not 316 * be appended. 317 */ 318 319 int 320 add_host_to_hostfile(const char *filename, const char *host, const Key *key, 321 int store_hash) 322 { 323 FILE *f; 324 int success = 0; 325 char *hashed_host = NULL; 326 327 if (key == NULL) 328 return 1; /* XXX ? */ 329 f = fopen(filename, "a"); 330 if (!f) 331 return 0; 332 333 if (store_hash) { 334 if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { 335 error("add_host_to_hostfile: host_hash failed"); 336 fclose(f); 337 return 0; 338 } 339 } 340 fprintf(f, "%s ", store_hash ? hashed_host : host); 341 342 if (key_write(key, f)) { 343 success = 1; 344 } else { 345 error("add_host_to_hostfile: saving key in %s failed", filename); 346 } 347 fprintf(f, "\n"); 348 fclose(f); 349 return success; 350 } 351