2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/os/dnsglue.c
2N/A *
2N/A * Copyright 2004, 2009 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "autoconf.h"
2N/A#ifdef KRB5_DNS_LOOKUP
2N/A
2N/A#include "dnsglue.h"
2N/A
2N/A/*
2N/A * Only use res_ninit() if there's also a res_ndestroy(), to avoid
2N/A * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x,
2N/A * 5.x). While we're at it, make sure res_nsearch() is there too.
2N/A *
2N/A * In any case, it is probable that platforms having broken
2N/A * res_ninit() will have thread safety hacks for res_init() and _res.
2N/A */
2N/A#if HAVE_RES_NINIT && HAVE_RES_NDESTROY && HAVE_RES_NSEARCH
2N/A#define USE_RES_NINIT 1
2N/A#endif
2N/A
2N/A/*
2N/A * Opaque handle
2N/A */
2N/Astruct krb5int_dns_state {
2N/A int nclass;
2N/A int ntype;
2N/A void *ansp;
2N/A int anslen;
2N/A int ansmax;
2N/A#if HAVE_NS_INITPARSE
2N/A int cur_ans;
2N/A ns_msg msg;
2N/A#else
2N/A unsigned char *ptr;
2N/A unsigned short nanswers;
2N/A#endif
2N/A};
2N/A
2N/A#if !HAVE_NS_INITPARSE
2N/Astatic int initparse(struct krb5int_dns_state *);
2N/A#endif
2N/A
2N/A/*
2N/A * krb5int_dns_init()
2N/A *
2N/A * Initialize an opaque handle. Do name lookup and initial parsing of
2N/A * reply, skipping question section. Prepare to iterate over answer
2N/A * section. Returns -1 on error, 0 on success.
2N/A */
2N/Aint
2N/Akrb5int_dns_init(struct krb5int_dns_state **dsp,
2N/A char *host, int nclass, int ntype)
2N/A{
2N/A#if USE_RES_NINIT
2N/A struct __res_state statbuf;
2N/A#endif
2N/A struct krb5int_dns_state *ds;
2N/A int len, ret;
2N/A size_t nextincr, maxincr;
2N/A unsigned char *p;
2N/A
2N/A *dsp = ds = malloc(sizeof(*ds));
2N/A if (ds == NULL)
2N/A return -1;
2N/A
2N/A ret = -1;
2N/A ds->nclass = nclass;
2N/A ds->ntype = ntype;
2N/A ds->ansp = NULL;
2N/A ds->anslen = 0;
2N/A ds->ansmax = 0;
2N/A nextincr = 2048;
2N/A maxincr = INT_MAX;
2N/A
2N/A#if HAVE_NS_INITPARSE
2N/A ds->cur_ans = 0;
2N/A#endif
2N/A
2N/A#if USE_RES_NINIT
2N/A memset(&statbuf, 0, sizeof(statbuf));
2N/A ret = res_ninit(&statbuf);
2N/A#else
2N/A ret = res_init();
2N/A#endif
2N/A if (ret < 0)
2N/A return -1;
2N/A
2N/A do {
2N/A p = (ds->ansp == NULL)
2N/A ? malloc(nextincr) : realloc(ds->ansp, nextincr);
2N/A
2N/A if (p == NULL) {
2N/A ret = -1;
2N/A goto errout;
2N/A }
2N/A ds->ansp = p;
2N/A ds->ansmax = nextincr;
2N/A
2N/A#if USE_RES_NINIT
2N/A len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
2N/A ds->ansp, ds->ansmax);
2N/A#else
2N/A len = res_search(host, ds->nclass, ds->ntype,
2N/A ds->ansp, ds->ansmax);
2N/A#endif
2N/A if ((size_t) len > maxincr) {
2N/A ret = -1;
2N/A goto errout;
2N/A }
2N/A while (nextincr < (size_t) len)
2N/A nextincr *= 2;
2N/A if (len < 0 || nextincr > maxincr) {
2N/A ret = -1;
2N/A goto errout;
2N/A }
2N/A } while (len > ds->ansmax);
2N/A
2N/A ds->anslen = len;
2N/A#if HAVE_NS_INITPARSE
2N/A ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
2N/A#else
2N/A ret = initparse(ds);
2N/A#endif
2N/A if (ret < 0)
2N/A goto errout;
2N/A
2N/A ret = 0;
2N/A
2N/Aerrout:
2N/A#if USE_RES_NINIT
2N/A res_ndestroy(&statbuf);
2N/A#endif
2N/A if (ret < 0) {
2N/A if (ds->ansp != NULL) {
2N/A free(ds->ansp);
2N/A ds->ansp = NULL;
2N/A }
2N/A }
2N/A
2N/A return ret;
2N/A}
2N/A
2N/A#if HAVE_NS_INITPARSE
2N/A/*
2N/A * krb5int_dns_nextans - get next matching answer record
2N/A *
2N/A * Sets pp to NULL if no more records. Returns -1 on error, 0 on
2N/A * success.
2N/A */
2N/Aint
2N/Akrb5int_dns_nextans(struct krb5int_dns_state *ds,
2N/A const unsigned char **pp, int *lenp)
2N/A{
2N/A int len;
2N/A ns_rr rr;
2N/A
2N/A *pp = NULL;
2N/A *lenp = 0;
2N/A while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
2N/A len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
2N/A if (len < 0)
2N/A return -1;
2N/A ds->cur_ans++;
2N/A if (ds->nclass == ns_rr_class(rr)
2N/A && ds->ntype == ns_rr_type(rr)) {
2N/A *pp = ns_rr_rdata(rr);
2N/A *lenp = ns_rr_rdlen(rr);
2N/A return 0;
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A#endif
2N/A
2N/A/*
2N/A * krb5int_dns_expand - wrapper for dn_expand()
2N/A */
2N/Aint
2N/Akrb5int_dns_expand(struct krb5int_dns_state *ds, const unsigned char *p,
2N/A char *buf, int len)
2N/A{
2N/A
2N/A#if HAVE_NS_NAME_UNCOMPRESS
2N/A return ns_name_uncompress(ds->ansp,
2N/A (unsigned char *)ds->ansp + ds->anslen,
2N/A p, buf, (size_t)len);
2N/A#else
2N/A return dn_expand(ds->ansp,
2N/A (unsigned char *)ds->ansp + ds->anslen,
2N/A p, buf, len);
2N/A#endif
2N/A}
2N/A
2N/A/*
2N/A * Free stuff.
2N/A */
2N/Avoid
2N/Akrb5int_dns_fini(struct krb5int_dns_state *ds)
2N/A{
2N/A if (ds == NULL)
2N/A return;
2N/A if (ds->ansp != NULL)
2N/A free(ds->ansp);
2N/A free(ds);
2N/A}
2N/A
2N/A/*
2N/A * Compat routines for BIND 4
2N/A */
2N/A#if !HAVE_NS_INITPARSE
2N/A
2N/A/*
2N/A * initparse
2N/A *
2N/A * Skip header and question section of reply. Set a pointer to the
2N/A * beginning of the answer section, and prepare to iterate over
2N/A * answer records.
2N/A */
2N/Astatic int
2N/Ainitparse(struct krb5int_dns_state *ds)
2N/A{
2N/A HEADER *hdr;
2N/A unsigned char *p;
2N/A unsigned short nqueries, nanswers;
2N/A int len;
2N/A#if !HAVE_DN_SKIPNAME
2N/A char host[MAXDNAME];
2N/A#endif
2N/A
2N/A if ((size_t) ds->anslen < sizeof(HEADER))
2N/A return -1;
2N/A
2N/A hdr = (HEADER *)ds->ansp;
2N/A p = ds->ansp;
2N/A nqueries = ntohs((unsigned short)hdr->qdcount);
2N/A nanswers = ntohs((unsigned short)hdr->ancount);
2N/A p += sizeof(HEADER);
2N/A
2N/A /*
2N/A * Skip query records.
2N/A */
2N/A while (nqueries--) {
2N/A#if HAVE_DN_SKIPNAME
2N/A len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
2N/A#else
2N/A len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
2N/A p, host, sizeof(host));
2N/A#endif
2N/A if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4))
2N/A return -1;
2N/A p += len + 4;
2N/A }
2N/A ds->ptr = p;
2N/A ds->nanswers = nanswers;
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * krb5int_dns_nextans() - get next answer record
2N/A *
2N/A * Sets pp to NULL if no more records.
2N/A */
2N/Aint
2N/Akrb5int_dns_nextans(struct krb5int_dns_state *ds,
2N/A const unsigned char **pp, int *lenp)
2N/A{
2N/A int len;
2N/A unsigned char *p;
2N/A unsigned short ntype, nclass, rdlen;
2N/A#if !HAVE_DN_SKIPNAME
2N/A char host[MAXDNAME];
2N/A#endif
2N/A
2N/A *pp = NULL;
2N/A *lenp = 0;
2N/A p = ds->ptr;
2N/A
2N/A while (ds->nanswers--) {
2N/A#if HAVE_DN_SKIPNAME
2N/A len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
2N/A#else
2N/A len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
2N/A p, host, sizeof(host));
2N/A#endif
2N/A if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
2N/A return -1;
2N/A p += len;
2N/A SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
2N/A /* Also skip 4 bytes of TTL */
2N/A SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
2N/A SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
2N/A
2N/A if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
2N/A return -1;
2N/A/* Solaris Kerberos - resync */
2N/A#if 0
2N/A if (rdlen > INT_MAX)
2N/A return -1;
2N/A#endif
2N/A if (nclass == ds->nclass && ntype == ds->ntype) {
2N/A *pp = p;
2N/A *lenp = rdlen;
2N/A ds->ptr = p + rdlen;
2N/A return 0;
2N/A }
2N/A p += rdlen;
2N/A }
2N/A return 0;
2N/Aout:
2N/A return -1;
2N/A}
2N/A
2N/A#endif
2N/A
2N/A/*
2N/A * Try to look up a TXT record pointing to a Kerberos realm
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm)
2N/A{
2N/A krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN;
2N/A const unsigned char *p, *base;
2N/A char host[MAXDNAME];
2N/A int ret, rdlen, len;
2N/A struct krb5int_dns_state *ds = NULL;
2N/A struct k5buf buf;
2N/A
2N/A /*
2N/A * Form our query, and send it via DNS
2N/A */
2N/A
2N/A krb5int_buf_init_fixed(&buf, host, sizeof(host));
2N/A if (name == NULL || name[0] == '\0') {
2N/A krb5int_buf_add(&buf, prefix);
2N/A } else {
2N/A krb5int_buf_add_fmt(&buf, "%s.%s", prefix, name);
2N/A
2N/A /* Realm names don't (normally) end with ".", but if the query
2N/A doesn't end with "." and doesn't get an answer as is, the
2N/A resolv code will try appending the local domain. Since the
2N/A realm names are absolutes, let's stop that.
2N/A
2N/A But only if a name has been specified. If we are performing
2N/A a search on the prefix alone then the intention is to allow
2N/A the local domain or domain search lists to be expanded.
2N/A */
2N/A
2N/A len = krb5int_buf_len(&buf);
2N/A if (len > 0 && host[len - 1] != '.')
2N/A krb5int_buf_add(&buf, ".");
2N/A }
2N/A if (krb5int_buf_data(&buf) == NULL)
2N/A return KRB5_ERR_HOST_REALM_UNKNOWN;
2N/A ret = krb5int_dns_init(&ds, host, C_IN, T_TXT);
2N/A if (ret < 0)
2N/A goto errout;
2N/A
2N/A ret = krb5int_dns_nextans(ds, &base, &rdlen);
2N/A if (ret < 0 || base == NULL)
2N/A goto errout;
2N/A
2N/A p = base;
2N/A if (!INCR_OK(base, rdlen, p, 1))
2N/A goto errout;
2N/A len = *p++;
2N/A *realm = malloc((size_t)len + 1);
2N/A if (*realm == NULL) {
2N/A retval = ENOMEM;
2N/A goto errout;
2N/A }
2N/A strncpy(*realm, (const char *)p, (size_t)len);
2N/A (*realm)[len] = '\0';
2N/A /* Avoid a common error. */
2N/A if ( (*realm)[len-1] == '.' )
2N/A (*realm)[len-1] = '\0';
2N/A retval = 0;
2N/A
2N/Aerrout:
2N/A if (ds != NULL) {
2N/A krb5int_dns_fini(ds);
2N/A ds = NULL;
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/A#endif /* KRB5_DNS_LOOKUP */