Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: dns.c,v 1.27 2010/08/31 11:54:45 djm Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
      5  * Copyright (c) 2003 Jakob Schlyter. All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "includes.h"
     29 
     30 #include <sys/types.h>
     31 #include <sys/socket.h>
     32 
     33 #include <netdb.h>
     34 #include <stdarg.h>
     35 #include <stdio.h>
     36 #include <string.h>
     37 
     38 #include "xmalloc.h"
     39 #include "key.h"
     40 #include "dns.h"
     41 #include "log.h"
     42 
     43 static const char *errset_text[] = {
     44 	"success",		/* 0 ERRSET_SUCCESS */
     45 	"out of memory",	/* 1 ERRSET_NOMEMORY */
     46 	"general failure",	/* 2 ERRSET_FAIL */
     47 	"invalid parameter",	/* 3 ERRSET_INVAL */
     48 	"name does not exist",	/* 4 ERRSET_NONAME */
     49 	"data does not exist",	/* 5 ERRSET_NODATA */
     50 };
     51 
     52 static const char *
     53 dns_result_totext(unsigned int res)
     54 {
     55 	switch (res) {
     56 	case ERRSET_SUCCESS:
     57 		return errset_text[ERRSET_SUCCESS];
     58 	case ERRSET_NOMEMORY:
     59 		return errset_text[ERRSET_NOMEMORY];
     60 	case ERRSET_FAIL:
     61 		return errset_text[ERRSET_FAIL];
     62 	case ERRSET_INVAL:
     63 		return errset_text[ERRSET_INVAL];
     64 	case ERRSET_NONAME:
     65 		return errset_text[ERRSET_NONAME];
     66 	case ERRSET_NODATA:
     67 		return errset_text[ERRSET_NODATA];
     68 	default:
     69 		return "unknown error";
     70 	}
     71 }
     72 
     73 /*
     74  * Read SSHFP parameters from key buffer.
     75  */
     76 static int
     77 dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
     78     u_char **digest, u_int *digest_len, Key *key)
     79 {
     80 	int success = 0;
     81 
     82 	switch (key->type) {
     83 	case KEY_RSA:
     84 		*algorithm = SSHFP_KEY_RSA;
     85 		break;
     86 	case KEY_DSA:
     87 		*algorithm = SSHFP_KEY_DSA;
     88 		break;
     89 	/* XXX KEY_ECDSA */
     90 	default:
     91 		*algorithm = SSHFP_KEY_RESERVED; /* 0 */
     92 	}
     93 
     94 	if (*algorithm) {
     95 		*digest_type = SSHFP_HASH_SHA1;
     96 		*digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len);
     97 		if (*digest == NULL)
     98 			fatal("dns_read_key: null from key_fingerprint_raw()");
     99 		success = 1;
    100 	} else {
    101 		*digest_type = SSHFP_HASH_RESERVED;
    102 		*digest = NULL;
    103 		*digest_len = 0;
    104 		success = 0;
    105 	}
    106 
    107 	return success;
    108 }
    109 
    110 /*
    111  * Read SSHFP parameters from rdata buffer.
    112  */
    113 static int
    114 dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type,
    115     u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len)
    116 {
    117 	int success = 0;
    118 
    119 	*algorithm = SSHFP_KEY_RESERVED;
    120 	*digest_type = SSHFP_HASH_RESERVED;
    121 
    122 	if (rdata_len >= 2) {
    123 		*algorithm = rdata[0];
    124 		*digest_type = rdata[1];
    125 		*digest_len = rdata_len - 2;
    126 
    127 		if (*digest_len > 0) {
    128 			*digest = (u_char *) xmalloc(*digest_len);
    129 			memcpy(*digest, rdata + 2, *digest_len);
    130 		} else {
    131 			*digest = (u_char *)xstrdup("");
    132 		}
    133 
    134 		success = 1;
    135 	}
    136 
    137 	return success;
    138 }
    139 
    140 /*
    141  * Check if hostname is numerical.
    142  * Returns -1 if hostname is numeric, 0 otherwise
    143  */
    144 static int
    145 is_numeric_hostname(const char *hostname)
    146 {
    147 	struct addrinfo hints, *ai;
    148 
    149 	/*
    150 	 * We shouldn't ever get a null host but if we do then log an error
    151 	 * and return -1 which stops DNS key fingerprint processing.
    152 	 */
    153 	if (hostname == NULL) {
    154 		error("is_numeric_hostname called with NULL hostname");
    155 		return -1;
    156 	}
    157 
    158 	memset(&hints, 0, sizeof(hints));
    159 	hints.ai_socktype = SOCK_DGRAM;
    160 	hints.ai_flags = AI_NUMERICHOST;
    161 
    162 	if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) {
    163 		freeaddrinfo(ai);
    164 		return -1;
    165 	}
    166 
    167 	return 0;
    168 }
    169 
    170 /*
    171  * Verify the given hostname, address and host key using DNS.
    172  * Returns 0 if lookup succeeds, -1 otherwise
    173  */
    174 int
    175 verify_host_key_dns(const char *hostname, struct sockaddr *address,
    176     Key *hostkey, int *flags)
    177 {
    178 	u_int counter;
    179 	int result;
    180 	struct rrsetinfo *fingerprints = NULL;
    181 
    182 	u_int8_t hostkey_algorithm;
    183 	u_int8_t hostkey_digest_type;
    184 	u_char *hostkey_digest;
    185 	u_int hostkey_digest_len;
    186 
    187 	u_int8_t dnskey_algorithm;
    188 	u_int8_t dnskey_digest_type;
    189 	u_char *dnskey_digest;
    190 	u_int dnskey_digest_len;
    191 
    192 	*flags = 0;
    193 
    194 	debug3("verify_host_key_dns");
    195 	if (hostkey == NULL)
    196 		fatal("No key to look up!");
    197 
    198 	if (is_numeric_hostname(hostname)) {
    199 		debug("skipped DNS lookup for numerical hostname");
    200 		return -1;
    201 	}
    202 
    203 #ifndef ANDROID
    204 	result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
    205 	    DNS_RDATATYPE_SSHFP, 0, &fingerprints);
    206 #else
    207 	/* unsupported in android */
    208 	result = -1;
    209 #endif
    210 	if (result) {
    211 		verbose("DNS lookup error: %s", dns_result_totext(result));
    212 		return -1;
    213 	}
    214 
    215 	if (fingerprints->rri_flags & RRSET_VALIDATED) {
    216 		*flags |= DNS_VERIFY_SECURE;
    217 		debug("found %d secure fingerprints in DNS",
    218 		    fingerprints->rri_nrdatas);
    219 	} else {
    220 		debug("found %d insecure fingerprints in DNS",
    221 		    fingerprints->rri_nrdatas);
    222 	}
    223 
    224 	/* Initialize host key parameters */
    225 	if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
    226 	    &hostkey_digest, &hostkey_digest_len, hostkey)) {
    227 		error("Error calculating host key fingerprint.");
    228 #ifndef ANDROID
    229 		freerrset(fingerprints);
    230 #endif
    231 		return -1;
    232 	}
    233 
    234 	if (fingerprints->rri_nrdatas)
    235 		*flags |= DNS_VERIFY_FOUND;
    236 
    237 	for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) {
    238 		/*
    239 		 * Extract the key from the answer. Ignore any badly
    240 		 * formatted fingerprints.
    241 		 */
    242 		if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type,
    243 		    &dnskey_digest, &dnskey_digest_len,
    244 		    fingerprints->rri_rdatas[counter].rdi_data,
    245 		    fingerprints->rri_rdatas[counter].rdi_length)) {
    246 			verbose("Error parsing fingerprint from DNS.");
    247 			continue;
    248 		}
    249 
    250 		/* Check if the current key is the same as the given key */
    251 		if (hostkey_algorithm == dnskey_algorithm &&
    252 		    hostkey_digest_type == dnskey_digest_type) {
    253 
    254 			if (hostkey_digest_len == dnskey_digest_len &&
    255 			    memcmp(hostkey_digest, dnskey_digest,
    256 			    hostkey_digest_len) == 0) {
    257 
    258 				*flags |= DNS_VERIFY_MATCH;
    259 			}
    260 		}
    261 		xfree(dnskey_digest);
    262 	}
    263 
    264 	xfree(hostkey_digest); /* from key_fingerprint_raw() */
    265 #ifndef ANDROID
    266 	freerrset(fingerprints);
    267 #endif
    268 
    269 	if (*flags & DNS_VERIFY_FOUND)
    270 		if (*flags & DNS_VERIFY_MATCH)
    271 			debug("matching host key fingerprint found in DNS");
    272 		else
    273 			debug("mismatching host key fingerprint found in DNS");
    274 	else
    275 		debug("no host key fingerprint found in DNS");
    276 
    277 	return 0;
    278 }
    279 
    280 /*
    281  * Export the fingerprint of a key as a DNS resource record
    282  */
    283 int
    284 export_dns_rr(const char *hostname, Key *key, FILE *f, int generic)
    285 {
    286 	u_int8_t rdata_pubkey_algorithm = 0;
    287 	u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
    288 	u_char *rdata_digest;
    289 	u_int rdata_digest_len;
    290 
    291 	u_int i;
    292 	int success = 0;
    293 
    294 	if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type,
    295 	    &rdata_digest, &rdata_digest_len, key)) {
    296 
    297 		if (generic)
    298 			fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname,
    299 			    DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len,
    300 			    rdata_pubkey_algorithm, rdata_digest_type);
    301 		else
    302 			fprintf(f, "%s IN SSHFP %d %d ", hostname,
    303 			    rdata_pubkey_algorithm, rdata_digest_type);
    304 
    305 		for (i = 0; i < rdata_digest_len; i++)
    306 			fprintf(f, "%02x", rdata_digest[i]);
    307 		fprintf(f, "\n");
    308 		xfree(rdata_digest); /* from key_fingerprint_raw() */
    309 		success = 1;
    310 	} else {
    311 		error("export_dns_rr: unsupported algorithm");
    312 	}
    313 
    314 	return success;
    315 }
    316