From 54ba1d03b1c26b25f6c23fdd0c7ba58c7d9e2066 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Tue, 21 Sep 2004 18:06:56 +0000 Subject: Add DNS resolver glue layer. Use it ticket: 2710 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16769 dc483132-0cff-0310-8789-dd5450dbe970 --- src/ChangeLog | 11 ++ src/aclocal.m4 | 83 ++++++------ src/include/ChangeLog | 4 + src/include/configure.in | 3 - src/lib/krb5/os/ChangeLog | 16 +++ src/lib/krb5/os/Makefile.in | 3 + src/lib/krb5/os/dnsglue.c | 299 ++++++++++++++++++++++++++++++++++++++++++++ src/lib/krb5/os/dnsglue.h | 148 ++++++++++++++++++++++ src/lib/krb5/os/dnssrv.c | 244 +++++++++--------------------------- src/lib/krb5/os/hst_realm.c | 182 ++++++--------------------- 10 files changed, 615 insertions(+), 378 deletions(-) create mode 100644 src/lib/krb5/os/dnsglue.c create mode 100644 src/lib/krb5/os/dnsglue.h (limited to 'src') diff --git a/src/ChangeLog b/src/ChangeLog index 82ae263..54a49cb 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2004-09-21 Tom Yu + + * aclocal.m4 (KRB5_AC_NEED_BIND_8_COMPAT): Remove. + (AC_LIBRARY_NET): Remove KRB5_AC_NEED_BIND_8_COMPAT. Call + _KRB5_AC_CHECK_RES_FUNCS to check declarations and linkability of + vairous resolver functions. Explicitly check linkability of + res_search() in case it's not explicitly declared. + (_KRB5_AC_CHECK_RES_FUNCS, _KRB5_AC_CHECK_RES_FUNC): New + functions. Check resolver library function prototypes and + linkability. + 2004-09-17 Tom Yu * Makefile.in (install-unix): Use $(INSTALL_SCRIPT) for scripts. diff --git a/src/aclocal.m4 b/src/aclocal.m4 index 04e0f8b..c3acf0b 100644 --- a/src/aclocal.m4 +++ b/src/aclocal.m4 @@ -1335,7 +1335,6 @@ dnl The check for libresolv is in case you are attempting to link statically dnl and happen to have a libresolv.a lying around (and no libnsl.a). dnl AC_DEFUN(AC_LIBRARY_NET, [ -AC_REQUIRE([KRB5_AC_NEED_BIND_8_COMPAT]) # Most operating systems have gethostbyname() in the default searched # libraries (i.e. libc): AC_CHECK_FUNC(gethostbyname, , [ @@ -1361,36 +1360,44 @@ AC_REQUIRE([KRB5_AC_NEED_BIND_8_COMPAT]) # We assume that if libresolv exists we can link against it. # This may get us a gethostby* that doesn't respect nsswitch. AC_CHECK_LIB(resolv, main) - krb5_res_search_found=no - AC_CHECK_DECL(res_nsearch, - [AC_DEFINE(HAVE_RES_NSEARCH, 1, [Have the res_nsearch function]) -krb5_res_search_found=yes], , -[[#include -#include -#include -#include -]]) - if test $krb5_res_search_found != yes; then - AC_CHECK_DECL(res_search, - [AC_DEFINE(HAVE_RES_SEARCH, 1, [Have the res_search function]) -krb5_res_search_found=yes], , -[[#include -#include -#include -#include -]]) +_KRB5_AC_CHECK_RES_FUNCS(res_nsearch res_search ns_name_uncompress dn_skipname) + if test $krb5_cv_func_res_nsearch = no \ + && test $krb5_cv_func_res_search = no; then + # Attempt to link with res_search(), in case it's not prototyped. + AC_CHECK_FUNC(res_search, + [AC_DEFINE(HAVE_RES_SEARCH, 1, + [Define to 1 if you have the `res_search' function])], + [AC_ERROR([cannot find res_nsearch or res_search])]) fi - if test $krb5_res_search_found != yes; then - AC_CHECK_FUNC(res_search, - [AC_DEFINE(HAVE_RES_SEARCH, 1, [Have the res_search function])], - [AC_MSG_ERROR(Failed to find resolver search routine)], -[[#include + fi +]) +AC_DEFUN([_KRB5_AC_CHECK_RES_FUNCS], +[AC_FOREACH([AC_Func], [$1], + [AH_TEMPLATE(AS_TR_CPP(HAVE_[]AC_Func), + [Define to 1 if you have the `]AC_Func[' function.])])dnl +for krb5_func in $1; do +_KRB5_AC_CHECK_RES_FUNC($krb5_func) +done +]) +AC_DEFUN([_KRB5_AC_CHECK_RES_FUNC], [ +# Solaris 9 prototypes ns_name_uncompress() in arpa/nameser.h, but +# doesn't export it from libresolv.so, so we use extreme paranoia here +# and check both for the declaration and that we can link against the +# function. +AC_CACHE_CHECK([for $1], [krb5_cv_func_$1], [AC_TRY_LINK( +[#include #include #include -#include -]]) - fi - fi +@%:@include ], +[/* + * Use volatile, or else optimization can cause false positives. + */ +void (* volatile p)() = (void (*)())$1;], + [AS_VAR_SET(krb5_cv_func_$1, yes)], + [AS_VAR_SET(krb5_cv_func_$1, no)])]) +AS_IF([test AS_VAR_GET(krb5_cv_func_$1) = yes], + [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), 1, + [Define to 1 if you have the `$1' function])])[]dnl ]) dnl dnl @@ -1606,26 +1613,6 @@ AC_SUBST(DB_LIB) AC_SUBST(KDB5_DB_LIB) ]) dnl -dnl -dnl KRB5_AC_NEED_BIND_8_COMPAT --- check to see if we are on a bind 9 system -dnl -dnl -AC_DEFUN(KRB5_AC_NEED_BIND_8_COMPAT,[ -AC_REQUIRE([AC_PROG_CC])dnl -dnl -dnl On a bind 9 system, we need to define BIND_8_COMPAT -dnl -AC_MSG_CHECKING(for bind 9 or higher) -AC_CACHE_VAL(krb5_cv_need_bind_8_compat,[ -AC_TRY_COMPILE([#include ], [HEADER hdr;], -krb5_cv_need_bind_8_compat=no, -[AC_TRY_COMPILE([#define BIND_8_COMPAT -#include ], [HEADER hdr;], -krb5_cv_need_bind_8_compat=yes, krb5_cv_need_bind_8_compat=no)])]) -AC_MSG_RESULT($krb5_cv_need_bind_8_compat) -test $krb5_cv_need_bind_8_compat = yes && AC_DEFINE(BIND_8_COMPAT,1,[Define if OS has bind 9]) -]) -dnl dnl KRB5_AC_PRIOCNTL_HACK dnl dnl diff --git a/src/include/ChangeLog b/src/include/ChangeLog index f87b186..f21974d 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -1,3 +1,7 @@ +2004-09-21 Tom Yu + + * configure.in: Remove KRB5_AC_NEED_BIND_8_COMPAT. + 2004-09-15 Tom Yu * configure.in: Check for h_errno declaration in netdb.h. diff --git a/src/include/configure.in b/src/include/configure.in index 277f206..5c41810 100644 --- a/src/include/configure.in +++ b/src/include/configure.in @@ -255,9 +255,6 @@ if test $krb5_cv_header_netdb_h_h_errno = yes; then fi dnl dnl -KRB5_AC_NEED_BIND_8_COMPAT -dnl -dnl AC_ARG_ENABLE([athena], [ --enable-athena build with MIT Project Athena configuration], AC_DEFINE(KRB5_ATHENA_COMPAT,1,[Define if MIT Project Athena default configuration should be used]),) diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index 2666431..903bf58 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -1,3 +1,19 @@ +2004-09-20 Tom Yu + + * Makefile.in (STLIBOBJS, OBJS, SRCS): Add dnsglue.c. + + * dnsglue.c: New file. Implement resolver glue layer to abstract + away the details of calling res_search or res_nsearch, and of + parsing the reply packet. + + * dnsglue.h: New file. + + * dnssrv.c (krb5int_make_srv_query_realm): Use dnsglue. Use + MAXDNAME from dnsglue.h or resolv.h instead of MAX_DNS_NAMELEN. + + * hst_realm.c (krb5_try_realm_txt_rr): Use dnsglue. Use MAXDNAME + from dnsglue.h or resolv.h instead of MAX_DNS_NAMELEN. + 2004-09-13 Tom Yu * dnssrv.c: diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in index bcf6774..8db0b75 100644 --- a/src/lib/krb5/os/Makefile.in +++ b/src/lib/krb5/os/Makefile.in @@ -17,6 +17,7 @@ STLIBOBJS= \ def_realm.o \ ccdefname.o \ changepw.o \ + dnsglue.o \ dnssrv.o \ free_krbhs.o \ free_hstrl.o \ @@ -61,6 +62,7 @@ OBJS= \ $(OUTPRE)def_realm.$(OBJEXT) \ $(OUTPRE)ccdefname.$(OBJEXT) \ $(OUTPRE)changepw.$(OBJEXT) \ + $(OUTPRE)dnsglue.$(OBJEXT) \ $(OUTPRE)dnssrv.$(OBJEXT) \ $(OUTPRE)free_krbhs.$(OBJEXT) \ $(OUTPRE)free_hstrl.$(OBJEXT) \ @@ -105,6 +107,7 @@ SRCS= \ $(srcdir)/def_realm.c \ $(srcdir)/ccdefname.c \ $(srcdir)/changepw.c \ + $(srcdir)/dnsglue.c \ $(srcdir)/dnssrv.c \ $(srcdir)/free_krbhs.c \ $(srcdir)/free_hstrl.c \ diff --git a/src/lib/krb5/os/dnsglue.c b/src/lib/krb5/os/dnsglue.c new file mode 100644 index 0000000..352134b --- /dev/null +++ b/src/lib/krb5/os/dnsglue.c @@ -0,0 +1,299 @@ +/* + * lib/krb5/os/dnsglue.c + * + * Copyright 2004 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ +#ifdef KRB5_DNS_LOOKUP + +#include "dnsglue.h" + +/* + * Opaque handle + */ +struct krb5int_dns_state { + int nclass; + int ntype; + void *ansp; + int anslen; + int ansmax; +#if HAVE_RES_NSEARCH + int cur_ans; + ns_msg msg; +#else + unsigned char *ptr; + unsigned short nanswers; +#endif +}; + +#if !HAVE_RES_NSEARCH +static int initparse(struct krb5int_dns_state *); +#endif + +/* + * krb5int_dns_init() + * + * Initialize an opaue handl. Do name lookup and initial parsing of + * reply, skipping question section. Prepare to iterate over answer + * section. Returns -1 on error, 0 on success. + */ +int +krb5int_dns_init(struct krb5int_dns_state **dsp, + char *host, int nclass, int ntype) +{ +#if HAVE_RES_NSEARCH + struct __res_state statbuf; +#endif + struct krb5int_dns_state *ds; + int len; + size_t nextincr, maxincr; + unsigned char *p; + + *dsp = ds = malloc(sizeof(*ds)); + if (ds == NULL) + return -1; + + ds->nclass = nclass; + ds->ntype = ntype; + ds->ansp = NULL; + ds->anslen = 0; + ds->ansmax = 0; + nextincr = 2048; + maxincr = INT_MAX; + +#if HAVE_RES_NSEARCH + ds->cur_ans = 0; + len = res_ninit(&statbuf); + if (len < 0) + return -1; +#endif + + do { + p = (ds->ansp == NULL) + ? malloc(nextincr) : realloc(ds->ansp, nextincr); + + if (p == NULL && ds->ansp != NULL) { + free(ds->ansp); + return -1; + } + ds->ansp = p; + ds->ansmax = nextincr; + +#if HAVE_RES_NSEARCH + len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype, + ds->ansp, ds->ansmax); +#else + len = res_search(host, ds->nclass, ds->ntype, + ds->ansp, ds->ansmax); +#endif + if (len > maxincr) + return -1; + while (nextincr < len) + nextincr *= 2; + if (len < 0 || nextincr > maxincr) { + free(ds->ansp); + return -1; + } + } while (len > ds->ansmax); + + ds->anslen = len; +#if HAVE_RES_NSEARCH + len = ns_initparse(ds->ansp, ds->anslen, &ds->msg); +#else + len = initparse(ds); +#endif + if (len < 0) { + free(ds->ansp); + return -1; + } + + return 0; +} + +#if HAVE_RES_NSEARCH +/* + * krb5int_dns_nextans - get next matching answer record + * + * Sets pp to NULL if no more records. Returns -1 on error, 0 on + * success. + */ +int +krb5int_dns_nextans(struct krb5int_dns_state *ds, + const unsigned char **pp, int *lenp) +{ + int len; + ns_rr rr; + + *pp = NULL; + *lenp = 0; + while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) { + len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr); + if (len < 0) + return -1; + ds->cur_ans++; + if (ds->nclass == ns_rr_class(rr) + && ds->ntype == ns_rr_type(rr)) { + *pp = ns_rr_rdata(rr); + *lenp = ns_rr_rdlen(rr); + return 0; + } + } + return 0; +} +#endif + +/* + * krb5int_dns_expand - wrapper for dn_expand() + */ +int krb5int_dns_expand(struct krb5int_dns_state *ds, + const unsigned char *p, + char *buf, int len) +{ + +#if HAVE_NS_NAME_UNCOMPRESS + return ns_name_uncompress(ds->ansp, + (unsigned char *)ds->ansp + ds->anslen, + p, buf, (size_t)len); +#else + return dn_expand(ds->ansp, + (unsigned char *)ds->ansp + ds->anslen, + p, buf, len); +#endif +} + +/* + * Free stuff. + */ +void +krb5int_dns_fini(struct krb5int_dns_state *ds) +{ + if (ds->ansp != NULL) + free(ds->ansp); + if (ds != NULL) + free(ds); +} + +/* + * Compat routines for BIND 4 + */ +#if !HAVE_RES_NSEARCH + +/* + * initparse + * + * Skip header and question section of reply. Set a pointer to the + * beginning of the answer section, and prepare to iterate over + * answer records. + */ +static int +initparse(struct krb5int_dns_state *ds) +{ + HEADER *hdr; + unsigned char *p; + unsigned short nqueries, nanswers; + int len; +#if !HAVE_DN_SKIPNAME + char host[MAXDNAME]; +#endif + + if (ds->anslen < sizeof(HEADER)) + return -1; + + hdr = (HEADER *)ds->ansp; + p = ds->ansp; + nqueries = ntohs((unsigned short)hdr->qdcount); + nanswers = ntohs((unsigned short)hdr->ancount); + p += sizeof(HEADER); + + /* + * Skip query records. + */ + while (nqueries--) { +#if HAVE_DN_SKIPNAME + len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); +#else + len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, + p, host, sizeof(host)); +#endif + if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len)) + return -1; + p += len; + } + ds->ptr = p; + ds->nanswers = nanswers; + return 0; +} + +/* + * krb5int_dns_nextans() - get next answer record + * + * Sets pp to NULL if no more records. + */ +int +krb5int_dns_nextans(struct krb5int_dns_state *ds, + const unsigned char **pp, int *lenp) +{ + int len; + unsigned char *p; + unsigned short ntype, nclass, rdlen; +#if !HAVE_DN_SKIPNAME + char host[MAXDNAME]; +#endif + + *pp = NULL; + *lenp = 0; + p = ds->ptr; + + while (ds->nanswers--) { +#if HAVE_DN_SKIPNAME + len = dn_skipname(ds->ansp, (unsigned char *)ds->ansp + ds->anslen); +#else + len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, + p, host, sizeof(host)); +#endif + if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len)) + return -1; + SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out); + /* Also skip 4 bytes of TTL */ + SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out); + SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out); + + if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen)) + return -1; + if (rdlen > INT_MAX) + return -1; + if (nclass == ds->nclass && ntype == ds->ntype) { + *pp = p; + *lenp = rdlen; + ds->ptr = p + rdlen; + return 0; + } + } + return 0; +out: + return -1; +} + +#endif + +#endif /* KRB5_DNS_LOOKUP */ diff --git a/src/lib/krb5/os/dnsglue.h b/src/lib/krb5/os/dnsglue.h new file mode 100644 index 0000000..d2d1927 --- /dev/null +++ b/src/lib/krb5/os/dnsglue.h @@ -0,0 +1,148 @@ +/* + * lib/krb5/os/dnsglue.h + * + * Copyright 2004 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * Glue layer for DNS resolver, to make parsing of replies easier + * whether we are using BIND 4, 8, or 9. + */ + +/* + * BIND 4 doesn't have the ns_initparse() API, so we need to do some + * manual parsing via the HEADER struct. BIND 8 does have + * ns_initparse(), but has enums for the various protocol constants + * rather than the BIND 4 macros. BIND 9 (at least on Mac OS X + * Panther) appears to disable res_nsearch() if BIND_8_COMPAT is + * defined (which is necessary to obtain the HEADER struct). + * + * We use ns_initparse() if available at all, and never define + * BIND_8_COMPAT. If there is no ns_initparse(), we do manual parsing + * by using the HEADER struct. + */ + +#ifndef KRB5_DNSGLUE_H +#define KRB5_DNSGLUE_H + +#ifdef KRB5_DNS_LOOKUP + +#define NEED_SOCKETS +#include "k5-int.h" +#include "os-proto.h" +#ifdef WSHELPER +#include +#else /* WSHELPER */ +#include +#include +#include +#include +#include +#endif /* WSHELPER */ + +#if HAVE_SYS_PARAM_H +#include /* for MAXHOSTNAMELEN */ +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 /* if we can't find it elswhere */ +#endif + +#ifndef MAXDNAME + +#ifdef NS_MAXDNAME +#define MAXDNAME NS_MAXDNAME +#else +#ifdef MAXLABEL +#define MAXDNAME (16 * MAXLABEL) +#else +#define MAXDNAME (16 * MAXHOSTNAMELEN) +#endif +#endif + +#endif + +#if HAVE_RES_NSEARCH +/* + * Some BIND 8 / BIND 9 implementations disable the BIND 4 style + * constants. + */ +#ifndef C_IN +#define C_IN ns_c_in +#endif +#ifndef T_SRV +#define T_SRV ns_t_srv +#endif +#ifndef T_TXT +#define T_TXT ns_t_txt +#endif + +#else /* !HAVE_RES_NSEARCH */ + +/* + * Some BIND implementations might be old enough to lack these. + */ +#ifndef T_TXT +#define T_TXT 15 +#endif +#ifndef T_SRV +#define T_SRV 33 +#endif + +#endif /* HAVE_RES_NSEARCH */ + +/* + * INCR_OK + * + * Given moving pointer PTR offset from BASE, return true if adding + * INCR to PTR doesn't move it PTR than MAX bytes from BASE. + */ +#define INCR_OK(base, max, ptr, incr) \ + ((incr) <= (max) - ((const unsigned char *)(ptr) \ + - (const unsigned char *)(base))) + +/* + * SAFE_GETUINT16 + * + * Given PTR offset from BASE, if at least INCR bytes are safe to + * read, get network byte order uint16 into S, and increment PTR. On + * failure, goto LABEL. + */ + +#define SAFE_GETUINT16(base, max, ptr, incr, s, label) \ + do { \ + if (!INCR_OK(base, max, ptr, incr)) goto label; \ + (s) = (unsigned short)(p)[0] << 8 \ + | (unsigned short)(p)[1]; \ + (p) += (incr); \ + } while (0) + +struct krb5int_dns_state; + +int krb5int_dns_init(struct krb5int_dns_state **, char *, int, int); +int krb5int_dns_nextans(struct krb5int_dns_state *, + const unsigned char **, int *); +int krb5int_dns_expand(struct krb5int_dns_state *, + const unsigned char *, char *, int); +void krb5int_dns_fini(struct krb5int_dns_state *); + +#endif /* KRB5_DNS_LOOKUP */ +#endif /* !defined(KRB5_DNSGLUE_H) */ diff --git a/src/lib/krb5/os/dnssrv.c b/src/lib/krb5/os/dnssrv.c index 32851d2..e0c5930 100644 --- a/src/lib/krb5/os/dnssrv.c +++ b/src/lib/krb5/os/dnssrv.c @@ -28,29 +28,8 @@ */ #ifdef KRB5_DNS_LOOKUP -#define NEED_SOCKETS -#include "k5-int.h" -#include "os-proto.h" -#include -#ifdef WSHELPER -#include -#else /* WSHELPER */ -#include -#include -#include -#include -#include -#endif /* WSHELPER */ -#ifndef T_SRV -#define T_SRV 33 -#endif /* T_SRV */ -/* for old Unixes and friends ... */ -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 64 -#endif - -#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) +#include "dnsglue.h" /* * Lookup a KDC via DNS SRV records @@ -79,16 +58,11 @@ krb5int_make_srv_query_realm(const krb5_data *realm, const char *protocol, struct srv_dns_entry **answers) { - union { - unsigned char bytes[2048]; - HEADER hdr; - } answer; - unsigned char *p=NULL; - char host[MAX_DNS_NAMELEN], *h; - int type, rrclass; - int priority, weight, size, len, numanswers, numqueries, rdlen; - unsigned short port; - const int hdrsize = sizeof(HEADER); + const unsigned char *p = NULL, *base = NULL; + char host[MAXDNAME], *h; + int size, ret, rdlen, nlen; + unsigned short priority, weight, port; + struct krb5int_dns_state *ds = NULL; struct srv_dns_entry *head = NULL; struct srv_dns_entry *srv = NULL, *entry = NULL; @@ -107,7 +81,7 @@ krb5int_make_srv_query_realm(const krb5_data *realm, if (memchr(realm->data, 0, realm->length)) return 0; if ( strlen(service) + strlen(protocol) + realm->length + 6 - > MAX_DNS_NAMELEN ) + > MAXDNAME ) return 0; sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length, realm->data); @@ -129,173 +103,77 @@ krb5int_make_srv_query_realm(const krb5_data *realm, fprintf (stderr, "sending DNS SRV query for %s\n", host); #endif -#ifdef HAVE_RES_NSEARCH - { - res_state statp; - /* Weird... the man pages I've been looking at (Solaris 9) say - we pass a res_state object (which is a pointer) into - various routines, but they don't say much of anything about - what it should point to initially or how it should be - allocated. - - They also give no indication what the return value of - res_ninit is. */ - typedef union { - struct sockaddr_storage ss; - INT64_TYPE i64; - double d; - } aligned_thing; - aligned_thing statp_buf[(sizeof(*statp) + sizeof(aligned_thing) - 1) / sizeof(aligned_thing)]; - int n; - - statp = (res_state) &statp_buf; - memset(&statp_buf, 0, sizeof(statp_buf)); - n = res_ninit(statp); - /* ignore n? */ - size = res_nsearch(statp, host, C_IN, T_SRV, - answer.bytes, sizeof(answer.bytes)); - } -#else - size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes)); -#endif - - if ((size < hdrsize) || (size > sizeof(answer.bytes))) + size = krb5int_dns_init(&ds, host, C_IN, T_SRV); + if (size < 0) goto out; - /* - * We got an answer! First off, parse the header and figure out how - * many answers we got back. - */ - - p = answer.bytes; - - numqueries = ntohs(answer.hdr.qdcount); - numanswers = ntohs(answer.hdr.ancount); - - p += sizeof(HEADER); - - /* - * We need to skip over all of the questions, so we have to iterate - * over every query record. dn_expand() is able to tell us the size - * of compress DNS names, so we use it. - */ - -#define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out -#define CHECK(x,y) if (x + y > size + answer.bytes) goto out -#define NTOHSP(x,y) x[0] << 8 | x[1]; x += y - - while (numqueries--) { - len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host)); - if (len < 0) + for (;;) { + ret = krb5int_dns_nextans(ds, &base, &rdlen); + if (ret < 0 || base == NULL) goto out; - INCR_CHECK(p, len + 4); - } - /* - * We're now pointing at the answer records. Only process them if - * they're actually T_SRV records (they might be CNAME records, - * for instance). - * - * But in a DNS reply, if you get a CNAME you always get the associated - * "real" RR for that CNAME. RFC 1034, 3.6.2: - * - * CNAME RRs cause special action in DNS software. When a name server - * fails to find a desired RR in the resource set associated with the - * domain name, it checks to see if the resource set consists of a CNAME - * record with a matching class. If so, the name server includes the CNAME - * record in the response and restarts the query at the domain name - * specified in the data field of the CNAME record. The one exception to - * this rule is that queries which match the CNAME type are not restarted. - * - * In other words, CNAMEs do not need to be expanded by the client. - */ + p = base; - while (numanswers--) { + SAFE_GETUINT16(base, rdlen, p, 2, priority, out); + SAFE_GETUINT16(base, rdlen, p, 2, weight, out); + SAFE_GETUINT16(base, rdlen, p, 2, port, out); - /* First is the name; use dn_expand to get the compressed size */ - len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host)); - if (len < 0) + /* + * RFC 2782 says the target is never compressed in the reply; + * do we believe that? We need to flatten it anyway, though. + */ + nlen = krb5int_dns_expand(ds, p, host, sizeof(host)); + if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen)) goto out; - INCR_CHECK(p, len); - - /* Next is the query type */ - CHECK(p, 2); - type = NTOHSP(p,2); - - /* Next is the query class; also skip over 4 byte TTL */ - CHECK(p, 6); - rrclass = NTOHSP(p,6); - - /* Record data length */ - - CHECK(p,2); - rdlen = NTOHSP(p,2); /* - * If this is an SRV record, process it. Record format is: - * - * Priority - * Weight - * Port - * Server name + * We got everything! Insert it into our list, but make sure + * it's in the right order. Right now we don't do anything + * with the weight field */ - if (rrclass == C_IN && type == T_SRV) { - CHECK(p,2); - priority = NTOHSP(p,2); - CHECK(p, 2); - weight = NTOHSP(p,2); - CHECK(p, 2); - port = NTOHSP(p,2); - len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host)); - if (len < 0) - goto out; - INCR_CHECK(p, len); + srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry)); + if (srv == NULL) + goto out; + + srv->priority = priority; + srv->weight = weight; + srv->port = port; + srv->host = strdup(host); + if (srv->host == NULL) { + free(srv); + goto out; + } + if (head == NULL || head->priority > srv->priority) { + srv->next = head; + head = srv; + } else { /* - * We got everything! Insert it into our list, but make sure - * it's in the right order. Right now we don't do anything - * with the weight field + * This is confusing. Only insert an entry into this + * spot if: + * The next person has a higher priority (lower priorities + * are preferred). + * Or + * There is no next entry (we're at the end) */ - - srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry)); - if (srv == NULL) - goto out; - - srv->priority = priority; - srv->weight = weight; - srv->port = port; - srv->host = strdup(host); - if (srv->host == NULL) { - free(srv); - goto out; + for (entry = head; entry != NULL; entry = entry->next) { + if ((entry->next && + entry->next->priority > srv->priority) || + entry->next == NULL) { + srv->next = entry->next; + entry->next = srv; + break; + } } + } + } - if (head == NULL || head->priority > srv->priority) { - srv->next = head; - head = srv; - } else - /* - * This is confusing. Only insert an entry into this - * spot if: - * The next person has a higher priority (lower priorities - * are preferred). - * Or - * There is no next entry (we're at the end) - */ - for (entry = head; entry != NULL; entry = entry->next) - if ((entry->next && - entry->next->priority > srv->priority) || - entry->next == NULL) { - srv->next = entry->next; - entry->next = srv; - break; - } - } else - INCR_CHECK(p, rdlen); +out: + if (ds != NULL) { + krb5int_dns_fini(ds); + ds = NULL; } - - out: *answers = head; return 0; } diff --git a/src/lib/krb5/os/hst_realm.c b/src/lib/krb5/os/hst_realm.c index be538a0..574fe70 100644 --- a/src/lib/krb5/os/hst_realm.c +++ b/src/lib/krb5/os/hst_realm.c @@ -65,6 +65,8 @@ * host names should be in the usual form (e.g. FOO.BAR.BAZ) */ +#include "dnsglue.h" + #define NEED_SOCKETS #include "k5-int.h" #include "os-proto.h" @@ -76,30 +78,8 @@ #include #endif -#ifdef KRB5_DNS_LOOKUP -#ifdef WSHELPER -#include -#else /* WSHELPER */ -#include -#include -#include -#ifndef T_TXT /* not defined on SunOS 4 */ -# define T_TXT 15 -#endif -#include -#include -#endif /* WSHELPER */ -#endif /* KRB5_DNS_LOOKUP */ - #include "fake-addrinfo.h" -/* for old Unixes and friends ... */ -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 64 -#endif - -#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) - #ifdef KRB5_DNS_LOOKUP /* * Try to look up a TXT record pointing to a Kerberos realm @@ -108,14 +88,11 @@ krb5_error_code krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm) { - union { - unsigned char bytes[2048]; - HEADER hdr; - } answer; - unsigned char *p; - char host[MAX_DNS_NAMELEN], *h; - int size; - int type, rrclass, numanswers, numqueries, rdlen, len; + krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN; + const unsigned char *p, *base; + char host[MAXDNAME], *h; + int ret, rdlen, len; + struct krb5int_dns_state *ds = NULL; /* * Form our query, and send it via DNS @@ -126,7 +103,7 @@ krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm) return KRB5_ERR_HOST_REALM_UNKNOWN; strcpy(host,prefix); } else { - if ( strlen(prefix) + strlen(name) + 3 > MAX_DNS_NAMELEN ) + if ( strlen(prefix) + strlen(name) + 3 > MAXDNAME ) return KRB5_ERR_HOST_REALM_UNKNOWN; sprintf(host,"%s.%s", prefix, name); @@ -144,119 +121,36 @@ krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm) if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host))) strcpy (h, "."); } -#ifdef HAVE_RES_NSEARCH - { - res_state statp; - /* Weird... the man pages I've been looking at (Solaris 9) say - we pass a res_state object (which is a pointer) into - various routines, but they don't say much of anything about - what it should point to initially or how it should be - allocated. - - They also give no indication what the return value of - res_ninit is. */ - typedef union { - struct sockaddr_storage ss; - INT64_TYPE i64; - double d; - } aligned_thing; - aligned_thing statp_buf[(sizeof(*statp) + sizeof(aligned_thing) - 1) / sizeof(aligned_thing)]; - int n; - - statp = (res_state) &statp_buf; - memset(&statp_buf, 0, sizeof(statp_buf)); - n = res_ninit(statp); - /* ignore n? */ - size = res_nsearch(statp, host, C_IN, T_TXT, - answer.bytes, sizeof(answer.bytes)); + ret = krb5int_dns_init(&ds, host, C_IN, T_TXT); + if (ret < 0) + goto errout; + + ret = krb5int_dns_nextans(ds, &base, &rdlen); + if (ret < 0 || base == NULL) + goto errout; + + p = base; + if (!INCR_OK(base, rdlen, p, 1)) + goto errout; + len = *p++; + *realm = malloc((size_t)len + 1); + if (*realm == NULL) { + retval = ENOMEM; + goto errout; } -#else - size = res_search(host, C_IN, T_TXT, answer.bytes, sizeof(answer.bytes)); -#endif - - if ((size < sizeof(HEADER)) || (size > sizeof(answer.bytes))) - return KRB5_ERR_HOST_REALM_UNKNOWN; - - p = answer.bytes; - - numqueries = ntohs(answer.hdr.qdcount); - numanswers = ntohs(answer.hdr.ancount); - - p += sizeof(HEADER); - - /* - * We need to skip over the questions before we can get to the answers, - * which means we have to iterate over every query record. We use - * dn_expand to tell us how long each compressed name is. - */ - -#define INCR_CHECK(x, y) x += y; if (x > size + answer.bytes) \ - return KRB5_ERR_HOST_REALM_UNKNOWN -#define CHECK(x, y) if (x + y > size + answer.bytes) \ - return KRB5_ERR_HOST_REALM_UNKNOWN -#define NTOHSP(x, y) x[0] << 8 | x[1]; x += y - - while (numqueries--) { - len = dn_expand(answer.bytes, answer.bytes + size, p, host, - sizeof(host)); - if (len < 0) - return KRB5_ERR_HOST_REALM_UNKNOWN; - INCR_CHECK(p, len + 4); /* Name plus type plus class */ - } - - /* - * We're now pointing at the answer records. Process the first - * TXT record we find. - */ - - while (numanswers--) { - - /* First the name; use dn_expand to get the compressed size */ - len = dn_expand(answer.bytes, answer.bytes + size, p, - host, sizeof(host)); - if (len < 0) - return KRB5_ERR_HOST_REALM_UNKNOWN; - INCR_CHECK(p, len); - - /* Next is the query type */ - CHECK(p, 2); - type = NTOHSP(p,2); - - /* Next is the query class; also skip over 4 byte TTL */ - CHECK(p,6); - rrclass = NTOHSP(p,6); - - /* Record data length - make sure we aren't truncated */ - - CHECK(p,2); - rdlen = NTOHSP(p,2); - - if (p + rdlen > answer.bytes + size) - return KRB5_ERR_HOST_REALM_UNKNOWN; - - /* - * If this is a TXT record, return the string. Note that the - * string has a 1-byte length in the front - */ - /* XXX What about flagging multiple TXT records as an error? */ - - if (rrclass == C_IN && type == T_TXT) { - len = *p++; - if (p + len > answer.bytes + size) - return KRB5_ERR_HOST_REALM_UNKNOWN; - *realm = malloc(len + 1); - if (*realm == NULL) - return ENOMEM; - strncpy(*realm, (char *) p, len); - (*realm)[len] = '\0'; - /* Avoid a common error. */ - if ( (*realm)[len-1] == '.' ) - (*realm)[len-1] = '\0'; - return 0; - } + strncpy(*realm, (const char *)p, (size_t)len); + (*realm)[len] = '\0'; + /* Avoid a common error. */ + if ( (*realm)[len-1] == '.' ) + (*realm)[len-1] = '\0'; + retval = 0; + +errout: + if (ds != NULL) { + krb5int_dns_fini(ds); + ds = NULL; } - - return KRB5_ERR_HOST_REALM_UNKNOWN; + return retval; } #endif /* KRB5_DNS_LOOKUP */ @@ -301,7 +195,7 @@ krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp) char *default_realm, *realm, *cp, *temp_realm; krb5_error_code retval; int l; - char local_host[MAX_DNS_NAMELEN+1]; + char local_host[MAXDNAME+1]; if (host) { /* Filter out numeric addresses if the caller utterly failed to @@ -326,7 +220,7 @@ krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp) /* IPv6 numeric address form? Bye bye. */ return KRB5_ERR_NUMERIC_REALM; - /* Should probably error out if strlen(host) > MAX_DNS_NAMELEN. */ + /* Should probably error out if strlen(host) > MAXDNAME. */ strncpy(local_host, host, sizeof(local_host)); local_host[sizeof(local_host) - 1] = '\0'; } else { -- cgit v1.1