xref: /freebsd/crypto/openssh/dns.c (revision d056fa046c6a91b90cd98165face0e42a33a5173)
1 /*	$OpenBSD: dns.c,v 1.16 2005/10/17 14:13:35 stevesk 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 RCSID("$OpenBSD: dns.c,v 1.16 2005/10/17 14:13:35 stevesk Exp $");
30 
31 #include <netdb.h>
32 
33 #include "xmalloc.h"
34 #include "key.h"
35 #include "dns.h"
36 #include "log.h"
37 
38 static const char *errset_text[] = {
39 	"success",		/* 0 ERRSET_SUCCESS */
40 	"out of memory",	/* 1 ERRSET_NOMEMORY */
41 	"general failure",	/* 2 ERRSET_FAIL */
42 	"invalid parameter",	/* 3 ERRSET_INVAL */
43 	"name does not exist",	/* 4 ERRSET_NONAME */
44 	"data does not exist",	/* 5 ERRSET_NODATA */
45 };
46 
47 static const char *
48 dns_result_totext(unsigned int res)
49 {
50 	switch (res) {
51 	case ERRSET_SUCCESS:
52 		return errset_text[ERRSET_SUCCESS];
53 	case ERRSET_NOMEMORY:
54 		return errset_text[ERRSET_NOMEMORY];
55 	case ERRSET_FAIL:
56 		return errset_text[ERRSET_FAIL];
57 	case ERRSET_INVAL:
58 		return errset_text[ERRSET_INVAL];
59 	case ERRSET_NONAME:
60 		return errset_text[ERRSET_NONAME];
61 	case ERRSET_NODATA:
62 		return errset_text[ERRSET_NODATA];
63 	default:
64 		return "unknown error";
65 	}
66 }
67 
68 /*
69  * Read SSHFP parameters from key buffer.
70  */
71 static int
72 dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
73     u_char **digest, u_int *digest_len, const Key *key)
74 {
75 	int success = 0;
76 
77 	switch (key->type) {
78 	case KEY_RSA:
79 		*algorithm = SSHFP_KEY_RSA;
80 		break;
81 	case KEY_DSA:
82 		*algorithm = SSHFP_KEY_DSA;
83 		break;
84 	default:
85 		*algorithm = SSHFP_KEY_RESERVED; /* 0 */
86 	}
87 
88 	if (*algorithm) {
89 		*digest_type = SSHFP_HASH_SHA1;
90 		*digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len);
91 		if (*digest == NULL)
92 			fatal("dns_read_key: null from key_fingerprint_raw()");
93 		success = 1;
94 	} else {
95 		*digest_type = SSHFP_HASH_RESERVED;
96 		*digest = NULL;
97 		*digest_len = 0;
98 		success = 0;
99 	}
100 
101 	return success;
102 }
103 
104 /*
105  * Read SSHFP parameters from rdata buffer.
106  */
107 static int
108 dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type,
109     u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len)
110 {
111 	int success = 0;
112 
113 	*algorithm = SSHFP_KEY_RESERVED;
114 	*digest_type = SSHFP_HASH_RESERVED;
115 
116 	if (rdata_len >= 2) {
117 		*algorithm = rdata[0];
118 		*digest_type = rdata[1];
119 		*digest_len = rdata_len - 2;
120 
121 		if (*digest_len > 0) {
122 			*digest = (u_char *) xmalloc(*digest_len);
123 			memcpy(*digest, rdata + 2, *digest_len);
124 		} else {
125 			*digest = xstrdup("");
126 		}
127 
128 		success = 1;
129 	}
130 
131 	return success;
132 }
133 
134 /*
135  * Check if hostname is numerical.
136  * Returns -1 if hostname is numeric, 0 otherwise
137  */
138 static int
139 is_numeric_hostname(const char *hostname)
140 {
141 	struct addrinfo hints, *ai;
142 
143 	memset(&hints, 0, sizeof(hints));
144 	hints.ai_socktype = SOCK_DGRAM;
145 	hints.ai_flags = AI_NUMERICHOST;
146 
147 	if (getaddrinfo(hostname, "0", &hints, &ai) == 0) {
148 		freeaddrinfo(ai);
149 		return -1;
150 	}
151 
152 	return 0;
153 }
154 
155 /*
156  * Verify the given hostname, address and host key using DNS.
157  * Returns 0 if lookup succeeds, -1 otherwise
158  */
159 int
160 verify_host_key_dns(const char *hostname, struct sockaddr *address,
161     const Key *hostkey, int *flags)
162 {
163 	u_int counter;
164 	int result;
165 	struct rrsetinfo *fingerprints = NULL;
166 
167 	u_int8_t hostkey_algorithm;
168 	u_int8_t hostkey_digest_type;
169 	u_char *hostkey_digest;
170 	u_int hostkey_digest_len;
171 
172 	u_int8_t dnskey_algorithm;
173 	u_int8_t dnskey_digest_type;
174 	u_char *dnskey_digest;
175 	u_int dnskey_digest_len;
176 
177 	*flags = 0;
178 
179 	debug3("verify_host_key_dns");
180 	if (hostkey == NULL)
181 		fatal("No key to look up!");
182 
183 	if (is_numeric_hostname(hostname)) {
184 		debug("skipped DNS lookup for numerical hostname");
185 		return -1;
186 	}
187 
188 	result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
189 	    DNS_RDATATYPE_SSHFP, 0, &fingerprints);
190 	if (result) {
191 		verbose("DNS lookup error: %s", dns_result_totext(result));
192 		return -1;
193 	}
194 
195 	if (fingerprints->rri_flags & RRSET_VALIDATED) {
196 		*flags |= DNS_VERIFY_SECURE;
197 		debug("found %d secure fingerprints in DNS",
198 		    fingerprints->rri_nrdatas);
199 	} else {
200 		debug("found %d insecure fingerprints in DNS",
201 		    fingerprints->rri_nrdatas);
202 	}
203 
204 	/* Initialize host key parameters */
205 	if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
206 	    &hostkey_digest, &hostkey_digest_len, hostkey)) {
207 		error("Error calculating host key fingerprint.");
208 		freerrset(fingerprints);
209 		return -1;
210 	}
211 
212 	if (fingerprints->rri_nrdatas)
213 		*flags |= DNS_VERIFY_FOUND;
214 
215 	for (counter = 0; counter < fingerprints->rri_nrdatas; counter++)  {
216 		/*
217 		 * Extract the key from the answer. Ignore any badly
218 		 * formatted fingerprints.
219 		 */
220 		if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type,
221 		    &dnskey_digest, &dnskey_digest_len,
222 		    fingerprints->rri_rdatas[counter].rdi_data,
223 		    fingerprints->rri_rdatas[counter].rdi_length)) {
224 			verbose("Error parsing fingerprint from DNS.");
225 			continue;
226 		}
227 
228 		/* Check if the current key is the same as the given key */
229 		if (hostkey_algorithm == dnskey_algorithm &&
230 		    hostkey_digest_type == dnskey_digest_type) {
231 
232 			if (hostkey_digest_len == dnskey_digest_len &&
233 			    memcmp(hostkey_digest, dnskey_digest,
234 			    hostkey_digest_len) == 0) {
235 
236 				*flags |= DNS_VERIFY_MATCH;
237 			}
238 		}
239 		xfree(dnskey_digest);
240 	}
241 
242 	xfree(hostkey_digest); /* from key_fingerprint_raw() */
243 	freerrset(fingerprints);
244 
245 	if (*flags & DNS_VERIFY_FOUND)
246 		if (*flags & DNS_VERIFY_MATCH)
247 			debug("matching host key fingerprint found in DNS");
248 		else
249 			debug("mismatching host key fingerprint found in DNS");
250 	else
251 		debug("no host key fingerprint found in DNS");
252 
253 	return 0;
254 }
255 
256 /*
257  * Export the fingerprint of a key as a DNS resource record
258  */
259 int
260 export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic)
261 {
262 	u_int8_t rdata_pubkey_algorithm = 0;
263 	u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
264 	u_char *rdata_digest;
265 	u_int rdata_digest_len;
266 
267 	u_int i;
268 	int success = 0;
269 
270 	if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type,
271 	    &rdata_digest, &rdata_digest_len, key)) {
272 
273 		if (generic)
274 			fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname,
275 			    DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len,
276 			    rdata_pubkey_algorithm, rdata_digest_type);
277 		else
278 			fprintf(f, "%s IN SSHFP %d %d ", hostname,
279 			    rdata_pubkey_algorithm, rdata_digest_type);
280 
281 		for (i = 0; i < rdata_digest_len; i++)
282 			fprintf(f, "%02x", rdata_digest[i]);
283 		fprintf(f, "\n");
284 		xfree(rdata_digest); /* from key_fingerprint_raw() */
285 		success = 1;
286 	} else {
287 		error("export_dns_rr: unsupported algorithm");
288 	}
289 
290 	return success;
291 }
292