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