--- /dev/null
+/*
+ * Copyright (c) 2000,2002,2011-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * ocspdClient.cpp - Client interface to OCSP helper daemon
+ */
+
+#include "ocspdClient.h"
+#include "ocspdTypes.h"
+#include "ocspdDebug.h"
+#include <Security/cssmapple.h>
+#include <security_utilities/threading.h>
+#include <security_utilities/mach++.h>
+#include <security_utilities/unix++.h>
+#include <security_ocspd/ocspd.h> /* MIG interface */
+#include <Security/SecBase.h>
+class ocspdGlobals
+{
+public:
+ ocspdGlobals();
+ ~ocspdGlobals();
+ mach_port_t serverPort();
+private:
+ UnixPlusPlus::ForkMonitor mForkMonitor;
+ MachPlusPlus::Port mServerPort;
+ Mutex mLock;
+};
+
+ocspdGlobals::ocspdGlobals()
+ : mServerPort(0)
+{
+ /* nothing here, the real work is done in serverPort() */
+}
+
+ocspdGlobals::~ocspdGlobals()
+{
+ /* I don't believe this should ever execute */
+}
+
+mach_port_t ocspdGlobals::serverPort()
+{
+ StLock<Mutex> _(mLock);
+
+ // Guard against fork-without-exec. If we are the child of a fork
+ // (that has not exec'ed), our apparent connection to SecurityServer
+ // is just a mirage, and we better reset it.
+ mach_port_t rtnPort = mServerPort.port();
+ if (mForkMonitor()) {
+ rtnPort = 0;
+ }
+ if(rtnPort != 0) {
+ return rtnPort;
+ }
+
+ const char *serverName = NULL;
+ #ifndef NDEBUG
+ serverName = getenv(OCSPD_BOOTSTRAP_ENV);
+ #endif
+ if(serverName == NULL) {
+ serverName = (char*) OCSPD_BOOTSTRAP_NAME;
+ }
+ try {
+ mServerPort = MachPlusPlus::Bootstrap().lookup2(serverName);
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdGlobals: error contacting server\n");
+ throw;
+ }
+ return mServerPort;
+}
+
+static ModuleNexus<ocspdGlobals> OcspdGlobals;
+
+/*
+ * Perform network fetch of an OCSP response. Result is not verified in any
+ * way.
+ */
+CSSM_RETURN ocspdFetch(
+ Allocator &alloc,
+ const CSSM_DATA &ocspdReq, // DER-encoded SecAsn1OCSPDRequests
+ CSSM_DATA &ocspdResp) // DER-encoded kSecAsn1OCSPDReplies
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+ unsigned char *rtnData = NULL;
+ unsigned rtnLen = 0;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdFetch: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+
+ krtn = ocsp_client_ocspdFetch(serverPort, ocspdReq.Data, (mach_msg_type_number_t)ocspdReq.Length,
+ (void **)&rtnData, &rtnLen);
+ if(krtn) {
+ ocspdErrorLog("ocspdFetch: RPC returned %d\n", krtn);
+ return CSSMERR_APPLETP_OCSP_UNAVAILABLE;
+ }
+ if((rtnData == NULL) || (rtnLen == 0)) {
+ ocspdErrorLog("ocspdFetch: RPC returned NULL data\n");
+ return CSSMERR_APPLETP_OCSP_UNAVAILABLE;
+ }
+ ocspdResp.Data = (uint8 *)alloc.malloc(rtnLen);
+ ocspdResp.Length = rtnLen;
+ memmove(ocspdResp.Data, rtnData, rtnLen);
+ mig_deallocate((vm_address_t)rtnData, rtnLen);
+ return CSSM_OK;
+}
+
+/*
+ * Flush all responses associated with specifed CertID from cache.
+ */
+CSSM_RETURN ocspdCacheFlush(
+ const CSSM_DATA &certID)
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCacheFlush: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+ krtn = ocsp_client_ocspdCacheFlush(serverPort, certID.Data, (mach_msg_type_number_t)certID.Length);
+ if(krtn) {
+ ocspdErrorLog("ocspdCacheFlush: RPC returned %d\n", krtn);
+ return CSSMERR_APPLETP_OCSP_UNAVAILABLE;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * Flush stale entries from cache.
+ */
+CSSM_RETURN ocspdCacheFlushStale()
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCacheFlush: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+ krtn = ocsp_client_ocspdCacheFlushStale(serverPort);
+ if(krtn) {
+ ocspdErrorLog("ocsp_client_ocspdCacheFlushStale: RPC returned %d\n", krtn);
+ return (CSSM_RETURN)krtn;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * fetch a certificate from the net.
+ */
+CSSM_RETURN ocspdCertFetch(
+ Allocator &alloc,
+ const CSSM_DATA &certURL,
+ CSSM_DATA &certData) // mallocd via alloc and RETURNED
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+ unsigned char *rtnData = NULL;
+ unsigned rtnLen = 0;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCertFetch: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+
+ krtn = ocsp_client_certFetch(serverPort, certURL.Data, (mach_msg_type_number_t)certURL.Length,
+ (void **)&rtnData, &rtnLen);
+ if(krtn) {
+ ocspdErrorLog("ocspdCertFetch: RPC returned %d\n", krtn);
+ return CSSMERR_APPLETP_NETWORK_FAILURE;
+ }
+
+ if((rtnData == NULL) || (rtnLen == 0)) {
+ ocspdErrorLog("ocspdCertFetch: RPC returned NULL data\n");
+ return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
+ }
+ certData.Data = (uint8 *)alloc.malloc(rtnLen);
+ certData.Length = rtnLen;
+ memmove(certData.Data, rtnData, rtnLen);
+ mig_deallocate((vm_address_t)rtnData, rtnLen);
+ return CSSM_OK;
+}
+
+/*
+ * Fetch a CRL from net with optional cache lookup and store.
+ * verifyTime only used for cache lookup.
+ */
+CSSM_RETURN ocspdCRLFetch(
+ Allocator &alloc,
+ const CSSM_DATA &crlURL,
+ const CSSM_DATA *crlIssuer, // optional
+ bool cacheReadEnable,
+ bool cacheWriteEnable,
+ CSSM_TIMESTRING verifyTime,
+ CSSM_DATA &crlData) // mallocd via alloc and RETURNED
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+ unsigned char *rtnData = NULL;
+ unsigned rtnLen = 0;
+
+ if(verifyTime == NULL) {
+ ocspdErrorLog("ocspdCRLFetch: verifyTime NOT OPTIONAL\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCRLFetch: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+
+ krtn = ocsp_client_crlFetch(serverPort, crlURL.Data, (mach_msg_type_number_t)crlURL.Length,
+ crlIssuer ? crlIssuer->Data : NULL, crlIssuer ? (mach_msg_type_number_t)crlIssuer->Length : 0,
+ cacheReadEnable, cacheWriteEnable,
+ verifyTime, (mach_msg_type_number_t)strlen(verifyTime),
+ (void **)&rtnData, &rtnLen);
+ if(krtn) {
+ ocspdErrorLog("ocspdCRLFetch: RPC returned %d\n", krtn);
+ return CSSMERR_APPLETP_NETWORK_FAILURE;
+ }
+
+ if((rtnData == NULL) || (rtnLen == 0)) {
+ ocspdErrorLog("ocspdCRLFetch: RPC returned NULL data\n");
+ return CSSMERR_APPLETP_CRL_NOT_FOUND;
+ }
+ crlData.Data = (uint8 *)alloc.malloc(rtnLen);
+ crlData.Length = rtnLen;
+ memmove(crlData.Data, rtnData, rtnLen);
+ mig_deallocate((vm_address_t)rtnData, rtnLen);
+ return CSSM_OK;
+}
+
+
+/*
+ * Get CRL status for given serial number and issuing entity
+ */
+CSSM_RETURN ocspdCRLStatus(
+ const CSSM_DATA &serialNumber,
+ const CSSM_DATA &issuers,
+ const CSSM_DATA *crlIssuer, // optional if URL is supplied
+ const CSSM_DATA *crlURL) // optional if issuer is supplied
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+
+ if(!crlIssuer && !crlURL) {
+ ocspdErrorLog("ocspdCRLStatus: either an issuer or URL is required\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCRLStatus: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+
+ krtn = ocsp_client_crlStatus(serverPort,
+ serialNumber.Data, (mach_msg_type_number_t)serialNumber.Length,
+ issuers.Data, (mach_msg_type_number_t)issuers.Length,
+ crlIssuer ? crlIssuer->Data : NULL, crlIssuer ? (mach_msg_type_number_t)crlIssuer->Length : 0,
+ crlURL ? crlURL->Data : NULL, crlURL ? (mach_msg_type_number_t)crlURL->Length : 0);
+
+ return krtn;
+}
+
+/*
+ * Refresh the CRL cache.
+ */
+CSSM_RETURN ocspdCRLRefresh(
+ unsigned staleDays,
+ unsigned expireOverlapSeconds,
+ bool purgeAll,
+ bool fullCryptoVerify)
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCRLRefresh: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+
+ krtn = ocsp_client_crlRefresh(serverPort, staleDays, expireOverlapSeconds,
+ purgeAll, fullCryptoVerify);
+ if(krtn) {
+ ocspdErrorLog("ocspdCRLRefresh: RPC returned %d\n", krtn);
+ return CSSMERR_APPLETP_NETWORK_FAILURE;
+ }
+
+ return CSSM_OK;
+}
+
+/*
+ * Flush all CRLs obtained from specified URL from cache. Called by client when
+ * *it* detects a bad CRL.
+ */
+CSSM_RETURN ocspdCRLFlush(
+ const CSSM_DATA &crlURL)
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdCRLFlush: OCSPD server error\n");
+ return CSSMERR_TP_INTERNAL_ERROR;
+ }
+
+ krtn = ocsp_client_crlFlush(serverPort, crlURL.Data, (mach_msg_type_number_t)crlURL.Length);
+ if(krtn) {
+ ocspdErrorLog("ocspdCRLFlush: RPC returned %d\n", krtn);
+ return CSSMERR_APPLETP_NETWORK_FAILURE;
+ }
+ return CSSM_OK;
+}
+
+/*
+ * Obtain TrustSettings.
+ */
+OSStatus ocspdTrustSettingsRead(
+ Allocator &alloc,
+ SecTrustSettingsDomain domain,
+ CSSM_DATA &trustSettings) // mallocd via alloc and RETURNED
+{
+ mach_port_t serverPort = 0;
+ kern_return_t krtn;
+ unsigned char *rtnData = NULL;
+ unsigned rtnLen = 0;
+ OSStatus ortn;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdTrustSettingsRead: OCSPD server error\n");
+ return errSecInternalComponent;
+ }
+
+ krtn = ocsp_client_trustSettingsRead(serverPort, domain,
+ (void **)&rtnData, &rtnLen, &ortn);
+ if(krtn) {
+ ocspdErrorLog("ocspdTrustSettingsRead: RPC returned %d\n", krtn);
+ return errSecNotAvailable;
+ }
+ if(ortn) {
+ /* e.g., errSecNoUserTrustRecord */
+ return ortn;
+ }
+ if((rtnData == NULL) || (rtnLen == 0)) {
+ ocspdErrorLog("ocspdTrustSettingsRead: RPC returned NULL data\n");
+ return errSecItemNotFound;
+ }
+ trustSettings.Data = (uint8 *)alloc.malloc(rtnLen);
+ trustSettings.Length = rtnLen;
+ memmove(trustSettings.Data, rtnData, rtnLen);
+ mig_deallocate((vm_address_t)rtnData, rtnLen);
+ return errSecSuccess;
+}
+
+/*
+ * Write TrustSettings to disk. Results in authentication dialog.
+ */
+OSStatus ocspdTrustSettingsWrite(
+ SecTrustSettingsDomain domain,
+ const CSSM_DATA &authBlob,
+ const CSSM_DATA &trustSettings)
+{
+ mach_port_t serverPort = 0;
+ mach_port_t clientPort = 0;
+ kern_return_t krtn;
+ OSStatus ortn;
+
+ try {
+ serverPort = OcspdGlobals().serverPort();
+ clientPort = MachPlusPlus::Bootstrap();
+ }
+ catch(...) {
+ ocspdErrorLog("ocspdTrustSettingsWrite: OCSPD server error\n");
+ return errSecInternalComponent;
+ }
+
+ krtn = ocsp_client_trustSettingsWrite(serverPort, clientPort, domain,
+ authBlob.Data, (mach_msg_type_number_t)authBlob.Length,
+ trustSettings.Data, (mach_msg_type_number_t)trustSettings.Length,
+ &ortn);
+ if(krtn) {
+ ocspdErrorLog("ocspdTrustSettingsWrite: RPC returned %d\n", krtn);
+ return errSecInternalComponent;
+ }
+ return ortn;
+}