Fixed set but unused variable bugs
[gnupg.git] / common / pka.c
1 /* pka.c - DNS Public Key Association RR access
2  * Copyright (C) 2005, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifdef USE_DNS_PKA
27 #include <sys/types.h>
28 #ifdef _WIN32
29 #include <windows.h>
30 #else
31 #include <netinet/in.h>
32 #include <arpa/nameser.h>
33 #include <resolv.h>
34 #endif
35 #endif /* USE_DNS_PKA */
36 #ifdef USE_ADNS
37 # include <adns.h>
38 # ifndef HAVE_ADNS_FREE
39 #  define adns_free free
40 # endif
41 #endif
42
43 #include "util.h"
44 #include "pka.h"
45
46 #ifdef USE_DNS_PKA
47 /* Parse the TXT resource record. Format is:
48
49    v=pka1;fpr=a4d94e92b0986ab5ee9dcd755de249965b0358a2;uri=string
50
51    For simplicity white spaces are not allowed.  Because we expect to
52    use a new RRTYPE for this in the future we define the TXT really
53    strict for simplicity: No white spaces, case sensitivity of the
54    names, order must be as given above.  Only URI is optional.
55
56    This function modifies BUFFER.  On success 0 is returned, the 20
57    byte fingerprint stored at FPR and BUFFER contains the URI or an
58    empty string.
59 */
60 static int
61 parse_txt_record (char *buffer, unsigned char *fpr)
62 {
63   char *p, *pend;
64   int i;
65
66   p = buffer;
67   pend = strchr (p, ';');
68   if (!pend)
69     return -1;
70   *pend++ = 0;
71   if (strcmp (p, "v=pka1"))
72     return -1; /* Wrong or missing version. */
73
74   p = pend;
75   pend = strchr (p, ';');
76   if (pend)
77     *pend++ = 0;
78   if (strncmp (p, "fpr=", 4))
79     return -1; /* Missing fingerprint part. */
80   p += 4;
81   for (i=0; i < 20 && hexdigitp (p) && hexdigitp (p+1); i++, p += 2)
82     fpr[i] = xtoi_2 (p);
83   if (i != 20)
84     return -1; /* Fingerprint consists not of exactly 40 hexbytes. */
85
86   p = pend;
87   if (!p || !*p)
88     {
89       *buffer = 0;
90       return 0; /* Success (no URI given). */
91     }
92   if (strncmp (p, "uri=", 4))
93     return -1; /* Unknown part. */
94   p += 4;
95   /* There is an URI, copy it to the start of the buffer. */
96   while (*p)
97     *buffer++ = *p++;
98   *buffer = 0;
99   return 0;
100 }
101
102
103 /* For the given email ADDRESS lookup the PKA information in the DNS.
104
105    On success the 20 byte SHA-1 fingerprint is stored at FPR and the
106    URI will be returned in an allocated buffer.  Note that the URI
107    might be an zero length string as this information is optional.
108    Caller must xfree the returned string.
109
110    On error NULL is returned and the 20 bytes at FPR are not
111    defined. */
112 char *
113 get_pka_info (const char *address, unsigned char *fpr)
114 {
115 #ifdef USE_ADNS
116   int rc;
117   adns_state state;
118   const char *domain;
119   char *name;
120   adns_answer *answer = NULL;
121   char *buffer = NULL;
122
123   domain = strrchr (address, '@');
124   if (!domain || domain == address || !domain[1])
125     return NULL; /* Invalid mail address given.  */
126   name = xtrymalloc (strlen (address) + 5 + 1);
127   if (!name)
128     return NULL;
129   memcpy (name, address, domain - address);
130   strcpy (stpcpy (name + (domain-address), "._pka."), domain+1);
131
132   rc = adns_init (&state, adns_if_noerrprint, NULL);
133   if (rc)
134     {
135       log_error ("error initializing adns: %s\n", strerror (errno));
136       xfree (name);
137       return NULL;
138     }
139
140   rc = adns_synchronous (state, name, adns_r_txt, adns_qf_quoteok_query,
141                          &answer);
142   xfree (name);
143   if (rc)
144     {
145       log_error ("DNS query failed: %s\n", strerror (errno));
146       adns_finish (state);
147       return NULL;
148     }
149   if (answer->status != adns_s_ok
150       || answer->type != adns_r_txt || !answer->nrrs)
151     {
152       log_error ("DNS query returned an error: %s (%s)\n",
153                  adns_strerror (answer->status),
154                  adns_errabbrev (answer->status));
155       adns_free (answer);
156       adns_finish (state);
157       return NULL;
158     }
159
160   /* We use a PKA records iff there is exactly one record.  */
161   if (answer->nrrs == 1 && answer->rrs.manyistr[0]->i != -1)
162     {
163       buffer = xtrystrdup (answer->rrs.manyistr[0]->str);
164       if (parse_txt_record (buffer, fpr))
165         {
166           xfree (buffer);
167           buffer = NULL;   /* Not a valid gpg trustdns RR. */
168         }
169     }
170
171   adns_free (answer);
172   adns_finish (state);
173   return buffer;
174
175 #else /*!USE_ADNS*/
176   unsigned char answer[PACKETSZ];
177   int anslen;
178   int qdcount, ancount;
179   int rc;
180   unsigned char *p, *pend;
181   const char *domain;
182   char *name;
183   HEADER header;
184
185   domain = strrchr (address, '@');
186   if (!domain || domain == address || !domain[1])
187     return NULL; /* invalid mail address given. */
188
189   name = xtrymalloc (strlen (address) + 5 + 1);
190   if (!name)
191     return NULL;
192   memcpy (name, address, domain - address);
193   strcpy (stpcpy (name + (domain-address), "._pka."), domain+1);
194
195   anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ);
196   xfree (name);
197   if (anslen < sizeof(HEADER))
198     return NULL; /* DNS resolver returned a too short answer. */
199
200   /* Don't despair: A good compiler should optimize this away, as
201      header is just 32 byte and constant at compile time.  It's
202      one way to comply with strict aliasing rules.  */
203   memcpy (&header, answer, sizeof (header));
204
205   if ( (rc=header.rcode) != NOERROR )
206     return NULL; /* DNS resolver returned an error. */
207
208   /* We assume that PACKETSZ is large enough and don't do dynmically
209      expansion of the buffer. */
210   if (anslen > PACKETSZ)
211     return NULL; /* DNS resolver returned a too long answer */
212
213   qdcount = ntohs (header.qdcount);
214   ancount = ntohs (header.ancount);
215
216   if (!ancount)
217     return NULL; /* Got no answer. */
218
219   p = answer + sizeof (HEADER);
220   pend = answer + anslen; /* Actually points directly behind the buffer. */
221
222   while (qdcount-- && p < pend)
223     {
224       rc = dn_skipname (p, pend);
225       if (rc == -1)
226         return NULL;
227       p += rc + QFIXEDSZ;
228     }
229
230   if (ancount > 1)
231     return NULL; /* more than one possible gpg trustdns record - none used. */
232
233   while (ancount-- && p <= pend)
234     {
235       unsigned int type, class, txtlen, n;
236       char *buffer, *bufp;
237
238       rc = dn_skipname (p, pend);
239       if (rc == -1)
240         return NULL;
241       p += rc;
242       if (p >= pend - 10)
243         return NULL; /* RR too short. */
244
245       type = *p++ << 8;
246       type |= *p++;
247       class = *p++ << 8;
248       class |= *p++;
249       p += 4;
250       txtlen = *p++ << 8;
251       txtlen |= *p++;
252       if (type != T_TXT || class != C_IN)
253         return NULL; /* Answer does not match the query. */
254
255       buffer = bufp = xmalloc (txtlen + 1);
256       while (txtlen && p < pend)
257         {
258           for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--)
259             *bufp++ = *p++;
260         }
261       *bufp = 0;
262       if (parse_txt_record (buffer, fpr))
263         {
264           xfree (buffer);
265           return NULL; /* Not a valid gpg trustdns RR. */
266         }
267       return buffer;
268     }
269
270   return NULL;
271 #endif /*!USE_ADNS*/
272 }
273
274 #else /* !USE_DNS_PKA */
275
276 /* Dummy version of the function if we can't use the resolver
277    functions. */
278 char *
279 get_pka_info (const char *address, unsigned char *fpr)
280 {
281   (void)address;
282   (void)fpr;
283   return NULL;
284 }
285 #endif /* !USE_DNS_PKA */
286
287
288 #ifdef TEST
289 int
290 main(int argc,char *argv[])
291 {
292   unsigned char fpr[20];
293   char *uri;
294   int i;
295
296   if (argc < 2)
297     {
298       fprintf (stderr, "usage: pka mail-addresses\n");
299       return 1;
300     }
301   argc--;
302   argv++;
303
304   for (; argc; argc--, argv++)
305     {
306       uri = get_pka_info ( *argv, fpr );
307       printf ("%s", *argv);
308       if (uri)
309         {
310           putchar (' ');
311           for (i=0; i < 20; i++)
312             printf ("%02X", fpr[i]);
313           if (*uri)
314             printf (" %s", uri);
315           xfree (uri);
316         }
317       putchar ('\n');
318     }
319   return 0;
320 }
321 #endif /* TEST */
322
323 /*
324 Local Variables:
325 compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv ../tools/no-libgcrypt.o ../jnlib/libjnlib.a"
326 End:
327 */