1*4a421b63SDag-Erling Smørgrav /* $OpenBSD: hostfile.c,v 1.50 2010/12/04 13:31:37 djm Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * Functions for manipulating the known hosts files. 7511b41d2SMark Murray * 8c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 9c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 11c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13c2d3a559SKris Kennaway * 14c2d3a559SKris Kennaway * 15c2d3a559SKris Kennaway * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. 16c2d3a559SKris Kennaway * Copyright (c) 1999 Niels Provos. All rights reserved. 17c2d3a559SKris Kennaway * 18c2d3a559SKris Kennaway * Redistribution and use in source and binary forms, with or without 19c2d3a559SKris Kennaway * modification, are permitted provided that the following conditions 20c2d3a559SKris Kennaway * are met: 21c2d3a559SKris Kennaway * 1. Redistributions of source code must retain the above copyright 22c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer. 23c2d3a559SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 24c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer in the 25c2d3a559SKris Kennaway * documentation and/or other materials provided with the distribution. 26c2d3a559SKris Kennaway * 27c2d3a559SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28c2d3a559SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29c2d3a559SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30c2d3a559SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31c2d3a559SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32c2d3a559SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33c2d3a559SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34c2d3a559SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35c2d3a559SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36c2d3a559SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37511b41d2SMark Murray */ 38511b41d2SMark Murray 39511b41d2SMark Murray #include "includes.h" 40aa49c926SDag-Erling Smørgrav 41333ee039SDag-Erling Smørgrav #include <sys/types.h> 42333ee039SDag-Erling Smørgrav 43333ee039SDag-Erling Smørgrav #include <netinet/in.h> 44333ee039SDag-Erling Smørgrav 45aa49c926SDag-Erling Smørgrav #include <openssl/hmac.h> 46aa49c926SDag-Erling Smørgrav #include <openssl/sha.h> 47511b41d2SMark Murray 48333ee039SDag-Erling Smørgrav #include <resolv.h> 49333ee039SDag-Erling Smørgrav #include <stdarg.h> 50333ee039SDag-Erling Smørgrav #include <stdio.h> 51333ee039SDag-Erling Smørgrav #include <stdlib.h> 52333ee039SDag-Erling Smørgrav #include <string.h> 53333ee039SDag-Erling Smørgrav 54333ee039SDag-Erling Smørgrav #include "xmalloc.h" 55a8f6863aSKris Kennaway #include "match.h" 56a8f6863aSKris Kennaway #include "key.h" 57a8f6863aSKris Kennaway #include "hostfile.h" 58ca3176e7SBrian Feldman #include "log.h" 59*4a421b63SDag-Erling Smørgrav #include "misc.h" 60*4a421b63SDag-Erling Smørgrav 61*4a421b63SDag-Erling Smørgrav struct hostkeys { 62*4a421b63SDag-Erling Smørgrav struct hostkey_entry *entries; 63*4a421b63SDag-Erling Smørgrav u_int num_entries; 64*4a421b63SDag-Erling Smørgrav }; 65aa49c926SDag-Erling Smørgrav 66aa49c926SDag-Erling Smørgrav static int 67aa49c926SDag-Erling Smørgrav extract_salt(const char *s, u_int l, char *salt, size_t salt_len) 68aa49c926SDag-Erling Smørgrav { 69aa49c926SDag-Erling Smørgrav char *p, *b64salt; 70aa49c926SDag-Erling Smørgrav u_int b64len; 71aa49c926SDag-Erling Smørgrav int ret; 72aa49c926SDag-Erling Smørgrav 73aa49c926SDag-Erling Smørgrav if (l < sizeof(HASH_MAGIC) - 1) { 74aa49c926SDag-Erling Smørgrav debug2("extract_salt: string too short"); 75aa49c926SDag-Erling Smørgrav return (-1); 76aa49c926SDag-Erling Smørgrav } 77aa49c926SDag-Erling Smørgrav if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { 78aa49c926SDag-Erling Smørgrav debug2("extract_salt: invalid magic identifier"); 79aa49c926SDag-Erling Smørgrav return (-1); 80aa49c926SDag-Erling Smørgrav } 81aa49c926SDag-Erling Smørgrav s += sizeof(HASH_MAGIC) - 1; 82aa49c926SDag-Erling Smørgrav l -= sizeof(HASH_MAGIC) - 1; 83aa49c926SDag-Erling Smørgrav if ((p = memchr(s, HASH_DELIM, l)) == NULL) { 84aa49c926SDag-Erling Smørgrav debug2("extract_salt: missing salt termination character"); 85aa49c926SDag-Erling Smørgrav return (-1); 86aa49c926SDag-Erling Smørgrav } 87aa49c926SDag-Erling Smørgrav 88aa49c926SDag-Erling Smørgrav b64len = p - s; 89aa49c926SDag-Erling Smørgrav /* Sanity check */ 90aa49c926SDag-Erling Smørgrav if (b64len == 0 || b64len > 1024) { 91aa49c926SDag-Erling Smørgrav debug2("extract_salt: bad encoded salt length %u", b64len); 92aa49c926SDag-Erling Smørgrav return (-1); 93aa49c926SDag-Erling Smørgrav } 94aa49c926SDag-Erling Smørgrav b64salt = xmalloc(1 + b64len); 95aa49c926SDag-Erling Smørgrav memcpy(b64salt, s, b64len); 96aa49c926SDag-Erling Smørgrav b64salt[b64len] = '\0'; 97aa49c926SDag-Erling Smørgrav 98aa49c926SDag-Erling Smørgrav ret = __b64_pton(b64salt, salt, salt_len); 99aa49c926SDag-Erling Smørgrav xfree(b64salt); 100aa49c926SDag-Erling Smørgrav if (ret == -1) { 101aa49c926SDag-Erling Smørgrav debug2("extract_salt: salt decode error"); 102aa49c926SDag-Erling Smørgrav return (-1); 103aa49c926SDag-Erling Smørgrav } 104aa49c926SDag-Erling Smørgrav if (ret != SHA_DIGEST_LENGTH) { 105b74df5b2SDag-Erling Smørgrav debug2("extract_salt: expected salt len %d, got %d", 106b74df5b2SDag-Erling Smørgrav SHA_DIGEST_LENGTH, ret); 107aa49c926SDag-Erling Smørgrav return (-1); 108aa49c926SDag-Erling Smørgrav } 109aa49c926SDag-Erling Smørgrav 110aa49c926SDag-Erling Smørgrav return (0); 111aa49c926SDag-Erling Smørgrav } 112aa49c926SDag-Erling Smørgrav 113aa49c926SDag-Erling Smørgrav char * 114aa49c926SDag-Erling Smørgrav host_hash(const char *host, const char *name_from_hostfile, u_int src_len) 115aa49c926SDag-Erling Smørgrav { 116aa49c926SDag-Erling Smørgrav const EVP_MD *md = EVP_sha1(); 117aa49c926SDag-Erling Smørgrav HMAC_CTX mac_ctx; 118aa49c926SDag-Erling Smørgrav char salt[256], result[256], uu_salt[512], uu_result[512]; 119aa49c926SDag-Erling Smørgrav static char encoded[1024]; 120aa49c926SDag-Erling Smørgrav u_int i, len; 121aa49c926SDag-Erling Smørgrav 122aa49c926SDag-Erling Smørgrav len = EVP_MD_size(md); 123aa49c926SDag-Erling Smørgrav 124aa49c926SDag-Erling Smørgrav if (name_from_hostfile == NULL) { 125aa49c926SDag-Erling Smørgrav /* Create new salt */ 126aa49c926SDag-Erling Smørgrav for (i = 0; i < len; i++) 127aa49c926SDag-Erling Smørgrav salt[i] = arc4random(); 128aa49c926SDag-Erling Smørgrav } else { 129aa49c926SDag-Erling Smørgrav /* Extract salt from known host entry */ 130aa49c926SDag-Erling Smørgrav if (extract_salt(name_from_hostfile, src_len, salt, 131aa49c926SDag-Erling Smørgrav sizeof(salt)) == -1) 132aa49c926SDag-Erling Smørgrav return (NULL); 133aa49c926SDag-Erling Smørgrav } 134aa49c926SDag-Erling Smørgrav 135aa49c926SDag-Erling Smørgrav HMAC_Init(&mac_ctx, salt, len, md); 136aa49c926SDag-Erling Smørgrav HMAC_Update(&mac_ctx, host, strlen(host)); 137aa49c926SDag-Erling Smørgrav HMAC_Final(&mac_ctx, result, NULL); 138aa49c926SDag-Erling Smørgrav HMAC_cleanup(&mac_ctx); 139aa49c926SDag-Erling Smørgrav 140aa49c926SDag-Erling Smørgrav if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 || 141aa49c926SDag-Erling Smørgrav __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1) 142aa49c926SDag-Erling Smørgrav fatal("host_hash: __b64_ntop failed"); 143aa49c926SDag-Erling Smørgrav 144aa49c926SDag-Erling Smørgrav snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt, 145aa49c926SDag-Erling Smørgrav HASH_DELIM, uu_result); 146aa49c926SDag-Erling Smørgrav 147aa49c926SDag-Erling Smørgrav return (encoded); 148aa49c926SDag-Erling Smørgrav } 149511b41d2SMark Murray 150511b41d2SMark Murray /* 151a8f6863aSKris Kennaway * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the 152a8f6863aSKris Kennaway * pointer over the key. Skips any whitespace at the beginning and at end. 153511b41d2SMark Murray */ 154511b41d2SMark Murray 155511b41d2SMark Murray int 156ca3176e7SBrian Feldman hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) 157511b41d2SMark Murray { 158511b41d2SMark Murray char *cp; 159511b41d2SMark Murray 160511b41d2SMark Murray /* Skip leading whitespace. */ 161511b41d2SMark Murray for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) 162511b41d2SMark Murray ; 163511b41d2SMark Murray 164ca3176e7SBrian Feldman if (key_read(ret, &cp) != 1) 165511b41d2SMark Murray return 0; 166511b41d2SMark Murray 167511b41d2SMark Murray /* Skip trailing whitespace. */ 168511b41d2SMark Murray for (; *cp == ' ' || *cp == '\t'; cp++) 169511b41d2SMark Murray ; 170511b41d2SMark Murray 171511b41d2SMark Murray /* Return results. */ 172511b41d2SMark Murray *cpp = cp; 173*4a421b63SDag-Erling Smørgrav if (bitsp != NULL) 174ca3176e7SBrian Feldman *bitsp = key_size(ret); 175511b41d2SMark Murray return 1; 176511b41d2SMark Murray } 177511b41d2SMark Murray 178af12a3e7SDag-Erling Smørgrav static int 179*4a421b63SDag-Erling Smørgrav hostfile_check_key(int bits, const Key *key, const char *host, 180*4a421b63SDag-Erling Smørgrav const char *filename, u_long linenum) 181511b41d2SMark Murray { 182ca3176e7SBrian Feldman if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) 183a8f6863aSKris Kennaway return 1; 184a8f6863aSKris Kennaway if (bits != BN_num_bits(key->rsa->n)) { 185*4a421b63SDag-Erling Smørgrav logit("Warning: %s, line %lu: keysize mismatch for host %s: " 186a8f6863aSKris Kennaway "actual %d vs. announced %d.", 187a8f6863aSKris Kennaway filename, linenum, host, BN_num_bits(key->rsa->n), bits); 188*4a421b63SDag-Erling Smørgrav logit("Warning: replace %d with %d in %s, line %lu.", 189a8f6863aSKris Kennaway bits, BN_num_bits(key->rsa->n), filename, linenum); 190511b41d2SMark Murray } 191a8f6863aSKris Kennaway return 1; 192511b41d2SMark Murray } 193511b41d2SMark Murray 194*4a421b63SDag-Erling Smørgrav static HostkeyMarker 195b15c8340SDag-Erling Smørgrav check_markers(char **cpp) 196b15c8340SDag-Erling Smørgrav { 197b15c8340SDag-Erling Smørgrav char marker[32], *sp, *cp = *cpp; 198b15c8340SDag-Erling Smørgrav int ret = MRK_NONE; 199b15c8340SDag-Erling Smørgrav 200b15c8340SDag-Erling Smørgrav while (*cp == '@') { 201b15c8340SDag-Erling Smørgrav /* Only one marker is allowed */ 202b15c8340SDag-Erling Smørgrav if (ret != MRK_NONE) 203b15c8340SDag-Erling Smørgrav return MRK_ERROR; 204b15c8340SDag-Erling Smørgrav /* Markers are terminated by whitespace */ 205b15c8340SDag-Erling Smørgrav if ((sp = strchr(cp, ' ')) == NULL && 206b15c8340SDag-Erling Smørgrav (sp = strchr(cp, '\t')) == NULL) 207b15c8340SDag-Erling Smørgrav return MRK_ERROR; 208b15c8340SDag-Erling Smørgrav /* Extract marker for comparison */ 209b15c8340SDag-Erling Smørgrav if (sp <= cp + 1 || sp >= cp + sizeof(marker)) 210b15c8340SDag-Erling Smørgrav return MRK_ERROR; 211b15c8340SDag-Erling Smørgrav memcpy(marker, cp, sp - cp); 212b15c8340SDag-Erling Smørgrav marker[sp - cp] = '\0'; 213b15c8340SDag-Erling Smørgrav if (strcmp(marker, CA_MARKER) == 0) 214b15c8340SDag-Erling Smørgrav ret = MRK_CA; 215b15c8340SDag-Erling Smørgrav else if (strcmp(marker, REVOKE_MARKER) == 0) 216b15c8340SDag-Erling Smørgrav ret = MRK_REVOKE; 217b15c8340SDag-Erling Smørgrav else 218b15c8340SDag-Erling Smørgrav return MRK_ERROR; 219b15c8340SDag-Erling Smørgrav 220b15c8340SDag-Erling Smørgrav /* Skip past marker and any whitespace that follows it */ 221b15c8340SDag-Erling Smørgrav cp = sp; 222b15c8340SDag-Erling Smørgrav for (; *cp == ' ' || *cp == '\t'; cp++) 223b15c8340SDag-Erling Smørgrav ; 224b15c8340SDag-Erling Smørgrav } 225b15c8340SDag-Erling Smørgrav *cpp = cp; 226b15c8340SDag-Erling Smørgrav return ret; 227b15c8340SDag-Erling Smørgrav } 228b15c8340SDag-Erling Smørgrav 229*4a421b63SDag-Erling Smørgrav struct hostkeys * 230*4a421b63SDag-Erling Smørgrav init_hostkeys(void) 231*4a421b63SDag-Erling Smørgrav { 232*4a421b63SDag-Erling Smørgrav struct hostkeys *ret = xcalloc(1, sizeof(*ret)); 233511b41d2SMark Murray 234*4a421b63SDag-Erling Smørgrav ret->entries = NULL; 235*4a421b63SDag-Erling Smørgrav return ret; 236*4a421b63SDag-Erling Smørgrav } 237*4a421b63SDag-Erling Smørgrav 238*4a421b63SDag-Erling Smørgrav void 239*4a421b63SDag-Erling Smørgrav load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) 240511b41d2SMark Murray { 241511b41d2SMark Murray FILE *f; 242511b41d2SMark Murray char line[8192]; 243*4a421b63SDag-Erling Smørgrav u_long linenum = 0, num_loaded = 0; 244aa49c926SDag-Erling Smørgrav char *cp, *cp2, *hashed_host; 245*4a421b63SDag-Erling Smørgrav HostkeyMarker marker; 246*4a421b63SDag-Erling Smørgrav Key *key; 247*4a421b63SDag-Erling Smørgrav int kbits; 248511b41d2SMark Murray 249*4a421b63SDag-Erling Smørgrav if ((f = fopen(path, "r")) == NULL) 250*4a421b63SDag-Erling Smørgrav return; 251*4a421b63SDag-Erling Smørgrav debug3("%s: loading entries for host \"%.100s\" from file \"%s\"", 252*4a421b63SDag-Erling Smørgrav __func__, host, path); 253*4a421b63SDag-Erling Smørgrav while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { 254511b41d2SMark Murray cp = line; 255511b41d2SMark Murray 256511b41d2SMark Murray /* Skip any leading whitespace, comments and empty lines. */ 257511b41d2SMark Murray for (; *cp == ' ' || *cp == '\t'; cp++) 258511b41d2SMark Murray ; 259511b41d2SMark Murray if (!*cp || *cp == '#' || *cp == '\n') 260511b41d2SMark Murray continue; 261511b41d2SMark Murray 262*4a421b63SDag-Erling Smørgrav if ((marker = check_markers(&cp)) == MRK_ERROR) { 263*4a421b63SDag-Erling Smørgrav verbose("%s: invalid marker at %s:%lu", 264*4a421b63SDag-Erling Smørgrav __func__, path, linenum); 265b15c8340SDag-Erling Smørgrav continue; 266*4a421b63SDag-Erling Smørgrav } 267b15c8340SDag-Erling Smørgrav 268511b41d2SMark Murray /* Find the end of the host name portion. */ 269511b41d2SMark Murray for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 270511b41d2SMark Murray ; 271511b41d2SMark Murray 272511b41d2SMark Murray /* Check if the host name matches. */ 273aa49c926SDag-Erling Smørgrav if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) { 274aa49c926SDag-Erling Smørgrav if (*cp != HASH_DELIM) 275511b41d2SMark Murray continue; 276aa49c926SDag-Erling Smørgrav hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); 277aa49c926SDag-Erling Smørgrav if (hashed_host == NULL) { 278*4a421b63SDag-Erling Smørgrav debug("Invalid hashed host line %lu of %s", 279*4a421b63SDag-Erling Smørgrav linenum, path); 280aa49c926SDag-Erling Smørgrav continue; 281aa49c926SDag-Erling Smørgrav } 282aa49c926SDag-Erling Smørgrav if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) 283aa49c926SDag-Erling Smørgrav continue; 284aa49c926SDag-Erling Smørgrav } 285511b41d2SMark Murray 286511b41d2SMark Murray /* Got a match. Skip host name. */ 287511b41d2SMark Murray cp = cp2; 288511b41d2SMark Murray 289511b41d2SMark Murray /* 290511b41d2SMark Murray * Extract the key from the line. This will skip any leading 291511b41d2SMark Murray * whitespace. Ignore badly formatted lines. 292511b41d2SMark Murray */ 293*4a421b63SDag-Erling Smørgrav key = key_new(KEY_UNSPEC); 294*4a421b63SDag-Erling Smørgrav if (!hostfile_read_key(&cp, &kbits, key)) { 295*4a421b63SDag-Erling Smørgrav key_free(key); 296*4a421b63SDag-Erling Smørgrav key = key_new(KEY_RSA1); 297*4a421b63SDag-Erling Smørgrav if (!hostfile_read_key(&cp, &kbits, key)) { 298*4a421b63SDag-Erling Smørgrav key_free(key); 299*4a421b63SDag-Erling Smørgrav continue; 300*4a421b63SDag-Erling Smørgrav } 301*4a421b63SDag-Erling Smørgrav } 302*4a421b63SDag-Erling Smørgrav if (!hostfile_check_key(kbits, key, host, path, linenum)) 303a8f6863aSKris Kennaway continue; 304511b41d2SMark Murray 305*4a421b63SDag-Erling Smørgrav debug3("%s: found %skey type %s in file %s:%lu", __func__, 306*4a421b63SDag-Erling Smørgrav marker == MRK_NONE ? "" : 307*4a421b63SDag-Erling Smørgrav (marker == MRK_CA ? "ca " : "revoked "), 308*4a421b63SDag-Erling Smørgrav key_type(key), path, linenum); 309*4a421b63SDag-Erling Smørgrav hostkeys->entries = xrealloc(hostkeys->entries, 310*4a421b63SDag-Erling Smørgrav hostkeys->num_entries + 1, sizeof(*hostkeys->entries)); 311*4a421b63SDag-Erling Smørgrav hostkeys->entries[hostkeys->num_entries].host = xstrdup(host); 312*4a421b63SDag-Erling Smørgrav hostkeys->entries[hostkeys->num_entries].file = xstrdup(path); 313*4a421b63SDag-Erling Smørgrav hostkeys->entries[hostkeys->num_entries].line = linenum; 314*4a421b63SDag-Erling Smørgrav hostkeys->entries[hostkeys->num_entries].key = key; 315*4a421b63SDag-Erling Smørgrav hostkeys->entries[hostkeys->num_entries].marker = marker; 316*4a421b63SDag-Erling Smørgrav hostkeys->num_entries++; 317*4a421b63SDag-Erling Smørgrav num_loaded++; 318*4a421b63SDag-Erling Smørgrav } 319*4a421b63SDag-Erling Smørgrav debug3("%s: loaded %lu keys", __func__, num_loaded); 320333ee039SDag-Erling Smørgrav fclose(f); 321*4a421b63SDag-Erling Smørgrav return; 322f388f5efSDag-Erling Smørgrav } 323f388f5efSDag-Erling Smørgrav 324*4a421b63SDag-Erling Smørgrav void 325*4a421b63SDag-Erling Smørgrav free_hostkeys(struct hostkeys *hostkeys) 326*4a421b63SDag-Erling Smørgrav { 327*4a421b63SDag-Erling Smørgrav u_int i; 328f388f5efSDag-Erling Smørgrav 329*4a421b63SDag-Erling Smørgrav for (i = 0; i < hostkeys->num_entries; i++) { 330*4a421b63SDag-Erling Smørgrav xfree(hostkeys->entries[i].host); 331*4a421b63SDag-Erling Smørgrav xfree(hostkeys->entries[i].file); 332*4a421b63SDag-Erling Smørgrav key_free(hostkeys->entries[i].key); 333*4a421b63SDag-Erling Smørgrav bzero(hostkeys->entries + i, sizeof(*hostkeys->entries)); 334b15c8340SDag-Erling Smørgrav } 335*4a421b63SDag-Erling Smørgrav if (hostkeys->entries != NULL) 336*4a421b63SDag-Erling Smørgrav xfree(hostkeys->entries); 337*4a421b63SDag-Erling Smørgrav hostkeys->entries = NULL; 338*4a421b63SDag-Erling Smørgrav hostkeys->num_entries = 0; 339*4a421b63SDag-Erling Smørgrav xfree(hostkeys); 340b15c8340SDag-Erling Smørgrav } 341b15c8340SDag-Erling Smørgrav 342*4a421b63SDag-Erling Smørgrav static int 343*4a421b63SDag-Erling Smørgrav check_key_not_revoked(struct hostkeys *hostkeys, Key *k) 344*4a421b63SDag-Erling Smørgrav { 345*4a421b63SDag-Erling Smørgrav int is_cert = key_is_cert(k); 346*4a421b63SDag-Erling Smørgrav u_int i; 347*4a421b63SDag-Erling Smørgrav 348*4a421b63SDag-Erling Smørgrav for (i = 0; i < hostkeys->num_entries; i++) { 349*4a421b63SDag-Erling Smørgrav if (hostkeys->entries[i].marker != MRK_REVOKE) 350*4a421b63SDag-Erling Smørgrav continue; 351*4a421b63SDag-Erling Smørgrav if (key_equal_public(k, hostkeys->entries[i].key)) 352*4a421b63SDag-Erling Smørgrav return -1; 353*4a421b63SDag-Erling Smørgrav if (is_cert && 354*4a421b63SDag-Erling Smørgrav key_equal_public(k->cert->signature_key, 355*4a421b63SDag-Erling Smørgrav hostkeys->entries[i].key)) 356*4a421b63SDag-Erling Smørgrav return -1; 357511b41d2SMark Murray } 358*4a421b63SDag-Erling Smørgrav return 0; 359*4a421b63SDag-Erling Smørgrav } 360*4a421b63SDag-Erling Smørgrav 361511b41d2SMark Murray /* 362*4a421b63SDag-Erling Smørgrav * Match keys against a specified key, or look one up by key type. 363*4a421b63SDag-Erling Smørgrav * 364*4a421b63SDag-Erling Smørgrav * If looking for a keytype (key == NULL) and one is found then return 365*4a421b63SDag-Erling Smørgrav * HOST_FOUND, otherwise HOST_NEW. 366*4a421b63SDag-Erling Smørgrav * 367*4a421b63SDag-Erling Smørgrav * If looking for a key (key != NULL): 368*4a421b63SDag-Erling Smørgrav * 1. If the key is a cert and a matching CA is found, return HOST_OK 369*4a421b63SDag-Erling Smørgrav * 2. If the key is not a cert and a matching key is found, return HOST_OK 370*4a421b63SDag-Erling Smørgrav * 3. If no key matches but a key with a different type is found, then 371*4a421b63SDag-Erling Smørgrav * return HOST_CHANGED 372*4a421b63SDag-Erling Smørgrav * 4. If no matching keys are found, then return HOST_NEW. 373*4a421b63SDag-Erling Smørgrav * 374*4a421b63SDag-Erling Smørgrav * Finally, check any found key is not revoked. 375511b41d2SMark Murray */ 376*4a421b63SDag-Erling Smørgrav static HostStatus 377*4a421b63SDag-Erling Smørgrav check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, 378*4a421b63SDag-Erling Smørgrav Key *k, int keytype, const struct hostkey_entry **found) 379*4a421b63SDag-Erling Smørgrav { 380*4a421b63SDag-Erling Smørgrav u_int i; 381*4a421b63SDag-Erling Smørgrav HostStatus end_return = HOST_NEW; 382*4a421b63SDag-Erling Smørgrav int want_cert = key_is_cert(k); 383*4a421b63SDag-Erling Smørgrav HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE; 384*4a421b63SDag-Erling Smørgrav int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2; 385*4a421b63SDag-Erling Smørgrav 386*4a421b63SDag-Erling Smørgrav if (found != NULL) 387*4a421b63SDag-Erling Smørgrav *found = NULL; 388*4a421b63SDag-Erling Smørgrav 389*4a421b63SDag-Erling Smørgrav for (i = 0; i < hostkeys->num_entries; i++) { 390*4a421b63SDag-Erling Smørgrav if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1) 391*4a421b63SDag-Erling Smørgrav continue; 392*4a421b63SDag-Erling Smørgrav if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1) 393*4a421b63SDag-Erling Smørgrav continue; 394*4a421b63SDag-Erling Smørgrav if (hostkeys->entries[i].marker != want_marker) 395*4a421b63SDag-Erling Smørgrav continue; 396*4a421b63SDag-Erling Smørgrav if (k == NULL) { 397*4a421b63SDag-Erling Smørgrav if (hostkeys->entries[i].key->type != keytype) 398*4a421b63SDag-Erling Smørgrav continue; 399*4a421b63SDag-Erling Smørgrav end_return = HOST_FOUND; 400*4a421b63SDag-Erling Smørgrav if (found != NULL) 401*4a421b63SDag-Erling Smørgrav *found = hostkeys->entries + i; 402*4a421b63SDag-Erling Smørgrav k = hostkeys->entries[i].key; 403*4a421b63SDag-Erling Smørgrav break; 404*4a421b63SDag-Erling Smørgrav } 405*4a421b63SDag-Erling Smørgrav if (want_cert) { 406*4a421b63SDag-Erling Smørgrav if (key_equal_public(k->cert->signature_key, 407*4a421b63SDag-Erling Smørgrav hostkeys->entries[i].key)) { 408*4a421b63SDag-Erling Smørgrav /* A matching CA exists */ 409*4a421b63SDag-Erling Smørgrav end_return = HOST_OK; 410*4a421b63SDag-Erling Smørgrav if (found != NULL) 411*4a421b63SDag-Erling Smørgrav *found = hostkeys->entries + i; 412*4a421b63SDag-Erling Smørgrav break; 413*4a421b63SDag-Erling Smørgrav } 414*4a421b63SDag-Erling Smørgrav } else { 415*4a421b63SDag-Erling Smørgrav if (key_equal(k, hostkeys->entries[i].key)) { 416*4a421b63SDag-Erling Smørgrav end_return = HOST_OK; 417*4a421b63SDag-Erling Smørgrav if (found != NULL) 418*4a421b63SDag-Erling Smørgrav *found = hostkeys->entries + i; 419*4a421b63SDag-Erling Smørgrav break; 420*4a421b63SDag-Erling Smørgrav } 421*4a421b63SDag-Erling Smørgrav /* A non-maching key exists */ 422511b41d2SMark Murray end_return = HOST_CHANGED; 423*4a421b63SDag-Erling Smørgrav if (found != NULL) 424*4a421b63SDag-Erling Smørgrav *found = hostkeys->entries + i; 425511b41d2SMark Murray } 426*4a421b63SDag-Erling Smørgrav } 427*4a421b63SDag-Erling Smørgrav if (check_key_not_revoked(hostkeys, k) != 0) { 428*4a421b63SDag-Erling Smørgrav end_return = HOST_REVOKED; 429*4a421b63SDag-Erling Smørgrav if (found != NULL) 430*4a421b63SDag-Erling Smørgrav *found = NULL; 431*4a421b63SDag-Erling Smørgrav } 432511b41d2SMark Murray return end_return; 433511b41d2SMark Murray } 434511b41d2SMark Murray 435f388f5efSDag-Erling Smørgrav HostStatus 436*4a421b63SDag-Erling Smørgrav check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key, 437*4a421b63SDag-Erling Smørgrav const struct hostkey_entry **found) 438f388f5efSDag-Erling Smørgrav { 439f388f5efSDag-Erling Smørgrav if (key == NULL) 440f388f5efSDag-Erling Smørgrav fatal("no key to look up"); 441*4a421b63SDag-Erling Smørgrav return check_hostkeys_by_key_or_type(hostkeys, key, 0, found); 442f388f5efSDag-Erling Smørgrav } 443f388f5efSDag-Erling Smørgrav 444f388f5efSDag-Erling Smørgrav int 445*4a421b63SDag-Erling Smørgrav lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, 446*4a421b63SDag-Erling Smørgrav const struct hostkey_entry **found) 447f388f5efSDag-Erling Smørgrav { 448*4a421b63SDag-Erling Smørgrav return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype, 449*4a421b63SDag-Erling Smørgrav found) == HOST_FOUND); 450f388f5efSDag-Erling Smørgrav } 451f388f5efSDag-Erling Smørgrav 452511b41d2SMark Murray /* 453511b41d2SMark Murray * Appends an entry to the host file. Returns false if the entry could not 454511b41d2SMark Murray * be appended. 455511b41d2SMark Murray */ 456511b41d2SMark Murray 457511b41d2SMark Murray int 458aa49c926SDag-Erling Smørgrav add_host_to_hostfile(const char *filename, const char *host, const Key *key, 459aa49c926SDag-Erling Smørgrav int store_hash) 460511b41d2SMark Murray { 461511b41d2SMark Murray FILE *f; 462a8f6863aSKris Kennaway int success = 0; 463d4ecd108SDag-Erling Smørgrav char *hashed_host = NULL; 464aa49c926SDag-Erling Smørgrav 465a8f6863aSKris Kennaway if (key == NULL) 466e8aafc91SKris Kennaway return 1; /* XXX ? */ 467511b41d2SMark Murray f = fopen(filename, "a"); 468511b41d2SMark Murray if (!f) 469511b41d2SMark Murray return 0; 470aa49c926SDag-Erling Smørgrav 471aa49c926SDag-Erling Smørgrav if (store_hash) { 472aa49c926SDag-Erling Smørgrav if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { 473aa49c926SDag-Erling Smørgrav error("add_host_to_hostfile: host_hash failed"); 474aa49c926SDag-Erling Smørgrav fclose(f); 475aa49c926SDag-Erling Smørgrav return 0; 476aa49c926SDag-Erling Smørgrav } 477aa49c926SDag-Erling Smørgrav } 478aa49c926SDag-Erling Smørgrav fprintf(f, "%s ", store_hash ? hashed_host : host); 479aa49c926SDag-Erling Smørgrav 480a8f6863aSKris Kennaway if (key_write(key, f)) { 481a8f6863aSKris Kennaway success = 1; 482a8f6863aSKris Kennaway } else { 483e8aafc91SKris Kennaway error("add_host_to_hostfile: saving key in %s failed", filename); 484511b41d2SMark Murray } 485e8aafc91SKris Kennaway fprintf(f, "\n"); 486511b41d2SMark Murray fclose(f); 487a8f6863aSKris Kennaway return success; 488511b41d2SMark Murray } 489