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