/* $NetBSD: db_sec.c,v 1.1.1.2 2001/01/27 06:16:52 itojun Exp $ */ #if !defined(lint) && !defined(SABER) static const char rcsid[] = "Id: db_sec.c,v 8.32 2000/12/23 08:14:36 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-2000 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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: * */ 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) ? "" : (*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)); }