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  }