#include "idl_types.h" /* IDL structures and constants for conditional aces. */ import "security.idl"; interface conditional_ace { /* * Conditional ACEs have an expression at the end of the ACE. * We know it is there because the ACE type has CALLBACK in * its name, and we know how long it is because the size field * in the ACE points somewhere beyond the otherwise accounted * for objects: * * | type | flags | size | access_mask | trustee | | * `---------------------------------->| * * If the first 4 bytes of the extra bit (called "coda" in our * structs) are {'a', 'r', 't', 'x'}, the callback ACE is a * conditional ACE. On Windows it is possible to register * other kinds of callback ACEs with different magic strings * that get handled by callback functions. There is little * evidence of this ever happening, but that explains the * name. * * After the "artx", a conditional ACE consists of a series of * tokens that describe an expression tree in reverse Polish * order. The expression can work with claim and SID values * from the security token, comparing them to each other and * to literal values. [MS-DTYP] is reasonably clear about how * they work. */ /* * Token types from [MS-DTYP] 2.4.4.17 "Conditional ACEs". */ typedef [enum8bit] enum { /* * Microsoft counts padding zeroes as a kind of token. * There should be up to three of these at the end, to * round out the size to a multiple of four. */ CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING = 0x00, /* Literal tokens * ============== * * Literal integers. These are *all* stored using 10 * bytes: * * - 8 bytes for the value, limited to the correct range * (e.g. -128 to 127 for INT8) * - 1 byte for sign, probably just used for display * - 1 byte for base, just used for display * * SDDL integers are all stored using 64 bits, but * different token types can be used to pretend they * have smaller width. In comparisons (which is all * they can be used for) the type does not matter. The * only special thing a non-64 bit literal can do is * to cause a parsing error by being out of range (it * is an open question as to how you would end up with * short integers, let alone invalid ones, as the SDDL * syntax does not have a way of specifying them). */ CONDITIONAL_ACE_TOKEN_INT8 = 0x01, CONDITIONAL_ACE_TOKEN_INT16 = 0x02, CONDITIONAL_ACE_TOKEN_INT32 = 0x03, CONDITIONAL_ACE_TOKEN_INT64 = 0x04, /* * Literal strings and structured types. * * These have an unsigned 32 bit byte length, followed * by data. * * for unicode the data is UTF-16. * octet strings are bytes. * the composite type is a list type. * the sid type has an ordinary binary sid after the length. */ CONDITIONAL_ACE_TOKEN_UNICODE = 0x10, CONDITIONAL_ACE_TOKEN_OCTET_STRING = 0x18, CONDITIONAL_ACE_TOKEN_COMPOSITE = 0x50, CONDITIONAL_ACE_TOKEN_SID = 0x51, CONDITIONAL_ACE_LOCAL_ATTRIBUTE = 0xf8, CONDITIONAL_ACE_USER_ATTRIBUTE = 0xf9, CONDITIONAL_ACE_RESOURCE_ATTRIBUTE = 0xfa, CONDITIONAL_ACE_DEVICE_ATTRIBUTE = 0xfb, /* * Unary relational operator tokens * ================================ * * For the membership ops, the operand can be a single * SID or a composite list of SIDs. * * Member_Of: true if the security token user SIDs * array contains all of the SIDs in the operand. */ CONDITIONAL_ACE_TOKEN_MEMBER_OF = 0x89, /* * Device_Member_Of: true if the security token device * SIDs array contains all of the SIDs in the operand. */ CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF = 0x8a, /* * Member_Of_Any: true if the user SIDs array contains any of * the SIDs in the operand. */ CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY = 0x8b, /* * Device_Member_Of_Any: true if the device SIDs array * contains any of the SIDs in the operand. */ CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY = 0x8c, /* * Logical inverses of the member-of crew. */ CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF = 0x90, CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF = 0x91, CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY = 0x92, CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY = 0x93, /* * Binary relational operators * =========================== * * The left hand side argument (LHS) is an attribute. * The RHS is an attribute or a value or composite * list of values (depending on the operation). * * If the types mismatch, the result is UNKNOWN. */ CONDITIONAL_ACE_TOKEN_EQUAL = 0x80, /* == */ CONDITIONAL_ACE_TOKEN_NOT_EQUAL = 0x81, /* != */ CONDITIONAL_ACE_TOKEN_LESS_THAN = 0x82, /* < */ CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL = 0x83, /* <= */ CONDITIONAL_ACE_TOKEN_GREATER_THAN = 0x84, /* > */ CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL = 0x85, /* >= */ /* * "contains" implies "all of", in contrast to the "any of" * operators. */ CONDITIONAL_ACE_TOKEN_CONTAINS = 0x86, CONDITIONAL_ACE_TOKEN_ANY_OF = 0x88, CONDITIONAL_ACE_TOKEN_NOT_CONTAINS = 0x8e, CONDITIONAL_ACE_TOKEN_NOT_ANY_OF = 0x8f, /* * Unary logical operators * ======================= * * The operand for the existence operators must be a * local attribute or a resource attribute. */ CONDITIONAL_ACE_TOKEN_EXISTS = 0x87, /* Exists */ CONDITIONAL_ACE_TOKEN_NOT_EXISTS = 0x8d, /* Not_Exists */ /* NOT operator */ CONDITIONAL_ACE_TOKEN_NOT = 0xa2, /* ! */ /* * Binary logical operators * ======================== */ CONDITIONAL_ACE_TOKEN_AND = 0xa0, /* && */ CONDITIONAL_ACE_TOKEN_OR = 0xa1, /* || */ /* * Samba specific pseudo-tokens * ============================ * * In running the conditional ace we maintain a stack * that is used as operands to the operators. Some of * the values on the stack are literals found inline * in the data, some are primitives resulting from * attribute look-up operations, and some are logical * results from comparison operations, which are in * the ternary form just mentioned. [MS-DTYP] * describes no token form for these ternary values, * as they are not used on the wire (that is, you * can't have a literal 'true' in a conditional ace). * So we add a token representation for Boolean result * types to use on the stack, using an available * opcode. The result of a lookup can also be 'NULL', * or an error, and we have opcodes for those too. * * These token types raise an error if they show up in * a conditional ACE, just like any other unknown * token type. They are for internal use only. * * In [MS-DTYP] these are called "Result Value". */ CONDITIONAL_ACE_SAMBA_RESULT_BOOL = 0x0f, CONDITIONAL_ACE_SAMBA_RESULT_NULL = 0x0e, CONDITIONAL_ACE_SAMBA_RESULT_ERROR = 0x0d, /* * Samba specific parentheses pseudo-tokens * ======================================== * * These are useful for compiling SDDL, but will never show * up in the compiled ACE or during evaluation. */ CONDITIONAL_ACE_SAMBA_SDDL_PAREN = 0x09, CONDITIONAL_ACE_SAMBA_SDDL_PAREN_END = 0x08 } token_type; /* * Integer attributes. * ================== * * Integers are stored with a base indicator and a sign * indicator. * * Integer base is stored for display purposes. For example, * the number 17 will be shown as "021" with option 1, "17" * with 2, and "0x11" with 3. Comparisons are not affected. */ typedef [enum8bit] enum { CONDITIONAL_ACE_INT_BASE_8 = 0x01, CONDITIONAL_ACE_INT_BASE_10 = 0x02, CONDITIONAL_ACE_INT_BASE_16 = 0x03 } int_base; /* * Integer sign, mostly for display purposes[1]. It seems * negative numbers should be flagged here as negative (i.e. * with 2), while positive numbers should be flagged with * "none" (3), unless you want them to show up with a plus * sign in SDDL. * * [1] it is possible this has some real significance, perhaps * acting as an unsigned flag. TO BE DETERMINED. */ typedef [enum8bit] enum { CONDITIONAL_ACE_INT_SIGN_POSITIVE = 0x01, CONDITIONAL_ACE_INT_SIGN_NEGATIVE = 0x02, CONDITIONAL_ACE_INT_SIGN_NONE = 0x03 } int_sign; /* * Ternary logical values * * Conditional ACEs use a ternary logic where values can be * unknown as well as true or false. * * The "Bool" result token can take any of these three values. * There is no literal Boolean value, but an integer of value * 0 or 1 can be compared with a Boolean result. */ typedef enum { ACE_CONDITION_FALSE = 0, ACE_CONDITION_TRUE = 1, ACE_CONDITION_UNKNOWN = -1 } ternary_logic_value; /* * Sub-structures for struct ace_condition_token -> data, * which vary according to the token->type. */ typedef [flag(NDR_NOALIGN)] struct { int64 value; } ace_condition_result; typedef [public] struct { int64 value; uint8 sign; uint8 base; } ace_condition_int; typedef [public] struct { /* * Zeroes are not allowed in the binary format (which * is otherwise UTF-16), and if we did let them * through we would end up with a truncated string. */ [flag(STR_SIZE4|STR_NOTERM|STR_BYTESIZE|STR_NO_EMBEDDED_NUL)] string value; } ace_condition_unicode; typedef [public] struct { [subcontext(4)] dom_sid sid; } ace_condition_sid; /* * The composite type has an array of sub-tokens, which can * themselves be composites containing composites, though this * is unlikely to be useful when dealing with claims. * * This structure is not representative of the wire format. */ typedef struct { ace_condition_token *tokens; uint32 n_members; } ace_condition_composite; /* * Operators have no data, but it is sometimes helpful for * SDDL compilation messages to record the position in the * string. */ typedef struct { uint32 sddl_position; } ace_condition_op; /* * struct ace_condition_sddl_op is not as real token, but is * used in compiling sddl. The idea is, for example, that if * popping with a ')' doesn't match the right '(', the details * of the '(' are there for the error message. */ typedef struct { uint32 start; uint32 position; } ace_condition_sddl_op; typedef [nodiscriminant] union { [case(CONDITIONAL_ACE_TOKEN_SID)] ace_condition_sid sid; [case(CONDITIONAL_ACE_TOKEN_COMPOSITE)]ace_condition_composite composite; [case(CONDITIONAL_ACE_TOKEN_OCTET_STRING)] DATA_BLOB bytes; [case(CONDITIONAL_ACE_TOKEN_UNICODE)]ace_condition_unicode unicode; [case(CONDITIONAL_ACE_LOCAL_ATTRIBUTE)]ace_condition_unicode local_attr; [case(CONDITIONAL_ACE_USER_ATTRIBUTE)]ace_condition_unicode user_attr; [case(CONDITIONAL_ACE_DEVICE_ATTRIBUTE)]ace_condition_unicode device_attr; [case(CONDITIONAL_ACE_RESOURCE_ATTRIBUTE)]ace_condition_unicode resource_attr; [case(CONDITIONAL_ACE_TOKEN_INT64)]ace_condition_int int64; [case(CONDITIONAL_ACE_TOKEN_INT32)]ace_condition_int int32; [case(CONDITIONAL_ACE_TOKEN_INT16)]ace_condition_int int16; [case(CONDITIONAL_ACE_TOKEN_INT8)]ace_condition_int int8; [case(CONDITIONAL_ACE_SAMBA_SDDL_PAREN)]ace_condition_sddl_op sddl_op; [case(CONDITIONAL_ACE_SAMBA_RESULT_BOOL)]ace_condition_result result; /* NULL and Error results are empty */ [case(CONDITIONAL_ACE_SAMBA_RESULT_NULL)]ace_condition_result result_null; [case(CONDITIONAL_ACE_SAMBA_RESULT_ERROR)]ace_condition_result result_error; /* operations */ [case(CONDITIONAL_ACE_TOKEN_MEMBER_OF)]ace_condition_op member_of; [case(CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF)]ace_condition_op device_member_of; [case(CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY)]ace_condition_op member_of_any; [case(CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY)]ace_condition_op device_member_of_any; [case(CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF)]ace_condition_op not_member_of; [case(CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF)]ace_condition_op not_device_member_of; [case(CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY)]ace_condition_op not_member_of_any; [case(CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY)]ace_condition_op not_device_member_of_any; [case(CONDITIONAL_ACE_TOKEN_EQUAL)]ace_condition_op equal; [case(CONDITIONAL_ACE_TOKEN_NOT_EQUAL)]ace_condition_op not_equal; [case(CONDITIONAL_ACE_TOKEN_LESS_THAN)]ace_condition_op less_than; [case(CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL)]ace_condition_op less_or_equal; [case(CONDITIONAL_ACE_TOKEN_GREATER_THAN)]ace_condition_op greater_than; [case(CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL)]ace_condition_op greater_or_equal; [case(CONDITIONAL_ACE_TOKEN_CONTAINS)]ace_condition_op contains; [case(CONDITIONAL_ACE_TOKEN_ANY_OF)]ace_condition_op any_of; [case(CONDITIONAL_ACE_TOKEN_NOT_CONTAINS)]ace_condition_op not_contains; [case(CONDITIONAL_ACE_TOKEN_NOT_ANY_OF)]ace_condition_op not_any_of; [case(CONDITIONAL_ACE_TOKEN_AND)]ace_condition_op and; [case(CONDITIONAL_ACE_TOKEN_OR)]ace_condition_op or; [case(CONDITIONAL_ACE_TOKEN_NOT)]ace_condition_op not; [case(CONDITIONAL_ACE_TOKEN_EXISTS)]ace_condition_op exists; [case(CONDITIONAL_ACE_TOKEN_NOT_EXISTS)]ace_condition_op not_exists; [default] ace_condition_op op; } ace_condition_token_data; /* * struct ace_condition_token is the fundamental building * block of a conditional ACE expression. */ typedef [public] struct { [switch_is(type)] ace_condition_token_data data; uint32 flags; token_type type; } ace_condition_token; /* * The expression as a whole is an just an array of tokens. * * But because we are always going to need a stack for * evaluating the expression, we allocate that and keep it * handy. */ typedef [public] struct { ace_condition_token *tokens; ace_condition_token *stack; uint32 length; } ace_condition_script; typedef enum { ACE_CONDITION_FLAG_ALLOW_DEVICE = 0x01 } ace_condition_flags; /* * Flags for ace_condition_token.flags field. * * The following flags from security claims are used: * * CLAIM_SECURITY_ATTRIBUTE_NON_INHERITABLE = 1 * CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE = 2 * * CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED = 1 << 30 * * The first two of these are used on the wire in * CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 structures, while the * latter is in an application specific range that is not * seen on the wire. It is used to indicate that a composite * token contains no duplicate values, which is supposed to * be true for composite values from claims (including from * resource attribute ACEs), but not literal composites. It's * expensive to check, so this flag helps us avoid extra work * can avoid doing it over and over if we remember. * * * CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR is set when a token * value on the stack is set from an attribute lookup. * * This is necessary because for binary relational operators * (MS-DTYP 2.4.4.17.6), the left-hand argument must be an * attribute lookup, but by the time we have come to the * operator that argument has been resolved into an ordinary * token. So we set the flag so the operator can know. */ const uint32 CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR = UINT32_C(1) << 31; /* * The maximum size of the conditional ACE conditions in the * binary form. There is an absolute limit of slightly less * than 64k, as the security descriptor, the ACL, and the ace * all have 16 bit length fields, and each adds some overhead. * * In practice, a couple of hundred bytes would do, and people * making extremely large conditional expressions probably * don't have good intentions. */ const int CONDITIONAL_ACE_MAX_LENGTH = 10000; /* * CONDITIONAL_ACE_MAX_TOKENS is another arbitrarily chosen * number used to allocate token arrays and stacks. * * The relationship between the number of tokens and the byte * length is variable, depending on the nature of the * conditions. An operator token takes up one byte in the * binary format (which CONDITIONAL_ACE_MAX_LENGTH above * measures), an integer 10 bytes, and attributes and strings * at least two bytes per character plus four for the length. * SIDs are stored as struct dom_sid, around sixty-eight * bytes, plus a four byte length field. */ const int CONDITIONAL_ACE_MAX_TOKENS = 2000; }