/* $NetBSD: getcertsbyname.c,v 1.4 2006/09/09 16:22:09 manu Exp $ */
/* $KAME: getcertsbyname.c,v 1.7 2001/11/16 04:12:59 sakane Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#if (defined(__APPLE__) && defined(__MACH__))
# include <nameser8_compat.h>
#endif
#include <resolv.h>
#ifdef HAVE_LWRES_GETRRSETBYNAME
#include <lwres/netdb.h>
#include <lwres/lwres.h>
#else
#include <netdb.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef DNSSEC_DEBUG
#include <stdio.h>
#include <strings.h>
#endif
#include "netdb_dnssec.h"
/* XXX should it use ci_errno to hold errno instead of h_errno ? */
extern int h_errno;
static struct certinfo *getnewci __P((int, int, int, int, int,
unsigned char *));
static struct certinfo *
getnewci(qtype, keytag, algorithm, flags, certlen, cert)
int qtype, keytag, algorithm, flags, certlen;
unsigned char *cert;
{
struct certinfo *res;
res = malloc(sizeof(*res));
if (!res)
return NULL;
memset(res, 0, sizeof(*res));
res->ci_type = qtype;
res->ci_keytag = keytag;
res->ci_algorithm = algorithm;
res->ci_flags = flags;
res->ci_certlen = certlen;
res->ci_cert = malloc(certlen);
if (!res->ci_cert) {
free(res);
return NULL;
}
memcpy(res->ci_cert, cert, certlen);
return res;
}
void
freecertinfo(ci)
struct certinfo *ci;
{
struct certinfo *next;
do {
next = ci->ci_next;
if (ci->ci_cert)
free(ci->ci_cert);
free(ci);
ci = next;
} while (ci);
}
/*
* get CERT RR by FQDN and create certinfo structure chain.
*/
#ifdef HAVE_LWRES_GETRRSETBYNAME
#define getrrsetbyname lwres_getrrsetbyname
#define freerrset lwres_freerrset
#define hstrerror lwres_hstrerror
#endif
#if defined(HAVE_LWRES_GETRRSETBYNAME) || defined(AHVE_GETRRSETBYNAME)
int
getcertsbyname(name, res)
char *name;
struct certinfo **res;
{
int rdlength;
char *cp;
int type, keytag, algorithm;
struct certinfo head, *cur;
struct rrsetinfo *rr = NULL;
int i;
int error = -1;
/* initialize res */
*res = NULL;
memset(&head, 0, sizeof(head));
cur = &head;
error = getrrsetbyname(name, C_IN, T_CERT, 0, &rr);
if (error) {
#ifdef DNSSEC_DEBUG
printf("getrrsetbyname: %s\n", hstrerror(error));
#endif
h_errno = NO_RECOVERY;
goto end;
}
if (rr->rri_rdclass != C_IN
|| rr->rri_rdtype != T_CERT
|| rr->rri_nrdatas == 0) {
#ifdef DNSSEC_DEBUG
printf("getrrsetbyname: %s", hstrerror(error));
#endif
h_errno = NO_RECOVERY;
goto end;
}
#ifdef DNSSEC_DEBUG
if (!(rr->rri_flags & LWRDATA_VALIDATED))
printf("rr is not valid");
#endif
for (i = 0; i < rr->rri_nrdatas; i++) {
rdlength = rr->rri_rdatas[i].rdi_length;
cp = rr->rri_rdatas[i].rdi_data;
GETSHORT(type, cp); /* type */
rdlength -= INT16SZ;
GETSHORT(keytag, cp); /* key tag */
rdlength -= INT16SZ;
algorithm = *cp++; /* algorithm */
rdlength -= 1;
#ifdef DNSSEC_DEBUG
printf("type=%d keytag=%d alg=%d len=%d\n",
type, keytag, algorithm, rdlength);
#endif
/* create new certinfo */
cur->ci_next = getnewci(type, keytag, algorithm,
rr->rri_flags, rdlength, cp);
if (!cur->ci_next) {
#ifdef DNSSEC_DEBUG
printf("getnewci: %s", strerror(errno));
#endif
h_errno = NO_RECOVERY;
goto end;
}
cur = cur->ci_next;
}
*res = head.ci_next;
error = 0;
end:
if (rr)
freerrset(rr);
if (error && head.ci_next)
freecertinfo(head.ci_next);
return error;
}
#else /*!HAVE_LWRES_GETRRSETBYNAME*/
int
getcertsbyname(name, res)
char *name;
struct certinfo **res;
{
unsigned char *answer = NULL, *p;
int buflen, anslen, len;
HEADER *hp;
int qdcount, ancount, rdlength;
unsigned char *cp, *eom;
char hostbuf[1024]; /* XXX */
int qtype, qclass, keytag, algorithm;
struct certinfo head, *cur;
int error = -1;
/* initialize res */
*res = NULL;
memset(&head, 0, sizeof(head));
cur = &head;
/* get CERT RR */
buflen = 512;
do {
buflen *= 2;
p = realloc(answer, buflen);
if (!p) {
#ifdef DNSSEC_DEBUG
printf("realloc: %s", strerror(errno));
#endif
h_errno = NO_RECOVERY;
goto end;
}
answer = p;
anslen = res_query(name, C_IN, T_CERT, answer, buflen);
if (anslen == -1)
goto end;
} while (buflen < anslen);
#ifdef DNSSEC_DEBUG
printf("get a DNS packet len=%d\n", anslen);
#endif
/* parse CERT RR */
eom = answer + anslen;
hp = (HEADER *)answer;
qdcount = ntohs(hp->qdcount);
ancount = ntohs(hp->ancount);
/* question section */
if (qdcount != 1) {
#ifdef DNSSEC_DEBUG
printf("query count is not 1.\n");
#endif
h_errno = NO_RECOVERY;
goto end;
}
cp = (unsigned char *)(hp + 1);
len = dn_expand(answer, eom, cp, hostbuf, sizeof(hostbuf));
if (len < 0) {
#ifdef DNSSEC_DEBUG
printf("dn_expand failed.\n");
#endif
goto end;
}
cp += len;
GETSHORT(qtype, cp); /* QTYPE */
GETSHORT(qclass, cp); /* QCLASS */
/* answer section */
while (ancount-- && cp < eom) {
len = dn_expand(answer, eom, cp, hostbuf, sizeof(hostbuf));
if (len < 0) {
#ifdef DNSSEC_DEBUG
printf("dn_expand failed.\n");
#endif
goto end;
}
cp += len;
GETSHORT(qtype, cp); /* TYPE */
GETSHORT(qclass, cp); /* CLASS */
cp += INT32SZ; /* TTL */
GETSHORT(rdlength, cp); /* RDLENGTH */
/* CERT RR */
if (qtype != T_CERT) {
#ifdef DNSSEC_DEBUG
printf("not T_CERT\n");
#endif
h_errno = NO_RECOVERY;
goto end;
}
GETSHORT(qtype, cp); /* type */
rdlength -= INT16SZ;
GETSHORT(keytag, cp); /* key tag */
rdlength -= INT16SZ;
algorithm = *cp++; /* algorithm */
rdlength -= 1;
if (cp + rdlength > eom) {
#ifdef DNSSEC_DEBUG
printf("rdlength is too long.\n");
#endif
h_errno = NO_RECOVERY;
goto end;
}
#ifdef DNSSEC_DEBUG
printf("type=%d keytag=%d alg=%d len=%d\n",
qtype, keytag, algorithm, rdlength);
#endif
/* create new certinfo */
cur->ci_next = getnewci(qtype, keytag, algorithm,
0, rdlength, cp);
if (!cur->ci_next) {
#ifdef DNSSEC_DEBUG
printf("getnewci: %s", strerror(errno));
#endif
h_errno = NO_RECOVERY;
goto end;
}
cur = cur->ci_next;
cp += rdlength;
}
*res = head.ci_next;
error = 0;
end:
if (answer)
free(answer);
if (error && head.ci_next)
freecertinfo(head.ci_next);
return error;
}
#endif
#ifdef DNSSEC_DEBUG
int
b64encode(p, len)
char *p;
int len;
{
static const char b64t[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/=";
while (len > 2) {
printf("%c", b64t[(p[0] >> 2) & 0x3f]);
printf("%c", b64t[((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)]);
printf("%c", b64t[((p[1] << 2) & 0x3c) | ((p[2] >> 6) & 0x03)]);
printf("%c", b64t[p[2] & 0x3f]);
len -= 3;
p += 3;
}
if (len == 2) {
printf("%c", b64t[(p[0] >> 2) & 0x3f]);
printf("%c", b64t[((p[0] << 4) & 0x30)| ((p[1] >> 4) & 0x0f)]);
printf("%c", b64t[((p[1] << 2) & 0x3c)]);
printf("%c", '=');
} else if (len == 1) {
printf("%c", b64t[(p[0] >> 2) & 0x3f]);
printf("%c", b64t[((p[0] << 4) & 0x30)]);
printf("%c", '=');
printf("%c", '=');
}
return 0;
}
int
main(ac, av)
int ac;
char **av;
{
struct certinfo *res, *p;
int i;
if (ac < 2) {
printf("Usage: a.out (FQDN)\n");
exit(1);
}
i = getcertsbyname(*(av + 1), &res);
if (i != 0) {
herror("getcertsbyname");
exit(1);
}
printf("getcertsbyname succeeded.\n");
i = 0;
for (p = res; p; p = p->ci_next) {
printf("certinfo[%d]:\n", i);
printf("\tci_type=%d\n", p->ci_type);
printf("\tci_keytag=%d\n", p->ci_keytag);
printf("\tci_algorithm=%d\n", p->ci_algorithm);
printf("\tci_flags=%d\n", p->ci_flags);
printf("\tci_certlen=%d\n", p->ci_certlen);
printf("\tci_cert: ");
b64encode(p->ci_cert, p->ci_certlen);
printf("\n");
i++;
}
freecertinfo(res);
exit(0);
}
#endif