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