/* * Checks if CA a is trusted by CA b */ bool trusted_ca(chunk_t a, chunk_t b, int *pathlen) { bool match = FALSE; char abuf[ASN1_BUF_LEN], bbuf[ASN1_BUF_LEN]; dntoa(abuf, ASN1_BUF_LEN, a); dntoa(bbuf, ASN1_BUF_LEN, b); DBG(DBG_X509 | DBG_CONTROLMORE, DBG_log(" trusted_ca called with a=%s b=%s", abuf, bbuf)); /* no CA b specified -> any CA a is accepted */ if (b.ptr == NULL) { *pathlen = (a.ptr == NULL) ? 0 : MAX_CA_PATH_LEN; return TRUE; } /* no CA a specified -> trust cannot be established */ if (a.ptr == NULL) { *pathlen = MAX_CA_PATH_LEN; return FALSE; } *pathlen = 0; /* CA a equals CA b -> we have a match */ if (same_dn(a, b)) return TRUE; /* CA a might be a subordinate CA of b */ lock_authcert_list("trusted_ca"); while ((*pathlen)++ < MAX_CA_PATH_LEN) { x509cert_t *cacert = get_authcert(a, empty_chunk, empty_chunk, AUTH_CA); /* cacert not found or self-signed root cacert-> exit */ if (cacert == NULL || same_dn(cacert->issuer, a)) break; /* does the issuer of CA a match CA b? */ match = same_dn(cacert->issuer, b); /* we have a match and exit the loop */ if (match) break; /* go one level up in the CA chain */ a = cacert->issuer; } unlock_authcert_list("trusted_ca"); DBG(DBG_X509 | DBG_CONTROLMORE, DBG_log(" trusted_ca returning with %s", match ? "match" : "failed")); return match; }
/* * get a X.509 authority certificate with a given subject or keyid */ x509cert_t *get_authcert(chunk_t subject, chunk_t serial, chunk_t keyid, u_char auth_flags) { x509cert_t *cert = x509authcerts; x509cert_t *prev_cert = NULL; while (cert != NULL) { if (cert->authority_flags & auth_flags && ((keyid.ptr != NULL) ? same_keyid(keyid, cert->subjectKeyID) : (same_dn(subject, cert->subject) && same_serial(serial, cert->serialNumber)))) { if (cert != x509authcerts) { /* bring the certificate up front */ prev_cert->next = cert->next; cert->next = x509authcerts; x509authcerts = cert; } return cert; } prev_cert = cert; cert = cert->next; } return NULL; }
/* * get the X.509 CRL with a given issuer */ static x509crl_t* get_x509crl(chunk_t issuer, chunk_t serial, chunk_t keyid) { x509crl_t *crl = x509crls; x509crl_t *prev_crl = NULL; while(crl != NULL) { if ((keyid.ptr != NULL && crl->authKeyID.ptr != NULL) ? same_keyid(keyid, crl->authKeyID) : (same_dn(crl->issuer, issuer) && same_serial(serial, crl->authKeySerialNumber))) { if (crl != x509crls) { /* bring the CRL up front */ prev_crl->next = crl->next; crl->next = x509crls; x509crls = crl; } return crl; } prev_crl = crl; crl = crl->next; } return NULL; }
/* * compare two ocsp locations for equality */ static bool same_ocsp_location(const ocsp_location_t *a, const ocsp_location_t *b) { return ((a->authKeyID.ptr != NULL) ? same_keyid(a->authKeyID, b->authKeyID) : (same_dn(a->issuer, b->issuer) && same_serial(a->authKeySerialNumber, b->authKeySerialNumber))) && same_chunk(a->uri, b->uri); }
/* compare two struct id values */ bool same_id(const struct id *a, const struct id *b) { a = resolve_myid(a); b = resolve_myid(b); if(b->kind == ID_NONE || a->kind==ID_NONE) { return TRUE; /* it's the wildcard */ } if (a->kind != b->kind) return FALSE; switch (a->kind) { case ID_NONE: return TRUE; /* repeat of above for completeness */ case ID_IPV4_ADDR: case ID_IPV6_ADDR: return sameaddr(&a->ip_addr, &b->ip_addr); case ID_FQDN: case ID_USER_FQDN: /* assumptions: * - case should be ignored * - trailing "." should be ignored (even if the only character?) */ { size_t al = a->name.len , bl = b->name.len; while (al > 0 && a->name.ptr[al - 1] == '.') al--; while (bl > 0 && b->name.ptr[bl - 1] == '.') bl--; return al == bl && strncasecmp((char *)a->name.ptr , (char *)b->name.ptr, al) == 0; } case ID_DER_ASN1_DN: return same_dn(a->name, b->name); case ID_KEY_ID: return a->name.len == b->name.len && memcmp(a->name.ptr, b->name.ptr, a->name.len) == 0; default: bad_case(a->kind); } /* NOTREACHED */ return FALSE; }
/* * get a X.509 certificate with a given issuer found at a certain position */ x509cert_t *get_x509cert(chunk_t issuer, chunk_t serial, chunk_t keyid, x509cert_t *chain) { x509cert_t *cert = (chain != NULL) ? chain->next : x509certs; while (cert != NULL) { if ((keyid.ptr != NULL) ? same_keyid(keyid, cert->authKeyID) : (same_dn(issuer, cert->issuer) && same_serial(serial, cert->authKeySerialNumber))) return cert; cert = cert->next; } return NULL; }
/* * get a cacert with a given subject or keyid from an alternative list */ static const x509cert_t*get_alt_cacert(chunk_t subject, chunk_t serial, chunk_t keyid, const x509cert_t *cert) { while (cert != NULL) { if ((keyid.ptr != NULL) ? same_keyid(keyid, cert->subjectKeyID) : (same_dn(subject, cert->subject) && same_serial(serial, cert->serialNumber))) return cert; cert = cert->next; } return NULL; }
/* * stores a chained list of end certs and CA certs * * @verified_ca is a copied list of the verified authcerts that have * been placed in the global authcert chain */ void store_x509certs(x509cert_t **firstcert, x509cert_t **verified_ca, bool strict) { x509cert_t *cacerts = NULL; x509cert_t **pp = firstcert; /* first extract CA certs, discarding root CA certs */ while (*pp != NULL) { x509cert_t *cert = *pp; if (cert->isCA) { *pp = cert->next; /* we don't accept self-signed CA certs */ if (same_dn(cert->issuer, cert->subject)) { libreswan_log("self-signed cacert rejected"); free_x509cert(cert); } else { /* insertion into temporary chain of candidate CA certs */ cert->next = cacerts; cacerts = cert; } } else { pp = &cert->next; } } /* now verify the candidate CA certs */ x509cert_t *ver = NULL; while (cacerts != NULL) { realtime_t valid_until; x509cert_t *cert = cacerts; cacerts = cacerts->next; if (trust_authcert_candidate(cert, cacerts) && verify_x509cert(cert, strict, &valid_until, cacerts)) { add_authcert(cert, AUTH_CA); if (ver == NULL) { ver = clone_thing(*cert, "x509cert_t"); *verified_ca = ver; } else { ver->next = clone_thing(*cert, "x509cert_t"); ver = ver->next; } ver->next = NULL; } else { libreswan_log("intermediate cacert rejected"); free_x509cert(cert); } } /* now verify the end certificates */ pp = firstcert; while (*pp != NULL) { realtime_t valid_until; x509cert_t *cert = *pp; if (verify_x509cert(cert, strict, &valid_until, NULL)) { DBG(DBG_X509 | DBG_PARSING, DBG_log("public key validated")); add_x509_public_key(NULL, cert, valid_until, DAL_SIGNED); } else { libreswan_log("X.509 certificate rejected"); } *pp = cert->next; free_x509cert(cert); } }
/* compare two struct id values */ bool same_id(const struct id *a, const struct id *b) { a = resolve_myid(a); b = resolve_myid(b); if (b->kind == ID_NONE || a->kind == ID_NONE) { DBG(DBG_PARSING, DBG_log("id type with ID_NONE means wildcard match")); return TRUE; /* it's the wildcard */ } if (a->kind != b->kind) { return FALSE; } switch (a->kind) { case ID_NONE: return TRUE; /* repeat of above for completeness */ case ID_NULL: if (a->kind == b->kind) { DBG(DBG_PARSING, DBG_log("ID_NULL: id kind matches")); return TRUE; } return FALSE; case ID_IPV4_ADDR: case ID_IPV6_ADDR: return sameaddr(&a->ip_addr, &b->ip_addr); case ID_FQDN: case ID_USER_FQDN: /* * assumptions: * - case should be ignored * - trailing "." should be ignored * (even if the only character?) */ { size_t al = a->name.len, bl = b->name.len; while (al > 0 && a->name.ptr[al - 1] == '.') al--; while (bl > 0 && b->name.ptr[bl - 1] == '.') bl--; return al == bl && strncaseeq((char *)a->name.ptr, (char *)b->name.ptr, al); } case ID_FROMCERT: DBG(DBG_CONTROL, DBG_log("same_id() received ID_FROMCERT - unexpected")); /* FALLTHROUGH */ case ID_DER_ASN1_DN: return same_dn(a->name, b->name); case ID_KEY_ID: return a->name.len == b->name.len && memeq(a->name.ptr, b->name.ptr, a->name.len); default: bad_case(a->kind); /* NOTREACHED */ return FALSE; } }
/* establish trust into a candidate authcert by going up the trust chain. * validity and revocation status are not checked. */ bool trust_authcert_candidate(const x509cert_t *cert, const x509cert_t *alt_chain) { int pathlen; lock_authcert_list("trust_authcert_candidate"); for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++) { const x509cert_t *authcert = NULL; DBG(DBG_CONTROL, char buf[ASN1_BUF_LEN]; dntoa(buf, ASN1_BUF_LEN, cert->subject); DBG_log("subject: '%s'",buf); dntoa(buf, ASN1_BUF_LEN, cert->issuer); DBG_log("issuer: '%s'",buf); if (cert->authKeyID.ptr != NULL) { datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':' , buf, ASN1_BUF_LEN); DBG_log("authkey: %s", buf); } ); /* search in alternative chain first */ authcert = get_alt_cacert(cert->issuer, cert->authKeySerialNumber , cert->authKeyID, alt_chain); if (authcert != NULL) { DBG(DBG_CONTROL, DBG_log("issuer cacert found in alternative chain") ) } else { /* search in trusted chain */ authcert = get_authcert(cert->issuer, cert->authKeySerialNumber , cert->authKeyID, AUTH_CA); if (authcert != NULL) { DBG(DBG_CONTROL, DBG_log("issuer cacert found") ) } else { plog("issuer cacert not found"); unlock_authcert_list("trust_authcert_candidate"); return FALSE; } } if (!check_signature(cert->tbsCertificate, cert->signature, cert->algorithm, authcert)) { plog("invalid certificate signature"); unlock_authcert_list("trust_authcert_candidate"); return FALSE; } DBG(DBG_CONTROL, DBG_log("valid certificate signature") ) /* check if cert is a self-signed root ca */ if (pathlen > 0 && same_dn(cert->issuer, cert->subject)) { DBG(DBG_CONTROL, DBG_log("reached self-signed root ca") ) unlock_authcert_list("trust_authcert_candidate"); return TRUE; } /* go up one step in the trust chain */ cert = authcert; }