Improved detection of bad/invalid signer keys.
[gnupg.git] / common / pka.c
blob79a0bc3f53bcfb33a19a1dec8006b49799cc42aa
1 /* pka.c - DNS Public Key Association RR access
2 * Copyright (C) 2005 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
20 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
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 */
37 #include "util.h"
38 #include "pka.h"
40 #ifdef USE_DNS_PKA
41 /* Parse the TXT resource record. Format is:
43 v=pka1;fpr=a4d94e92b0986ab5ee9dcd755de249965b0358a2;uri=string
45 For simplicity white spaces are not allowed. Because we expect to
46 use a new RRTYPE for this in the future we define the TXT really
47 strict for simplicity: No white spaces, case sensitivity of the
48 names, order must be as given above. Only URI is optional.
50 This function modifies BUFFER. On success 0 is returned, the 20
51 byte fingerprint stored at FPR and BUFFER contains the URI or an
52 empty string.
54 static int
55 parse_txt_record (char *buffer, unsigned char *fpr)
57 char *p, *pend;
58 int i;
60 p = buffer;
61 pend = strchr (p, ';');
62 if (!pend)
63 return -1;
64 *pend++ = 0;
65 if (strcmp (p, "v=pka1"))
66 return -1; /* Wrong or missing version. */
68 p = pend;
69 pend = strchr (p, ';');
70 if (pend)
71 *pend++ = 0;
72 if (strncmp (p, "fpr=", 4))
73 return -1; /* Missing fingerprint part. */
74 p += 4;
75 for (i=0; i < 20 && hexdigitp (p) && hexdigitp (p+1); i++, p += 2)
76 fpr[i] = xtoi_2 (p);
77 if (i != 20)
78 return -1; /* Fingerprint consists not of exactly 40 hexbytes. */
80 p = pend;
81 if (!p || !*p)
83 *buffer = 0;
84 return 0; /* Success (no URI given). */
86 if (strncmp (p, "uri=", 4))
87 return -1; /* Unknown part. */
88 p += 4;
89 /* There is an URI, copy it to the start of the buffer. */
90 while (*p)
91 *buffer++ = *p++;
92 *buffer = 0;
93 return 0;
97 /* For the given email ADDRESS lookup the PKA information in the DNS.
99 On success the 20 byte SHA-1 fingerprint is stored at FPR and the
100 URI will be returned in an allocated buffer. Note that the URI
101 might be an zero length string as this information is optional.
102 Caller must xfree the returned string.
104 On error NULL is returned and the 20 bytes at FPR are not
105 defined. */
106 char *
107 get_pka_info (const char *address, unsigned char *fpr)
109 unsigned char answer[PACKETSZ];
110 int anslen;
111 int qdcount, ancount, nscount, arcount;
112 int rc;
113 unsigned char *p, *pend;
114 const char *domain;
115 char *name;
118 domain = strrchr (address, '@');
119 if (!domain || domain == address || !domain[1])
120 return NULL; /* invalid mail address given. */
122 name = xtrymalloc (strlen (address) + 5 + 1);
123 if (!name)
124 return NULL;
125 memcpy (name, address, domain - address);
126 strcpy (stpcpy (name + (domain-address), "._pka."), domain+1);
128 anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ);
129 xfree (name);
130 if (anslen < sizeof(HEADER))
131 return NULL; /* DNS resolver returned a too short answer. */
132 if ( (rc=((HEADER*)answer)->rcode) != NOERROR )
133 return NULL; /* DNS resolver returned an error. */
135 /* We assume that PACKETSZ is large enough and don't do dynmically
136 expansion of the buffer. */
137 if (anslen > PACKETSZ)
138 return NULL; /* DNS resolver returned a too long answer */
140 qdcount = ntohs (((HEADER*)answer)->qdcount);
141 ancount = ntohs (((HEADER*)answer)->ancount);
142 nscount = ntohs (((HEADER*)answer)->nscount);
143 arcount = ntohs (((HEADER*)answer)->arcount);
145 if (!ancount)
146 return NULL; /* Got no answer. */
148 p = answer + sizeof (HEADER);
149 pend = answer + anslen; /* Actually points directly behind the buffer. */
151 while (qdcount-- && p < pend)
153 rc = dn_skipname (p, pend);
154 if (rc == -1)
155 return NULL;
156 p += rc + QFIXEDSZ;
159 if (ancount > 1)
160 return NULL; /* more than one possible gpg trustdns record - none used. */
162 while (ancount-- && p <= pend)
164 unsigned int type, class, txtlen, n;
165 char *buffer, *bufp;
167 rc = dn_skipname (p, pend);
168 if (rc == -1)
169 return NULL;
170 p += rc;
171 if (p >= pend - 10)
172 return NULL; /* RR too short. */
174 type = *p++ << 8;
175 type |= *p++;
176 class = *p++ << 8;
177 class |= *p++;
178 p += 4;
179 txtlen = *p++ << 8;
180 txtlen |= *p++;
181 if (type != T_TXT || class != C_IN)
182 return NULL; /* Answer does not match the query. */
184 buffer = bufp = xmalloc (txtlen + 1);
185 while (txtlen && p < pend)
187 for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--)
188 *bufp++ = *p++;
190 *bufp = 0;
191 if (parse_txt_record (buffer, fpr))
193 xfree (buffer);
194 return NULL; /* Not a valid gpg trustdns RR. */
196 return buffer;
199 return NULL;
201 #else /* !USE_DNS_PKA */
203 /* Dummy version of the function if we can't use the resolver
204 functions. */
205 char *
206 get_pka_info (const char *address, unsigned char *fpr)
208 return NULL;
210 #endif /* !USE_DNS_PKA */
213 #ifdef TEST
215 main(int argc,char *argv[])
217 unsigned char fpr[20];
218 char *uri;
219 int i;
221 if (argc < 2)
223 fprintf (stderr, "usage: pka mail-addresses\n");
224 return 1;
226 argc--;
227 argv++;
229 for (; argc; argc--, argv++)
231 uri = get_pka_info ( *argv, fpr );
232 printf ("%s", *argv);
233 if (uri)
235 putchar (' ');
236 for (i=0; i < 20; i++)
237 printf ("%02X", fpr[i]);
238 if (*uri)
239 printf (" %s", uri);
240 xfree (uri);
242 putchar ('\n');
244 return 0;
246 #endif /* TEST */
249 Local Variables:
250 compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv libutil.a"
251 End: