1/*
2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 */
24
25/*
26 *  SecCAIssuerCache.c - securityd
27 */
28
29#include <securityd/SecCAIssuerCache.h>
30#include <utilities/debugging.h>
31#include <Security/SecCertificateInternal.h>
32#include <Security/SecFramework.h>
33#include <Security/SecInternal.h>
34#include <sqlite3.h>
35#include <AssertMacros.h>
36#include <stdlib.h>
37#include <limits.h>
38#include <string.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <errno.h>
42#include <dispatch/dispatch.h>
43#include <asl.h>
44#include "utilities/sqlutils.h"
45#include "utilities/iOSforOSX.h"
46
47#include <CoreFoundation/CFUtilities.h>
48#include <utilities/SecFileLocations.h>
49
50#define caissuerErrorLog(args...)     asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
51
52static const char expireSQL[] = "DELETE FROM issuers WHERE expires<?";
53static const char beginTxnSQL[] = "BEGIN EXCLUSIVE TRANSACTION";
54static const char endTxnSQL[] = "COMMIT TRANSACTION";
55static const char insertIssuerSQL[] = "INSERT OR REPLACE INTO issuers "
56    "(uri,expires,certificate) VALUES (?,?,?)";
57static const char selectIssuerSQL[] = "SELECT certificate FROM "
58    "issuers WHERE uri=?";
59
60#define kSecCAIssuerFileName "caissuercache.sqlite3"
61
62typedef struct __SecCAIssuerCache *SecCAIssuerCacheRef;
63struct __SecCAIssuerCache {
64    dispatch_queue_t queue;
65	sqlite3 *s3h;
66	sqlite3_stmt *expire;
67	sqlite3_stmt *beginTxn;
68	sqlite3_stmt *endTxn;
69	sqlite3_stmt *insertIssuer;
70	sqlite3_stmt *selectIssuer;
71    bool in_transaction;
72};
73
74static dispatch_once_t kSecCAIssuerCacheOnce;
75static SecCAIssuerCacheRef kSecCAIssuerCache;
76
77/* @@@ Duplicated from SecTrustStore.c */
78static int sec_create_path(const char *path)
79{
80	char pathbuf[PATH_MAX];
81	size_t pos, len = strlen(path);
82	if (len == 0 || len > PATH_MAX)
83		return SQLITE_CANTOPEN;
84	memcpy(pathbuf, path, len);
85	for (pos = len-1; pos > 0; --pos)
86	{
87		/* Search backwards for trailing '/'. */
88		if (pathbuf[pos] == '/')
89		{
90			pathbuf[pos] = '\0';
91			/* Attempt to create parent directories of the database. */
92			if (!mkdir(pathbuf, 0777))
93				break;
94			else
95			{
96				int err = errno;
97				if (err == EEXIST)
98					return 0;
99				if (err == ENOTDIR)
100					return SQLITE_CANTOPEN;
101				if (err == EROFS)
102					return SQLITE_READONLY;
103				if (err == EACCES)
104					return SQLITE_PERM;
105				if (err == ENOSPC || err == EDQUOT)
106					return SQLITE_FULL;
107				if (err == EIO)
108					return SQLITE_IOERR;
109
110				/* EFAULT || ELOOP | ENAMETOOLONG || something else */
111				return SQLITE_INTERNAL;
112			}
113		}
114	}
115	return SQLITE_OK;
116}
117
118static int sec_sqlite3_open(const char *db_name, sqlite3 **s3h,
119                            bool create_path)
120{
121	int s3e;
122	s3e = sqlite3_open(db_name, s3h);
123	if (s3e == SQLITE_CANTOPEN && create_path) {
124		/* Make sure the path to db_name exists and is writable, then
125         try again. */
126		s3e = sec_create_path(db_name);
127		if (!s3e)
128			s3e = sqlite3_open(db_name, s3h);
129	}
130
131	return s3e;
132}
133
134static int sec_sqlite3_reset(sqlite3_stmt *stmt, int s3e) {
135    int s3e2;
136    if (s3e == SQLITE_ROW || s3e == SQLITE_DONE)
137        s3e = SQLITE_OK;
138    s3e2 = sqlite3_reset(stmt);
139    if (s3e2 && !s3e)
140        s3e = s3e2;
141    s3e2 = sqlite3_clear_bindings(stmt);
142    if (s3e2 && !s3e)
143        s3e = s3e2;
144    return s3e;
145}
146
147static int SecCAIssuerCacheEnsureTxn(SecCAIssuerCacheRef this) {
148    int s3e, s3e2;
149
150    if (this->in_transaction)
151        return SQLITE_OK;
152
153    s3e = sqlite3_step(this->beginTxn);
154    if (s3e == SQLITE_DONE) {
155        this->in_transaction = true;
156        s3e = SQLITE_OK;
157    } else {
158        secdebug("caissuercache", "sqlite3_step returned [%d]: %s", s3e,
159                 sqlite3_errmsg(this->s3h));
160    }
161    s3e2 = sqlite3_reset(this->beginTxn);
162    if (s3e2 && !s3e)
163        s3e = s3e2;
164
165    return s3e;
166}
167
168static int SecCAIssuerCacheCommitTxn(SecCAIssuerCacheRef this) {
169    int s3e, s3e2;
170
171    if (!this->in_transaction)
172        return SQLITE_OK;
173
174    s3e = sqlite3_step(this->endTxn);
175    if (s3e == SQLITE_DONE) {
176        this->in_transaction = false;
177        s3e = SQLITE_OK;
178    } else {
179        secdebug("caissuercache", "sqlite3_step returned [%d]: %s", s3e,
180                 sqlite3_errmsg(this->s3h));
181    }
182    s3e2 = sqlite3_reset(this->endTxn);
183    if (s3e2 && !s3e)
184        s3e = s3e2;
185
186    return s3e;
187}
188
189static SecCAIssuerCacheRef SecCAIssuerCacheCreate(const char *db_name) {
190	SecCAIssuerCacheRef this;
191	int s3e;
192    bool create = true;
193
194    require(this = (SecCAIssuerCacheRef)calloc(sizeof(struct __SecCAIssuerCache), 1), errOut);
195    require_action_quiet((this->queue = dispatch_queue_create("caissuercache", 0)), errOut, s3e = errSecAllocate);
196    require_noerr(s3e = sec_sqlite3_open(db_name, &this->s3h, create), errOut);
197    this->in_transaction = false;
198
199	s3e = sqlite3_prepare_v2(this->s3h, beginTxnSQL, sizeof(beginTxnSQL),
200                             &this->beginTxn, NULL);
201	require_noerr(s3e, errOut);
202	s3e = sqlite3_prepare_v2(this->s3h, endTxnSQL, sizeof(endTxnSQL),
203                             &this->endTxn, NULL);
204	require_noerr(s3e, errOut);
205
206	s3e = sqlite3_prepare_v2(this->s3h, expireSQL, sizeof(expireSQL),
207                             &this->expire, NULL);
208	if (create && s3e == SQLITE_ERROR) {
209        s3e = SecCAIssuerCacheEnsureTxn(this);
210		require_noerr(s3e, errOut);
211
212		/* sqlite3_prepare returns SQLITE_ERROR if the table we are
213         compiling this statement for doesn't exist. */
214		char *errmsg = NULL;
215		s3e = sqlite3_exec(this->s3h,
216                           "CREATE TABLE issuers("
217                           "uri BLOB PRIMARY KEY,"
218                           "expires DOUBLE NOT NULL,"
219                           "certificate BLOB NOT NULL"
220                           ");"
221                           "CREATE INDEX iexpires ON issuers(expires);"
222                           , NULL, NULL, &errmsg);
223		if (errmsg) {
224			caissuerErrorLog("caissuer db CREATE TABLES: %s", errmsg);
225			sqlite3_free(errmsg);
226		}
227		require_noerr(s3e, errOut);
228        s3e = sqlite3_prepare_v2(this->s3h, expireSQL, sizeof(expireSQL),
229                                 &this->expire, NULL);
230	}
231	require_noerr(s3e, errOut);
232	s3e = sqlite3_prepare_v2(this->s3h, insertIssuerSQL, sizeof(insertIssuerSQL),
233                             &this->insertIssuer, NULL);
234	require_noerr(s3e, errOut);
235	s3e = sqlite3_prepare_v2(this->s3h, selectIssuerSQL, sizeof(selectIssuerSQL),
236                             &this->selectIssuer, NULL);
237	require_noerr(s3e, errOut);
238
239	return this;
240
241errOut:
242	if (this) {
243        if (this->queue)
244            dispatch_release(this->queue);
245        if (this->s3h)
246            sqlite3_close(this->s3h);
247		free(this);
248	}
249
250	return NULL;
251}
252
253static void SecCAIssuerCacheInit(void) {
254    WithPathInKeychainDirectory(CFSTR(kSecCAIssuerFileName), ^(const char *utf8String) {
255        kSecCAIssuerCache = SecCAIssuerCacheCreate(utf8String);
256    });
257
258    if (kSecCAIssuerCache)
259        atexit(SecCAIssuerCacheGC);
260}
261
262/* Instance implemenation. */
263
264static void _SecCAIssuerCacheAddCertificate(SecCAIssuerCacheRef this,
265                                            SecCertificateRef certificate,
266                                            CFURLRef uri, CFAbsoluteTime expires) {
267    int s3e;
268
269    secdebug("caissuercache", "adding certificate from %@", uri);
270    require_noerr(s3e = SecCAIssuerCacheEnsureTxn(this), errOut);
271
272    /* issuer.uri */
273    CFDataRef uriData;
274    require_action(uriData = CFURLCreateData(kCFAllocatorDefault, uri,
275        kCFStringEncodingUTF8, false), errOut, s3e = SQLITE_NOMEM);
276    s3e = sqlite3_bind_blob_wrapper(this->insertIssuer, 1,
277        CFDataGetBytePtr(uriData), CFDataGetLength(uriData), SQLITE_TRANSIENT);
278    CFRelease(uriData);
279
280    /* issuer.expires */
281    if (!s3e) s3e = sqlite3_bind_double(this->insertIssuer, 2, expires);
282
283    /* issuer.certificate */
284    if (!s3e) s3e = sqlite3_bind_blob_wrapper(this->insertIssuer, 3,
285        SecCertificateGetBytePtr(certificate),
286        SecCertificateGetLength(certificate), SQLITE_TRANSIENT);
287
288    /* Execute the insert statement. */
289    if (!s3e) s3e = sqlite3_step(this->insertIssuer);
290    require_noerr(s3e = sec_sqlite3_reset(this->insertIssuer, s3e), errOut);
291
292errOut:
293    if (s3e) {
294        caissuerErrorLog("caissuer cache add failed: %s", sqlite3_errmsg(this->s3h));
295        /* TODO: Blow away the cache and create a new db. */
296    }
297}
298
299static SecCertificateRef _SecCAIssuerCacheCopyMatching(SecCAIssuerCacheRef this,
300                                                       CFURLRef uri) {
301    SecCertificateRef certificate = NULL;
302    int s3e = SQLITE_OK;
303
304    CFDataRef uriData = NULL;
305    require(uriData = CFURLCreateData(kCFAllocatorDefault, uri,
306                                      kCFStringEncodingUTF8, false), errOut);
307    s3e = sqlite3_bind_blob_wrapper(this->selectIssuer, 1, CFDataGetBytePtr(uriData),
308                            CFDataGetLength(uriData), SQLITE_TRANSIENT);
309    CFRelease(uriData);
310
311    if (!s3e) s3e = sqlite3_step(this->selectIssuer);
312    if (s3e == SQLITE_ROW) {
313        /* Found an entry! */
314        secdebug("caissuercache", "found cached response for %@", uri);
315
316        const void *respData = sqlite3_column_blob(this->selectIssuer, 0);
317        int respLen = sqlite3_column_bytes(this->selectIssuer, 0);
318        certificate = SecCertificateCreateWithBytes(NULL, respData, respLen);
319    }
320
321    require_noerr(s3e = sec_sqlite3_reset(this->selectIssuer, s3e), errOut);
322
323errOut:
324    if (s3e) {
325        if (s3e != SQLITE_DONE) {
326            caissuerErrorLog("caissuer cache lookup failed: %s", sqlite3_errmsg(this->s3h));
327            /* TODO: Blow away the cache and create a new db. */
328        }
329
330        if (certificate) {
331            CFRelease(certificate);
332            certificate = NULL;
333        }
334    }
335
336    secdebug("caissuercache", "returning %s for %@", (certificate ? "cached response" : "NULL"), uri);
337
338    return certificate;
339}
340
341static void _SecCAIssuerCacheGC(void *context) {
342    SecCAIssuerCacheRef this = context;
343    int s3e;
344
345    require_noerr(s3e = SecCAIssuerCacheEnsureTxn(this), errOut);
346    secdebug("caissuercache", "expiring stale responses");
347    s3e = sqlite3_bind_double(this->expire, 1, CFAbsoluteTimeGetCurrent());
348    if (!s3e) s3e = sqlite3_step(this->expire);
349    require_noerr(s3e = sec_sqlite3_reset(this->expire, s3e), errOut);
350    require_noerr(s3e = SecCAIssuerCacheCommitTxn(this), errOut);
351
352errOut:
353    if (s3e) {
354        caissuerErrorLog("caissuer cache expire failed: %s", sqlite3_errmsg(this->s3h));
355        /* TODO: Blow away the cache and create a new db. */
356    }
357}
358
359static void _SecCAIssuerCacheFlush(void *context) {
360    SecCAIssuerCacheRef this = context;
361    int s3e;
362
363    secdebug("caissuercache", "flushing pending changes");
364    s3e = SecCAIssuerCacheCommitTxn(this);
365
366    if (s3e) {
367        caissuerErrorLog("caissuer cache flush failed: %s", sqlite3_errmsg(this->s3h));
368        /* TODO: Blow away the cache and create a new db. */
369    }
370}
371
372/* Public API */
373
374void SecCAIssuerCacheAddCertificate(SecCertificateRef certificate,
375                                    CFURLRef uri, CFAbsoluteTime expires) {
376    dispatch_once(&kSecCAIssuerCacheOnce, ^{
377        SecCAIssuerCacheInit();
378    });
379    if (!kSecCAIssuerCache)
380        return;
381
382    dispatch_sync(kSecCAIssuerCache->queue, ^{
383        _SecCAIssuerCacheAddCertificate(kSecCAIssuerCache, certificate, uri, expires);
384    });
385}
386
387SecCertificateRef SecCAIssuerCacheCopyMatching(CFURLRef uri) {
388    dispatch_once(&kSecCAIssuerCacheOnce, ^{
389        SecCAIssuerCacheInit();
390    });
391    __block SecCertificateRef cert = NULL;
392    if (kSecCAIssuerCache)
393        dispatch_sync(kSecCAIssuerCache->queue, ^{
394            cert = _SecCAIssuerCacheCopyMatching(kSecCAIssuerCache, uri);
395        });
396    return cert;
397}
398
399/* This should be called on a normal non emergency exit. This function
400 effectively does a SecCAIssuerCacheFlush.
401 Currently this is called from our atexit handeler.
402 This function expires any records that are stale and commits.
403
404 Idea for future cache management policies:
405 Expire old cache entires from database if:
406 - The time to do so has arrived based on the nextExpire date in the
407 policy table.
408 - If the size of the database exceeds the limit set in the maxSize field
409 in the policy table, vacuum the db.  If the database is still too
410 big, expire records on a LRU basis.
411 */
412void SecCAIssuerCacheGC(void) {
413    if (kSecCAIssuerCache)
414        dispatch_sync(kSecCAIssuerCache->queue, ^{
415            _SecCAIssuerCacheGC(kSecCAIssuerCache);
416        });
417}
418
419/* Call this periodically or perhaps when we are exiting due to low memory. */
420void SecCAIssuerCacheFlush(void) {
421    if (kSecCAIssuerCache)
422        dispatch_sync(kSecCAIssuerCache->queue, ^{
423            _SecCAIssuerCacheFlush(kSecCAIssuerCache);
424        });
425}
426