1511b41d2SMark Murray /* 2511b41d2SMark Murray * 3511b41d2SMark Murray * hostfile.c 4511b41d2SMark Murray * 5511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 6511b41d2SMark Murray * 7511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 8511b41d2SMark Murray * All rights reserved 9511b41d2SMark Murray * 10511b41d2SMark Murray * Created: Thu Jun 29 07:10:56 1995 ylo 11511b41d2SMark Murray * 12511b41d2SMark Murray * Functions for manipulating the known hosts files. 13511b41d2SMark Murray * 14511b41d2SMark Murray */ 15511b41d2SMark Murray 16511b41d2SMark Murray #include "includes.h" 17a8f6863aSKris Kennaway RCSID("$OpenBSD: hostfile.c,v 1.14 2000/03/23 22:15:33 markus Exp $"); 18511b41d2SMark Murray 19511b41d2SMark Murray #include "packet.h" 20a8f6863aSKris Kennaway #include "match.h" 21511b41d2SMark Murray #include "ssh.h" 22a8f6863aSKris Kennaway #include <ssl/rsa.h> 23a8f6863aSKris Kennaway #include <ssl/dsa.h> 24a8f6863aSKris Kennaway #include "key.h" 25a8f6863aSKris Kennaway #include "hostfile.h" 26511b41d2SMark Murray 27511b41d2SMark Murray /* 28a8f6863aSKris Kennaway * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the 29a8f6863aSKris Kennaway * pointer over the key. Skips any whitespace at the beginning and at end. 30511b41d2SMark Murray */ 31511b41d2SMark Murray 32511b41d2SMark Murray int 33a8f6863aSKris Kennaway hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) 34511b41d2SMark Murray { 35511b41d2SMark Murray unsigned int bits; 36511b41d2SMark Murray char *cp; 37511b41d2SMark Murray 38511b41d2SMark Murray /* Skip leading whitespace. */ 39511b41d2SMark Murray for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) 40511b41d2SMark Murray ; 41511b41d2SMark Murray 42511b41d2SMark Murray /* Get number of bits. */ 43511b41d2SMark Murray if (*cp < '0' || *cp > '9') 44511b41d2SMark Murray return 0; /* Bad bit count... */ 45511b41d2SMark Murray for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) 46511b41d2SMark Murray bits = 10 * bits + *cp - '0'; 47511b41d2SMark Murray 48a8f6863aSKris Kennaway if (!key_read(ret, bits, &cp)) 49511b41d2SMark Murray return 0; 50511b41d2SMark Murray 51511b41d2SMark Murray /* Skip trailing whitespace. */ 52511b41d2SMark Murray for (; *cp == ' ' || *cp == '\t'; cp++) 53511b41d2SMark Murray ; 54511b41d2SMark Murray 55511b41d2SMark Murray /* Return results. */ 56511b41d2SMark Murray *cpp = cp; 57511b41d2SMark Murray *bitsp = bits; 58511b41d2SMark Murray return 1; 59511b41d2SMark Murray } 60511b41d2SMark Murray 61a8f6863aSKris Kennaway int 62a8f6863aSKris Kennaway auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) 63a8f6863aSKris Kennaway { 64a8f6863aSKris Kennaway Key *k = key_new(KEY_RSA); 65a8f6863aSKris Kennaway int ret = hostfile_read_key(cpp, bitsp, k); 66a8f6863aSKris Kennaway BN_copy(e, k->rsa->e); 67a8f6863aSKris Kennaway BN_copy(n, k->rsa->n); 68a8f6863aSKris Kennaway key_free(k); 69a8f6863aSKris Kennaway return ret; 70a8f6863aSKris Kennaway } 71511b41d2SMark Murray 72511b41d2SMark Murray int 73a8f6863aSKris Kennaway hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum) 74511b41d2SMark Murray { 75a8f6863aSKris Kennaway if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) 76a8f6863aSKris Kennaway return 1; 77a8f6863aSKris Kennaway if (bits != BN_num_bits(key->rsa->n)) { 78a8f6863aSKris Kennaway error("Warning: %s, line %d: keysize mismatch for host %s: " 79a8f6863aSKris Kennaway "actual %d vs. announced %d.", 80a8f6863aSKris Kennaway filename, linenum, host, BN_num_bits(key->rsa->n), bits); 81a8f6863aSKris Kennaway error("Warning: replace %d with %d in %s, line %d.", 82a8f6863aSKris Kennaway bits, BN_num_bits(key->rsa->n), filename, linenum); 83511b41d2SMark Murray } 84a8f6863aSKris Kennaway return 1; 85511b41d2SMark Murray } 86511b41d2SMark Murray 87511b41d2SMark Murray /* 88511b41d2SMark Murray * Checks whether the given host (which must be in all lowercase) is already 89511b41d2SMark Murray * in the list of our known hosts. Returns HOST_OK if the host is known and 90511b41d2SMark Murray * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED 91511b41d2SMark Murray * if the host is known but used to have a different host key. 92511b41d2SMark Murray */ 93511b41d2SMark Murray 94511b41d2SMark Murray HostStatus 95a8f6863aSKris Kennaway check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found) 96511b41d2SMark Murray { 97511b41d2SMark Murray FILE *f; 98511b41d2SMark Murray char line[8192]; 99511b41d2SMark Murray int linenum = 0; 100511b41d2SMark Murray unsigned int kbits, hostlen; 101511b41d2SMark Murray char *cp, *cp2; 102511b41d2SMark Murray HostStatus end_return; 103511b41d2SMark Murray 104a8f6863aSKris Kennaway if (key == NULL) 105a8f6863aSKris Kennaway fatal("no key to look up"); 106511b41d2SMark Murray /* Open the file containing the list of known hosts. */ 107511b41d2SMark Murray f = fopen(filename, "r"); 108511b41d2SMark Murray if (!f) 109511b41d2SMark Murray return HOST_NEW; 110511b41d2SMark Murray 111511b41d2SMark Murray /* Cache the length of the host name. */ 112511b41d2SMark Murray hostlen = strlen(host); 113511b41d2SMark Murray 114511b41d2SMark Murray /* 115511b41d2SMark Murray * Return value when the loop terminates. This is set to 116511b41d2SMark Murray * HOST_CHANGED if we have seen a different key for the host and have 117511b41d2SMark Murray * not found the proper one. 118511b41d2SMark Murray */ 119511b41d2SMark Murray end_return = HOST_NEW; 120511b41d2SMark Murray 121511b41d2SMark Murray /* Go trough the file. */ 122511b41d2SMark Murray while (fgets(line, sizeof(line), f)) { 123511b41d2SMark Murray cp = line; 124511b41d2SMark Murray linenum++; 125511b41d2SMark Murray 126511b41d2SMark Murray /* Skip any leading whitespace, comments and empty lines. */ 127511b41d2SMark Murray for (; *cp == ' ' || *cp == '\t'; cp++) 128511b41d2SMark Murray ; 129511b41d2SMark Murray if (!*cp || *cp == '#' || *cp == '\n') 130511b41d2SMark Murray continue; 131511b41d2SMark Murray 132511b41d2SMark Murray /* Find the end of the host name portion. */ 133511b41d2SMark Murray for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 134511b41d2SMark Murray ; 135511b41d2SMark Murray 136511b41d2SMark Murray /* Check if the host name matches. */ 137511b41d2SMark Murray if (!match_hostname(host, cp, (unsigned int) (cp2 - cp))) 138511b41d2SMark Murray continue; 139511b41d2SMark Murray 140511b41d2SMark Murray /* Got a match. Skip host name. */ 141511b41d2SMark Murray cp = cp2; 142511b41d2SMark Murray 143511b41d2SMark Murray /* 144511b41d2SMark Murray * Extract the key from the line. This will skip any leading 145511b41d2SMark Murray * whitespace. Ignore badly formatted lines. 146511b41d2SMark Murray */ 147a8f6863aSKris Kennaway if (!hostfile_read_key(&cp, &kbits, found)) 148a8f6863aSKris Kennaway continue; 149a8f6863aSKris Kennaway if (!hostfile_check_key(kbits, found, host, filename, linenum)) 150511b41d2SMark Murray continue; 151511b41d2SMark Murray 152511b41d2SMark Murray /* Check if the current key is the same as the given key. */ 153a8f6863aSKris Kennaway if (key_equal(key, found)) { 154511b41d2SMark Murray /* Ok, they match. */ 155511b41d2SMark Murray fclose(f); 156511b41d2SMark Murray return HOST_OK; 157511b41d2SMark Murray } 158511b41d2SMark Murray /* 159511b41d2SMark Murray * They do not match. We will continue to go through the 160511b41d2SMark Murray * file; however, we note that we will not return that it is 161511b41d2SMark Murray * new. 162511b41d2SMark Murray */ 163511b41d2SMark Murray end_return = HOST_CHANGED; 164511b41d2SMark Murray } 165511b41d2SMark Murray /* Clear variables and close the file. */ 166511b41d2SMark Murray fclose(f); 167511b41d2SMark Murray 168511b41d2SMark Murray /* 169511b41d2SMark Murray * Return either HOST_NEW or HOST_CHANGED, depending on whether we 170511b41d2SMark Murray * saw a different key for the host. 171511b41d2SMark Murray */ 172511b41d2SMark Murray return end_return; 173511b41d2SMark Murray } 174511b41d2SMark Murray 175511b41d2SMark Murray /* 176511b41d2SMark Murray * Appends an entry to the host file. Returns false if the entry could not 177511b41d2SMark Murray * be appended. 178511b41d2SMark Murray */ 179511b41d2SMark Murray 180511b41d2SMark Murray int 181a8f6863aSKris Kennaway add_host_to_hostfile(const char *filename, const char *host, Key *key) 182511b41d2SMark Murray { 183511b41d2SMark Murray FILE *f; 184a8f6863aSKris Kennaway int success = 0; 185a8f6863aSKris Kennaway 186a8f6863aSKris Kennaway if (key == NULL) 187a8f6863aSKris Kennaway return 1; 188511b41d2SMark Murray 189511b41d2SMark Murray /* Open the file for appending. */ 190511b41d2SMark Murray f = fopen(filename, "a"); 191511b41d2SMark Murray if (!f) 192511b41d2SMark Murray return 0; 193511b41d2SMark Murray 194a8f6863aSKris Kennaway fprintf(f, "%s ", host); 195a8f6863aSKris Kennaway if (key_write(key, f)) { 196a8f6863aSKris Kennaway fprintf(f, "\n"); 197a8f6863aSKris Kennaway success = 1; 198a8f6863aSKris Kennaway } else { 199a8f6863aSKris Kennaway error("add_host_to_hostfile: saving key failed"); 200511b41d2SMark Murray } 201511b41d2SMark Murray 202511b41d2SMark Murray /* Close the file. */ 203511b41d2SMark Murray fclose(f); 204a8f6863aSKris Kennaway return success; 205511b41d2SMark Murray } 206