]> git.saurik.com Git - apple/security.git/blob - OSX/authd/authitems.c
Security-58286.251.4.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 if (sensitiveLength > len) {
259 os_log_error(AUTHD_LOG, "Sensitive data len %zu is not valid", sensitiveLength);
260 goto done;
261 }
262 item->bufLen = sensitiveLength;
263 item->data.valueLength = sensitiveLength;
264 item->data.value = calloc(1u, sensitiveLength);
265 memcpy(item->data.value, value, sensitiveLength);
266 memset_s((void *)value, len, 0, sensitiveLength); // clear the sensitive data, memset_s is never optimized away
267 } else {
268 item->bufLen = len;
269 item->data.valueLength = len;
270 item->data.value = calloc(1u, len);
271 memcpy(item->data.value, value, len);
272 }
273 }
274
275 done:
276 return item;
277 }
278
279 static bool auth_item_crypt_worker(auth_item_t item, CFDataRef key, int operation)
280 {
281 bool result = false;
282
283 if (!key)
284 return result;
285
286 if (item->data.value && item->data.valueLength) {
287 size_t required_length = 0;
288 CCCryptorStatus status = CCCrypt(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding,
289 CFDataGetBytePtr(key), CFDataGetLength(key), NULL,
290 item->data.value, item->data.valueLength, NULL, 0, &required_length);
291 require(status == kCCBufferTooSmall, done);
292
293 void *buffer = calloc(1u, required_length);
294 status = CCCrypt(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding,
295 CFDataGetBytePtr(key), CFDataGetLength(key), NULL,
296 item->data.value, item->data.valueLength, buffer, required_length, &required_length);
297 if (status == kCCSuccess) {
298 memset(item->data.value, 0, item->data.valueLength);
299 free(item->data.value);
300 item->data.value = buffer;
301 item->data.valueLength = required_length;
302 result = true;
303 } else {
304 free(buffer);
305 }
306 }
307
308 done:
309 return result;
310 }
311
312 static bool auth_item_decrypt(auth_item_t item, CFDataRef key)
313 {
314 return auth_item_crypt_worker(item, key, kCCDecrypt);
315 }
316
317 static bool auth_item_encrypt(auth_item_t item, CFDataRef key)
318 {
319 return auth_item_crypt_worker(item, key, kCCEncrypt);
320 }
321
322 #pragma mark -
323 #pragma mark auth_items_t
324
325 struct _auth_items_s {
326 __AUTH_BASE_STRUCT_HEADER__;
327
328 CFMutableDictionaryRef dictionary;
329 AuthorizationItemSet set;
330 };
331
332 static void
333 _auth_items_finalize(CFTypeRef value)
334 {
335 auth_items_t items = (auth_items_t)value;
336
337 CFReleaseNull(items->dictionary);
338 free_safe(items->set.items)
339 }
340
341 static Boolean
342 _auth_items_equal(CFTypeRef value1, CFTypeRef value2)
343 {
344 auth_items_t items1 = (auth_items_t)value1;
345 auth_items_t items2 = (auth_items_t)value2;
346
347 return CFEqual(items1->dictionary, items2->dictionary);
348 }
349
350 static void
351 auth_items_add_item(auth_items_t items, auth_item_t item)
352 {
353 CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, item->data.name, kCFStringEncodingUTF8);
354 if (cfName) {
355 CFDictionarySetValue(items->dictionary, cfName, item);
356 CFRelease(cfName);
357 }
358 }
359
360 static CFStringRef
361 _auth_items_copy_description(CFTypeRef value)
362 {
363 auth_items_t items = (auth_items_t)value;
364 return CFCopyDescription(items->dictionary);
365 }
366
367 AUTH_TYPE_INSTANCE(auth_items,
368 .init = NULL,
369 .copy = NULL,
370 .finalize = _auth_items_finalize,
371 .equal = _auth_items_equal,
372 .hash = NULL,
373 .copyFormattingDesc = NULL,
374 .copyDebugDesc = _auth_items_copy_description
375 );
376
377 CFTypeID auth_items_get_type_id()
378 {
379 static CFTypeID type_id = _kCFRuntimeNotATypeID;
380 static dispatch_once_t onceToken;
381
382 dispatch_once(&onceToken, ^{
383 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_items);
384 });
385
386 return type_id;
387 }
388
389 static auth_items_t
390 _auth_items_create(bool createDict)
391 {
392 auth_items_t items = NULL;
393
394 items = (auth_items_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_items_get_type_id(), AUTH_CLASS_SIZE(auth_items), NULL);
395 require(items != NULL, done);
396
397 if (createDict) {
398 items->dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
399 }
400
401 done:
402 return items;
403 }
404
405 auth_items_t
406 auth_items_create()
407 {
408 auth_items_t items = NULL;
409
410 items = _auth_items_create(true);
411 require(items != NULL, done);
412
413 done:
414 return items;
415 }
416
417 static bool
418 _auth_items_parse_xpc(auth_items_t items, const xpc_object_t data)
419 {
420 bool result = false;
421 require(data != NULL, done);
422 require(xpc_get_type(data) == XPC_TYPE_ARRAY, done);
423
424 result = xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) {
425
426 auth_item_t item = auth_item_create_with_xpc(value);
427 if (item) {
428 auth_items_add_item(items, item);
429 CFRelease(item);
430 }
431
432 return true;
433 });
434
435 done:
436 return result;
437 }
438
439 auth_items_t auth_items_create_with_xpc(const xpc_object_t data)
440 {
441 auth_items_t items = NULL;
442
443 items = _auth_items_create(true);
444 require(items != NULL, done);
445
446 _auth_items_parse_xpc(items, data);
447
448 done:
449 return items;
450 }
451
452 auth_items_t
453 auth_items_create_copy(auth_items_t copy)
454 {
455 auth_items_t items = NULL;
456
457 items = _auth_items_create(false);
458 require(items != NULL, done);
459
460 items->dictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(copy->dictionary), copy->dictionary);
461
462 done:
463 return items;
464 }
465
466 size_t
467 auth_items_get_count(auth_items_t items)
468 {
469 return (size_t)CFDictionaryGetCount(items->dictionary);
470 }
471
472 xpc_object_t
473 auth_items_export_xpc(auth_items_t items)
474 {
475 xpc_object_t array = xpc_array_create(NULL, 0);
476
477 _cf_dictionary_iterate(items->dictionary, ^bool(CFTypeRef key AUTH_UNUSED, CFTypeRef value) {
478 auth_item_t item = (auth_item_t)value;
479 xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item);
480 xpc_array_append_value(array, xpc_data);
481 xpc_release_safe(xpc_data);
482 return true;
483 });
484
485 return array;
486 }
487
488 static auth_item_t
489 _find_item(auth_items_t items, const char * key)
490 {
491 auth_item_t item = NULL;
492 CFStringRef lookup = NULL;
493 require(key != NULL, done);
494
495 lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
496 require(lookup != NULL, done);
497
498 item = (auth_item_t)CFDictionaryGetValue(items->dictionary, lookup);
499
500 done:
501 CFReleaseSafe(lookup);
502 return item;
503 }
504
505 void
506 auth_items_set_flags(auth_items_t items, const char *key, uint32_t flags)
507 {
508 auth_item_t item = _find_item(items,key);
509 if (item) {
510 item->data.flags |= flags;
511 }
512 }
513
514 void
515 auth_items_clear_flags(auth_items_t items, const char *key, uint32_t flags)
516 {
517 auth_item_t item = _find_item(items,key);
518 if (item) {
519 item->data.flags &= ~flags;
520 }
521 }
522
523 uint32_t
524 auth_items_get_flags(auth_items_t items, const char *key)
525 {
526 auth_item_t item = _find_item(items,key);
527 if (item) {
528 return item->data.flags;
529 }
530
531 return 0;
532 }
533
534 bool
535 auth_items_check_flags(auth_items_t items, const char *key, uint32_t flags)
536 {
537 // When several bits are set in uint32_t flags, "(current & flags) != 0" checks if ANY flag is set, not all flags!
538 // This odd behavior is currently being relied upon in several places, so be careful when changing / fixing this.
539 // However, this also risks unwanted information leakage in
540 // AuthorizationCopyInfo ==> authorization_copy_info ==> [all info] auth_items_copy_with_flags
541 uint32_t current = auth_items_get_flags(items,key);
542 return flags ? (current & flags) != 0 : current == 0;
543 }
544
545 void
546 auth_items_set_key(auth_items_t items, const char *key)
547 {
548 auth_item_t item = _find_item(items,key);
549 if (!item) {
550 item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0);
551 if (item) {
552 auth_items_add_item(items, item);
553 CFRelease(item);
554 }
555 }
556 }
557
558 bool
559 auth_items_exist(auth_items_t items, const char *key)
560 {
561 return _find_item(items,key) != NULL;
562 }
563
564 void
565 auth_items_remove(auth_items_t items, const char *key)
566 {
567 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
568 CFDictionaryRemoveValue(items->dictionary, lookup);
569 CFReleaseSafe(lookup);
570 }
571
572 void
573 auth_items_remove_with_flags(auth_items_t items, uint32_t flags)
574 {
575 auth_items_iterate(items, ^bool(const char *key) {
576 if (auth_items_check_flags(items, key, flags)) {
577 auth_items_remove(items,key);
578 }
579 return true;
580 });
581 }
582
583 void
584 auth_items_clear(auth_items_t items)
585 {
586 CFDictionaryRemoveAllValues(items->dictionary);
587 }
588
589 void
590 auth_items_crypt_worker(auth_items_t items, CFDataRef encryption_key, bool(*function)(auth_item_t, CFDataRef))
591 {
592 auth_items_iterate(items, ^bool(const char *key) {
593 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
594 auth_item_t item = (auth_item_t)CFDictionaryGetValue(items->dictionary, lookup);
595 function(item, encryption_key);
596 CFReleaseSafe(lookup);
597 return true;
598 });
599 }
600
601 void
602 auth_items_encrypt(auth_items_t items, CFDataRef encryption_key)
603 {
604 auth_items_crypt_worker(items, encryption_key, auth_item_encrypt);
605 }
606
607 void
608 auth_items_decrypt(auth_items_t items, CFDataRef encryption_key)
609 {
610 auth_items_crypt_worker(items, encryption_key, auth_item_decrypt);
611 }
612
613 void
614 auth_items_copy(auth_items_t items, auth_items_t src)
615 {
616 auth_items_iterate(src, ^bool(const char *key) {
617 if (!key) {
618 return true;
619 }
620 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
621 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
622 if (item) auth_items_add_item(items, item);
623 CFReleaseSafe(lookup);
624 return true;
625 });
626 }
627
628 void
629 auth_items_content_copy(auth_items_t items, auth_items_t src)
630 {
631 auth_items_iterate(src, ^bool(const char *key) {
632 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
633 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
634 auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
635 if (new_item) auth_items_add_item(items, new_item);
636 CFReleaseSafe(lookup);
637 CFReleaseSafe(new_item);
638 return true;
639 });
640 }
641
642 void
643 auth_items_copy_xpc(auth_items_t items, const xpc_object_t src)
644 {
645 _auth_items_parse_xpc(items,src);
646 }
647
648 void
649 auth_items_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
650 {
651 auth_items_iterate(src, ^bool(const char *key) {
652 if (auth_items_check_flags(src, key, flags)) {
653 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
654 if (CFDictionaryContainsKey(items->dictionary, lookup)) {
655 CFDictionaryRemoveValue(items->dictionary, lookup); // we do not want to have preserved unretained key
656 }
657 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
658 if (item) auth_items_add_item(items, item);
659 CFReleaseSafe(lookup);
660 }
661 return true;
662 });
663 }
664
665 // unlike previous method, this one creates true new copy including their content
666 void
667 auth_items_content_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
668 {
669 auth_items_iterate(src, ^bool(const char *key) {
670 if (auth_items_check_flags(src, key, flags)) {
671 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
672 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
673 auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
674 if (new_item) {
675 auth_items_add_item(items, new_item);
676 CFRelease(new_item);
677 }
678 CFReleaseSafe(lookup);
679 }
680 return true;
681 });
682 }
683
684 bool
685 auth_items_iterate(auth_items_t items, auth_items_iterator_t iter)
686 {
687 bool result = false;
688 CFTypeRef* keys = NULL;
689 CFTypeRef* values = NULL;
690
691 CFIndex count = CFDictionaryGetCount(items->dictionary);
692 keys = calloc((size_t)count, sizeof(CFTypeRef));
693 require(keys != NULL, done);
694
695 values = calloc((size_t)count, sizeof(CFTypeRef));
696 require(values != NULL, done);
697
698 CFDictionaryGetKeysAndValues(items->dictionary, keys, values);
699 for (CFIndex i = 0; i < count; i++) {
700 auth_item_t item = (auth_item_t)values[i];
701 result = iter(item->data.name);
702 if (!result) {
703 break;
704 }
705 }
706
707 done:
708 free_safe(keys);
709 free_safe(values);
710 return result;
711 }
712
713 void
714 auth_items_set_string(auth_items_t items, const char *key, const char *value)
715 {
716 assert(value); // marked non-null
717
718 size_t valLen = strlen(value);
719 auth_item_t item = _find_item(items,key);
720 if (item && item->type == AI_TYPE_STRING && valLen < item->bufLen) {
721 memcpy(item->data.value, value, valLen+1); // copy null
722 item->data.valueLength = valLen;
723 } else {
724 item = auth_item_create(AI_TYPE_STRING, key, value, valLen, 0);
725 if (item) {
726 auth_items_add_item(items, item);
727 CFRelease(item);
728 }
729 }
730 }
731
732 const char *
733 auth_items_get_string(auth_items_t items, const char *key)
734 {
735 auth_item_t item = _find_item(items,key);
736 if (item) {
737 #if DEBUG
738 if (!(item->type == AI_TYPE_STRING || item->type == AI_TYPE_UNKNOWN)) {
739 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
740 item->data.name, item->type, AI_TYPE_STRING);
741 }
742 #endif
743 return auth_item_get_string(item);
744 }
745
746 return NULL;
747 }
748
749 void
750 auth_items_set_data(auth_items_t items, const char *key, const void *value, size_t len)
751 {
752 assert(value); // marked non-null
753
754 if (len) {
755 auth_item_t item = _find_item(items,key);
756 if (item && item->type == AI_TYPE_DATA && len <= item->bufLen) {
757 memcpy(item->data.value, value, len);
758 item->data.valueLength = len;
759 } else {
760 item = auth_item_create(AI_TYPE_DATA, key, value, len, 0);
761 if (item) {
762 auth_items_add_item(items, item);
763 CFRelease(item);
764 }
765 }
766 }
767 }
768
769 const void *
770 auth_items_get_data(auth_items_t items, const char *key, size_t *len)
771 {
772 assert(len); // marked non-null
773
774 auth_item_t item = _find_item(items,key);
775 if (item) {
776 #if DEBUG
777 if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
778 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
779 item->data.name, item->type, AI_TYPE_DATA);
780 }
781 #endif
782 *len = item->data.valueLength;
783 return item->data.value;
784 }
785
786 return NULL;
787 }
788
789 const void *
790 auth_items_get_data_with_flags(auth_items_t items, const char *key, size_t *len, uint32_t flags)
791 {
792 assert(len); // marked non-null
793
794 auth_item_t item = _find_item(items,key);
795 if (item && (item->data.flags & flags) == flags) {
796 #if DEBUG
797 if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
798 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
799 item->data.name, item->type, AI_TYPE_DATA);
800 }
801 #endif
802 *len = item->data.valueLength;
803
804 return item->data.value;
805 }
806
807 return NULL;
808 }
809
810 void
811 auth_items_set_bool(auth_items_t items, const char *key, bool value)
812 {
813 auth_item_t item = _find_item(items,key);
814 if (item && item->type == AI_TYPE_BOOL) {
815 *(bool*)item->data.value = value;
816 } else {
817 item = auth_item_create(AI_TYPE_BOOL, key, &value, sizeof(bool), 0);
818 if (item) {
819 auth_items_add_item(items, item);
820 CFRelease(item);
821 }
822 }
823 }
824
825 bool
826 auth_items_get_bool(auth_items_t items, const char *key)
827 {
828 auth_item_t item = _find_item(items,key);
829 if (item) {
830 #if DEBUG
831 if (!(item->type == AI_TYPE_BOOL || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(bool))) {
832 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
833 item->data.name, item->type, AI_TYPE_BOOL, item->data.valueLength, sizeof(bool));
834 }
835 #endif
836 if (item->type == AI_TYPE_STRING) {
837 return atoi(auth_item_get_string(item));
838 }
839
840 require(item->data.value != NULL, done);
841 require(item->data.valueLength == sizeof(bool), done);
842
843 return *(bool*)item->data.value;
844 }
845
846 done:
847 return false;
848 }
849
850 void
851 auth_items_set_int(auth_items_t items, const char *key, int32_t value)
852 {
853 auth_item_t item = _find_item(items,key);
854 if (item && item->type == AI_TYPE_INT) {
855 *(int32_t*)item->data.value = value;
856 } else {
857 item = auth_item_create(AI_TYPE_INT, key, &value, sizeof(int32_t), 0);
858 if (item) {
859 auth_items_add_item(items, item);
860 CFRelease(item);
861 }
862 }
863 }
864
865 int32_t
866 auth_items_get_int(auth_items_t items, const char *key)
867 {
868 auth_item_t item = _find_item(items,key);
869 if (item) {
870 #if DEBUG
871 if (!(item->type ==AI_TYPE_INT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int32_t))) {
872 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
873 item->data.name, item->type, AI_TYPE_INT, item->data.valueLength, sizeof(int32_t));
874 }
875 #endif
876 if (item->type == AI_TYPE_STRING) {
877 return atoi(auth_item_get_string(item));
878 }
879
880 require(item->data.value != NULL, done);
881 require(item->data.valueLength == sizeof(int32_t), done);
882
883 return *(int32_t*)item->data.value;
884 }
885
886 done:
887 return 0;
888 }
889
890 void
891 auth_items_set_uint(auth_items_t items, const char *key, uint32_t value)
892 {
893 auth_item_t item = _find_item(items,key);
894 if (item && item->type == AI_TYPE_UINT) {
895 *(uint32_t*)item->data.value = value;
896 } else {
897 item = auth_item_create(AI_TYPE_UINT, key, &value, sizeof(uint32_t), 0);
898 if (item) {
899 auth_items_add_item(items, item);
900 CFRelease(item);
901 }
902 }
903 }
904
905 uint32_t
906 auth_items_get_uint(auth_items_t items, const char *key)
907 {
908 auth_item_t item = _find_item(items,key);
909 if (item) {
910 #if DEBUG
911 if (!(item->type ==AI_TYPE_UINT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint32_t))) {
912 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
913 item->data.name, item->type, AI_TYPE_UINT, item->data.valueLength, sizeof(uint32_t));
914 }
915 #endif
916 if (item->type == AI_TYPE_STRING) {
917 return (uint32_t)atoi(auth_item_get_string(item));
918 }
919
920 require(item->data.value != NULL, done);
921 require(item->data.valueLength == sizeof(uint32_t), done);
922
923 return *(uint32_t*)item->data.value;
924 }
925
926 done:
927 return 0;
928 }
929
930 void
931 auth_items_set_int64(auth_items_t items, const char *key, int64_t value)
932 {
933 auth_item_t item = _find_item(items,key);
934 if (item && item->type == AI_TYPE_INT64) {
935 *(int64_t*)item->data.value = value;
936 } else {
937 item = auth_item_create(AI_TYPE_INT64, key, &value, sizeof(int64_t), 0);
938 if (item) {
939 auth_items_add_item(items, item);
940 CFRelease(item);
941 }
942 }
943 }
944
945 int64_t
946 auth_items_get_int64(auth_items_t items, const char *key)
947 {
948 auth_item_t item = _find_item(items,key);
949 if (item) {
950 #if DEBUG
951 if (!(item->type ==AI_TYPE_INT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int64_t))) {
952 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
953 item->data.name, item->type, AI_TYPE_INT64, item->data.valueLength, sizeof(int64_t));
954 }
955 #endif
956 if (item->type == AI_TYPE_STRING) {
957 return atoll(auth_item_get_string(item));
958 }
959
960 require(item->data.value != NULL, done);
961 require(item->data.valueLength == sizeof(int64_t), done);
962
963 return *(int64_t*)item->data.value;
964 }
965
966 done:
967 return 0;
968 }
969
970 void
971 auth_items_set_uint64(auth_items_t items, const char *key, uint64_t value)
972 {
973 auth_item_t item = _find_item(items,key);
974 if (item && item->type == AI_TYPE_UINT64) {
975 *(uint64_t*)item->data.value = value;
976 } else {
977 item = auth_item_create(AI_TYPE_UINT64, key, &value, sizeof(uint64_t), 0);
978 if (item) {
979 auth_items_add_item(items, item);
980 CFRelease(item);
981 }
982 }
983 }
984
985 uint64_t
986 auth_items_get_uint64(auth_items_t items, const char *key)
987 {
988 auth_item_t item = _find_item(items,key);
989 if (item) {
990 #if DEBUG
991 if (!(item->type ==AI_TYPE_UINT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint64_t))) {
992 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
993 item->data.name, item->type, AI_TYPE_UINT64, item->data.valueLength, sizeof(uint64_t));
994 }
995 #endif
996 if (item->type == AI_TYPE_STRING) {
997 return (uint64_t)atoll(auth_item_get_string(item));
998 }
999
1000 require(item->data.value != NULL, done);
1001 require(item->data.valueLength == sizeof(uint64_t), done);
1002
1003 return *(uint64_t*)item->data.value;
1004 }
1005
1006 done:
1007 return 0;
1008 }
1009
1010 void auth_items_set_double(auth_items_t items, const char *key, double value)
1011 {
1012 auth_item_t item = _find_item(items,key);
1013 if (item && item->type == AI_TYPE_DOUBLE) {
1014 *(double*)item->data.value = value;
1015 } else {
1016 item = auth_item_create(AI_TYPE_DOUBLE, key, &value, sizeof(double), 0);
1017 if (item) {
1018 auth_items_add_item(items, item);
1019 CFRelease(item);
1020 }
1021 }
1022 }
1023
1024 double auth_items_get_double(auth_items_t items, const char *key)
1025 {
1026 auth_item_t item = _find_item(items,key);
1027 if (item) {
1028 #if DEBUG
1029 if (!(item->type ==AI_TYPE_DOUBLE || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(double))) {
1030 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
1031 item->data.name, item->type, AI_TYPE_DOUBLE, item->data.valueLength, sizeof(double));
1032 }
1033 #endif
1034 if (item->type == AI_TYPE_STRING) {
1035 return atof(auth_item_get_string(item));
1036 }
1037
1038 require(item->data.value != NULL, done);
1039 require(item->data.valueLength == sizeof(double), done);
1040
1041 return *(double*)item->data.value;
1042 }
1043
1044 done:
1045 return 0;
1046 }
1047
1048 uint32_t auth_items_get_type(auth_items_t items, const char *key)
1049 {
1050 auth_item_t item = _find_item(items,key);
1051 if (item) {
1052 return item->type;
1053 }
1054
1055 return AI_TYPE_UNKNOWN;
1056 }
1057
1058 size_t auth_items_get_length(auth_items_t items, const char *key)
1059 {
1060 auth_item_t item = _find_item(items,key);
1061 if (item) {
1062 return item->data.valueLength;
1063 }
1064
1065 return 0;
1066 }
1067
1068 void auth_items_set_value(auth_items_t items, const char *key, uint32_t type, uint32_t flags, const void *value, size_t len)
1069 {
1070 auth_item_t item = auth_item_create(type, key, value, len, flags);
1071 if (item) {
1072 auth_items_add_item(items, item);
1073 CFRelease(item);
1074 }
1075 }
1076
1077 #pragma mark -
1078 #pragma mark auth_rights_t
1079
1080 struct _auth_rights_s {
1081 __AUTH_BASE_STRUCT_HEADER__;
1082
1083 CFMutableArrayRef array;
1084 };
1085
1086 static void
1087 _auth_rights_finalize(CFTypeRef value)
1088 {
1089 auth_rights_t rights = (auth_rights_t)value;
1090
1091 CFReleaseNull(rights->array);
1092 }
1093
1094 static Boolean
1095 _auth_rights_equal(CFTypeRef value1, CFTypeRef value2)
1096 {
1097 auth_rights_t rights1 = (auth_rights_t)value1;
1098 auth_rights_t rights2 = (auth_rights_t)value2;
1099
1100 return CFEqual(rights1->array, rights2->array);
1101 }
1102
1103 static CFStringRef
1104 _auth_rights_copy_description(CFTypeRef value)
1105 {
1106 auth_rights_t rights = (auth_rights_t)value;
1107 return CFCopyDescription(rights->array);
1108 }
1109
1110 AUTH_TYPE_INSTANCE(auth_rights,
1111 .init = NULL,
1112 .copy = NULL,
1113 .finalize = _auth_rights_finalize,
1114 .equal = _auth_rights_equal,
1115 .hash = NULL,
1116 .copyFormattingDesc = NULL,
1117 .copyDebugDesc = _auth_rights_copy_description
1118 );
1119
1120 static CFTypeID auth_rights_get_type_id()
1121 {
1122 static CFTypeID type_id = _kCFRuntimeNotATypeID;
1123 static dispatch_once_t onceToken;
1124
1125 dispatch_once(&onceToken, ^{
1126 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_rights);
1127 });
1128
1129 return type_id;
1130 }
1131
1132 static auth_rights_t
1133 _auth_rights_create()
1134 {
1135 auth_rights_t rights = NULL;
1136
1137 rights = (auth_rights_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_rights_get_type_id(), AUTH_CLASS_SIZE(auth_rights), NULL);
1138 require(rights != NULL, done);
1139
1140 rights->array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1141
1142 done:
1143 return rights;
1144 }
1145
1146 auth_rights_t
1147 auth_rights_create()
1148 {
1149 auth_rights_t rights = _auth_rights_create();
1150 require(rights != NULL, done);
1151
1152 done:
1153 return rights;
1154 }
1155
1156 auth_rights_t auth_rights_create_with_xpc(const xpc_object_t data)
1157 {
1158 auth_rights_t rights = _auth_rights_create();
1159 require(rights != NULL, done);
1160 require(data != NULL, done);
1161 require(xpc_get_type(data) == XPC_TYPE_ARRAY, done);
1162
1163 xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) {
1164
1165 auth_item_t item = auth_item_create_with_xpc(value);
1166 if (item) {
1167 CFArrayAppendValue(rights->array, item);
1168 CFReleaseSafe(item);
1169 }
1170
1171 return true;
1172 });
1173
1174 done:
1175 return rights;
1176 }
1177
1178 xpc_object_t auth_rights_export_xpc(auth_rights_t rights)
1179 {
1180 xpc_object_t array = xpc_array_create(NULL, 0);
1181
1182 CFIndex count = CFArrayGetCount(rights->array);
1183 for (CFIndex i = 0; i < count; i++) {
1184 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1185 xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item);
1186 xpc_array_append_value(array, xpc_data);
1187 xpc_release_safe(xpc_data);
1188 }
1189
1190 return array;
1191 }
1192
1193 static auth_item_t
1194 _find_right_item(auth_rights_t rights, const char * key)
1195 {
1196 auth_item_t item = NULL;
1197 require(key != NULL, done);
1198
1199 CFIndex count = CFArrayGetCount(rights->array);
1200 for (CFIndex i = 0; i < count; i++) {
1201 auth_item_t tmp = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1202 if (tmp && strcmp(tmp->data.name, key) == 0) {
1203 item = tmp;
1204 break;
1205 }
1206 }
1207
1208 done:
1209 return item;
1210 }
1211
1212 void auth_rights_set_flags(auth_rights_t rights, const char *key, uint32_t flags)
1213 {
1214 auth_item_t item = _find_right_item(rights,key);
1215 if (item) {
1216 item->data.flags |= flags;
1217 }
1218 }
1219
1220 void auth_rights_clear_flags(auth_rights_t rights, const char *key, uint32_t flags)
1221 {
1222 auth_item_t item = _find_right_item(rights,key);
1223 if (item) {
1224 item->data.flags &= ~flags;
1225 }
1226 }
1227
1228 uint32_t auth_rights_get_flags(auth_rights_t rights, const char *key)
1229 {
1230 auth_item_t item = _find_right_item(rights,key);
1231 if (item) {
1232 return item->data.flags;
1233 }
1234
1235 return 0;
1236 }
1237
1238 bool auth_rights_check_flags(auth_rights_t rights, const char *key, uint32_t flags)
1239 {
1240 uint32_t current = auth_rights_get_flags(rights,key);
1241 return flags ? (current & flags) != 0 : current == 0;
1242 }
1243
1244 size_t auth_rights_get_count(auth_rights_t rights)
1245 {
1246 return (size_t)CFArrayGetCount(rights->array);
1247 }
1248
1249 void auth_rights_add(auth_rights_t rights, const char *key)
1250 {
1251 auth_item_t item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0);
1252 if (item) {
1253 CFArrayAppendValue(rights->array, item);
1254 CFReleaseSafe(item);
1255 }
1256 }
1257
1258 bool auth_rights_exist(auth_rights_t rights, const char *key)
1259 {
1260 return (_find_right_item(rights,key) != NULL);
1261 }
1262
1263 void auth_rights_remove(auth_rights_t rights, const char *key)
1264 {
1265 CFIndex count = CFArrayGetCount(rights->array);
1266 for (CFIndex i = 0; i < count; i++) {
1267 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1268 if (item && strcmp(item->data.name, key) == 0) {
1269 CFArrayRemoveValueAtIndex(rights->array, i);
1270 i--;
1271 count--;
1272 }
1273 }
1274 }
1275
1276 void auth_rights_clear(auth_rights_t rights)
1277 {
1278 CFArrayRemoveAllValues(rights->array);
1279 }
1280
1281 bool
1282 auth_rights_iterate(auth_rights_t rights, bool(^iter)(const char * key))
1283 {
1284 bool result = false;
1285
1286 CFIndex count = CFArrayGetCount(rights->array);
1287 for (CFIndex i = 0; i < count; i++) {
1288 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1289 result = iter(item->data.name);
1290 if (!result) {
1291 break;
1292 }
1293 }
1294
1295 return result;
1296 }