seccode.c
1 // 2 // seccode.c 3 // secseccodeapitest 4 // 5 #include "authd_private.h" 6 7 #include <stdio.h> 8 #include <xpc/xpc.h> 9 #include <Security/SecCode.h> 10 #include <libkern/OSAtomic.h> 11 #include <AssertMacros.h> 12 13 #define BEGIN() \ 14 ({ \ 15 fprintf(stdout, "[BEGIN] %s\n", __FUNCTION__); \ 16 }) 17 18 #define INFO(fmt, ...) \ 19 ({ \ 20 fprintf(stdout, fmt "\n", ##__VA_ARGS__); \ 21 }) 22 23 #define PASS(fmt, ...) \ 24 ({ \ 25 fprintf(stdout, "[PASS] %s " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ 26 }) 27 28 #define FAIL(fmt, ...) \ 29 ({ \ 30 fprintf(stdout, "[FAIL] %s " fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ 31 }) 32 33 #define SAFE_RELEASE(x) \ 34 ({ \ 35 if (x) { \ 36 CFRelease(x); \ 37 x = NULL; \ 38 } \ 39 }) 40 41 enum xpcConnectionStates { 42 kXPCConnectionStateNotCancelled = 0, 43 kXPCConnectionStateCancelled, 44 kXPCConnectionStateOkayToExit, 45 kXPCConnectionStateServerNotAvailable, 46 }; 47 48 static int 49 _validatePathFromSecCode(SecCodeRef processRef, const char *path) 50 { 51 int ret = -1; 52 OSStatus status; 53 SecStaticCodeRef staticProcessRef = NULL; 54 CFURLRef pathURL = NULL; 55 CFStringRef pathString = NULL; 56 57 /* Get the StaticCodeRef for this SecCodeRef */ 58 status = SecCodeCopyStaticCode(processRef, kSecCSDefaultFlags, &staticProcessRef); 59 require_noerr_action(status, exit, ret = -1); 60 61 INFO("Successfully created a SecStaticCodeRef"); 62 63 /* Copy the path of requested service */ 64 status = SecCodeCopyPath(staticProcessRef, kSecCSDefaultFlags, &pathURL); 65 require_noerr_action(status, exit, ret = -1); 66 67 INFO("Successfully created a CFURLRef"); 68 69 /* Get the CFStringRef from the CFURLRef */ 70 pathString = CFURLGetString(pathURL); 71 require_action(pathString, exit, ret = -1); 72 73 INFO("Successfully created a CFStingRef"); 74 75 if (!strncmp(path, CFStringGetCStringPtr(pathString, kCFStringEncodingUTF8), strlen(path))) { 76 INFO("Successfully confirmed the location of requested service"); 77 ret = 0; 78 } else { 79 INFO("Location of service incorrect: %s", CFStringGetCStringPtr(pathString, kCFStringEncodingUTF8)); 80 ret = -1; 81 } 82 83 exit: 84 SAFE_RELEASE(pathURL); 85 SAFE_RELEASE(staticProcessRef); 86 87 return ret; 88 } 89 90 static int 91 CheckCreateWithXPCMessage(void) 92 { 93 BEGIN(); 94 95 int ret; 96 OSStatus status; 97 xpc_connection_t connection = NULL; 98 xpc_object_t message = NULL, reply = NULL; 99 SecCodeRef processRef = NULL; 100 volatile static int xpcState = kXPCConnectionStateNotCancelled; 101 102 connection = xpc_connection_create(SECURITY_AUTH_NAME, NULL); 103 if (NULL == connection) { 104 FAIL("Unable to create an XPC connection with %s", SECURITY_AUTH_NAME); 105 return -1; 106 } 107 108 INFO("XPC Connection with %s created", SECURITY_AUTH_NAME); 109 110 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { 111 if (xpc_get_type(event) == XPC_TYPE_ERROR && event == XPC_ERROR_CONNECTION_INVALID) { 112 if (OSAtomicCompareAndSwapInt(kXPCConnectionStateCancelled, kXPCConnectionStateOkayToExit, &xpcState)) { 113 INFO("XPC Connection Cancelled"); 114 } else { 115 xpcState = kXPCConnectionStateServerNotAvailable; 116 FAIL("Authorization server not available"); 117 } 118 } 119 }); 120 121 xpc_connection_resume(connection); 122 123 INFO("XPC Connection resumed"); 124 125 /* Create an empty dictionary */ 126 message = xpc_dictionary_create(NULL, NULL, 0); 127 128 /* 129 * Set _type to something invalid. This is done because authd will simply 130 * return an "invalid type" for this case, which means no state changes in 131 * the authd daemon. 132 */ 133 xpc_dictionary_set_uint64(message, AUTH_XPC_TYPE, AUTHORIZATION_COPY_RIGHT_PROPERTIES+512); 134 135 /* Send object and wait for response */ 136 reply = xpc_connection_send_message_with_reply_sync(connection, message); 137 xpc_release(message); 138 139 INFO("XPC Message received"); 140 141 /* Create a SecCode using the XPC Message */ 142 status = SecCodeCreateWithXPCMessage(reply, kSecCSDefaultFlags, &processRef); 143 if (status) { 144 FAIL("Unable to create a SecCodeRef from message reply [%d]", status); 145 xpc_release(reply); 146 return -1; 147 } 148 xpc_release(reply); 149 150 INFO("Successfully created a SecCodeRef"); 151 152 const char *authdLocation = "file:///System/Library/Frameworks/Security.framework/Versions/A/XPCServices/authd.xpc/"; 153 if (_validatePathFromSecCode(processRef, authdLocation)) { 154 FAIL("Unable to verify authd location"); 155 ret = -1; 156 } else { 157 PASS("authd location successfully verified"); 158 ret = 0; 159 } 160 161 SAFE_RELEASE(processRef); 162 163 // Potential race condition in getting an actual XPC_TYPE_ERROR vs getting 164 // a connection cancelled. We are okay with this since this is extremely unlikely... 165 if (OSAtomicCompareAndSwapInt(kXPCConnectionStateNotCancelled, kXPCConnectionStateCancelled, &xpcState)) { 166 xpc_connection_cancel(connection); 167 } 168 169 while (xpcState != kXPCConnectionStateOkayToExit) { 170 if (xpcState == kXPCConnectionStateServerNotAvailable) { 171 break; 172 } 173 usleep(1000 * 1); 174 } 175 176 return ret; 177 } 178 179 static int 180 CheckCreateWithXPCMessage_invalidXPCObject(void) 181 { 182 BEGIN(); 183 184 OSStatus status; 185 xpc_object_t invalidObject = NULL; 186 SecCodeRef processRef = NULL; 187 188 /* Create an NULL object */ 189 invalidObject = xpc_null_create(); 190 191 INFO("Created a NULL object"); 192 193 /* Try and acquire a SecCodeRef through the NULL object -- should fail with errSecCSInvalidObjectRef */ 194 status = SecCodeCreateWithXPCMessage(invalidObject, kSecCSDefaultFlags, &processRef); 195 if (status != errSecCSInvalidObjectRef) { 196 FAIL("Return code unexpected [%d]", status); 197 return -1; 198 } 199 200 PASS("Got expected return code"); 201 return 0; 202 } 203 204 static int 205 CheckCreateWithXPCMessage_NULLConnectionInObject(void) 206 { 207 BEGIN(); 208 209 OSStatus status; 210 xpc_object_t emptyDictionary = NULL; 211 SecCodeRef processRef = NULL; 212 213 /* Create an empty dictionary object */ 214 emptyDictionary = xpc_dictionary_create_empty(); 215 216 INFO("Created an empty dictionary object"); 217 218 /* Try and acquire a SecCodeRef through the empty dictionary -- should fail with errSecCSInvalidObjectRef */ 219 status = SecCodeCreateWithXPCMessage(emptyDictionary, kSecCSDefaultFlags, &processRef); 220 if (status != errSecCSInvalidObjectRef) { 221 FAIL("Return code unexpected [%d]", status); 222 return -1; 223 } 224 225 PASS("Got expected return code"); 226 return 0; 227 } 228 229 static int 230 CheckValidateWithNoNetwork(void) 231 { 232 BEGIN(); 233 234 OSStatus status; 235 SecCodeRef processRef = NULL; 236 237 status = SecCodeCopySelf(kSecCSDefaultFlags, &processRef); 238 if (status) { 239 FAIL("Return code unexpected [%d]", status); 240 return -1; 241 } 242 243 status = SecCodeCheckValidity(processRef, kSecCSNoNetworkAccess, NULL); 244 if (status == errSecCSInvalidFlags) { 245 FAIL("SecCodeCheckValidity did not accept kSecCSNoNetworkAccess"); 246 return -1; 247 } else if (status) { 248 FAIL("Return code unexpected [%d]", status); 249 return -1; 250 } 251 252 PASS("Flag was accepted for validation call."); 253 return 0; 254 } 255 256 int main(void) 257 { 258 fprintf(stdout, "[TEST] secseccodeapitest\n"); 259 260 int i; 261 int (*testList[])(void) = { 262 CheckCreateWithXPCMessage, 263 CheckCreateWithXPCMessage_invalidXPCObject, 264 CheckCreateWithXPCMessage_NULLConnectionInObject, 265 CheckValidateWithNoNetwork, 266 }; 267 const int numberOfTests = sizeof(testList) / sizeof(*testList); 268 int testResults[numberOfTests] = {0}; 269 270 for (i = 0; i < numberOfTests; i++) { 271 testResults[i] = testList[i](); 272 } 273 274 fprintf(stdout, "[SUMMARY]\n"); 275 for (i = 0; i < numberOfTests; i++) { 276 fprintf(stdout, "%d. %s\n", i+1, testResults[i] == 0 ? "Passed" : "Failed"); 277 } 278 279 return 0; 280 }