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