1021d409fSDag-Erling Smørgrav /* $OpenBSD: dns.c,v 1.16 2005/10/17 14:13:35 stevesk Exp $ */ 2d95e11bfSDag-Erling Smørgrav 3d95e11bfSDag-Erling Smørgrav /* 4d95e11bfSDag-Erling Smørgrav * Copyright (c) 2003 Wesley Griffin. All rights reserved. 5d95e11bfSDag-Erling Smørgrav * Copyright (c) 2003 Jakob Schlyter. All rights reserved. 6d95e11bfSDag-Erling Smørgrav * 7d95e11bfSDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 8d95e11bfSDag-Erling Smørgrav * modification, are permitted provided that the following conditions 9d95e11bfSDag-Erling Smørgrav * are met: 10d95e11bfSDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 11d95e11bfSDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer. 12d95e11bfSDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 13d95e11bfSDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 14d95e11bfSDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 15d95e11bfSDag-Erling Smørgrav * 16d95e11bfSDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17d95e11bfSDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18d95e11bfSDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19d95e11bfSDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20d95e11bfSDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21d95e11bfSDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22d95e11bfSDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23d95e11bfSDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24d95e11bfSDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25d95e11bfSDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26d95e11bfSDag-Erling Smørgrav */ 27d95e11bfSDag-Erling Smørgrav 28d95e11bfSDag-Erling Smørgrav #include "includes.h" 29021d409fSDag-Erling Smørgrav RCSID("$OpenBSD: dns.c,v 1.16 2005/10/17 14:13:35 stevesk Exp $"); 30d95e11bfSDag-Erling Smørgrav 31d95e11bfSDag-Erling Smørgrav #include <netdb.h> 32d95e11bfSDag-Erling Smørgrav 33d95e11bfSDag-Erling Smørgrav #include "xmalloc.h" 34d95e11bfSDag-Erling Smørgrav #include "key.h" 35d95e11bfSDag-Erling Smørgrav #include "dns.h" 36d95e11bfSDag-Erling Smørgrav #include "log.h" 37d95e11bfSDag-Erling Smørgrav 38d95e11bfSDag-Erling Smørgrav static const char *errset_text[] = { 39d95e11bfSDag-Erling Smørgrav "success", /* 0 ERRSET_SUCCESS */ 40d95e11bfSDag-Erling Smørgrav "out of memory", /* 1 ERRSET_NOMEMORY */ 41d95e11bfSDag-Erling Smørgrav "general failure", /* 2 ERRSET_FAIL */ 42d95e11bfSDag-Erling Smørgrav "invalid parameter", /* 3 ERRSET_INVAL */ 43d95e11bfSDag-Erling Smørgrav "name does not exist", /* 4 ERRSET_NONAME */ 44d95e11bfSDag-Erling Smørgrav "data does not exist", /* 5 ERRSET_NODATA */ 45d95e11bfSDag-Erling Smørgrav }; 46d95e11bfSDag-Erling Smørgrav 47d95e11bfSDag-Erling Smørgrav static const char * 48d74d50a8SDag-Erling Smørgrav dns_result_totext(unsigned int res) 49d95e11bfSDag-Erling Smørgrav { 50d74d50a8SDag-Erling Smørgrav switch (res) { 51d95e11bfSDag-Erling Smørgrav case ERRSET_SUCCESS: 52d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_SUCCESS]; 53d95e11bfSDag-Erling Smørgrav case ERRSET_NOMEMORY: 54d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NOMEMORY]; 55d95e11bfSDag-Erling Smørgrav case ERRSET_FAIL: 56d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_FAIL]; 57d95e11bfSDag-Erling Smørgrav case ERRSET_INVAL: 58d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_INVAL]; 59d95e11bfSDag-Erling Smørgrav case ERRSET_NONAME: 60d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NONAME]; 61d95e11bfSDag-Erling Smørgrav case ERRSET_NODATA: 62d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NODATA]; 63d95e11bfSDag-Erling Smørgrav default: 64d95e11bfSDag-Erling Smørgrav return "unknown error"; 65d95e11bfSDag-Erling Smørgrav } 66d95e11bfSDag-Erling Smørgrav } 67d95e11bfSDag-Erling Smørgrav 68d95e11bfSDag-Erling Smørgrav /* 69d95e11bfSDag-Erling Smørgrav * Read SSHFP parameters from key buffer. 70d95e11bfSDag-Erling Smørgrav */ 71d95e11bfSDag-Erling Smørgrav static int 72d95e11bfSDag-Erling Smørgrav dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 73efcad6b7SDag-Erling Smørgrav u_char **digest, u_int *digest_len, const Key *key) 74d95e11bfSDag-Erling Smørgrav { 75d95e11bfSDag-Erling Smørgrav int success = 0; 76d95e11bfSDag-Erling Smørgrav 77d95e11bfSDag-Erling Smørgrav switch (key->type) { 78d95e11bfSDag-Erling Smørgrav case KEY_RSA: 79d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RSA; 80d95e11bfSDag-Erling Smørgrav break; 81d95e11bfSDag-Erling Smørgrav case KEY_DSA: 82d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_DSA; 83d95e11bfSDag-Erling Smørgrav break; 84d95e11bfSDag-Erling Smørgrav default: 85021d409fSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RESERVED; /* 0 */ 86d95e11bfSDag-Erling Smørgrav } 87d95e11bfSDag-Erling Smørgrav 88d95e11bfSDag-Erling Smørgrav if (*algorithm) { 89d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_SHA1; 90d95e11bfSDag-Erling Smørgrav *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); 91021d409fSDag-Erling Smørgrav if (*digest == NULL) 92021d409fSDag-Erling Smørgrav fatal("dns_read_key: null from key_fingerprint_raw()"); 93d95e11bfSDag-Erling Smørgrav success = 1; 94d95e11bfSDag-Erling Smørgrav } else { 95d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_RESERVED; 96d95e11bfSDag-Erling Smørgrav *digest = NULL; 97d95e11bfSDag-Erling Smørgrav *digest_len = 0; 98d95e11bfSDag-Erling Smørgrav success = 0; 99d95e11bfSDag-Erling Smørgrav } 100d95e11bfSDag-Erling Smørgrav 101d95e11bfSDag-Erling Smørgrav return success; 102d95e11bfSDag-Erling Smørgrav } 103d95e11bfSDag-Erling Smørgrav 104d95e11bfSDag-Erling Smørgrav /* 105d95e11bfSDag-Erling Smørgrav * Read SSHFP parameters from rdata buffer. 106d95e11bfSDag-Erling Smørgrav */ 107d95e11bfSDag-Erling Smørgrav static int 108d95e11bfSDag-Erling Smørgrav dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, 109d95e11bfSDag-Erling Smørgrav u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) 110d95e11bfSDag-Erling Smørgrav { 111d95e11bfSDag-Erling Smørgrav int success = 0; 112d95e11bfSDag-Erling Smørgrav 113d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RESERVED; 114d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_RESERVED; 115d95e11bfSDag-Erling Smørgrav 116d95e11bfSDag-Erling Smørgrav if (rdata_len >= 2) { 117d95e11bfSDag-Erling Smørgrav *algorithm = rdata[0]; 118d95e11bfSDag-Erling Smørgrav *digest_type = rdata[1]; 119d95e11bfSDag-Erling Smørgrav *digest_len = rdata_len - 2; 120d95e11bfSDag-Erling Smørgrav 121d95e11bfSDag-Erling Smørgrav if (*digest_len > 0) { 122d95e11bfSDag-Erling Smørgrav *digest = (u_char *) xmalloc(*digest_len); 123d95e11bfSDag-Erling Smørgrav memcpy(*digest, rdata + 2, *digest_len); 124d95e11bfSDag-Erling Smørgrav } else { 125021d409fSDag-Erling Smørgrav *digest = xstrdup(""); 126d95e11bfSDag-Erling Smørgrav } 127d95e11bfSDag-Erling Smørgrav 128d95e11bfSDag-Erling Smørgrav success = 1; 129d95e11bfSDag-Erling Smørgrav } 130d95e11bfSDag-Erling Smørgrav 131d95e11bfSDag-Erling Smørgrav return success; 132d95e11bfSDag-Erling Smørgrav } 133d95e11bfSDag-Erling Smørgrav 134043840dfSDag-Erling Smørgrav /* 135043840dfSDag-Erling Smørgrav * Check if hostname is numerical. 136043840dfSDag-Erling Smørgrav * Returns -1 if hostname is numeric, 0 otherwise 137043840dfSDag-Erling Smørgrav */ 138043840dfSDag-Erling Smørgrav static int 139043840dfSDag-Erling Smørgrav is_numeric_hostname(const char *hostname) 140043840dfSDag-Erling Smørgrav { 141043840dfSDag-Erling Smørgrav struct addrinfo hints, *ai; 142043840dfSDag-Erling Smørgrav 143043840dfSDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 144043840dfSDag-Erling Smørgrav hints.ai_socktype = SOCK_DGRAM; 145043840dfSDag-Erling Smørgrav hints.ai_flags = AI_NUMERICHOST; 146043840dfSDag-Erling Smørgrav 147043840dfSDag-Erling Smørgrav if (getaddrinfo(hostname, "0", &hints, &ai) == 0) { 148043840dfSDag-Erling Smørgrav freeaddrinfo(ai); 149043840dfSDag-Erling Smørgrav return -1; 150043840dfSDag-Erling Smørgrav } 151043840dfSDag-Erling Smørgrav 152043840dfSDag-Erling Smørgrav return 0; 153043840dfSDag-Erling Smørgrav } 154d95e11bfSDag-Erling Smørgrav 155d95e11bfSDag-Erling Smørgrav /* 156d95e11bfSDag-Erling Smørgrav * Verify the given hostname, address and host key using DNS. 157efcad6b7SDag-Erling Smørgrav * Returns 0 if lookup succeeds, -1 otherwise 158d95e11bfSDag-Erling Smørgrav */ 159d95e11bfSDag-Erling Smørgrav int 160d95e11bfSDag-Erling Smørgrav verify_host_key_dns(const char *hostname, struct sockaddr *address, 161efcad6b7SDag-Erling Smørgrav const Key *hostkey, int *flags) 162d95e11bfSDag-Erling Smørgrav { 163043840dfSDag-Erling Smørgrav u_int counter; 164d95e11bfSDag-Erling Smørgrav int result; 165d95e11bfSDag-Erling Smørgrav struct rrsetinfo *fingerprints = NULL; 166d95e11bfSDag-Erling Smørgrav 167d95e11bfSDag-Erling Smørgrav u_int8_t hostkey_algorithm; 168d95e11bfSDag-Erling Smørgrav u_int8_t hostkey_digest_type; 169d95e11bfSDag-Erling Smørgrav u_char *hostkey_digest; 170d95e11bfSDag-Erling Smørgrav u_int hostkey_digest_len; 171d95e11bfSDag-Erling Smørgrav 172d95e11bfSDag-Erling Smørgrav u_int8_t dnskey_algorithm; 173d95e11bfSDag-Erling Smørgrav u_int8_t dnskey_digest_type; 174d95e11bfSDag-Erling Smørgrav u_char *dnskey_digest; 175d95e11bfSDag-Erling Smørgrav u_int dnskey_digest_len; 176d95e11bfSDag-Erling Smørgrav 177efcad6b7SDag-Erling Smørgrav *flags = 0; 178d95e11bfSDag-Erling Smørgrav 179021d409fSDag-Erling Smørgrav debug3("verify_host_key_dns"); 180d95e11bfSDag-Erling Smørgrav if (hostkey == NULL) 181d95e11bfSDag-Erling Smørgrav fatal("No key to look up!"); 182d95e11bfSDag-Erling Smørgrav 183043840dfSDag-Erling Smørgrav if (is_numeric_hostname(hostname)) { 184043840dfSDag-Erling Smørgrav debug("skipped DNS lookup for numerical hostname"); 185043840dfSDag-Erling Smørgrav return -1; 186043840dfSDag-Erling Smørgrav } 187043840dfSDag-Erling Smørgrav 188d95e11bfSDag-Erling Smørgrav result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, 189d95e11bfSDag-Erling Smørgrav DNS_RDATATYPE_SSHFP, 0, &fingerprints); 190d95e11bfSDag-Erling Smørgrav if (result) { 191d95e11bfSDag-Erling Smørgrav verbose("DNS lookup error: %s", dns_result_totext(result)); 192efcad6b7SDag-Erling Smørgrav return -1; 193d95e11bfSDag-Erling Smørgrav } 194d95e11bfSDag-Erling Smørgrav 195efcad6b7SDag-Erling Smørgrav if (fingerprints->rri_flags & RRSET_VALIDATED) { 196efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_SECURE; 197efcad6b7SDag-Erling Smørgrav debug("found %d secure fingerprints in DNS", 198efcad6b7SDag-Erling Smørgrav fingerprints->rri_nrdatas); 199efcad6b7SDag-Erling Smørgrav } else { 200efcad6b7SDag-Erling Smørgrav debug("found %d insecure fingerprints in DNS", 201efcad6b7SDag-Erling Smørgrav fingerprints->rri_nrdatas); 202d95e11bfSDag-Erling Smørgrav } 203d95e11bfSDag-Erling Smørgrav 204d95e11bfSDag-Erling Smørgrav /* Initialize host key parameters */ 205d95e11bfSDag-Erling Smørgrav if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, 206d95e11bfSDag-Erling Smørgrav &hostkey_digest, &hostkey_digest_len, hostkey)) { 207d95e11bfSDag-Erling Smørgrav error("Error calculating host key fingerprint."); 208d95e11bfSDag-Erling Smørgrav freerrset(fingerprints); 209efcad6b7SDag-Erling Smørgrav return -1; 210d95e11bfSDag-Erling Smørgrav } 211d95e11bfSDag-Erling Smørgrav 212efcad6b7SDag-Erling Smørgrav if (fingerprints->rri_nrdatas) 213efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_FOUND; 214efcad6b7SDag-Erling Smørgrav 215d95e11bfSDag-Erling Smørgrav for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) { 216d95e11bfSDag-Erling Smørgrav /* 217d95e11bfSDag-Erling Smørgrav * Extract the key from the answer. Ignore any badly 218d95e11bfSDag-Erling Smørgrav * formatted fingerprints. 219d95e11bfSDag-Erling Smørgrav */ 220d95e11bfSDag-Erling Smørgrav if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, 221d95e11bfSDag-Erling Smørgrav &dnskey_digest, &dnskey_digest_len, 222d95e11bfSDag-Erling Smørgrav fingerprints->rri_rdatas[counter].rdi_data, 223d95e11bfSDag-Erling Smørgrav fingerprints->rri_rdatas[counter].rdi_length)) { 224d95e11bfSDag-Erling Smørgrav verbose("Error parsing fingerprint from DNS."); 225d95e11bfSDag-Erling Smørgrav continue; 226d95e11bfSDag-Erling Smørgrav } 227d95e11bfSDag-Erling Smørgrav 228d95e11bfSDag-Erling Smørgrav /* Check if the current key is the same as the given key */ 229d95e11bfSDag-Erling Smørgrav if (hostkey_algorithm == dnskey_algorithm && 230d95e11bfSDag-Erling Smørgrav hostkey_digest_type == dnskey_digest_type) { 231d95e11bfSDag-Erling Smørgrav 232d95e11bfSDag-Erling Smørgrav if (hostkey_digest_len == dnskey_digest_len && 233d95e11bfSDag-Erling Smørgrav memcmp(hostkey_digest, dnskey_digest, 234d95e11bfSDag-Erling Smørgrav hostkey_digest_len) == 0) { 235d95e11bfSDag-Erling Smørgrav 236efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_MATCH; 237efcad6b7SDag-Erling Smørgrav } 238efcad6b7SDag-Erling Smørgrav } 239021d409fSDag-Erling Smørgrav xfree(dnskey_digest); 240efcad6b7SDag-Erling Smørgrav } 241efcad6b7SDag-Erling Smørgrav 242021d409fSDag-Erling Smørgrav xfree(hostkey_digest); /* from key_fingerprint_raw() */ 243d95e11bfSDag-Erling Smørgrav freerrset(fingerprints); 244efcad6b7SDag-Erling Smørgrav 245efcad6b7SDag-Erling Smørgrav if (*flags & DNS_VERIFY_FOUND) 246efcad6b7SDag-Erling Smørgrav if (*flags & DNS_VERIFY_MATCH) 247d95e11bfSDag-Erling Smørgrav debug("matching host key fingerprint found in DNS"); 248efcad6b7SDag-Erling Smørgrav else 249efcad6b7SDag-Erling Smørgrav debug("mismatching host key fingerprint found in DNS"); 250efcad6b7SDag-Erling Smørgrav else 251efcad6b7SDag-Erling Smørgrav debug("no host key fingerprint found in DNS"); 252d95e11bfSDag-Erling Smørgrav 253efcad6b7SDag-Erling Smørgrav return 0; 254d95e11bfSDag-Erling Smørgrav } 255d95e11bfSDag-Erling Smørgrav 256d95e11bfSDag-Erling Smørgrav /* 257d95e11bfSDag-Erling Smørgrav * Export the fingerprint of a key as a DNS resource record 258d95e11bfSDag-Erling Smørgrav */ 259d95e11bfSDag-Erling Smørgrav int 260efcad6b7SDag-Erling Smørgrav export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) 261d95e11bfSDag-Erling Smørgrav { 262d95e11bfSDag-Erling Smørgrav u_int8_t rdata_pubkey_algorithm = 0; 263d95e11bfSDag-Erling Smørgrav u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; 264d95e11bfSDag-Erling Smørgrav u_char *rdata_digest; 265d95e11bfSDag-Erling Smørgrav u_int rdata_digest_len; 266d95e11bfSDag-Erling Smørgrav 267043840dfSDag-Erling Smørgrav u_int i; 268d95e11bfSDag-Erling Smørgrav int success = 0; 269d95e11bfSDag-Erling Smørgrav 270d95e11bfSDag-Erling Smørgrav if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, 271d95e11bfSDag-Erling Smørgrav &rdata_digest, &rdata_digest_len, key)) { 272d95e11bfSDag-Erling Smørgrav 273d95e11bfSDag-Erling Smørgrav if (generic) 274d95e11bfSDag-Erling Smørgrav fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, 275d95e11bfSDag-Erling Smørgrav DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, 276d95e11bfSDag-Erling Smørgrav rdata_pubkey_algorithm, rdata_digest_type); 277d95e11bfSDag-Erling Smørgrav else 278d95e11bfSDag-Erling Smørgrav fprintf(f, "%s IN SSHFP %d %d ", hostname, 279d95e11bfSDag-Erling Smørgrav rdata_pubkey_algorithm, rdata_digest_type); 280d95e11bfSDag-Erling Smørgrav 281d95e11bfSDag-Erling Smørgrav for (i = 0; i < rdata_digest_len; i++) 282d95e11bfSDag-Erling Smørgrav fprintf(f, "%02x", rdata_digest[i]); 283d95e11bfSDag-Erling Smørgrav fprintf(f, "\n"); 284021d409fSDag-Erling Smørgrav xfree(rdata_digest); /* from key_fingerprint_raw() */ 285d95e11bfSDag-Erling Smørgrav success = 1; 286d95e11bfSDag-Erling Smørgrav } else { 287021d409fSDag-Erling Smørgrav error("export_dns_rr: unsupported algorithm"); 288d95e11bfSDag-Erling Smørgrav } 289d95e11bfSDag-Erling Smørgrav 290d95e11bfSDag-Erling Smørgrav return success; 291d95e11bfSDag-Erling Smørgrav } 292