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