]> git.saurik.com Git - apple/security.git/blob - OSX/authd/authitems.c
Security-58286.31.2.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 if (!key) {
653 return true;
654 }
655 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
656 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
657 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
658 CFReleaseSafe(lookup);
659 return true;
660 });
661 }
662
663 void
664 auth_items_content_copy(auth_items_t items, auth_items_t src)
665 {
666 auth_items_iterate(src, ^bool(const char *key) {
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 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(new_item), new_item);
672 CFReleaseSafe(lookup);
673 CFReleaseSafe(new_item);
674 return true;
675 });
676 }
677
678 void
679 auth_items_copy_xpc(auth_items_t items, const xpc_object_t src)
680 {
681 _auth_items_parse_xpc(items,src);
682 }
683
684 void
685 auth_items_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
686 {
687 auth_items_iterate(src, ^bool(const char *key) {
688 if (auth_items_check_flags(src, key, flags)) {
689 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
690 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
691 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
692 CFReleaseSafe(lookup);
693 }
694 return true;
695 });
696 }
697
698 // unlike previous method, this one creates true new copy including their content
699 void
700 auth_items_content_copy_with_flags(auth_items_t items, auth_items_t src, uint32_t flags)
701 {
702 auth_items_iterate(src, ^bool(const char *key) {
703 if (auth_items_check_flags(src, key, flags)) {
704 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
705 auth_item_t item = (auth_item_t)CFDictionaryGetValue(src->dictionary, lookup);
706 auth_item_t new_item = auth_item_create(item->type, item->data.name, item->data.value, item->data.valueLength, item->data.flags);
707 if (new_item)
708 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(new_item), new_item);
709 CFReleaseSafe(lookup);
710 CFReleaseSafe(new_item);
711 }
712 return true;
713 });
714 }
715
716 bool
717 auth_items_iterate(auth_items_t items, auth_items_iterator_t iter)
718 {
719 bool result = false;
720 CFTypeRef* keys = NULL;
721 CFTypeRef* values = NULL;
722
723 CFIndex count = CFDictionaryGetCount(items->dictionary);
724 keys = calloc((size_t)count, sizeof(CFTypeRef));
725 require(keys != NULL, done);
726
727 values = calloc((size_t)count, sizeof(CFTypeRef));
728 require(values != NULL, done);
729
730 CFDictionaryGetKeysAndValues(items->dictionary, keys, values);
731 for (CFIndex i = 0; i < count; i++) {
732 auth_item_t item = (auth_item_t)values[i];
733 result = iter(item->data.name);
734 if (!result) {
735 break;
736 }
737 }
738
739 done:
740 free_safe(keys);
741 free_safe(values);
742 return result;
743 }
744
745 void
746 auth_items_set_string(auth_items_t items, const char *key, const char *value)
747 {
748 assert(value); // marked non-null
749
750 size_t valLen = strlen(value);
751 auth_item_t item = _find_item(items,key);
752 if (item && item->type == AI_TYPE_STRING && valLen < item->bufLen) {
753 memcpy(item->data.value, value, valLen+1); // copy null
754 item->data.valueLength = valLen;
755 } else {
756 item = auth_item_create(AI_TYPE_STRING, key, value, valLen, 0);
757 if (item) {
758 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
759 CFReleaseSafe(item);
760 }
761 }
762 }
763
764 const char *
765 auth_items_get_string(auth_items_t items, const char *key)
766 {
767 auth_item_t item = _find_item(items,key);
768 if (item) {
769 #if DEBUG
770 if (!(item->type == AI_TYPE_STRING || item->type == AI_TYPE_UNKNOWN)) {
771 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
772 item->data.name, item->type, AI_TYPE_STRING);
773 }
774 #endif
775 return auth_item_get_string(item);
776 }
777
778 return NULL;
779 }
780
781 void
782 auth_items_set_data(auth_items_t items, const char *key, const void *value, size_t len)
783 {
784 assert(value); // marked non-null
785
786 if (len) {
787 auth_item_t item = _find_item(items,key);
788 if (item && item->type == AI_TYPE_DATA && len <= item->bufLen) {
789 memcpy(item->data.value, value, len);
790 item->data.valueLength = len;
791 } else {
792 item = auth_item_create(AI_TYPE_DATA, key, value, len, 0);
793 if (item) {
794 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
795 CFReleaseSafe(item);
796 }
797 }
798 }
799 }
800
801 const void *
802 auth_items_get_data(auth_items_t items, const char *key, size_t *len)
803 {
804 assert(len); // marked non-null
805
806 auth_item_t item = _find_item(items,key);
807 if (item) {
808 #if DEBUG
809 if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
810 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
811 item->data.name, item->type, AI_TYPE_DATA);
812 }
813 #endif
814 *len = item->data.valueLength;
815 return item->data.value;
816 }
817
818 return NULL;
819 }
820
821 const void *
822 auth_items_get_data_with_flags(auth_items_t items, const char *key, size_t *len, uint32_t flags)
823 {
824 assert(len); // marked non-null
825
826 auth_item_t item = _find_item(items,key);
827 if (item && (item->data.flags & flags) == flags) {
828 #if DEBUG
829 if (!(item->type == AI_TYPE_DATA || item->type == AI_TYPE_UNKNOWN)) {
830 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i",
831 item->data.name, item->type, AI_TYPE_DATA);
832 }
833 #endif
834 *len = item->data.valueLength;
835
836 return item->data.value;
837 }
838
839 return NULL;
840 }
841
842 void
843 auth_items_set_bool(auth_items_t items, const char *key, bool value)
844 {
845 auth_item_t item = _find_item(items,key);
846 if (item && item->type == AI_TYPE_BOOL) {
847 *(bool*)item->data.value = value;
848 } else {
849 item = auth_item_create(AI_TYPE_BOOL, key, &value, sizeof(bool), 0);
850 if (item) {
851 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
852 CFReleaseSafe(item);
853 }
854 }
855 }
856
857 bool
858 auth_items_get_bool(auth_items_t items, const char *key)
859 {
860 auth_item_t item = _find_item(items,key);
861 if (item) {
862 #if DEBUG
863 if (!(item->type == AI_TYPE_BOOL || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(bool))) {
864 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
865 item->data.name, item->type, AI_TYPE_BOOL, item->data.valueLength, sizeof(bool));
866 }
867 #endif
868 if (item->type == AI_TYPE_STRING) {
869 return atoi(auth_item_get_string(item));
870 }
871
872 require(item->data.value != NULL, done);
873 require(item->data.valueLength == sizeof(bool), done);
874
875 return *(bool*)item->data.value;
876 }
877
878 done:
879 return false;
880 }
881
882 void
883 auth_items_set_int(auth_items_t items, const char *key, int32_t value)
884 {
885 auth_item_t item = _find_item(items,key);
886 if (item && item->type == AI_TYPE_INT) {
887 *(int32_t*)item->data.value = value;
888 } else {
889 item = auth_item_create(AI_TYPE_INT, key, &value, sizeof(int32_t), 0);
890 if (item) {
891 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
892 CFReleaseSafe(item);
893 }
894 }
895 }
896
897 int32_t
898 auth_items_get_int(auth_items_t items, const char *key)
899 {
900 auth_item_t item = _find_item(items,key);
901 if (item) {
902 #if DEBUG
903 if (!(item->type ==AI_TYPE_INT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int32_t))) {
904 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
905 item->data.name, item->type, AI_TYPE_INT, item->data.valueLength, sizeof(int32_t));
906 }
907 #endif
908 if (item->type == AI_TYPE_STRING) {
909 return atoi(auth_item_get_string(item));
910 }
911
912 require(item->data.value != NULL, done);
913 require(item->data.valueLength == sizeof(int32_t), done);
914
915 return *(int32_t*)item->data.value;
916 }
917
918 done:
919 return 0;
920 }
921
922 void
923 auth_items_set_uint(auth_items_t items, const char *key, uint32_t value)
924 {
925 auth_item_t item = _find_item(items,key);
926 if (item && item->type == AI_TYPE_UINT) {
927 *(uint32_t*)item->data.value = value;
928 } else {
929 item = auth_item_create(AI_TYPE_UINT, key, &value, sizeof(uint32_t), 0);
930 if (item) {
931 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
932 CFReleaseSafe(item);
933 }
934 }
935 }
936
937 uint32_t
938 auth_items_get_uint(auth_items_t items, const char *key)
939 {
940 auth_item_t item = _find_item(items,key);
941 if (item) {
942 #if DEBUG
943 if (!(item->type ==AI_TYPE_UINT || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint32_t))) {
944 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
945 item->data.name, item->type, AI_TYPE_UINT, item->data.valueLength, sizeof(uint32_t));
946 }
947 #endif
948 if (item->type == AI_TYPE_STRING) {
949 return (uint32_t)atoi(auth_item_get_string(item));
950 }
951
952 require(item->data.value != NULL, done);
953 require(item->data.valueLength == sizeof(uint32_t), done);
954
955 return *(uint32_t*)item->data.value;
956 }
957
958 done:
959 return 0;
960 }
961
962 void
963 auth_items_set_int64(auth_items_t items, const char *key, int64_t value)
964 {
965 auth_item_t item = _find_item(items,key);
966 if (item && item->type == AI_TYPE_INT64) {
967 *(int64_t*)item->data.value = value;
968 } else {
969 item = auth_item_create(AI_TYPE_INT64, key, &value, sizeof(int64_t), 0);
970 if (item) {
971 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
972 CFReleaseSafe(item);
973 }
974 }
975 }
976
977 int64_t
978 auth_items_get_int64(auth_items_t items, const char *key)
979 {
980 auth_item_t item = _find_item(items,key);
981 if (item) {
982 #if DEBUG
983 if (!(item->type ==AI_TYPE_INT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(int64_t))) {
984 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
985 item->data.name, item->type, AI_TYPE_INT64, item->data.valueLength, sizeof(int64_t));
986 }
987 #endif
988 if (item->type == AI_TYPE_STRING) {
989 return atoll(auth_item_get_string(item));
990 }
991
992 require(item->data.value != NULL, done);
993 require(item->data.valueLength == sizeof(int64_t), done);
994
995 return *(int64_t*)item->data.value;
996 }
997
998 done:
999 return 0;
1000 }
1001
1002 void
1003 auth_items_set_uint64(auth_items_t items, const char *key, uint64_t value)
1004 {
1005 auth_item_t item = _find_item(items,key);
1006 if (item && item->type == AI_TYPE_UINT64) {
1007 *(uint64_t*)item->data.value = value;
1008 } else {
1009 item = auth_item_create(AI_TYPE_UINT64, key, &value, sizeof(uint64_t), 0);
1010 if (item) {
1011 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
1012 CFReleaseSafe(item);
1013 }
1014 }
1015 }
1016
1017 uint64_t
1018 auth_items_get_uint64(auth_items_t items, const char *key)
1019 {
1020 auth_item_t item = _find_item(items,key);
1021 if (item) {
1022 #if DEBUG
1023 if (!(item->type ==AI_TYPE_UINT64 || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(uint64_t))) {
1024 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
1025 item->data.name, item->type, AI_TYPE_UINT64, item->data.valueLength, sizeof(uint64_t));
1026 }
1027 #endif
1028 if (item->type == AI_TYPE_STRING) {
1029 return (uint64_t)atoll(auth_item_get_string(item));
1030 }
1031
1032 require(item->data.value != NULL, done);
1033 require(item->data.valueLength == sizeof(uint64_t), done);
1034
1035 return *(uint64_t*)item->data.value;
1036 }
1037
1038 done:
1039 return 0;
1040 }
1041
1042 void auth_items_set_double(auth_items_t items, const char *key, double value)
1043 {
1044 auth_item_t item = _find_item(items,key);
1045 if (item && item->type == AI_TYPE_DOUBLE) {
1046 *(double*)item->data.value = value;
1047 } else {
1048 item = auth_item_create(AI_TYPE_DOUBLE, key, &value, sizeof(double), 0);
1049 if (item) {
1050 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
1051 CFReleaseSafe(item);
1052 }
1053 }
1054 }
1055
1056 double auth_items_get_double(auth_items_t items, const char *key)
1057 {
1058 auth_item_t item = _find_item(items,key);
1059 if (item) {
1060 #if DEBUG
1061 if (!(item->type ==AI_TYPE_DOUBLE || item->type == AI_TYPE_UNKNOWN) || (item->data.valueLength != sizeof(double))) {
1062 os_log_debug(AUTHD_LOG, "items: key = %{public}s, invalid type=%i expected=%i or size=%li expected=%li",
1063 item->data.name, item->type, AI_TYPE_DOUBLE, item->data.valueLength, sizeof(double));
1064 }
1065 #endif
1066 if (item->type == AI_TYPE_STRING) {
1067 return atof(auth_item_get_string(item));
1068 }
1069
1070 require(item->data.value != NULL, done);
1071 require(item->data.valueLength == sizeof(double), done);
1072
1073 return *(double*)item->data.value;
1074 }
1075
1076 done:
1077 return 0;
1078 }
1079
1080 uint32_t auth_items_get_type(auth_items_t items, const char *key)
1081 {
1082 auth_item_t item = _find_item(items,key);
1083 if (item) {
1084 return item->type;
1085 }
1086
1087 return AI_TYPE_UNKNOWN;
1088 }
1089
1090 size_t auth_items_get_length(auth_items_t items, const char *key)
1091 {
1092 auth_item_t item = _find_item(items,key);
1093 if (item) {
1094 return item->data.valueLength;
1095 }
1096
1097 return 0;
1098 }
1099
1100 void auth_items_set_value(auth_items_t items, const char *key, uint32_t type, uint32_t flags, const void *value, size_t len)
1101 {
1102 auth_item_t item = auth_item_create(type, key, value, len, flags);
1103 if (item) {
1104 CFDictionarySetValue(items->dictionary, auth_item_get_cf_key(item), item);
1105 CFReleaseSafe(item);
1106 }
1107 }
1108
1109 #pragma mark -
1110 #pragma mark auth_rights_t
1111
1112 struct _auth_rights_s {
1113 __AUTH_BASE_STRUCT_HEADER__;
1114
1115 CFMutableArrayRef array;
1116 };
1117
1118 static void
1119 _auth_rights_finalize(CFTypeRef value)
1120 {
1121 auth_rights_t rights = (auth_rights_t)value;
1122
1123 CFReleaseNull(rights->array);
1124 }
1125
1126 static Boolean
1127 _auth_rights_equal(CFTypeRef value1, CFTypeRef value2)
1128 {
1129 auth_rights_t rights1 = (auth_rights_t)value1;
1130 auth_rights_t rights2 = (auth_rights_t)value2;
1131
1132 return CFEqual(rights1->array, rights2->array);
1133 }
1134
1135 static CFStringRef
1136 _auth_rights_copy_description(CFTypeRef value)
1137 {
1138 auth_rights_t rights = (auth_rights_t)value;
1139 return CFCopyDescription(rights->array);
1140 }
1141
1142 AUTH_TYPE_INSTANCE(auth_rights,
1143 .init = NULL,
1144 .copy = NULL,
1145 .finalize = _auth_rights_finalize,
1146 .equal = _auth_rights_equal,
1147 .hash = NULL,
1148 .copyFormattingDesc = NULL,
1149 .copyDebugDesc = _auth_rights_copy_description
1150 );
1151
1152 static CFTypeID auth_rights_get_type_id()
1153 {
1154 static CFTypeID type_id = _kCFRuntimeNotATypeID;
1155 static dispatch_once_t onceToken;
1156
1157 dispatch_once(&onceToken, ^{
1158 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_rights);
1159 });
1160
1161 return type_id;
1162 }
1163
1164 static auth_rights_t
1165 _auth_rights_create()
1166 {
1167 auth_rights_t rights = NULL;
1168
1169 rights = (auth_rights_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_rights_get_type_id(), AUTH_CLASS_SIZE(auth_rights), NULL);
1170 require(rights != NULL, done);
1171
1172 rights->array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1173
1174 done:
1175 return rights;
1176 }
1177
1178 auth_rights_t
1179 auth_rights_create()
1180 {
1181 auth_rights_t rights = _auth_rights_create();
1182 require(rights != NULL, done);
1183
1184 done:
1185 return rights;
1186 }
1187
1188 auth_rights_t auth_rights_create_with_xpc(const xpc_object_t data)
1189 {
1190 auth_rights_t rights = _auth_rights_create();
1191 require(rights != NULL, done);
1192 require(data != NULL, done);
1193 require(xpc_get_type(data) == XPC_TYPE_ARRAY, done);
1194
1195 xpc_array_apply(data, ^bool(size_t index AUTH_UNUSED, xpc_object_t value) {
1196
1197 auth_item_t item = auth_item_create_with_xpc(value);
1198 if (item) {
1199 CFArrayAppendValue(rights->array, item);
1200 CFReleaseSafe(item);
1201 }
1202
1203 return true;
1204 });
1205
1206 done:
1207 return rights;
1208 }
1209
1210 xpc_object_t auth_rights_export_xpc(auth_rights_t rights)
1211 {
1212 xpc_object_t array = xpc_array_create(NULL, 0);
1213
1214 CFIndex count = CFArrayGetCount(rights->array);
1215 for (CFIndex i = 0; i < count; i++) {
1216 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1217 xpc_object_t xpc_data = auth_item_copy_auth_item_xpc(item);
1218 xpc_array_append_value(array, xpc_data);
1219 xpc_release_safe(xpc_data);
1220 }
1221
1222 return array;
1223 }
1224
1225 static auth_item_t
1226 _find_right_item(auth_rights_t rights, const char * key)
1227 {
1228 auth_item_t item = NULL;
1229 CFStringRef lookup = NULL;
1230 require(key != NULL, done);
1231
1232 lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
1233 require(lookup != NULL, done);
1234
1235 CFIndex count = CFArrayGetCount(rights->array);
1236 for (CFIndex i = 0; i < count; i++) {
1237 auth_item_t tmp = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1238 if (tmp && CFEqual(auth_item_get_cf_key(tmp), lookup)) {
1239 item = tmp;
1240 break;
1241 }
1242 }
1243
1244 done:
1245 CFReleaseSafe(lookup);
1246 return item;
1247 }
1248
1249 void auth_rights_set_flags(auth_rights_t rights, const char *key, uint32_t flags)
1250 {
1251 auth_item_t item = _find_right_item(rights,key);
1252 if (item) {
1253 item->data.flags |= flags;
1254 }
1255 }
1256
1257 void auth_rights_clear_flags(auth_rights_t rights, const char *key, uint32_t flags)
1258 {
1259 auth_item_t item = _find_right_item(rights,key);
1260 if (item) {
1261 item->data.flags &= ~flags;
1262 }
1263 }
1264
1265 uint32_t auth_rights_get_flags(auth_rights_t rights, const char *key)
1266 {
1267 auth_item_t item = _find_right_item(rights,key);
1268 if (item) {
1269 return item->data.flags;
1270 }
1271
1272 return 0;
1273 }
1274
1275 bool auth_rights_check_flags(auth_rights_t rights, const char *key, uint32_t flags)
1276 {
1277 uint32_t current = auth_rights_get_flags(rights,key);
1278 return flags ? (current & flags) != 0 : current == 0;
1279 }
1280
1281 size_t auth_rights_get_count(auth_rights_t rights)
1282 {
1283 return (size_t)CFArrayGetCount(rights->array);
1284 }
1285
1286 void auth_rights_add(auth_rights_t rights, const char *key)
1287 {
1288 auth_item_t item = auth_item_create(AI_TYPE_RIGHT, key, NULL, 0, 0);
1289 if (item) {
1290 CFArrayAppendValue(rights->array, item);
1291 CFReleaseSafe(item);
1292 }
1293 }
1294
1295 bool auth_rights_exist(auth_rights_t rights, const char *key)
1296 {
1297 return (_find_right_item(rights,key) != NULL);
1298 }
1299
1300 void auth_rights_remove(auth_rights_t rights, const char *key)
1301 {
1302 CFStringRef lookup = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, key, kCFStringEncodingUTF8, kCFAllocatorNull);
1303 CFIndex count = CFArrayGetCount(rights->array);
1304 for (CFIndex i = 0; i < count; i++) {
1305 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1306 if (CFEqual(auth_item_get_cf_key(item), lookup)) {
1307 CFArrayRemoveValueAtIndex(rights->array, i);
1308 i--;
1309 count--;
1310 }
1311 }
1312 CFReleaseSafe(lookup);
1313 }
1314
1315 void auth_rights_clear(auth_rights_t rights)
1316 {
1317 CFArrayRemoveAllValues(rights->array);
1318 }
1319
1320 bool
1321 auth_rights_iterate(auth_rights_t rights, bool(^iter)(const char * key))
1322 {
1323 bool result = false;
1324
1325 CFIndex count = CFArrayGetCount(rights->array);
1326 for (CFIndex i = 0; i < count; i++) {
1327 auth_item_t item = (auth_item_t)CFArrayGetValueAtIndex(rights->array, i);
1328 result = iter(item->data.name);
1329 if (!result) {
1330 break;
1331 }
1332 }
1333
1334 return result;
1335 }