1043840dfSDag-Erling Smørgrav /* $OpenBSD: dns.c,v 1.12 2005/06/17 02:44:32 djm 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 29d95e11bfSDag-Erling Smørgrav #include "includes.h" 30d95e11bfSDag-Erling Smørgrav 31d95e11bfSDag-Erling Smørgrav #include <openssl/bn.h> 32d95e11bfSDag-Erling Smørgrav #ifdef LWRES 33d95e11bfSDag-Erling Smørgrav #include <lwres/netdb.h> 34d95e11bfSDag-Erling Smørgrav #include <dns/result.h> 35d95e11bfSDag-Erling Smørgrav #else /* LWRES */ 36d95e11bfSDag-Erling Smørgrav #include <netdb.h> 37d95e11bfSDag-Erling Smørgrav #endif /* LWRES */ 38d95e11bfSDag-Erling Smørgrav 39d95e11bfSDag-Erling Smørgrav #include "xmalloc.h" 40d95e11bfSDag-Erling Smørgrav #include "key.h" 41d95e11bfSDag-Erling Smørgrav #include "dns.h" 42d95e11bfSDag-Erling Smørgrav #include "log.h" 43d95e11bfSDag-Erling Smørgrav #include "uuencode.h" 44d95e11bfSDag-Erling Smørgrav 45d95e11bfSDag-Erling Smørgrav extern char *__progname; 46043840dfSDag-Erling Smørgrav RCSID("$OpenBSD: dns.c,v 1.12 2005/06/17 02:44:32 djm Exp $"); 47d95e11bfSDag-Erling Smørgrav 48d95e11bfSDag-Erling Smørgrav #ifndef LWRES 49d95e11bfSDag-Erling Smørgrav static const char *errset_text[] = { 50d95e11bfSDag-Erling Smørgrav "success", /* 0 ERRSET_SUCCESS */ 51d95e11bfSDag-Erling Smørgrav "out of memory", /* 1 ERRSET_NOMEMORY */ 52d95e11bfSDag-Erling Smørgrav "general failure", /* 2 ERRSET_FAIL */ 53d95e11bfSDag-Erling Smørgrav "invalid parameter", /* 3 ERRSET_INVAL */ 54d95e11bfSDag-Erling Smørgrav "name does not exist", /* 4 ERRSET_NONAME */ 55d95e11bfSDag-Erling Smørgrav "data does not exist", /* 5 ERRSET_NODATA */ 56d95e11bfSDag-Erling Smørgrav }; 57d95e11bfSDag-Erling Smørgrav 58d95e11bfSDag-Erling Smørgrav static const char * 59d74d50a8SDag-Erling Smørgrav dns_result_totext(unsigned int res) 60d95e11bfSDag-Erling Smørgrav { 61d74d50a8SDag-Erling Smørgrav switch (res) { 62d95e11bfSDag-Erling Smørgrav case ERRSET_SUCCESS: 63d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_SUCCESS]; 64d95e11bfSDag-Erling Smørgrav case ERRSET_NOMEMORY: 65d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NOMEMORY]; 66d95e11bfSDag-Erling Smørgrav case ERRSET_FAIL: 67d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_FAIL]; 68d95e11bfSDag-Erling Smørgrav case ERRSET_INVAL: 69d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_INVAL]; 70d95e11bfSDag-Erling Smørgrav case ERRSET_NONAME: 71d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NONAME]; 72d95e11bfSDag-Erling Smørgrav case ERRSET_NODATA: 73d95e11bfSDag-Erling Smørgrav return errset_text[ERRSET_NODATA]; 74d95e11bfSDag-Erling Smørgrav default: 75d95e11bfSDag-Erling Smørgrav return "unknown error"; 76d95e11bfSDag-Erling Smørgrav } 77d95e11bfSDag-Erling Smørgrav } 78d95e11bfSDag-Erling Smørgrav #endif /* LWRES */ 79d95e11bfSDag-Erling Smørgrav 80d95e11bfSDag-Erling Smørgrav 81d95e11bfSDag-Erling Smørgrav /* 82d95e11bfSDag-Erling Smørgrav * Read SSHFP parameters from key buffer. 83d95e11bfSDag-Erling Smørgrav */ 84d95e11bfSDag-Erling Smørgrav static int 85d95e11bfSDag-Erling Smørgrav dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 86efcad6b7SDag-Erling Smørgrav u_char **digest, u_int *digest_len, const Key *key) 87d95e11bfSDag-Erling Smørgrav { 88d95e11bfSDag-Erling Smørgrav int success = 0; 89d95e11bfSDag-Erling Smørgrav 90d95e11bfSDag-Erling Smørgrav switch (key->type) { 91d95e11bfSDag-Erling Smørgrav case KEY_RSA: 92d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RSA; 93d95e11bfSDag-Erling Smørgrav break; 94d95e11bfSDag-Erling Smørgrav case KEY_DSA: 95d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_DSA; 96d95e11bfSDag-Erling Smørgrav break; 97d95e11bfSDag-Erling Smørgrav default: 98d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RESERVED; 99d95e11bfSDag-Erling Smørgrav } 100d95e11bfSDag-Erling Smørgrav 101d95e11bfSDag-Erling Smørgrav if (*algorithm) { 102d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_SHA1; 103d95e11bfSDag-Erling Smørgrav *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); 104d95e11bfSDag-Erling Smørgrav success = 1; 105d95e11bfSDag-Erling Smørgrav } else { 106d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_RESERVED; 107d95e11bfSDag-Erling Smørgrav *digest = NULL; 108d95e11bfSDag-Erling Smørgrav *digest_len = 0; 109d95e11bfSDag-Erling Smørgrav success = 0; 110d95e11bfSDag-Erling Smørgrav } 111d95e11bfSDag-Erling Smørgrav 112d95e11bfSDag-Erling Smørgrav return success; 113d95e11bfSDag-Erling Smørgrav } 114d95e11bfSDag-Erling Smørgrav 115d95e11bfSDag-Erling Smørgrav /* 116d95e11bfSDag-Erling Smørgrav * Read SSHFP parameters from rdata buffer. 117d95e11bfSDag-Erling Smørgrav */ 118d95e11bfSDag-Erling Smørgrav static int 119d95e11bfSDag-Erling Smørgrav dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, 120d95e11bfSDag-Erling Smørgrav u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) 121d95e11bfSDag-Erling Smørgrav { 122d95e11bfSDag-Erling Smørgrav int success = 0; 123d95e11bfSDag-Erling Smørgrav 124d95e11bfSDag-Erling Smørgrav *algorithm = SSHFP_KEY_RESERVED; 125d95e11bfSDag-Erling Smørgrav *digest_type = SSHFP_HASH_RESERVED; 126d95e11bfSDag-Erling Smørgrav 127d95e11bfSDag-Erling Smørgrav if (rdata_len >= 2) { 128d95e11bfSDag-Erling Smørgrav *algorithm = rdata[0]; 129d95e11bfSDag-Erling Smørgrav *digest_type = rdata[1]; 130d95e11bfSDag-Erling Smørgrav *digest_len = rdata_len - 2; 131d95e11bfSDag-Erling Smørgrav 132d95e11bfSDag-Erling Smørgrav if (*digest_len > 0) { 133d95e11bfSDag-Erling Smørgrav *digest = (u_char *) xmalloc(*digest_len); 134d95e11bfSDag-Erling Smørgrav memcpy(*digest, rdata + 2, *digest_len); 135d95e11bfSDag-Erling Smørgrav } else { 136d95e11bfSDag-Erling Smørgrav *digest = NULL; 137d95e11bfSDag-Erling Smørgrav } 138d95e11bfSDag-Erling Smørgrav 139d95e11bfSDag-Erling Smørgrav success = 1; 140d95e11bfSDag-Erling Smørgrav } 141d95e11bfSDag-Erling Smørgrav 142d95e11bfSDag-Erling Smørgrav return success; 143d95e11bfSDag-Erling Smørgrav } 144d95e11bfSDag-Erling Smørgrav 145043840dfSDag-Erling Smørgrav /* 146043840dfSDag-Erling Smørgrav * Check if hostname is numerical. 147043840dfSDag-Erling Smørgrav * Returns -1 if hostname is numeric, 0 otherwise 148043840dfSDag-Erling Smørgrav */ 149043840dfSDag-Erling Smørgrav static int 150043840dfSDag-Erling Smørgrav is_numeric_hostname(const char *hostname) 151043840dfSDag-Erling Smørgrav { 152043840dfSDag-Erling Smørgrav struct addrinfo hints, *ai; 153043840dfSDag-Erling Smørgrav 154043840dfSDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 155043840dfSDag-Erling Smørgrav hints.ai_socktype = SOCK_DGRAM; 156043840dfSDag-Erling Smørgrav hints.ai_flags = AI_NUMERICHOST; 157043840dfSDag-Erling Smørgrav 158043840dfSDag-Erling Smørgrav if (getaddrinfo(hostname, "0", &hints, &ai) == 0) { 159043840dfSDag-Erling Smørgrav freeaddrinfo(ai); 160043840dfSDag-Erling Smørgrav return -1; 161043840dfSDag-Erling Smørgrav } 162043840dfSDag-Erling Smørgrav 163043840dfSDag-Erling Smørgrav return 0; 164043840dfSDag-Erling Smørgrav } 165d95e11bfSDag-Erling Smørgrav 166d95e11bfSDag-Erling Smørgrav /* 167d95e11bfSDag-Erling Smørgrav * Verify the given hostname, address and host key using DNS. 168efcad6b7SDag-Erling Smørgrav * Returns 0 if lookup succeeds, -1 otherwise 169d95e11bfSDag-Erling Smørgrav */ 170d95e11bfSDag-Erling Smørgrav int 171d95e11bfSDag-Erling Smørgrav verify_host_key_dns(const char *hostname, struct sockaddr *address, 172efcad6b7SDag-Erling Smørgrav const Key *hostkey, int *flags) 173d95e11bfSDag-Erling Smørgrav { 174043840dfSDag-Erling Smørgrav u_int counter; 175d95e11bfSDag-Erling Smørgrav int result; 176d95e11bfSDag-Erling Smørgrav struct rrsetinfo *fingerprints = NULL; 177d95e11bfSDag-Erling Smørgrav 178d95e11bfSDag-Erling Smørgrav u_int8_t hostkey_algorithm; 179d95e11bfSDag-Erling Smørgrav u_int8_t hostkey_digest_type; 180d95e11bfSDag-Erling Smørgrav u_char *hostkey_digest; 181d95e11bfSDag-Erling Smørgrav u_int hostkey_digest_len; 182d95e11bfSDag-Erling Smørgrav 183d95e11bfSDag-Erling Smørgrav u_int8_t dnskey_algorithm; 184d95e11bfSDag-Erling Smørgrav u_int8_t dnskey_digest_type; 185d95e11bfSDag-Erling Smørgrav u_char *dnskey_digest; 186d95e11bfSDag-Erling Smørgrav u_int dnskey_digest_len; 187d95e11bfSDag-Erling Smørgrav 188efcad6b7SDag-Erling Smørgrav *flags = 0; 189d95e11bfSDag-Erling Smørgrav 190d95e11bfSDag-Erling Smørgrav debug3("verify_hostkey_dns"); 191d95e11bfSDag-Erling Smørgrav if (hostkey == NULL) 192d95e11bfSDag-Erling Smørgrav fatal("No key to look up!"); 193d95e11bfSDag-Erling Smørgrav 194043840dfSDag-Erling Smørgrav if (is_numeric_hostname(hostname)) { 195043840dfSDag-Erling Smørgrav debug("skipped DNS lookup for numerical hostname"); 196043840dfSDag-Erling Smørgrav return -1; 197043840dfSDag-Erling Smørgrav } 198043840dfSDag-Erling Smørgrav 199d95e11bfSDag-Erling Smørgrav result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, 200d95e11bfSDag-Erling Smørgrav DNS_RDATATYPE_SSHFP, 0, &fingerprints); 201d95e11bfSDag-Erling Smørgrav if (result) { 202d95e11bfSDag-Erling Smørgrav verbose("DNS lookup error: %s", dns_result_totext(result)); 203efcad6b7SDag-Erling Smørgrav return -1; 204d95e11bfSDag-Erling Smørgrav } 205d95e11bfSDag-Erling Smørgrav 206efcad6b7SDag-Erling Smørgrav if (fingerprints->rri_flags & RRSET_VALIDATED) { 207efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_SECURE; 208efcad6b7SDag-Erling Smørgrav debug("found %d secure fingerprints in DNS", 209efcad6b7SDag-Erling Smørgrav fingerprints->rri_nrdatas); 210efcad6b7SDag-Erling Smørgrav } else { 211efcad6b7SDag-Erling Smørgrav debug("found %d insecure fingerprints in DNS", 212efcad6b7SDag-Erling Smørgrav fingerprints->rri_nrdatas); 213d95e11bfSDag-Erling Smørgrav } 214d95e11bfSDag-Erling Smørgrav 215d95e11bfSDag-Erling Smørgrav /* Initialize host key parameters */ 216d95e11bfSDag-Erling Smørgrav if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, 217d95e11bfSDag-Erling Smørgrav &hostkey_digest, &hostkey_digest_len, hostkey)) { 218d95e11bfSDag-Erling Smørgrav error("Error calculating host key fingerprint."); 219d95e11bfSDag-Erling Smørgrav freerrset(fingerprints); 220efcad6b7SDag-Erling Smørgrav return -1; 221d95e11bfSDag-Erling Smørgrav } 222d95e11bfSDag-Erling Smørgrav 223efcad6b7SDag-Erling Smørgrav if (fingerprints->rri_nrdatas) 224efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_FOUND; 225efcad6b7SDag-Erling Smørgrav 226d95e11bfSDag-Erling Smørgrav for (counter = 0 ; counter < fingerprints->rri_nrdatas ; counter++) { 227d95e11bfSDag-Erling Smørgrav /* 228d95e11bfSDag-Erling Smørgrav * Extract the key from the answer. Ignore any badly 229d95e11bfSDag-Erling Smørgrav * formatted fingerprints. 230d95e11bfSDag-Erling Smørgrav */ 231d95e11bfSDag-Erling Smørgrav if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, 232d95e11bfSDag-Erling Smørgrav &dnskey_digest, &dnskey_digest_len, 233d95e11bfSDag-Erling Smørgrav fingerprints->rri_rdatas[counter].rdi_data, 234d95e11bfSDag-Erling Smørgrav fingerprints->rri_rdatas[counter].rdi_length)) { 235d95e11bfSDag-Erling Smørgrav verbose("Error parsing fingerprint from DNS."); 236d95e11bfSDag-Erling Smørgrav continue; 237d95e11bfSDag-Erling Smørgrav } 238d95e11bfSDag-Erling Smørgrav 239d95e11bfSDag-Erling Smørgrav /* Check if the current key is the same as the given key */ 240d95e11bfSDag-Erling Smørgrav if (hostkey_algorithm == dnskey_algorithm && 241d95e11bfSDag-Erling Smørgrav hostkey_digest_type == dnskey_digest_type) { 242d95e11bfSDag-Erling Smørgrav 243d95e11bfSDag-Erling Smørgrav if (hostkey_digest_len == dnskey_digest_len && 244d95e11bfSDag-Erling Smørgrav memcmp(hostkey_digest, dnskey_digest, 245d95e11bfSDag-Erling Smørgrav hostkey_digest_len) == 0) { 246d95e11bfSDag-Erling Smørgrav 247efcad6b7SDag-Erling Smørgrav *flags |= DNS_VERIFY_MATCH; 248efcad6b7SDag-Erling Smørgrav } 249efcad6b7SDag-Erling Smørgrav } 250efcad6b7SDag-Erling Smørgrav } 251efcad6b7SDag-Erling Smørgrav 252d95e11bfSDag-Erling Smørgrav freerrset(fingerprints); 253efcad6b7SDag-Erling Smørgrav 254efcad6b7SDag-Erling Smørgrav if (*flags & DNS_VERIFY_FOUND) 255efcad6b7SDag-Erling Smørgrav if (*flags & DNS_VERIFY_MATCH) 256d95e11bfSDag-Erling Smørgrav debug("matching host key fingerprint found in DNS"); 257efcad6b7SDag-Erling Smørgrav else 258efcad6b7SDag-Erling Smørgrav debug("mismatching host key fingerprint found in DNS"); 259efcad6b7SDag-Erling Smørgrav else 260efcad6b7SDag-Erling Smørgrav debug("no host key fingerprint found in DNS"); 261d95e11bfSDag-Erling Smørgrav 262efcad6b7SDag-Erling Smørgrav return 0; 263d95e11bfSDag-Erling Smørgrav } 264d95e11bfSDag-Erling Smørgrav 265d95e11bfSDag-Erling Smørgrav 266d95e11bfSDag-Erling Smørgrav /* 267d95e11bfSDag-Erling Smørgrav * Export the fingerprint of a key as a DNS resource record 268d95e11bfSDag-Erling Smørgrav */ 269d95e11bfSDag-Erling Smørgrav int 270efcad6b7SDag-Erling Smørgrav export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) 271d95e11bfSDag-Erling Smørgrav { 272d95e11bfSDag-Erling Smørgrav u_int8_t rdata_pubkey_algorithm = 0; 273d95e11bfSDag-Erling Smørgrav u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; 274d95e11bfSDag-Erling Smørgrav u_char *rdata_digest; 275d95e11bfSDag-Erling Smørgrav u_int rdata_digest_len; 276d95e11bfSDag-Erling Smørgrav 277043840dfSDag-Erling Smørgrav u_int i; 278d95e11bfSDag-Erling Smørgrav int success = 0; 279d95e11bfSDag-Erling Smørgrav 280d95e11bfSDag-Erling Smørgrav if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, 281d95e11bfSDag-Erling Smørgrav &rdata_digest, &rdata_digest_len, key)) { 282d95e11bfSDag-Erling Smørgrav 283d95e11bfSDag-Erling Smørgrav if (generic) 284d95e11bfSDag-Erling Smørgrav fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, 285d95e11bfSDag-Erling Smørgrav DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, 286d95e11bfSDag-Erling Smørgrav rdata_pubkey_algorithm, rdata_digest_type); 287d95e11bfSDag-Erling Smørgrav else 288d95e11bfSDag-Erling Smørgrav fprintf(f, "%s IN SSHFP %d %d ", hostname, 289d95e11bfSDag-Erling Smørgrav rdata_pubkey_algorithm, rdata_digest_type); 290d95e11bfSDag-Erling Smørgrav 291d95e11bfSDag-Erling Smørgrav for (i = 0; i < rdata_digest_len; i++) 292d95e11bfSDag-Erling Smørgrav fprintf(f, "%02x", rdata_digest[i]); 293d95e11bfSDag-Erling Smørgrav fprintf(f, "\n"); 294d95e11bfSDag-Erling Smørgrav success = 1; 295d95e11bfSDag-Erling Smørgrav } else { 296d95e11bfSDag-Erling Smørgrav error("dns_export_rr: unsupported algorithm"); 297d95e11bfSDag-Erling Smørgrav } 298d95e11bfSDag-Erling Smørgrav 299d95e11bfSDag-Erling Smørgrav return success; 300d95e11bfSDag-Erling Smørgrav } 301