/* 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 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * 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 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 part, for example, * ldap:///o=University%20of%20Michigan,c=US * 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); }