--- sys/src/libsec/port/x509.c
+++ sys/src/libsec/port/x509.c
@@ -1562,6 +1562,14 @@ freevalfields(Value* v)
* revocationDate UTCTime}
*/
+typedef struct CertExtension CertExtension;
+struct CertExtension {
+ CertExtension* next;
+ Ints* oid;
+ int critical;
+ Bytes* value; /* extnValue OCTET STRING contents */
+};
+
typedef struct CertX509 {
int serial;
char* issuer;
@@ -1574,6 +1582,8 @@ typedef struct CertX509 {
int signature_alg;
Bytes* signature;
Bytes* ext; /* raw extensions SEQUENCE (opaque); nil if cert has none */
+ CertExtension* extensions; /* parsed list, nil if no extensions or unparseable */
+ int unrecognizedCritical; /* RFC 5280 Section 4.2 flag, checked by validator */
} CertX509;
/* Algorithm object-ids */
@@ -1678,6 +1688,9 @@ static void (*namedcurves[NUMCURVES])(mpint*, mpint*,
secp384r1,
};
+static void freecertextensions(CertExtension* e);
+static void parse_extensions(CertX509* c);
+
static void
freecert(CertX509* c)
{
@@ -1693,10 +1706,108 @@ freecert(CertX509* c)
freebytes(c->publickey);
freebytes(c->signature);
freebytes(c->ext);
+ freecertextensions(c->extensions);
free(c);
}
+static void
+freecertextensions(CertExtension *e)
+{
+ CertExtension *n;
+
+ while(e != nil){
+ n = e->next;
+ freeints(e->oid);
+ freebytes(e->value);
+ free(e);
+ e = n;
+ }
+}
+
/*
+ * Parse one TBSCertificate extension element per RFC 5280 Section 4.2:
+ * Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ * Returns nil on parse failure (caller skips the entry).
+ */
+static CertExtension*
+parse_one_extension(Elem *e, int *critical_out)
+{
+ Elist *l;
+ Ints *oid;
+ Bytes *val;
+ CertExtension *ext;
+ int critical;
+
+ *critical_out = -1;
+ if(!is_seq(e, &l) || elistlen(l) < 2)
+ return nil;
+ if(!is_oid(&l->hd, &oid))
+ return nil;
+ l = l->tl;
+ critical = 0;
+ if(l != nil && l->hd.val.tag == VBool){
+ critical = l->hd.val.u.boolval;
+ l = l->tl;
+ }
+ /* Critical determined (explicit BOOLEAN or DEFAULT FALSE).
+ * Set the out-param now so caller can apply RFC 5280 Section 4.2
+ * even if the extnValue parse below fails. */
+ *critical_out = critical;
+ if(l == nil || !is_octetstring(&l->hd, &val))
+ return nil;
+
+ ext = (CertExtension*)emalloc(sizeof(CertExtension));
+ ext->oid = makeints(oid->data, oid->len);
+ ext->critical = critical;
+ ext->value = makebytes(val->data, val->len);
+ ext->next = nil;
+ return ext;
+}
+
+/*
+ * Walk c->ext (raw SEQUENCE OF Extension) and build the c->extensions
+ * list. Sets c->unrecognizedCritical if any critical extension is
+ * encountered (refined by per-OID recognition in libsec-x509-rfc5280-
+ * constraints). Silent on parse failure: c->extensions stays nil.
+ */
+static void
+parse_extensions(CertX509 *c)
+{
+ Elem eext;
+ Elist *el;
+ CertExtension *ext, **tail;
+ int critical;
+
+ if(c->ext == nil)
+ return;
+ if(decode(c->ext->data, c->ext->len, &eext) != ASN_OK)
+ return;
+ if(!is_seq(&eext, &el)){
+ freevalfields(&eext.val);
+ return;
+ }
+
+ tail = &c->extensions;
+ for(; el != nil; el = el->tl){
+ ext = parse_one_extension(&el->hd, &critical);
+ /* RFC 5280 Section 4.2: MUST reject a critical extension
+ * we cannot process. Malformed extnValue with a
+ * determinable critical flag falls under "cannot process". */
+ if(ext == nil && critical == 1)
+ c->unrecognizedCritical = 1;
+ if(ext == nil)
+ continue;
+ *tail = ext;
+ tail = &ext->next;
+ if(ext->critical)
+ c->unrecognizedCritical = 1;
+ }
+ freevalfields(&eext.val);
+}
+
+/*
* Parse the Name ASN1 type.
* The sequence of RelativeDistinguishedName's gives a sort of pathname,
* from most general to most specific. Each element of the path can be
@@ -1925,6 +2036,7 @@ decode_cert(Bytes* a)
}
el = el->tl;
}
+ parse_extensions(c);
/*resume Certificate */
if(c->signature_alg < 0)
|