* cert.c (get_cert): Properly chase down CNAMEs pointing to CERTs.
[gnupg.git] / util / cert.c
1 /* cert.c - DNS CERT code
2  * Copyright (C) 2005 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
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,char **url)
49 {
50   unsigned char *answer;
51   int r,ret=-1;
52   u16 count;
53
54   answer=xmalloc(max_size);
55
56   r=res_query(name,C_IN,T_CERT,answer,max_size);
57   /* Not too big, not too small, no errors and at least 1 answer. */
58   if(r>=sizeof(HEADER) && r<=max_size
59      && (((HEADER *)answer)->rcode)==NOERROR
60      && (count=ntohs(((HEADER *)answer)->ancount)))
61     {
62       int rc;
63       unsigned char *pt,*emsg;
64
65       emsg=&answer[r];
66
67       pt=&answer[sizeof(HEADER)];
68
69       /* Skip over the query */
70
71       rc=dn_skipname(pt,emsg);
72       if(rc==-1)
73         goto fail;
74
75       pt+=rc+QFIXEDSZ;
76
77       /* There are several possible response types for a CERT request.
78          We're interested in the PGP (a key) and IPGP (a URI) types.
79          Skip all others.  TODO: A key is better than a URI since
80          we've gone through all this bother to fetch it, so favor that
81          if we have both PGP and IPGP? */
82
83       while(count-->0 && pt<emsg)
84         {
85           u16 type,class,dlen,ctype;
86
87           rc=dn_skipname(pt,emsg); /* the name we just queried for */
88           if(rc==-1)
89             break;
90
91           pt+=rc;
92
93           /* Truncated message? */
94           if((emsg-pt)<15)
95             break;
96
97           type=*pt++ << 8;
98           type|=*pt++;
99
100           class=*pt++ << 8;
101           class|=*pt++;
102           /* We asked for IN and got something else !? */
103           if(class!=C_IN)
104             break;
105
106           /* ttl */
107           pt+=4;
108
109           /* data length */
110           dlen=*pt++ << 8;
111           dlen|=*pt++;
112
113           /* We asked for CERT and got something else - might be a
114              CNAME, so loop around again. */
115           if(type!=T_CERT)
116             {
117               pt+=dlen;
118               continue;
119             }
120
121           /* The CERT type */
122           ctype=*pt++ << 8;
123           ctype|=*pt++;
124
125           /* Skip the CERT key tag and algo which we don't need. */
126           pt+=3;
127
128           dlen-=5;
129
130           if(ctype==3 && iobuf)
131             {
132               /* PGP type */
133               *iobuf=iobuf_temp_with_content((char *)pt,dlen);
134               ret=1;
135               break;
136             }
137           else if(ctype==6 && dlen<1023 && url)
138             {
139               /* Sanity check the IPGP URL type that the URL isn't too
140                  long */
141
142               *url=xmalloc(dlen+1);
143               memcpy(*url,pt,dlen);
144               (*url)[dlen]='\0';
145               ret=2;
146               break;
147             }
148
149           /* Neither type matches, so go around to the next answer. */
150           pt+=dlen;
151         }
152     }
153
154  fail:
155   xfree(answer);
156
157   return ret;
158 }
159
160 #else /* !USE_DNS_CERT */
161
162 int
163 get_cert(const char *name,size_t max_size,IOBUF *iobuf,char **url)
164 {
165   return -1;
166 }
167
168 #endif
169
170 /* Test with simon.josefsson.org */
171
172 #ifdef TEST
173 int
174 main(int argc,char *argv[])
175 {
176   char *url;
177   int rc;
178   IOBUF iobuf;
179
180   if(argc!=2)
181     {
182       printf("cert-test [name]\n");
183       return 1;
184     }
185
186   printf("CERT lookup on %s\n",argv[1]);
187
188   rc=get_cert(argv[1],16384,&iobuf,&url);
189   if(rc==-1)
190     printf("error\n");
191   else if(rc==0)
192     printf("no answer\n");
193   else if(rc==1)
194     {
195       printf("key found: %d bytes\n",iobuf_get_temp_length(iobuf));
196       iobuf_close(iobuf);
197     }
198   else if(rc==2)
199     {
200       printf("URL found: %s\n",url);
201       xfree(url);
202     }
203
204   return 0;
205 }
206 #endif /* TEST */