| /* $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 |