dirmngr: Improve DNS code to retrieve arbitrary records.
[gnupg.git] / dirmngr / dns-cert.c
1 /* dns-cert.c - DNS CERT code (rfc-4398)
2  * Copyright (C) 2005, 2006, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <http://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <sys/types.h>
32 #ifdef USE_DNS_CERT
33 # ifdef HAVE_W32_SYSTEM
34 #  ifdef HAVE_WINSOCK2_H
35 #   include <winsock2.h>
36 #  endif
37 #  include <windows.h>
38 # else
39 #  include <netinet/in.h>
40 #  include <arpa/nameser.h>
41 #  include <resolv.h>
42 # endif
43 # include <string.h>
44 #endif
45 #ifdef USE_ADNS
46 # include <adns.h>
47 #endif
48
49 #include "util.h"
50 #include "host2net.h"
51 #include "dns-cert.h"
52
53 /* Not every installation has gotten around to supporting CERTs
54    yet... */
55 #ifndef T_CERT
56 # define T_CERT 37
57 #endif
58
59 /* ADNS has no support for CERT yet. */
60 #define my_adns_r_cert 37
61
62
63
64 /* Returns 0 on success or an error code.  If a PGP CERT record was
65    found, the malloced data is returned at (R_KEY, R_KEYLEN) and
66    the other return parameters are set to NULL/0.  If an IPGP CERT
67    record was found the fingerprint is stored as an allocated block at
68    R_FPR and its length at R_FPRLEN; an URL is is allocated as a
69    string and returned at R_URL.  If WANT_CERTTYPE is 0 this function
70    returns the first CERT found with a supported type; it is expected
71    that only one CERT record is used.  If WANT_CERTTYPE is one of the
72    supported certtypes only records with this certtype are considered
73    and the first found is returned.  (R_KEY,R_KEYLEN) are optional. */
74 gpg_error_t
75 get_dns_cert (const char *name, int want_certtype,
76               void **r_key, size_t *r_keylen,
77               unsigned char **r_fpr, size_t *r_fprlen, char **r_url)
78 {
79 #ifdef USE_DNS_CERT
80 #ifdef USE_ADNS
81   gpg_error_t err;
82   adns_state state;
83   adns_answer *answer = NULL;
84   unsigned int ctype;
85   int count;
86
87   if (r_key)
88     *r_key = NULL;
89   if (r_keylen)
90     *r_keylen = 0;
91   *r_fpr = NULL;
92   *r_fprlen = 0;
93   *r_url = NULL;
94
95   if (adns_init (&state, adns_if_noerrprint, NULL))
96     {
97       err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
98       log_error ("error initializing adns: %s\n", strerror (errno));
99       return err;
100     }
101
102   if (adns_synchronous (state, name,
103                         (adns_r_unknown
104                          | (want_certtype < DNS_CERTTYPE_RRBASE
105                             ? my_adns_r_cert
106                             : (want_certtype - DNS_CERTTYPE_RRBASE))),
107                         adns_qf_quoteok_query, &answer))
108     {
109       err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
110       /* log_error ("DNS query failed: %s\n", strerror (errno)); */
111       adns_finish (state);
112       return err;
113     }
114   if (answer->status != adns_s_ok)
115     {
116       /* log_error ("DNS query returned an error: %s (%s)\n", */
117       /*            adns_strerror (answer->status), */
118       /*            adns_errabbrev (answer->status)); */
119       err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND);
120       goto leave;
121     }
122
123   err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND);
124   for (count = 0; count < answer->nrrs; count++)
125     {
126       int datalen = answer->rrs.byteblock[count].len;
127       const unsigned char *data = answer->rrs.byteblock[count].data;
128
129       /* First check for our generic RR hack.  */
130       if (datalen
131           && want_certtype >= DNS_CERTTYPE_RRBASE
132           && ((want_certtype - DNS_CERTTYPE_RRBASE)
133               == (answer->type & ~adns_r_unknown)))
134         {
135           /* Found the requested record - return it.  */
136           *r_key = xtrymalloc (datalen);
137           if (!*r_key)
138             err = gpg_err_make (default_errsource,
139                                 gpg_err_code_from_syserror ());
140           else
141             {
142               memcpy (*r_key, data, datalen);
143               *r_keylen = datalen;
144               err = 0;
145             }
146           goto leave;
147         }
148
149       if (datalen < 5)
150         continue;  /* Truncated CERT record - skip.  */
151
152       ctype = buf16_to_uint (data);
153       /* (key tag and algorithm fields are not required.) */
154       data += 5;
155       datalen -= 5;
156
157       if (want_certtype && want_certtype != ctype)
158         ; /* Not of the requested certtype.  */
159       else if (ctype == DNS_CERTTYPE_PGP && datalen >= 11 && r_key && r_keylen)
160         {
161           /* CERT type is PGP.  Gpg checks for a minimum length of 11,
162              thus we do the same.  */
163           *r_key = xtrymalloc (datalen);
164           if (!*r_key)
165             err = gpg_err_make (default_errsource,
166                                 gpg_err_code_from_syserror ());
167           else
168             {
169               memcpy (*r_key, data, datalen);
170               *r_keylen = datalen;
171               err = 0;
172             }
173           goto leave;
174         }
175       else if (ctype == DNS_CERTTYPE_IPGP && datalen && datalen < 1023
176                && datalen >= data[0] + 1 && r_fpr && r_fprlen && r_url)
177         {
178           /* CERT type is IPGP.  We made sure that the data is
179              plausible and that the caller requested this
180              information.  */
181           *r_fprlen = data[0];
182           if (*r_fprlen)
183             {
184               *r_fpr = xtrymalloc (*r_fprlen);
185               if (!*r_fpr)
186                 {
187                   err = gpg_err_make (default_errsource,
188                                       gpg_err_code_from_syserror ());
189                   goto leave;
190                 }
191               memcpy (*r_fpr, data + 1, *r_fprlen);
192             }
193           else
194             *r_fpr = NULL;
195
196           if (datalen > *r_fprlen + 1)
197             {
198               *r_url = xtrymalloc (datalen - (*r_fprlen + 1) + 1);
199               if (!*r_url)
200                 {
201                   err = gpg_err_make (default_errsource,
202                                       gpg_err_code_from_syserror ());
203                   xfree (*r_fpr);
204                   *r_fpr = NULL;
205                   goto leave;
206                 }
207               memcpy (*r_url,
208                       data + (*r_fprlen + 1), datalen - (*r_fprlen + 1));
209               (*r_url)[datalen - (*r_fprlen + 1)] = '\0';
210             }
211           else
212             *r_url = NULL;
213
214           err = 0;
215           goto leave;
216         }
217     }
218
219  leave:
220   adns_free (answer);
221   adns_finish (state);
222   return err;
223
224 #else /*!USE_ADNS*/
225
226   gpg_error_t err;
227   unsigned char *answer;
228   int r;
229   u16 count;
230
231   if (r_key)
232     *r_key = NULL;
233   if (r_keylen)
234     *r_keylen = 0;
235   *r_fpr = NULL;
236   *r_fprlen = 0;
237   *r_url = NULL;
238
239   /* Allocate a 64k buffer which is the limit for an DNS response.  */
240   answer = xtrymalloc (65536);
241   if (!answer)
242     return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
243
244   err = gpg_err_make (default_errsource, GPG_ERR_NOT_FOUND);
245
246   r = res_query (name, C_IN,
247                  (want_certtype < DNS_CERTTYPE_RRBASE
248                   ? T_CERT
249                   : (want_certtype - DNS_CERTTYPE_RRBASE)),
250                  answer, 65536);
251   /* Not too big, not too small, no errors and at least 1 answer. */
252   if (r >= sizeof (HEADER) && r <= 65536
253       && (((HEADER *) answer)->rcode) == NOERROR
254       && (count = ntohs (((HEADER *) answer)->ancount)))
255     {
256       int rc;
257       unsigned char *pt, *emsg;
258
259       emsg = &answer[r];
260
261       pt = &answer[sizeof (HEADER)];
262
263       /* Skip over the query */
264
265       rc = dn_skipname (pt, emsg);
266       if (rc == -1)
267         {
268           err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ);
269           goto leave;
270         }
271       pt += rc + QFIXEDSZ;
272
273       /* There are several possible response types for a CERT request.
274          We're interested in the PGP (a key) and IPGP (a URI) types.
275          Skip all others.  TODO: A key is better than a URI since
276          we've gone through all this bother to fetch it, so favor that
277          if we have both PGP and IPGP? */
278
279       while (count-- > 0 && pt < emsg)
280         {
281           u16 type, class, dlen, ctype;
282
283           rc = dn_skipname (pt, emsg);  /* the name we just queried for */
284           if (rc == -1)
285             {
286               err = gpg_err_make (default_errsource, GPG_ERR_INV_OBJ);
287               goto leave;
288             }
289
290           pt += rc;
291
292           /* Truncated message? 15 bytes takes us to the point where
293              we start looking at the ctype. */
294           if ((emsg - pt) < 15)
295             break;
296
297           type = buf16_to_u16 (pt);
298           pt += 2;
299
300           class = buf16_to_u16 (pt);
301           pt += 2;
302
303           if (class != C_IN)
304             break;
305
306           /* ttl */
307           pt += 4;
308
309           /* data length */
310           dlen = buf16_to_u16 (pt);
311           pt += 2;
312
313           /* Check the type and parse.  */
314           if (want_certtype >= DNS_CERTTYPE_RRBASE
315               && type == (want_certtype - DNS_CERTTYPE_RRBASE)
316               && r_key)
317             {
318               *r_key = xtrymalloc (dlen);
319               if (!*r_key)
320                 err = gpg_err_make (default_errsource,
321                                     gpg_err_code_from_syserror ());
322               else
323                 {
324                   memcpy (*r_key, pt, dlen);
325                   *r_keylen = dlen;
326                   err = 0;
327                 }
328               goto leave;
329             }
330           else if (want_certtype >= DNS_CERTTYPE_RRBASE)
331             {
332               /* We did not found the requested RR.  */
333               pt += dlen;
334             }
335           else if (type == T_CERT)
336             {
337               /* We got a CERT type.   */
338               ctype = buf16_to_u16 (pt);
339               pt += 2;
340
341               /* Skip the CERT key tag and algo which we don't need. */
342               pt += 3;
343
344               dlen -= 5;
345
346               /* 15 bytes takes us to here */
347               if (want_certtype && want_certtype != ctype)
348                 ; /* Not of the requested certtype.  */
349               else if (ctype == DNS_CERTTYPE_PGP && dlen && r_key && r_keylen)
350                 {
351                   /* PGP type */
352                   *r_key = xtrymalloc (dlen);
353                   if (!*r_key)
354                     err = gpg_err_make (default_errsource,
355                                         gpg_err_code_from_syserror ());
356                   else
357                     {
358                       memcpy (*r_key, pt, dlen);
359                       *r_keylen = dlen;
360                       err = 0;
361                     }
362                   goto leave;
363                 }
364               else if (ctype == DNS_CERTTYPE_IPGP
365                        && dlen && dlen < 1023 && dlen >= pt[0] + 1)
366                 {
367                   /* IPGP type */
368                   *r_fprlen = pt[0];
369                   if (*r_fprlen)
370                     {
371                       *r_fpr = xtrymalloc (*r_fprlen);
372                       if (!*r_fpr)
373                         {
374                           err = gpg_err_make (default_errsource,
375                                               gpg_err_code_from_syserror ());
376                           goto leave;
377                         }
378                       memcpy (*r_fpr, &pt[1], *r_fprlen);
379                     }
380                   else
381                     *r_fpr = NULL;
382
383                   if (dlen > *r_fprlen + 1)
384                     {
385                       *r_url = xtrymalloc (dlen - (*r_fprlen + 1) + 1);
386                       if (!*r_fpr)
387                         {
388                           err = gpg_err_make (default_errsource,
389                                               gpg_err_code_from_syserror ());
390                           xfree (*r_fpr);
391                           *r_fpr = NULL;
392                           goto leave;
393                         }
394                       memcpy (*r_url, &pt[*r_fprlen + 1],
395                               dlen - (*r_fprlen + 1));
396                       (*r_url)[dlen - (*r_fprlen + 1)] = '\0';
397                     }
398                   else
399                     *r_url = NULL;
400
401                   err = 0;
402                   goto leave;
403                 }
404
405               /* No subtype matches, so continue with the next answer. */
406               pt += dlen;
407             }
408           else
409             {
410               /* Not a requested type - might be a CNAME. Try next item.  */
411               pt += dlen;
412             }
413         }
414     }
415
416  leave:
417   xfree (answer);
418   return err;
419
420 #endif /*!USE_ADNS */
421 #else /* !USE_DNS_CERT */
422   (void)name;
423   if (r_key)
424     *r_key = NULL;
425   if (r_keylen)
426     *r_keylen = NULL;
427   *r_fpr = NULL;
428   *r_fprlen = 0;
429   *r_url = NULL;
430
431   return gpg_err_make (default_errsource, GPG_ERR_NOT_SUPPORTED);
432 #endif
433 }