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