1255767Sdes/* $OpenBSD: dns.c,v 1.29 2013/05/17 00:13:13 djm Exp $ */ 2124208Sdes 3124208Sdes/* 4124208Sdes * Copyright (c) 2003 Wesley Griffin. All rights reserved. 5124208Sdes * Copyright (c) 2003 Jakob Schlyter. All rights reserved. 6124208Sdes * 7124208Sdes * Redistribution and use in source and binary forms, with or without 8124208Sdes * modification, are permitted provided that the following conditions 9124208Sdes * are met: 10124208Sdes * 1. Redistributions of source code must retain the above copyright 11124208Sdes * notice, this list of conditions and the following disclaimer. 12124208Sdes * 2. Redistributions in binary form must reproduce the above copyright 13124208Sdes * notice, this list of conditions and the following disclaimer in the 14124208Sdes * documentation and/or other materials provided with the distribution. 15124208Sdes * 16124208Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17124208Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18124208Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19124208Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20124208Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21124208Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22124208Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23124208Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24124208Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25124208Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26124208Sdes */ 27124208Sdes 28124208Sdes#include "includes.h" 29124208Sdes 30162852Sdes#include <sys/types.h> 31162852Sdes#include <sys/socket.h> 32162852Sdes 33124208Sdes#include <netdb.h> 34162852Sdes#include <stdarg.h> 35162852Sdes#include <stdio.h> 36162852Sdes#include <string.h> 37124208Sdes 38124208Sdes#include "xmalloc.h" 39124208Sdes#include "key.h" 40124208Sdes#include "dns.h" 41124208Sdes#include "log.h" 42124208Sdes 43124208Sdesstatic const char *errset_text[] = { 44124208Sdes "success", /* 0 ERRSET_SUCCESS */ 45124208Sdes "out of memory", /* 1 ERRSET_NOMEMORY */ 46124208Sdes "general failure", /* 2 ERRSET_FAIL */ 47124208Sdes "invalid parameter", /* 3 ERRSET_INVAL */ 48124208Sdes "name does not exist", /* 4 ERRSET_NONAME */ 49124208Sdes "data does not exist", /* 5 ERRSET_NODATA */ 50124208Sdes}; 51124208Sdes 52124208Sdesstatic const char * 53137015Sdesdns_result_totext(unsigned int res) 54124208Sdes{ 55137015Sdes switch (res) { 56124208Sdes case ERRSET_SUCCESS: 57124208Sdes return errset_text[ERRSET_SUCCESS]; 58124208Sdes case ERRSET_NOMEMORY: 59124208Sdes return errset_text[ERRSET_NOMEMORY]; 60124208Sdes case ERRSET_FAIL: 61124208Sdes return errset_text[ERRSET_FAIL]; 62124208Sdes case ERRSET_INVAL: 63124208Sdes return errset_text[ERRSET_INVAL]; 64124208Sdes case ERRSET_NONAME: 65124208Sdes return errset_text[ERRSET_NONAME]; 66124208Sdes case ERRSET_NODATA: 67124208Sdes return errset_text[ERRSET_NODATA]; 68124208Sdes default: 69124208Sdes return "unknown error"; 70124208Sdes } 71124208Sdes} 72124208Sdes 73124208Sdes/* 74124208Sdes * Read SSHFP parameters from key buffer. 75124208Sdes */ 76124208Sdesstatic int 77124208Sdesdns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, 78204917Sdes u_char **digest, u_int *digest_len, Key *key) 79124208Sdes{ 80124208Sdes int success = 0; 81240075Sdes enum fp_type fp_type = 0; 82124208Sdes 83124208Sdes switch (key->type) { 84124208Sdes case KEY_RSA: 85124208Sdes *algorithm = SSHFP_KEY_RSA; 86240075Sdes if (!*digest_type) 87240075Sdes *digest_type = SSHFP_HASH_SHA1; 88124208Sdes break; 89124208Sdes case KEY_DSA: 90124208Sdes *algorithm = SSHFP_KEY_DSA; 91240075Sdes if (!*digest_type) 92240075Sdes *digest_type = SSHFP_HASH_SHA1; 93124208Sdes break; 94240075Sdes case KEY_ECDSA: 95240075Sdes *algorithm = SSHFP_KEY_ECDSA; 96240075Sdes if (!*digest_type) 97240075Sdes *digest_type = SSHFP_HASH_SHA256; 98240075Sdes break; 99124208Sdes default: 100157016Sdes *algorithm = SSHFP_KEY_RESERVED; /* 0 */ 101240075Sdes *digest_type = SSHFP_HASH_RESERVED; /* 0 */ 102124208Sdes } 103124208Sdes 104240075Sdes switch (*digest_type) { 105240075Sdes case SSHFP_HASH_SHA1: 106240075Sdes fp_type = SSH_FP_SHA1; 107240075Sdes break; 108240075Sdes case SSHFP_HASH_SHA256: 109240075Sdes fp_type = SSH_FP_SHA256; 110240075Sdes break; 111240075Sdes default: 112240075Sdes *digest_type = SSHFP_HASH_RESERVED; /* 0 */ 113240075Sdes } 114240075Sdes 115240075Sdes if (*algorithm && *digest_type) { 116240075Sdes *digest = key_fingerprint_raw(key, fp_type, digest_len); 117157016Sdes if (*digest == NULL) 118157016Sdes fatal("dns_read_key: null from key_fingerprint_raw()"); 119124208Sdes success = 1; 120124208Sdes } else { 121124208Sdes *digest = NULL; 122124208Sdes *digest_len = 0; 123124208Sdes success = 0; 124124208Sdes } 125124208Sdes 126124208Sdes return success; 127124208Sdes} 128124208Sdes 129124208Sdes/* 130124208Sdes * Read SSHFP parameters from rdata buffer. 131124208Sdes */ 132124208Sdesstatic int 133124208Sdesdns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, 134124208Sdes u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) 135124208Sdes{ 136124208Sdes int success = 0; 137124208Sdes 138124208Sdes *algorithm = SSHFP_KEY_RESERVED; 139124208Sdes *digest_type = SSHFP_HASH_RESERVED; 140124208Sdes 141124208Sdes if (rdata_len >= 2) { 142124208Sdes *algorithm = rdata[0]; 143124208Sdes *digest_type = rdata[1]; 144124208Sdes *digest_len = rdata_len - 2; 145124208Sdes 146124208Sdes if (*digest_len > 0) { 147124208Sdes *digest = (u_char *) xmalloc(*digest_len); 148124208Sdes memcpy(*digest, rdata + 2, *digest_len); 149124208Sdes } else { 150162852Sdes *digest = (u_char *)xstrdup(""); 151124208Sdes } 152124208Sdes 153124208Sdes success = 1; 154124208Sdes } 155124208Sdes 156124208Sdes return success; 157124208Sdes} 158124208Sdes 159149749Sdes/* 160149749Sdes * Check if hostname is numerical. 161149749Sdes * Returns -1 if hostname is numeric, 0 otherwise 162149749Sdes */ 163149749Sdesstatic int 164149749Sdesis_numeric_hostname(const char *hostname) 165149749Sdes{ 166149749Sdes struct addrinfo hints, *ai; 167124208Sdes 168181111Sdes /* 169181111Sdes * We shouldn't ever get a null host but if we do then log an error 170181111Sdes * and return -1 which stops DNS key fingerprint processing. 171181111Sdes */ 172181111Sdes if (hostname == NULL) { 173181111Sdes error("is_numeric_hostname called with NULL hostname"); 174181111Sdes return -1; 175181111Sdes } 176181111Sdes 177149749Sdes memset(&hints, 0, sizeof(hints)); 178149749Sdes hints.ai_socktype = SOCK_DGRAM; 179149749Sdes hints.ai_flags = AI_NUMERICHOST; 180149749Sdes 181181111Sdes if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) { 182149749Sdes freeaddrinfo(ai); 183149749Sdes return -1; 184149749Sdes } 185149749Sdes 186149749Sdes return 0; 187149749Sdes} 188149749Sdes 189124208Sdes/* 190124208Sdes * Verify the given hostname, address and host key using DNS. 191126274Sdes * Returns 0 if lookup succeeds, -1 otherwise 192124208Sdes */ 193124208Sdesint 194124208Sdesverify_host_key_dns(const char *hostname, struct sockaddr *address, 195204917Sdes Key *hostkey, int *flags) 196124208Sdes{ 197149749Sdes u_int counter; 198124208Sdes int result; 199124208Sdes struct rrsetinfo *fingerprints = NULL; 200124208Sdes 201124208Sdes u_int8_t hostkey_algorithm; 202240075Sdes u_int8_t hostkey_digest_type = SSHFP_HASH_RESERVED; 203124208Sdes u_char *hostkey_digest; 204124208Sdes u_int hostkey_digest_len; 205124208Sdes 206124208Sdes u_int8_t dnskey_algorithm; 207124208Sdes u_int8_t dnskey_digest_type; 208124208Sdes u_char *dnskey_digest; 209124208Sdes u_int dnskey_digest_len; 210124208Sdes 211126274Sdes *flags = 0; 212124208Sdes 213157016Sdes debug3("verify_host_key_dns"); 214124208Sdes if (hostkey == NULL) 215124208Sdes fatal("No key to look up!"); 216124208Sdes 217149749Sdes if (is_numeric_hostname(hostname)) { 218149749Sdes debug("skipped DNS lookup for numerical hostname"); 219149749Sdes return -1; 220149749Sdes } 221149749Sdes 222124208Sdes result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, 223124208Sdes DNS_RDATATYPE_SSHFP, 0, &fingerprints); 224124208Sdes if (result) { 225124208Sdes verbose("DNS lookup error: %s", dns_result_totext(result)); 226126274Sdes return -1; 227124208Sdes } 228124208Sdes 229126274Sdes if (fingerprints->rri_flags & RRSET_VALIDATED) { 230126274Sdes *flags |= DNS_VERIFY_SECURE; 231126274Sdes debug("found %d secure fingerprints in DNS", 232126274Sdes fingerprints->rri_nrdatas); 233126274Sdes } else { 234126274Sdes debug("found %d insecure fingerprints in DNS", 235126274Sdes fingerprints->rri_nrdatas); 236124208Sdes } 237124208Sdes 238240075Sdes /* Initialize default host key parameters */ 239124208Sdes if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, 240124208Sdes &hostkey_digest, &hostkey_digest_len, hostkey)) { 241124208Sdes error("Error calculating host key fingerprint."); 242124208Sdes freerrset(fingerprints); 243126274Sdes return -1; 244124208Sdes } 245124208Sdes 246126274Sdes if (fingerprints->rri_nrdatas) 247126274Sdes *flags |= DNS_VERIFY_FOUND; 248126274Sdes 249181111Sdes for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) { 250124208Sdes /* 251124208Sdes * Extract the key from the answer. Ignore any badly 252124208Sdes * formatted fingerprints. 253124208Sdes */ 254124208Sdes if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, 255124208Sdes &dnskey_digest, &dnskey_digest_len, 256124208Sdes fingerprints->rri_rdatas[counter].rdi_data, 257124208Sdes fingerprints->rri_rdatas[counter].rdi_length)) { 258124208Sdes verbose("Error parsing fingerprint from DNS."); 259124208Sdes continue; 260124208Sdes } 261124208Sdes 262240075Sdes if (hostkey_digest_type != dnskey_digest_type) { 263240075Sdes hostkey_digest_type = dnskey_digest_type; 264255767Sdes free(hostkey_digest); 265240075Sdes 266240075Sdes /* Initialize host key parameters */ 267240075Sdes if (!dns_read_key(&hostkey_algorithm, 268240075Sdes &hostkey_digest_type, &hostkey_digest, 269240075Sdes &hostkey_digest_len, hostkey)) { 270240075Sdes error("Error calculating key fingerprint."); 271240075Sdes freerrset(fingerprints); 272240075Sdes return -1; 273240075Sdes } 274240075Sdes } 275240075Sdes 276124208Sdes /* Check if the current key is the same as the given key */ 277124208Sdes if (hostkey_algorithm == dnskey_algorithm && 278124208Sdes hostkey_digest_type == dnskey_digest_type) { 279124208Sdes if (hostkey_digest_len == dnskey_digest_len && 280240075Sdes timingsafe_bcmp(hostkey_digest, dnskey_digest, 281240075Sdes hostkey_digest_len) == 0) 282126274Sdes *flags |= DNS_VERIFY_MATCH; 283124208Sdes } 284255767Sdes free(dnskey_digest); 285124208Sdes } 286124208Sdes 287255767Sdes free(hostkey_digest); /* from key_fingerprint_raw() */ 288124208Sdes freerrset(fingerprints); 289124208Sdes 290126274Sdes if (*flags & DNS_VERIFY_FOUND) 291126274Sdes if (*flags & DNS_VERIFY_MATCH) 292126274Sdes debug("matching host key fingerprint found in DNS"); 293126274Sdes else 294126274Sdes debug("mismatching host key fingerprint found in DNS"); 295126274Sdes else 296126274Sdes debug("no host key fingerprint found in DNS"); 297124208Sdes 298126274Sdes return 0; 299124208Sdes} 300124208Sdes 301124208Sdes/* 302124208Sdes * Export the fingerprint of a key as a DNS resource record 303124208Sdes */ 304124208Sdesint 305204917Sdesexport_dns_rr(const char *hostname, Key *key, FILE *f, int generic) 306124208Sdes{ 307124208Sdes u_int8_t rdata_pubkey_algorithm = 0; 308240075Sdes u_int8_t rdata_digest_type = SSHFP_HASH_RESERVED; 309240075Sdes u_int8_t dtype; 310124208Sdes u_char *rdata_digest; 311240075Sdes u_int i, rdata_digest_len; 312124208Sdes int success = 0; 313124208Sdes 314240075Sdes for (dtype = SSHFP_HASH_SHA1; dtype < SSHFP_HASH_MAX; dtype++) { 315240075Sdes rdata_digest_type = dtype; 316240075Sdes if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, 317240075Sdes &rdata_digest, &rdata_digest_len, key)) { 318240075Sdes if (generic) { 319240075Sdes fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", 320240075Sdes hostname, DNS_RDATATYPE_SSHFP, 321240075Sdes 2 + rdata_digest_len, 322240075Sdes rdata_pubkey_algorithm, rdata_digest_type); 323240075Sdes } else { 324240075Sdes fprintf(f, "%s IN SSHFP %d %d ", hostname, 325240075Sdes rdata_pubkey_algorithm, rdata_digest_type); 326240075Sdes } 327240075Sdes for (i = 0; i < rdata_digest_len; i++) 328240075Sdes fprintf(f, "%02x", rdata_digest[i]); 329240075Sdes fprintf(f, "\n"); 330255767Sdes free(rdata_digest); /* from key_fingerprint_raw() */ 331240075Sdes success = 1; 332240075Sdes } 333240075Sdes } 334124208Sdes 335240075Sdes /* No SSHFP record was generated at all */ 336240075Sdes if (success == 0) { 337240075Sdes error("%s: unsupported algorithm and/or digest_type", __func__); 338124208Sdes } 339124208Sdes 340124208Sdes return success; 341124208Sdes} 342