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