root/crypto/dist/ipsec-tools/src/racoon/getcertsbyname.c
/*      $NetBSD: getcertsbyname.c,v 1.6 2025/03/08 16:39:08 christos 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(int, int, int, int, int, unsigned char *);

static struct certinfo *
getnewci(int qtype, int keytag, int algorithm, int flags, int 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(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(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(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 */
        (void)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