Exemple #1
0
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;
}
Exemple #2
0
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;
}