1/*	$KAME: getcertsbyname.c,v 1.7 2001/11/16 04:12:59 sakane Exp $	*/
2
3/*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include "config.h"
33
34#include <sys/types.h>
35#include <sys/param.h>
36#include <sys/socket.h>
37
38#include <netinet/in.h>
39#include <arpa/nameser_compat.h>
40#include <resolv.h>
41#ifdef HAVE_LWRES_GETRRSETBYNAME
42#include <lwres/netdb.h>
43#include <lwres/lwres.h>
44#else
45#include <netdb.h>
46#endif
47#include <stdlib.h>
48#include <string.h>
49#include <errno.h>
50
51#ifdef DNSSEC_DEBUG
52#include <stdio.h>
53#include <strings.h>
54#endif
55
56#include "var.h"
57#include "netdb_dnssec.h"
58
59/* XXX should it use ci_errno to hold errno instead of h_errno ? */
60extern int h_errno;
61
62static struct certinfo *getnewci (int, int, int, int, int,
63			unsigned char *);
64
65static struct certinfo *
66getnewci(qtype, keytag, algorithm, flags, certlen, cert)
67	int qtype, keytag, algorithm, flags, certlen;
68	unsigned char *cert;
69{
70	struct certinfo *res;
71
72	res = malloc(sizeof(*res));
73	if (!res)
74		return NULL;
75
76	memset(res, 0, sizeof(*res));
77	res->ci_type = qtype;
78	res->ci_keytag = keytag;
79	res->ci_algorithm = algorithm;
80	res->ci_flags = flags;
81	res->ci_certlen = certlen;
82	res->ci_cert = malloc(certlen);
83	if (!res->ci_cert) {
84		free(res);
85		return NULL;
86	}
87	memcpy(res->ci_cert, cert, certlen);
88
89	return res;
90}
91
92void
93freecertinfo(ci)
94	struct certinfo *ci;
95{
96	struct certinfo *next;
97
98	do {
99		next = ci->ci_next;
100		if (ci->ci_cert)
101			free(ci->ci_cert);
102		free(ci);
103		ci = next;
104	} while (ci);
105}
106
107/*
108 * get CERT RR by FQDN and create certinfo structure chain.
109 */
110#ifdef HAVE_LWRES_GETRRSETBYNAME
111#define getrrsetbyname lwres_getrrsetbyname
112#define freerrset lwres_freerrset
113#define hstrerror lwres_hstrerror
114#endif
115#if defined(HAVE_LWRES_GETRRSETBYNAME) || defined(HAVE_GETRRSETBYNAME) 	//%%% BUG FIX - HAVE misspelled
116int
117getcertsbyname(name, res)
118	char *name;
119	struct certinfo **res;
120{
121	int rdlength;
122	char *cp;
123	int type, keytag, algorithm;
124	struct certinfo head, *cur;
125	struct rrsetinfo *rr = NULL;
126	int i;
127	int error = -1;
128
129	/* initialize res */
130	*res = NULL;
131
132	memset(&head, 0, sizeof(head));
133	cur = &head;
134
135	error = getrrsetbyname(name, C_IN, T_CERT, 0, &rr);
136	if (error) {
137#ifdef DNSSEC_DEBUG
138		printf("getrrsetbyname: %s\n", hstrerror(error));
139#endif
140		h_errno = NO_RECOVERY;
141		goto end;
142	}
143
144	if (rr->rri_rdclass != C_IN
145	 || rr->rri_rdtype != T_CERT
146	 || rr->rri_nrdatas == 0) {
147#ifdef DNSSEC_DEBUG
148		printf("getrrsetbyname: %s", hstrerror(error));
149#endif
150		h_errno = NO_RECOVERY;
151		goto end;
152	}
153#ifdef DNSSEC_DEBUG
154	if (!(rr->rri_flags & LWRDATA_VALIDATED))
155		printf("rr is not valid");
156#endif
157
158	for (i = 0; i < rr->rri_nrdatas; i++) {
159		rdlength = rr->rri_rdatas[i].rdi_length;
160		cp = rr->rri_rdatas[i].rdi_data;
161
162		GETSHORT(type, cp);	/* type */
163		rdlength -= INT16SZ;
164		GETSHORT(keytag, cp);	/* key tag */
165		rdlength -= INT16SZ;
166		algorithm = *cp++;	/* algorithm */
167		rdlength -= 1;
168
169#ifdef DNSSEC_DEBUG
170		printf("type=%d keytag=%d alg=%d len=%d\n",
171			type, keytag, algorithm, rdlength);
172#endif
173
174		/* create new certinfo */
175		cur->ci_next = getnewci(type, keytag, algorithm,
176					rr->rri_flags, rdlength, cp);
177		if (!cur->ci_next) {
178#ifdef DNSSEC_DEBUG
179			printf("getnewci: %s", strerror(errno));
180#endif
181			h_errno = NO_RECOVERY;
182			goto end;
183		}
184		cur = cur->ci_next;
185	}
186
187	*res = head.ci_next;
188	error = 0;
189
190end:
191	if (rr)
192		freerrset(rr);
193	if (error && head.ci_next)
194		freecertinfo(head.ci_next);
195
196	return error;
197}
198#else	/*!HAVE_LWRES_GETRRSETBYNAME*/
199int
200getcertsbyname(name, res)
201	char *name;
202	struct certinfo **res;
203{
204	unsigned char *answer = NULL, *p;
205	int buflen, anslen, len;
206	HEADER *hp;
207	int qdcount, ancount, rdlength;
208	unsigned char *cp, *eom;
209	char hostbuf[1024];	/* XXX */
210	int qtype, qclass, keytag, algorithm;
211	struct certinfo head, *cur;
212	int error = -1;
213
214	/* initialize res */
215	*res = NULL;
216
217	memset(&head, 0, sizeof(head));
218	cur = &head;
219
220	/* get CERT RR */
221	buflen = 512;
222	do {
223
224		buflen *= 2;
225		p = realloc(answer, buflen);
226		if (!p) {
227#ifdef DNSSEC_DEBUG
228			printf("realloc: %s", strerror(errno));
229#endif
230			h_errno = NO_RECOVERY;
231			goto end;
232		}
233		answer = p;
234
235		anslen = res_query(name,  C_IN, T_CERT, answer, buflen);
236		if (anslen == -1)
237			goto end;
238
239	} while (buflen < anslen);
240
241#ifdef DNSSEC_DEBUG
242	printf("get a DNS packet len=%d\n", anslen);
243#endif
244
245	/* parse CERT RR */
246	eom = answer + anslen;
247
248	hp = ALIGNED_CAST(HEADER *)answer;
249	qdcount = ntohs(hp->qdcount);
250	ancount = ntohs(hp->ancount);
251
252	/* question section */
253	if (qdcount != 1) {
254#ifdef DNSSEC_DEBUG
255		printf("query count is not 1.\n");
256#endif
257		h_errno = NO_RECOVERY;
258		goto end;
259	}
260	cp = (unsigned char *)(hp + 1);
261	len = dn_expand(answer, eom, cp, hostbuf, sizeof(hostbuf));
262	if (len < 0) {
263#ifdef DNSSEC_DEBUG
264		printf("dn_expand failed.\n");
265#endif
266		goto end;
267	}
268	cp += len;
269	GETSHORT(qtype, cp);		/* QTYPE */
270	GETSHORT(qclass, cp);		/* QCLASS */
271
272	/* answer section */
273	while (ancount-- && cp < eom) {
274		len = dn_expand(answer, eom, cp, hostbuf, sizeof(hostbuf));
275		if (len < 0) {
276#ifdef DNSSEC_DEBUG
277			printf("dn_expand failed.\n");
278#endif
279			goto end;
280		}
281		cp += len;
282		GETSHORT(qtype, cp);	/* TYPE */
283		GETSHORT(qclass, cp);	/* CLASS */
284		cp += INT32SZ;		/* TTL */
285		GETSHORT(rdlength, cp);	/* RDLENGTH */
286
287		/* CERT RR */
288		if (qtype != T_CERT) {
289#ifdef DNSSEC_DEBUG
290			printf("not T_CERT\n");
291#endif
292			h_errno = NO_RECOVERY;
293			goto end;
294		}
295		GETSHORT(qtype, cp);	/* type */
296		rdlength -= INT16SZ;
297		GETSHORT(keytag, cp);	/* key tag */
298		rdlength -= INT16SZ;
299		algorithm = *cp++;	/* algorithm */
300		rdlength -= 1;
301		if (cp + rdlength > eom) {
302#ifdef DNSSEC_DEBUG
303			printf("rdlength is too long.\n");
304#endif
305			h_errno = NO_RECOVERY;
306			goto end;
307		}
308#ifdef DNSSEC_DEBUG
309		printf("type=%d keytag=%d alg=%d len=%d\n",
310			qtype, keytag, algorithm, rdlength);
311#endif
312
313		/* create new certinfo */
314		cur->ci_next = getnewci(qtype, keytag, algorithm,
315					0, rdlength, cp);
316		if (!cur->ci_next) {
317#ifdef DNSSEC_DEBUG
318			printf("getnewci: %s", strerror(errno));
319#endif
320			h_errno = NO_RECOVERY;
321			goto end;
322		}
323		cur = cur->ci_next;
324
325		cp += rdlength;
326	}
327
328	*res = head.ci_next;
329	error = 0;
330
331end:
332	if (answer)
333		free(answer);
334	if (error && head.ci_next)
335		freecertinfo(head.ci_next);
336
337	return error;
338}
339#endif
340
341#ifdef DNSSEC_DEBUG
342int
343b64encode(p, len)
344	char *p;
345	int len;
346{
347	static const char b64t[] =
348		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
349		"abcdefghijklmnopqrstuvwxyz"
350		"0123456789+/=";
351
352	while (len > 2) {
353                printf("%c", b64t[(p[0] >> 2) & 0x3f]);
354                printf("%c", b64t[((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)]);
355                printf("%c", b64t[((p[1] << 2) & 0x3c) | ((p[2] >> 6) & 0x03)]);
356                printf("%c", b64t[p[2] & 0x3f]);
357		len -= 3;
358		p += 3;
359	}
360
361	if (len == 2) {
362                printf("%c", b64t[(p[0] >> 2) & 0x3f]);
363                printf("%c", b64t[((p[0] << 4) & 0x30)| ((p[1] >> 4) & 0x0f)]);
364                printf("%c", b64t[((p[1] << 2) & 0x3c)]);
365                printf("%c", '=');
366        } else if (len == 1) {
367                printf("%c", b64t[(p[0] >> 2) & 0x3f]);
368                printf("%c", b64t[((p[0] << 4) & 0x30)]);
369                printf("%c", '=');
370                printf("%c", '=');
371	}
372
373	return 0;
374}
375
376int
377main(ac, av)
378	int ac;
379	char **av;
380{
381	struct certinfo *res, *p;
382	int i;
383
384	if (ac < 2) {
385		printf("Usage: a.out (FQDN)\n");
386		exit(1);
387	}
388
389	i = getcertsbyname(*(av + 1), &res);
390	if (i != 0) {
391		herror("getcertsbyname");
392		exit(1);
393	}
394	printf("getcertsbyname succeeded.\n");
395
396	i = 0;
397	for (p = res; p; p = p->ci_next) {
398		printf("certinfo[%d]:\n", i);
399		printf("\tci_type=%d\n", p->ci_type);
400		printf("\tci_keytag=%d\n", p->ci_keytag);
401		printf("\tci_algorithm=%d\n", p->ci_algorithm);
402		printf("\tci_flags=%d\n", p->ci_flags);
403		printf("\tci_certlen=%d\n", p->ci_certlen);
404		printf("\tci_cert: ");
405		b64encode(p->ci_cert, p->ci_certlen);
406		printf("\n");
407		i++;
408	}
409
410	freecertinfo(res);
411
412	exit(0);
413}
414#endif
415