]> git.saurik.com Git - apple/security.git/blobdiff - sec/securityd/SecOCSPCache.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / securityd / SecOCSPCache.c
index 614b3cc3f9f0b0ca84971a711ba8331408607d7e..1ae99dbed5951534f54f1ea79745b1bd66190c7e 100644 (file)
  *  SecOCSPCache.c - securityd
  */
 
  *  SecOCSPCache.c - securityd
  */
 
+#include <CoreFoundation/CFUtilities.h>
+#include <CoreFoundation/CFString.h>
 #include <securityd/SecOCSPCache.h>
 #include <securityd/SecOCSPCache.h>
-#include <security_utilities/debugging.h>
+#include <utilities/debugging.h>
 #include <Security/SecCertificateInternal.h>
 #include <Security/SecFramework.h>
 #include <Security/SecInternal.h>
 #include <Security/SecCertificateInternal.h>
 #include <Security/SecFramework.h>
 #include <Security/SecInternal.h>
-#include <sqlite3.h>
 #include <AssertMacros.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <AssertMacros.h>
 #include <stdlib.h>
 #include <limits.h>
-#include <string.h>
-#include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <errno.h>
-#include <pthread.h>
 #include <asl.h>
 #include <asl.h>
-#include "sqlutils.h"
-
-#define ocspErrorLog(args...)     asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
-
-static const char expireSQL[] = "DELETE FROM responses WHERE expires<?";
-static const char beginTxnSQL[] = "BEGIN EXCLUSIVE TRANSACTION";
-static const char endTxnSQL[] = "COMMIT TRANSACTION";
-static const char insertResponseSQL[] = "INSERT INTO responses "
-    "(ocspResponse,responderURI,expires,lastUsed) VALUES (?,?,?,?)";
-static const char insertLinkSQL[] = "INSERT INTO ocsp (hashAlgorithm,"
-    "issuerNameHash,issuerPubKeyHash,serialNum,responseId) VALUES (?,?,?,?,?)";
-static const char selectHashAlgorithmSQL[] = "SELECT DISTINCT hashAlgorithm "
-    "FROM ocsp WHERE serialNum=?";
-static const char selectResponseSQL[] = "SELECT ocspResponse,responseId FROM "
-    "responses WHERE responseId=(SELECT responseId FROM ocsp WHERE "
-    "issuerNameHash=? AND issuerPubKeyHash=? AND serialNum=? AND hashAlgorithm=?)"
-    " ORDER BY expires DESC";
-
-#if NO_SERVER
-CF_EXPORT
-CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName);     /* Pass NULL for the current user's home directory */
-#endif
-
-#define kSecOCSPCachePath "/Library/Keychains/ocspcache.sqlite3";
+#include "utilities/SecDb.h"
+#include "utilities/SecFileLocations.h"
+#include "utilities/iOSforOSX.h"
+
+#define expireSQL  CFSTR("DELETE FROM responses WHERE expires<?")
+#define beginTxnSQL  CFSTR("BEGIN EXCLUSIVE TRANSACTION")
+#define endTxnSQL  CFSTR("COMMIT TRANSACTION")
+#define insertResponseSQL  CFSTR("INSERT INTO responses " \
+    "(ocspResponse,responderURI,expires,lastUsed) VALUES (?,?,?,?)")
+#define insertLinkSQL  CFSTR("INSERT INTO ocsp (hashAlgorithm," \
+    "issuerNameHash,issuerPubKeyHash,serialNum,responseId) VALUES (?,?,?,?,?)")
+#define selectHashAlgorithmSQL  CFSTR("SELECT DISTINCT hashAlgorithm " \
+    "FROM ocsp WHERE serialNum=?")
+#define selectResponseSQL  CFSTR("SELECT ocspResponse,responseId FROM " \
+    "responses WHERE responseId=(SELECT responseId FROM ocsp WHERE " \
+    "issuerNameHash=? AND issuerPubKeyHash=? AND serialNum=? AND hashAlgorithm=?)" \
+    " ORDER BY expires DESC")
+
+
+#define kSecOCSPCacheFileName CFSTR("ocspcache.sqlite3")
+
+
+// MARK; -
+// MARK: SecOCSPCacheDb
+
+static SecDbRef SecOCSPCacheDbCreate(CFStringRef path) {
+    return SecDbCreate(path, ^bool (SecDbConnectionRef dbconn, bool didCreate, CFErrorRef *error) {
+        __block bool ok;
+        ok = (SecDbExec(dbconn, CFSTR("PRAGMA auto_vacuum = FULL"), error) &&
+              SecDbExec(dbconn, CFSTR("PRAGMA journal_mode = WAL"), error));
+        CFErrorRef localError = NULL;
+        if (ok && !SecDbWithSQL(dbconn, selectHashAlgorithmSQL /* expireSQL */, &localError, NULL) && CFErrorGetCode(localError) == SQLITE_ERROR) {
+            /* SecDbWithSQL returns SQLITE_ERROR if the table we are preparing the above statement for doesn't exist. */
+            ok &= SecDbTransaction(dbconn, kSecDbExclusiveTransactionType, error, ^(bool *commit) {
+                ok = SecDbExec(dbconn,
+                    CFSTR("CREATE TABLE ocsp("
+                          "issuerNameHash BLOB NOT NULL,"
+                          "issuerPubKeyHash BLOB NOT NULL,"
+                          "serialNum BLOB NOT NULL,"
+                          "hashAlgorithm BLOB NOT NULL,"
+                          "responseId INTEGER NOT NULL"
+                          ");"
+                          "CREATE INDEX iResponseId ON ocsp(responseId);"
+                          "CREATE INDEX iserialNum ON ocsp(serialNum);"
+                          "CREATE INDEX iSNumDAlg ON ocsp(serialNum,hashAlgorithm);"
+                          "CREATE TABLE responses("
+                          "responseId INTEGER PRIMARY KEY,"
+                          "ocspResponse BLOB NOT NULL,"
+                          "responderURI BLOB,"
+                          "expires DOUBLE NOT NULL,"
+                          "lastUsed DOUBLE NOT NULL"
+                          ");"
+                          "CREATE INDEX iexpires ON responses(expires);"
+                          "CREATE TRIGGER tocspdel BEFORE DELETE ON responses FOR EACH ROW "
+                          "BEGIN "
+                          "DELETE FROM ocsp WHERE responseId=OLD.responseId;"
+                          " END;"), error);
+                *commit = ok;
+            });
+        }
+        CFReleaseSafe(localError);
+        if (!ok)
+            secerror("%s failed: %@", didCreate ? "Create" : "Open", error ? *error : NULL);
+        return ok;
+    });
+}
+
+// MARK; -
+// MARK: SecOCSPCache
 
 typedef struct __SecOCSPCache *SecOCSPCacheRef;
 struct __SecOCSPCache {
 
 typedef struct __SecOCSPCache *SecOCSPCacheRef;
 struct __SecOCSPCache {
-       sqlite3 *s3h;
-       sqlite3_stmt *expire;
-       sqlite3_stmt *beginTxn;
-       sqlite3_stmt *endTxn;
-       sqlite3_stmt *insertResponse;
-       sqlite3_stmt *insertLink;
-       sqlite3_stmt *selectHashAlgorithm;
-       sqlite3_stmt *selectResponse;
-    bool in_transaction;
+       SecDbRef db;
 };
 
 };
 
-static pthread_once_t kSecOCSPCacheOnce = PTHREAD_ONCE_INIT;
+static dispatch_once_t kSecOCSPCacheOnce;
 static SecOCSPCacheRef kSecOCSPCache = NULL;
 
 static SecOCSPCacheRef kSecOCSPCache = NULL;
 
-/* @@@ Duplicated from SecTrustStore.c */
-static int sec_create_path(const char *path)
-{
-       char pathbuf[PATH_MAX];
-       size_t pos, len = strlen(path);
-       if (len == 0 || len > PATH_MAX)
-               return SQLITE_CANTOPEN;
-       memcpy(pathbuf, path, len);
-       for (pos = len-1; pos > 0; --pos)
-       {
-               /* Search backwards for trailing '/'. */
-               if (pathbuf[pos] == '/')
-               {
-                       pathbuf[pos] = '\0';
-                       /* Attempt to create parent directories of the database. */
-                       if (!mkdir(pathbuf, 0777))
-                               break;
-                       else
-                       {
-                               int err = errno;
-                               if (err == EEXIST)
-                                       return 0;
-                               if (err == ENOTDIR)
-                                       return SQLITE_CANTOPEN;
-                               if (err == EROFS)
-                                       return SQLITE_READONLY;
-                               if (err == EACCES)
-                                       return SQLITE_PERM;
-                               if (err == ENOSPC || err == EDQUOT)
-                                       return SQLITE_FULL;
-                               if (err == EIO)
-                                       return SQLITE_IOERR;
-
-                               /* EFAULT || ELOOP | ENAMETOOLONG || something else */
-                               return SQLITE_INTERNAL;
-                       }
-               }
-       }
-       return SQLITE_OK;
-}
-
-static int sec_sqlite3_open(const char *db_name, sqlite3 **s3h,
-       bool create_path)
-{
-       int s3e;
-       s3e = sqlite3_open(db_name, s3h);
-       if (s3e == SQLITE_CANTOPEN && create_path) {
-               /* Make sure the path to db_name exists and is writable, then
-                  try again. */
-               s3e = sec_create_path(db_name);
-               if (!s3e)
-                       s3e = sqlite3_open(db_name, s3h);
-       }
-
-       return s3e;
-}
-
-static int sec_sqlite3_reset(sqlite3_stmt *stmt, int s3e) {
-    int s3e2;
-    if (s3e == SQLITE_ROW || s3e == SQLITE_DONE)
-        s3e = SQLITE_OK;
-    s3e2 = sqlite3_reset(stmt);
-    if (s3e2 && !s3e)
-        s3e = s3e2;
-    s3e2 = sqlite3_clear_bindings(stmt);
-    if (s3e2 && !s3e)
-        s3e = s3e2;
-    return s3e;
-}
-
-static int SecOCSPCacheEnsureTxn(SecOCSPCacheRef this) {
-    int s3e, s3e2;
-
-    if (this->in_transaction)
-        return SQLITE_OK;
-
-    s3e = sqlite3_step(this->beginTxn);
-    if (s3e == SQLITE_DONE) {
-        this->in_transaction = true;
-        s3e = SQLITE_OK;
-    } else {
-        secdebug("ocspcache", "sqlite3_step returned [%d]: %s", s3e,
-            sqlite3_errmsg(this->s3h));
-    }
-    s3e2 = sqlite3_reset(this->beginTxn);
-    if (s3e2 && !s3e)
-        s3e = s3e2;
-
-    return s3e;
-}
-
-static int SecOCSPCacheCommitTxn(SecOCSPCacheRef this) {
-    int s3e, s3e2;
-
-    if (!this->in_transaction)
-        return SQLITE_OK;
-
-    s3e = sqlite3_step(this->endTxn);
-    if (s3e == SQLITE_DONE) {
-        this->in_transaction = false;
-        s3e = SQLITE_OK;
-    } else {
-        secdebug("ocspcache", "sqlite3_step returned [%d]: %s", s3e,
-            sqlite3_errmsg(this->s3h));
-    }
-    s3e2 = sqlite3_reset(this->endTxn);
-    if (s3e2 && !s3e)
-        s3e = s3e2;
-
-    return s3e;
-}
-
-static SecOCSPCacheRef SecOCSPCacheCreate(const char *db_name) {
+static SecOCSPCacheRef SecOCSPCacheCreate(CFStringRef db_name) {
        SecOCSPCacheRef this;
        SecOCSPCacheRef this;
-       int s3e;
-    bool create = true;
 
        require(this = (SecOCSPCacheRef)malloc(sizeof(struct __SecOCSPCache)), errOut);
 
        require(this = (SecOCSPCacheRef)malloc(sizeof(struct __SecOCSPCache)), errOut);
-       require_noerr(s3e = sec_sqlite3_open(db_name, &this->s3h, create), errOut);
-    this->in_transaction = false;
-
-       s3e = sqlite3_prepare_v2(this->s3h, beginTxnSQL, sizeof(beginTxnSQL),
-               &this->beginTxn, NULL);
-       require_noerr(s3e, errOut);
-       s3e = sqlite3_prepare_v2(this->s3h, endTxnSQL, sizeof(endTxnSQL),
-               &this->endTxn, NULL);
-       require_noerr(s3e, errOut);
-
-       s3e = sqlite3_prepare_v2(this->s3h, expireSQL, sizeof(expireSQL),
-               &this->expire, NULL);
-       if (create && s3e == SQLITE_ERROR) {
-        s3e = SecOCSPCacheEnsureTxn(this);
-               require_noerr(s3e, errOut);
-
-               /* sqlite3_prepare returns SQLITE_ERROR if the table we are
-                  compiling this statement for doesn't exist. */
-               char *errmsg = NULL;
-               s3e = sqlite3_exec(this->s3h,
-                       "CREATE TABLE ocsp("
-                       "issuerNameHash BLOB NOT NULL,"
-                       "issuerPubKeyHash BLOB NOT NULL,"
-                       "serialNum BLOB NOT NULL,"
-                       "hashAlgorithm BLOB NOT NULL,"
-                       "responseId INTEGER NOT NULL"
-                       ");"
-                       "CREATE INDEX iResponseId ON ocsp(responseId);"
-                       "CREATE INDEX iserialNum ON ocsp(serialNum);"
-                       "CREATE INDEX iSNumDAlg ON ocsp(serialNum,hashAlgorithm);"
-                       "CREATE TABLE responses("
-            "responseId INTEGER PRIMARY KEY,"
-                       "ocspResponse BLOB NOT NULL,"
-                       "responderURI BLOB,"
-            "expires DOUBLE NOT NULL,"
-            "lastUsed DOUBLE NOT NULL"
-                       ");"
-                       "CREATE INDEX iexpires ON responses(expires);"
-            "CREATE TRIGGER tocspdel BEFORE DELETE ON responses FOR EACH ROW "
-            "BEGIN "
-            "DELETE FROM ocsp WHERE responseId=OLD.responseId;"
-            " END;"
-                       , NULL, NULL, &errmsg);
-               if (errmsg) {
-                       ocspErrorLog("ocsp db CREATE TABLES: %s", errmsg);
-                       sqlite3_free(errmsg);
-               }
-               require_noerr(s3e, errOut);
-        s3e = sqlite3_prepare_v2(this->s3h, expireSQL, sizeof(expireSQL),
-            &this->expire, NULL);
-       }
-       require_noerr(s3e, errOut);
-       s3e = sqlite3_prepare_v2(this->s3h, insertResponseSQL, sizeof(insertResponseSQL),
-               &this->insertResponse, NULL);
-       require_noerr(s3e, errOut);
-       s3e = sqlite3_prepare_v2(this->s3h, insertLinkSQL, sizeof(insertLinkSQL),
-               &this->insertLink, NULL);
-       require_noerr(s3e, errOut);
-       s3e = sqlite3_prepare_v2(this->s3h, selectHashAlgorithmSQL, sizeof(selectHashAlgorithmSQL),
-               &this->selectHashAlgorithm, NULL);
-       require_noerr(s3e, errOut);
-       s3e = sqlite3_prepare_v2(this->s3h, selectResponseSQL, sizeof(selectResponseSQL),
-               &this->selectResponse, NULL);
-       require_noerr(s3e, errOut);
+    require(this->db = SecOCSPCacheDbCreate(db_name), errOut);
 
        return this;
 
 errOut:
        if (this) {
 
        return this;
 
 errOut:
        if (this) {
-               sqlite3_close(this->s3h);
+        CFReleaseSafe(this->db);
                free(this);
        }
 
        return NULL;
 }
 
                free(this);
        }
 
        return NULL;
 }
 
-static void SecOCSPCacheInit(void) {
-       static const char *path = kSecOCSPCachePath;
-#if NO_SERVER
-    /* Added this block of code back to keep the tests happy for now. */
-       const char *home = getenv("HOME");
-       char buffer[PATH_MAX];
-       size_t homeLen;
-       size_t pathLen = strlen(path);
-       if (home) {
-               homeLen = strlen(home);
-               if (homeLen + pathLen >= sizeof(buffer)) {
-                       return;
-               }
-
-               strlcpy(buffer, home, sizeof(buffer));
-       } else {
-               CFURLRef homeURL = CFCopyHomeDirectoryURLForUser(NULL);
-               if (!homeURL)
-                       return;
-
-               CFURLGetFileSystemRepresentation(homeURL, true, (uint8_t *)buffer,
-                       sizeof(buffer));
-               CFRelease(homeURL);
-               homeLen = strlen(buffer);
-               buffer[homeLen] = '\0';
-               if (homeLen + pathLen >= sizeof(buffer)) {
-                       return;
-               }
-       }
-
-       strlcat(buffer, path, sizeof(buffer));
-
-    path = buffer;
-
-#endif
+static CFStringRef SecOCSPCacheCopyPath(void) {
+    CFStringRef ocspRelPath = kSecOCSPCacheFileName;
+    CFURLRef ocspURL = SecCopyURLForFileInKeychainDirectory(ocspRelPath);
+    CFStringRef ocspPath = NULL;
+    if (ocspURL) {
+        ocspPath = CFURLCopyFileSystemPath(ocspURL, kCFURLPOSIXPathStyle);
+        CFRelease(ocspURL);
+    }
+    return ocspPath;
+}
 
 
-    kSecOCSPCache = SecOCSPCacheCreate(path);
-    if (kSecOCSPCache)
-        atexit(SecOCSPCacheGC);
+static void SecOCSPCacheWith(void(^cacheJob)(SecOCSPCacheRef cache)) {
+    dispatch_once(&kSecOCSPCacheOnce, ^{
+        CFStringRef dbPath = SecOCSPCacheCopyPath();
+        if (dbPath) {
+            kSecOCSPCache = SecOCSPCacheCreate(dbPath);
+            CFRelease(dbPath);
+        }
+    });
+    // Do pre job run work here (cancel idle timers etc.)
+    cacheJob(kSecOCSPCache);
+    // Do post job run work here (gc timer, etc.)
 }
 
 /* Instance implemenation. */
 
 static void _SecOCSPCacheAddResponse(SecOCSPCacheRef this,
     SecOCSPResponseRef ocspResponse, CFURLRef localResponderURI) {
 }
 
 /* Instance implemenation. */
 
 static void _SecOCSPCacheAddResponse(SecOCSPCacheRef this,
     SecOCSPResponseRef ocspResponse, CFURLRef localResponderURI) {
-    int s3e;
-
     secdebug("ocspcache", "adding response from %@", localResponderURI);
     secdebug("ocspcache", "adding response from %@", localResponderURI);
-    require_noerr(s3e = SecOCSPCacheEnsureTxn(this), errOut);
-
     /* responses.ocspResponse */
     CFDataRef responseData = SecOCSPResponseGetData(ocspResponse);
     /* responses.ocspResponse */
     CFDataRef responseData = SecOCSPResponseGetData(ocspResponse);
-    s3e = sqlite3_bind_blob_wrapper(this->insertResponse, 1,
-        CFDataGetBytePtr(responseData),
-        CFDataGetLength(responseData), SQLITE_TRANSIENT);
-
-    /* responses.responderURI */
-    if (!s3e) {
-        CFDataRef uriData = NULL;
-        if (localResponderURI) {
-            uriData = CFURLCreateData(kCFAllocatorDefault, localResponderURI,
-                kCFStringEncodingUTF8, false);
-        }
-        if (uriData) {
-            s3e = sqlite3_bind_blob_wrapper(this->insertResponse, 2,
-                    CFDataGetBytePtr(uriData),
-                    CFDataGetLength(uriData), SQLITE_TRANSIENT);
-            CFRelease(uriData);
-        } else {
-            s3e = sqlite3_bind_null(this->insertResponse, 2);
-        }
-    }
-    /* responses.expires */
-    if (!s3e) s3e = sqlite3_bind_double(this->insertResponse, 3,
-            SecOCSPResponseGetExpirationTime(ocspResponse));
-    /* responses.lastUsed */
-    if (!s3e) s3e = sqlite3_bind_double(this->insertResponse, 4,
-            SecOCSPResponseVerifyTime(ocspResponse));
-
-    /* Execute the insert statement. */
-    if (!s3e) s3e = sqlite3_step(this->insertResponse);
-    require_noerr(s3e = sec_sqlite3_reset(this->insertResponse, s3e), errOut);
-
-    sqlite3_int64 responseId = sqlite3_last_insert_rowid(this->s3h);
-
-    /* Now add a link record for every singleResponse in the ocspResponse. */
-    SecAsn1OCSPSingleResponse **responses;
-    for (responses = ocspResponse->responseData.responses;
-        *responses; ++responses) {
-               SecAsn1OCSPSingleResponse *resp = *responses;
-        SecAsn1OCSPCertID *certId = &resp->certID;
-
-        s3e = sqlite3_bind_blob_wrapper(this->insertLink, 1,
-            certId->algId.algorithm.Data, certId->algId.algorithm.Length,
-            SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->insertLink, 2,
-            certId->issuerNameHash.Data, certId->issuerNameHash.Length,
-            SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->insertLink, 3,
-            certId->issuerPubKeyHash.Data, certId->issuerPubKeyHash.Length,
-            SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->insertLink, 4,
-            certId->serialNumber.Data, certId->serialNumber.Length,
-            SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_int64(this->insertLink, 5,
-            responseId);
-
-        /* Execute the insert statement. */
-        if (!s3e) s3e = sqlite3_step(this->insertLink);
-        require_noerr(s3e = sec_sqlite3_reset(this->insertLink, s3e), errOut);
-    }
-
-errOut:
-    if (s3e) {
-        ocspErrorLog("ocsp cache add failed: %s", sqlite3_errmsg(this->s3h));
-        /* @@@ Blow away the cache and create a new db. */
+    __block CFErrorRef localError = NULL;
+    __block bool ok = true;
+    ok &= SecDbPerformWrite(this->db, &localError, ^(SecDbConnectionRef dbconn) {
+        ok &= SecDbTransaction(dbconn, kSecDbExclusiveTransactionType, &localError, ^(bool *commit) {
+            __block sqlite3_int64 responseId;
+            ok = SecDbWithSQL(dbconn, insertResponseSQL, &localError, ^bool(sqlite3_stmt *insertResponse) {
+                if (ok)
+                    ok = SecDbBindBlob(insertResponse, 1,
+                                       CFDataGetBytePtr(responseData),
+                                       CFDataGetLength(responseData),
+                                       SQLITE_TRANSIENT, &localError);
+
+                /* responses.responderURI */
+                if (ok) {
+                    CFDataRef uriData = NULL;
+                    if (localResponderURI) {
+                        uriData = CFURLCreateData(kCFAllocatorDefault, localResponderURI,
+                                                  kCFStringEncodingUTF8, false);
+                    }
+                    if (uriData) {
+                        ok = SecDbBindBlob(insertResponse, 2,
+                                           CFDataGetBytePtr(uriData),
+                                           CFDataGetLength(uriData),
+                                           SQLITE_TRANSIENT, &localError);
+                        CFRelease(uriData);
+                    } else {
+                        // Since we use SecDbClearBindings this shouldn't be needed.
+                        //ok = SecDbBindNull(insertResponse, 2, &localError);
+                    }
+                }
+                /* responses.expires */
+                if (ok)
+                    ok = SecDbBindDouble(insertResponse, 3,
+                                         SecOCSPResponseGetExpirationTime(ocspResponse),
+                                         &localError);
+                /* responses.lastUsed */
+                if (ok)
+                    ok = SecDbBindDouble(insertResponse, 4,
+                                         SecOCSPResponseVerifyTime(ocspResponse),
+                                         &localError);
+
+                /* Execute the insert statement. */
+                if (ok)
+                    ok = SecDbStep(dbconn, insertResponse, &localError, NULL);
+
+                responseId = sqlite3_last_insert_rowid(SecDbHandle(dbconn));
+                return ok;
+            });
+
+            /* Now add a link record for every singleResponse in the ocspResponse. */
+            if (ok) ok = SecDbWithSQL(dbconn, insertLinkSQL, &localError, ^bool(sqlite3_stmt *insertLink) {
+                SecAsn1OCSPSingleResponse **responses;
+                for (responses = ocspResponse->responseData.responses;
+                     *responses; ++responses) {
+                    SecAsn1OCSPSingleResponse *resp = *responses;
+                    SecAsn1OCSPCertID *certId = &resp->certID;
+                    if (ok) ok = SecDbBindBlob(insertLink, 1,
+                                               certId->algId.algorithm.Data,
+                                               certId->algId.algorithm.Length,
+                                               SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindBlob(insertLink, 2,
+                                               certId->issuerNameHash.Data,
+                                               certId->issuerNameHash.Length,
+                                               SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindBlob(insertLink, 3,
+                                               certId->issuerPubKeyHash.Data,
+                                               certId->issuerPubKeyHash.Length,
+                                               SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindBlob(insertLink, 4,
+                                               certId->serialNumber.Data,
+                                               certId->serialNumber.Length,
+                                               SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindInt64(insertLink, 5, responseId, &localError);
+
+                    /* Execute the insert statement. */
+                    if (ok) ok = SecDbStep(dbconn, insertLink, &localError, NULL);
+                    if (ok) ok = SecDbReset(insertLink, &localError);
+                }
+                return ok;
+            });
+            if (!ok)
+                *commit = false;
+        });
+    });
+    if (!ok) {
+        secerror("_SecOCSPCacheAddResponse failed: %@", localError);
     }
     }
+    CFReleaseSafe(localError);
 }
 
 static SecOCSPResponseRef _SecOCSPCacheCopyMatching(SecOCSPCacheRef this,
     SecOCSPRequestRef request, CFURLRef responderURI) {
 }
 
 static SecOCSPResponseRef _SecOCSPCacheCopyMatching(SecOCSPCacheRef this,
     SecOCSPRequestRef request, CFURLRef responderURI) {
-    SecOCSPResponseRef response = NULL;
     const DERItem *publicKey;
     CFDataRef issuer = NULL;
     CFDataRef serial = NULL;
     const DERItem *publicKey;
     CFDataRef issuer = NULL;
     CFDataRef serial = NULL;
-    int s3e = SQLITE_ERROR;
+    __block SecOCSPResponseRef response = NULL;
+    __block CFErrorRef localError = NULL;
+    __block bool ok = true;
 
     require(publicKey = SecCertificateGetPublicKeyData(request->issuer), errOut);
     require(issuer = SecCertificateCopyIssuerSequence(request->certificate), errOut);
     require(serial = SecCertificateCopySerialNumber(request->certificate), errOut);
 
     require(publicKey = SecCertificateGetPublicKeyData(request->issuer), errOut);
     require(issuer = SecCertificateCopyIssuerSequence(request->certificate), errOut);
     require(serial = SecCertificateCopySerialNumber(request->certificate), errOut);
-    s3e = sqlite3_bind_blob_wrapper(this->selectHashAlgorithm, 1,
-        CFDataGetBytePtr(serial), CFDataGetLength(serial), SQLITE_TRANSIENT);
-    while (!s3e && !response &&
-        (s3e = sqlite3_step(this->selectHashAlgorithm)) == SQLITE_ROW) {
-        SecAsn1Oid algorithm;
-        algorithm.Data = (uint8_t *)sqlite3_column_blob(this->selectHashAlgorithm, 0);
-        algorithm.Length = sqlite3_column_bytes(this->selectHashAlgorithm, 0);
-
-        /* Calcluate the issuerKey and issuerName digests using the returned
-           hashAlgorithm. */
-        CFDataRef issuerNameHash = SecDigestCreate(kCFAllocatorDefault,
-            &algorithm, NULL, CFDataGetBytePtr(issuer), CFDataGetLength(issuer));
-        CFDataRef issuerPubKeyHash = SecDigestCreate(kCFAllocatorDefault,
-            &algorithm, NULL, publicKey->data, publicKey->length);
-
-        require(issuerNameHash && issuerPubKeyHash, nextResponse);
-
-        /* Now we have the serial, algorithm, issuerNameHash and
-           issuerPubKeyHash so let's lookup the db entry. */
-        s3e = sqlite3_bind_blob_wrapper(this->selectResponse, 1, CFDataGetBytePtr(issuerNameHash),
-            CFDataGetLength(issuerNameHash), SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->selectResponse, 2, CFDataGetBytePtr(issuerPubKeyHash),
-            CFDataGetLength(issuerPubKeyHash), SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->selectResponse, 3, CFDataGetBytePtr(serial),
-            CFDataGetLength(serial), SQLITE_TRANSIENT);
-        if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->selectResponse, 4, algorithm.Data,
-            algorithm.Length, SQLITE_TRANSIENT);
-
-        if (!s3e) s3e = sqlite3_step(this->selectResponse);
-        if (s3e == SQLITE_ROW) {
-            /* Found an entry! */
-            secdebug("ocspcache", "found cached response");
-
-            const void *respData = sqlite3_column_blob(this->selectResponse, 0);
-            int respLen = sqlite3_column_bytes(this->selectResponse, 0);
-            CFDataRef resp = CFDataCreate(kCFAllocatorDefault, respData, respLen);
-            if (resp) {
-                response = SecOCSPResponseCreate(resp, NULL_TIME);
-                CFRelease(resp);
-            }
-            if (response) {
-                //sqlite3_int64 responseId = sqlite3_column_int64(this->selectResponse, 1);
-                /* @@@ Update the lastUsed field in the db. */
-            }
-        }
 
 
-nextResponse:
-        s3e = sec_sqlite3_reset(this->selectResponse, s3e);
-        CFReleaseSafe(issuerNameHash);
-        CFReleaseSafe(issuerPubKeyHash);
-    }
-    require_noerr(s3e = sec_sqlite3_reset(this->selectHashAlgorithm, s3e), errOut);
+    ok &= SecDbPerformRead(this->db, &localError, ^(SecDbConnectionRef dbconn) {
+        ok &= SecDbWithSQL(dbconn, selectHashAlgorithmSQL, &localError, ^bool(sqlite3_stmt *selectHash) {
+            ok = SecDbBindBlob(selectHash, 1, CFDataGetBytePtr(serial), CFDataGetLength(serial), SQLITE_TRANSIENT, &localError);
+            ok &= SecDbStep(dbconn, selectHash, &localError, ^(bool *stopHash) {
+                SecAsn1Oid algorithm;
+                algorithm.Data = (uint8_t *)sqlite3_column_blob(selectHash, 0);
+                algorithm.Length = sqlite3_column_bytes(selectHash, 0);
+
+                /* Calcluate the issuerKey and issuerName digests using the returned
+                 hashAlgorithm. */
+                CFDataRef issuerNameHash = SecDigestCreate(kCFAllocatorDefault,
+                                                           &algorithm, NULL, CFDataGetBytePtr(issuer), CFDataGetLength(issuer));
+                CFDataRef issuerPubKeyHash = SecDigestCreate(kCFAllocatorDefault,
+                                                             &algorithm, NULL, publicKey->data, publicKey->length);
+
+                if (issuerNameHash && issuerPubKeyHash && ok) ok &= SecDbWithSQL(dbconn, selectResponseSQL, &localError, ^bool(sqlite3_stmt *selectResponse) {
+                    /* Now we have the serial, algorithm, issuerNameHash and
+                     issuerPubKeyHash so let's lookup the db entry. */
+                    if (ok) ok = SecDbBindBlob(selectResponse, 1, CFDataGetBytePtr(issuerNameHash),
+                                               CFDataGetLength(issuerNameHash), SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindBlob(selectResponse, 2, CFDataGetBytePtr(issuerPubKeyHash),
+                                               CFDataGetLength(issuerPubKeyHash), SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindBlob(selectResponse, 3, CFDataGetBytePtr(serial),
+                                               CFDataGetLength(serial), SQLITE_TRANSIENT, &localError);
+                    if (ok) ok = SecDbBindBlob(selectResponse, 4, algorithm.Data,
+                                               algorithm.Length, SQLITE_TRANSIENT, &localError);
+                    if (ok) ok &= SecDbStep(dbconn, selectResponse, &localError, ^(bool *stopResponse) {
+                        /* Found an entry! */
+                        secdebug("ocspcache", "found cached response");
+                        CFDataRef resp = CFDataCreate(kCFAllocatorDefault,
+                                                      sqlite3_column_blob(selectResponse, 0),
+                                                      sqlite3_column_bytes(selectResponse, 0));
+                        if (resp) {
+                            response = SecOCSPResponseCreate(resp, NULL_TIME);
+                            CFRelease(resp);
+                        }
+                        if (response) {
+                            //sqlite3_int64 responseId = sqlite3_column_int64(this->selectResponse, 1);
+                            /* @@@ Update the lastUsed field in the db. */
+                        }
+                    });
+                    return ok;
+                });
+
+                CFReleaseSafe(issuerNameHash);
+                CFReleaseSafe(issuerPubKeyHash);
+            });
+            return ok;
+        });
+    });
 
 errOut:
     CFReleaseSafe(serial);
     CFReleaseSafe(issuer);
 
 
 errOut:
     CFReleaseSafe(serial);
     CFReleaseSafe(issuer);
 
-    if (s3e) {
-        ocspErrorLog("ocsp cache lookup failed: %s", sqlite3_errmsg(this->s3h));
-        /* @@@ Blow away the cache and create a new db. */
-
+    if (!ok) {
+        secerror("ocsp cache lookup failed: %@", localError);
         if (response) {
             SecOCSPResponseFinalize(response);
             response = NULL;
         }
     }
         if (response) {
             SecOCSPResponseFinalize(response);
             response = NULL;
         }
     }
+    CFReleaseSafe(localError);
 
     secdebug("ocspcache", "returning %s", (response ? "cached response" : "NULL"));
 
 
     secdebug("ocspcache", "returning %s", (response ? "cached response" : "NULL"));
 
@@ -478,51 +336,47 @@ errOut:
 }
 
 static void _SecOCSPCacheGC(SecOCSPCacheRef this) {
 }
 
 static void _SecOCSPCacheGC(SecOCSPCacheRef this) {
-    int s3e;
-
-    require_noerr(s3e = SecOCSPCacheEnsureTxn(this), errOut);
     secdebug("ocspcache", "expiring stale responses");
     secdebug("ocspcache", "expiring stale responses");
-    s3e = sqlite3_bind_double(this->expire, 1, CFAbsoluteTimeGetCurrent());
-    if (!s3e) s3e = sqlite3_step(this->expire);
-    require_noerr(s3e = sec_sqlite3_reset(this->expire, s3e), errOut);
-    require_noerr(s3e = SecOCSPCacheCommitTxn(this), errOut);
 
 
-errOut:
-    if (s3e) {
-        ocspErrorLog("ocsp cache expire failed: %s", sqlite3_errmsg(this->s3h));
-        /* @@@ Blow away the cache and create a new db. */
+    __block CFErrorRef localError = NULL;
+    __block bool ok = true;
+    ok &= SecDbPerformWrite(this->db, &localError, ^(SecDbConnectionRef dbconn) {
+        ok &= SecDbTransaction(dbconn, kSecDbExclusiveTransactionType, &localError, ^(bool *commit) {
+            ok &= SecDbWithSQL(dbconn, expireSQL, &localError, ^bool(sqlite3_stmt *expire) {
+                return SecDbBindDouble(expire, 1, CFAbsoluteTimeGetCurrent(), &localError) &&
+                    SecDbStep(dbconn, expire, &localError, NULL);
+            });
+            *commit = ok;
+        });
+    });
+
+    if (!ok) {
+        secerror("ocsp cache expire failed: %@", localError);
     }
     }
+    CFReleaseSafe(localError);
 }
 
 static void _SecOCSPCacheFlush(SecOCSPCacheRef this) {
 }
 
 static void _SecOCSPCacheFlush(SecOCSPCacheRef this) {
-    int s3e;
     secdebug("ocspcache", "flushing pending changes");
     secdebug("ocspcache", "flushing pending changes");
-    s3e = SecOCSPCacheCommitTxn(this);
-
-    if (s3e) {
-        ocspErrorLog("ocsp cache flush failed: %s", sqlite3_errmsg(this->s3h));
-        /* @@@ Blow away the cache and create a new db. */
-    }
+    // NOOP since we use WAL now and commit right away.
 }
 
 /* Public API */
 
 void SecOCSPCacheAddResponse(SecOCSPResponseRef response,
     CFURLRef localResponderURI) {
 }
 
 /* Public API */
 
 void SecOCSPCacheAddResponse(SecOCSPResponseRef response,
     CFURLRef localResponderURI) {
-    pthread_once(&kSecOCSPCacheOnce, SecOCSPCacheInit);
-    if (!kSecOCSPCache)
-        return;
-
-    _SecOCSPCacheAddResponse(kSecOCSPCache, response, localResponderURI);
+    SecOCSPCacheWith(^(SecOCSPCacheRef cache) {
+        _SecOCSPCacheAddResponse(cache, response, localResponderURI);
+    });
 }
 
 SecOCSPResponseRef SecOCSPCacheCopyMatching(SecOCSPRequestRef request,
     CFURLRef localResponderURI /* may be NULL */) {
 }
 
 SecOCSPResponseRef SecOCSPCacheCopyMatching(SecOCSPRequestRef request,
     CFURLRef localResponderURI /* may be NULL */) {
-    pthread_once(&kSecOCSPCacheOnce, SecOCSPCacheInit);
-    if (!kSecOCSPCache)
-        return NULL;
-
-    return _SecOCSPCacheCopyMatching(kSecOCSPCache, request, localResponderURI);
+    __block SecOCSPResponseRef response = NULL;
+    SecOCSPCacheWith(^(SecOCSPCacheRef cache) {
+        response = _SecOCSPCacheCopyMatching(cache, request, localResponderURI);
+    });
+    return response;
 }
 
 /* This should be called on a normal non emergency exit. This function
 }
 
 /* This should be called on a normal non emergency exit. This function