diff options
Diffstat (limited to 'contrib/bind/bin/named/db_sec.c')
-rw-r--r-- | contrib/bind/bin/named/db_sec.c | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/contrib/bind/bin/named/db_sec.c b/contrib/bind/bin/named/db_sec.c new file mode 100644 index 0000000..bb31fae --- /dev/null +++ b/contrib/bind/bin/named/db_sec.c @@ -0,0 +1,1097 @@ + +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: db_sec.c,v 8.30 1999/10/15 21:06:49 vixie Exp $"; +#endif /* not lint */ + +/* + * Copyright (c) 1986, 1990 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> +#include <isc/tree.h> + +#include <isc/dst.h> + +#include "port_after.h" + +#include "named.h" + +struct zpubkey { + struct dst_key *zpk_key; /* Should be DST_KEY */ + char *zpk_name; + struct zpubkey *zpk_next; +}; + +typedef struct zpubkey *zpubkey_list; + +static int nxt_match_rrset(struct databuf *dp, struct db_rrset *rrset); + +/* + * A converted databuf is a stripped down databuf after converting the + * data to wire format. + */ +struct converted_databuf { + struct converted_databuf *cd_next; + u_char *cd_data; + int cd_size, cd_alloc; +}; + +/* All of the trusted keys and zone keys */ +static tree *trusted_keys = NULL; + +static int +compare_pubkey (struct zpubkey *zpk1, struct zpubkey *zpk2) { + char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; + + if (ns_makecanon(zpk1->zpk_name, ta, sizeof ta) < 0 || + ns_makecanon(zpk2->zpk_name, tb, sizeof tb) < 0) + return (-1); + return (strcasecmp(ta, tb)); +} + +static struct zpubkey * +tree_srch_pubkey (const char *name) { + struct zpubkey tkey, *key; + + tkey.zpk_name = (char *) name; + if (trusted_keys == NULL) { + tree_init(&trusted_keys); + return (NULL); + } + key = (struct zpubkey *)tree_srch(&trusted_keys, compare_pubkey, + &tkey); + return (key); +} + +static DST_KEY * +find_public_key (const char *name, u_int16_t key_id) { + struct namebuf *knp; + struct hashbuf *htp; + struct databuf *dp; + const char *fname; + DST_KEY *key; + + ns_debug(ns_log_default, 5, "find_public_key(%s, %d)", name, key_id); + + htp = hashtab; + knp = nlookup (name, &htp, &fname, 0); + if (fname != name) + /* The name doesn't exist, so there's no key */ + return (NULL); + + for (dp = knp->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_type != ns_t_key || dp->d_secure < DB_S_SECURE) + continue; + key = dst_dnskey_to_key(name, dp->d_data, dp->d_size); + /* XXX what about multiple keys with same footprint? */ + if (key) { + if (key->dk_id == ntohs(key_id)) + return (key); + else + dst_free_key(key); + } + } + return (NULL); +} + + +static DST_KEY * +find_trusted_key (const char *name, u_int16_t key_id) { + struct zpubkey *zpk; + zpubkey_list keylist = tree_srch_pubkey (name); + + ns_debug(ns_log_default, 5, "find_trusted_key(%s, %d)", name, key_id); + + for (zpk = keylist; zpk; zpk = zpk->zpk_next) + if (zpk->zpk_key->dk_id == ntohs(key_id)) + return (zpk->zpk_key); + + return (NULL); +} + +int +add_trusted_key (const char *name, const int flags, const int proto, + const int alg, const char *str) +{ + zpubkey_list keylist; + struct zpubkey *zpk; + u_char buf[1024]; + int n; + + keylist = tree_srch_pubkey (name); + + zpk = (struct zpubkey *) memget (sizeof (struct zpubkey)); + if (zpk == NULL) + ns_panic(ns_log_default, 1, + "add_trusted_key: memget failed(%s)", name); + n = b64_pton(str, buf, sizeof(buf)); + if (n < 0) + goto failure; + zpk->zpk_key = dst_buffer_to_key(name, alg, flags, proto, buf, n); + if (zpk->zpk_key == NULL) { + ns_warning(ns_log_default, + "add_trusted_key: dst_buffer_to_key(%s) failed", + name); + goto failure; + } + zpk->zpk_name = zpk->zpk_key->dk_key_name; + zpk->zpk_next = NULL; + + if (keylist == NULL) { + if (tree_add (&trusted_keys, compare_pubkey, zpk, NULL) == NULL) + goto failure; + } + else { + struct zpubkey *tkey = keylist; + while (tkey->zpk_next) + tkey = tkey->zpk_next; + tkey->zpk_next = zpk; + } + + return (1); + failure: + memput(zpk, sizeof (struct zpubkey)); + return (0); +} + +/* Can the signer sign records for this name? This is a heuristic. */ +static int +can_sign(const char *name, const char *signer) { + return (ns_samedomain(name, signer) && + dn_count_labels(name) - dn_count_labels(signer) <= 2); +} + +static int +rrset_set_security(struct db_rrset *rrset, int slev) { + struct dnode *dnp; + + for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) + dnp->dp->d_secure = slev; + for (dnp = rrset->rr_sigs; dnp != NULL; dnp = dnp->dn_next) + dnp->dp->d_secure = slev; + return (slev); +} + +static int +convert_databuf(struct databuf *dp, struct converted_databuf *cdp) { + u_char *bp = cdp->cd_data; + u_char *cp = dp->d_data; + u_char *eob = cdp->cd_data + cdp->cd_alloc; + int len; + u_char buf[MAXDNAME]; + + switch (dp->d_type) { + case ns_t_soa: + case ns_t_minfo: + case ns_t_rp: + if (eob - bp < strlen((char *)cp) + 1) + return (-1); + if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) + return (-1); + len = ns_name_ntol(buf, bp, eob - bp); + if (len < 0) + return (-1); + bp += len; + cp += strlen((char *)cp) + 1; + + if (eob - bp < strlen((char *)cp) + 1) + return (-1); + if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) + return (-1); + len = ns_name_ntol(buf, bp, eob - bp); + if (len < 0) + return (-1); + bp += len; + cp += strlen((char *)cp) + 1; + + if (dp->d_type == ns_t_soa) { + if (eob - bp < 5 * INT32SZ) + return (-1); + memcpy(bp, cp, 5 * INT32SZ); + bp += (5 * INT32SZ); + cp += (5 * INT32SZ); + } + + break; + + case ns_t_ns: + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ptr: + case ns_t_nxt: + if (eob - bp < strlen((char *)cp) + 1) + return (-1); + if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) + return (-1); + len = ns_name_ntol(buf, bp, eob - bp); + if (len < 0) + return (-1); + bp += len; + cp += (len = strlen((char *)cp) + 1); + + if (dp->d_type == ns_t_nxt) { + if (eob - bp < dp->d_size - len) + return (-1); + memcpy(bp, cp, dp->d_size - len); + bp += (dp->d_size - len); + cp += (dp->d_size - len); + } + break; + + case ns_t_srv: + if (eob - bp < 2 * INT16SZ) + return (-1); + memcpy(bp, cp, 2 * INT16SZ); + bp += (2 * INT16SZ); + cp += (2 * INT16SZ); + /* no break */ + case ns_t_rt: + case ns_t_mx: + case ns_t_afsdb: + case ns_t_px: + if (eob - bp < INT16SZ) + return (-1); + memcpy (bp, cp, INT16SZ); + bp += INT16SZ; + cp += INT16SZ; + + if (eob - bp < strlen((char *)cp) + 1) + return (-1); + if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) + return (-1); + len = ns_name_ntol(buf, bp, eob - bp); + if (len < 0) + return (-1); + bp += len; + cp += strlen((char *)cp) + 1; + + if (dp->d_type == ns_t_px) { + if (eob - bp < strlen((char *)cp) + 1) + return (-1); + if (ns_name_pton((char *)cp, buf, sizeof buf) < 0) + return (-1); + len = ns_name_ntol(buf, bp, eob - bp); + if (len < 0) + return (-1); + bp += len; + cp += strlen((char *)cp) + 1; + } + break; + + default: + if (eob - bp < dp->d_size) + return (-1); + memcpy(bp, cp, dp->d_size); + bp += dp->d_size; + } + cdp->cd_size = bp - cdp->cd_data; + return (cdp->cd_size); +} + +static int +digest_rr(char *envelope, int elen, struct converted_databuf *cdp, + char *buffer, int blen) +{ + char *bp = buffer, *eob = buffer + blen; + + if (eob - bp < elen) + return (-1); + memcpy (bp, envelope, elen); + bp += elen; + + if (eob - bp < INT16SZ) + return (-1); + PUTSHORT(cdp->cd_size, bp); + + if (eob - bp < cdp->cd_size) + return (-1); + memcpy (bp, cdp->cd_data, cdp->cd_size); + bp += cdp->cd_size; + + return (bp - buffer); +} + +/* Sorts the converted databuf in the list */ +static void +insert_converted_databuf(struct converted_databuf *cdp, + struct converted_databuf **clist) +{ + struct converted_databuf *tcdp, *next; + int t; + +#define compare_cdatabuf(c1, c2, t) \ + (t = memcmp(c1->cd_data, c2->cd_data, MIN(c1->cd_size, c2->cd_size)), \ + t == 0 ? c1->cd_size - c2->cd_size : t) + + if (*clist == NULL) { + *clist = cdp; + return; + } + + tcdp = *clist; + if (compare_cdatabuf(cdp, tcdp, t) < 0) { + cdp->cd_next = tcdp; + *clist = cdp; + return; + } + + next = tcdp->cd_next; + while (next) { + if (compare_cdatabuf(cdp, next, t) < 0) { + cdp->cd_next = next; + tcdp->cd_next = cdp; + return; + } + tcdp = next; + next = next->cd_next; + } + tcdp->cd_next = cdp; +#undef compare_cdatabuf +} + +static void +free_clist(struct converted_databuf *clist) { + struct converted_databuf *cdp; + + while (clist != NULL) { + cdp = clist; + clist = clist->cd_next; + memput(cdp->cd_data, cdp->cd_alloc); + memput(cdp, sizeof(struct converted_databuf)); + } +} + +/* Removes all empty nodes from an rrset's SIG list. */ +static void +rrset_trim_sigs(struct db_rrset *rrset) { + struct dnode *dnp, *odnp, *ndnp; + + odnp = NULL; + dnp = rrset->rr_sigs; + while (dnp != NULL) { + if (dnp->dp != NULL) { + odnp = dnp; + dnp = dnp->dn_next; + } + else { + if (odnp != NULL) + odnp->dn_next = dnp->dn_next; + else + rrset->rr_sigs = dnp->dn_next; + ndnp = dnp->dn_next; + memput(dnp, sizeof(struct dnode)); + dnp = ndnp; + } + } +} + +int +verify_set(struct db_rrset *rrset) { + DST_KEY *key = NULL; + struct sig_record *sigdata; + struct dnode *sigdn; + struct databuf *sigdp; + time_t now; + char *signer; + u_char name_n[MAXDNAME]; + u_char *sig, *eom; + int trustedkey = 0, siglen, labels, len = 0, ret; + u_char *buffer = NULL, *bp; + u_char envelope[MAXDNAME+32], *ep; + struct dnode *dnp; + int bufsize = 2048; /* Large enough for MAXDNAME + SIG_HDR_SIZE */ + struct converted_databuf *clist = NULL, *cdp; + int dnssec_failed = 0, dnssec_succeeded = 0; + int return_value; + int i; + + if (rrset == NULL || rrset->rr_name == NULL) { + ns_warning (ns_log_default, "verify_set: missing rrset/name"); + return (rrset_set_security(rrset, DB_S_FAILED)); + } + + if (rrset->rr_sigs == NULL) + return (rrset_set_security(rrset, DB_S_INSECURE)); + + ns_debug(ns_log_default, 5, "verify_set(%s, %s, %s)", rrset->rr_name, + p_type(rrset->rr_type), p_class(rrset->rr_class)); + + now = time(NULL); + + for (sigdn = rrset->rr_sigs; sigdn != NULL; sigdn = sigdn->dn_next) { + u_int32_t namefield; + struct sig_record sigrec; + + sigdp = sigdn->dp; + + eom = sigdp->d_data + sigdp->d_size; + if (sigdp->d_size < SIG_HDR_SIZE) { + return_value = DB_S_FAILED; + goto end; + } + memcpy(&sigrec, sigdp->d_data, SIG_HDR_SIZE); + sigdata = &sigrec; + signer = (char *)sigdp->d_data + SIG_HDR_SIZE; + sig = (u_char *)signer + strlen(signer) + 1; + siglen = eom - sig; + + /* + * Don't verify a set if the SIG inception time is in + * the future. This should be fixed before 2038 (BEW) + */ + if (ntohl(sigdata->sig_time_n) > now) + continue; + + /* An expired set is dropped, but the data is not. */ + if (ntohl(sigdata->sig_exp_n) < now) { + db_freedata(sigdp); + sigdn->dp = NULL; + continue; + } + + /* Cleanup from the last iteration if we continue'd */ + if (trustedkey == 0 && key != NULL) + dst_free_key(key); + + key = find_trusted_key(signer, sigdata->sig_keyid_n); + + if (key == NULL) { + trustedkey = 0; + key = find_public_key(signer, sigdata->sig_keyid_n); + } + else + trustedkey = 1; + + /* if we don't have the key, either + * - the data should be considered insecure + * - the sig is not a dnssec signature + */ + if (key == NULL) + continue; + + /* Can a key with this name sign the data? */ + if (!can_sign(rrset->rr_name, signer)) + continue; + + /* Check the protocol and flags of the key */ + if (key->dk_proto != NS_KEY_PROT_DNSSEC && + key->dk_proto != NS_KEY_PROT_ANY) + continue; + if (key->dk_flags & NS_KEY_NO_AUTH) + continue; + namefield = key->dk_flags & NS_KEY_NAME_TYPE; + if (namefield == NS_KEY_NAME_USER || + namefield == NS_KEY_NAME_RESERVED) + continue; + if (namefield == NS_KEY_NAME_ENTITY && + (key->dk_flags & NS_KEY_SIGNATORYMASK == 0)) + continue; + + /* + * If we're still here, we have a non-null key that's either + * a zone key or an entity key with signing authority. + */ + + if (buffer == NULL) { + bp = buffer = memget(bufsize); + if (bp == NULL) { + return_value = DB_S_FAILED; + goto end; + } + } + else + bp = buffer; + + + /* Digest the fixed portion of the SIG record */ + memcpy(bp, (char *) sigdata, SIG_HDR_SIZE); + bp += SIG_HDR_SIZE; + + /* Digest the signer's name, canonicalized */ + if (ns_name_pton(signer, name_n, sizeof name_n) < 0) { + return_value = DB_S_FAILED; + goto end; + } + i = ns_name_ntol(name_n, (u_char *)bp, bufsize - SIG_HDR_SIZE); + if (i < 0) { + return_value = DB_S_FAILED; + goto end; + } + bp += i; + + /* create the dns record envelope: + * <name><type><class><Original TTL> + */ + if (ns_name_pton(rrset->rr_name, name_n, sizeof name_n) < 0 || + ns_name_ntol(name_n, (u_char *)envelope, sizeof envelope) < 0) { + return_value = DB_S_FAILED; + goto end; + } + + labels = dn_count_labels(rrset->rr_name); + if (labels > sigdata->sig_labels_n) { + ep = envelope; + for (i=0; i < (labels - 1 - sigdata->sig_labels_n); i++) + ep += (*ep+1); + i = dn_skipname(ep, envelope + sizeof envelope); + if (i < 0) { + return_value = DB_S_FAILED; + goto end; + } + envelope[0] = '\001'; + envelope[1] = '*'; + memmove(envelope + 2, ep, i); + } + i = dn_skipname(envelope, envelope + sizeof envelope); + if (i < 0) { + return_value = DB_S_FAILED; + goto end; + } + ep = envelope + i; + PUTSHORT (rrset->rr_type, ep); + PUTSHORT (rrset->rr_class, ep); + if (envelope + sizeof(envelope) - ep < INT32SZ) { + return_value = DB_S_FAILED; + goto end; + } + memcpy (ep, &sigdata->sig_ottl_n, INT32SZ); + ep += INT32SZ; + + if (clist == NULL) { + for (dnp = rrset->rr_list; + dnp != NULL; + dnp = dnp->dn_next) + { + struct databuf *dp = dnp->dp; + + cdp = memget(sizeof(struct converted_databuf)); + if (cdp == NULL) { + return_value = DB_S_FAILED; + goto end; + } + memset(cdp, 0, sizeof(*cdp)); + /* Should be large enough... */ + cdp->cd_alloc = dp->d_size + 8; + cdp->cd_data = memget(cdp->cd_alloc); + if (cdp->cd_data == NULL) { + memput(cdp, sizeof(*cdp)); + return_value = DB_S_FAILED; + goto end; + } + while (convert_databuf(dp, cdp) < 0) { + memput(cdp->cd_data, cdp->cd_alloc); + cdp->cd_alloc *= 2; + cdp->cd_data = memget(cdp->cd_alloc); + if (cdp->cd_data == NULL) { + memput(cdp, sizeof(*cdp)); + return_value = DB_S_FAILED; + goto end; + } + } + insert_converted_databuf(cdp, &clist); + } + } + + for (cdp = clist; cdp != NULL; cdp = cdp->cd_next) { + len = digest_rr((char *)envelope, ep-envelope, cdp, + (char *)bp, bufsize - (bp - buffer)); + while (len < 0) { + u_char *newbuf; + + /* Double the buffer size */ + newbuf = memget(bufsize*2); + if (newbuf == NULL) { + return_value = DB_S_FAILED; + goto end; + } + memcpy(newbuf, buffer, bp - buffer); + bp = (bp - buffer) + newbuf; + memput(buffer, bufsize); + buffer = newbuf; + bufsize *= 2; + + len = digest_rr((char *)envelope, ep-envelope, + cdp, (char *)bp, + bufsize - (bp - buffer)); + } + bp += len; + } + + if (len < 0) { + return_value = DB_S_FAILED; + goto end; + } + + ret = dst_verify_data(SIG_MODE_ALL, key, NULL, buffer, + bp - buffer, sig, siglen); + + if (ret < 0) { + dnssec_failed++; + db_freedata(sigdp); + sigdn->dp = NULL; + } + else + dnssec_succeeded++; + } + +end: + if (dnssec_failed > 0) + rrset_trim_sigs(rrset); + if (trustedkey == 0 && key != NULL) + dst_free_key(key); + + if (dnssec_failed > 0 && dnssec_succeeded == 0) { + ns_warning (ns_log_default, + "verify_set(%s, %s, %s) failed", + rrset->rr_name, p_type(rrset->rr_type), + p_class(rrset->rr_class)); + return_value = DB_S_FAILED; + } + else if (dnssec_succeeded > 0) + return_value = DB_S_SECURE; + else + return_value = DB_S_INSECURE; + free_clist(clist); + if (buffer != NULL) + memput(buffer, bufsize); + return (rrset_set_security(rrset, return_value)); +} + +static void +rrset_free_partial(struct db_rrset *rrset, int free_data, struct dnode *start) { + struct dnode *dnp; + int found_start = 0; + + ns_debug(ns_log_default, 5, "rrset_free(%s)", rrset->rr_name); + + if (start == NULL) + found_start = 1; + + while (rrset->rr_list) { + dnp = rrset->rr_list; + if (dnp == start) + found_start = 1; + rrset->rr_list = rrset->rr_list->dn_next; + if (dnp->dp != NULL && free_data == 1 && found_start == 1) + db_freedata(dnp->dp); + memput(dnp, sizeof(struct dnode)); + } + while (rrset->rr_sigs) { + dnp = rrset->rr_sigs; + if (dnp == start) + found_start = 1; + rrset->rr_sigs = rrset->rr_sigs->dn_next; + if (dnp->dp != NULL && free_data == 1 && found_start == 1) + db_freedata(dnp->dp); + memput(dnp, sizeof(struct dnode)); + } +} + +static void +rrset_free(struct db_rrset *rrset, int free_data) { + rrset_free_partial(rrset, free_data, NULL); +} + +/* + * This is called when we have an rrset with SIGs and no other data. + * Returns 1 if we either found the necessary data or if the SIG can be added + * with no other data. 0 indicates that the SIG cannot be added. + */ +static int +attach_data(struct db_rrset *rrset) { + int type, class; + struct databuf *dp, *newdp, *sigdp; + struct dnode *dnp; + struct namebuf *np; + struct hashbuf *htp; + char *signer; + const char *fname; + char *name = rrset->rr_name; + + sigdp = rrset->rr_sigs->dp; + + type = SIG_COVERS(sigdp); + class = sigdp->d_class; + signer = (char *)(sigdp + SIG_HDR_SIZE); + + /* First, see if the signer can sign data for the name. If not, + * it's not a DNSSEC signature, so we can insert it with no + * corresponding data. + */ + if (!can_sign(name, signer)) + return (1); + + htp = hashtab; + np = nlookup (name, &htp, &fname, 0); + if (fname != name) + return (0); + + for (dp = np->n_data; dp != NULL; dp = dp->d_next) { + if (dp->d_type == type && dp->d_class == class) { + newdp = savedata(class, type, dp->d_ttl, dp->d_data, + dp->d_size); + dnp = (struct dnode *) memget (sizeof (struct dnode)); + if (dnp == NULL) + ns_panic(ns_log_default, 1, + "attach_data: memget failed"); + dnp->dp = newdp; + dnp->dn_next = rrset->rr_list; + rrset->rr_list = dnp; + } + } + if (rrset->rr_list != NULL) + return (1); + else + return (0); +} + +static int +rrset_db_update(struct db_rrset *rrset, int flags, struct hashbuf **htpp, + struct sockaddr_in from, int *rrcount) +{ + struct dnode *dnp; + struct databuf *dp; + int ret; + + /* If we have any unattached SIG records that are DNSSEC signatures, + * don't cache them unless we already have the corresponding data. + * If we do cache unattached SIGs, we run into problems later if we + * have a SIG X and get a query for type X. + */ + if (rrset->rr_list == NULL) { + if (attach_data(rrset) == 0) { + rrset_free(rrset, 1); + return (OK); + } + + if (rrset->rr_list != NULL && + verify_set(rrset) == DB_S_FAILED) + { + rrset_free(rrset, 1); + return (OK); + } + } + + for (dnp = rrset->rr_list; dnp != NULL; dnp = dnp->dn_next) { + dp = dnp->dp; + ret = db_update(rrset->rr_name, dp, dp, NULL, + flags, (*htpp), from); + if (ret != OK) { + /* XXX Probably should do rollback. */ + db_err(ret, rrset->rr_name, dp->d_type, + dnp->file, dnp->line); + if (ret != DATAEXISTS) { + rrset_free_partial(rrset, 1, dnp); + return (ret); + } + db_freedata(dp); + } + if (rrcount != NULL) + (*rrcount)++; + dnp->dp = NULL; + } + for (dnp = rrset->rr_sigs; dnp != NULL; dnp = dnp->dn_next) { + dp = dnp->dp; + if (dp == NULL) /* verifyset() can remove sigs */ + continue; + ret = db_update(rrset->rr_name, dp, dp, NULL, + flags, (*htpp), from); + if (ret != OK) { + /* XXX Probably should do rollback. */ + db_err(ret, rrset->rr_name, dp->d_type, + dnp->file, dnp->line); + if (ret != DATAEXISTS) { + rrset_free_partial(rrset, 1, dnp); + return (ret); + } + db_freedata(dp); + } + if (rrcount != NULL) + (*rrcount)++; + dnp->dp = NULL; + } + rrset_free(rrset, 0); + return (OK); +} + +static int +rr_in_set(struct databuf *rr, struct dnode *set) { + struct dnode *dnp; + + if (set == NULL) + return (0); + + for(dnp = set; dnp != NULL; dnp = dnp->dn_next) { + if (dnp->dp->d_size == rr->d_size && + memcmp(dnp->dp->d_data, rr->d_data, dnp->dp->d_size) == 0) + return (1); + } + return (0); +} + +static int +add_to_rrset_list(struct db_rrset **rrsets, char *name, struct databuf *dp, + int line, const char *file) +{ + struct db_rrset *rrset = *rrsets; + struct dnode *dnp; + + while (rrset != NULL) { + if (rrset->rr_type != ns_t_nxt || dp->d_type != ns_t_nxt) { + if (dp->d_type == ns_t_sig) { + if (SIG_COVERS(dp) == rrset->rr_type) + break; + } else { + if (dp->d_type == rrset->rr_type) + break; + } + } + else if (nxt_match_rrset(dp, rrset)) + break; + rrset = rrset->rr_next; + } + + if (rrset != NULL) { + if ((dp->d_type == ns_t_sig && rr_in_set(dp, rrset->rr_sigs)) || + (dp->d_type != ns_t_sig && rr_in_set(dp, rrset->rr_list))) + { + db_freedata(dp); + return (DATAEXISTS); + } + } else { + rrset = (struct db_rrset *) memget(sizeof(struct db_rrset)); + if (rrset == NULL) + ns_panic(ns_log_default, 1, + "add_to_rrset_list: memget failed(%s)", name); + memset(rrset, 0, sizeof(struct db_rrset)); + rrset->rr_name = savestr(name, 1); + rrset->rr_class = dp->d_class; + if (dp->d_type == ns_t_sig) + rrset->rr_type = SIG_COVERS(dp); + else + rrset->rr_type = dp->d_type; + rrset->rr_next = *rrsets; + *rrsets = rrset; + } + + dnp = (struct dnode *) memget(sizeof(struct dnode)); + if (dnp == NULL) + ns_panic(ns_log_default, 1, + "add_to_rrset_list: memget failed(%s)", name); + memset(dnp, 0, sizeof(struct dnode)); + dnp->dp = dp; + if (dp->d_type == ns_t_sig) { + if (rrset->rr_sigs != NULL) { + struct dnode *fdnp; + + /* Preserve the order of the RRs */ + /* Add this one to the end of the list */ + for (fdnp = rrset->rr_sigs; + fdnp->dn_next != NULL; + fdnp = fdnp->dn_next) + /* NULL */ ; + fdnp->dn_next = dnp; + } else + rrset->rr_sigs = dnp; + } else { + if (rrset->rr_list != NULL) { + struct dnode *fdnp; + + /* Preserve the order of the RRs */ + /* Add this one to the end of the list */ + for (fdnp = rrset->rr_list; + fdnp->dn_next != NULL; + fdnp = fdnp->dn_next) + /* NULL */ ; + fdnp->dn_next = dnp; + } else + rrset->rr_list = dnp; + } + dnp->file = (char *) file; + dnp->line = line; + return (0); +} + +static int +update_rrset_list(struct db_rrset **rrsets, int flags, struct hashbuf **htpp, + struct sockaddr_in from, int *rrcount) +{ + struct db_rrset *rrset = *rrsets, *next = NULL, *last = NULL; + int result = 0, tresult, cnameandother = 0; + + while (rrset != NULL) { + if (rrset->rr_type == ns_t_key) + break; + last = rrset; + rrset = rrset->rr_next; + } + + if (rrset != NULL && last != NULL) { + last->rr_next = rrset->rr_next; + rrset->rr_next = *rrsets; + *rrsets = rrset; + } + + rrset = *rrsets; + + while (rrset != NULL) { + if (verify_set(rrset) > DB_S_FAILED) { + ns_debug(ns_log_default, 10, + "update_rrset_list(%s, %s): set verified", + rrset->rr_name, p_type(rrset->rr_type)); + tresult = rrset_db_update(rrset, flags, htpp, + from, rrcount); + if (tresult == CNAMEANDOTHER) + cnameandother++; + if (tresult != OK) + result = tresult; + } + else { + rrset_free(rrset, 1); + result = DNSSECFAIL; + } + freestr(rrset->rr_name); + next = rrset->rr_next; + memput(rrset, sizeof(struct db_rrset)); + rrset = next; + } + *rrsets = NULL; + if (cnameandother != 0) + return (CNAMEANDOTHER); + return (result); +} + +int +db_set_update(char *name, struct databuf *dp, void **state, + int flags, struct hashbuf **htpp, struct sockaddr_in from, + int *rrcount, int line, const char *file) +{ + struct db_rrset **rrsets; + struct db_rrset *rrset; + int result = 0; + + ns_debug(ns_log_default, 5, "db_set_update(%s)", + (name == NULL) ? "<NULL>" : (*name == 0) ? "." : name); + + if (state == NULL) + ns_panic(ns_log_default, 1, + "Called db_set_update with state == NULL"); + + rrsets = (struct db_rrset **) state; + + if (*rrsets != NULL) { + rrset = *rrsets; + if (rrset->rr_name != NULL && dp != NULL && + name != NULL && ns_samename(name, rrset->rr_name) == 1 && + dp->d_class == rrset->rr_class) + return (add_to_rrset_list(rrsets, name, dp, + line, file)); + } + + if (*rrsets != NULL) + result = update_rrset_list(rrsets, flags, htpp, from, rrcount); + + if (dp != NULL) { + ns_debug(ns_log_default, 10, + "db_set_update(%s), creating new list", name); + + (void) add_to_rrset_list(rrsets, name, dp, line, file); + } + return (result); +} + +static int +nxt_match_rrset(struct databuf *dp, struct db_rrset *rrset) { + if (rrset->rr_list != NULL) + return (nxtmatch(rrset->rr_name, dp, rrset->rr_list->dp)); + else + return (nxtmatch(rrset->rr_name, dp, rrset->rr_sigs->dp)); +} |