1511b41d2SMark Murray /* 2511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 3511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4511b41d2SMark Murray * All rights reserved 5511b41d2SMark Murray * Functions for manipulating the known hosts files. 6511b41d2SMark Murray * 7c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 8c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 9c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 10c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 11c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 12c2d3a559SKris Kennaway * 13c2d3a559SKris Kennaway * 14c2d3a559SKris Kennaway * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. 15c2d3a559SKris Kennaway * Copyright (c) 1999 Niels Provos. All rights reserved. 16c2d3a559SKris Kennaway * 17c2d3a559SKris Kennaway * Redistribution and use in source and binary forms, with or without 18c2d3a559SKris Kennaway * modification, are permitted provided that the following conditions 19c2d3a559SKris Kennaway * are met: 20c2d3a559SKris Kennaway * 1. Redistributions of source code must retain the above copyright 21c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer. 22c2d3a559SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 23c2d3a559SKris Kennaway * notice, this list of conditions and the following disclaimer in the 24c2d3a559SKris Kennaway * documentation and/or other materials provided with the distribution. 25c2d3a559SKris Kennaway * 26c2d3a559SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27c2d3a559SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28c2d3a559SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29c2d3a559SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30c2d3a559SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31c2d3a559SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32c2d3a559SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33c2d3a559SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34c2d3a559SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35c2d3a559SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36511b41d2SMark Murray */ 37511b41d2SMark Murray 38511b41d2SMark Murray #include "includes.h" 39c2d3a559SKris Kennaway RCSID("$OpenBSD: hostfile.c,v 1.20 2000/09/07 20:27:51 deraadt Exp $"); 40c2d3a559SKris Kennaway RCSID("$FreeBSD$"); 41511b41d2SMark Murray 42511b41d2SMark Murray #include "packet.h" 43a8f6863aSKris Kennaway #include "match.h" 44511b41d2SMark Murray #include "ssh.h" 4518fa3c2eSKris Kennaway #include <openssl/rsa.h> 4618fa3c2eSKris Kennaway #include <openssl/dsa.h> 47a8f6863aSKris Kennaway #include "key.h" 48a8f6863aSKris Kennaway #include "hostfile.h" 49511b41d2SMark Murray 50511b41d2SMark Murray /* 51a8f6863aSKris Kennaway * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the 52a8f6863aSKris Kennaway * pointer over the key. Skips any whitespace at the beginning and at end. 53511b41d2SMark Murray */ 54511b41d2SMark Murray 55511b41d2SMark Murray int 56a8f6863aSKris Kennaway hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) 57511b41d2SMark Murray { 58511b41d2SMark Murray unsigned int bits; 59511b41d2SMark Murray char *cp; 60511b41d2SMark Murray 61511b41d2SMark Murray /* Skip leading whitespace. */ 62511b41d2SMark Murray for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) 63511b41d2SMark Murray ; 64511b41d2SMark Murray 65e8aafc91SKris Kennaway bits = key_read(ret, &cp); 66e8aafc91SKris Kennaway if (bits == 0) 67511b41d2SMark Murray return 0; 68511b41d2SMark Murray 69511b41d2SMark Murray /* Skip trailing whitespace. */ 70511b41d2SMark Murray for (; *cp == ' ' || *cp == '\t'; cp++) 71511b41d2SMark Murray ; 72511b41d2SMark Murray 73511b41d2SMark Murray /* Return results. */ 74511b41d2SMark Murray *cpp = cp; 75511b41d2SMark Murray *bitsp = bits; 76511b41d2SMark Murray return 1; 77511b41d2SMark Murray } 78511b41d2SMark Murray 79a8f6863aSKris Kennaway int 80a8f6863aSKris Kennaway auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) 81a8f6863aSKris Kennaway { 82a8f6863aSKris Kennaway Key *k = key_new(KEY_RSA); 83a8f6863aSKris Kennaway int ret = hostfile_read_key(cpp, bitsp, k); 84a8f6863aSKris Kennaway BN_copy(e, k->rsa->e); 85a8f6863aSKris Kennaway BN_copy(n, k->rsa->n); 86a8f6863aSKris Kennaway key_free(k); 87a8f6863aSKris Kennaway return ret; 88a8f6863aSKris Kennaway } 89511b41d2SMark Murray 90511b41d2SMark Murray int 91a8f6863aSKris Kennaway hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum) 92511b41d2SMark Murray { 93a8f6863aSKris Kennaway if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) 94a8f6863aSKris Kennaway return 1; 95a8f6863aSKris Kennaway if (bits != BN_num_bits(key->rsa->n)) { 96e8aafc91SKris Kennaway log("Warning: %s, line %d: keysize mismatch for host %s: " 97a8f6863aSKris Kennaway "actual %d vs. announced %d.", 98a8f6863aSKris Kennaway filename, linenum, host, BN_num_bits(key->rsa->n), bits); 99e8aafc91SKris Kennaway log("Warning: replace %d with %d in %s, line %d.", 100a8f6863aSKris Kennaway bits, BN_num_bits(key->rsa->n), filename, linenum); 101511b41d2SMark Murray } 102a8f6863aSKris Kennaway return 1; 103511b41d2SMark Murray } 104511b41d2SMark Murray 105511b41d2SMark Murray /* 106511b41d2SMark Murray * Checks whether the given host (which must be in all lowercase) is already 107511b41d2SMark Murray * in the list of our known hosts. Returns HOST_OK if the host is known and 108511b41d2SMark Murray * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED 109511b41d2SMark Murray * if the host is known but used to have a different host key. 110511b41d2SMark Murray */ 111511b41d2SMark Murray 112511b41d2SMark Murray HostStatus 113a8f6863aSKris Kennaway check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found) 114511b41d2SMark Murray { 115511b41d2SMark Murray FILE *f; 116511b41d2SMark Murray char line[8192]; 117511b41d2SMark Murray int linenum = 0; 118511b41d2SMark Murray unsigned int kbits, hostlen; 119511b41d2SMark Murray char *cp, *cp2; 120511b41d2SMark Murray HostStatus end_return; 121511b41d2SMark Murray 122a8f6863aSKris Kennaway if (key == NULL) 123a8f6863aSKris Kennaway fatal("no key to look up"); 124511b41d2SMark Murray /* Open the file containing the list of known hosts. */ 125511b41d2SMark Murray f = fopen(filename, "r"); 126511b41d2SMark Murray if (!f) 127511b41d2SMark Murray return HOST_NEW; 128511b41d2SMark Murray 129511b41d2SMark Murray /* Cache the length of the host name. */ 130511b41d2SMark Murray hostlen = strlen(host); 131511b41d2SMark Murray 132511b41d2SMark Murray /* 133511b41d2SMark Murray * Return value when the loop terminates. This is set to 134511b41d2SMark Murray * HOST_CHANGED if we have seen a different key for the host and have 135511b41d2SMark Murray * not found the proper one. 136511b41d2SMark Murray */ 137511b41d2SMark Murray end_return = HOST_NEW; 138511b41d2SMark Murray 139511b41d2SMark Murray /* Go trough the file. */ 140511b41d2SMark Murray while (fgets(line, sizeof(line), f)) { 141511b41d2SMark Murray cp = line; 142511b41d2SMark Murray linenum++; 143511b41d2SMark Murray 144511b41d2SMark Murray /* Skip any leading whitespace, comments and empty lines. */ 145511b41d2SMark Murray for (; *cp == ' ' || *cp == '\t'; cp++) 146511b41d2SMark Murray ; 147511b41d2SMark Murray if (!*cp || *cp == '#' || *cp == '\n') 148511b41d2SMark Murray continue; 149511b41d2SMark Murray 150511b41d2SMark Murray /* Find the end of the host name portion. */ 151511b41d2SMark Murray for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 152511b41d2SMark Murray ; 153511b41d2SMark Murray 154511b41d2SMark Murray /* Check if the host name matches. */ 155c2d3a559SKris Kennaway if (match_hostname(host, cp, (unsigned int) (cp2 - cp)) != 1) 156511b41d2SMark Murray continue; 157511b41d2SMark Murray 158511b41d2SMark Murray /* Got a match. Skip host name. */ 159511b41d2SMark Murray cp = cp2; 160511b41d2SMark Murray 161511b41d2SMark Murray /* 162511b41d2SMark Murray * Extract the key from the line. This will skip any leading 163511b41d2SMark Murray * whitespace. Ignore badly formatted lines. 164511b41d2SMark Murray */ 165a8f6863aSKris Kennaway if (!hostfile_read_key(&cp, &kbits, found)) 166a8f6863aSKris Kennaway continue; 167a8f6863aSKris Kennaway if (!hostfile_check_key(kbits, found, host, filename, linenum)) 168511b41d2SMark Murray continue; 169511b41d2SMark Murray 170511b41d2SMark Murray /* Check if the current key is the same as the given key. */ 171a8f6863aSKris Kennaway if (key_equal(key, found)) { 172511b41d2SMark Murray /* Ok, they match. */ 173511b41d2SMark Murray fclose(f); 174511b41d2SMark Murray return HOST_OK; 175511b41d2SMark Murray } 176511b41d2SMark Murray /* 177511b41d2SMark Murray * They do not match. We will continue to go through the 178511b41d2SMark Murray * file; however, we note that we will not return that it is 179511b41d2SMark Murray * new. 180511b41d2SMark Murray */ 181511b41d2SMark Murray end_return = HOST_CHANGED; 182511b41d2SMark Murray } 183511b41d2SMark Murray /* Clear variables and close the file. */ 184511b41d2SMark Murray fclose(f); 185511b41d2SMark Murray 186511b41d2SMark Murray /* 187511b41d2SMark Murray * Return either HOST_NEW or HOST_CHANGED, depending on whether we 188511b41d2SMark Murray * saw a different key for the host. 189511b41d2SMark Murray */ 190511b41d2SMark Murray return end_return; 191511b41d2SMark Murray } 192511b41d2SMark Murray 193511b41d2SMark Murray /* 194511b41d2SMark Murray * Appends an entry to the host file. Returns false if the entry could not 195511b41d2SMark Murray * be appended. 196511b41d2SMark Murray */ 197511b41d2SMark Murray 198511b41d2SMark Murray int 199a8f6863aSKris Kennaway add_host_to_hostfile(const char *filename, const char *host, Key *key) 200511b41d2SMark Murray { 201511b41d2SMark Murray FILE *f; 202a8f6863aSKris Kennaway int success = 0; 203a8f6863aSKris Kennaway if (key == NULL) 204e8aafc91SKris Kennaway return 1; /* XXX ? */ 205511b41d2SMark Murray f = fopen(filename, "a"); 206511b41d2SMark Murray if (!f) 207511b41d2SMark Murray return 0; 208a8f6863aSKris Kennaway fprintf(f, "%s ", host); 209a8f6863aSKris Kennaway if (key_write(key, f)) { 210a8f6863aSKris Kennaway success = 1; 211a8f6863aSKris Kennaway } else { 212e8aafc91SKris Kennaway error("add_host_to_hostfile: saving key in %s failed", filename); 213511b41d2SMark Murray } 214e8aafc91SKris Kennaway fprintf(f, "\n"); 215511b41d2SMark Murray fclose(f); 216a8f6863aSKris Kennaway return success; 217511b41d2SMark Murray } 218