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