]> git.saurik.com Git - apple/security.git/blob - OSX/authd/authitems.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / authd / authitems.c
1 /* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */
2
3 #include "authitems.h"
4 #include "crc.h"
5 #include "debugging.h"
6
7 #include "authutilities.h"
8 #include <Security/AuthorizationTags.h>
9 #include <Security/AuthorizationTagsPriv.h>
10 #include <dispatch/private.h>
11 #include <CommonCrypto/CommonCrypto.h>
12
13 #include <security_utilities/simulatecrash_assert.h>
14
15 AUTHD_DEFINE_LOG
16
17 typedef struct _auth_item_s * auth_item_t;
18 void auth_items_crypt_worker(auth_items_t items, CFDataRef encryption_key, bool(*function)(auth_item_t, CFDataRef));
19
20 #pragma mark -
21 #pragma mark auth_item_t
22
23 struct _auth_item_s {
24 __AUTH_BASE_STRUCT_HEADER__;
25
26 AuthorizationItem data;
27 uint32_t type;
28 size_t bufLen;
29 };
30
31 static const char *
32 auth_item_get_string(auth_item_t item)
33 {
34 if (item->bufLen <= item->data.valueLength) {
35 item->bufLen = item->data.valueLength+1; // make sure buffer has a null char
36 item->data.value = realloc(item->data.value, item->bufLen);
37 if (item->data.value == NULL) {
38 // this is added to prevent running off into random memory if a string buffer doesn't have a null char
39 os_log_error(AUTHD_LOG, "items: realloc failed");
40 abort();
41 }
42 ((uint8_t*)item->data.value)[item->bufLen-1] = '\0';
43 }
44 return item->data.value;
45 }
46
47 static xpc_object_t
48 auth_item_copy_auth_item_xpc(auth_item_t item)
49 {
50 xpc_object_t xpc_data = xpc_dictionary_create(NULL, NULL, 0);
51 xpc_dictionary_set_string(xpc_data, AUTH_XPC_ITEM_NAME, item->data.name);
52 if (item->data.value) {
53 // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear
54 bool sensitive = strcmp(item->data.name, AGENT_PASSWORD) == 0;
55 if (sensitive) {
56 vm_address_t vmBytes = 0;
57 size_t xpcOutOfBandBlockSize = (item->data.valueLength > 32768 ? item->data.valueLength : 32768); // min 16K on 64-bit systems and 12K on 32-bit systems
58 vm_allocate(mach_task_self(), &vmBytes, xpcOutOfBandBlockSize, VM_FLAGS_ANYWHERE);
59 memcpy((void *)vmBytes, item->data.value, item->data.valueLength);
60 dispatch_data_t dispData = dispatch_data_create((void *)vmBytes, xpcOutOfBandBlockSize, DISPATCH_TARGET_QUEUE_DEFAULT, DISPATCH_DATA_DESTRUCTOR_VM_DEALLOCATE); // out-of-band mapping
61 xpc_object_t xpcData = xpc_data_create_with_dispatch_data(dispData);
62 dispatch_release(dispData);
63 xpc_dictionary_set_value(xpc_data, AUTH_XPC_ITEM_VALUE, xpcData);
64 xpc_release(xpcData);
65 xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH, item->data.valueLength);
66 } else {
67 xpc_dictionary_set_data(xpc_data, AUTH_XPC_ITEM_VALUE, item->data.value, item->data.valueLength);
68 }
69 }
70 xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_FLAGS, item->data.flags);
71 xpc_dictionary_set_uint64(xpc_data, AUTH_XPC_ITEM_TYPE, item->type);
72 return xpc_data;
73 }
74
75 static void
76 _auth_item_finalize(CFTypeRef value)
77 {
78 auth_item_t item = (auth_item_t)value;
79
80 if (item->data.name) {
81 free((void*)item->data.name);
82 /* cannot set item->data.name to NULL because item->data.name is non-nullable public API (rdar://problem/32235322)
83 * cannot leave item->data.name pointing to original data (rdar://problem/31006596)
84 * => suppress the warning */
85 #ifndef __clang_analyzer__
86 item->data.name = NULL;
87 #endif
88 }
89
90 if (item->data.value) {
91 memset(item->data.value, 0, item->data.valueLength);
92 free_safe(item->data.value);
93 }
94 }
95
96 static Boolean
97 _auth_item_equal(CFTypeRef value1, CFTypeRef value2)
98 {
99 return (CFHash(value1) == CFHash(value2));
100 }
101
102 static CFStringRef
103 _auth_item_copy_description(CFTypeRef value)
104 {
105 bool hidden = false;
106 auth_item_t item = (auth_item_t)value;
107
108 #ifndef DEBUG
109 static size_t passLen = strlen(kAuthorizationEnvironmentPassword);
110 if (strncasecmp(item->data.name, kAuthorizationEnvironmentPassword, passLen) == 0) {
111 hidden = true;
112 }
113 #endif
114
115 CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0);
116 CFStringAppendFormat(desc, NULL, CFSTR("auth_item: %s, type=%i, length=%li, flags=%x"),
117 item->data.name, item->type,
118 hidden ? 0 : item->data.valueLength, (unsigned int)item->data.flags);
119
120 switch (item->type) {
121 case AI_TYPE_STRING:
122 CFStringAppendFormat(desc, NULL, CFSTR(" value=%s"), hidden ? "(hidden)" : auth_item_get_string(item));
123 break;
124 case AI_TYPE_INT:
125 CFStringAppendFormat(desc, NULL, CFSTR(" value=%i"), *(int32_t*)item->data.value);
126 break;
127 case AI_TYPE_UINT:
128 CFStringAppendFormat(desc, NULL, CFSTR(" value=%u"), *(uint32_t*)item->data.value);
129 break;
130 case AI_TYPE_INT64:
131 CFStringAppendFormat(desc, NULL, CFSTR(" value=%lli"), *(int64_t*)item->data.value);
132 break;
133 case AI_TYPE_UINT64:
134 CFStringAppendFormat(desc, NULL, CFSTR(" value=%llu"), *(uint64_t*)item->data.value);
135 break;
136 case AI_TYPE_BOOL:
137 CFStringAppendFormat(desc, NULL, CFSTR(" value=%i"), *(bool*)item->data.value);
138 break;
139 case AI_TYPE_DOUBLE:
140 CFStringAppendFormat(desc, NULL, CFSTR(" value=%f"), *(double*)item->data.value);
141 break;
142 case AI_TYPE_DATA:
143 case AI_TYPE_UNKNOWN:
144 if (hidden) {
145 CFStringAppendFormat(desc, NULL, CFSTR(" value=(hidden)"));
146 } else {
147 CFStringAppendFormat(desc, NULL, CFSTR(" value=0x"));
148 size_t i = item->data.valueLength < 10 ? item->data.valueLength : 10;
149 uint8_t * data = item->data.value;
150 for (; i > 0; i--) {
151 CFStringAppendFormat(desc, NULL, CFSTR("%02x"), data[i-1]);
152 }
153 }
154 break;
155 default:
156 break;
157 }
158 return desc;
159 }
160
161 static CFHashCode
162 _auth_item_hash(CFTypeRef value)
163 {
164 auth_item_t item = (auth_item_t)value;
165 uint64_t crc = crc64_init();
166 crc = crc64_update(crc, item->data.name, strlen(item->data.name));
167 if (item->data.value) {
168 crc = crc64_update(crc, item->data.value, item->data.valueLength);
169 }
170 crc = crc64_update(crc, &item->data.flags, sizeof(item->data.flags));
171
172 crc = crc64_final(crc);
173 return (CFHashCode)crc;
174 }
175
176 AUTH_TYPE_INSTANCE(auth_item,
177 .init = NULL,
178 .copy = NULL,
179 .finalize = _auth_item_finalize,
180 .equal = _auth_item_equal,
181 .hash = _auth_item_hash,
182 .copyFormattingDesc = NULL,
183 .copyDebugDesc = _auth_item_copy_description
184 );
185
186 static CFTypeID auth_item_get_type_id() {
187 static CFTypeID type_id = _kCFRuntimeNotATypeID;
188 static dispatch_once_t onceToken;
189
190 dispatch_once(&onceToken, ^{
191 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_item);
192 });
193
194 return type_id;
195 }
196
197 static auth_item_t
198 _auth_item_create()
199 {
200 auth_item_t item = NULL;
201
202 item = (auth_item_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_item_get_type_id(), AUTH_CLASS_SIZE(auth_item), NULL);
203 require(item != NULL, done);
204
205 done:
206 return item;
207 }
208
209 static auth_item_t
210 auth_item_create(uint32_t type, const char * name, const void * value, size_t valueLen, uint32_t flags)
211 {
212 auth_item_t item = NULL;
213 require(name != NULL, done);
214
215 item = _auth_item_create();
216 require(item != NULL, done);
217
218 item->type = type;
219 item->data.flags = flags;
220 item->data.name = _copy_string(name);
221 item->data.valueLength = valueLen;
222 item->bufLen = valueLen;
223 if (value) {
224 if (item->type == AI_TYPE_STRING) {
225 item->bufLen++;
226 item->data.value = calloc(1u, item->bufLen);
227 } else if (valueLen) {
228 item->data.value = calloc(1u, item->bufLen);
229 }
230 if (valueLen) {
231 memcpy(item->data.value, value, valueLen);
232 }
233 }
234
235 done:
236 return item;
237 }
238
239 static auth_item_t
240 auth_item_create_with_xpc(xpc_object_t data)
241 {
242 auth_item_t item = NULL;
243 require(data != NULL, done);
244 require(xpc_get_type(data) == XPC_TYPE_DICTIONARY, done);
245 require(xpc_dictionary_get_string(data, AUTH_XPC_ITEM_NAME) != NULL, done);
246
247 item = _auth_item_create();
248 require(item != NULL, done);
249
250 item->data.name = _copy_string(xpc_dictionary_get_string(data, AUTH_XPC_ITEM_NAME));
251 item->data.flags = (uint32_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_FLAGS);
252 item->type = (uint32_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_TYPE);
253
254 size_t len;
255 const void * value = xpc_dictionary_get_data(data, AUTH_XPC_ITEM_VALUE, &len);
256 if (value) {
257 // <rdar://problem/13033889> authd is holding on to multiple copies of my password in the clear
258 bool sensitive = xpc_dictionary_get_value(data, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
259 if (sensitive) {
260 size_t sensitiveLength = (size_t)xpc_dictionary_get_uint64(data, AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH);
261 if (sensitiveLength > len) {
262 os_log_error(AUTHD_LOG, "Sensitive data len %zu is not valid", sensitiveLength);
263 goto done;
264 }
265 item->bufLen = sensitiveLength;
266 item->data.valueLength = sensitiveLength;
267 item->data.value = calloc(1u, sensitiveLength);
268 memcpy(item->data.value, value, sensitiveLength);
269 memset_s((void *)value, len, 0, sensitiveLength); // clear the sensitive data, memset_s is never optimized away
270 } else {
271 item->bufLen = len;
272 item->data.valueLength = len;
273 item->data.value = calloc(1u, len);
274 memcpy(item->data.value, value, len);
275 }
276 }
277
278 done:
279 return item;
280 }
281
282 static bool auth_item_crypt_worker(auth_item_t item, CFDataRef key, int operation)
283 {
284 bool result = false;
285
286 if (!key)
287 return result;
288
289 if (item->data.value && item->data.valueLength) {
290 size_t required_length = 0;
291 CCCryptorStatus status = CCCrypt(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding,
292 CFDataGetBytePtr(key), CFDataGetLength(key), NULL,
293 item->data.value, item->data.valueLength, NULL, 0, &required_length);
294 require(status == kCCBufferTooSmall, done);
295
296 void *buffer = calloc(1u, required_length);
297 status = CCCrypt(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding,
298 CFDataGetBytePtr(key), CFDataGetLength(key), NULL,
299 item->data.value, item->data.valueLength, buffer, required_length, &required_length);
300 if (status == kCCSuccess) {
301 memset(item->data.value, 0, item->data.valueLength);
302 free(item->data.value);
303 item->data.value = buffer;
304 item->data.valueLength = required_length;
305 result = true;
306 } else {
307 free(buffer);
308 }
309 }
310
311 done:
312 return result;
313 }
314
315 static bool auth_item_decrypt(auth_item_t item, CFDataRef key)
316 {
317 return auth_item_crypt_worker(item, key, kCCDecrypt);
318 }
319
320 static bool auth_item_encrypt(auth_item_t item, CFDataRef key)
321 {
322 return auth_item_crypt_worker(item, key, kCCEncrypt);
323 }
324
325 #pragma mark -
326 #pragma mark auth_items_t
327
328 struct _auth_items_s {
329 __AUTH_BASE_STRUCT_HEADER__;
330
331 CFMutableDictionaryRef dictionary;
332 AuthorizationItemSet set;
333 };
334
335 static void
336 _auth_items_finalize(CFTypeRef value)
337 {
338 auth_items_t items = (auth_items_t)value;
339
340 CFReleaseNull(items->dictionary);
341 free_safe(items->set.items)
342 }
343
344 static Boolean
345 _auth_items_equal(CFTypeRef value1, CFTypeRef value2)
346 {
347 auth_items_t items1 = (auth_items_t)value1;
348 auth_items_t items2 = (auth_items_t)value2;
349
350 return CFEqual(items1->dictionary, items2->dictionary);
351 }
352
353 static void
354 auth_items_add_item(auth_items_t items, auth_item_t item)
355 {
356 CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, item->data.name, kCFStringEncodingUTF8);
357 if (cfName) {
358 CFDictionarySetValue(items->dictionary, cfName, item);
359 CFRelease(cfName);
360 }
361 }
362
363 static CFStringRef
364 _auth_items_copy_description(CFTypeRef value)
365 {
366 auth_items_t items = (auth_items_t)value;
367 return CFCopyDescription(items->dictionary);
368 }
369
370 AUTH_TYPE_INSTANCE(auth_items,
371 .init = NULL,
372 .copy = NULL,
373 .finalize = _auth_items_finalize,
374 .equal = _auth_items_equal,
375 .hash = NULL,
376 .copyFormattingDesc = NULL,
377 .copyDebugDesc = _auth_items_copy_description
378 );
379
380 CFTypeID auth_items_get_type_id()
381 {
382 static CFTypeID type_id = _kCFRuntimeNotATypeID;
383 static dispatch_once_t onceToken;
384
385 dispatch_once(&onceToken, ^{
386 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_items);
387 });
388
389 return type_id;
390 }
391
392 static auth_items_t
393 _auth_items_create(bool createDict)
394 {
395 auth_items_t items = NULL;
396
397 items = (auth_items_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_items_get_type_id(), AUTH_CLASS_SIZE(auth_items), NULL);
398 require(items != NULL, done);
399
400 if (createDict) {
401 items->dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
402 }
403
404 done:
405 return items;
406 }
407
408 auth_items_t
409 auth_items_create()
410 {
411 auth_items_t items = NULL;
412
413 items = _auth_items_create(true);
414 require(items != NULL, done);
415
416 done:
417 return items;
418 }
419
420 static bool
421 _auth_items_parse_xpc(auth_items_t items, const xpc_object_t data)
422 {
423 bool result = false;
424 require(data != NULL, done);
425 require(xpc_get_type(data) == XPC_TYPE_ARRAY, done);
426
427 result = xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) {
428
429 auth_item_t item = auth_item_create_with_xpc(value);
430 if (item) {
431 auth_items_add_item(items, item);
432 CFRelease(item);
433 }
434
435 return true;
436 });
437
438 done:
439 return result;
440 }
441
442 auth_items_t auth_items_create_with_xpc(const xpc_object_t data)
443 {
444 auth_items_t items = NULL;
445
446 items = _auth_items_create(true);
447 require(items != NULL, done);
448
449 _auth_items_parse_xpc(items, data);
450
451 done:
452 return items;
453 }
454
455 auth_items_t
456 auth_items_create_copy(auth_items_t copy)
457 {
458 auth_items_t items = NULL;
459
460 items = _auth_items_create(false);
461 require(items != NULL, done);
462
463 items->dictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(copy->dictionary), copy->dictionary);
464
465 done:
466 return items;
467 }
468
469 size_t
470 auth_items_get_count(auth_items_t items)
471 {
472 return (size_t)CFDictionaryGetCount(items->dictionary);
473 }
474
475 xpc_object_t
476 auth_items_export_xpc(auth_items_t items)
477 {
478 xpc_object_t array = xpc_array_create(NULL, 0);
479
480 _cf_dictionary_iterate(items->dictionary, ^bool(CFTypeRef key AUTH_UNUSED, CFTypeRef value) {
481 auth_item_t item = (auth_item_t)value;
482 xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item);
483 xpc_array_append_value(array, xpc_data);
484 xpc_release_safe(xpc_data);
485 return true;
486 });
487
488 return array;
489 }
490
491 static auth_item_t
492 _find_item(auth_items_t items, const char * key)
493 {
494 auth_item_t item = NULL;
495 CFStringRef lookup = NULL;
496 require(key != NULL, done);
497
498 lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
499 require(lookup != NULL, done);
500
501 item = (auth_item_t)CFDictionaryGetValue(items->dictionary, lookup);
502
503 done:
504 CFReleaseSafe(lookup);
505 return item;
506 }
507
508 void
509 auth_items_set_flags(auth_items_t items, const char *key, uint32_t flags)
510 {
511 auth_item_t item = _find_item(items,key);
512 if (item) {
513 item->data.flags |= flags;
514 }
515 }
516
517 void
518 auth_items_clear_flags(auth_items_t items, const char *key, uint32_t flags)
519 {
520 auth_item_t item = _find_item(items,key);
521 if (item) {
522 item->data.flags &= ~flags;
523 }
524 }
525
526 uint32_t
527 auth_items_get_flags(auth_items_t items, const char *key)
528 {
529 auth_item_t item = _find_item(items,key);
530 if (item) {
531 return item->data.flags;
532 }
533
534 return 0;
535 }
536
537 bool
538 auth_items_check_flags(auth_items_t items, const char *key, uint32_t flags)
539 {
540 // When several bits are set in uint32_t flags, "(current & flags) != 0" checks if ANY flag is set, not all flags!
541 // This odd behavior is currently being relied upon in several places, so be careful when changing / fixing this.
542 // However, this also risks unwanted information leakage in
543 // AuthorizationCopyInfo ==> authorization_copy_info ==> [all info] auth_items_copy_with_flags
544 uint32_t current = auth_items_get_flags(items,key);
545 return flags ? (current & flags) != 0 : current == 0;
546 }
547
548 void
549 auth_items_set_key(auth_items_t items, const char *key)
550 {
551 auth_item_t item = _find_item(items,key);
552 if (!item) {
553 item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0);
554 if (item) {
555 auth_items_add_item(items, item);
556 CFRelease(item);
557 }
558 }
559 }
560
561 bool
562 auth_items_exist(auth_items_t items, const char *key)
563 {
564 return _find_item(items,key) != NULL;
565 }
566
567 void
568 auth_items_remove(auth_items_t items, const char *key)
569 {
570 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
571 CFDictionaryRemoveValue(items->dictionary, lookup);
572 CFReleaseSafe(lookup);
573 }
574
575 void
576 auth_items_remove_with_flags(auth_items_t items, uint32_t flags)
577 {
578 auth_items_iterate(items, ^bool(const char *key) {
579 if (auth_items_check_flags(items, key, flags)) {
580 auth_items_remove(items,key);
581 }
582 return true;
583 });
584 }
585
586 void
587 auth_items_clear(auth_items_t items)
588 {
589 CFDictionaryRemoveAllValues(items->dictionary);
590 }
591
592 void
593 auth_items_crypt_worker(auth_items_t items, CFDataRef encryption_key, bool(*function)(auth_item_t, CFDataRef))
594 {
595 auth_items_iterate(items, ^bool(const char *key) {
596 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
597 auth_item_t item = (auth_item_t)CFDictionaryGetValue(items->dictionary, lookup);
598 function(item, encryption_key);
599 CFReleaseSafe(lookup);
600 return true;
601 });
602 }
603
604 void
605 auth_items_encrypt(auth_items_t items, CFDataRef encryption_key)
606 {
607 auth_items_crypt_worker(items, encryption_key, auth_item_encrypt);
608 }
609
610 void
611 auth_items_decrypt(auth_items_t items, CFDataRef encryption_key)
612 {
613 auth_items_crypt_worker(items, encryption_key, auth_item_decrypt);
614 }
615
616 void
617 auth_items_copy(auth_items_t items, auth_items_t src)
618 {
619 auth_items_iterate(src, ^bool(const char *key) {
620 if (!key) {
621 return true;
622 }
623 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
624 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
625 if (item) auth_items_add_item(items, item);
626 CFReleaseSafe(lookup);
627 return true;
628 });
629 }
630
631 void
632 auth_items_content_copy(auth_items_t items, auth_items_t src)
633 {
634 auth_items_iterate(src, ^bool(const char *key) {
635 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
636 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
637 auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
638 if (new_item) auth_items_add_item(items, new_item);
639 CFReleaseSafe(lookup);
640 CFReleaseSafe(new_item);
641 return true;
642 });
643 }
644
645 void
646 auth_items_copy_xpc(auth_items_t items, const xpc_object_t src)
647 {
648 _auth_items_parse_xpc(items,src);
649 }
650
651 void
652 auth_items_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
653 {
654 auth_items_iterate(src, ^bool(const char *key) {
655 if (auth_items_check_flags(src, key, flags)) {
656 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
657 if (CFDictionaryContainsKey(items->dictionary, lookup)) {
658 CFDictionaryRemoveValue(items->dictionary, lookup); // we do not want to have preserved unretained key
659 }
660 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
661 if (item) auth_items_add_item(items, item);
662 CFReleaseSafe(lookup);
663 }
664 return true;
665 });
666 }
667
668 // unlike previous method, this one creates true new copy including their content
669 void
670 auth_items_content_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
671 {
672 auth_items_iterate(src, ^bool(const char *key) {
673 if (auth_items_check_flags(src, key, flags)) {
674 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
675 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
676 auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
677 if (new_item) {
678 auth_items_add_item(items, new_item);
679 CFRelease(new_item);
680 }
681 CFReleaseSafe(lookup);
682 }
683 return true;
684 });
685 }
686
687 bool
688 auth_items_iterate(auth_items_t items, auth_items_iterator_t iter)
689 {
690 bool result = false;
691 CFTypeRef* keys = NULL;
692 CFTypeRef* values = NULL;
693
694 CFIndex count = CFDictionaryGetCount(items->dictionary);
695 keys = calloc((size_t)count, sizeof(CFTypeRef));
696 require(keys != NULL, done);
697
698 values = calloc((size_t)count, sizeof(CFTypeRef));
699 require(values != NULL, done);
700
701 CFDictionaryGetKeysAndValues(items->dictionary, keys, values);
702 for (CFIndex i = 0; i < count; i++) {
703 auth_item_t item = (auth_item_t)values[i];
704 result = iter(item->data.name);
705 if (!result) {
706 break;
707 }
708 }
709
710 done:
711 free_safe(keys);
712 free_safe(values);
713 return result;
714 }
715
716 void
717 auth_items_set_string(auth_items_t items, const char *key, const char *value)
718 {
719 assert(value); // marked non-null
720
721 size_t valLen = strlen(value);
722 auth_item_t item = _find_item(items,key);
723 if (item && item->type == AI_TYPE_STRING && valLen < item->bufLen) {
724 memcpy(item->data.value, value, valLen+1); // copy null
725 item->data.valueLength = valLen;
726 } else {
727 item = auth_item_create(AI_TYPE_STRING, key, value, valLen, 0);
728 if (item) {
729 auth_items_add_item(items, item);
730 CFRelease(item);
731 }
732 }
733 }
734
735 const char *
736 auth_items_get_string(auth_items_t items, const char *key)
737 {
738 auth_item_t item = _find_item(items,key);
739 if (item) {
740 #if DEBUG
741 if (!(item->type == AI_TYPE_STRING || item->type == AI_TYPE_UNKNOWN)) {
742 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
743 item->data.name, item->type, AI_TYPE_STRING);
744 }
745 #endif
746 return auth_item_get_string(item);
747 }
748
749 return NULL;
750 }
751
752 void
753 auth_items_set_data(auth_items_t items, const char *key, const void *value, size_t len)
754 {
755 assert(value); // marked non-null
756
757 if (len) {
758 auth_item_t item = _find_item(items,key);
759 if (item && item->type == AI_TYPE_DATA && len <= item->bufLen) {
760 memcpy(item->data.value, value, len);
761 item->data.valueLength = len;
762 } else {
763 item = auth_item_create(AI_TYPE_DATA, key, value, len, 0);
764 if (item) {
765 auth_items_add_item(items, item);
766 CFRelease(item);
767 }
768 }
769 }
770 }
771
772 const void *
773 auth_items_get_data(auth_items_t items, const char *key, size_t *len)
774 {
775 assert(len); // marked non-null
776
777 auth_item_t item = _find_item(items,key);
778 if (item) {
779 #if DEBUG
780 if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
781 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
782 item->data.name, item->type, AI_TYPE_DATA);
783 }
784 #endif
785 *len = item->data.valueLength;
786 return item->data.value;
787 }
788
789 return NULL;
790 }
791
792 const void *
793 auth_items_get_data_with_flags(auth_items_t items, const char *key, size_t *len, uint32_t flags)
794 {
795 assert(len); // marked non-null
796
797 auth_item_t item = _find_item(items,key);
798 if (item && (item->data.flags & flags) == flags) {
799 #if DEBUG
800 if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
801 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
802 item->data.name, item->type, AI_TYPE_DATA);
803 }
804 #endif
805 *len = item->data.valueLength;
806
807 return item->data.value;
808 }
809
810 return NULL;
811 }
812
813 void
814 auth_items_set_bool(auth_items_t items, const char *key, bool value)
815 {
816 auth_item_t item = _find_item(items,key);
817 if (item && item->type == AI_TYPE_BOOL) {
818 *(bool*)item->data.value = value;
819 } else {
820 item = auth_item_create(AI_TYPE_BOOL, key, &value, sizeof(bool), 0);
821 if (item) {
822 auth_items_add_item(items, item);
823 CFRelease(item);
824 }
825 }
826 }
827
828 bool
829 auth_items_get_bool(auth_items_t items, const char *key)
830 {
831 auth_item_t item = _find_item(items,key);
832 if (item) {
833 #if DEBUG
834 if (!(item->type == AI_TYPE_BOOL || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(bool))) {
835 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
836 item->data.name, item->type, AI_TYPE_BOOL, item->data.valueLength, sizeof(bool));
837 }
838 #endif
839 if (item->type == AI_TYPE_STRING) {
840 return atoi(auth_item_get_string(item));
841 }
842
843 require(item->data.value != NULL, done);
844 require(item->data.valueLength == sizeof(bool), done);
845
846 return *(bool*)item->data.value;
847 }
848
849 done:
850 return false;
851 }
852
853 void
854 auth_items_set_int(auth_items_t items, const char *key, int32_t value)
855 {
856 auth_item_t item = _find_item(items,key);
857 if (item && item->type == AI_TYPE_INT) {
858 *(int32_t*)item->data.value = value;
859 } else {
860 item = auth_item_create(AI_TYPE_INT, key, &value, sizeof(int32_t), 0);
861 if (item) {
862 auth_items_add_item(items, item);
863 CFRelease(item);
864 }
865 }
866 }
867
868 int32_t
869 auth_items_get_int(auth_items_t items, const char *key)
870 {
871 auth_item_t item = _find_item(items,key);
872 if (item) {
873 #if DEBUG
874 if (!(item->type ==AI_TYPE_INT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int32_t))) {
875 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
876 item->data.name, item->type, AI_TYPE_INT, item->data.valueLength, sizeof(int32_t));
877 }
878 #endif
879 if (item->type == AI_TYPE_STRING) {
880 return atoi(auth_item_get_string(item));
881 }
882
883 require(item->data.value != NULL, done);
884 require(item->data.valueLength == sizeof(int32_t), done);
885
886 return *(int32_t*)item->data.value;
887 }
888
889 done:
890 return 0;
891 }
892
893 void
894 auth_items_set_uint(auth_items_t items, const char *key, uint32_t value)
895 {
896 auth_item_t item = _find_item(items,key);
897 if (item && item->type == AI_TYPE_UINT) {
898 *(uint32_t*)item->data.value = value;
899 } else {
900 item = auth_item_create(AI_TYPE_UINT, key, &value, sizeof(uint32_t), 0);
901 if (item) {
902 auth_items_add_item(items, item);
903 CFRelease(item);
904 }
905 }
906 }
907
908 uint32_t
909 auth_items_get_uint(auth_items_t items, const char *key)
910 {
911 auth_item_t item = _find_item(items,key);
912 if (item) {
913 #if DEBUG
914 if (!(item->type ==AI_TYPE_UINT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint32_t))) {
915 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
916 item->data.name, item->type, AI_TYPE_UINT, item->data.valueLength, sizeof(uint32_t));
917 }
918 #endif
919 if (item->type == AI_TYPE_STRING) {
920 return (uint32_t)atoi(auth_item_get_string(item));
921 }
922
923 require(item->data.value != NULL, done);
924 require(item->data.valueLength == sizeof(uint32_t), done);
925
926 return *(uint32_t*)item->data.value;
927 }
928
929 done:
930 return 0;
931 }
932
933 void
934 auth_items_set_int64(auth_items_t items, const char *key, int64_t value)
935 {
936 auth_item_t item = _find_item(items,key);
937 if (item && item->type == AI_TYPE_INT64) {
938 *(int64_t*)item->data.value = value;
939 } else {
940 item = auth_item_create(AI_TYPE_INT64, key, &value, sizeof(int64_t), 0);
941 if (item) {
942 auth_items_add_item(items, item);
943 CFRelease(item);
944 }
945 }
946 }
947
948 int64_t
949 auth_items_get_int64(auth_items_t items, const char *key)
950 {
951 auth_item_t item = _find_item(items,key);
952 if (item) {
953 #if DEBUG
954 if (!(item->type ==AI_TYPE_INT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int64_t))) {
955 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
956 item->data.name, item->type, AI_TYPE_INT64, item->data.valueLength, sizeof(int64_t));
957 }
958 #endif
959 if (item->type == AI_TYPE_STRING) {
960 return atoll(auth_item_get_string(item));
961 }
962
963 require(item->data.value != NULL, done);
964 require(item->data.valueLength == sizeof(int64_t), done);
965
966 return *(int64_t*)item->data.value;
967 }
968
969 done:
970 return 0;
971 }
972
973 void
974 auth_items_set_uint64(auth_items_t items, const char *key, uint64_t value)
975 {
976 auth_item_t item = _find_item(items,key);
977 if (item && item->type == AI_TYPE_UINT64) {
978 *(uint64_t*)item->data.value = value;
979 } else {
980 item = auth_item_create(AI_TYPE_UINT64, key, &value, sizeof(uint64_t), 0);
981 if (item) {
982 auth_items_add_item(items, item);
983 CFRelease(item);
984 }
985 }
986 }
987
988 uint64_t
989 auth_items_get_uint64(auth_items_t items, const char *key)
990 {
991 auth_item_t item = _find_item(items,key);
992 if (item) {
993 #if DEBUG
994 if (!(item->type ==AI_TYPE_UINT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint64_t))) {
995 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
996 item->data.name, item->type, AI_TYPE_UINT64, item->data.valueLength, sizeof(uint64_t));
997 }
998 #endif
999 if (item->type == AI_TYPE_STRING) {
1000 return (uint64_t)atoll(auth_item_get_string(item));
1001 }
1002
1003 require(item->data.value != NULL, done);
1004 require(item->data.valueLength == sizeof(uint64_t), done);
1005
1006 return *(uint64_t*)item->data.value;
1007 }
1008
1009 done:
1010 return 0;
1011 }
1012
1013 void auth_items_set_double(auth_items_t items, const char *key, double value)
1014 {
1015 auth_item_t item = _find_item(items,key);
1016 if (item && item->type == AI_TYPE_DOUBLE) {
1017 *(double*)item->data.value = value;
1018 } else {
1019 item = auth_item_create(AI_TYPE_DOUBLE, key, &value, sizeof(double), 0);
1020 if (item) {
1021 auth_items_add_item(items, item);
1022 CFRelease(item);
1023 }
1024 }
1025 }
1026
1027 double auth_items_get_double(auth_items_t items, const char *key)
1028 {
1029 auth_item_t item = _find_item(items,key);
1030 if (item) {
1031 #if DEBUG
1032 if (!(item->type ==AI_TYPE_DOUBLE || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(double))) {
1033 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
1034 item->data.name, item->type, AI_TYPE_DOUBLE, item->data.valueLength, sizeof(double));
1035 }
1036 #endif
1037 if (item->type == AI_TYPE_STRING) {
1038 return atof(auth_item_get_string(item));
1039 }
1040
1041 require(item->data.value != NULL, done);
1042 require(item->data.valueLength == sizeof(double), done);
1043
1044 return *(double*)item->data.value;
1045 }
1046
1047 done:
1048 return 0;
1049 }
1050
1051 uint32_t auth_items_get_type(auth_items_t items, const char *key)
1052 {
1053 auth_item_t item = _find_item(items,key);
1054 if (item) {
1055 return item->type;
1056 }
1057
1058 return AI_TYPE_UNKNOWN;
1059 }
1060
1061 size_t auth_items_get_length(auth_items_t items, const char *key)
1062 {
1063 auth_item_t item = _find_item(items,key);
1064 if (item) {
1065 return item->data.valueLength;
1066 }
1067
1068 return 0;
1069 }
1070
1071 void auth_items_set_value(auth_items_t items, const char *key, uint32_t type, uint32_t flags, const void *value, size_t len)
1072 {
1073 auth_item_t item = auth_item_create(type, key, value, len, flags);
1074 if (item) {
1075 auth_items_add_item(items, item);
1076 CFRelease(item);
1077 }
1078 }
1079
1080 #pragma mark -
1081 #pragma mark auth_rights_t
1082
1083 struct _auth_rights_s {
1084 __AUTH_BASE_STRUCT_HEADER__;
1085
1086 CFMutableArrayRef array;
1087 };
1088
1089 static void
1090 _auth_rights_finalize(CFTypeRef value)
1091 {
1092 auth_rights_t rights = (auth_rights_t)value;
1093
1094 CFReleaseNull(rights->array);
1095 }
1096
1097 static Boolean
1098 _auth_rights_equal(CFTypeRef value1, CFTypeRef value2)
1099 {
1100 auth_rights_t rights1 = (auth_rights_t)value1;
1101 auth_rights_t rights2 = (auth_rights_t)value2;
1102
1103 return CFEqual(rights1->array, rights2->array);
1104 }
1105
1106 static CFStringRef
1107 _auth_rights_copy_description(CFTypeRef value)
1108 {
1109 auth_rights_t rights = (auth_rights_t)value;
1110 return CFCopyDescription(rights->array);
1111 }
1112
1113 AUTH_TYPE_INSTANCE(auth_rights,
1114 .init = NULL,
1115 .copy = NULL,
1116 .finalize = _auth_rights_finalize,
1117 .equal = _auth_rights_equal,
1118 .hash = NULL,
1119 .copyFormattingDesc = NULL,
1120 .copyDebugDesc = _auth_rights_copy_description
1121 );
1122
1123 static CFTypeID auth_rights_get_type_id()
1124 {
1125 static CFTypeID type_id = _kCFRuntimeNotATypeID;
1126 static dispatch_once_t onceToken;
1127
1128 dispatch_once(&onceToken, ^{
1129 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_rights);
1130 });
1131
1132 return type_id;
1133 }
1134
1135 static auth_rights_t
1136 _auth_rights_create()
1137 {
1138 auth_rights_t rights = NULL;
1139
1140 rights = (auth_rights_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_rights_get_type_id(), AUTH_CLASS_SIZE(auth_rights), NULL);
1141 require(rights != NULL, done);
1142
1143 rights->array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1144
1145 done:
1146 return rights;
1147 }
1148
1149 auth_rights_t
1150 auth_rights_create()
1151 {
1152 auth_rights_t rights = _auth_rights_create();
1153 require(rights != NULL, done);
1154
1155 done:
1156 return rights;
1157 }
1158
1159 auth_rights_t auth_rights_create_with_xpc(const xpc_object_t data)
1160 {
1161 auth_rights_t rights = _auth_rights_create();
1162 require(rights != NULL, done);
1163 require(data != NULL, done);
1164 require(xpc_get_type(data) == XPC_TYPE_ARRAY, done);
1165
1166 xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) {
1167
1168 auth_item_t item = auth_item_create_with_xpc(value);
1169 if (item) {
1170 CFArrayAppendValue(rights->array, item);
1171 CFReleaseSafe(item);
1172 }
1173
1174 return true;
1175 });
1176
1177 done:
1178 return rights;
1179 }
1180
1181 xpc_object_t auth_rights_export_xpc(auth_rights_t rights)
1182 {
1183 xpc_object_t array = xpc_array_create(NULL, 0);
1184
1185 CFIndex count = CFArrayGetCount(rights->array);
1186 for (CFIndex i = 0; i < count; i++) {
1187 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1188 xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item);
1189 xpc_array_append_value(array, xpc_data);
1190 xpc_release_safe(xpc_data);
1191 }
1192
1193 return array;
1194 }
1195
1196 static auth_item_t
1197 _find_right_item(auth_rights_t rights, const char * key)
1198 {
1199 auth_item_t item = NULL;
1200 require(key != NULL, done);
1201
1202 CFIndex count = CFArrayGetCount(rights->array);
1203 for (CFIndex i = 0; i < count; i++) {
1204 auth_item_t tmp = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1205 if (tmp && strcmp(tmp->data.name, key) == 0) {
1206 item = tmp;
1207 break;
1208 }
1209 }
1210
1211 done:
1212 return item;
1213 }
1214
1215 void auth_rights_set_flags(auth_rights_t rights, const char *key, uint32_t flags)
1216 {
1217 auth_item_t item = _find_right_item(rights,key);
1218 if (item) {
1219 item->data.flags |= flags;
1220 }
1221 }
1222
1223 void auth_rights_clear_flags(auth_rights_t rights, const char *key, uint32_t flags)
1224 {
1225 auth_item_t item = _find_right_item(rights,key);
1226 if (item) {
1227 item->data.flags &= ~flags;
1228 }
1229 }
1230
1231 uint32_t auth_rights_get_flags(auth_rights_t rights, const char *key)
1232 {
1233 auth_item_t item = _find_right_item(rights,key);
1234 if (item) {
1235 return item->data.flags;
1236 }
1237
1238 return 0;
1239 }
1240
1241 bool auth_rights_check_flags(auth_rights_t rights, const char *key, uint32_t flags)
1242 {
1243 uint32_t current = auth_rights_get_flags(rights,key);
1244 return flags ? (current & flags) != 0 : current == 0;
1245 }
1246
1247 size_t auth_rights_get_count(auth_rights_t rights)
1248 {
1249 return (size_t)CFArrayGetCount(rights->array);
1250 }
1251
1252 void auth_rights_add(auth_rights_t rights, const char *key)
1253 {
1254 auth_item_t item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0);
1255 if (item) {
1256 CFArrayAppendValue(rights->array, item);
1257 CFReleaseSafe(item);
1258 }
1259 }
1260
1261 bool auth_rights_exist(auth_rights_t rights, const char *key)
1262 {
1263 return (_find_right_item(rights,key) != NULL);
1264 }
1265
1266 void auth_rights_remove(auth_rights_t rights, const char *key)
1267 {
1268 CFIndex count = CFArrayGetCount(rights->array);
1269 for (CFIndex i = 0; i < count; i++) {
1270 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1271 if (item && strcmp(item->data.name, key) == 0) {
1272 CFArrayRemoveValueAtIndex(rights->array, i);
1273 i--;
1274 count--;
1275 }
1276 }
1277 }
1278
1279 void auth_rights_clear(auth_rights_t rights)
1280 {
1281 CFArrayRemoveAllValues(rights->array);
1282 }
1283
1284 bool
1285 auth_rights_iterate(auth_rights_t rights, bool(^iter)(const char * key))
1286 {
1287 bool result = false;
1288
1289 CFIndex count = CFArrayGetCount(rights->array);
1290 for (CFIndex i = 0; i < count; i++) {
1291 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1292 result = iter(item->data.name);
1293 if (!result) {
1294 break;
1295 }
1296 }
1297
1298 return result;
1299 }