enum sec_status dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, struct rbtree_t** sortree, int* buf_canon, char** reason) { enum sec_status sec; uint8_t* sig; /* RRSIG rdata */ size_t siglen; size_t rrnum = rrset_get_count(rrset); uint8_t* signer; /* rrsig signer name */ size_t signer_len; unsigned char* sigblock; /* signature rdata field */ unsigned int sigblock_len; uint16_t ktag; /* DNSKEY key tag */ unsigned char* key; /* public key rdata field */ unsigned int keylen; rrset_get_rdata(rrset, rrnum + sig_idx, &sig, &siglen); /* min length of rdatalen, fixed rrsig, root signer, 1 byte sig */ if(siglen < 2+20) { verbose(VERB_QUERY, "verify: signature too short"); *reason = "signature too short"; return sec_status_bogus; } if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) { verbose(VERB_QUERY, "verify: dnskey without ZSK flag"); *reason = "dnskey without ZSK flag"; return sec_status_bogus; } if(dnskey_get_protocol(dnskey, dnskey_idx) != LDNS_DNSSEC_KEYPROTO) { /* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */ verbose(VERB_QUERY, "verify: dnskey has wrong key protocol"); *reason = "dnskey has wrong protocolnumber"; return sec_status_bogus; } /* verify as many fields in rrsig as possible */ signer = sig+2+18; signer_len = dname_valid(signer, siglen-2-18); if(!signer_len) { verbose(VERB_QUERY, "verify: malformed signer name"); *reason = "signer name malformed"; return sec_status_bogus; /* signer name invalid */ } if(!dname_subdomain_c(rrset->rk.dname, signer)) { verbose(VERB_QUERY, "verify: signer name is off-tree"); *reason = "signer name off-tree"; return sec_status_bogus; /* signer name offtree */ } sigblock = (unsigned char*)signer+signer_len; if(siglen < 2+18+signer_len+1) { verbose(VERB_QUERY, "verify: too short, no signature data"); *reason = "signature too short, no signature data"; return sec_status_bogus; /* sig rdf is < 1 byte */ } sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len); /* verify key dname == sig signer name */ if(query_dname_compare(signer, dnskey->rk.dname) != 0) { verbose(VERB_QUERY, "verify: wrong key for rrsig"); log_nametypeclass(VERB_QUERY, "RRSIG signername is", signer, 0, 0); log_nametypeclass(VERB_QUERY, "the key name is", dnskey->rk.dname, 0, 0); *reason = "signer name mismatches key name"; return sec_status_bogus; } /* verify covered type */ /* memcmp works because type is in network format for rrset */ if(memcmp(sig+2, &rrset->rk.type, 2) != 0) { verbose(VERB_QUERY, "verify: wrong type covered"); *reason = "signature covers wrong type"; return sec_status_bogus; } /* verify keytag and sig algo (possibly again) */ if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) { verbose(VERB_QUERY, "verify: wrong algorithm"); *reason = "signature has wrong algorithm"; return sec_status_bogus; } ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx)); if(memcmp(sig+2+16, &ktag, 2) != 0) { verbose(VERB_QUERY, "verify: wrong keytag"); *reason = "signature has wrong keytag"; return sec_status_bogus; } /* verify labels is in a valid range */ if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) { verbose(VERB_QUERY, "verify: labelcount out of range"); *reason = "signature labelcount out of range"; return sec_status_bogus; } /* original ttl, always ok */ if(!*buf_canon) { /* create rrset canonical format in buffer, ready for * signature */ if(!rrset_canonical(region, buf, rrset, sig+2, 18 + signer_len, sortree)) { log_err("verify: failed due to alloc error"); return sec_status_unchecked; } *buf_canon = 1; } /* check that dnskey is available */ dnskey_get_pubkey(dnskey, dnskey_idx, &key, &keylen); if(!key) { verbose(VERB_QUERY, "verify: short DNSKEY RR"); return sec_status_unchecked; } /* verify */ sec = verify_canonrrset(buf, (int)sig[2+2], sigblock, sigblock_len, key, keylen, reason); if(sec == sec_status_secure) { /* check if TTL is too high - reduce if so */ adjust_ttl(ve, now, rrset, sig+2+4, sig+2+8, sig+2+12); /* verify inception, expiration dates * Do this last so that if you ignore expired-sigs the * rest is sure to be OK. */ if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) { return sec_status_bogus; } } return sec; }
bool tumbler_new(tumbler__s *const tum,const char *text) { /*---------------------------------------------------------------------- ; all variables are defined here, even though u2, u3, u4, segments and ; part aren't used until tumbler_new_range. It appears that defining them ; there causes a compiler error. It's either a bug in the version of GCC ; I'm using, or you can't declare variables after a goto target label. ; ; I'm not sure what the case is here, so I'm moving them up here. ;----------------------------------------------------------------------*/ struct value u1; struct value u2; struct value u3; struct value u4; bool part = false; assert(tum != NULL); assert(text != NULL); memset(tum,0,sizeof(tumbler__s)); /*------------------------------------------------- ; parse year ;-------------------------------------------------*/ if (!parse_num(&u1,&text,g_blog->first.year,g_blog->last.year)) return false; if (u1.val == g_blog->first.year) { tum->start.month = g_blog->first.month; tum->start.day = g_blog->first.day; } else { tum->start.month = 1; tum->start.day = 1; } tum->ustart = tum->ustop = UNIT_YEAR; tum->start.year = tum->stop.year = u1.val; tum->start.part = 1; tum->stop.month = 12; tum->stop.day = 31; tum->stop.part = ENTRY_MAX; if (*text == '\0') return check_dates(tum); if (*text == '-') goto tumbler_new_range; if (*text != '/') return false; text++; if (*text == '\0') return tum->redirect |= true; /*------------------------------------------------- ; parse month ;-------------------------------------------------*/ if (!parse_num(&u1,&text,1,12)) return false; assert(tum->start.part == 1); assert(tum->stop.part == ENTRY_MAX); tum->start.month = tum->stop.month = u1.val; tum->ustart = tum->ustop = UNIT_MONTH; tum->stop.day = max_monthday(tum->start.year,tum->start.month); if (u1.len == 1) tum->redirect |= true; if (*text == '\0') return check_dates(tum); if (*text == '-') goto tumbler_new_range; if (*text != '/') return false; text++; if (*text == '\0') return tum->redirect |= true; /*-------------------- ; parse day ;---------------------*/ if (!parse_num(&u1,&text,1,max_monthday(tum->start.year,tum->start.month))) return false; tum->start.day = tum->stop.day = u1.val; tum->ustart = tum->ustop = UNIT_DAY; if (u1.len == 1) tum->redirect |= true; if (*text == '\0') return check_dates(tum); if (*text == '-') goto tumbler_new_range; if (*text == '/') goto tumbler_new_file; if (*text != '.') return false; text++; if (*text == '\0') return false; /*----------------------------- ; parse part ;-----------------------------*/ if (!parse_num(&u1,&text,1,ENTRY_MAX)) return false; tum->start.part = tum->stop.part = u1.val; tum->ustart = tum->ustop = UNIT_PART; if ((u1.len > 1) && (*u1.txt == '0')) tum->redirect |= true; if (*text == '\0') return check_dates(tum); if (*text != '-') return false; assert(*text == '-'); goto tumbler_new_range; /*----------------------------- ; parse file ;-----------------------------*/ tumbler_new_file: assert(*text == '/'); tum->file = true; text++; for (size_t i = 0 ; i < FILENAME_MAX ; i++) { tum->filename[i] = *text++; if (tum->filename[i] == '\0') return check_dates(tum); if (tum->filename[i] == '/') return false; } return false; /*-------------------------------------- ; parse the range portion of the tumbler. Since the number of segments in ; this portion is variable, we parse first without reguard to the actual ; ranges, keep track of the number of segments found, then after we're ; done with the parsing, we figure everything out. ;---------------------------------------*/ tumbler_new_range: assert(*text == '-'); tum->range = true; text++; /*-------------------- ; first unit ;---------------------*/ if (!parse_num(&u1,&text,1,INT_MAX)) return false; tum->segments++; if (*text == '\0') goto tumbler_new_calculate; if (*text == '.') goto tumbler_new_range_part; if (*text != '/') return false; text++; if (*text == '\0') { tum->redirect |= true; goto tumbler_new_calculate; } /*------------------------------ ; second unit ;------------------------------*/ if (!parse_num(&u2,&text,1,INT_MAX)) return false; tum->segments++; if (*text == '\0') goto tumbler_new_calculate; if (*text == '.') goto tumbler_new_range_part; if (*text != '/') return false; text++; if (*text == '\0') { tum->redirect |= true; goto tumbler_new_calculate; } /*---------------------------------- ; third unit ;-----------------------------------*/ if (!parse_num(&u3,&text,1,INT_MAX)) return false; tum->segments++; if (*text == '\0') goto tumbler_new_calculate; if (*text != '.') return false; /*----------------------------------------------------------------------- ; the fourth unit, OR the part. The part will always be stuffed into u4. ;------------------------------------------------------------------------*/ tumbler_new_range_part: assert(part == false); assert(*text == '.'); assert(tum->segments >= 1); text++; if (!parse_num(&u4,&text,1,ENTRY_MAX)) return false; tum->segments++; part = true; if ((u4.len > 1) && (*u4.txt == '0')) tum->redirect |= true; if (*text != '\0') return false; /*---------------------------------------------------------------------- ; Now figure everything out. If we have all four segments, then this part ; is easy---we fill out every part of the range portion with our values. ; All values have already been checked against the lower bound of 1, so we ; only have to check against the upper limit for the most part. ; ; Here is also were we check for leading or missing zeros, and set the ; redirect flag appropriately. The month and day require a leading zero, ; while the part does not. ;------------------------------------------------------------------------*/ tumbler_new_calculate: if (tum->segments == 4) { tum->redirect |= (u2.len == 1) || (u3.len == 1) || ((u4.len > 1) && (*u4.txt == '0')) ; tum->stop.year = u1.val; tum->stop.month = u2.val; tum->stop.day = u3.val; tum->stop.part = u4.val; tum->ustop = UNIT_PART; return check_dates(tum); } /*--------------------------------------------------------------- ; If we have three segments, there are two cases to consider: ; ; 2000/02/03-2004/05/06 ; ; or ; ; 2000/02/03-04/05.6 ; ; The first is when part is false. This is a fairly easy case to handle. ;-----------------------------------------------------------------------*/ else if (tum->segments == 3) { if (part) { tum->redirect |= (u1.len == 1) || (u2.len == 1) || ((u4.len > 1) && (*u4.txt == '0')) ; tum->stop.month = u1.val; tum->stop.day = u2.val; tum->stop.part = u4.val; tum->ustop = UNIT_PART; return check_dates(tum); } else { tum->stop.year = u1.val; tum->stop.month = u2.val; tum->stop.day = u3.val; tum->stop.part = ENTRY_MAX; return check_dates(tum); } } /*------------------------------------------------------------------ ; We have two segments. If there is a part, then it's easy: ; ; 2000/02/03-04.1 ; ; The next few cases not so: ; ; 2000/02/03-2004/05 ; 2000/02/03-04/05 ; ; We use some huristics to distinquish between the two. This means we ; can't specify years before 13 AD, but I doubt that will be an issue any ; time soon. ;------------------------------------------------------------------------*/ else if (tum->segments == 2) { if (part) { tum->redirect |= (u1.len == 1) || ((u4.len > 1) && (*u4.txt == '0')) ; tum->stop.day = u1.val; tum->stop.part = u4.val; tum->ustop = UNIT_PART; return check_dates(tum); } else { /*--------------------------------------- ; check for year/month vs. month/day ;--------------------------------------*/ if (u1.val >= g_blog->first.year) { tum->redirect |= (u2.len == 1); tum->stop.year = u1.val; tum->stop.month = u2.val; tum->stop.day = max_monthday(tum->stop.year,tum->stop.month); tum->stop.part = ENTRY_MAX; tum->ustop = UNIT_MONTH; return check_dates(tum); } else { tum->redirect |= (u1.len == 1) || (u2.len == 1) ; tum->stop.month = u1.val; tum->stop.day = u2.val; tum->stop.part = ENTRY_MAX; tum->ustop = UNIT_DAY; return check_dates(tum); } } } /*------------------------------------------------------------------ ; Final case---one segment. We use the unit defined in the starting ; portion to determine what type of segment this is. ;-------------------------------------------------------------------*/ else { switch(tum->ustart) { case UNIT_YEAR: tum->stop.year = u1.val; tum->stop.month = 12; tum->stop.day = 31; /* there are always 31 days in Debtember */ tum->stop.part = ENTRY_MAX; tum->ustop = UNIT_YEAR; return check_dates(tum); case UNIT_MONTH: tum->redirect |= (u1.len == 1); tum->stop.month = u1.val; tum->stop.day = max_monthday(tum->stop.year,tum->stop.month); tum->stop.part = ENTRY_MAX; tum->ustop = UNIT_MONTH; return check_dates(tum); case UNIT_DAY: tum->redirect |= (u1.len == 1); tum->stop.day = u1.val; tum->stop.part = ENTRY_MAX; tum->ustop = UNIT_DAY; return check_dates(tum); case UNIT_PART: tum->redirect |= ((u1.len > 1) && (*u1.txt == '0')); tum->stop.part = u1.val; tum->ustop = UNIT_PART; return check_dates(tum); case UNIT_INDEX: assert(0); return false; } } return false; }