]> git.saurik.com Git - apple/security.git/blob - sec/securityd/SecTrustStoreServer.c
Security-55178.0.1.tar.gz
[apple/security.git] / sec / securityd / SecTrustStoreServer.c
1 /*
2 * Copyright (c) 2007-2010 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 * SecTrustStoreServer.c - CertificateSource API to a system root certificate store
26 */
27 #include "SecTrustStoreServer.h"
28
29 #include <Security/SecCertificateInternal.h>
30 #include <Security/SecFramework.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <pthread.h>
34 #include <sqlite3.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <CoreFoundation/CFData.h>
42 #include <CoreFoundation/CFPropertyList.h>
43 #include <CoreFoundation/CFURL.h>
44 #include <AssertMacros.h>
45 #include <security_utilities/debugging.h>
46 #include "SecBasePriv.h"
47 #include <Security/SecInternal.h>
48 #include "securityd_client.h"
49 #include "securityd_server.h"
50 #include "sqlutils.h"
51
52 /* uid of the _securityd user. */
53 #define SECURTYD_UID 64
54
55 #if 0
56 #include <CoreFoundation/CFPriv.h>
57 #else
58 CF_EXPORT
59 CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName); /* Pass NULL for the current user's home directory */
60 #endif
61
62 static pthread_once_t kSecTrustStoreUserOnce = PTHREAD_ONCE_INIT;
63 static SecTrustStoreRef kSecTrustStoreUser = NULL;
64
65 static const char copyParentsSQL[] = "SELECT data FROM tsettings WHERE subj=?";
66 static const char containsSQL[] = "SELECT tset FROM tsettings WHERE sha1=?";
67 static const char insertSQL[] = "INSERT INTO tsettings(sha1,subj,tset,data)VALUES(?,?,?,?)";
68 static const char updateSQL[] = "UPDATE tsettings SET tset=? WHERE sha1=?";
69 static const char deleteSQL[] = "DELETE FROM tsettings WHERE sha1=?";
70 static const char deleteAllSQL[] = "BEGIN EXCLUSIVE TRANSACTION; DELETE from tsettings; COMMIT TRANSACTION; VACUUM;";
71
72 #define kSecTrustStoreName CFSTR("TrustStore")
73 #define kSecTrustStoreDbExtension CFSTR("sqlite3")
74
75 #define kSecTrustStoreUserPath "/Library/Keychains/TrustStore.sqlite3"
76
77
78 struct __SecTrustStore {
79 pthread_mutex_t lock;
80 sqlite3 *s3h;
81 sqlite3_stmt *copyParents;
82 sqlite3_stmt *contains;
83 bool readOnly;
84 };
85
86 static int sec_create_path(const char *path)
87 {
88 char pathbuf[PATH_MAX];
89 size_t pos, len = strlen(path);
90 if (len == 0 || len > PATH_MAX)
91 return SQLITE_CANTOPEN;
92 memcpy(pathbuf, path, len);
93 for (pos = len-1; pos > 0; --pos)
94 {
95 /* Search backwards for trailing '/'. */
96 if (pathbuf[pos] == '/')
97 {
98 pathbuf[pos] = '\0';
99 /* Attempt to create parent directories of the database. */
100 if (!mkdir(pathbuf, 0777))
101 break;
102 else
103 {
104 int err = errno;
105 if (err == EEXIST)
106 return 0;
107 if (err == ENOTDIR)
108 return SQLITE_CANTOPEN;
109 if (err == EROFS)
110 return SQLITE_READONLY;
111 if (err == EACCES)
112 return SQLITE_PERM;
113 if (err == ENOSPC || err == EDQUOT)
114 return SQLITE_FULL;
115 if (err == EIO)
116 return SQLITE_IOERR;
117
118 /* EFAULT || ELOOP | ENAMETOOLONG || something else */
119 return SQLITE_INTERNAL;
120 }
121 }
122 }
123 return SQLITE_OK;
124 }
125
126 static int sec_sqlite3_open(const char *db_name, sqlite3 **s3h,
127 bool create_path)
128 {
129 int s3e;
130 s3e = sqlite3_open(db_name, s3h);
131 if (s3e == SQLITE_CANTOPEN && create_path) {
132 /* Make sure the path to db_name exists and is writable, then
133 try again. */
134 s3e = sec_create_path(db_name);
135 if (!s3e)
136 s3e = sqlite3_open(db_name, s3h);
137 }
138
139 return s3e;
140 }
141
142 static SecTrustStoreRef SecTrustStoreCreate(const char *db_name,
143 bool create) {
144 SecTrustStoreRef ts;
145 int s3e;
146
147 require(ts = (SecTrustStoreRef)malloc(sizeof(struct __SecTrustStore)), errOut);
148 pthread_mutex_init(&ts->lock, NULL);
149 require_noerr(s3e = sec_sqlite3_open(db_name, &ts->s3h, create), errOut);
150
151 s3e = sqlite3_prepare(ts->s3h, copyParentsSQL, sizeof(copyParentsSQL),
152 &ts->copyParents, NULL);
153 if (create && s3e == SQLITE_ERROR) {
154 /* sqlite3_prepare returns SQLITE_ERROR if the table we are
155 compiling this statement for doesn't exist. */
156 char *errmsg = NULL;
157 s3e = sqlite3_exec(ts->s3h,
158 "CREATE TABLE tsettings("
159 "sha1 BLOB NOT NULL DEFAULT '',"
160 "subj BLOB NOT NULL DEFAULT '',"
161 "tset BLOB,"
162 "data BLOB,"
163 "PRIMARY KEY(sha1)"
164 ");"
165 "CREATE INDEX isubj ON tsettings(subj);"
166 , NULL, NULL, &errmsg);
167 if (errmsg) {
168 secwarning("CREATE TABLE cert: %s", errmsg);
169 sqlite3_free(errmsg);
170 }
171 require_noerr(s3e, errOut);
172 s3e = sqlite3_prepare(ts->s3h, copyParentsSQL, sizeof(copyParentsSQL),
173 &ts->copyParents, NULL);
174 }
175 require_noerr(s3e, errOut);
176 require_noerr(s3e = sqlite3_prepare(ts->s3h, containsSQL, sizeof(containsSQL),
177 &ts->contains, NULL), errOut);
178
179 return ts;
180
181 errOut:
182 if (ts) {
183 sqlite3_close(ts->s3h);
184 pthread_mutex_destroy(&ts->lock);
185 free(ts);
186 }
187
188 return NULL;
189 }
190
191 static void SecTrustStoreInitUser(void) {
192 static const char * path = kSecTrustStoreUserPath;
193 #if NO_SERVER
194 /* Added this block of code back to keep the tests happy for now. */
195 const char *home = getenv("HOME");
196 char buffer[PATH_MAX];
197 size_t homeLen;
198 size_t pathLen = strlen(path);
199 if (home) {
200 homeLen = strlen(home);
201 if (homeLen + pathLen >= sizeof(buffer)) {
202 return;
203 }
204
205 strlcpy(buffer, home, sizeof(buffer));
206 } else {
207 CFURLRef homeURL = CFCopyHomeDirectoryURLForUser(NULL);
208 if (!homeURL)
209 return;
210
211 CFURLGetFileSystemRepresentation(homeURL, true, (uint8_t *)buffer,
212 sizeof(buffer));
213 CFRelease(homeURL);
214 homeLen = strlen(buffer);
215 buffer[homeLen] = '\0';
216 if (homeLen + pathLen >= sizeof(buffer)) {
217 return;
218 }
219 }
220
221 strlcat(buffer, path, sizeof(buffer));
222
223 path = buffer;
224
225 #endif
226
227 kSecTrustStoreUser = SecTrustStoreCreate(path, true);
228 if (kSecTrustStoreUser)
229 kSecTrustStoreUser->readOnly = false;
230 }
231
232 /* AUDIT[securityd](done):
233 domainName (ok) is a caller provided string of any length (might be 0), only
234 its cf type has been checked.
235 */
236 SecTrustStoreRef SecTrustStoreForDomainName(CFStringRef domainName) {
237 if (CFEqual(CFSTR("user"), domainName)) {
238 pthread_once(&kSecTrustStoreUserOnce, SecTrustStoreInitUser);
239 return kSecTrustStoreUser;
240 } else {
241 return NULL;
242 }
243 }
244
245 /* AUDIT[securityd](done):
246 ts (ok) might be NULL.
247 certificate (ok) is a valid SecCertificateRef.
248 trustSettingsDictOrArray (checked by CFPropertyListCreateXMLData) is either
249 NULL, a dictionary or an array, but its contents have not been checked.
250 */
251 OSStatus _SecTrustStoreSetTrustSettings(SecTrustStoreRef ts,
252 SecCertificateRef certificate,
253 CFTypeRef trustSettingsDictOrArray) {
254 OSStatus status = errSecParam;
255
256 require_quiet(ts, errOutNotLocked);
257 if (ts->readOnly) {
258 status = errSecReadOnly;
259 goto errOutNotLocked;
260 }
261 require_noerr(pthread_mutex_lock(&ts->lock), errOutNotLocked);
262 sqlite3_stmt *insert = NULL, *update = NULL;
263 CFDataRef xmlData = NULL;
264 CFArrayRef array = NULL;
265
266 CFDataRef subject;
267 require(subject = SecCertificateGetNormalizedSubjectContent(certificate),
268 errOut);
269 CFDataRef digest;
270 require(digest = SecCertificateGetSHA1Digest(certificate), errOut);
271
272 /* Do some basic checks on the trust settings passed in. */
273 if(trustSettingsDictOrArray == NULL) {
274 require(array = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks), errOut);
275 trustSettingsDictOrArray = array;
276 }
277 else if(CFGetTypeID(trustSettingsDictOrArray) == CFDictionaryGetTypeID()) {
278 /* array-ize it */
279 array = CFArrayCreate(NULL, &trustSettingsDictOrArray, 1,
280 &kCFTypeArrayCallBacks);
281 trustSettingsDictOrArray = array;
282 }
283 else {
284 require(CFGetTypeID(trustSettingsDictOrArray) == CFArrayGetTypeID(), errOut);
285 }
286
287 require(xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault,
288 trustSettingsDictOrArray), errOut);
289
290 int s3e = sqlite3_exec(ts->s3h, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL);
291 if (s3e != SQLITE_OK) {
292 status = errSecInternal;
293 goto errOut;
294 }
295
296 /* Parameter order is sha1,subj,tset,data. */
297 require_noerr(sqlite3_prepare(ts->s3h, insertSQL, sizeof(insertSQL),
298 &insert, NULL), errOutSql);
299 require_noerr(sqlite3_bind_blob_wrapper(insert, 1,
300 CFDataGetBytePtr(digest), CFDataGetLength(digest), SQLITE_STATIC),
301 errOutSql);
302 require_noerr(sqlite3_bind_blob_wrapper(insert, 2,
303 CFDataGetBytePtr(subject), CFDataGetLength(subject),
304 SQLITE_STATIC), errOutSql);
305 require_noerr(sqlite3_bind_blob_wrapper(insert, 3,
306 CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData),
307 SQLITE_STATIC), errOutSql);
308 require_noerr(sqlite3_bind_blob_wrapper(insert, 4,
309 SecCertificateGetBytePtr(certificate),
310 SecCertificateGetLength(certificate), SQLITE_STATIC), errOutSql);
311 s3e = sqlite3_step(insert);
312 if (s3e == SQLITE_DONE) {
313 /* Great the insert worked. */
314 status = noErr;
315 } else if (s3e == SQLITE_ERROR) {
316 /* Try update. */
317 require_noerr(sqlite3_prepare(ts->s3h, updateSQL, sizeof(updateSQL),
318 &update, NULL), errOutSql);
319 require_noerr(sqlite3_bind_blob_wrapper(update, 1,
320 CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData),
321 SQLITE_STATIC), errOutSql);
322 require_noerr(sqlite3_bind_blob_wrapper(update, 2,
323 CFDataGetBytePtr(digest), CFDataGetLength(digest), SQLITE_STATIC),
324 errOutSql);
325 s3e = sqlite3_step(update);
326 require(s3e == SQLITE_DONE, errOutSql);
327 status = noErr;
328 } else {
329 require_noerr(s3e, errOutSql);
330 }
331
332 errOutSql:
333 if (insert)
334 s3e = sqlite3_finalize(insert);
335 if (update)
336 s3e = sqlite3_finalize(update);
337
338 if (s3e == SQLITE_OK)
339 sqlite3_exec(ts->s3h, "COMMIT TRANSACTION", NULL, NULL, NULL);
340 else
341 sqlite3_exec(ts->s3h, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
342
343 errOut:
344 CFReleaseSafe(xmlData);
345 CFReleaseSafe(array);
346 verify_noerr(pthread_mutex_unlock(&ts->lock));
347 errOutNotLocked:
348 return status;
349 }
350
351 /* AUDIT[securityd](done):
352 ts (ok) might be NULL.
353 digest (ok) is a data of any length (might be 0).
354 */
355 OSStatus SecTrustStoreRemoveCertificateWithDigest(SecTrustStoreRef ts,
356 CFDataRef digest) {
357 sqlite3_stmt *deleteStmt = NULL;
358
359 require_quiet(ts, errOutNotLocked);
360 require(!ts->readOnly, errOutNotLocked);
361 require_noerr(pthread_mutex_lock(&ts->lock), errOutNotLocked);
362 require_noerr(sqlite3_prepare(ts->s3h, deleteSQL, sizeof(deleteSQL),
363 &deleteStmt, NULL), errOut);
364 require_noerr(sqlite3_bind_blob_wrapper(deleteStmt, 1,
365 CFDataGetBytePtr(digest), CFDataGetLength(digest), SQLITE_STATIC),
366 errOut);
367 sqlite3_step(deleteStmt);
368
369 errOut:
370 if (deleteStmt) {
371 verify_noerr(sqlite3_finalize(deleteStmt));
372 }
373 verify_noerr(pthread_mutex_unlock(&ts->lock));
374 errOutNotLocked:
375 return noErr;
376 }
377
378 bool _SecTrustStoreRemoveAll(SecTrustStoreRef ts)
379 {
380 bool removed_all = false;
381 require(ts, errOutNotLocked);
382 require(!ts->readOnly, errOutNotLocked);
383 require_noerr(pthread_mutex_lock(&ts->lock), errOutNotLocked);
384 if (SQLITE_OK == sqlite3_exec(ts->s3h, deleteAllSQL, NULL, NULL, NULL))
385 removed_all = true;
386
387 /* prepared statements become unusable after deleteAllSQL, reset them */
388 if (ts->copyParents)
389 sqlite3_finalize(ts->copyParents);
390 sqlite3_prepare(ts->s3h, copyParentsSQL, sizeof(copyParentsSQL),
391 &ts->copyParents, NULL);
392 if (ts->contains)
393 sqlite3_finalize(ts->contains);
394 sqlite3_prepare(ts->s3h, containsSQL, sizeof(containsSQL),
395 &ts->contains, NULL);
396
397 verify_noerr(pthread_mutex_unlock(&ts->lock));
398 errOutNotLocked:
399 return removed_all;
400 }
401
402 CFArrayRef SecTrustStoreCopyParents(SecTrustStoreRef ts,
403 SecCertificateRef certificate) {
404 CFMutableArrayRef parents = NULL;
405
406 require(ts, errOutNotLocked);
407 require_noerr(pthread_mutex_lock(&ts->lock), errOutNotLocked);
408
409 CFDataRef issuer;
410 require(issuer = SecCertificateGetNormalizedIssuerContent(certificate),
411 errOut);
412 /* @@@ Might have to use SQLITE_TRANSIENT */
413 require_noerr(sqlite3_bind_blob_wrapper(ts->copyParents, 1,
414 CFDataGetBytePtr(issuer), CFDataGetLength(issuer),
415 SQLITE_STATIC), errOut);
416
417 require(parents = CFArrayCreateMutable(kCFAllocatorDefault, 0,
418 &kCFTypeArrayCallBacks), errOut);
419 for (;;) {
420 int s3e = sqlite3_step(ts->copyParents);
421 if (s3e == SQLITE_ROW) {
422 SecCertificateRef cert;
423 require(cert = SecCertificateCreateWithBytes(kCFAllocatorDefault,
424 sqlite3_column_blob(ts->copyParents, 0),
425 sqlite3_column_bytes(ts->copyParents, 0)), errOut);
426 CFArrayAppendValue(parents, cert);
427 CFRelease(cert);
428 } else {
429 require(s3e == SQLITE_DONE, errOut);
430 break;
431 }
432 }
433
434 goto ok;
435 errOut:
436 if (parents) {
437 CFRelease(parents);
438 parents = NULL;
439 }
440 ok:
441 verify_noerr(sqlite3_reset(ts->copyParents));
442 verify_noerr(sqlite3_clear_bindings(ts->copyParents));
443 verify_noerr(pthread_mutex_unlock(&ts->lock));
444 errOutNotLocked:
445 return parents;
446 }
447
448 /* AUDIT[securityd](done):
449 ts (ok) might be NULL.
450 digest (ok) is a data of any length (might be 0), only its cf type has
451 been checked.
452 */
453 bool SecTrustStoreContainsCertificateWithDigest(SecTrustStoreRef ts,
454 CFDataRef digest) {
455 bool contains = false;
456
457 require_quiet(ts, errOutNotLocked);
458 require_noerr(pthread_mutex_lock(&ts->lock), errOutNotLocked);
459
460 /* @@@ Might have to use SQLITE_TRANSIENT */
461 require_noerr(sqlite3_bind_blob_wrapper(ts->contains, 1,
462 CFDataGetBytePtr(digest), CFDataGetLength(digest), SQLITE_STATIC),
463 errOut);
464 int s3e = sqlite3_step(ts->contains);
465 if (s3e == SQLITE_ROW) {
466 contains = true;
467 } else {
468 require(s3e == SQLITE_DONE, errOut);
469 }
470
471 errOut:
472 verify_noerr(sqlite3_reset(ts->contains));
473 verify_noerr(sqlite3_clear_bindings(ts->contains));
474 verify_noerr(pthread_mutex_unlock(&ts->lock));
475 errOutNotLocked:
476 return contains;
477 }