]> git.saurik.com Git - apple/security.git/blob - OSX/authd/server.c
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / authd / server.c
1 /* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */
2
3 #include "server.h"
4 #include "session.h"
5 #include "process.h"
6 #include "authtoken.h"
7 #include "authdb.h"
8 #include "rule.h"
9 #include "authutilities.h"
10 #include "crc.h"
11 #include "mechanism.h"
12 #include "agent.h"
13 #include "authitems.h"
14 #include "debugging.h"
15 #include "engine.h"
16 #include "connection.h"
17 #include "AuthorizationTags.h"
18 #include "PreloginUserDb.h"
19
20 #include <bsm/libbsm.h>
21 #include <Security/Authorization.h>
22 #include <Security/AuthorizationPriv.h>
23 #include <Security/AuthorizationTagsPriv.h>
24 #include <Security/AuthorizationPlugin.h>
25 #include <xpc/private.h>
26 #include <dispatch/dispatch.h>
27 #include <CoreFoundation/CoreFoundation.h>
28 #include <CoreFoundation/CFXPCBridge.h>
29 #include <IOKit/IOMessage.h>
30 #include <IOKit/pwr_mgt/IOPMLib.h>
31 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
32
33 #include <security_utilities/simulatecrash_assert.h>
34
35 AUTHD_DEFINE_LOG
36
37 #define MAX_PROCESS_RIGHTS 100
38
39 static CFMutableDictionaryRef gProcessMap = NULL;
40 static CFMutableDictionaryRef gSessionMap = NULL;
41 static CFMutableDictionaryRef gAuthTokenMap = NULL;
42 static authdb_t gDatabase = NULL;
43
44 static bool gXPCTransaction = false;
45
46 static dispatch_queue_t
47 get_server_dispatch_queue()
48 {
49 static dispatch_once_t onceToken;
50 static dispatch_queue_t server_queue = NULL;
51
52 dispatch_once(&onceToken, ^{
53 server_queue = dispatch_queue_create("com.apple.security.auth.server", DISPATCH_QUEUE_SERIAL);
54 check(server_queue != NULL);
55 });
56
57 return server_queue;
58 }
59
60 static Boolean _processEqualCallBack(const void *value1, const void *value2)
61 {
62 audit_info_s * info1 = (audit_info_s*)value1;
63 audit_info_s * info2 = (audit_info_s*)value2;
64 if (info1->pid == info2->pid) {
65 if (info1->tid == info2->tid) {
66 return true;
67 }
68 }
69 return false;
70 }
71
72 static CFHashCode _processHashCallBack(const void *value)
73 {
74 audit_info_s * info = (audit_info_s*)value;
75 uint64_t crc = crc64_init();
76 crc = crc64_update(crc, &info->pid, sizeof(info->pid));
77 crc = crc64_update(crc, &info->tid, sizeof(info->tid));
78 crc = crc64_final(crc);
79 return (CFHashCode)crc;
80 }
81
82 static const CFDictionaryKeyCallBacks kProcessMapKeyCallBacks = {
83 .version = 0,
84 .retain = NULL,
85 .release = NULL,
86 .copyDescription = NULL,
87 .equal = &_processEqualCallBack,
88 .hash = &_processHashCallBack
89 };
90
91 static Boolean _sessionEqualCallBack(const void *value1, const void *value2)
92 {
93 return (*(session_id_t*)value1) == (*(session_id_t*)value2);
94 }
95
96 static CFHashCode _sessionHashCallBack(const void *value)
97 {
98 return (CFHashCode)(*(session_id_t*)(value));
99 }
100
101 static const CFDictionaryKeyCallBacks kSessionMapKeyCallBacks = {
102 .version = 0,
103 .retain = NULL,
104 .release = NULL,
105 .copyDescription = NULL,
106 .equal = &_sessionEqualCallBack,
107 .hash = &_sessionHashCallBack
108 };
109
110 void server_cleanup()
111 {
112 CFRelease(gProcessMap);
113 CFRelease(gSessionMap);
114 CFRelease(gAuthTokenMap);
115
116 dispatch_queue_t queue = get_server_dispatch_queue();
117 if (queue) {
118 dispatch_release(queue);
119 }
120 }
121
122 bool server_in_dark_wake()
123 {
124 return IOPMIsADarkWake(IOPMConnectionGetSystemCapabilities());
125 }
126
127 authdb_t server_get_database()
128 {
129 return gDatabase;
130 }
131
132 static void _setupAuditSessionMonitor()
133 {
134 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
135 au_sdev_handle_t *dev = au_sdev_open(AU_SDEVF_ALLSESSIONS);
136 int event;
137 auditinfo_addr_t aia;
138
139 if (NULL == dev) {
140 os_log_error(AUTHD_LOG, "server: could not open %{public}s %d", AUDIT_SDEV_PATH, errno);
141 return;
142 }
143
144 for (;;) {
145 if (0 != au_sdev_read_aia(dev, &event, &aia)) {
146 os_log_error(AUTHD_LOG, "server: au_sdev_read_aia failed: %d", errno);
147 continue;
148 }
149 os_log_debug(AUTHD_LOG, "server: au_sdev_handle_t event=%i, session=%i", event, aia.ai_asid);
150 if (event == AUE_SESSION_END) {
151 dispatch_async(get_server_dispatch_queue(), ^{
152 os_log_debug(AUTHD_LOG, "server: session %i destroyed", aia.ai_asid);
153 CFDictionaryRemoveValue(gSessionMap, &aia.ai_asid);
154 });
155 }
156 }
157
158 });
159 }
160
161 static void _setupSignalHandlers()
162 {
163 signal(SIGTERM, SIG_IGN);
164 static dispatch_source_t sigtermHandler;
165 sigtermHandler = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, get_server_dispatch_queue());
166 if (sigtermHandler) {
167 dispatch_source_set_event_handler(sigtermHandler, ^{
168
169 // should we clean up any state?
170 exit(EXIT_SUCCESS);
171 });
172 dispatch_resume(sigtermHandler);
173 }
174 }
175
176 OSStatus server_init(void)
177 {
178 OSStatus status = errAuthorizationSuccess;
179
180 auditinfo_addr_t info;
181 memset(&info, 0, sizeof(info));
182 getaudit_addr(&info, sizeof(info));
183 os_log_debug(AUTHD_LOG, "server: uid=%i, sid=%i", info.ai_auid, info.ai_asid);
184
185 require_action(get_server_dispatch_queue() != NULL, done, status = errAuthorizationInternal);
186
187 gProcessMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kProcessMapKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
188 require_action(gProcessMap != NULL, done, status = errAuthorizationInternal);
189
190 gSessionMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kSessionMapKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
191 require_action(gSessionMap != NULL, done, status = errAuthorizationInternal);
192
193 gAuthTokenMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kAuthTokenKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
194 require_action(gAuthTokenMap != NULL, done, status = errAuthorizationInternal);
195
196 gDatabase = authdb_create();
197 require_action(gDatabase != NULL, done, status = errAuthorizationInternal);
198
199 // check to see if we have an updates
200 authdb_connection_t dbconn = authdb_connection_acquire(gDatabase);
201 authdb_maintenance(dbconn);
202 authdb_connection_release(&dbconn);
203
204 _setupAuditSessionMonitor();
205 _setupSignalHandlers();
206
207 done:
208 return status;
209 }
210
211 static void _server_parse_audit_token(audit_token_t * token, audit_info_s * info)
212 {
213 if (token && info) {
214 memset(info, 0, sizeof(*info));
215 au_tid_t tid;
216 memset(&tid, 0, sizeof(tid));
217 audit_token_to_au32(*token, &info->auid, &info->euid,
218 &info->egid, &info->ruid, &info->rgid,
219 &info->pid, &info->asid, &tid);
220 info->tid = tid.port;
221 info->opaqueToken = *token;
222 }
223 }
224
225 connection_t
226 server_register_connection(xpc_connection_t connection)
227 {
228 __block connection_t conn = NULL;
229 __block session_t session = NULL;
230 __block process_t proc = NULL;
231 __block CFIndex conn_count = 0;
232
233 require(connection != NULL, done);
234
235 audit_token_t auditToken;
236 audit_info_s info;
237 xpc_connection_get_audit_token(connection, &auditToken);
238 _server_parse_audit_token(&auditToken, &info);
239
240
241 dispatch_sync(get_server_dispatch_queue(), ^{
242 session = (session_t)CFDictionaryGetValue(gSessionMap, &info.asid);
243 if (session) {
244 CFRetain(session);
245 } else {
246 session = session_create(info.asid);
247 CFDictionarySetValue(gSessionMap, session_get_key(session), session);
248 }
249
250 proc = (process_t)CFDictionaryGetValue(gProcessMap, &info);
251 if (proc) {
252 CFRetain(proc);
253 }
254
255 if (proc) {
256 conn = connection_create(proc);
257 conn_count = process_add_connection(proc, conn);
258 } else {
259 proc = process_create(&info, session);
260 if (proc) {
261 conn = connection_create(proc);
262 conn_count = process_add_connection(proc, conn);
263 session_add_process(session, proc);
264 CFDictionarySetValue(gProcessMap, process_get_key(proc), proc);
265 }
266 }
267
268 if (!gXPCTransaction) {
269 xpc_transaction_begin();
270 gXPCTransaction = true;
271 }
272 });
273
274 os_log_debug(AUTHD_LOG, "server: registered connection (total=%li)", conn_count);
275
276 done:
277 CFReleaseSafe(session);
278 CFReleaseSafe(proc);
279 return conn;
280 }
281
282 void
283 server_unregister_connection(connection_t conn)
284 {
285 assert(conn); // marked non-null
286 process_t proc = connection_get_process(conn);
287
288 dispatch_sync(get_server_dispatch_queue(), ^{
289 CFIndex connectionCount = process_get_connection_count(proc);
290 os_log_debug(AUTHD_LOG, "server: unregistered connection (total=%li)", connectionCount);
291
292 if (connectionCount == 1) {
293 CFDictionaryRemoveValue(gProcessMap, process_get_key(proc));
294 }
295
296 if (CFDictionaryGetCount(gProcessMap) == 0) {
297 xpc_transaction_end();
298 gXPCTransaction = false;
299 }
300 });
301 // move the destruction of the connection/process off the server queue
302 CFRelease(conn);
303 }
304
305 void
306 server_register_auth_token(auth_token_t auth)
307 {
308 assert(auth); // marked non-null
309 dispatch_sync(get_server_dispatch_queue(), ^{
310 os_log_debug(AUTHD_LOG, "server: registering authorization");
311 CFDictionarySetValue(gAuthTokenMap, auth_token_get_key(auth), auth);
312 auth_token_set_state(auth, auth_token_state_registered);
313 });
314 }
315
316 void
317 server_unregister_auth_token(auth_token_t auth)
318 {
319 assert(auth);
320 AuthorizationBlob blob = *(AuthorizationBlob*)auth_token_get_key(auth);
321 dispatch_async(get_server_dispatch_queue(), ^{
322 os_log_debug(AUTHD_LOG, "server: unregistering authorization");
323 CFDictionaryRemoveValue(gAuthTokenMap, &blob);
324 });
325 }
326
327 auth_token_t
328 server_find_copy_auth_token(AuthorizationBlob * blob)
329 {
330 assert(blob); // marked non-null
331 __block auth_token_t auth = NULL;
332 dispatch_sync(get_server_dispatch_queue(), ^{
333 auth = (auth_token_t)CFDictionaryGetValue(gAuthTokenMap, blob);
334 if (auth) {
335 CFRetain(auth);
336 }
337 });
338 return auth;
339 }
340
341 session_t
342 server_find_copy_session(session_id_t sid, bool create)
343 {
344 __block session_t session = NULL;
345
346 dispatch_sync(get_server_dispatch_queue(), ^{
347 session = (session_t)CFDictionaryGetValue(gSessionMap, &sid);
348 if (session) {
349 CFRetain(session);
350 } else if (create) {
351 session = session_create(sid);
352 if (session) {
353 CFDictionarySetValue(gSessionMap, session_get_key(session), session);
354 }
355 }
356 });
357
358 return session;
359 }
360
361 #pragma mark -
362 #pragma mark API
363
364 static OSStatus
365 _process_find_copy_auth_token_from_xpc(process_t proc, xpc_object_t message, auth_token_t * auth_out)
366 {
367 OSStatus status = errAuthorizationSuccess;
368 require_action(auth_out != NULL, done, status = errAuthorizationInternal);
369
370 size_t len;
371 AuthorizationBlob * blob = (AuthorizationBlob *)xpc_dictionary_get_data(message, AUTH_XPC_BLOB, &len);
372 require_action(blob != NULL, done, status = errAuthorizationInvalidRef);
373 require_action(len == sizeof(AuthorizationBlob), done, status = errAuthorizationInvalidRef);
374
375 auth_token_t auth = process_find_copy_auth_token(proc, blob);
376 require_action(auth != NULL, done, status = errAuthorizationInvalidRef);
377
378 #if DEBUG
379 os_log_debug(AUTHD_LOG, "server: authtoken lookup %#x%x %p", blob->data[1],blob->data[0], auth);
380 #else
381 os_log_debug(AUTHD_LOG, "server: authtoken lookup");
382 #endif
383
384 *auth_out = auth;
385
386 done:
387 return status;
388 }
389
390 static OSStatus _server_get_right_properties(connection_t conn, const char *rightName, CFDictionaryRef *properties)
391 {
392 OSStatus status = errAuthorizationDenied;
393 auth_token_t auth = NULL;
394 engine_t engine = NULL;
395
396 require_action(conn, done, status = errAuthorizationInternal);
397
398 auth = auth_token_create(connection_get_process(conn), false);
399 require_action(auth, done, status = errAuthorizationInternal);
400
401 engine = engine_create(conn, auth);
402 require_action(engine, done, status = errAuthorizationInternal);
403
404 status = engine_get_right_properties(engine, rightName, properties);
405
406 done:
407 CFReleaseSafe(engine);
408 CFReleaseSafe(auth);
409 return status;
410 }
411
412 static OSStatus _server_authorize(connection_t conn, auth_token_t auth, AuthorizationFlags flags, auth_rights_t rights, auth_items_t environment, engine_t * engine_out)
413 {
414 __block OSStatus status = errAuthorizationDenied;
415 engine_t engine = NULL;
416
417 require_action(conn, done, status = errAuthorizationInternal);
418
419 engine = engine_create(conn, auth);
420 require_action(engine, done, status = errAuthorizationInternal);
421
422 if (flags & kAuthorizationFlagInteractionAllowed) {
423 dispatch_sync(connection_get_dispatch_queue(conn), ^{
424 connection_set_engine(conn, engine);
425 status = engine_authorize(engine, rights, environment, flags);
426 connection_set_engine(conn, NULL);
427 });
428 } else {
429 status = engine_authorize(engine, rights, environment, flags);
430 }
431
432 done:
433 if (engine) {
434 if (engine_out) {
435 *engine_out = engine;
436 } else {
437 CFRelease(engine);
438 }
439 }
440 return status;
441 }
442
443 // IN: AUTH_XPC_RIGHTS, AUTH_XPC_ENVIRONMENT, AUTH_XPC_FLAGS
444 // OUT: AUTH_XPC_BLOB
445 OSStatus
446 authorization_create(connection_t conn, xpc_object_t message, xpc_object_t reply)
447 {
448 OSStatus status = errAuthorizationDenied;
449
450 process_t proc = connection_get_process(conn);
451
452 // Passed in args
453 auth_rights_t rights = auth_rights_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_RIGHTS));
454 auth_items_t environment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIRONMENT));
455 AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
456
457 // Create Authorization Token
458 auth_token_t auth = auth_token_create(proc, flags & kAuthorizationFlagLeastPrivileged);
459 require_action(auth != NULL, done, status = errAuthorizationInternal);
460
461 if (!(flags & kAuthorizationFlagNoData)) {
462 process_add_auth_token(proc,auth);
463 }
464
465 status = _server_authorize(conn, auth, flags, rights, environment, NULL);
466 require_noerr(status, done);
467
468 //reply
469 xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob));
470
471 done:
472 CFReleaseSafe(rights);
473 CFReleaseSafe(environment);
474 CFReleaseSafe(auth);
475 return status;
476 }
477
478 // IN: AUTH_XPC_DATA, AUTH_XPC_ENVIRONMENT, AUTH_XPC_FLAGS
479 // OUT: AUTH_XPC_BLOB
480 OSStatus authorization_create_with_audit_token(connection_t conn, xpc_object_t message, xpc_object_t reply)
481 {
482 OSStatus status = errAuthorizationDenied;
483 auth_token_t auth = NULL;
484
485 process_t proc = connection_get_process(conn);
486 require(process_get_uid(proc) == 0, done); //only root can use this call
487
488 // Passed in args
489 size_t len = 0;
490 const char * data = xpc_dictionary_get_data(message, AUTH_XPC_DATA, &len);
491 require(data != NULL, done);
492 require(len == sizeof(audit_token_t), done);
493
494 // auth_items_t environment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIRONMENT));
495 AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
496
497 audit_info_s auditInfo;
498 _server_parse_audit_token((audit_token_t*)data, &auditInfo);
499
500 // Create Authorization Token
501 auth = auth_token_create(proc, flags & kAuthorizationFlagLeastPrivileged);
502 require_action(auth != NULL, done, status = errAuthorizationInternal);
503
504 process_add_auth_token(proc,auth);
505
506 //reply
507 xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob));
508
509 done:
510 // CFReleaseSafe(environment);
511 CFReleaseSafe(auth);
512 return status;
513 }
514
515 // IN: AUTH_XPC_BLOB, AUTH_XPC_FLAGS
516 // OUT:
517 OSStatus
518 authorization_free(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
519 {
520 OSStatus status = errAuthorizationSuccess;
521 AuthorizationFlags flags = 0;
522 process_t proc = connection_get_process(conn);
523
524 auth_token_t auth = NULL;
525 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
526 require_noerr(status, done);
527
528 flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
529
530 if (flags & kAuthorizationFlagDestroyRights) {
531 auth_token_credentials_iterate(auth, ^bool(credential_t cred) {
532 credential_invalidate(cred);
533 os_log_debug(AUTHD_LOG, "engine[%i]: invalidating %{public}scredential %{public}s (%i)", connection_get_pid(conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred));
534 return true;
535 });
536
537 session_credentials_purge(auth_token_get_session(auth));
538 }
539
540 process_remove_auth_token(proc, auth, flags);
541
542 done:
543 CFReleaseSafe(auth);
544 os_log_debug(AUTHD_LOG, "server: AuthorizationFree %d (flags:%x)", (int)status, (unsigned int)flags);
545 return status;
546 }
547
548 // IN: AUTH_XPC_BLOB, AUTH_XPC_DATA
549 // OUT:
550 OSStatus
551 authorization_copy_right_properties(connection_t conn, xpc_object_t message, xpc_object_t reply)
552 {
553 OSStatus status = errAuthorizationDenied;
554 CFDataRef serializedProperties = NULL;
555 CFDictionaryRef properties = NULL;
556
557 // Passed in args
558 const char *right = xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME);
559 os_log_debug(AUTHD_LOG, "server: right %s", right);
560
561 require_action(right != NULL, done, status = errAuthorizationInvalidPointer);
562
563 status = _server_get_right_properties(conn, right, &properties);
564 require_noerr(status, done);
565
566 if (properties) {
567 serializedProperties = CFPropertyListCreateData(kCFAllocatorDefault, properties, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
568 if (serializedProperties) {
569 xpc_dictionary_set_data(reply, AUTH_XPC_OUT_ITEMS, CFDataGetBytePtr(serializedProperties), CFDataGetLength(serializedProperties));
570 }
571 }
572
573 done:
574 CFReleaseSafe(serializedProperties);
575 CFReleaseSafe(properties);
576 return status;
577 }
578
579
580 // IN: AUTH_XPC_BLOB, AUTH_XPC_RIGHTS, AUTH_XPC_ENVIRONMENT, AUTH_XPC_FLAGS
581 // OUT: AUTH_XPC_OUT_ITEMS
582 OSStatus
583 authorization_copy_rights(connection_t conn, xpc_object_t message, xpc_object_t reply)
584 {
585 OSStatus status = errAuthorizationDenied;
586 engine_t engine = NULL;
587
588 process_t proc = connection_get_process(conn);
589
590 // Passed in args
591 auth_rights_t rights = auth_rights_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_RIGHTS));
592 auth_items_t environment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIRONMENT));
593 AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
594
595 auth_token_t auth = NULL;
596 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
597 require_noerr_action_quiet(status, done, os_log_error(AUTHD_LOG, "copy_rights: no auth token"));
598
599 status = _server_authorize(conn, auth, flags, rights, environment, &engine);
600 require_noerr_action_quiet(status, done, os_log_error(AUTHD_LOG, "copy_rights: authorization failed"));
601
602 //reply
603 xpc_object_t outItems = auth_rights_export_xpc(engine_get_granted_rights(engine));
604 xpc_dictionary_set_value(reply, AUTH_XPC_OUT_ITEMS, outItems);
605 xpc_release_safe(outItems);
606
607 done:
608 CFReleaseSafe(rights);
609 CFReleaseSafe(environment);
610 CFReleaseSafe(auth);
611 CFReleaseSafe(engine);
612
613 return status;
614 }
615
616 // IN: AUTH_XPC_BLOB, AUTH_XPC_TAG
617 // OUT: AUTH_XPC_OUT_ITEMS
618 OSStatus
619 authorization_copy_info(connection_t conn, xpc_object_t message, xpc_object_t reply)
620 {
621
622 OSStatus status = errAuthorizationSuccess;
623 auth_items_t items = NULL;
624 auth_items_t local_items = NULL;
625 const char * tag = NULL;
626
627 process_t proc = connection_get_process(conn);
628
629 auth_token_t auth = NULL;
630 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
631 require_noerr_action_quiet(status, done, os_log_error(AUTHD_LOG, "copy_info: no auth token"));
632
633 items = auth_items_create();
634
635 tag = xpc_dictionary_get_string(message, AUTH_XPC_TAG);
636 os_log_debug(AUTHD_LOG, "server: requested tag: %{public}s", tag ? tag : "(all)");
637 if (tag) {
638 size_t len;
639 const void * data = auth_items_get_data_with_flags(auth_token_get_context(auth), tag, &len, kAuthorizationContextFlagExtractable);
640 if (data) {
641 os_log_debug(AUTHD_LOG, "server: requested tag found");
642 auth_items_set_data(items, tag, data, len);
643 }
644 } else {
645 auth_items_copy_with_flags(items, auth_token_get_context(auth), kAuthorizationContextFlagExtractable);
646 }
647
648 local_items = auth_items_create();
649 auth_items_content_copy(local_items, items); // we do not want decrypt content of the authorizationref memory which is where pointers point to
650 auth_items_decrypt(local_items, auth_token_get_encryption_key(auth));
651 os_log_debug(AUTHD_LOG, "server: decrypted authorization context data");
652
653 #if DEBUG
654 os_log_debug(AUTHD_LOG, "server: Dumping requested AuthRef items: %{public}@", items);
655 #endif
656
657 if (auth_items_exist(local_items, kAuthorizationEnvironmentPassword)) {
658 // check if caller is entitled to get the password
659 CFTypeRef extract_password_entitlement = process_copy_entitlement_value(proc, "com.apple.authorization.extract-password");
660 if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) {
661 os_log_debug(AUTHD_LOG, "server: caller allowed to extract password");
662 } else {
663 os_log_error(AUTHD_LOG, "server: caller NOT allowed to extract password");
664 auth_items_remove(local_items, kAuthorizationEnvironmentPassword);
665 }
666 CFReleaseSafe(extract_password_entitlement);
667 }
668
669 //reply
670 xpc_object_t outItems = auth_items_export_xpc(local_items);
671 xpc_dictionary_set_value(reply, AUTH_XPC_OUT_ITEMS, outItems);
672 xpc_release_safe(outItems);
673
674 done:
675 CFReleaseSafe(local_items);
676 CFReleaseSafe(items);
677 CFReleaseSafe(auth);
678 os_log_debug(AUTHD_LOG, "server: AuthorizationCopyInfo %i", (int) status);
679 return status;
680 }
681
682 // IN: AUTH_XPC_BLOB
683 // OUT: AUTH_XPC_EXTERNAL
684 OSStatus
685 authorization_make_external_form(connection_t conn, xpc_object_t message, xpc_object_t reply)
686 {
687 OSStatus status = errAuthorizationSuccess;
688
689 process_t proc = connection_get_process(conn);
690
691 auth_token_t auth = NULL;
692 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
693 require_noerr(status, done);
694
695 AuthorizationExternalForm exForm;
696 AuthorizationExternalBlob * exBlob = (AuthorizationExternalBlob *)&exForm;
697 memset(&exForm, 0, sizeof(exForm));
698
699 exBlob->blob = *auth_token_get_blob(auth);
700 exBlob->session = process_get_session_id(proc);
701
702 xpc_dictionary_set_data(reply, AUTH_XPC_EXTERNAL, &exForm, sizeof(exForm));
703 server_register_auth_token(auth);
704
705 done:
706 CFReleaseSafe(auth);
707 os_log_debug(AUTHD_LOG, "server: AuthorizationMakeExternalForm %d", (int)status);
708 return status;
709 }
710
711 // IN: AUTH_XPC_EXTERNAL
712 // OUT: AUTH_XPC_BLOB
713 OSStatus
714 authorization_create_from_external_form(connection_t conn, xpc_object_t message, xpc_object_t reply)
715 {
716 OSStatus status = errAuthorizationSuccess;
717 auth_token_t auth = NULL;
718
719 process_t proc = connection_get_process(conn);
720
721 size_t len;
722 AuthorizationExternalForm * exForm = (AuthorizationExternalForm *)xpc_dictionary_get_data(message, AUTH_XPC_EXTERNAL, &len);
723 require_action(exForm != NULL, done, status = errAuthorizationInternal);
724 require_action(len == sizeof(AuthorizationExternalForm), done, status = errAuthorizationInvalidRef);
725
726 AuthorizationExternalBlob * exBlob = (AuthorizationExternalBlob *)exForm;
727 auth = server_find_copy_auth_token(&exBlob->blob);
728 require_action(auth != NULL, done, status = errAuthorizationDenied);
729
730 process_add_auth_token(proc, auth);
731 xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob));
732
733 done:
734 CFReleaseSafe(auth);
735 os_log_debug(AUTHD_LOG, "server: AuthorizationCreateFromExternalForm %d", (int)status);
736 return status;
737 }
738
739 // IN: AUTH_XPC_RIGHT_NAME
740 // OUT: AUTH_XPC_DATA
741 OSStatus
742 authorization_right_get(connection_t conn AUTH_UNUSED, xpc_object_t message, xpc_object_t reply)
743 {
744 OSStatus status = errAuthorizationDenied;
745 rule_t rule = NULL;
746 CFTypeRef cfdict = NULL;
747 xpc_object_t xpcdict = NULL;
748
749 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database());
750 rule = rule_create_with_string(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME), dbconn);
751 require(rule != NULL, done);
752 require(rule_get_id(rule) != 0, done);
753
754 cfdict = rule_copy_to_cfobject(rule, dbconn);
755 require(cfdict != NULL, done);
756
757 xpcdict = _CFXPCCreateXPCObjectFromCFObject(cfdict);
758 require(xpcdict != NULL, done);
759
760 // reply
761 xpc_dictionary_set_value(reply, AUTH_XPC_DATA, xpcdict);
762
763 status = errAuthorizationSuccess;
764
765 done:
766 authdb_connection_release(&dbconn);
767 CFReleaseSafe(cfdict);
768 xpc_release_safe(xpcdict);
769 CFReleaseSafe(rule);
770 os_log_debug(AUTHD_LOG, "server: AuthorizationRightGet %d", (int)status);
771 return status;
772 }
773
774 static bool _prompt_for_modifications(process_t __unused proc, rule_t __unused rule)
775 {
776 // <rdar://problem/13853228> will put back it back at some later date
777 // SecRequirementRef ruleReq = rule_get_requirement(rule);
778 //
779 // if (ruleReq && process_verify_requirment(proc, ruleReq)) {
780 // return false;
781 // }
782
783 return true;
784 }
785
786 static CFIndex _get_mechanism_index(CFArrayRef mechanisms, CFStringRef m_name)
787 {
788 CFIndex index = -1;
789 require(mechanisms, done);
790
791 CFIndex c = CFArrayGetCount(mechanisms);
792 CFStringRef i_name = NULL;
793 for (CFIndex i = 0; i < c; ++i)
794 {
795 i_name = CFArrayGetValueAtIndex(mechanisms, i);
796 if (i_name && (CFGetTypeID(m_name) == CFStringGetTypeID())) {
797 if (CFStringCompare(i_name, m_name, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
798 index = i;
799 break;
800 }
801 }
802 }
803
804 done:
805 return index;
806 }
807
808 static bool _update_rule_mechanism(authdb_connection_t dbconn, const char * rule_name, CFStringRef mechanism_name, CFStringRef insert_after_name, bool remove)
809 {
810 bool updated = false;
811 rule_t rule = NULL;
812 rule_t update_rule = NULL;
813 CFMutableDictionaryRef cfdict = NULL;
814 CFStringRef update_name = NULL;
815
816 require(mechanism_name, done);
817
818 rule = rule_create_with_string(rule_name, dbconn);
819 require(rule_get_id(rule) != 0, done); // rule doesn't exist in the database
820
821 cfdict = rule_copy_to_cfobject(rule, dbconn);
822 require(cfdict != NULL, done);
823
824 CFMutableArrayRef mechanisms = NULL;
825 bool res = CFDictionaryGetValueIfPresent(cfdict, CFSTR(kAuthorizationRuleParameterMechanisms), (void*)&mechanisms);
826 require(res == true, done);
827
828 CFIndex index = -1;
829
830 if (remove) {
831 index = _get_mechanism_index(mechanisms, mechanism_name);
832 } else {
833 if (insert_after_name) {
834 if ((index = _get_mechanism_index(mechanisms, insert_after_name)) != -1) {
835 index++;
836 } else {
837 index = 0; // if we couldn't find the index add it to the begining
838 }
839 } else {
840 index = 0;
841 }
842 }
843
844 if (index != -1) {
845 if(remove) {
846 CFArrayRemoveValueAtIndex(mechanisms, index);
847 } else {
848 if (index < CFArrayGetCount(mechanisms)) {
849 require_action(CFStringCompare(CFArrayGetValueAtIndex(mechanisms, index), mechanism_name, kCFCompareCaseInsensitive) != kCFCompareEqualTo, done, updated = true);
850 }
851 CFArrayInsertValueAtIndex(mechanisms, index, mechanism_name);
852 }
853
854 CFDictionarySetValue(cfdict, CFSTR(kAuthorizationRuleParameterMechanisms), mechanisms);
855
856 // and write it back
857 update_name = CFStringCreateWithCString(kCFAllocatorDefault, rule_name, kCFStringEncodingUTF8);
858 require(update_name, done);
859 update_rule = rule_create_with_plist(rule_get_type(rule), update_name, cfdict, dbconn);
860 require(update_rule, done);
861
862 require(rule_sql_commit(update_rule, dbconn, CFAbsoluteTimeGetCurrent(), NULL), done);
863 }
864
865 updated = true;
866
867 done:
868 CFReleaseSafe(rule);
869 CFReleaseSafe(update_rule);
870 CFReleaseSafe(cfdict);
871 CFReleaseSafe(update_name);
872 return updated;
873 }
874
875 /// IN: AUTH_XPC_BLOB, AUTH_XPC_INT64
876 // OUT:
877 OSStatus
878 authorization_enable_smartcard(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
879 {
880 const CFStringRef SMARTCARD_LINE = CFSTR("builtin:smartcard-sniffer,privileged");
881 const CFStringRef BUILTIN_LINE = CFSTR("builtin:policy-banner");
882 const char* SYSTEM_LOGIN_CONSOLE = "system.login.console";
883 const char* AUTHENTICATE = "authenticate";
884
885 __block OSStatus status = errAuthorizationSuccess;
886 bool enable_smartcard = false;
887 authdb_connection_t dbconn = NULL;
888 auth_token_t auth = NULL;
889 auth_rights_t checkRight = NULL;
890
891 process_t proc = connection_get_process(conn);
892
893 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
894 require_noerr(status, done);
895
896 checkRight = auth_rights_create();
897 auth_rights_add(checkRight, "config.modify.smartcard");
898 status = _server_authorize(conn, auth, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, checkRight, NULL, NULL);
899 require_noerr(status, done);
900
901 enable_smartcard = xpc_dictionary_get_bool(message, AUTH_XPC_DATA);
902
903 dbconn = authdb_connection_acquire(server_get_database());
904
905 if (!_update_rule_mechanism(dbconn, SYSTEM_LOGIN_CONSOLE, SMARTCARD_LINE, BUILTIN_LINE, enable_smartcard ? false : true)) {
906 status = errAuthorizationInternal;
907 os_log_error(AUTHD_LOG, "server: smartcard: enable(%i) failed to update %{public}s", enable_smartcard, SYSTEM_LOGIN_CONSOLE);
908 }
909 if (!_update_rule_mechanism(dbconn, AUTHENTICATE, SMARTCARD_LINE, NULL, enable_smartcard ? false : true)) {
910 status = errAuthorizationInternal;
911 os_log_error(AUTHD_LOG, "server: smartcard: enable(%i) failed to update %{public}s", enable_smartcard, AUTHENTICATE);
912 }
913
914 authdb_checkpoint(dbconn);
915
916 done:
917 authdb_connection_release(&dbconn);
918 CFReleaseSafe(checkRight);
919 CFReleaseSafe(auth);
920 return status;
921 }
922
923 static int64_t _process_get_identifier_count(process_t proc, authdb_connection_t conn)
924 {
925 __block int64_t result = 0;
926
927 authdb_step(conn, "SELECT COUNT(*) AS cnt FROM rules WHERE identifier = ? ", ^(sqlite3_stmt *stmt) {
928 sqlite3_bind_text(stmt, 1, process_get_identifier(proc), -1, NULL);
929 }, ^bool(auth_items_t data) {
930 result = auth_items_get_int64(data, "cnt");
931 return true;
932 });
933
934 return result;
935 }
936
937 static int64_t _get_max_process_rights()
938 {
939 static dispatch_once_t onceToken;
940 static int64_t max_rights = MAX_PROCESS_RIGHTS;
941
942 //sudo defaults write /Library/Preferences/com.apple.authd max_process_rights -bool true
943 dispatch_once(&onceToken, ^{
944 CFTypeRef max = (CFNumberRef)CFPreferencesCopyValue(CFSTR("max_process_rights"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
945
946 if (max && CFGetTypeID(max) == CFNumberGetTypeID()) {
947 CFNumberGetValue(max, kCFNumberSInt64Type, &max_rights);
948 }
949 CFReleaseSafe(max);
950 });
951
952 return max_rights;
953 }
954
955 // IN: AUTH_XPC_BLOB, AUTH_XPC_RIGHT_NAME, AUTH_XPC_DATA
956 // OUT:
957 OSStatus
958 authorization_right_set(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
959 {
960 __block OSStatus status = errAuthorizationDenied;
961 __block engine_t engine = NULL;
962 CFStringRef cf_rule_name = NULL;
963 CFDictionaryRef cf_rule_dict = NULL;
964 rule_t rule = NULL;
965 rule_t existingRule = NULL;
966 authdb_connection_t dbconn = NULL;
967 auth_token_t auth = NULL;
968 bool force_modify = false;
969 RuleType rule_type = RT_RIGHT;
970 const char * rule_name = NULL;
971 bool auth_rule = false;
972
973 process_t proc = connection_get_process(conn);
974
975 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
976 require_noerr(status, done);
977
978 require_action(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME) != NULL, done, status = errAuthorizationInternal);
979 require_action(xpc_dictionary_get_value(message, AUTH_XPC_DATA) != NULL, done, status = errAuthorizationInternal);
980
981 rule_name = xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME);
982 require(rule_name != NULL, done);
983
984 if (_compare_string(rule_name, "authenticate")) {
985 rule_type = RT_RULE;
986 auth_rule = true;
987 }
988
989 cf_rule_name = CFStringCreateWithCString(kCFAllocatorDefault, rule_name, kCFStringEncodingUTF8);
990 require(cf_rule_name != NULL, done);
991
992 cf_rule_dict = _CFXPCCreateCFObjectFromXPCObject(xpc_dictionary_get_value(message, AUTH_XPC_DATA));
993 require(cf_rule_dict != NULL, done);
994
995 dbconn = authdb_connection_acquire(server_get_database());
996
997 rule = rule_create_with_plist(rule_type, cf_rule_name, cf_rule_dict, dbconn);
998 if (process_get_uid(proc) != 0) {
999 require_action(rule_get_extract_password(rule) == false, done, status = errAuthorizationDenied; os_log_error(AUTHD_LOG, "server: AuthorizationRightSet not allowed to set extract-password. (denied)"));
1000 }
1001
1002 // if rule doesn't currently exist then we have to check to see if they are over the Max.
1003 if (rule_get_id(rule) == 0) {
1004 if (process_get_identifier(proc) == NULL) {
1005 os_log_error(AUTHD_LOG, "server: AuthorizationRightSet required for process %{public}s (missing code signature). To add rights to the Authorization database, your process must have a code signature.", process_get_code_url(proc));
1006 force_modify = true;
1007 } else {
1008 int64_t process_rule_count = _process_get_identifier_count(proc, dbconn);
1009 if ((process_rule_count >= _get_max_process_rights())) {
1010 if (!connection_get_syslog_warn(conn)) {
1011 os_log_error(AUTHD_LOG, "server: AuthorizationRightSet Denied API abuse process %{public}s already contains %lli rights.", process_get_code_url(proc), _get_max_process_rights());
1012 connection_set_syslog_warn(conn);
1013 }
1014 status = errAuthorizationDenied;
1015 goto done;
1016 }
1017 }
1018 } else {
1019 if (auth_rule) {
1020 if (process_get_uid(proc) != 0) {
1021 os_log_error(AUTHD_LOG, "server: AuthorizationRightSet denied, root required to update the 'authenticate' rule");
1022 status = errAuthorizationDenied;
1023 goto done;
1024 }
1025 } else {
1026 // verify they are updating a right and not a rule
1027 existingRule = rule_create_with_string(rule_get_name(rule), dbconn);
1028 if (rule_get_type(existingRule) == RT_RULE) {
1029 os_log_error(AUTHD_LOG, "server: AuthorizationRightSet Denied updating '%{public}s' rule is prohibited", rule_get_name(existingRule));
1030 status = errAuthorizationDenied;
1031 goto done;
1032 }
1033 }
1034 }
1035
1036 if (_prompt_for_modifications(proc,rule)) {
1037 authdb_connection_release(&dbconn);
1038
1039 dispatch_sync(connection_get_dispatch_queue(conn), ^{
1040 engine = engine_create(conn, auth);
1041 connection_set_engine(conn, engine);
1042 status = engine_verify_modification(engine, rule, false, force_modify);
1043 connection_set_engine(conn, NULL);
1044 });
1045 require_noerr(status, done);
1046
1047 dbconn = authdb_connection_acquire(server_get_database());
1048 }
1049
1050 if (rule_sql_commit(rule, dbconn, engine ? engine_get_time(engine) : CFAbsoluteTimeGetCurrent(), proc)) {
1051 os_log_debug(AUTHD_LOG, "server: Successfully updated rule %{public}s", rule_get_name(rule));
1052 authdb_checkpoint(dbconn);
1053 status = errAuthorizationSuccess;
1054 } else {
1055 os_log_error(AUTHD_LOG, "server: Failed to update rule %{public}s", rule_get_name(rule));
1056 status = errAuthorizationDenied;
1057 }
1058
1059 done:
1060 authdb_connection_release(&dbconn);
1061 CFReleaseSafe(existingRule);
1062 CFReleaseSafe(cf_rule_name);
1063 CFReleaseSafe(cf_rule_dict);
1064 CFReleaseSafe(auth);
1065 CFReleaseSafe(rule);
1066 CFReleaseSafe(engine);
1067 return status;
1068 }
1069
1070 // IN: AUTH_XPC_BLOB, AUTH_XPC_RIGHT_NAME
1071 // OUT:
1072 OSStatus
1073 authorization_right_remove(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
1074 {
1075 __block OSStatus status = errAuthorizationDenied;
1076 __block engine_t engine = NULL;
1077 rule_t rule = NULL;
1078 authdb_connection_t dbconn = NULL;
1079
1080 process_t proc = connection_get_process(conn);
1081
1082 auth_token_t auth = NULL;
1083 status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
1084 require_noerr(status, done);
1085
1086 dbconn = authdb_connection_acquire(server_get_database());
1087
1088 rule = rule_create_with_string(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME), dbconn);
1089 require(rule != NULL, done);
1090
1091 if (_prompt_for_modifications(proc,rule)) {
1092 authdb_connection_release(&dbconn);
1093
1094 dispatch_sync(connection_get_dispatch_queue(conn), ^{
1095 engine = engine_create(conn, auth);
1096 connection_set_engine(conn, engine);
1097 status = engine_verify_modification(engine, rule, true, false);
1098 connection_set_engine(conn, NULL);
1099 });
1100 require_noerr(status, done);
1101
1102 dbconn = authdb_connection_acquire(server_get_database());
1103 }
1104
1105 if (rule_get_id(rule) != 0) {
1106 rule_sql_remove(rule, dbconn, proc);
1107 }
1108
1109 done:
1110 authdb_connection_release(&dbconn);
1111 CFReleaseSafe(auth);
1112 CFReleaseSafe(rule);
1113 CFReleaseSafe(engine);
1114 os_log_debug(AUTHD_LOG, "server: AuthorizationRightRemove %d", (int)status);
1115 return status;
1116 }
1117
1118 #pragma mark -
1119 #pragma mark test code
1120
1121 OSStatus
1122 session_set_user_preferences(connection_t conn, xpc_object_t message, xpc_object_t reply)
1123 {
1124 (void)conn;
1125 (void)message;
1126 (void)reply;
1127 return errAuthorizationSuccess;
1128 }
1129
1130 void
1131 server_dev() {
1132 // rule_t rule = rule_create_with_string("system.preferences.accounts");
1133 // CFDictionaryRef dict = rule_copy_to_cfobject(rule);
1134 // _show_cf(dict);
1135 // CFReleaseSafe(rule);
1136 // CFReleaseSafe(dict);
1137
1138 // auth_items_t config = NULL;
1139 // double d2 = 0, d1 = 5;
1140 // authdb_get_key_value(server_get_authdb_reader(), "config", &config);
1141 // auth_items_set_double(config, "test", d1);
1142 // d2 = auth_items_get_double(config, "test");
1143 // os_log_debug(AUTHD_LOG, "d1=%f d2=%f", d1, d2);
1144 // CFReleaseSafe(config);
1145
1146
1147 // auth_items_t items = auth_items_create();
1148 // auth_items_set_string(items, "test", "testing 1");
1149 // auth_items_set_string(items, "test2", "testing 2");
1150 // auth_items_set_string(items, "test3", "testing 3");
1151 // auth_items_set_flags(items, "test3", 4);
1152 // auth_items_set_string(items, "apple", "apple");
1153 // auth_items_set_flags(items, "apple", 1);
1154 // auth_items_set_int(items, "int", 45);
1155 // auth_items_set_flags(items, "int", 2);
1156 // auth_items_set_bool(items, "true", true);
1157 // auth_items_set_bool(items, "false", false);
1158 // auth_items_set(items, "com.apple.");
1159 // auth_show(items);
1160 // LOGD("Yeah it works: %s", auth_items_get_string(items, "test3"));
1161 // LOGD("Yeah it works: %i", auth_items_get_bool(items, "true"));
1162 // LOGD("Yeah it works: %i", auth_items_get_bool(items, "false"));
1163 // LOGD("Yeah it works: %i", auth_items_get_int(items, "int"));
1164 // (void)auth_items_get_bool(items, "test3");
1165 // AuthorizationItemSet * itemSet = auth_items_get_item_set(items);
1166 // for (uint32_t i = 0; i < itemSet->count; i++) {
1167 // LOGD("item: %s", itemSet->items[i].name);
1168 // }
1169 //
1170 // xpc_object_t xpcdata = SerializeItemSet(auth_items_get_item_set(items));
1171 // auth_items_t items2 = auth_items_create_with_xpc(xpcdata);
1172 // xpc_release(xpcdata);
1173 // auth_items_remove_with_flags(items2, 7);
1174 //// auth_items_set_string(items2, "test3", "testing 3 very good");
1175 // auth_items_copy_with_flags(items2, items, 7);
1176 // LOGD("Yeah it works: %s", auth_items_get_string(items2, "test3"));
1177 // auth_show(items2);
1178 // CFReleaseSafe(items2);
1179 //
1180 // CFReleaseSafe(items);
1181 }
1182
1183 // IN: AUTH_XPC_TAG, AUTH_XPC_FLAGS
1184 // OUT: AUTH_XPC_DATA
1185 OSStatus
1186 authorization_copy_prelogin_userdb(connection_t conn, xpc_object_t message, xpc_object_t reply)
1187 {
1188 OSStatus status = errAuthorizationDenied;
1189 xpc_object_t xpcarr = NULL;
1190 CFArrayRef cfarray = NULL;
1191
1192 const char *uuid = xpc_dictionary_get_string(message, AUTH_XPC_TAG);
1193 UInt32 flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
1194
1195 status = preloginudb_copy_userdb(uuid, flags, &cfarray);
1196 xpc_dictionary_set_int64(reply, AUTH_XPC_STATUS, status);
1197 require_noerr_action_quiet(status, done, os_log_error(AUTHD_LOG, "authorization_copy_prelogin_userdb: database failed"));
1198
1199 xpcarr = _CFXPCCreateXPCObjectFromCFObject(cfarray);
1200 require(xpcarr != NULL, done);
1201 xpc_dictionary_set_value(reply, AUTH_XPC_DATA, xpcarr);
1202
1203 done:
1204 CFReleaseSafe(cfarray);
1205 xpc_release_safe(xpcarr);
1206
1207 return status;
1208 }