1#include <Security/SecKeychain.h>
2#include <Security/SecKeychainPriv.h>
3#include <stdlib.h>
4#include <unistd.h>
5
6#include "testenv.h"
7#include "testleaks.h"
8#include "testmore.h"
9#include "testsecevent.h"
10
11void tests(int dont_skip)
12{
13	const char *user = getenv("USER");
14	ok((user != NULL && strlen(user) != 0), "USER must be non-nil and non-zero length");
15	fprintf(stdout, "Testing login for user \"%s\"\n", user);
16
17	ok_status(test_sec_event_register(kSecEveryEventMask),
18		"register for all events");
19
20	// test SecKeychainLogin for $USER with password "test"
21	ok_status(SecKeychainLogin(strlen(user), user, 4, "test"), "login user");
22
23	// wait for a list changed event
24	is_sec_event(kSecKeychainListChangedEvent, NULL, NULL, NULL,
25		"list changed event");
26	no_sec_event("no event");
27
28	// get the default keychain (should be login.keychain if none is explicitly set)
29	SecKeychainRef default_keychain = NULL;
30	ok_status(SecKeychainCopyDefault(&default_keychain), "get default");
31
32	// test status of default keychain (should be read/write and unlocked)
33	SecKeychainStatus status = 0;
34	ok_status(SecKeychainGetStatus(default_keychain, &status), "get status");
35	is(status, kSecUnlockStateStatus|kSecReadPermStatus|kSecWritePermStatus,
36		"default should be read/write/unlocked");
37
38	// get the path for the default keychain
39	char path[1024];
40	UInt32 path_len = sizeof(path) - 1;
41	ok_status(SecKeychainGetPath(default_keychain, &path_len, path),
42		"get path");
43	fprintf(stdout, "Default keychain path is %s\n", path);
44	path[path_len] = 0;
45	const char *login_path = "Library/Keychains/login.keychain";
46	cmp_ok(path_len, >, strlen(login_path), "path len is enough");
47	eq_string(path + path_len - strlen(login_path), login_path, "check path");
48
49	// check retain count on default keychain (why??)
50	is(CFGetRetainCount(default_keychain), 1, "default retain count is 1");
51	CFRelease(default_keychain);
52	default_keychain = NULL;
53
54	// lock and unlock events have been removed because they can't be made reliable
55
56	ok_status(test_sec_event_deregister(), "deregister events.");
57
58	// rename login.keychain to $USER to simulate a Panther-style keychain
59	char testuser_path[1024];
60	sprintf(testuser_path, "Library/Keychains/%s", user);
61	ok_unix(rename(login_path, testuser_path),
62		"rename login.keychain to $USER");
63
64	// login and verify that SecKeychainLogin cleans up the $USER keychain
65	// (either by renaming to $USER.keychain, or renaming to login.keychain)
66    ok_status(SecKeychainLogin(strlen(user), user, 4, "test"), "login again");
67
68	// get the default keychain (should be login.keychain if none is explicitly set)
69	ok_status(SecKeychainCopyDefault(&default_keychain), "get default");
70	path_len = sizeof(path) - 1;
71	ok_status(SecKeychainGetPath(default_keychain, &path_len, path),
72		"get path");
73	path[path_len] = 0;
74	cmp_ok(path_len, >, strlen(testuser_path), "path len is enough");
75
76	// lock the default keychain
77	ok_status(SecKeychainLock(default_keychain), "lock default");
78	CFRelease(default_keychain);
79
80	ok(tests_end(1), "cleanup");
81}
82
83int main(int argc, char *const *argv)
84{
85	int dont_skip = argc > 1 && !strcmp(argv[1], "-s");
86	plan_tests(21);
87
88	if (!tests_begin(argc, argv))
89		BAIL_OUT("tests_begin failed");
90
91	tests(dont_skip);
92	ok_leaks("no leaks");
93
94	return 0;
95}
96