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