📄 ns_validate.c
字号:
/************************************************************************** * ns_validate.c (was security.c in original ISI contribution) * author: anant kumar * contributed: March 17, 1993 * * implements validation procedure for RR's received from a server as a * response to a query. */#include <sys/param.h>#include <sys/socket.h>#include <sys/file.h>#include <netinet/in.h>#include <arpa/nameser.h>#include <syslog.h>#include <errno.h>#include <stdio.h>#include <resolv.h>#include "named.h"#ifdef VALIDATEstatic int isvalid __P((struct namebuf *, int, int, char *, int)), check_addr_ns __P((struct databuf **, struct sockaddr_in *, char *)), check_in_tables __P((struct databuf **, struct sockaddr_in *, char *));static void stick_in_queue __P((char *, int, int, char *));static NAMEADDR nameaddrlist[MAXNAMECACHE];static int firstNA = 0, lastNA = 0;static TO_Validate *validateQ, *currentVQ;static int VQcount;/***************************************************************** * validate() is called from dovalidate(). it takes as parameters, * the domain name sought, the class, type etc. of record, the server * that gave us the answer and the data it gave us * * it returns VALID if it is able to validate the record, INVALID if it cannot. * furtehr VALID is split into VALID_CACHE if we need to cache this record * since the domainname is not something we are authoritative for and * VALID_NO_CACHE if the name is something we are authoritative for. * * pseudocode for function validate is as follows: * validate(domain, server, type, class, data, dlen, rcode) { * * if(dname or a higher level name not found in cache) * return INVALID; * if (NS records for "domain" found in cache){ * * if(we are authoritative) /findns() returned NXDOMAIN;/ * if(we did not have an exact match on names) * =>the name does not exist in our database * => data is bad: return INVALID * if(data agrees with what we have) * return VALID_NO_CACHE; * else return INVALID; * * if(we are not authoritative) /findns() returned OK;/ * if (address records for NS's found in cache){ * if("server" = one of the addresses){ * return VALID_CACHE; * }else{ * stick in queue of "to_validate" data; * return (INVALID); * } * else return INVALID; * * This performs the validation procedure described above. Checks * for the longest component of the dname that has a NS record * associated with it. At any stage, if no data is found, it implies * that the name is bad (has an unknown domain identifier) thus, we * return INVALID. * If address of one of these servers matches the address of the server * that returned us this data, we are happy! * * since findns will set needs_prime_cache if np = NULL is passed, we always * reset it. will let ns_req do it when we are searching for ns records to * query someone. hence in all the three cases of switch(findns()) * we have needs_prime_cache = 0; *****************************************************************************/intvalidate(dname, server, type, class, data, dlen#ifdef NCACHE ,rcode#endif ) char *dname; struct sockaddr_in *server; int type; int class; char *data; int dlen;#ifdef NCACHE int rcode;#endif{ struct namebuf *np, *dnamep; struct hashbuf *htp; struct databuf *nsp[NSMAX]; int count; char *fname; int exactmatch = 0; struct fwdinfo *fwd;#ifdef DATUMREFCNT nsp[0] = NULL;#endif dprintf(3, (ddt, "validate(), d:%s, s:[%s], t:%d, c:%d\n", dname, inet_ntoa(server->sin_addr), type, class)); /* everything from forwarders is the GOSPEL */ for (fwd = fwdtab; fwd != NULL; fwd = fwd->next) { if (server->sin_addr.s_addr == fwd->fwdaddr.sin_addr.s_addr) return(VALID_CACHE); } htp = hashtab; if (priming && (dname[0] == '\0')) np = NULL; else np = nlookup(dname, &htp, &fname, 0); /* we were able to locate namebufs for this domain, or a parent domain, * or ??? */ if (np == NULL) { fname = malloc(1); fname[0] = '\0'; } dprintf(5, (ddt, "validate:namebuf found np:0x%x, d:\"%s\", f:\"%s\"\n", np, dname, fname)); /* save the namebuf if we were able to locate the exact dname */ if (!strcasecmp(dname, fname)) { dnamep = np; exactmatch = 1; } if (np == NULL && fname != NULL) { free((char *)fname); } switch (findns(&np, class, nsp, &count, 0)) { case NXDOMAIN: /** we are authoritative for this domain, lookup name * in our zone data, if it matches, return valid. * in either case, do not cache **/ dprintf(5, (ddt, "validate: auth data found\n"));#ifdef DATUMREFCNT free_nsp(nsp);#endif if (needs_prime_cache) needs_prime_cache = 0;#ifdef NCACHE if(rcode == NXDOMAIN) { /* If we had an exactmatch on the name, we found the * name in our authority database, so this couldn't * have been a bad name. INVALID data, say so */ if (exactmatch) return INVALID; else /* we did not have an exactmatch, the data is * good, we do not NCACHE stuff we are * authoritative for, though. */ return VALID_NO_CACHE; }#endif if (!strcasecmp(dname, np->n_dname)) { /* if the name we seek is the same as that we have ns * records for, compare the data we have to see if it * matches. if it does, return valid_no_cache, if it * doesn't, invalid. */ if (isvalid(np, type, class, data, dlen)) return VALID_NO_CACHE; else return INVALID; } /* we found ns records in a higher level, if we were unable to * locate the exact name earlier, it means we are * authoritative for this domain but do not have records for * this name. this name is obviously invalid */ if (!exactmatch) return INVALID; /* we found the exact name earlier and we are obviously * authoritative so check for data records and see if any * match. */ if (isvalid(dnamep, type, class, data, dlen)) return VALID_NO_CACHE; else return INVALID; case SERVFAIL:/* could not find name server records*/ /* stick_in_queue(dname, type, class, data); */ if (needs_prime_cache) needs_prime_cache = 0;#ifdef DATUMREFCNT free_nsp(nsp);#endif return INVALID; case OK: /*proceed */ dprintf(5, (ddt, "validate:found ns records:calling check_addr_ns\n")); if (needs_prime_cache) needs_prime_cache = 0; if (check_addr_ns(nsp, server, dname)) {#ifdef DATUMREFCNT free_nsp(nsp);#endif return VALID_CACHE; } /* server is not one of those we know of */ /* stick_in_queue(dname, type, class, data); */#ifdef DATUMREFCNT free_nsp(nsp);#endif return INVALID; default:#ifdef DATUMREFCNT free_nsp(nsp);#endif return INVALID; } /*switch*/} /*validate*//*********************************************************************** * validate rr returned by somebody against your own database, if you are * authoritative for the information. if you have a record that matches, * return 1, else return 0. validate() above will use this and determine * if the record should be returned/discarded. ***********************************************************************/static intisvalid(np, type, class, data, dlen) struct namebuf *np; int type, class; char *data; int dlen;{ register struct databuf *dp; for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (!wanted(dp, class, type)) { if ((type == T_CNAME) && (class == dp->d_class)) { /* if a cname exists, any other will not */ return(0); /* we come here only for zone info, * so -ve $ed info can't be */ } continue; } /* type and class match, if i get here * let's now compare the data section, per RR type */ /* unless, of course, the data was negative, in which case * we should return FAILURE since we should not have found * data here. */ if ((data == NULL) || (dlen == 0)) { return 0; } switch (type) { char *td; u_char *tdp; int x; case T_A: case T_WKS: case T_HINFO: case T_UINFO: case T_UID: case T_GID: case T_TXT:#ifdef ALLOW_T_UNSPEC case T_UNSPEC:#endif x = memcmp(dp->d_data, data, dlen); dprintf(3, (ddt, "type = %d, GOOD = %d\n", type, x)); if (x == 0) return (1); else break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: x = strncasecmp((char *)dp->d_data, data, dlen); dprintf(3, (ddt, "type = %d, GOOD = %d\n", type, x)); if (x == 0) return (1); else break; case T_MINFO: case T_SOA: case T_RP: /* compare first string */ x = strncasecmp((char *)dp->d_data, data, strlen(data) + 1); if (x != 0) break; /* move to second string */ td = data + (strlen(data) + 1); tdp = dp->d_data + (strlen((char *)dp->d_data)+1); /* compare second string */ x = strncasecmp(td, (char *)tdp, strlen((char *)td+1)); if (x != 0) break; /* move beyond second string, to * set of words in SOA. * RP and MINFO stuff really * ends here */ td = td + strlen((char *)td) + 1; tdp = tdp + strlen((char *)tdp) + 1; if (type == T_SOA) { x = memcmp(td, (char *)tdp, 5*sizeof(u_int32_t)); if (x != 0) break; } /* everything was equal, wow! * so return a success */ return (1); case T_MX: case T_AFSDB: x = memcmp(dp->d_data, data, sizeof(u_int16_t)); if (x != 0) break; td = data + sizeof(u_int16_t); tdp = dp->d_data + sizeof(u_int16_t); x = strncasecmp(td, (char *)tdp, strlen((char *)td) + 1); if (x != 0) break; return (1); default: dprintf(3, (ddt, "unknown type %d\n", type)); return (0); } /* continue in loop if record did not match */ } /* saw no record of interest in whole chain * If the data we were trying to validate was negative, we succeeded! * else we failed */ if ((data == NULL) || (dlen == 0)) { /* negative data, report success */ return 1; } /* positive data, no such RR, validation failed */ return (0);}/****************************************************************** * get a list of databufs that have ns addresses for the closest domain * you know about, get their addresses and confirm that server indeed * is one of them. if yes return 1 else 0. * first checks the cache that we build in nslookup() earlier * when we ns_forw(). if unableto find it there, it checks the entire * hash table to do address translations. *******************************************************************/static intcheck_addr_ns(nsp, server, dname) struct databuf **nsp; struct sockaddr_in *server; char *dname;{ int i, found=0; char sname[MAXDNAME]; struct in_addr *saddr = &(server->sin_addr); struct databuf **nsdp; dprintf(5, (ddt, "check_addr_ns: s:[%s], db:0x%x, d:\"%s\"\n", inet_ntoa(*saddr), nsp, dname)); for(i = lastNA; i != firstNA; i = (i+1) % MAXNAMECACHE) { if (!bcmp((char *)saddr, (char *)&(nameaddrlist[i].ns_addr), sizeof(struct in_addr))) { strcpy(sname, nameaddrlist[i].nsname); found = 1; break; } } if (found) { dprintf(3, (ddt, "check_addr_ns: found address:[%s]\n", inet_ntoa(*saddr))); for (nsdp = nsp; *nsdp != NULL;nsdp++) { dprintf(5, (ddt, "check_addr_ns:names are:%s, %s\n", sname,(*nsdp)->d_data)); if (!strcasecmp(sname,(char *)((*nsdp)->d_data))) { return (1); } } /* syslog(LOG_ERR,"check_addr_ns: %s != %s? %s", inet_ntoa(*saddr), sname, *dname?dname:"'.'"); */ return (0); } /* could not find name in my cache of servers, must go through the * whole grind */ dprintf(2, (ddt, "check_addr_ns:calling check_in_tables()\n")); return (check_in_tables(nsp, server, dname));}/************************************************************************* * checks in hash tables for the address of servers whose name is in the * data section of nsp records. borrows code from nslookup()/ns_forw.c * largely. *************************************************************************/static intcheck_in_tables(nsp, server, syslogdname) struct databuf *nsp[]; struct sockaddr_in *server; char *syslogdname;{ register struct namebuf *np; register struct databuf *dp, *nsdp; struct hashbuf *tmphtp; char *dname, *fname; int class; int qcomp(); dprintf(3, (ddt, "check_in_tables(nsp=x%x,qp=x%x,'%s')\n", nsp, server, syslogdname)); while ((nsdp = *nsp++) != NULL) { class = nsdp->d_class; dname = (char *)nsdp->d_data; dprintf(3, (ddt, "check_in_tables: NS %s c%d t%d (x%x)\n", dname, class, nsdp->d_type, nsdp->d_flags)); tmphtp = ((nsdp->d_flags & DB_F_HINT) ? fcachetab : hashtab); np = nlookup(dname, &tmphtp, &fname, 1); if (np == NULL || fname != dname) { dprintf(3, (ddt, "%s: not found %s %x\n", dname, fname, np)); continue; } /* look for name server addresses */ for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (stale(dp)) continue; if (dp->d_type != T_A || dp->d_class != class) continue;#ifdef NCACHE if (dp->d_rcode) continue;#endif if (!bcmp((char *)dp->d_data, (char *)&(server->sin_addr), sizeof(struct in_addr))) { return (1); } } } return (0); /* haven't been able to locate the right address */}/************************************************************************ * is called in nslookup() and stores the name vs address of a name server * --& check_in_tables above-- * we contact, in a list of a maximum MAXNAMECACHE entries. we later refer * -- NAMEADDR nameaddrlist[MAXNAMECACHE]; -- * to this list when we are trying to resolve the name in check_addr_ns(). *************************************************************************/voidstore_name_addr(servername, serveraddr, syslogdname, sysloginfo) char *servername; struct in_addr *serveraddr; char *syslogdname; char *sysloginfo;{ int i; dprintf(3, (ddt, "store_name_addr:s:%s, a:[%s]\n", servername, inet_ntoa(*serveraddr))); /* if we already have the name address pair in cache, return */ for(i = lastNA; i != firstNA; i = (i+1) % MAXNAMECACHE) { if (strcasecmp(servername, nameaddrlist[i].nsname) == 0) { if (serveraddr->s_addr == nameaddrlist[i].ns_addr.s_addr) { dprintf(5, (ddt, "store_name_addr:found n and a [%s] [%s] in our $\n", inet_ntoa(nameaddrlist[i].ns_addr), inet_ntoa(*serveraddr))); return; } /* if */ } else if (serveraddr->s_addr == nameaddrlist[i].ns_addr.s_addr) { /* * log this as it needs to be fixed. * replace old name by new, next query likely to have * NS record matching new */ if (!haveComplained( (char*)dhash((u_char*)nameaddrlist[i].nsname, strlen(nameaddrlist[i].nsname) ), (char*)dhash((u_char*)servername, strlen(servername) ) ) ) { syslog(LOG_INFO, "%s: server name mismatch for [%s]: (%s != %s) (server for %s)", sysloginfo, inet_ntoa(*serveraddr), nameaddrlist[i].nsname, servername, syslogdname); } free(nameaddrlist[i].nsname); nameaddrlist[i].nsname = malloc((unsigned)strlen(servername)+1); strcpy(nameaddrlist[i].nsname, servername); return; } } /* we have to add this one to our cache */ nameaddrlist[firstNA].nsname = malloc((unsigned)strlen(servername)+1); strcpy(nameaddrlist[firstNA].nsname, servername); bcopy((char *)serveraddr, (char *)&(nameaddrlist[firstNA].ns_addr), sizeof(struct in_addr)); dprintf(2, (ddt, "store_name_addr:added entry #:%d n:%s a:[%s]\n", firstNA, nameaddrlist[firstNA].nsname, inet_ntoa(nameaddrlist[firstNA].ns_addr))); firstNA = (firstNA+1) % MAXNAMECACHE; if (firstNA == lastNA) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -