static int vfy_chain_pkix(CERTCertificate **chain, int chain_len, CERTCertificate **end_out, bool *rev_opts) { CERTCertificate *end_cert = NULL; CERTCertList *trustcl = get_all_root_certs(); if (trustcl == NULL) { DBG(DBG_X509, DBG_log("X509: no trust anchor available for verification")); return VERIFY_RET_SKIP; } int i; for (i = 0; i < chain_len; i++) { if (!CERT_IsCACert(chain[i], NULL)) { end_cert = chain[i]; break; } } if (end_cert == NULL) { libreswan_log("X509: no EE-cert in chain!"); return VERIFY_RET_FAIL; } CERTVerifyLog *cur_log = NULL; CERTVerifyLog vfy_log; CERTVerifyLog vfy_log2; new_vfy_log(&vfy_log); new_vfy_log(&vfy_log2); CERTRevocationFlags rev; zero(&rev); /* ??? are there pointer fields? */ PRUint64 revFlagsLeaf[2] = { 0, 0 }; PRUint64 revFlagsChain[2] = { 0, 0 }; set_rev_per_meth(&rev, revFlagsLeaf, revFlagsChain); set_rev_params(&rev, rev_opts[RO_CRL_S], rev_opts[RO_OCSP], rev_opts[RO_OCSP_S]); int in_idx = 0; CERTValInParam cvin[7]; CERTValOutParam cvout[3]; zero(&cvin); /* ??? are there pointer fields? */ zero(&cvout); /* ??? are there pointer fields? */ cvin[in_idx].type = cert_pi_revocationFlags; cvin[in_idx++].value.pointer.revocation = &rev; cvin[in_idx].type = cert_pi_useAIACertFetch; cvin[in_idx++].value.scalar.b = rev_opts[RO_OCSP]; cvin[in_idx].type = cert_pi_trustAnchors; cvin[in_idx++].value.pointer.chain = trustcl; cvin[in_idx].type = cert_pi_useOnlyTrustAnchors; cvin[in_idx++].value.scalar.b = PR_TRUE; cvin[in_idx].type = cert_pi_end; cvout[0].type = cert_po_errorLog; cvout[0].value.pointer.log = cur_log = &vfy_log; cvout[1].type = cert_po_certList; cvout[1].value.pointer.chain = NULL; cvout[2].type = cert_po_end; /* kludge alert!! * verification may be performed twice: once with the * 'client' usage and once with 'server', which is an NSS * detail and not related to IKE. In the absence of a real * IKE profile being available for NSS, this covers more * KU/EKU combinations */ int fin; SECCertificateUsage usage; for (usage = certificateUsageSSLClient; ; usage = certificateUsageSSLServer) { SECStatus rv = CERT_PKIXVerifyCert(end_cert, usage, cvin, cvout, NULL); if (rv != SECSuccess || cur_log->count > 0) { if (cur_log->count > 0 && cur_log->head != NULL) { if (usage == certificateUsageSSLClient && RETRYABLE_TYPE(cur_log->head->error)) { /* try again, after some adjustments */ DBG(DBG_X509, DBG_log("retrying verification with the NSS serverAuth profile")); /* ??? since we are about to overwrite cvout[1], * should we be doing: * if (cvout[1].value.pointer.chain != NULL) * CERT_DestroyCertList(cvout[1].value.pointer.chain); */ cvout[0].value.pointer.log = cur_log = &vfy_log2; cvout[1].value.pointer.chain = NULL; continue; } else { fin = nss_err_to_revfail(cur_log->head); } } else { /* * An rv != SECSuccess without CERTVerifyLog results should not * happen, but catch it anyway */ libreswan_log("X509: unspecified NSS verification failure"); fin = VERIFY_RET_FAIL; } } else { DBG(DBG_X509, DBG_log("certificate is valid")); *end_out = end_cert; fin = VERIFY_RET_OK; } break; } CERT_DestroyCertList(trustcl); PORT_FreeArena(vfy_log.arena, PR_FALSE); PORT_FreeArena(vfy_log2.arena, PR_FALSE); if (cvout[1].value.pointer.chain != NULL) { CERT_DestroyCertList(cvout[1].value.pointer.chain); } return fin; }
int main(int argc, char *argv[]) { int opt; long fin = 0; int use_pkix = 0; SECStatus rv; char pbuf[1024]; PRBool crlcheck = PR_FALSE; PRBool ocspcheck = PR_FALSE; PRBool strict = PR_FALSE; CERTCertDBHandle *handle = NULL; CERTCertificate **certout = NULL; CERTVerifyLog vfy_log; CERTVerifyLog vfy_log2; CERTVerifyLog *cur_log; CERTValOutParam *pkixout = NULL; SECItem c1; SECItem c2; SECItem *certs[2]; certs[0] = &c1; certs[1] = &c2; int numcerts = 0; while ((opt = getopt(argc, argv, "u:d:e:pn:s:coSr")) != -1) { switch(opt) { /* usage type */ case 'u': set_usage(optarg); break; case 'd': db_dir = optarg; break; case 's': sub_file = optarg; break; case 'c': crlcheck = PR_TRUE; break; case 'o': ocspcheck = PR_TRUE; break; case 'S': strict = PR_TRUE; break; case 'e': end_file = optarg; break; case 'p': use_pkix = 1; break; case 'n': rightca_nick = optarg; break; case 'r': retry_verify = PR_TRUE; break; default: print_usage(); break; } } if (db_dir == NULL) db_dir = "testfiles/"; if (end_file == NULL) end_file = "testfiles/end.pem"; get_file(certs[numcerts++], end_file); if (sub_file != NULL) { get_file(certs[numcerts++], sub_file); } snprintf(pbuf, sizeof(pbuf), "sql:%s", db_dir); if (NSS_Initialize(pbuf, "", "", "secmod.db", 0x1) != SECSuccess) { printf("NSS_Initialize failed %d\n", PORT_GetError()); exit(-1); } if ((handle = CERT_GetDefaultCertDB()) == NULL) { printf("NULL handle\n"); exit(-1); } if (ocspcheck) { CERT_EnableOCSPChecking(handle); CERT_DisableOCSPDefaultResponder(handle); if (strict) CERT_SetOCSPFailureMode(ocspMode_FailureIsNotAVerificationFailure); } rv = CERT_ImportCerts(handle, 0, numcerts, certs, &certout, PR_FALSE, PR_FALSE, NULL); if (rv != SECSuccess) { printf("CERT_ImportCerts failed %d\n", PORT_GetError()); exit(-1); } vfy_log.count = 0; vfy_log.head = NULL; vfy_log.tail = NULL; vfy_log.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); vfy_log2.count = 0; vfy_log2.head = NULL; vfy_log2.tail = NULL; vfy_log2.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (use_pkix) { int in_idx = 0; CERTValInParam cvin[7]; CERTValOutParam cvout[3]; CERTCertList *trustcl = NULL; CERTRevocationFlags rev; PRUint64 revFlagsLeaf[2] = { 0, 0 }; PRUint64 revFlagsChain[2] = { 0, 0 }; zero(&cvin); /* ??? is this reasonable? */ zero(&cvout); /* ??? is this reasonable? */ zero(&rev); /* ??? is this reasonable? */ if (rightca_nick == NULL) rightca_nick = "root"; if ((trustcl = get_trust_certlist(handle, rightca_nick)) == NULL) { printf("Couldn't find trust anchor\n"); exit(-1); } cvin[in_idx].type = cert_pi_useAIACertFetch; cvin[in_idx++].value.scalar.b = PR_TRUE; cvin[in_idx].type = cert_pi_revocationFlags; cvin[in_idx++].value.pointer.revocation = &rev; cvin[in_idx].type = cert_pi_trustAnchors; cvin[in_idx++].value.pointer.chain = trustcl; cvin[in_idx].type = cert_pi_useOnlyTrustAnchors; cvin[in_idx++].value.scalar.b = PR_TRUE; set_rev_per_meth(&rev, revFlagsLeaf, revFlagsChain); set_rev_params(&rev, crlcheck, ocspcheck, strict); cvin[in_idx].type = cert_pi_end; cvout[0].type = cert_po_errorLog; cvout[0].value.pointer.log = &vfy_log; cur_log = &vfy_log; cvout[1].type = cert_po_certList; cvout[1].value.pointer.chain = NULL; cvout[2].type = cert_po_end; pkixout = &cvout[0]; pkixredo: rv = CERT_PKIXVerifyCert(*certout, pkixusage, cvin, cvout, NULL); //CERT_DestroyCertList(trustcl); } else { cur_log = &vfy_log; vfyredo: rv = CERT_VerifyCert(handle, *certout, PR_TRUE, usage, PR_Now(), NULL, cur_log); } if (rv != SECSuccess || cur_log->count > 0) { if (cur_log->count > 0 && cur_log->head != NULL) { fin = err_stat(cur_log->head); } else { fin = PORT_GetError(); } if (fin == SEC_ERROR_INADEQUATE_KEY_USAGE) { printf("SEC_ERROR_INADEQUATE_KEY_USAGE : Certificate key usage inadequate for attempted operation.\n" ); } else if (fin == SEC_ERROR_INADEQUATE_CERT_TYPE) { printf("SEC_ERROR_INADEQUATE_CERT_TYPE : Certificate type not approved for application.\n" ); } else { printf("OTHER : %ld", fin); } } if ((fin == SEC_ERROR_INADEQUATE_CERT_TYPE || fin == SEC_ERROR_INADEQUATE_KEY_USAGE) && retry_verify && !retried) { printf("Retrying verification\n"); fin = 0; retried = PR_TRUE; if (use_pkix) { pkixout[0].value.pointer.log = &vfy_log2; cur_log = &vfy_log2; pkixout[1].value.pointer.chain = NULL; if (pkixusage == certificateUsageSSLClient) { pkixusage = certificateUsageSSLServer; } else { pkixusage = certificateUsageSSLClient; } goto pkixredo; } else { if (usage == certUsageSSLClient) { usage = certUsageSSLServer; } else { usage = certUsageSSLClient; } goto vfyredo; } } PORT_FreeArena(vfy_log.arena, PR_FALSE); PORT_FreeArena(vfy_log2.arena, PR_FALSE); NSS_Shutdown(); exit(fin == 0 ? 0 : 1); }