]> git.saurik.com Git - apple/security.git/blob - trust/trustd/SecTrustExceptionResetCount.m
Security-59306.41.2.tar.gz
[apple/security.git] / trust / trustd / SecTrustExceptionResetCount.m
1 //
2 // SecTrustExceptionResetCount.m
3 // Security_ios
4 //
5
6 #import <Foundation/Foundation.h>
7 #import "SecTrustExceptionResetCount.h"
8
9 #import <utilities/SecCFWrappers.h>
10 #import <utilities/SecFileLocations.h>
11
12 static NSString *kExceptionResetCountKey = @"ExceptionResetCount";
13 static NSString *exceptionResetCounterFile = @"com.apple.security.exception_reset_counter.plist";
14
15 /* Returns the path to the, existing or internally-created, 'exceptionResetCounterFile' file. */
16 static NSString *SecPlistFileExistsInKeychainDirectory(CFErrorRef *error) {
17 NSString *status = NULL;
18
19 NSString *path = [(__bridge_transfer NSURL*)SecCopyURLForFileInKeychainDirectory((__bridge CFStringRef)exceptionResetCounterFile) path];
20 if (!path) {
21 secerror("Unable to address permanent storage for '%{public}@'.", exceptionResetCounterFile);
22 if (error) {
23 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ENOENT, NULL);
24 }
25 return status;
26 }
27 secinfo("trust", "'%{public}@' is at '%{public}@'.", exceptionResetCounterFile, path);
28
29 NSFileManager *fm = [NSFileManager defaultManager];
30 if (!fm) {
31 secerror("Failed to initialize the file manager in '%{public}s'.", __func__);
32 if (error) {
33 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ENOMEM, NULL);
34 }
35 return status;
36 }
37
38 BOOL isDir = false;
39 bool fileExists = [fm fileExistsAtPath:path isDirectory:&isDir];
40 if (isDir) {
41 secerror("'%{public}@' is a directory. (not a file)", path);
42 if (error) {
43 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, EISDIR, NULL);
44 }
45 return status;
46 }
47 if (fileExists) {
48 secdebug("trust", "'%{public}@' already exists.", path);
49 status = path;
50 return status;
51 }
52
53 if (![fm createFileAtPath:path contents:nil attributes:nil]) {
54 secerror("Failed to create permanent storage at '%{public}@'.", path);
55 if (error) {
56 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, EIO, NULL);
57 }
58 return status;
59 }
60 secinfo("trust", "'%{public}@' has been created.", path);
61 status = path;
62
63 return status;
64 }
65
66 static uint64_t SecReadPlistFromFileInKeychainDirectory(CFErrorRef *error) {
67 uint64_t value = 0;
68
69 CFErrorRef localError = NULL;
70 NSString *path = SecPlistFileExistsInKeychainDirectory(&localError);
71 if (localError) {
72 if (error) {
73 *error = localError;
74 }
75 secerror("Permanent storage for the exceptions epoch is unavailable.");
76 return value;
77 }
78 if (!path) {
79 secinfo("trust", "Permanent storage for the exceptions epoch is missing. Defaulting to value %llu.", value);
80 return value;
81 }
82
83 NSMutableDictionary *plDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
84 if (!plDict) {
85 secerror("Failed to read from permanent storage at '%{public}@' or the data is bad. Defaulting to value %llu.", path, value);
86 if (error) {
87 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ENXIO, NULL);
88 }
89 return value;
90 }
91
92 id valueObject = [plDict objectForKey:kExceptionResetCountKey];
93 if (!valueObject) {
94 secinfo("trust", "Could not find key '%{public}@'. Defaulting to value %llu.", kExceptionResetCountKey, value);
95 if (error) {
96 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ENXIO, NULL);
97 }
98 return value;
99 }
100 if (![valueObject isKindOfClass:[NSNumber class]]) {
101 secerror("The value for key '%{public}@' is not a number.", kExceptionResetCountKey);
102 if (error) {
103 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, EDOM, NULL);
104 }
105 return value;
106 }
107
108 value = [valueObject unsignedIntValue];
109
110 secinfo("trust", "'%{public}@' is %llu.", kExceptionResetCountKey, value);
111 return value;
112 }
113
114 static bool SecWritePlistToFileInKeychainDirectory(uint64_t exceptionResetCount, CFErrorRef *error) {
115 bool status = false;
116
117 CFErrorRef localError = NULL;
118 SecPlistFileExistsInKeychainDirectory(&localError);
119 if (localError) {
120 if (error) {
121 *error = localError;
122 }
123 secerror("Permanent storage for the exceptions epoch is unavailable.");
124 return status;
125 }
126
127 NSString *path = [(__bridge_transfer NSURL*)SecCopyURLForFileInKeychainDirectory((__bridge CFStringRef)exceptionResetCounterFile) path];
128 if (!path) {
129 secerror("Unable to address permanent storage for '%{public}@'.", exceptionResetCounterFile);
130 if (error) {
131 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, EIO, NULL);
132 }
133 return status;
134 }
135
136 NSMutableDictionary *dataToSave = [NSMutableDictionary new];
137 if (!dataToSave) {
138 secerror("Failed to allocate memory for the exceptions epoch structure.");
139 if (error) {
140 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ENOMEM, NULL);
141 }
142 return status;
143 }
144 dataToSave[@"Version"] = [NSNumber numberWithUnsignedInteger:1];
145 dataToSave[kExceptionResetCountKey] = [NSNumber numberWithUnsignedInteger:exceptionResetCount];
146
147 status = [dataToSave writeToFile:path atomically:YES];
148 if (!status) {
149 secerror("Failed to write to permanent storage at '%{public}@'.", path);
150 if (error) {
151 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, EIO, NULL);
152 }
153 return status;
154 }
155
156 secinfo("trust", "'%{public}@' has been committed to permanent storage at '%{public}@'.", kExceptionResetCountKey, path);
157 return status;
158 }
159
160 uint64_t SecTrustServerGetExceptionResetCount(CFErrorRef *error) {
161 CFErrorRef localError = NULL;
162 uint64_t exceptionResetCount = SecReadPlistFromFileInKeychainDirectory(&localError);
163 /* Treat ENXIO as a transient error; I/O seems to be working but we have failed to read the current epoch.
164 * That's expected when epoch is still 0 and there is nothing to store in permanent storage. (and later read)
165 */
166 if (localError && CFEqualSafe(CFErrorGetDomain(localError), kCFErrorDomainPOSIX) && CFErrorGetCode(localError) == ENXIO) {
167 CFRelease(localError);
168 localError = NULL;
169 }
170 if (error && localError) {
171 *error = localError;
172 }
173 secinfo("trust", "exceptionResetCount: %llu (%s)", exceptionResetCount, error ? (*error ? "Error" : "OK") : "N/A");
174 return exceptionResetCount;
175 }
176
177 bool SecTrustServerIncrementExceptionResetCount(CFErrorRef *error) {
178 bool status = false;
179
180 uint64_t currentExceptionResetCount = SecTrustServerGetExceptionResetCount(error);
181 if (error && *error) {
182 secerror("Failed to increment the extensions epoch.");
183 return status;
184 }
185 if (currentExceptionResetCount >= INT64_MAX) {
186 secerror("Current exceptions epoch value is too large. (%llu) Won't increment.", currentExceptionResetCount);
187 if (error) {
188 *error = CFErrorCreate(NULL, kCFErrorDomainPOSIX, ERANGE, NULL);
189 }
190 return status;
191 }
192
193 return SecWritePlistToFileInKeychainDirectory(currentExceptionResetCount + 1, error);
194 }