6 #import <Security/Authorization.h>
7 #import <Security/AuthorizationPriv.h>
8 #import <Security/AuthorizationDB.h>
9 #import <Security/AuthorizationTagsPriv.h>
10 #import <Foundation/Foundation.h>
11 #import "authd/debugging.h"
12 #import "authdtestlist.h"
14 void runRaft(NSString *arguments);
15 int authd_03_uiauthorization(int argc, char *const *argv);
16 bool getCredentials(void);
18 #define AuthorizationFreeItemSetNull(IS) { AuthorizationItemSet *_is = (IS); \
19 if (_is) { (IS) = NULL; AuthorizationFreeItemSet(_is); } }
21 #define SAMPLE_RIGHT "com.apple.security.syntheticinput"
22 #define SAMPLE_SHARED_RIGHT "system.preferences"
23 #define SAMPLE_PASSWORD_RIGHT "system.csfde.requestpassword"
25 NSString *correctUsername;
26 NSString *correctPassword;
28 #define INCORRECT_UNAME "fs;lgp-984-25opsdakflasdg"
29 #define INCORRECT_PWD "654sa65gsqihr6hhsfd'lbo[0q2,m23-odasdf"
31 #define SA_TIMEOUT (20)
33 #define RAFT_FILL @"target.processes()[\"SecurityAgent\"].mainWindow().textFields()[\"User Name:\"].click();keyboard.typeString_withModifiersMask_(\"a\", (kUIACommandKeyMask));keyboard.typeVirtualKey_(117);keyboard.typeString_(\"%s\");target.processes()[\"SecurityAgent\"].mainWindow().textFields()[\"Password:\"].click();keyboard.typeString_withModifiersMask_(\"a\", (kUIACommandKeyMask));keyboard.typeVirtualKey_(117);keyboard.typeString_(\"%s\");target.processes()[\"SecurityAgent\"].mainWindow().buttons()[\"OK\"].click();quit();"
35 #define RAFT_CANCEL @"target.processes()[\"SecurityAgent\"].mainWindow().buttons()[\"Cancel\"].click();quit();"
37 AuthorizationItem validCredentials[] = {
38 {AGENT_USERNAME, 0, NULL, 0},
39 {AGENT_PASSWORD, 0, NULL, 0}
42 AuthorizationItem invalidCredentials[] = {
43 {AGENT_USERNAME, strlen(INCORRECT_UNAME), (void *)INCORRECT_UNAME, 0},
44 {AGENT_PASSWORD, strlen(INCORRECT_PWD), (void *)INCORRECT_PWD,0}
49 static dispatch_once_t onceToken = 0;
50 dispatch_once(&onceToken, ^{
51 NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:@"/etc/credentials.plist"];
52 correctUsername = dict[@"username"];
53 correctPassword = dict[@"password"];
54 if (correctUsername) {
55 validCredentials[0].value = (void *)correctUsername.UTF8String;
56 if (validCredentials[0].value) {
57 validCredentials[0].valueLength = strlen(correctUsername.UTF8String);
60 if (correctPassword) {
61 validCredentials[1].value = (void *)correctPassword.UTF8String;
62 if (validCredentials[1].value) {
63 validCredentials[1].valueLength = strlen(correctPassword.UTF8String);
67 return (correctUsername != nil) && (correctPassword != nil);
70 void runRaft(NSString *arguments)
72 NSTask *task = [[NSTask alloc] init];
73 [task setLaunchPath:@"/usr/local/bin/raft"];
74 [task setArguments:@[ @"-b", @"-o", arguments]];
79 int authd_01_authorizationdb(int argc, char *const *argv)
83 CFDictionaryRef outDict = NULL;
84 OSStatus status = AuthorizationRightGet(SAMPLE_RIGHT, &outDict);
85 ok(status == errAuthorizationSuccess, "AuthorizationRightGet existing right");
86 CFReleaseNull(outDict);
88 status = AuthorizationRightGet("non-existing-right", &outDict);
89 ok(status == errAuthorizationDenied, "AuthorizationRightGet non-existing right");
94 int authd_02_basicauthorization(int argc, char *const *argv)
97 if (!getCredentials()) {
98 fail("Not able to read credentials for current user!");
101 AuthorizationRef authorizationRef;
103 OSStatus status = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authorizationRef);
104 ok(status == errAuthorizationSuccess, "AuthorizationRef create");
106 AuthorizationItem myItems = {SAMPLE_RIGHT, 0, NULL, 0};
107 AuthorizationRights myRights = {1, &myItems};
108 AuthorizationRights *authorizedRights = NULL;
109 AuthorizationEnvironment environment = {sizeof(validCredentials)/sizeof(AuthorizationItem), validCredentials};
110 status = AuthorizationCopyRights(authorizationRef, &myRights, &environment, kAuthorizationFlagDefaults, &authorizedRights);
111 ok(status == errAuthorizationDenied, "Standard authorization without kAuthorizationFlagExtendRights");
112 AuthorizationFreeItemSetNull(authorizedRights);
114 status = AuthorizationCopyRights(authorizationRef, &myRights, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights, &authorizedRights);
115 ok(status == errAuthorizationInteractionNotAllowed, "Authorization fail with UI not allowed");
116 AuthorizationFreeItemSetNull(authorizedRights);
118 status = AuthorizationCopyRights(authorizationRef, &myRights, &environment, kAuthorizationFlagExtendRights, &authorizedRights);
119 ok(status == errAuthorizationSuccess, "Standard authorization");
120 AuthorizationFreeItemSetNull(authorizedRights);
122 AuthorizationItem extendedItems = {SAMPLE_SHARED_RIGHT, 0, NULL, 0};
123 AuthorizationRights extendedRights = {1, &extendedItems};
125 status = AuthorizationCopyRights(authorizationRef, &extendedRights, &environment, kAuthorizationFlagExtendRights, &authorizedRights);
126 ok(status == errAuthorizationSuccess, "Extending authorization rights");
127 AuthorizationFreeItemSetNull(authorizedRights);
129 AuthorizationItem pwdExtractItems = {SAMPLE_PASSWORD_RIGHT, 0, NULL, 0};
130 AuthorizationRights pwdExtractRight = {1, &pwdExtractItems};
132 // check that non-entitled process cannot extract password from AuthorizationRef
133 status = AuthorizationCopyRights(authorizationRef, &pwdExtractRight, &environment, kAuthorizationFlagExtendRights, &authorizedRights);
134 Boolean passwordFound = false;
135 if (status == errAuthorizationSuccess) {
136 AuthorizationItemSet *returnedInfo;
137 status = AuthorizationCopyInfo(authorizationRef, NULL, &returnedInfo);
138 if (status == errSecSuccess && returnedInfo) {
139 for (uint32_t index = 0; index < returnedInfo->count; ++index) {
140 AuthorizationItem item = returnedInfo->items[index];
141 if (strncpy((char *)item.name, kAuthorizationEnvironmentPassword, strlen(kAuthorizationEnvironmentPassword)) == 0) {
142 passwordFound = true;
145 AuthorizationFreeItemSetNull(returnedInfo);
149 ok(status == errAuthorizationSuccess && passwordFound == false, "Extracting password from AuthorizationRef");
150 AuthorizationFreeItemSetNull(authorizedRights);
153 AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
157 int authd_03_uiauthorization(int argc, char *const *argv)
160 if (!getCredentials()) {
161 fail("Not able to read credentials for current user!");
164 AuthorizationRef authorizationRef;
166 OSStatus status = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authorizationRef);
167 ok(status == errAuthorizationSuccess, "AuthorizationRef create");
169 AuthorizationItem myItems = {SAMPLE_RIGHT, 0, NULL, 0};
170 AuthorizationRights myRights = {1, &myItems};
172 NSString *raftFillValid = [NSString stringWithFormat:RAFT_FILL, correctUsername.UTF8String, correctPassword.UTF8String];
174 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
176 AuthorizationAsyncCallback internalBlock = ^(OSStatus err, AuthorizationRights *blockAuthorizedRights) {
177 AuthorizationFreeItemSetNull(blockAuthorizedRights);
178 ok(err == errAuthorizationInternal, "Async authorization interal error");
179 dispatch_semaphore_signal(sem);
181 AuthorizationAsyncCallback denyBlock = ^(OSStatus err, AuthorizationRights *blockAuthorizedRights) {
182 AuthorizationFreeItemSetNull(blockAuthorizedRights);
183 ok(err == errAuthorizationDenied, "Async authorization denial");
184 dispatch_semaphore_signal(sem);
186 AuthorizationAsyncCallback allowBlock = ^(OSStatus err, AuthorizationRights *blockAuthorizedRights) {
187 AuthorizationFreeItemSetNull(blockAuthorizedRights);
188 ok(err == errAuthorizationSuccess, "Async authorization");
189 dispatch_semaphore_signal(sem);
191 AuthorizationAsyncCallback cancelBlock = ^(OSStatus err, AuthorizationRights *blockAuthorizedRights) {
192 AuthorizationFreeItemSetNull(blockAuthorizedRights);
193 ok(err == errAuthorizationCanceled, "Async authorization cancel");
194 dispatch_semaphore_signal(sem);
196 AuthorizationCopyRightsAsync(authorizationRef, &myRights, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, cancelBlock);
197 sleep(3); // give some time to SecurityAgent to appear
198 runRaft(RAFT_CANCEL);
199 if (dispatch_semaphore_wait(sem, SA_TIMEOUT * NSEC_PER_SEC) != 0) {
200 fail("Async authorization cancel");
202 AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
204 AuthorizationCopyRightsAsync(authorizationRef, &myRights, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, allowBlock);
205 sleep(3); // give some time to SecurityAgent to appear
206 runRaft(raftFillValid);
207 if (dispatch_semaphore_wait(sem, SA_TIMEOUT * NSEC_PER_SEC) != 0) {
208 fail("Async authorization");
209 } AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);
214 int authd_04_executewithprivileges(int argc, char *const *argv)
216 const int NUMBER_OF_ITERATIONS = 10;
217 plan_tests(2 + 4 * NUMBER_OF_ITERATIONS);
219 if (!getCredentials()) {
220 fail("Not able to read credentials for current user!");
223 AuthorizationRef authorizationRef;
224 OSStatus status = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authorizationRef);
225 ok(status == errAuthorizationSuccess, "AuthorizationRef create");
227 AuthorizationItem myItems = { kAuthorizationRightExecute, 0, NULL, 0};
228 AuthorizationRights myRights = {1, &myItems};
229 AuthorizationRights *authorizedRights = NULL;
230 AuthorizationEnvironment environment = {sizeof(validCredentials)/sizeof(AuthorizationItem), validCredentials};
231 status = AuthorizationCopyRights(authorizationRef, &myRights, &environment, kAuthorizationFlagExtendRights, &authorizedRights);
232 ok(status == errAuthorizationSuccess, "Standard authorization");
233 AuthorizationFreeItemSetNull(authorizedRights);
235 for (int i = 0; i < NUMBER_OF_ITERATIONS; ++i) {
236 NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
237 static const char *toolArgv[3];
238 NSString *arg = [NSString stringWithFormat:@"%s %@", "/usr/bin/whoami && /bin/echo", guid];
239 NSString *expected = [NSString stringWithFormat:@"root\n%@", guid];
241 toolArgv[1] = arg.UTF8String;
245 status = AuthorizationExecuteWithPrivileges(authorizationRef, "/bin/zsh", 0, (char *const *)toolArgv, &output);
246 ok(status == errAuthorizationSuccess, "AuthorizationExecuteWithPrivileges call succeess");
253 size_t bytesRead = 0;
254 size_t totalBytesRead = 0;
255 size_t buffSize = sizeof(buffer);
256 memset(buffer, 0, buffSize);
257 while ((bytesRead = fread (buffer, 1, buffSize, output) > 0)) {
258 totalBytesRead += bytesRead; // overwriting buffer is OK since we are reading just a small amount of data
261 ok(ferror(output) == 0, "Authorized tool pipe closed did not end with ferror");
262 if (ferror(output)) {
263 // test failed, ferror happened
268 ok(feof(output), "Authorized tool pipe closed with feof");
270 // test failed, feof not happened
276 if (strncmp(buffer, expected.UTF8String, guid.length) == 0) {
277 pass("Authorized tool output matches");
279 fail("AuthorizationExecuteWithPrivileges output %s does not match %s", buffer, expected.UTF8String);
283 AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
287 int authd_05_rightproperties(int argc, char *const *argv)
291 NSDictionary *properties;
292 CFDictionaryRef cfProperties;
294 NSNumber *passwordOnly;
296 OSStatus status = AuthorizationCopyRightProperties("system.csfde.requestpassword", &cfProperties);
297 properties = CFBridgingRelease(cfProperties);
298 if (status != errAuthorizationSuccess) {
299 fail("AuthorizationCopyRightProperties failed with %d", (int)status);
303 pass("AuthorizationCopyRightProperties call succeess");
304 passwordOnly = properties[@(kAuthorizationRuleParameterPasswordOnly)];
305 ok(passwordOnly.boolValue, "Returned system.csfde.requestpassword as password only right");
306 group = properties[@(kAuthorizationRuleParameterGroup)];
307 ok([group isEqualToString:@"admin"], "Returned admin as a required group for system.csfde.requestpassword");
309 status = AuthorizationCopyRightProperties("com.apple.Safari.allow-unsigned-app-extensions", &cfProperties);
310 properties = CFBridgingRelease(cfProperties);
311 if (status != errAuthorizationSuccess) {
312 fail("AuthorizationCopyRightProperties failed with %d", (int)status);
315 group = properties[@(kAuthorizationRuleParameterGroup)];
316 passwordOnly = properties[@(kAuthorizationRuleParameterPasswordOnly)];
317 ok(group.length == 0 && passwordOnly.boolValue == NO, "Returned safari right as non-password only, no specific group");
319 status = AuthorizationCopyRightProperties("non-existing-right", &cfProperties);
320 ok(status == errAuthorizationSuccess, "Returned success for default for unknown right: %d", (int)status);