2008-02-01 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / common / pka.c
blobe8cdee61e2346a0622131d47e26fd8e70dc0ac5b
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 optiobnal.
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 = malloc (strlen (address) + 5 + 1);
123 memcpy (name, address, domain - address);
124 strcpy (stpcpy (name + (domain-address), "._pka."), domain+1);
126 anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ);
127 xfree (name);
128 if (anslen < sizeof(HEADER))
129 return NULL; /* DNS resolver returned a too short answer. */
130 if ( (rc=((HEADER*)answer)->rcode) != NOERROR )
131 return NULL; /* DNS resolver returned an error. */
133 /* We assume that PACKETSZ is large enough and don't do dynmically
134 expansion of the buffer. */
135 if (anslen > PACKETSZ)
136 return NULL; /* DNS resolver returned a too long answer */
138 qdcount = ntohs (((HEADER*)answer)->qdcount);
139 ancount = ntohs (((HEADER*)answer)->ancount);
140 nscount = ntohs (((HEADER*)answer)->nscount);
141 arcount = ntohs (((HEADER*)answer)->arcount);
143 if (!ancount)
144 return NULL; /* Got no answer. */
146 p = answer + sizeof (HEADER);
147 pend = answer + anslen; /* Actually points directly behind the buffer. */
149 while (qdcount-- && p < pend)
151 rc = dn_skipname (p, pend);
152 if (rc == -1)
153 return NULL;
154 p += rc + QFIXEDSZ;
157 if (ancount > 1)
158 return NULL; /* more than one possible gpg trustdns record - none used. */
160 while (ancount-- && p <= pend)
162 unsigned int type, class, txtlen, n;
163 char *buffer, *bufp;
165 rc = dn_skipname (p, pend);
166 if (rc == -1)
167 return NULL;
168 p += rc;
169 if (p >= pend - 10)
170 return NULL; /* RR too short. */
172 type = *p++ << 8;
173 type |= *p++;
174 class = *p++ << 8;
175 class |= *p++;
176 p += 4;
177 txtlen = *p++ << 8;
178 txtlen |= *p++;
179 if (type != T_TXT || class != C_IN)
180 return NULL; /* Answer does not match the query. */
182 buffer = bufp = xmalloc (txtlen + 1);
183 while (txtlen && p < pend)
185 for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--)
186 *bufp++ = *p++;
188 *bufp = 0;
189 if (parse_txt_record (buffer, fpr))
191 xfree (buffer);
192 return NULL; /* Not a valid gpg trustdns RR. */
194 return buffer;
197 return NULL;
199 #else /* !USE_DNS_PKA */
201 /* Dummy version of the function if we can't use the resolver
202 functions. */
203 char *
204 get_pka_info (const char *address, unsigned char *fpr)
206 return NULL;
208 #endif /* !USE_DNS_PKA */
211 #ifdef TEST
213 main(int argc,char *argv[])
215 unsigned char fpr[20];
216 char *uri;
217 int i;
219 if (argc < 2)
221 fprintf (stderr, "usage: pka mail-addresses\n");
222 return 1;
224 argc--;
225 argv++;
227 for (; argc; argc--, argv++)
229 uri = get_pka_info ( *argv, fpr );
230 printf ("%s", *argv);
231 if (uri)
233 putchar (' ');
234 for (i=0; i < 20; i++)
235 printf ("%02X", fpr[i]);
236 if (*uri)
237 printf (" %s", uri);
238 xfree (uri);
240 putchar ('\n');
242 return 0;
244 #endif /* TEST */
247 Local Variables:
248 compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv libutil.a"
249 End: