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 | |