/* * 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; }
/* * add an authority certificate to the chained list */ void add_authcert(x509cert_t *cert, u_char auth_flags) { x509cert_t *old_cert; /* set authority flags */ cert->authority_flags |= auth_flags; lock_authcert_list("add_authcert"); old_cert = get_authcert(cert->subject, cert->serialNumber, cert->subjectKeyID, auth_flags); if (old_cert != NULL) { if (same_x509cert(cert, old_cert)) { /* * cert is already present, just add additional * authority flags */ old_cert->authority_flags |= cert->authority_flags; DBG(DBG_X509 | DBG_PARSING, DBG_log(" authcert is already present and identical"); ); unlock_authcert_list("add_authcert"); free_x509cert(cert); return; } else {
/* * add an authority certificate to the chained list */ void add_authcert(x509cert_t *cert, u_char auth_flags) { x509cert_t *old_cert; /* set authority flags */ cert->authority_flags |= auth_flags; lock_authcert_list("add_authcert"); old_cert = get_authcert(cert->subject, cert->serialNumber , cert->subjectKeyID, auth_flags); if (old_cert != NULL) { if (same_x509cert(cert, old_cert)) { /* cert is already present, just add additional authority flags */ old_cert->authority_flags |= cert->authority_flags; DBG(DBG_X509 | DBG_PARSING , DBG_log(" authcert is already present and identical") ) unlock_authcert_list("add_authcert"); free_x509cert(cert); return; } else { /* cert is already present but will be replaced by new cert */ free_first_authcert(); DBG(DBG_X509 | DBG_PARSING , DBG_log(" existing authcert deleted") ) } } /* add new authcert to chained list */ cert->next = x509authcerts; x509authcerts = cert; share_x509cert(cert); /* set count to one */ DBG(DBG_X509 | DBG_PARSING, DBG_log(" authcert inserted") ) unlock_authcert_list("add_authcert"); }
/* build an ocsp location from certificate information * without unsharing its contents */ static bool build_ocsp_location(const x509cert_t *cert, ocsp_location_t *location) { static u_char digest[SHA1_DIGEST_SIZE]; /* temporary storage */ location->uri = (cert->accessLocation.ptr != NULL) ? cert->accessLocation : ocsp_default_uri; /* abort if no ocsp location uri is defined */ if (location->uri.ptr == NULL) return FALSE; setchunk(location->authNameID, digest, SHA1_DIGEST_SIZE); compute_digest(cert->issuer, OID_SHA1, &location->authNameID); location->next = NULL; location->issuer = cert->issuer; location->authKeyID = cert->authKeyID; location->authKeySerialNumber = cert->authKeySerialNumber; if (cert->authKeyID.ptr == NULL) { x509cert_t *authcert = get_authcert(cert->issuer , cert->authKeySerialNumber, cert->authKeyID, AUTH_CA); if (authcert != NULL) { location->authKeyID = authcert->subjectKeyID; location->authKeySerialNumber = authcert->serialNumber; } } location->nonce = empty_chunk; location->certinfo = NULL; return TRUE; }
/* * verify if a cert hasn't been revoked by a crl */ static bool verify_by_crl(/*const*/ x509cert_t *cert, bool strict, realtime_t *until) { x509crl_t *crl; char ibuf[ASN1_BUF_LEN], cbuf[ASN1_BUF_LEN]; lock_crl_list("verify_by_crl"); crl = get_x509crl(cert->issuer, cert->authKeySerialNumber, cert->authKeyID); dntoa(ibuf, ASN1_BUF_LEN, cert->issuer); if (crl == NULL) { unlock_crl_list("verify_by_crl"); libreswan_log("no crl from issuer \"%s\" found (strict=%s)", ibuf, strict ? "yes" : "no"); #if defined(LIBCURL) || defined(LDAP_VER) if (cert->crlDistributionPoints != NULL) { add_crl_fetch_request(cert->issuer, cert->crlDistributionPoints); wake_fetch_thread("verify_by_crl"); } #endif if (strict) return FALSE; } else { x509cert_t *issuer_cert; bool valid; DBG(DBG_X509, DBG_log("issuer crl \"%s\" found", ibuf)); #if defined(LIBCURL) || defined(LDAP_VER) add_distribution_points(cert->crlDistributionPoints, &crl->distributionPoints); #endif lock_authcert_list("verify_by_crl"); issuer_cert = get_authcert(crl->issuer, crl->authKeySerialNumber, crl->authKeyID, AUTH_CA); dntoa(cbuf, ASN1_BUF_LEN, crl->issuer); valid = check_signature(crl->tbsCertList, crl->signature, crl->algorithm, issuer_cert); unlock_authcert_list("verify_by_crl"); if (valid) { bool revoked_crl, expired_crl; DBG(DBG_X509, DBG_log("valid crl signature on \"%s\"", cbuf)); /* with strict crl policy the public key must have the same * lifetime as the crl */ if (strict && realbefore(crl->nextUpdate, *until)) *until = crl->nextUpdate; /* has the certificate been revoked? */ revoked_crl = x509_check_revocation(crl, cert->serialNumber); /* is the crl still valid? */ expired_crl = realbefore(crl->nextUpdate, realnow()); unlock_crl_list("verify_by_crl"); if (expired_crl) { char tbuf[REALTIMETOA_BUF]; libreswan_log( "crl update for \"%s\" is overdue since %s", cbuf, realtimetoa(crl->nextUpdate, TRUE, tbuf, sizeof(tbuf))); #if defined(LIBCURL) || defined(LDAP_VER) /* try to fetch a crl update */ if (cert->crlDistributionPoints != NULL) { add_crl_fetch_request(cert->issuer, cert->crlDistributionPoints); wake_fetch_thread("verify_by_crl"); } #endif } else { DBG(DBG_X509, DBG_log("crl is \"%s\" valid", cbuf)); } if (revoked_crl || (strict && expired_crl)) { /* remove any cached public keys */ remove_x509_public_key(cert); return FALSE; } } else { unlock_crl_list("verify_by_crl"); libreswan_log("invalid crl signature on \"%s\"", cbuf); if (strict) return FALSE; } } return TRUE; }
/* * Insert X.509 CRL into chained list */ bool insert_crl(chunk_t blob, chunk_t crl_uri) { x509crl_t *crl = alloc_thing(x509crl_t, "x509crl"); *crl = empty_x509crl; if (parse_x509crl(blob, 0, crl)) { x509cert_t *issuer_cert; x509crl_t *oldcrl; bool valid_sig; generalName_t *gn; /* add distribution point */ gn = alloc_thing(generalName_t, "generalName"); gn->kind = GN_URI; gn->name = crl_uri; gn->next = crl->distributionPoints; crl->distributionPoints = gn; lock_authcert_list("insert_crl"); /* get the issuer cacert */ issuer_cert = get_authcert(crl->issuer, crl->authKeySerialNumber, crl->authKeyID, AUTH_CA); if (issuer_cert == NULL) { chunk_t *n = &crl->distributionPoints->name; loglog(RC_LOG_SERIOUS, "CRL rejected: crl issuer cacert not found for %.*s", (int)n->len, (char *)n->ptr); free_crl(crl); unlock_authcert_list("insert_crl"); return FALSE; } DBG(DBG_X509, DBG_log("crl issuer cacert found")); /* check the issuer's signature of the crl */ valid_sig = check_signature(crl->tbsCertList, crl->signature, crl->algorithm, issuer_cert); unlock_authcert_list("insert_crl"); if (!valid_sig) { free_crl(crl); return FALSE; } DBG(DBG_X509, DBG_log("valid crl signature")); lock_crl_list("insert_crl"); oldcrl = get_x509crl(crl->issuer, crl->authKeySerialNumber, crl->authKeyID); if (oldcrl != NULL) { if (realbefore(oldcrl->thisUpdate, crl->thisUpdate)) { /* old CRL is older than new CRL: replace */ #if defined(LIBCURL) || defined(LDAP_VER) /* keep any known CRL distribution points */ add_distribution_points( oldcrl->distributionPoints, &crl->distributionPoints); #endif /* now delete the old CRL */ free_first_crl(); DBG(DBG_X509, DBG_log("thisUpdate is newer - existing crl deleted")); } else { /* old CRL is not older than new CRL: keep old one */ unlock_crl_list("insert_crls"); DBG(DBG_X509, DBG_log("thisUpdate is not newer - existing crl not replaced")); free_crl(crl); /* * is the fetched crl valid? * now + 2 * crl_check_interval < oldcrl->nextUpdate */ return realbefore(realtimesum(realnow(), deltatimescale(2, 1, crl_check_interval)), oldcrl->nextUpdate); } } /* insert new CRL */ crl->next = x509crls; x509crls = crl; unlock_crl_list("insert_crl"); /* * is the new crl valid? * now + 2 * crl_check_interval < crl->nextUpdate */ return realbefore(realtimesum(realnow(), deltatimescale(2, 1, crl_check_interval)), crl->nextUpdate); } else { loglog(RC_LOG_SERIOUS, " error in X.509 crl %s", (char *)crl_uri.ptr); free_crl(crl); return FALSE; } }
/* * Build an ocsp location from certificate information * without unsharing its contents */ static bool build_ocsp_location(const cert_t *cert, ocsp_location_t *location) { certificate_t *certificate = cert->cert; identification_t *issuer = certificate->get_issuer(certificate); x509_t *x509 = (x509_t*)certificate; chunk_t issuer_dn = issuer->get_encoding(issuer); chunk_t authKeyID = x509->get_authKeyIdentifier(x509); hasher_t *hasher; static u_char digest[HASH_SIZE_SHA1]; /* temporary storage */ enumerator_t *enumerator = x509->create_ocsp_uri_enumerator(x509); location->uri = NULL; while (enumerator->enumerate(enumerator, &location->uri)) { break; } enumerator->destroy(enumerator); if (location->uri == NULL) { ca_info_t *ca = get_ca_info(issuer, authKeyID); if (ca && ca->ocspuri) { location->uri = ca->ocspuri; } else { /* abort if no ocsp location uri is defined */ return FALSE; } } /* compute authNameID from as SHA-1 hash of issuer DN */ location->authNameID = chunk_create(digest, HASH_SIZE_SHA1); hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); if (hasher == NULL) { return FALSE; } hasher->get_hash(hasher, issuer_dn, digest); hasher->destroy(hasher); location->next = NULL; location->issuer = issuer; location->authKeyID = authKeyID; if (authKeyID.ptr == NULL) { cert_t *authcert = get_authcert(issuer, authKeyID, X509_CA); if (authcert) { x509_t *x509 = (x509_t*)authcert->cert; location->authKeyID = x509->get_subjectKeyIdentifier(x509); } } location->nonce = chunk_empty; location->certinfo = NULL; return TRUE; }
/* * Insert X.509 CRL into chained list */ bool insert_crl(chunk_t blob, chunk_t crl_uri) { x509crl_t *crl = alloc_thing(x509crl_t, "x509crl"); *crl = empty_x509crl; if (parse_x509crl(blob, 0, crl)) { x509cert_t *issuer_cert; x509crl_t *oldcrl; bool valid_sig; generalName_t *gn; /* add distribution point */ gn = alloc_thing(generalName_t, "generalName"); gn->kind = GN_URI; gn->name = crl_uri; gn->next = crl->distributionPoints; crl->distributionPoints = gn; lock_authcert_list("insert_crl"); /* get the issuer cacert */ issuer_cert = get_authcert(crl->issuer, crl->authKeySerialNumber, crl->authKeyID, AUTH_CA); if (issuer_cert == NULL) { char distpoint[PATH_MAX]; distpoint[0] = '\0'; strncat(distpoint, (char *)crl->distributionPoints->name.ptr, (crl->distributionPoints->name.len < PATH_MAX ? crl->distributionPoints->name.len : PATH_MAX)); openswan_log("crl issuer cacert not found for (%s)", distpoint);; free_crl(crl); unlock_authcert_list("insert_crl"); return FALSE; } DBG(DBG_X509, DBG_log("crl issuer cacert found") ) /* check the issuer's signature of the crl */ valid_sig = check_signature(crl->tbsCertList, crl->signature , crl->algorithm, issuer_cert); unlock_authcert_list("insert_crl"); if (!valid_sig) { free_crl(crl); return FALSE; } DBG(DBG_X509, DBG_log("valid crl signature") ) lock_crl_list("insert_crl"); oldcrl = get_x509crl(crl->issuer, crl->authKeySerialNumber , crl->authKeyID); if (oldcrl != NULL) { if (crl->thisUpdate > oldcrl->thisUpdate) { #ifdef HAVE_THREADS /* keep any known CRL distribution points */ add_distribution_points(oldcrl->distributionPoints , &crl->distributionPoints); #endif /* now delete the old CRL */ free_first_crl(); DBG(DBG_X509, DBG_log("thisUpdate is newer - existing crl deleted") ) } else {
/* 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; }