Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/*
* pkix_pl_aiamgr.c
*
* AIAMgr Object Definitions
*
*/
#include "pkix_pl_aiamgr.h"
extern PKIX_PL_HashTable *aiaConnectionCache;
#ifndef NSS_PKIX_NO_LDAP
/* --Virtual-LdapClient-Functions------------------------------------ */
PKIX_Error *
PKIX_PL_LdapClient_InitiateRequest(
PKIX_PL_LdapClient *client,
LDAPRequestParams *requestParams,
void **pNBIO,
PKIX_List **pResponse,
void *plContext)
{
PKIX_ENTER(LDAPCLIENT, "PKIX_PL_LdapClient_InitiateRequest");
PKIX_NULLCHECK_TWO(client, client->initiateFcn);
PKIX_CHECK(client->initiateFcn
(client, requestParams, pNBIO, pResponse, plContext),
PKIX_LDAPCLIENTINITIATEREQUESTFAILED);
cleanup:
PKIX_RETURN(LDAPCLIENT);
}
PKIX_Error *
PKIX_PL_LdapClient_ResumeRequest(
PKIX_PL_LdapClient *client,
void **pNBIO,
PKIX_List **pResponse,
void *plContext)
{
PKIX_ENTER(LDAPCLIENT, "PKIX_PL_LdapClient_ResumeRequest");
PKIX_NULLCHECK_TWO(client, client->resumeFcn);
PKIX_CHECK(client->resumeFcn
(client, pNBIO, pResponse, plContext),
PKIX_LDAPCLIENTRESUMEREQUESTFAILED);
cleanup:
PKIX_RETURN(LDAPCLIENT);
}
#endif /* !NSS_PKIX_NO_LDAP */
/* --Private-AIAMgr-Functions----------------------------------*/
/*
* FUNCTION: pkix_pl_AIAMgr_Destroy
* (see comments for PKIX_PL_DestructorCallback in pkix_pl_pki.h)
*/
static PKIX_Error *
pkix_pl_AIAMgr_Destroy(
PKIX_PL_Object *object,
void *plContext)
{
PKIX_PL_AIAMgr *aiaMgr = NULL;
PKIX_ENTER(AIAMGR, "pkix_pl_AIAMgr_Destroy");
PKIX_NULLCHECK_ONE(object);
PKIX_CHECK(pkix_CheckType(object, PKIX_AIAMGR_TYPE, plContext),
PKIX_OBJECTNOTAIAMGR);
aiaMgr = (PKIX_PL_AIAMgr *)object;
/* pointer to cert cache */
/* pointer to crl cache */
aiaMgr->method = 0;
aiaMgr->aiaIndex = 0;
aiaMgr->numAias = 0;
PKIX_DECREF(aiaMgr->aia);
PKIX_DECREF(aiaMgr->location);
PKIX_DECREF(aiaMgr->results);
#ifndef NSS_PKIX_NO_LDAP
PKIX_DECREF(aiaMgr->client.ldapClient);
#endif
cleanup:
PKIX_RETURN(AIAMGR);
}
/*
* FUNCTION: pkix_pl_AIAMgr_RegisterSelf
* DESCRIPTION:
* Registers PKIX_AIAMGR_TYPE and its related functions with systemClasses[]
* THREAD SAFETY:
* Not Thread Safe - for performance and complexity reasons
*
* Since this function is only called by PKIX_PL_Initialize, which should
* only be called once, it is acceptable that this function is not
* thread-safe.
*/
PKIX_Error *
pkix_pl_AIAMgr_RegisterSelf(void *plContext)
{
extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
pkix_ClassTable_Entry *entry = &systemClasses[PKIX_AIAMGR_TYPE];
PKIX_ENTER(AIAMGR, "pkix_pl_AIAMgr_RegisterSelf");
entry->description = "AIAMgr";
entry->typeObjectSize = sizeof(PKIX_PL_AIAMgr);
entry->destructor = pkix_pl_AIAMgr_Destroy;
PKIX_RETURN(AIAMGR);
}
#ifndef NSS_PKIX_NO_LDAP
/*
* FUNCTION: pkix_pl_AiaMgr_FindLDAPClient
* DESCRIPTION:
*
* This function checks the collection of LDAPClient connections held by the
* AIAMgr pointed to by "aiaMgr" for one matching the domain name given by
* "domainName". The string may include a port number: e.g., "betty.nist.gov"
* or "nss.red.iplanet.com:1389". If a match is found, that LDAPClient is
* stored at "pClient". Otherwise, an LDAPClient is created and added to the
* collection, and then stored at "pClient".
*
* PARAMETERS:
* "aiaMgr"
* The AIAMgr whose LDAPClient connected are to be managed. Must be
* non-NULL.
* "domainName"
* Address of a string pointing to a server name. Must be non-NULL.
* An empty string (which means no <host> is given in the LDAP URL) is
* not supported.
* "pClient"
* Address at which the returned LDAPClient is stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an AIAMgr Error if the function fails in a non-fatal way
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
static PKIX_Error *
pkix_pl_AiaMgr_FindLDAPClient(
PKIX_PL_AIAMgr *aiaMgr,
char *domainName,
PKIX_PL_LdapClient **pClient,
void *plContext)
{
PKIX_PL_String *domainString = NULL;
PKIX_PL_LdapDefaultClient *client = NULL;
PKIX_ENTER(AIAMGR, "pkix_pl_AiaMgr_FindLDAPClient");
PKIX_NULLCHECK_THREE(aiaMgr, domainName, pClient);
/*
* An LDAP URL may not have a <host> part, for example,
* PKIX_PL_LdapDefaultClient doesn't know how to discover the default
* LDAP server, so we don't support this kind of LDAP URL.
*/
if (*domainName == '\0') {
/* Simulate a PKIX_PL_LdapDefaultClient_CreateByName failure. */
PKIX_ERROR(PKIX_LDAPDEFAULTCLIENTCREATEBYNAMEFAILED);
}
/* create PKIX_PL_String from domain name */
PKIX_CHECK(PKIX_PL_String_Create
(PKIX_ESCASCII, domainName, 0, &domainString, plContext),
PKIX_STRINGCREATEFAILED);
/* Is this domainName already in cache? */
PKIX_CHECK(PKIX_PL_HashTable_Lookup
(aiaConnectionCache,
(PKIX_PL_Object *)domainString,
(PKIX_PL_Object **)&client,
plContext),
PKIX_HASHTABLELOOKUPFAILED);
if (client == NULL) {
/* No, create a connection (and cache it) */
PKIX_CHECK(PKIX_PL_LdapDefaultClient_CreateByName
(domainName,
/* Do not use NBIO until we verify, that
* it is working. For now use 1 min timeout. */
PR_SecondsToInterval(
((PKIX_PL_NssContext*)plContext)->timeoutSeconds),
NULL,
&client,
plContext),
PKIX_LDAPDEFAULTCLIENTCREATEBYNAMEFAILED);
PKIX_CHECK(PKIX_PL_HashTable_Add
(aiaConnectionCache,
(PKIX_PL_Object *)domainString,
(PKIX_PL_Object *)client,
plContext),
PKIX_HASHTABLEADDFAILED);
}
*pClient = (PKIX_PL_LdapClient *)client;
cleanup:
PKIX_DECREF(domainString);
PKIX_RETURN(AIAMGR);
}
#endif /* !NSS_PKIX_NO_LDAP */
PKIX_Error *
pkix_pl_AIAMgr_GetHTTPCerts(
PKIX_PL_AIAMgr *aiaMgr,
PKIX_PL_InfoAccess *ia,
void **pNBIOContext,
PKIX_List **pCerts,
void *plContext)
{
PKIX_PL_GeneralName *location = NULL;
PKIX_PL_String *locationString = NULL;
PKIX_UInt32 len = 0;
PRUint16 port = 0;
const SEC_HttpClientFcn *httpClient = NULL;
const SEC_HttpClientFcnV1 *hcv1 = NULL;
SECStatus rv = SECFailure;
SEC_HTTP_SERVER_SESSION serverSession = NULL;
SEC_HTTP_REQUEST_SESSION requestSession = NULL;
char *path = NULL;
char *hostname = NULL;
char *locationAscii = NULL;
void *nbio = NULL;
PRUint16 responseCode = 0;
const char *responseContentType = NULL;
const char *responseData = NULL;
PKIX_ENTER(AIAMGR, "pkix_pl_AIAMgr_GetHTTPCerts");
PKIX_NULLCHECK_FOUR(aiaMgr, ia, pNBIOContext, pCerts);
nbio = *pNBIOContext;
*pNBIOContext = NULL;
*pCerts = NULL;
if (nbio == NULL) { /* a new request */
PKIX_CHECK(PKIX_PL_InfoAccess_GetLocation
(ia, &location, plContext),
PKIX_INFOACCESSGETLOCATIONFAILED);
/* find or create httpClient = default client */
httpClient = SEC_GetRegisteredHttpClient();
aiaMgr->client.hdata.httpClient = httpClient;
if (!httpClient)
PKIX_ERROR(PKIX_OUTOFMEMORY);
if (httpClient->version == 1) {
PKIX_UInt32 timeout =
((PKIX_PL_NssContext*)plContext)->timeoutSeconds;
hcv1 = &(httpClient->fcnTable.ftable1);
/* create server session */
PKIX_TOSTRING(location, &locationString, plContext,
PKIX_GENERALNAMETOSTRINGFAILED);
PKIX_CHECK(PKIX_PL_String_GetEncoded
(locationString,
PKIX_ESCASCII,
(void **)&locationAscii,
&len,
plContext),
PKIX_STRINGGETENCODEDFAILED);
rv = CERT_ParseURL(locationAscii, &hostname, &port,
&path);
if ((rv != SECSuccess) ||
(hostname == NULL) ||
(path == NULL)) {
PKIX_ERROR(PKIX_URLPARSINGFAILED);
}
rv = (*hcv1->createSessionFcn)(hostname, port,
&serverSession);
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_HTTPCLIENTCREATESESSIONFAILED);
}
aiaMgr->client.hdata.serverSession = serverSession;
/* create request session */
rv = (*hcv1->createFcn)(serverSession, "http", path,
"GET", PR_SecondsToInterval(timeout),
&requestSession);
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_HTTPSERVERERROR);
}
aiaMgr->client.hdata.requestSession = requestSession;
} else {
PKIX_ERROR(PKIX_UNSUPPORTEDVERSIONOFHTTPCLIENT);
}
}
httpClient = aiaMgr->client.hdata.httpClient;
if (httpClient->version == 1) {
PRUint32 responseDataLen =
((PKIX_PL_NssContext*)plContext)->maxResponseLength;
hcv1 = &(httpClient->fcnTable.ftable1);
requestSession = aiaMgr->client.hdata.requestSession;
/* trySendAndReceive */
rv = (*hcv1->trySendAndReceiveFcn)(requestSession,
(PRPollDesc **)&nbio,
&responseCode,
(const char **)&responseContentType,
NULL, /* &responseHeaders */
(const char **)&responseData,
&responseDataLen);
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_HTTPSERVERERROR);
}
if (nbio != 0) {
*pNBIOContext = nbio;
goto cleanup;
}
PKIX_CHECK(pkix_pl_HttpCertStore_ProcessCertResponse
(responseCode,
responseContentType,
responseData,
responseDataLen,
pCerts,
plContext),
PKIX_HTTPCERTSTOREPROCESSCERTRESPONSEFAILED);
/* Session and request cleanup in case of success */
if (aiaMgr->client.hdata.requestSession != NULL) {
(*hcv1->freeFcn)(aiaMgr->client.hdata.requestSession);
aiaMgr->client.hdata.requestSession = NULL;
}
if (aiaMgr->client.hdata.serverSession != NULL) {
(*hcv1->freeSessionFcn)(aiaMgr->client.hdata.serverSession);
aiaMgr->client.hdata.serverSession = NULL;
}
aiaMgr->client.hdata.httpClient = 0; /* callback fn */
} else {
PKIX_ERROR(PKIX_UNSUPPORTEDVERSIONOFHTTPCLIENT);
}
cleanup:
/* Session and request cleanup in case of error. Passing through without cleanup
* if interrupted by blocked IO. */
if (PKIX_ERROR_RECEIVED) {
if (aiaMgr->client.hdata.requestSession != NULL) {
(*hcv1->freeFcn)(aiaMgr->client.hdata.requestSession);
aiaMgr->client.hdata.requestSession = NULL;
}
if (aiaMgr->client.hdata.serverSession != NULL) {
(*hcv1->freeSessionFcn)(aiaMgr->client.hdata.serverSession);
aiaMgr->client.hdata.serverSession = NULL;
}
aiaMgr->client.hdata.httpClient = 0; /* callback fn */
}
PKIX_DECREF(location);
PKIX_DECREF(locationString);
if (locationAscii) {
PORT_Free(locationAscii);
}
if (hostname) {
PORT_Free(hostname);
}
if (path) {
PORT_Free(path);
}
PKIX_RETURN(AIAMGR);
}
#ifndef NSS_PKIX_NO_LDAP
PKIX_Error *
pkix_pl_AIAMgr_GetLDAPCerts(
PKIX_PL_AIAMgr *aiaMgr,
PKIX_PL_InfoAccess *ia,
void **pNBIOContext,
PKIX_List **pCerts,
void *plContext)
{
PKIX_List *result = NULL;
PKIX_PL_GeneralName *location = NULL;
PKIX_PL_LdapClient *client = NULL;
LDAPRequestParams request;
PLArenaPool *arena = NULL;
char *domainName = NULL;
void *nbio = NULL;
PKIX_ENTER(AIAMGR, "pkix_pl_AIAMgr_GetLDAPCerts");
PKIX_NULLCHECK_FOUR(aiaMgr, ia, pNBIOContext, pCerts);
nbio = *pNBIOContext;
*pNBIOContext = NULL;
*pCerts = NULL;
if (nbio == NULL) { /* a new request */
/* Initiate an LDAP request */
request.scope = WHOLE_SUBTREE;
request.derefAliases = NEVER_DEREF;
request.sizeLimit = 0;
request.timeLimit = 0;
PKIX_CHECK(PKIX_PL_InfoAccess_GetLocation
(ia, &location, plContext),
PKIX_INFOACCESSGETLOCATIONFAILED);
/*
* Get a short-lived arena. We'll be done with
* this space once the request is encoded.
*/
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
PKIX_ERROR_FATAL(PKIX_OUTOFMEMORY);
}
PKIX_CHECK(pkix_pl_InfoAccess_ParseLocation
(location, arena, &request, &domainName, plContext),
PKIX_INFOACCESSPARSELOCATIONFAILED);
PKIX_DECREF(location);
/* Find or create a connection to LDAP server */
PKIX_CHECK(pkix_pl_AiaMgr_FindLDAPClient
(aiaMgr, domainName, &client, plContext),
PKIX_AIAMGRFINDLDAPCLIENTFAILED);
aiaMgr->client.ldapClient = client;
PKIX_CHECK(PKIX_PL_LdapClient_InitiateRequest
(aiaMgr->client.ldapClient,
&request,
&nbio,
&result,
plContext),
PKIX_LDAPCLIENTINITIATEREQUESTFAILED);
PKIX_PL_NSSCALL(AIAMGR, PORT_FreeArena, (arena, PR_FALSE));
} else {
PKIX_CHECK(PKIX_PL_LdapClient_ResumeRequest
(aiaMgr->client.ldapClient, &nbio, &result, plContext),
PKIX_LDAPCLIENTRESUMEREQUESTFAILED);
}
if (nbio != NULL) { /* WOULDBLOCK */
*pNBIOContext = nbio;
*pCerts = NULL;
goto cleanup;
}
PKIX_DECREF(aiaMgr->client.ldapClient);
if (result == NULL) {
*pCerts = NULL;
} else {
PKIX_CHECK(pkix_pl_LdapCertStore_BuildCertList
(result, pCerts, plContext),
PKIX_LDAPCERTSTOREBUILDCERTLISTFAILED);
}
*pNBIOContext = nbio;
cleanup:
if (arena && (PKIX_ERROR_RECEIVED)) {
PKIX_PL_NSSCALL(AIAMGR, PORT_FreeArena, (arena, PR_FALSE));
}
if (PKIX_ERROR_RECEIVED) {
PKIX_DECREF(aiaMgr->client.ldapClient);
}
PKIX_DECREF(location);
PKIX_RETURN(AIAMGR);
}
#endif /* !NSS_PKIX_NO_LDAP */
/*
* FUNCTION: PKIX_PL_AIAMgr_Create
* DESCRIPTION:
*
* This function creates an AIAMgr, storing the result at "pAIAMgr".
*
* PARAMETERS:
* "pAIAMGR"
* Address at which the returned AIAMgr is stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an AIAMgr Error if the function fails in a non-fatal way
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
PKIX_PL_AIAMgr_Create(
PKIX_PL_AIAMgr **pAIAMgr,
void *plContext)
{
PKIX_PL_AIAMgr *aiaMgr = NULL;
PKIX_ENTER(AIAMGR, "PKIX_PL_AIAMgr_Create");
PKIX_NULLCHECK_ONE(pAIAMgr);
PKIX_CHECK(PKIX_PL_Object_Alloc
(PKIX_AIAMGR_TYPE,
sizeof(PKIX_PL_AIAMgr),
(PKIX_PL_Object **)&aiaMgr,
plContext),
PKIX_COULDNOTCREATEAIAMGROBJECT);
/* pointer to cert cache */
/* pointer to crl cache */
aiaMgr->method = 0;
aiaMgr->aiaIndex = 0;
aiaMgr->numAias = 0;
aiaMgr->aia = NULL;
aiaMgr->location = NULL;
aiaMgr->results = NULL;
aiaMgr->client.hdata.httpClient = NULL;
aiaMgr->client.hdata.serverSession = NULL;
aiaMgr->client.hdata.requestSession = NULL;
*pAIAMgr = aiaMgr;
cleanup:
PKIX_RETURN(AIAMGR);
}
/* --Public-Functions------------------------------------------------------- */
/*
* FUNCTION: PKIX_PL_AIAMgr_GetAIACerts (see description in pkix_pl_pki.h)
*/
PKIX_Error *
PKIX_PL_AIAMgr_GetAIACerts(
PKIX_PL_AIAMgr *aiaMgr,
PKIX_PL_Cert *prevCert,
void **pNBIOContext,
PKIX_List **pCerts,
void *plContext)
{
PKIX_UInt32 numAias = 0;
PKIX_UInt32 aiaIndex = 0;
PKIX_UInt32 iaType = PKIX_INFOACCESS_LOCATION_UNKNOWN;
PKIX_List *certs = NULL;
PKIX_PL_InfoAccess *ia = NULL;
void *nbio = NULL;
PKIX_ENTER(AIAMGR, "PKIX_PL_AIAMgr_GetAIACerts");
PKIX_NULLCHECK_FOUR(aiaMgr, prevCert, pNBIOContext, pCerts);
nbio = *pNBIOContext;
*pCerts = NULL;
*pNBIOContext = NULL;
if (nbio == NULL) { /* a new request */
/* Does this Cert have an AIA extension? */
PKIX_CHECK(PKIX_PL_Cert_GetAuthorityInfoAccess
(prevCert, &aiaMgr->aia, plContext),
PKIX_CERTGETAUTHORITYINFOACCESSFAILED);
if (aiaMgr->aia != NULL) {
PKIX_CHECK(PKIX_List_GetLength
(aiaMgr->aia, &numAias, plContext),
PKIX_LISTGETLENGTHFAILED);
}
/* And if so, does it have any entries? */
if ((aiaMgr->aia == NULL) || (numAias == 0)) {
*pCerts = NULL;
goto cleanup;
}
aiaMgr->aiaIndex = 0;
aiaMgr->numAias = numAias;
aiaMgr->results = NULL;
}
for (aiaIndex = aiaMgr->aiaIndex;
aiaIndex < aiaMgr->numAias;
aiaIndex ++) {
PKIX_UInt32 method = 0;
PKIX_CHECK(PKIX_List_GetItem
(aiaMgr->aia,
aiaIndex,
(PKIX_PL_Object **)&ia,
plContext),
PKIX_LISTGETITEMFAILED);
PKIX_CHECK(PKIX_PL_InfoAccess_GetMethod
(ia, &method, plContext),
PKIX_INFOACCESSGETMETHODFAILED);
if (method != PKIX_INFOACCESS_CA_ISSUERS &&
method != PKIX_INFOACCESS_CA_REPOSITORY) {
PKIX_DECREF(ia);
continue;
}
PKIX_CHECK(PKIX_PL_InfoAccess_GetLocationType
(ia, &iaType, plContext),
PKIX_INFOACCESSGETLOCATIONTYPEFAILED);
if (iaType == PKIX_INFOACCESS_LOCATION_HTTP) {
PKIX_CHECK(pkix_pl_AIAMgr_GetHTTPCerts
(aiaMgr, ia, &nbio, &certs, plContext),
PKIX_AIAMGRGETHTTPCERTSFAILED);
#ifndef NSS_PKIX_NO_LDAP
} else if (iaType == PKIX_INFOACCESS_LOCATION_LDAP) {
PKIX_CHECK(pkix_pl_AIAMgr_GetLDAPCerts
(aiaMgr, ia, &nbio, &certs, plContext),
PKIX_AIAMGRGETLDAPCERTSFAILED);
#endif
} else {
/* We only support http and ldap requests. */
PKIX_DECREF(ia);
continue;
}
if (nbio != NULL) { /* WOULDBLOCK */
aiaMgr->aiaIndex = aiaIndex;
*pNBIOContext = nbio;
*pCerts = NULL;
goto cleanup;
}
/*
* We can't just use and modify the List we received.
* Because it's cached, it's set immutable.
*/
if (aiaMgr->results == NULL) {
PKIX_CHECK(PKIX_List_Create
(&(aiaMgr->results), plContext),
PKIX_LISTCREATEFAILED);
}
PKIX_CHECK(pkix_List_AppendList
(aiaMgr->results, certs, plContext),
PKIX_APPENDLISTFAILED);
PKIX_DECREF(certs);
PKIX_DECREF(ia);
}
PKIX_DECREF(aiaMgr->aia);
*pNBIOContext = NULL;
*pCerts = aiaMgr->results;
aiaMgr->results = NULL;
cleanup:
if (PKIX_ERROR_RECEIVED) {
PKIX_DECREF(aiaMgr->aia);
PKIX_DECREF(aiaMgr->results);
#ifndef NSS_PKIX_NO_LDAP
PKIX_DECREF(aiaMgr->client.ldapClient);
#endif
}
PKIX_DECREF(certs);
PKIX_DECREF(ia);
PKIX_RETURN(AIAMGR);
}