1//
2// QueryHelpers.h
3// CoreEntitlements
4//
5
6#ifndef CORE_ENTITLEMENTS_HELPERS_H
7#define CORE_ENTITLEMENTS_HELPERS_H
8
9#include <sys/cdefs.h>
10__ptrcheck_abi_assume_single();
11
12/*!
13 * @function CEDynamic
14 * Marks an opcode as being dynamic
15 *
16 * @param op
17 * Opcode
18 */
19#define CEDynamic(op) (CEQueryOpOpcode_t)((op) | kCEOpDynamic)
20
21/*!
22 * @function CESelectIndex
23 * Returns an operation that when executed will modify the context such that any subsequent operation is performed on the entitlement object at the specified index
24 *
25 * @param index
26 * The index of the object within a container
27 *
28 * @discussion
29 * Using an index that is past the container's size will result in an invalid context
30 */
31#define CESelectIndex(index) (CEQueryOperation_t){.opcode = kCEOpSelectIndex, .parameters = {.numericParameter = index}}
32
33/*!
34 * @function CESelectKey
35 * Returns an operation that when executed will modify the context such that any subsequent operation is performed on the key of the dictionary pair
36 * @discussion
37 * Selecting a key on a non-dictionary-pair object is undefined behavior (i..e. it is implementation defined)
38 */
39#define CESelectKey() CESelectIndex(0)
40
41/*!
42 * @function CESelectValue
43 * Returns an operation that when executed will modify the context such that any subsequent operation is performed on the value of the dictionary pair
44 * @discussion
45 * Selecting a value on a non-dictionary-pair object is undefined behavior (i..e. it is implementation defined)
46 */
47#define CESelectValue() CESelectIndex(1)
48
49
50/*!
51 * @function CESelectDictValue
52 * Returns an operation that when executed will modify the context such that any subsequent operation is performed on the object that corresponds
53 * to the value pointed to by the specified key
54 *
55 * @param key
56 * The key of the object within a container
57 *
58 * @discussion
59 * Using a key that is not found in the container will result in an invalid context
60 */
61#define CESelectDictValue(key) (CEQueryOperation_t){.opcode = kCEOpSelectKey, .parameters = {.stringParameter = {.data = key, .length = sizeof(key) - 1}}}
62#define CESelectDictValueDynamic(key, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpSelectKey), .parameters = {.dynamicParameter = {.data = key, .length = len}}}
63
64/*!
65 * @function CEMatchString
66 * Returns an operation that will return a valid context if and only if the context corresponds to a valid string and matches the string exactly
67 *
68 * @param string
69 * The string to match against (MUST BE A STRING LITERAL)
70 *
71 * @discussion
72 * If a valid context is returned it will be in the same state as the execution context
73 */
74#define CEMatchString(string) (CEQueryOperation_t){.opcode = kCEOpMatchString, .parameters = {.stringParameter = {.data = string, .length = sizeof(string) - 1}}}
75#define CEMatchDynamicString(string, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpMatchString), .parameters = {.dynamicParameter = {.data = string, .length = len}}}
76
77/*!
78 * @function CEMatchPrefix
79 * Returns an operation that will return a valid context if and only if the context corresponds to a valid string and it's prefix matched the passed in prefix
80 *
81 * @param prefix
82 * The prefix to match against (MUST BE A STRING LITERAL)
83 *
84 * @discussion
85 * If a valid context is returned it will be in the same state as the execution context
86 */
87#define CEMatchPrefix(prefix) (CEQueryOperation_t){.opcode = kCEOpMatchStringPrefix, .parameters = {.stringParameter = {.data = prefix, .length = sizeof(prefix) - 1}}}
88#define CEMatchDynamicPrefix(prefix, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpMatchStringPrefix), .parameters = {.dynamicParameter = {.data = prefix, .length = len}}}
89
90/*!
91 * @function CEMatchType
92 * Returns an operation that will return a valid context if and only if the type selected by the context corresponds to the one passed in.
93 *
94 * @param type
95 * The type to match against
96 *
97 * @discussion
98 * If a valid context is returned it will be in the same state as the execution context
99 */
100#define CEMatchType(type) (CEQueryOperation_t){.opcode = kCEOpMatchType, .parameters = {.numericParameter = (int64_t)type}}
101
102/*!
103 * @function CEMatchBool
104 * Returns an operation that will return a valid context if and only if the context corresponds to a valid boolean and matches the boolean exactly
105 *
106 * @param val
107 * The bool to match against
108 *
109 * @discussion
110 * If a valid context is returned it will be in the same state as the execution context
111 */
112#define CEMatchBool(val) (CEQueryOperation_t){.opcode = kCEOpMatchBool, .parameters = {.numericParameter = !!val}}
113
114/*!
115 * @function CEMatchInteger
116 * Returns an operation that will return a valid context if and only if the context corresponds to a valid integer and matches the integer exactly
117 *
118 * @param val
119 * The integer to match against
120 *
121 * @discussion
122 * If a valid context is returned it will be in the same state as the execution context
123 */
124#define CEMatchInteger(val) (CEQueryOperation_t){.opcode = kCEOpMatchInteger, .parameters = {.numericParameter = val}}
125
126/*!
127 * @function CEIsIntegerAllowed
128 * Returns an operation that will return a valid context if 1) the current context is an integer and allows the integer, or 2) the context is an array of integers that allows the integer
129 *
130 * @param integer
131 * The integer to match against
132 *
133 * @discussion
134 * If a valid context is returned it will be in the same state as the execution context
135 */
136#define CEIsIntegerAllowed(integer) (CEQueryOperation_t){.opcode = kCEOpIntegerValueAllowed, .parameters = {.numericParameter = integer}}
137
138/*!
139 * @function CEIsStringAllowed
140 * Returns an operation that will return a valid context if 1) the current context is a string and allows the string via wildcard rules, or 2) the context is an array of strings that allows the string
141 *
142 * @param string
143 * The string to match against (MUST BE A STRING LITERAL)
144 *
145 * @discussion
146 * If a valid context is returned it will be in the same state as the execution context
147 */
148#define CEIsStringAllowed(string) (CEQueryOperation_t){.opcode = kCEOpStringValueAllowed, .parameters = {.stringParameter = {.data = string, .length = sizeof(string) - 1}}}
149#define CEIsDynamicStringAllowed(string, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpStringValueAllowed), .parameters = {.dynamicParameter = {.data = string, .length = len}}}
150
151/*!
152 * @function CEIsStringPrefixAllowed
153 * Returns an operation that will return a valid context if 1) the current context is a string and matches the prefix or 2) has an array that has the matches the prefix
154 *
155 * @param string
156 * The string to match against (MUST BE A STRING LITERAL)
157 *
158 * @discussion
159 * If a valid context is returned it will be in the same state as the execution context
160 */
161#define CEIsStringPrefixAllowed(string) (CEQueryOperation_t){.opcode = kCEOpStringPrefixValueAllowed, .parameters = {.stringParameter = {.data = string, .length = sizeof(string) - 1}}}
162#define CEIsDynamicStringPrefixAllowed(string, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpStringPrefixValueAllowed), .parameters = {.dynamicParameter = {.data = (const uint8_t*)(string), .length = len}}}
163
164/*!
165 * @function CEMatchData
166 * Returns an operation that will return a valid context if and only if the context corresponds to valid data and matches the data exactly
167 *
168 * @param string
169 * The data to match against (MUST BE A BYTE ARRAY)
170 *
171 * @discussion
172 * If a valid context is returned it will be in the same state as the execution context
173 */
174#define CEMatchDynamicData(d, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpMatchData), .parameters = {.dynamicParameter = {.data = d, .length = len}}}
175
176/*!
177 * @function CEIsDataAllowed
178 * Returns an operation that will return a valid context if 1) the current context is data and allows the data, or 2) the context is an array of data elements that allows the data
179 *
180 * @param string
181 * The data to match against (MUST BE A BYTE ARRAY)
182 *
183 * @discussion
184 * If a valid context is returned it will be in the same state as the execution context
185 */
186#define CEIsDynamicDataAllowed(d, len) (CEQueryOperation_t){.opcode = CEDynamic(kCEOpMatchDataValueAllowed), .parameters = {.dynamicParameter = {.data = d, .length = len}}}
187
188#pragma mark Helpers
189/*
190 Macro magic
191 */
192#define _SELECT_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
193
194#define _mc_1(_call, x) _call(x),
195#define _mc_2(_call, x, ...) _call(x), _mc_1(_call, __VA_ARGS__)
196#define _mc_3(_call, x, ...) _call(x), _mc_2(_call, __VA_ARGS__)
197#define _mc_4(_call, x, ...) _call(x), _mc_3(_call, __VA_ARGS__)
198#define _mc_5(_call, x, ...) _call(x), _mc_4(_call, __VA_ARGS__)
199#define _mc_6(_call, x, ...) _call(x), _mc_5(_call, __VA_ARGS__)
200#define _mc_7(_call, x, ...) _call(x), _mc_6(_call, __VA_ARGS__)
201#define _mc_8(_call, x, ...) _call(x), _mc_7(_call, __VA_ARGS__)
202#define _mc_9(_call, x, ...) _call(x), _mc_8(_call, __VA_ARGS__)
203#define _mc_10(_call, x, ...) _call(x), _mc_9(_call, __VA_ARGS__)
204
205#define _MACRO_ITER(macro, ...) _SELECT_NTH_ARG(__VA_ARGS__, _mc_10, _mc_9, _mc_8 _mc_7, _mc_6, _mc_5, _mc_4, _mc_3, _mc_2, _mc_1)(macro, __VA_ARGS__)
206
207/*!
208 Macro to automatically generate a query path from a list of string sub components
209 So
210 @code
211 CE_SELECT_PATH("hello, "world") will select a key "hello" and then look up "world" in the dictionary stored in the value of "hello"
212 @endcode
213 */
214#define CE_SELECT_PATH(...) _MACRO_ITER(CESelectDictValue, __VA_ARGS__)
215
216// Macro for string equals
217#define CE_STRING_EQUALS(str) CEMatchString(str)
218
219/*
220 A macro that checks if the passed in context grants (via an explicit true boolean) the entitlement at the passed in path.
221 */
222#define CE_CONTEXT_GRANTS_ENTITLEMENT(ctx, ...) (CEContextQuery(ctx, (CEQuery_t){CE_SELECT_PATH(__VA_ARGS__) CEMatchBool(true)}, sizeof((CEQuery_t){CE_SELECT_PATH(__VA_ARGS__) CEMatchBool(true)}) / sizeof(CEQueryOperation_t)) == kCENoError)
223
224#endif
225