Switched to GPLv3.
[gnupg.git] / util / cert.c
1 /* cert.c - DNS CERT code
2  * Copyright (C) 2005, 2006, 2007 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 #include <sys/types.h>
22 #ifdef USE_DNS_CERT
23 #ifdef _WIN32
24 #include <windows.h>
25 #else
26 #include <netinet/in.h>
27 #include <arpa/nameser.h>
28 #include <arpa/inet.h>
29 #include <resolv.h>
30 #endif
31 #include <string.h>
32 #include "memory.h"
33 #endif
34 #include "iobuf.h"
35 #include "util.h"
36
37 /* Not every installation has gotten around to supporting CERTs
38    yet... */
39 #ifndef T_CERT
40 #define T_CERT 37
41 #endif
42
43 #ifdef USE_DNS_CERT
44
45 /* Returns -1 on error, 0 for no answer, 1 for PGP provided and 2 for
46    IPGP provided. */
47 int
48 get_cert(const char *name,size_t max_size,IOBUF *iobuf,
49          unsigned char **fpr,size_t *fpr_len,char **url)
50 {
51   unsigned char *answer;
52   int r,ret=-1;
53   u16 count;
54
55   if(fpr)
56     *fpr=NULL;
57
58   if(url)
59     *url=NULL;
60
61   answer=xmalloc(max_size);
62
63   r=res_query(name,C_IN,T_CERT,answer,max_size);
64   /* Not too big, not too small, no errors and at least 1 answer. */
65   if(r>=sizeof(HEADER) && r<=max_size
66      && (((HEADER *)answer)->rcode)==NOERROR
67      && (count=ntohs(((HEADER *)answer)->ancount)))
68     {
69       int rc;
70       unsigned char *pt,*emsg;
71
72       emsg=&answer[r];
73
74       pt=&answer[sizeof(HEADER)];
75
76       /* Skip over the query */
77
78       rc=dn_skipname(pt,emsg);
79       if(rc==-1)
80         goto fail;
81
82       pt+=rc+QFIXEDSZ;
83
84       /* There are several possible response types for a CERT request.
85          We're interested in the PGP (a key) and IPGP (a URI) types.
86          Skip all others.  TODO: A key is better than a URI since
87          we've gone through all this bother to fetch it, so favor that
88          if we have both PGP and IPGP? */
89
90       while(count-->0 && pt<emsg)
91         {
92           u16 type,class,dlen,ctype;
93
94           rc=dn_skipname(pt,emsg); /* the name we just queried for */
95           if(rc==-1)
96             break;
97
98           pt+=rc;
99
100           /* Truncated message? 15 bytes takes us to the point where
101              we start looking at the ctype. */
102           if((emsg-pt)<15)
103             break;
104
105           type=*pt++ << 8;
106           type|=*pt++;
107
108           class=*pt++ << 8;
109           class|=*pt++;
110           /* We asked for IN and got something else !? */
111           if(class!=C_IN)
112             break;
113
114           /* ttl */
115           pt+=4;
116
117           /* data length */
118           dlen=*pt++ << 8;
119           dlen|=*pt++;
120
121           /* We asked for CERT and got something else - might be a
122              CNAME, so loop around again. */
123           if(type!=T_CERT)
124             {
125               pt+=dlen;
126               continue;
127             }
128
129           /* The CERT type */
130           ctype=*pt++ << 8;
131           ctype|=*pt++;
132
133           /* Skip the CERT key tag and algo which we don't need. */
134           pt+=3;
135
136           dlen-=5;
137
138           /* 15 bytes takes us to here */
139
140           if(ctype==3 && iobuf && dlen)
141             {
142               /* PGP type */
143               *iobuf=iobuf_temp_with_content((char *)pt,dlen);
144               ret=1;
145               break;
146             }
147           else if(ctype==6 && dlen && dlen<1023 && dlen>=pt[0]+1
148                   && fpr && fpr_len && url)
149             {
150               /* IPGP type */
151               *fpr_len=pt[0];
152
153               if(*fpr_len)
154                 {
155                   *fpr=xmalloc(*fpr_len);
156                   memcpy(*fpr,&pt[1],*fpr_len);
157                 }
158               else
159                 *fpr=NULL;
160
161               if(dlen>*fpr_len+1)
162                 {
163                   *url=xmalloc(dlen-(*fpr_len+1)+1);
164                   memcpy(*url,&pt[*fpr_len+1],dlen-(*fpr_len+1));
165                   (*url)[dlen-(*fpr_len+1)]='\0';
166                 }
167               else
168                 *url=NULL;
169
170               ret=2;
171               break;
172             }
173
174           /* Neither type matches, so go around to the next answer. */
175           pt+=dlen;
176         }
177     }
178
179  fail:
180   xfree(answer);
181
182   return ret;
183 }
184
185 #else /* !USE_DNS_CERT */
186
187 int
188 get_cert(const char *name,size_t max_size,IOBUF *iobuf,
189          unsigned char **fpr,size_t *fpr_len,char **url)
190 {
191   return -1;
192 }
193
194 #endif
195
196 /* Test with simon.josefsson.org */
197
198 #ifdef TEST
199 int
200 main(int argc,char *argv[])
201 {
202   unsigned char *fpr;
203   size_t fpr_len;
204   char *url;
205   int rc;
206   IOBUF iobuf;
207
208   if(argc!=2)
209     {
210       printf("cert-test [name]\n");
211       return 1;
212     }
213
214   printf("CERT lookup on %s\n",argv[1]);
215
216   rc=get_cert(argv[1],16384,&iobuf,&fpr,&fpr_len,&url);
217   if(rc==-1)
218     printf("error\n");
219   else if(rc==0)
220     printf("no answer\n");
221   else if(rc==1)
222     {
223       printf("key found: %d bytes\n",(int)iobuf_get_temp_length(iobuf));
224       iobuf_close(iobuf);
225     }
226   else if(rc==2)
227     {
228       if(fpr)
229         {
230           size_t i;
231           printf("Fingerprint found (%d bytes): ",(int)fpr_len);
232           for(i=0;i<fpr_len;i++)
233             printf("%02X",fpr[i]);
234           printf("\n");
235         }
236       else
237         printf("No fingerprint found\n");
238
239       if(url)
240         printf("URL found: %s\n",url);
241       else
242         printf("No URL found\n");
243
244       xfree(fpr);
245       xfree(url);
246     }
247
248   return 0;
249 }
250 #endif /* TEST */