New code to do DNS CERT queries.
[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           /* We asked for CERT and got something else !? */
100           if(type!=T_CERT)
101             break;
102
103           class=*pt++ << 8;
104           class|=*pt++;
105           /* We asked for IN and got something else !? */
106           if(class!=C_IN)
107             break;
108
109           /* ttl */
110           pt+=4;
111
112           /* data length */
113           dlen=*pt++ << 8;
114           dlen|=*pt++;
115
116           /* The CERT type */
117           ctype=*pt++ << 8;
118           ctype|=*pt++;
119
120           /* Skip the CERT key tag and algo which we don't need. */
121           pt+=3;
122
123           dlen-=5;
124
125           if(ctype==3 && iobuf)
126             {
127               /* PGP type */
128               *iobuf=iobuf_temp_with_content(pt,dlen);
129               ret=1;
130               break;
131             }
132           else if(ctype==6 && dlen<1023 && url)
133             {
134               /* Sanity check the IPGP URL type that the URL isn't too
135                  long */
136
137               *url=xmalloc(dlen+1);
138               memcpy(*url,pt,dlen);
139               ret=2;
140               break;
141             }
142
143           /* Neither type matches, so go around to the next answer. */
144           pt+=dlen;
145         }
146     }
147
148  fail:
149   xfree(answer);
150
151   return ret;
152 }
153
154 #else /* !USE_DNS_CERT */
155
156 int
157 get_cert(const char *name,size_t max_size,IOBUF *iobuf,char **url)
158 {
159   return -1;
160 }
161
162 #endif
163
164 /* Test with simon.josefsson.org */
165
166 #ifdef TEST
167 int
168 main(int argc,char *argv[])
169 {
170   char *url;
171   int rc;
172   IOBUF iobuf;
173
174   if(argc!=2)
175     {
176       printf("cert-test [name]\n");
177       return 1;
178     }
179
180   printf("CERT lookup on %s\n",argv[1]);
181
182   rc=get_cert(argv[1],16384,&iobuf,&url);
183   if(rc==-1)
184     printf("error\n");
185   else if(rc==0)
186     printf("no answer\n");
187   else if(rc==1)
188     {
189       printf("key found: %d bytes\n",iobuf_get_temp_length(iobuf));
190       iobuf_close(iobuf);
191     }
192   else if(rc==2)
193     {
194       printf("URL found: %s\n",url);
195       xfree(url);
196     }
197
198   return 0;
199 }
200 #endif /* TEST */