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