1761efaa7SDag-Erling Smørgrav /* $OpenBSD: dns.c,v 1.23 2006/08/03 03:34:42 deraadt 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" 29761efaa7SDag-Erling Smørgrav 30761efaa7SDag-Erling Smørgrav #include <sys/types.h> 31761efaa7SDag-Erling Smørgrav #include <sys/socket.h> 32d95e11bfSDag-Erling Smørgrav 33d95e11bfSDag-Erling Smørgrav #include <netdb.h> 34761efaa7SDag-Erling Smørgrav #include <stdarg.h> 35761efaa7SDag-Erling Smørgrav #include <stdio.h> 36761efaa7SDag-Erling Smørgrav #include <string.h> 37d95e11bfSDag-Erling Smørgrav 38d95e11bfSDag-Erling Smørgrav #include "xmalloc.h" 39d95e11bfSDag-Erling Smørgrav #include "key.h" 40d95e11bfSDag-Erling Smørgrav #include "dns.h" 41d95e11bfSDag-Erling Smørgrav #include "log.h" 42d95e11bfSDag-Erling Smørgrav 43d95e11bfSDag-Erling Smørgrav static const char *errset_text[] = { 44d95e11bfSDag-Erling Smørgrav "success", /* 0 ERRSET_SUCCESS */ 45d95e11bfSDag-Erling Smørgrav "out of memory", /* 1 ERRSET_NOMEMORY */ 46d95e11bfSDag-Erling Smørgrav "general failure", /* 2 ERRSET_FAIL */ 47d95e11bfSDag-Erling Smørgrav "invalid parameter", /* 3 ERRSET_INVAL */ 48d95e11bfSDag-Erling Smørgrav "name does not exist", /* 4 ERRSET_NONAME */ 49d95e11bfSDag-Erling Smørgrav "data does not exist", /* 5 ERRSET_NODATA */ 50d95e11bfSDag-Erling Smørgrav }; 51d95e11bfSDag-Erling Smørgrav 52d95e11bfSDag-Erling Smørgrav static const char * 53d74d50a8SDag-Erling Smørgrav dns_result_totext(unsigned int res) 54d95e11bfSDag-Erling Smørgrav { 55d74d50a8SDag-Erling Smørgrav switch (res) { 56d95e11bfSDag-Erling Smørgrav case ERRSET_SUCCESS: 57d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_SUCCESS]; 58d95e11bfSDag-Erling Smørgrav case ERRSET_NOMEMORY: 59d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NOMEMORY]; 60d95e11bfSDag-Erling Smørgrav case ERRSET_FAIL: 61d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_FAIL]; 62d95e11bfSDag-Erling Smørgrav case ERRSET_INVAL: 63d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_INVAL]; 64d95e11bfSDag-Erling Smørgrav case ERRSET_NONAME: 65d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NONAME]; 66d95e11bfSDag-Erling Smørgrav case ERRSET_NODATA: 67d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NODATA]; 68d95e11bfSDag-Erling Smørgrav default: 69d95e11bfSDag-Erling Smørgrav return "unknown error"; 70d95e11bfSDag-Erling Smørgrav } 71d95e11bfSDag-Erling Smørgrav } 72d95e11bfSDag-Erling Smørgrav 73d95e11bfSDag-Erling Smørgrav /* 74d95e11bfSDag-Erling Smørgrav * Read SSHFP parameters from key buffer. 75d95e11bfSDag-Erling Smørgrav */ 76d95e11bfSDag-Erling Smørgrav static int 77d95e11bfSDag-Erling Smørgrav dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 78efcad6b7SDag-Erling Smørgrav u_char **digest, u_int *digest_len, const Key *key) 79d95e11bfSDag-Erling Smørgrav { 80d95e11bfSDag-Erling Smørgrav int success = 0; 81d95e11bfSDag-Erling Smørgrav 82d95e11bfSDag-Erling Smørgrav switch (key->type) { 83d95e11bfSDag-Erling Smørgrav case KEY_RSA: 84d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RSA; 85d95e11bfSDag-Erling Smørgrav break; 86d95e11bfSDag-Erling Smørgrav case KEY_DSA: 87d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_DSA; 88d95e11bfSDag-Erling Smørgrav break; 89d95e11bfSDag-Erling Smørgrav default: 90021d409fSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RESERVED; /* 0 */ 91d95e11bfSDag-Erling Smørgrav } 92d95e11bfSDag-Erling Smørgrav 93d95e11bfSDag-Erling Smørgrav if (*algorithm) { 94d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_SHA1; 95d95e11bfSDag-Erling Smørgrav *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); 96021d409fSDag-Erling Smørgrav if (*digest == NULL) 97021d409fSDag-Erling Smørgrav fatal("dns_read_key: null from key_fingerprint_raw()"); 98d95e11bfSDag-Erling Smørgrav success = 1; 99d95e11bfSDag-Erling Smørgrav } else { 100d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_RESERVED; 101d95e11bfSDag-Erling Smørgrav *digest = NULL; 102d95e11bfSDag-Erling Smørgrav *digest_len = 0; 103d95e11bfSDag-Erling Smørgrav success = 0; 104d95e11bfSDag-Erling Smørgrav } 105d95e11bfSDag-Erling Smørgrav 106d95e11bfSDag-Erling Smørgrav return success; 107d95e11bfSDag-Erling Smørgrav } 108d95e11bfSDag-Erling Smørgrav 109d95e11bfSDag-Erling Smørgrav /* 110d95e11bfSDag-Erling Smørgrav * Read SSHFP parameters from rdata buffer. 111d95e11bfSDag-Erling Smørgrav */ 112d95e11bfSDag-Erling Smørgrav static int 113d95e11bfSDag-Erling Smørgrav dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, 114d95e11bfSDag-Erling Smørgrav u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) 115d95e11bfSDag-Erling Smørgrav { 116d95e11bfSDag-Erling Smørgrav int success = 0; 117d95e11bfSDag-Erling Smørgrav 118d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RESERVED; 119d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_RESERVED; 120d95e11bfSDag-Erling Smørgrav 121d95e11bfSDag-Erling Smørgrav if (rdata_len >= 2) { 122d95e11bfSDag-Erling Smørgrav *algorithm = rdata[0]; 123d95e11bfSDag-Erling Smørgrav *digest_type = rdata[1]; 124d95e11bfSDag-Erling Smørgrav *digest_len = rdata_len - 2; 125d95e11bfSDag-Erling Smørgrav 126d95e11bfSDag-Erling Smørgrav if (*digest_len > 0) { 127d95e11bfSDag-Erling Smørgrav *digest = (u_char *) xmalloc(*digest_len); 128d95e11bfSDag-Erling Smørgrav memcpy(*digest, rdata + 2, *digest_len); 129d95e11bfSDag-Erling Smørgrav } else { 130761efaa7SDag-Erling Smørgrav *digest = (u_char *)xstrdup(""); 131d95e11bfSDag-Erling Smørgrav } 132d95e11bfSDag-Erling Smørgrav 133d95e11bfSDag-Erling Smørgrav success = 1; 134d95e11bfSDag-Erling Smørgrav } 135d95e11bfSDag-Erling Smørgrav 136d95e11bfSDag-Erling Smørgrav return success; 137d95e11bfSDag-Erling Smørgrav } 138d95e11bfSDag-Erling Smørgrav 139043840dfSDag-Erling Smørgrav /* 140043840dfSDag-Erling Smørgrav * Check if hostname is numerical. 141043840dfSDag-Erling Smørgrav * Returns -1 if hostname is numeric, 0 otherwise 142043840dfSDag-Erling Smørgrav */ 143043840dfSDag-Erling Smørgrav static int 144043840dfSDag-Erling Smørgrav is_numeric_hostname(const char *hostname) 145043840dfSDag-Erling Smørgrav { 146043840dfSDag-Erling Smørgrav struct addrinfo hints, *ai; 147043840dfSDag-Erling Smørgrav 148043840dfSDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 149043840dfSDag-Erling Smørgrav hints.ai_socktype = SOCK_DGRAM; 150043840dfSDag-Erling Smørgrav hints.ai_flags = AI_NUMERICHOST; 151043840dfSDag-Erling Smørgrav 152043840dfSDag-Erling Smørgrav if (getaddrinfo(hostname, "0", &hints, &ai) == 0) { 153043840dfSDag-Erling Smørgrav freeaddrinfo(ai); 154043840dfSDag-Erling Smørgrav return -1; 155043840dfSDag-Erling Smørgrav } 156043840dfSDag-Erling Smørgrav 157043840dfSDag-Erling Smørgrav return 0; 158043840dfSDag-Erling Smørgrav } 159d95e11bfSDag-Erling Smørgrav 160d95e11bfSDag-Erling Smørgrav /* 161d95e11bfSDag-Erling Smørgrav * Verify the given hostname, address and host key using DNS. 162efcad6b7SDag-Erling Smørgrav * Returns 0 if lookup succeeds, -1 otherwise 163d95e11bfSDag-Erling Smørgrav */ 164d95e11bfSDag-Erling Smørgrav int 165d95e11bfSDag-Erling Smørgrav verify_host_key_dns(const char *hostname, struct sockaddr *address, 166efcad6b7SDag-Erling Smørgrav const Key *hostkey, int *flags) 167d95e11bfSDag-Erling Smørgrav { 168043840dfSDag-Erling Smørgrav u_int counter; 169d95e11bfSDag-Erling Smørgrav int result; 170d95e11bfSDag-Erling Smørgrav struct rrsetinfo *fingerprints = NULL; 171d95e11bfSDag-Erling Smørgrav 172d95e11bfSDag-Erling Smørgrav u_int8_t hostkey_algorithm; 173d95e11bfSDag-Erling Smørgrav u_int8_t hostkey_digest_type; 174d95e11bfSDag-Erling Smørgrav u_char *hostkey_digest; 175d95e11bfSDag-Erling Smørgrav u_int hostkey_digest_len; 176d95e11bfSDag-Erling Smørgrav 177d95e11bfSDag-Erling Smørgrav u_int8_t dnskey_algorithm; 178d95e11bfSDag-Erling Smørgrav u_int8_t dnskey_digest_type; 179d95e11bfSDag-Erling Smørgrav u_char *dnskey_digest; 180d95e11bfSDag-Erling Smørgrav u_int dnskey_digest_len; 181d95e11bfSDag-Erling Smørgrav 182efcad6b7SDag-Erling Smørgrav *flags = 0; 183d95e11bfSDag-Erling Smørgrav 184021d409fSDag-Erling Smørgrav debug3("verify_host_key_dns"); 185d95e11bfSDag-Erling Smørgrav if (hostkey == NULL) 186d95e11bfSDag-Erling Smørgrav fatal("No key to look up!"); 187d95e11bfSDag-Erling Smørgrav 188043840dfSDag-Erling Smørgrav if (is_numeric_hostname(hostname)) { 189043840dfSDag-Erling Smørgrav debug("skipped DNS lookup for numerical hostname"); 190043840dfSDag-Erling Smørgrav return -1; 191043840dfSDag-Erling Smørgrav } 192043840dfSDag-Erling Smørgrav 193d95e11bfSDag-Erling Smørgrav result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, 194d95e11bfSDag-Erling Smørgrav DNS_RDATATYPE_SSHFP, 0, &fingerprints); 195d95e11bfSDag-Erling Smørgrav if (result) { 196d95e11bfSDag-Erling Smørgrav verbose("DNS lookup error: %s", dns_result_totext(result)); 197efcad6b7SDag-Erling Smørgrav return -1; 198d95e11bfSDag-Erling Smørgrav } 199d95e11bfSDag-Erling Smørgrav 200efcad6b7SDag-Erling Smørgrav if (fingerprints->rri_flags & RRSET_VALIDATED) { 201efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_SECURE; 202efcad6b7SDag-Erling Smørgrav debug("found %d secure fingerprints in DNS", 203efcad6b7SDag-Erling Smørgrav fingerprints->rri_nrdatas); 204efcad6b7SDag-Erling Smørgrav } else { 205efcad6b7SDag-Erling Smørgrav debug("found %d insecure fingerprints in DNS", 206efcad6b7SDag-Erling Smørgrav fingerprints->rri_nrdatas); 207d95e11bfSDag-Erling Smørgrav } 208d95e11bfSDag-Erling Smørgrav 209d95e11bfSDag-Erling Smørgrav /* Initialize host key parameters */ 210d95e11bfSDag-Erling Smørgrav if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, 211d95e11bfSDag-Erling Smørgrav &hostkey_digest, &hostkey_digest_len, hostkey)) { 212d95e11bfSDag-Erling Smørgrav error("Error calculating host key fingerprint."); 213d95e11bfSDag-Erling Smørgrav freerrset(fingerprints); 214efcad6b7SDag-Erling Smørgrav return -1; 215d95e11bfSDag-Erling Smørgrav } 216d95e11bfSDag-Erling Smørgrav 217efcad6b7SDag-Erling Smørgrav if (fingerprints->rri_nrdatas) 218efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_FOUND; 219efcad6b7SDag-Erling Smørgrav 220d95e11bfSDag-Erling Smørgrav for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) { 221d95e11bfSDag-Erling Smørgrav /* 222d95e11bfSDag-Erling Smørgrav * Extract the key from the answer. Ignore any badly 223d95e11bfSDag-Erling Smørgrav * formatted fingerprints. 224d95e11bfSDag-Erling Smørgrav */ 225d95e11bfSDag-Erling Smørgrav if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, 226d95e11bfSDag-Erling Smørgrav &dnskey_digest, &dnskey_digest_len, 227d95e11bfSDag-Erling Smørgrav fingerprints->rri_rdatas[counter].rdi_data, 228d95e11bfSDag-Erling Smørgrav fingerprints->rri_rdatas[counter].rdi_length)) { 229d95e11bfSDag-Erling Smørgrav verbose("Error parsing fingerprint from DNS."); 230d95e11bfSDag-Erling Smørgrav continue; 231d95e11bfSDag-Erling Smørgrav } 232d95e11bfSDag-Erling Smørgrav 233d95e11bfSDag-Erling Smørgrav /* Check if the current key is the same as the given key */ 234d95e11bfSDag-Erling Smørgrav if (hostkey_algorithm == dnskey_algorithm && 235d95e11bfSDag-Erling Smørgrav hostkey_digest_type == dnskey_digest_type) { 236d95e11bfSDag-Erling Smørgrav 237d95e11bfSDag-Erling Smørgrav if (hostkey_digest_len == dnskey_digest_len && 238d95e11bfSDag-Erling Smørgrav memcmp(hostkey_digest, dnskey_digest, 239d95e11bfSDag-Erling Smørgrav hostkey_digest_len) == 0) { 240d95e11bfSDag-Erling Smørgrav 241efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_MATCH; 242efcad6b7SDag-Erling Smørgrav } 243efcad6b7SDag-Erling Smørgrav } 244021d409fSDag-Erling Smørgrav xfree(dnskey_digest); 245efcad6b7SDag-Erling Smørgrav } 246efcad6b7SDag-Erling Smørgrav 247021d409fSDag-Erling Smørgrav xfree(hostkey_digest); /* from key_fingerprint_raw() */ 248d95e11bfSDag-Erling Smørgrav freerrset(fingerprints); 249efcad6b7SDag-Erling Smørgrav 250efcad6b7SDag-Erling Smørgrav if (*flags & DNS_VERIFY_FOUND) 251efcad6b7SDag-Erling Smørgrav if (*flags & DNS_VERIFY_MATCH) 252d95e11bfSDag-Erling Smørgrav debug("matching host key fingerprint found in DNS"); 253efcad6b7SDag-Erling Smørgrav else 254efcad6b7SDag-Erling Smørgrav debug("mismatching host key fingerprint found in DNS"); 255efcad6b7SDag-Erling Smørgrav else 256efcad6b7SDag-Erling Smørgrav debug("no host key fingerprint found in DNS"); 257d95e11bfSDag-Erling Smørgrav 258efcad6b7SDag-Erling Smørgrav return 0; 259d95e11bfSDag-Erling Smørgrav } 260d95e11bfSDag-Erling Smørgrav 261d95e11bfSDag-Erling Smørgrav /* 262d95e11bfSDag-Erling Smørgrav * Export the fingerprint of a key as a DNS resource record 263d95e11bfSDag-Erling Smørgrav */ 264d95e11bfSDag-Erling Smørgrav int 265efcad6b7SDag-Erling Smørgrav export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) 266d95e11bfSDag-Erling Smørgrav { 267d95e11bfSDag-Erling Smørgrav u_int8_t rdata_pubkey_algorithm = 0; 268d95e11bfSDag-Erling Smørgrav u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; 269d95e11bfSDag-Erling Smørgrav u_char *rdata_digest; 270d95e11bfSDag-Erling Smørgrav u_int rdata_digest_len; 271d95e11bfSDag-Erling Smørgrav 272043840dfSDag-Erling Smørgrav u_int i; 273d95e11bfSDag-Erling Smørgrav int success = 0; 274d95e11bfSDag-Erling Smørgrav 275d95e11bfSDag-Erling Smørgrav if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, 276d95e11bfSDag-Erling Smørgrav &rdata_digest, &rdata_digest_len, key)) { 277d95e11bfSDag-Erling Smørgrav 278d95e11bfSDag-Erling Smørgrav if (generic) 279d95e11bfSDag-Erling Smørgrav fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, 280d95e11bfSDag-Erling Smørgrav DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, 281d95e11bfSDag-Erling Smørgrav rdata_pubkey_algorithm, rdata_digest_type); 282d95e11bfSDag-Erling Smørgrav else 283d95e11bfSDag-Erling Smørgrav fprintf(f, "%s IN SSHFP %d %d ", hostname, 284d95e11bfSDag-Erling Smørgrav rdata_pubkey_algorithm, rdata_digest_type); 285d95e11bfSDag-Erling Smørgrav 286d95e11bfSDag-Erling Smørgrav for (i = 0; i < rdata_digest_len; i++) 287d95e11bfSDag-Erling Smørgrav fprintf(f, "%02x", rdata_digest[i]); 288d95e11bfSDag-Erling Smørgrav fprintf(f, "\n"); 289021d409fSDag-Erling Smørgrav xfree(rdata_digest); /* from key_fingerprint_raw() */ 290d95e11bfSDag-Erling Smørgrav success = 1; 291d95e11bfSDag-Erling Smørgrav } else { 292021d409fSDag-Erling Smørgrav error("export_dns_rr: unsupported algorithm"); 293d95e11bfSDag-Erling Smørgrav } 294d95e11bfSDag-Erling Smørgrav 295d95e11bfSDag-Erling Smørgrav return success; 296d95e11bfSDag-Erling Smørgrav } 297