dns.c revision 181097
1169695Skan/* $OpenBSD: dns.c,v 1.23 2006/08/03 03:34:42 deraadt Exp $ */ 2169695Skan 3169695Skan/* 4169695Skan * Copyright (c) 2003 Wesley Griffin. All rights reserved. 5169695Skan * Copyright (c) 2003 Jakob Schlyter. All rights reserved. 6169695Skan * 7169695Skan * Redistribution and use in source and binary forms, with or without 8169695Skan * modification, are permitted provided that the following conditions 9169695Skan * are met: 10169695Skan * 1. Redistributions of source code must retain the above copyright 11169695Skan * notice, this list of conditions and the following disclaimer. 12169695Skan * 2. Redistributions in binary form must reproduce the above copyright 13169695Skan * notice, this list of conditions and the following disclaimer in the 14169695Skan * documentation and/or other materials provided with the distribution. 15169695Skan * 16169695Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17169695Skan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18169695Skan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19169695Skan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20169695Skan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21169695Skan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22169695Skan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23169695Skan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24169695Skan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25169695Skan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26169695Skan */ 27169695Skan 28169695Skan#include "includes.h" 29169695Skan 30169695Skan#include <sys/types.h> 31169695Skan#include <sys/socket.h> 32169695Skan 33169695Skan#include <netdb.h> 34169695Skan#include <stdarg.h> 35169695Skan#include <stdio.h> 36169695Skan#include <string.h> 37169695Skan 38169695Skan#include "xmalloc.h" 39169695Skan#include "key.h" 40169695Skan#include "dns.h" 41169695Skan#include "log.h" 42169695Skan 43169695Skanstatic const char *errset_text[] = { 44169695Skan "success", /* 0 ERRSET_SUCCESS */ 45169695Skan "out of memory", /* 1 ERRSET_NOMEMORY */ 46169695Skan "general failure", /* 2 ERRSET_FAIL */ 47169695Skan "invalid parameter", /* 3 ERRSET_INVAL */ 48169695Skan "name does not exist", /* 4 ERRSET_NONAME */ 49169695Skan "data does not exist", /* 5 ERRSET_NODATA */ 50169695Skan}; 51169695Skan 52169695Skanstatic const char * 53169695Skandns_result_totext(unsigned int res) 54169695Skan{ 55169695Skan switch (res) { 56169695Skan case ERRSET_SUCCESS: 57169695Skan return errset_text[ERRSET_SUCCESS]; 58169695Skan case ERRSET_NOMEMORY: 59169695Skan return errset_text[ERRSET_NOMEMORY]; 60169695Skan case ERRSET_FAIL: 61169695Skan return errset_text[ERRSET_FAIL]; 62169695Skan case ERRSET_INVAL: 63169695Skan return errset_text[ERRSET_INVAL]; 64169695Skan case ERRSET_NONAME: 65169695Skan return errset_text[ERRSET_NONAME]; 66169695Skan case ERRSET_NODATA: 67169695Skan return errset_text[ERRSET_NODATA]; 68169695Skan default: 69169695Skan return "unknown error"; 70169695Skan } 71169695Skan} 72169695Skan 73169695Skan/* 74169695Skan * Read SSHFP parameters from key buffer. 75169695Skan */ 76169695Skanstatic int 77169695Skandns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 78169695Skan u_char **digest, u_int *digest_len, const Key *key) 79169695Skan{ 80169695Skan int success = 0; 81169695Skan 82169695Skan switch (key->type) { 83169695Skan case KEY_RSA: 84169695Skan *algorithm = SSHFP_KEY_RSA; 85169695Skan break; 86169695Skan case KEY_DSA: 87169695Skan *algorithm = SSHFP_KEY_DSA; 88169695Skan break; 89169695Skan default: 90169695Skan *algorithm = SSHFP_KEY_RESERVED; /* 0 */ 91169695Skan } 92169695Skan 93169695Skan if (*algorithm) { 94169695Skan *digest_type = SSHFP_HASH_SHA1; 95169695Skan *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); 96169695Skan if (*digest == NULL) 97169695Skan fatal("dns_read_key: null from key_fingerprint_raw()"); 98169695Skan success = 1; 99169695Skan } else { 100169695Skan *digest_type = SSHFP_HASH_RESERVED; 101169695Skan *digest = NULL; 102169695Skan *digest_len = 0; 103169695Skan success = 0; 104169695Skan } 105169695Skan 106169695Skan return success; 107169695Skan} 108169695Skan 109169695Skan/* 110169695Skan * Read SSHFP parameters from rdata buffer. 111169695Skan */ 112169695Skanstatic int 113169695Skandns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, 114169695Skan u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) 115169695Skan{ 116169695Skan int success = 0; 117169695Skan 118169695Skan *algorithm = SSHFP_KEY_RESERVED; 119169695Skan *digest_type = SSHFP_HASH_RESERVED; 120169695Skan 121169695Skan if (rdata_len >= 2) { 122169695Skan *algorithm = rdata[0]; 123169695Skan *digest_type = rdata[1]; 124169695Skan *digest_len = rdata_len - 2; 125169695Skan 126169695Skan if (*digest_len > 0) { 127169695Skan *digest = (u_char *) xmalloc(*digest_len); 128169695Skan memcpy(*digest, rdata + 2, *digest_len); 129169695Skan } else { 130169695Skan *digest = (u_char *)xstrdup(""); 131169695Skan } 132169695Skan 133169695Skan success = 1; 134169695Skan } 135169695Skan 136169695Skan return success; 137169695Skan} 138169695Skan 139169695Skan/* 140169695Skan * Check if hostname is numerical. 141169695Skan * Returns -1 if hostname is numeric, 0 otherwise 142169695Skan */ 143169695Skanstatic int 144169695Skanis_numeric_hostname(const char *hostname) 145169695Skan{ 146169695Skan struct addrinfo hints, *ai; 147169695Skan 148169695Skan memset(&hints, 0, sizeof(hints)); 149169695Skan hints.ai_socktype = SOCK_DGRAM; 150169695Skan hints.ai_flags = AI_NUMERICHOST; 151169695Skan 152169695Skan if (getaddrinfo(hostname, "0", &hints, &ai) == 0) { 153169695Skan freeaddrinfo(ai); 154169695Skan return -1; 155169695Skan } 156169695Skan 157169695Skan return 0; 158169695Skan} 159169695Skan 160169695Skan/* 161169695Skan * Verify the given hostname, address and host key using DNS. 162169695Skan * Returns 0 if lookup succeeds, -1 otherwise 163169695Skan */ 164169695Skanint 165169695Skanverify_host_key_dns(const char *hostname, struct sockaddr *address, 166169695Skan const Key *hostkey, int *flags) 167169695Skan{ 168169695Skan u_int counter; 169169695Skan int result; 170169695Skan struct rrsetinfo *fingerprints = NULL; 171169695Skan 172169695Skan u_int8_t hostkey_algorithm; 173169695Skan u_int8_t hostkey_digest_type; 174169695Skan u_char *hostkey_digest; 175169695Skan u_int hostkey_digest_len; 176169695Skan 177169695Skan u_int8_t dnskey_algorithm; 178169695Skan u_int8_t dnskey_digest_type; 179169695Skan u_char *dnskey_digest; 180169695Skan u_int dnskey_digest_len; 181169695Skan 182169695Skan *flags = 0; 183169695Skan 184169695Skan debug3("verify_host_key_dns"); 185169695Skan if (hostkey == NULL) 186169695Skan fatal("No key to look up!"); 187169695Skan 188169695Skan if (is_numeric_hostname(hostname)) { 189169695Skan debug("skipped DNS lookup for numerical hostname"); 190169695Skan return -1; 191169695Skan } 192169695Skan 193169695Skan result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, 194169695Skan DNS_RDATATYPE_SSHFP, 0, &fingerprints); 195169695Skan if (result) { 196169695Skan verbose("DNS lookup error: %s", dns_result_totext(result)); 197169695Skan return -1; 198169695Skan } 199169695Skan 200169695Skan if (fingerprints->rri_flags & RRSET_VALIDATED) { 201169695Skan *flags |= DNS_VERIFY_SECURE; 202169695Skan debug("found %d secure fingerprints in DNS", 203169695Skan fingerprints->rri_nrdatas); 204169695Skan } else { 205169695Skan debug("found %d insecure fingerprints in DNS", 206169695Skan fingerprints->rri_nrdatas); 207169695Skan } 208169695Skan 209169695Skan /* Initialize host key parameters */ 210169695Skan if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, 211169695Skan &hostkey_digest, &hostkey_digest_len, hostkey)) { 212169695Skan error("Error calculating host key fingerprint."); 213169695Skan freerrset(fingerprints); 214169695Skan return -1; 215169695Skan } 216169695Skan 217169695Skan if (fingerprints->rri_nrdatas) 218169695Skan *flags |= DNS_VERIFY_FOUND; 219169695Skan 220169695Skan for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) { 221169695Skan /* 222169695Skan * Extract the key from the answer. Ignore any badly 223169695Skan * formatted fingerprints. 224169695Skan */ 225169695Skan if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, 226169695Skan &dnskey_digest, &dnskey_digest_len, 227169695Skan fingerprints->rri_rdatas[counter].rdi_data, 228169695Skan fingerprints->rri_rdatas[counter].rdi_length)) { 229169695Skan verbose("Error parsing fingerprint from DNS."); 230169695Skan continue; 231169695Skan } 232169695Skan 233169695Skan /* Check if the current key is the same as the given key */ 234169695Skan if (hostkey_algorithm == dnskey_algorithm && 235169695Skan hostkey_digest_type == dnskey_digest_type) { 236169695Skan 237169695Skan if (hostkey_digest_len == dnskey_digest_len && 238169695Skan memcmp(hostkey_digest, dnskey_digest, 239169695Skan hostkey_digest_len) == 0) { 240169695Skan 241169695Skan *flags |= DNS_VERIFY_MATCH; 242169695Skan } 243169695Skan } 244169695Skan xfree(dnskey_digest); 245169695Skan } 246169695Skan 247169695Skan xfree(hostkey_digest); /* from key_fingerprint_raw() */ 248169695Skan freerrset(fingerprints); 249169695Skan 250169695Skan if (*flags & DNS_VERIFY_FOUND) 251169695Skan if (*flags & DNS_VERIFY_MATCH) 252169695Skan debug("matching host key fingerprint found in DNS"); 253169695Skan else 254169695Skan debug("mismatching host key fingerprint found in DNS"); 255169695Skan else 256169695Skan debug("no host key fingerprint found in DNS"); 257169695Skan 258169695Skan return 0; 259169695Skan} 260169695Skan 261169695Skan/* 262169695Skan * Export the fingerprint of a key as a DNS resource record 263169695Skan */ 264169695Skanint 265169695Skanexport_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) 266169695Skan{ 267169695Skan u_int8_t rdata_pubkey_algorithm = 0; 268169695Skan u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; 269169695Skan u_char *rdata_digest; 270169695Skan u_int rdata_digest_len; 271169695Skan 272169695Skan u_int i; 273169695Skan int success = 0; 274169695Skan 275169695Skan if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, 276169695Skan &rdata_digest, &rdata_digest_len, key)) { 277169695Skan 278169695Skan if (generic) 279169695Skan fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, 280169695Skan DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, 281169695Skan rdata_pubkey_algorithm, rdata_digest_type); 282169695Skan else 283169695Skan fprintf(f, "%s IN SSHFP %d %d ", hostname, 284169695Skan rdata_pubkey_algorithm, rdata_digest_type); 285169695Skan 286169695Skan for (i = 0; i < rdata_digest_len; i++) 287169695Skan fprintf(f, "%02x", rdata_digest[i]); 288169695Skan fprintf(f, "\n"); 289169695Skan xfree(rdata_digest); /* from key_fingerprint_raw() */ 290169695Skan success = 1; 291169695Skan } else { 292169695Skan error("export_dns_rr: unsupported algorithm"); 293169695Skan } 294169695Skan 295169695Skan return success; 296169695Skan} 297169695Skan