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