2 * Copyright (c) 2007-2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecTrustStoreServer.c - CertificateSource API to a system root certificate store
27 #include "SecTrustStoreServer.h"
29 #include <Security/SecCertificateInternal.h>
30 #include <Security/SecFramework.h>
38 #include <sys/param.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"
52 /* uid of the _securityd user. */
53 #define SECURTYD_UID 64
56 #include <CoreFoundation/CFPriv.h>
59 CFURLRef
CFCopyHomeDirectoryURLForUser(CFStringRef uName
); /* Pass NULL for the current user's home directory */
62 static pthread_once_t kSecTrustStoreUserOnce
= PTHREAD_ONCE_INIT
;
63 static SecTrustStoreRef kSecTrustStoreUser
= NULL
;
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;";
72 #define kSecTrustStoreName CFSTR("TrustStore")
73 #define kSecTrustStoreDbExtension CFSTR("sqlite3")
75 #define kSecTrustStoreUserPath "/Library/Keychains/TrustStore.sqlite3"
78 struct __SecTrustStore
{
81 sqlite3_stmt
*copyParents
;
82 sqlite3_stmt
*contains
;
86 static int sec_create_path(const char *path
)
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
)
95 /* Search backwards for trailing '/'. */
96 if (pathbuf
[pos
] == '/')
99 /* Attempt to create parent directories of the database. */
100 if (!mkdir(pathbuf
, 0777))
108 return SQLITE_CANTOPEN
;
110 return SQLITE_READONLY
;
113 if (err
== ENOSPC
|| err
== EDQUOT
)
118 /* EFAULT || ELOOP | ENAMETOOLONG || something else */
119 return SQLITE_INTERNAL
;
126 static int sec_sqlite3_open(const char *db_name
, sqlite3
**s3h
,
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
134 s3e
= sec_create_path(db_name
);
136 s3e
= sqlite3_open(db_name
, s3h
);
142 static SecTrustStoreRef
SecTrustStoreCreate(const char *db_name
,
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
);
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. */
157 s3e
= sqlite3_exec(ts
->s3h
,
158 "CREATE TABLE tsettings("
159 "sha1 BLOB NOT NULL DEFAULT '',"
160 "subj BLOB NOT NULL DEFAULT '',"
165 "CREATE INDEX isubj ON tsettings(subj);"
166 , NULL
, NULL
, &errmsg
);
168 secwarning("CREATE TABLE cert: %s", errmsg
);
169 sqlite3_free(errmsg
);
171 require_noerr(s3e
, errOut
);
172 s3e
= sqlite3_prepare(ts
->s3h
, copyParentsSQL
, sizeof(copyParentsSQL
),
173 &ts
->copyParents
, NULL
);
175 require_noerr(s3e
, errOut
);
176 require_noerr(s3e
= sqlite3_prepare(ts
->s3h
, containsSQL
, sizeof(containsSQL
),
177 &ts
->contains
, NULL
), errOut
);
183 sqlite3_close(ts
->s3h
);
184 pthread_mutex_destroy(&ts
->lock
);
191 static void SecTrustStoreInitUser(void) {
192 static const char * path
= kSecTrustStoreUserPath
;
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
];
198 size_t pathLen
= strlen(path
);
200 homeLen
= strlen(home
);
201 if (homeLen
+ pathLen
>= sizeof(buffer
)) {
205 strlcpy(buffer
, home
, sizeof(buffer
));
207 CFURLRef homeURL
= CFCopyHomeDirectoryURLForUser(NULL
);
211 CFURLGetFileSystemRepresentation(homeURL
, true, (uint8_t *)buffer
,
214 homeLen
= strlen(buffer
);
215 buffer
[homeLen
] = '\0';
216 if (homeLen
+ pathLen
>= sizeof(buffer
)) {
221 strlcat(buffer
, path
, sizeof(buffer
));
227 kSecTrustStoreUser
= SecTrustStoreCreate(path
, true);
228 if (kSecTrustStoreUser
)
229 kSecTrustStoreUser
->readOnly
= false;
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.
236 SecTrustStoreRef
SecTrustStoreForDomainName(CFStringRef domainName
) {
237 if (CFEqual(CFSTR("user"), domainName
)) {
238 pthread_once(&kSecTrustStoreUserOnce
, SecTrustStoreInitUser
);
239 return kSecTrustStoreUser
;
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.
251 OSStatus
_SecTrustStoreSetTrustSettings(SecTrustStoreRef ts
,
252 SecCertificateRef certificate
,
253 CFTypeRef trustSettingsDictOrArray
) {
254 OSStatus status
= errSecParam
;
256 require_quiet(ts
, errOutNotLocked
);
258 status
= errSecReadOnly
;
259 goto errOutNotLocked
;
261 require_noerr(pthread_mutex_lock(&ts
->lock
), errOutNotLocked
);
262 sqlite3_stmt
*insert
= NULL
, *update
= NULL
;
263 CFDataRef xmlData
= NULL
;
264 CFArrayRef array
= NULL
;
267 require(subject
= SecCertificateGetNormalizedSubjectContent(certificate
),
270 require(digest
= SecCertificateGetSHA1Digest(certificate
), errOut
);
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
;
277 else if(CFGetTypeID(trustSettingsDictOrArray
) == CFDictionaryGetTypeID()) {
279 array
= CFArrayCreate(NULL
, &trustSettingsDictOrArray
, 1,
280 &kCFTypeArrayCallBacks
);
281 trustSettingsDictOrArray
= array
;
284 require(CFGetTypeID(trustSettingsDictOrArray
) == CFArrayGetTypeID(), errOut
);
287 require(xmlData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
288 trustSettingsDictOrArray
), errOut
);
290 int s3e
= sqlite3_exec(ts
->s3h
, "BEGIN EXCLUSIVE TRANSACTION", NULL
, NULL
, NULL
);
291 if (s3e
!= SQLITE_OK
) {
292 status
= errSecInternal
;
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
),
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. */
315 } else if (s3e
== SQLITE_ERROR
) {
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
),
325 s3e
= sqlite3_step(update
);
326 require(s3e
== SQLITE_DONE
, errOutSql
);
329 require_noerr(s3e
, errOutSql
);
334 s3e
= sqlite3_finalize(insert
);
336 s3e
= sqlite3_finalize(update
);
338 if (s3e
== SQLITE_OK
)
339 sqlite3_exec(ts
->s3h
, "COMMIT TRANSACTION", NULL
, NULL
, NULL
);
341 sqlite3_exec(ts
->s3h
, "ROLLBACK TRANSACTION", NULL
, NULL
, NULL
);
344 CFReleaseSafe(xmlData
);
345 CFReleaseSafe(array
);
346 verify_noerr(pthread_mutex_unlock(&ts
->lock
));
351 /* AUDIT[securityd](done):
352 ts (ok) might be NULL.
353 digest (ok) is a data of any length (might be 0).
355 OSStatus
SecTrustStoreRemoveCertificateWithDigest(SecTrustStoreRef ts
,
357 sqlite3_stmt
*deleteStmt
= NULL
;
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
),
367 sqlite3_step(deleteStmt
);
371 verify_noerr(sqlite3_finalize(deleteStmt
));
373 verify_noerr(pthread_mutex_unlock(&ts
->lock
));
378 bool _SecTrustStoreRemoveAll(SecTrustStoreRef ts
)
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
))
387 /* prepared statements become unusable after deleteAllSQL, reset them */
389 sqlite3_finalize(ts
->copyParents
);
390 sqlite3_prepare(ts
->s3h
, copyParentsSQL
, sizeof(copyParentsSQL
),
391 &ts
->copyParents
, NULL
);
393 sqlite3_finalize(ts
->contains
);
394 sqlite3_prepare(ts
->s3h
, containsSQL
, sizeof(containsSQL
),
395 &ts
->contains
, NULL
);
397 verify_noerr(pthread_mutex_unlock(&ts
->lock
));
402 CFArrayRef
SecTrustStoreCopyParents(SecTrustStoreRef ts
,
403 SecCertificateRef certificate
) {
404 CFMutableArrayRef parents
= NULL
;
406 require(ts
, errOutNotLocked
);
407 require_noerr(pthread_mutex_lock(&ts
->lock
), errOutNotLocked
);
410 require(issuer
= SecCertificateGetNormalizedIssuerContent(certificate
),
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
);
417 require(parents
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
418 &kCFTypeArrayCallBacks
), errOut
);
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
);
429 require(s3e
== SQLITE_DONE
, errOut
);
441 verify_noerr(sqlite3_reset(ts
->copyParents
));
442 verify_noerr(sqlite3_clear_bindings(ts
->copyParents
));
443 verify_noerr(pthread_mutex_unlock(&ts
->lock
));
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
453 bool SecTrustStoreContainsCertificateWithDigest(SecTrustStoreRef ts
,
455 bool contains
= false;
457 require_quiet(ts
, errOutNotLocked
);
458 require_noerr(pthread_mutex_lock(&ts
->lock
), errOutNotLocked
);
460 /* @@@ Might have to use SQLITE_TRANSIENT */
461 require_noerr(sqlite3_bind_blob_wrapper(ts
->contains
, 1,
462 CFDataGetBytePtr(digest
), CFDataGetLength(digest
), SQLITE_STATIC
),
464 int s3e
= sqlite3_step(ts
->contains
);
465 if (s3e
== SQLITE_ROW
) {
468 require(s3e
== SQLITE_DONE
, errOut
);
472 verify_noerr(sqlite3_reset(ts
->contains
));
473 verify_noerr(sqlite3_clear_bindings(ts
->contains
));
474 verify_noerr(pthread_mutex_unlock(&ts
->lock
));