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