Branch: Tag:

2016-03-04

2016-03-04 13:38:56 by Simon Brenner <simonb@opera.com>

TURBO2-1409: Allow extra intermediates in certificate chains

Some servers send extraneous intermediate certificates that aren't
used to validate the leaf certificate. The Pike implementation of this
was quite to the letter of RFC5280/5246, which does say that each cert
has to be signed by the next certificate in the chain.

Only require that the certificates are in order, but ignore extra
certificates we didn't need to verify the leaf certificate.

411:    {    return low_get_sequence(2);    } +  // Attempt to create a presentable string from the issuer DER. +  string issuer_str() +  { +  return dn_str(issuer); +  }       //!    void `validity=(Sequence v)
475:    // Attempt to create a presentable string from the subject DER.    string subject_str()    { -  Sequence subj = low_get_sequence(4); +  return dn_str(subject); +  } +  +  string dn_str(Sequence dn) +  {    mapping ids = ([]); -  foreach(subj->elements, Compound pair) +  foreach(dn->elements, Compound pair)    {    if(pair->type_name!="SET" || !sizeof(pair)) continue;    pair = pair[0];
1690:    // last.       int len = sizeof(cert_chain); -  array chain_obj = allocate(len); -  array chain_cert = allocate(len); +  array chain_obj = ({}); +  array chain_cert = ({}); +  // Ignore certificates that aren't issuers of the leaf certificate. +  bool ignore_extraneous = true;    -  +  string last_issuer;    foreach(cert_chain; int idx; string c)    {    object cert = Standards.ASN1.Decode.secure_der_decode(c);
1700:    if(!tbs)    FATAL(CERT_INVALID);    -  int idx = len-idx-1; -  chain_cert[idx] = cert; -  chain_obj[idx] = tbs; +  string subject = tbs->subject->get_der(); +  if (ignore_extraneous && last_issuer && subject != last_issuer) +  { +  // This doesn't look relevant for validating the previous (closer to +  // the leaf) certificate. +  DBG("Skipping %q\n", tbs->subject_str()); +  continue;    } -  +  DBG("Adding %q to chain\n", tbs->subject_str()); +  +  // This is the leaf or is needed to validate the previous certificate. +  last_issuer = tbs->issuer->get_der(); +  chain_cert = ({ cert }) + chain_cert; +  chain_obj = ({ tbs }) + chain_obj; +  +  if (ignore_extraneous && has_index(authorities, last_issuer)) +  { +  // We've reached a certificate signed by a root we trust, end here. +  DBG("Ending at %q - signed by root %q\n", tbs->subject_str(), tbs->issuer_str()); +  break; +  } +  } +  +  // Update length since we've filtered the certificate chain a bit. +  len = sizeof(chain_obj);    m->certificates = chain_obj;       // Chain is now reversed so root is first and leaf is last.
1791:    && tbs)    {    DBG("signature is verified..\n"); -  m->verified = verified = 1; +  verified = 1;       // if we're the root of the chain and we've verified, this is    // the authority.
1804:    }    if (!verified)    ERROR(CERT_BAD_SIGNATURE); +  else if (zero_type(m->error_code)) // only if no error occured... +  m->verified = verified;    }    return m;