shithub: riscv

Download patch

ref: 2cc152f9e1c7435ff0a5bcc7c4467249afe227e9
parent: 9155b30f6d436d2197dcad2e75dac6de146f9499
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Wed Nov 20 17:35:52 EST 2013

ndb/dns: filter dns answers avoiding cache poisoning

only cache what we asked for or need to resolve the
query. filter out everything else.

--- a/sys/src/cmd/ndb/dblookup.c
+++ b/sys/src/cmd/ndb/dblookup.c
@@ -796,44 +796,31 @@
 int
 baddelegation(RR *rp, RR *nsrp, uchar *addr)
 {
-	Ndbtuple *nt;
 	static int whined;
 	static Ndbtuple *t;
+	Ndbtuple *nt;
 
+	if(rp->type != Tns)
+		return 0;
+
 	if(t == nil)
 		t = lookupinfo("dom");
-
-	for(; rp; rp = rp->next){
-		if(rp->type != Tns)
-			continue;
-
-		/* see if delegation is looping */
-		if(nsrp)
-		if(rp->owner != nsrp->owner)
-		if(subsume(rp->owner->name, nsrp->owner->name) &&
-		   strcmp(nsrp->owner->name, localservers) != 0){
-			dnslog("delegation loop %R -> %R from %I",
-				nsrp, rp, addr);
-			return 1;
-		}
-
-		if(t == nil)
-			continue;
-
+	if(t != nil){
 		/* see if delegating to us what we don't own */
 		for(nt = t; nt != nil; nt = nt->entry)
 			if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
 				break;
+
 		if(nt != nil && !inmyarea(rp->owner->name)){
 			if (!whined) {
 				whined = 1;
-				dnslog("bad delegation %R from %I; "
-					"no further logging of them", rp, addr);
+				dnslog("bad delegation %R from %I/%s; "
+					"no further logging of them",
+					rp, addr, nsrp->host->name);
 			}
 			return 1;
 		}
 	}
-
 	return 0;
 }
 
--- a/sys/src/cmd/ndb/dn.c
+++ b/sys/src/cmd/ndb/dn.c
@@ -1085,54 +1085,69 @@
 	return *start;
 }
 
-/*
- *  remove negative cache rr's from an rr list
- */
 RR*
-rrremneg(RR **l)
+rrremfilter(RR **l, int (*filter)(RR*, void*), void *arg)
 {
-	RR **nl, *rp;
-	RR *first;
+	RR *first, *rp;
+	RR **nl;
 
 	first = nil;
 	nl = &first;
 	while(*l != nil){
 		rp = *l;
-		if(rp->negative){
+		if((*filter)(rp, arg)){
 			*l = rp->next;
 			*nl = rp;
 			nl = &rp->next;
 			*nl = nil;
 		} else
-			l = &rp->next;
+			l = &(*l)->next;
 	}
 
 	return first;
 }
 
+static int
+filterneg(RR *rp, void*)
+{
+	return rp->negative;
+}
+static int
+filtertype(RR *rp, void *arg)
+{
+	return rp->type == *((int*)arg);
+}
+static int
+filterowner(RR *rp, void *arg)
+{
+	return rp->owner == (DN*)arg;
+}
+
 /*
+ *  remove negative cache rr's from an rr list
+ */
+RR*
+rrremneg(RR **l)
+{
+	return rrremfilter(l, filterneg, nil);
+}
+
+/*
  *  remove rr's of a particular type from an rr list
  */
 RR*
 rrremtype(RR **l, int type)
 {
-	RR *first, *rp;
-	RR **nl;
+	return rrremfilter(l, filtertype, &type);
+}
 
-	first = nil;
-	nl = &first;
-	while(*l != nil){
-		rp = *l;
-		if(rp->type == type){
-			*l = rp->next;
-			*nl = rp;
-			nl = &rp->next;
-			*nl = nil;
-		} else
-			l = &(*l)->next;
-	}
-
-	return first;
+/*
+ *  remove rr's of a particular owner from an rr list
+ */
+RR*
+rrremowner(RR **l, DN *owner)
+{
+	return rrremfilter(l, filterowner, owner);
 }
 
 static char *
--- a/sys/src/cmd/ndb/dnresolve.c
+++ b/sys/src/cmd/ndb/dnresolve.c
@@ -43,7 +43,8 @@
 struct Dest
 {
 	uchar	a[IPaddrlen];	/* ip address */
-	DN	*s;		/* name server */
+	DN	*s;		/* name server name */
+	RR	*n;		/* name server rr */
 	int	nx;		/* number of transmissions */
 	int	code;		/* response code; used to clear dp->respcode */
 };
@@ -56,7 +57,6 @@
 
 	RR	*nsrp;		/* name servers to consult */
 
-	/* dest must not be on the stack due to forking in slave() */
 	Dest	*dest;		/* array of destinations */
 	Dest	*curdest;	/* pointer to next to fill */
 	int	ndest;		/* transmit to this many on this round */
@@ -294,6 +294,8 @@
 {
 	int rv;
 
+	if(nsrp == nil)
+		return Answnone;
 	qp->nsrp = nsrp;
 	rv = netquery(qp, depth);
 	qp->nsrp = nil;		/* prevent accidents */
@@ -849,7 +851,14 @@
 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(p->a))
 			continue;
 		p->nx = 0;
+		p->n = nil;
 		p->s = trp->owner;
+		for(rp = qp->nsrp; rp; rp = rp->next){
+			if(rp->host == p->s){
+				p->n = rp;
+				break;
+			}
+		}
 		p->code = Rtimeout;
 		nd++;
 	}
@@ -1073,9 +1082,66 @@
 	return mp->an == nil && (mp->flags & Rmask) == Rname;
 }
 
+static int
+filterhints(RR *rp, void *arg)
+{
+	RR *nsrp;
+
+	if(rp->type != Ta && rp->type != Taaaa)
+		return 0;
+
+	for(nsrp = arg; nsrp; nsrp = nsrp->next)
+		if(nsrp->type == Tns && rp->owner == nsrp->host)
+			return 1;
+
+	return 0;
+}
+
+static int
+filterauth(RR *rp, void *arg)
+{
+	Dest *dest;
+	RR *nsrp;
+
+	dest = arg;
+	nsrp = dest->n;
+	if(nsrp == nil)
+		return 0;
+
+	if(rp->type == Tsoa && rp->owner != nsrp->owner
+	&& !subsume(nsrp->owner->name, rp->owner->name)
+	&& strncmp(nsrp->owner->name, "local#", 6) != 0)
+		return 1;
+
+	if(rp->type != Tns)
+		return 0;
+
+	if(rp->owner != nsrp->owner
+	&& !subsume(nsrp->owner->name, rp->owner->name)
+	&& strncmp(nsrp->owner->name, "local#", 6) != 0)
+		return 1;
+
+	return baddelegation(rp, nsrp, dest->a);
+}
+
+static void
+reportandfree(RR *l, char *note, Dest *p)
+{
+	RR *rp;
+
+	while(rp = l){
+		l = l->next;
+		rp->next = nil;
+		if(debug)
+			dnslog("ignoring %s from %I/%s: %R",
+				note, p->a, p->s->name, rp);
+		rrfree(rp);
+	}
+}
+
 /* returns Answerr (-1) on errors, else number of answers, which can be zero. */
 static int
-procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
+procansw(Query *qp, DNSmsg *mp, int depth, Dest *p)
 {
 	int rv;
 	char buf[32];
@@ -1083,7 +1149,7 @@
 	Query nq;
 	RR *tp, *soarr;
 
-	if (mp->an == nil)
+	if(mp->an == nil)
 		stats.negans++;
 
 	/* ignore any error replies */
@@ -1096,24 +1162,30 @@
 	}
 
 	/* ignore any bad delegations */
-	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
-		stats.negbaddeleg++;
-		if(mp->an == nil){
-			stats.negbdnoans++;
-			freeanswers(mp);
-			if(p != nil)
-				p->code = Rserver;
-			dnslog(" and no answers");
-			return Answerr;
-		}
-		dnslog(" but has answers; ignoring ns");
-		rrfreelistptr(&mp->ns);
-		mp->nscount = 0;
-	}
+	if((tp = rrremfilter(&mp->ns, filterauth, p)) != 0)
+		reportandfree(tp, "bad delegation", p);
 
 	/* remove any soa's from the authority section */
 	soarr = rrremtype(&mp->ns, Tsoa);
 
+	/* only nameservers remaining */
+	if((tp = rrremtype(&mp->ns, Tns)) != 0){
+		reportandfree(mp->ns, "non-nameserver", p);
+		mp->ns = tp;
+	}
+
+	/* remove answers not related to the question. */
+	if((tp = rrremowner(&mp->an, qp->dp)) != 0){
+		reportandfree(mp->an, "wrong subject answer", p);
+		mp->an = tp;
+	}
+	if(qp->type != Tall){
+		if((tp = rrremtype(&mp->an, qp->type)) != 0){
+			reportandfree(mp->an, "wrong type answer", p);
+			mp->an = tp;
+		}
+	}
+
 	/* incorporate answers */
 	unique(mp->an);
 	unique(mp->ns);
@@ -1122,17 +1194,23 @@
 	if(mp->an){
 		/*
 		 * only use cname answer when returned. some dns servers
-		 * attach (potential) spam hint address records which poisons the cache.
+		 * attach (potential) spam hint address records which poisons
+		 * the cache.
 		 */
 		if((tp = rrremtype(&mp->an, Tcname)) != 0){
-			if(mp->an)
-				rrfreelist(mp->an);
+			reportandfree(mp->an, "ip in cname answer", p);
 			mp->an = tp;
 		}
 		rrattach(mp->an, (mp->flags & Fauth) != 0);
 	}
-	if(mp->ar)
+	if(mp->ar){
+		/* restrict hints to address rr's for nameservers only */
+		if((tp = rrremfilter(&mp->ar, filterhints, mp->ns)) != 0){
+			reportandfree(mp->ar, "hint", p);
+			mp->ar = tp;
+		}
 		rrattach(mp->ar, Notauthoritative);
+	}
 	if(mp->ns && !cfg.justforw){
 		ndp = mp->ns->owner;
 		rrattach(mp->ns, Notauthoritative);
@@ -1303,21 +1381,23 @@
 			}
 
 			/* find responder */
-			// dnslog("queryns got reply from %I", srcip);
+			if(debug)
+				dnslog("queryns got reply from %I", srcip);
 			for(p = qp->dest; p < qp->curdest; p++)
 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
 					break;
+			if(p >= qp->curdest){
+				dnslog("response from %I but no destination", srcip);
+				continue;
+			}
 
-			if(p != qp->curdest) {
-				/* remove all addrs of responding server from list */
-				for(np = qp->dest; np < qp->curdest; np++)
-					if(np->s == p->s)
-						np->nx = Maxtrans;
-			} else
-				p = nil;
+			/* remove all addrs of responding server from list */
+			for(np = qp->dest; np < qp->curdest; np++)
+				if(np->s == p->s)
+					np->nx = Maxtrans;
 
 			/* free or incorporate RRs in m */
-			rv = procansw(qp, &m, srcip, depth, p);
+			rv = procansw(qp, &m, depth, p);
 			if (rv > Answnone) {
 				qp->dest = qp->curdest = nil; /* prevent accidents */
 				return rv;
--- a/sys/src/cmd/ndb/dns.h
+++ b/sys/src/cmd/ndb/dns.h
@@ -478,6 +478,8 @@
 char*	rrname(int, char*, int);
 RR*	rrremneg(RR**);
 RR*	rrremtype(RR**, int);
+RR*	rrremowner(RR**, DN*);
+RR*	rrremfilter(RR**, int (*)(RR*, void*), void*);
 int	rrsupported(int);
 int	rrtype(char*);
 void	slave(Request*);
--