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