/*
 *  Unix SMB implementation.
 *  Functions for understanding conditional ACEs
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "replace.h"
#include "librpc/gen_ndr/ndr_security.h"
#include "librpc/gen_ndr/ndr_conditional_ace.h"
#include "librpc/gen_ndr/conditional_ace.h"
#include "libcli/security/security.h"
#include "libcli/security/conditional_ace.h"
#include "libcli/security/claims-conversions.h"
#include "lib/util/tsort.h"
#include "lib/util/debug.h"
#include "lib/util/bytearray.h"
#include "lib/util/talloc_stack.h"
#include "util/discard.h"
#include "lib/util/stable_sort.h"
/*
 * Conditional ACE logic truth tables.
 *
 * Conditional ACES use a ternary logic, with "unknown" as well as true and
 * false. The ultimate meaning of unknown depends on the context; in a deny
 * ace, unknown means yes, in an allow ace, unknown means no. That is, we
 * treat unknown results with maximum suspicion.
 *
 *   AND   true   false  unknown
 *  true     T      F      ?
 *  false    F      F      F
 *  unknown  ?      F      ?
 *
 *   OR    true   false  unknown
 *  true     T      T      T
 *  false    T      F      ?
 *  unknown  T      ?      ?
 *
 *   NOT
 *  true     F
 *  false    T
 *  unknown  ?
 *
 * This can be summed up by saying unknown values taint the result except in
 * the cases where short circuit evaluation could apply (true OR anything,
 * false AND anything, which hold their value).
 *
 * What counts as unknown
 *
 * - NULL attributes.
 * - certain comparisons between incompatible types
 *
 * What counts as false
 *
 * - zero
 * - empty strings
 *
 * An error means the entire expression is unknown.
 */


static bool check_integer_range(const struct ace_condition_token *tok)
{
	int64_t val = tok->data.int64.value;
	switch (tok->type) {
	case CONDITIONAL_ACE_TOKEN_INT8:
		if (val < -128 || val > 127) {
			return false;
		}
		break;
	case CONDITIONAL_ACE_TOKEN_INT16:
		if (val < INT16_MIN || val > INT16_MAX) {
			return false;
		}
		break;
	case CONDITIONAL_ACE_TOKEN_INT32:
		if (val < INT32_MIN || val > INT32_MAX) {
			return false;
		}
		break;
	case CONDITIONAL_ACE_TOKEN_INT64:
		/* val has these limits naturally */
		break;
	default:
		return false;
	}

	if (tok->data.int64.base != CONDITIONAL_ACE_INT_BASE_8 &&
	    tok->data.int64.base != CONDITIONAL_ACE_INT_BASE_10 &&
	    tok->data.int64.base != CONDITIONAL_ACE_INT_BASE_16) {
		return false;
	}
	if (tok->data.int64.sign != CONDITIONAL_ACE_INT_SIGN_POSITIVE &&
	    tok->data.int64.sign != CONDITIONAL_ACE_INT_SIGN_NEGATIVE &&
	    tok->data.int64.sign != CONDITIONAL_ACE_INT_SIGN_NONE) {
		return false;
	}
	return true;
}


static ssize_t pull_integer(TALLOC_CTX *mem_ctx,
			uint8_t *data, size_t length,
			struct ace_condition_int *tok)
{
	ssize_t bytes_used;
	enum ndr_err_code ndr_err;
	DATA_BLOB v = data_blob_const(data, length);
	struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
	if (ndr == NULL) {
		return -1;
	}
	ndr_err = ndr_pull_ace_condition_int(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		TALLOC_FREE(ndr);
		return -1;
	}
	bytes_used = ndr->offset;
	TALLOC_FREE(ndr);
	return bytes_used;
}

static ssize_t push_integer(uint8_t *data, size_t available,
			const struct ace_condition_int *tok)
{
	enum ndr_err_code ndr_err;
	DATA_BLOB v;
	ndr_err = ndr_push_struct_blob(&v, NULL,
				       tok,
				       (ndr_push_flags_fn_t)ndr_push_ace_condition_int);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return -1;
	}
	if (available < v.length) {
		talloc_free(v.data);
		return -1;
	}
	memcpy(data, v.data, v.length);
	talloc_free(v.data);
	return v.length;
}


static ssize_t pull_unicode(TALLOC_CTX *mem_ctx,
			uint8_t *data, size_t length,
			struct ace_condition_unicode *tok)
{
	ssize_t bytes_used;
	enum ndr_err_code ndr_err;
	DATA_BLOB v = data_blob_const(data, length);
	struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
	if (ndr == NULL) {
		return -1;
	}
	ndr_err = ndr_pull_ace_condition_unicode(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		TALLOC_FREE(ndr);
		return -1;
	}
	bytes_used = ndr->offset;
	TALLOC_FREE(ndr);
	return bytes_used;
}

static ssize_t push_unicode(uint8_t *data, size_t available,
			const struct ace_condition_unicode *tok)
{
	enum ndr_err_code ndr_err;
	DATA_BLOB v;
	ndr_err = ndr_push_struct_blob(&v, NULL,
				       tok,
				       (ndr_push_flags_fn_t)ndr_push_ace_condition_unicode);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return -1;
	}
	if (available < v.length) {
		talloc_free(v.data);
		return -1;
	}
	memcpy(data, v.data, v.length);
	talloc_free(v.data);
	return v.length;
}


static ssize_t pull_bytes(TALLOC_CTX *mem_ctx,
			  uint8_t *data, size_t length,
			  DATA_BLOB *tok)
{
	ssize_t bytes_used;
	enum ndr_err_code ndr_err;
	DATA_BLOB v = data_blob_const(data, length);
	struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
	if (ndr == NULL) {
		return -1;
	}
	ndr_err = ndr_pull_DATA_BLOB(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		TALLOC_FREE(ndr);
		return -1;
	}
	bytes_used = ndr->offset;
	talloc_free(ndr);
	return bytes_used;
}

static ssize_t push_bytes(uint8_t *data, size_t available,
			const DATA_BLOB *tok)
{
	size_t offset;
	enum ndr_err_code ndr_err;
	TALLOC_CTX *frame = talloc_stackframe();
	struct ndr_push *ndr = ndr_push_init_ctx(frame);
	if (ndr == NULL) {
		TALLOC_FREE(frame);
		return -1;
	}

	ndr_err = ndr_push_DATA_BLOB(ndr, NDR_SCALARS|NDR_BUFFERS, *tok);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		TALLOC_FREE(frame);
		return -1;
	}

	if (available < ndr->offset) {
		TALLOC_FREE(frame);
		return -1;
	}
	memcpy(data, ndr->data, ndr->offset);
	offset = ndr->offset;
	TALLOC_FREE(frame);
	return offset;
}

static ssize_t pull_sid(TALLOC_CTX *mem_ctx,
			uint8_t *data, size_t length,
			struct ace_condition_sid *tok)
{
	ssize_t bytes_used;
	enum ndr_err_code ndr_err;
	DATA_BLOB v = data_blob_const(data, length);
	struct ndr_pull *ndr = ndr_pull_init_blob(&v, mem_ctx);
	if (ndr == NULL) {
		return -1;
	}
	ndr->flags |= LIBNDR_FLAG_SUBCONTEXT_NO_UNREAD_BYTES;

	ndr_err = ndr_pull_ace_condition_sid(ndr, NDR_SCALARS|NDR_BUFFERS, tok);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		TALLOC_FREE(ndr);
		return -1;
	}
	bytes_used = ndr->offset;
	TALLOC_FREE(ndr);
	return bytes_used;
}

static ssize_t push_sid(uint8_t *data, size_t available,
			const struct ace_condition_sid *tok)
{
	enum ndr_err_code ndr_err;
	DATA_BLOB v;
	ndr_err = ndr_push_struct_blob(&v, NULL,
				       tok,
				       (ndr_push_flags_fn_t)ndr_push_ace_condition_sid);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return -1;
	}
	if (available < v.length) {
		talloc_free(v.data);
		return -1;
	}
	memcpy(data, v.data, v.length);
	talloc_free(v.data);
	return v.length;
}


static ssize_t pull_composite(TALLOC_CTX *mem_ctx,
			      uint8_t *data, size_t length,
			      struct ace_condition_composite *tok)
{
	size_t i, j;
	size_t alloc_length;
	size_t byte_size;
	struct ace_condition_token *tokens = NULL;
	if (length < 4) {
		return -1;
	}
	byte_size = PULL_LE_U32(data, 0);
	if (byte_size > length - 4) {
		return -1;
	}
	/*
	 * There is a list of other literal tokens (possibly including nested
	 * composites), which we will store in an array.
	 *
	 * This array can *only* be literals.
	 */
	alloc_length = byte_size;
	tokens = talloc_array(mem_ctx,
			      struct ace_condition_token,
			      alloc_length);
	if (tokens == NULL) {
		return -1;
	}
	byte_size += 4;
	i = 4;
	j = 0;
	while (i < byte_size) {
		struct ace_condition_token *el = &tokens[j];
		ssize_t consumed;
		uint8_t *el_data = NULL;
		size_t available;
		bool ok;
		*el = (struct ace_condition_token) { .type = data[i] };
		i++;

		el_data = data + i;
		available = byte_size - i;

		switch (el->type) {
		case CONDITIONAL_ACE_TOKEN_INT8:
		case CONDITIONAL_ACE_TOKEN_INT16:
		case CONDITIONAL_ACE_TOKEN_INT32:
		case CONDITIONAL_ACE_TOKEN_INT64:
			consumed = pull_integer(mem_ctx,
						el_data,
						available,
						&el->data.int64);
			ok = check_integer_range(el);
			if (! ok) {
				goto error;
			}
			break;
		case CONDITIONAL_ACE_TOKEN_UNICODE:
			consumed = pull_unicode(mem_ctx,
						el_data,
						available,
						&el->data.unicode);
			break;

		case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
			consumed = pull_bytes(mem_ctx,
					      el_data,
					      available,
					      &el->data.bytes);
			break;

		case CONDITIONAL_ACE_TOKEN_SID:
			consumed = pull_sid(mem_ctx,
					    el_data,
					    available,
					    &el->data.sid);
			break;

		case CONDITIONAL_ACE_TOKEN_COMPOSITE:
			DBG_ERR("recursive composite tokens in conditional "
				"ACEs are not currently supported\n");
			goto error;
		default:
			goto error;
		}

		if (consumed < 0 || consumed + i > length) {
			goto error;
		}
		i += consumed;
		j++;
		if (j == UINT16_MAX) {
			talloc_free(tokens);
			return -1;
		}
		if (j == alloc_length) {
			struct ace_condition_token *new_tokens = NULL;

			alloc_length += 5;
			new_tokens = talloc_realloc(mem_ctx,
						    tokens,
						    struct ace_condition_token,
						    alloc_length);

			if (new_tokens == NULL) {
				goto error;
			}
			tokens = new_tokens;
		}
	}
	tok->n_members = j;
	tok->tokens = tokens;
	return byte_size;
error:
	talloc_free(tokens);
	return -1;
}


static ssize_t push_composite(uint8_t *data, size_t length,
			      const struct ace_condition_composite *tok)
{
	size_t i;
	uint8_t *byte_length_ptr;
	size_t used = 0;
	if (length < 4) {
		return -1;
	}
	/*
	 * We have no idea what the eventual length will be, so we keep a
	 * pointer to write it in at the end.
	 */
	byte_length_ptr = data;
	PUSH_LE_U32(data, 0, 0);
	used = 4;

	for (i = 0; i < tok->n_members && used < length; i++) {
		struct ace_condition_token *el = &tok->tokens[i];
		ssize_t consumed;
		uint8_t *el_data = NULL;
		size_t available;
		bool ok;
		data[used] = el->type;
		used++;
		if (used == length) {
			/*
			 * used == length is not expected here; the token
			 * types that only have an opcode and no data are not
			 * literals that can be in composites.
			 */
			return -1;
		}
		el_data = data + used;
		available = length - used;

		switch (el->type) {
		case CONDITIONAL_ACE_TOKEN_INT8:
		case CONDITIONAL_ACE_TOKEN_INT16:
		case CONDITIONAL_ACE_TOKEN_INT32:
		case CONDITIONAL_ACE_TOKEN_INT64:
			ok = check_integer_range(el);
			if (! ok) {
				return -1;
			}
			consumed = push_integer(el_data,
						available,
						&el->data.int64);
			break;
		case CONDITIONAL_ACE_TOKEN_UNICODE:
			consumed = push_unicode(el_data,
						available,
						&el->data.unicode);
			break;

		case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
			consumed = push_bytes(el_data,
					      available,
					      &el->data.bytes);
			break;

		case CONDITIONAL_ACE_TOKEN_SID:
			consumed = push_sid(el_data,
					    available,
					    &el->data.sid);
			break;

		case CONDITIONAL_ACE_TOKEN_COMPOSITE:
			consumed = push_composite(el_data,
						  available,
						  &el->data.composite);
			break;

		default:
			return -1;
		}

		if (consumed < 0) {
			return -1;
		}
		used += consumed;
	}
	if (used > length) {
		return -1;
	}

	PUSH_LE_U32(byte_length_ptr, 0, used - 4);
	return used;
}

static ssize_t pull_end_padding(uint8_t *data, size_t length)
{
	/*
	 * We just check that we have the right kind of number of zero
	 * bytes. The blob must end on a multiple of 4. One zero byte
	 * has already been swallowed as tok->type, which sends us
	 * here, so we expect 1 or two more -- total padding is 0, 1,
	 * 2, or 3.
	 *
	 * zero is also called CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING.
	 */
	ssize_t i;
	if (length > 2) {
		return -1;
	}
	for (i = 0; i < length; i++) {
		if (data[i] != 0) {
			return -1;
		}
	}
	return length;
}


struct ace_condition_script *parse_conditional_ace(TALLOC_CTX *mem_ctx,
						   DATA_BLOB data)
{
	size_t i, j;
	struct ace_condition_token *tokens = NULL;
	size_t alloc_length;
	struct ace_condition_script *program = NULL;

	if (data.length < 4 ||
	    data.data[0] != 'a' ||
	    data.data[1] != 'r' ||
	    data.data[2] != 't' ||
	    data.data[3] != 'x') {
		/*
		 * lacks the "artx" conditional ace identifier magic.
		 * NULL returns will deny access.
		 */
		return NULL;
	}
	if (data.length > CONDITIONAL_ACE_MAX_LENGTH ||
	    (data.length & 3) != 0) {
		/*
		 * >= 64k or non-multiples of 4 are not possible in the ACE
		 * wire format.
		 */
		return NULL;
	}

	program = talloc(mem_ctx, struct ace_condition_script);
	if (program == NULL) {
		return NULL;
	}

	/*
	 * We will normally end up with fewer than data.length tokens, as
	 * values are stored in multiple bytes (all integers are 10 bytes,
	 * strings and attributes are utf16 + length, SIDs are SID-size +
	 * length, etc). But operators are one byte, so something like
	 * !(!(!(!(!(!(x)))))) -- where each '!(..)' is one byte -- will bring
	 * the number of tokens close to the number of bytes.
	 *
	 * This is all to say we're guessing a token length that hopes to
	 * avoid reallocs without wasting too much up front.
	 */
	alloc_length = data.length / 2 + 1;
	tokens = talloc_array(program,
			      struct ace_condition_token,
			      alloc_length);
	if (tokens == NULL) {
		TALLOC_FREE(program);
		return NULL;
	}

	i = 4;
	j = 0;
	while(i < data.length) {
		struct ace_condition_token *tok = &tokens[j];
		ssize_t consumed = 0;
		uint8_t *tok_data = NULL;
		size_t available;
		bool ok;
		tok->type = data.data[i];
		tok->flags = 0;
		i++;
		tok_data = data.data + i;
		available = data.length - i;

		switch (tok->type) {
		case CONDITIONAL_ACE_TOKEN_INT8:
		case CONDITIONAL_ACE_TOKEN_INT16:
		case CONDITIONAL_ACE_TOKEN_INT32:
		case CONDITIONAL_ACE_TOKEN_INT64:
			consumed = pull_integer(program,
						tok_data,
						available,
						&tok->data.int64);
			ok = check_integer_range(tok);
			if (! ok) {
				goto fail;
			}
			break;
		case CONDITIONAL_ACE_TOKEN_UNICODE:
			/*
			 * The next four are pulled as unicode, but are
			 *  processed as user attribute look-ups.
			 */
		case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
		case CONDITIONAL_ACE_USER_ATTRIBUTE:
		case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
		case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
			consumed = pull_unicode(program,
						tok_data,
						available,
						&tok->data.unicode);
			break;

		case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
			consumed = pull_bytes(program,
					      tok_data,
					      available,
					      &tok->data.bytes);
			break;

		case CONDITIONAL_ACE_TOKEN_SID:
			consumed = pull_sid(program,
					    tok_data,
					    available,
					    &tok->data.sid);
			break;

		case CONDITIONAL_ACE_TOKEN_COMPOSITE:
			consumed = pull_composite(program,
						  tok_data,
						  available,
						  &tok->data.composite);
			break;

		case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
			/*
			 * these require a SID or composite SID list operand,
			 * and we could check that now in most cases.
			 */
			break;
		/* binary relational operators */
		case CONDITIONAL_ACE_TOKEN_EQUAL:
		case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
		case CONDITIONAL_ACE_TOKEN_LESS_THAN:
		case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
		case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_CONTAINS:
		case CONDITIONAL_ACE_TOKEN_ANY_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
		case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
		/* unary logical operators */
		case CONDITIONAL_ACE_TOKEN_EXISTS:
		case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
		case CONDITIONAL_ACE_TOKEN_NOT:
		/* binary logical operators */
		case CONDITIONAL_ACE_TOKEN_AND:
		case CONDITIONAL_ACE_TOKEN_OR:
			break;
		case CONDITIONAL_ACE_TOKEN_INVALID_OR_PADDING:
			/* this is only valid at the end */
			consumed = pull_end_padding(tok_data,
						    available);
			j--; /* don't add this token */
			break;
		default:
			goto fail;
		}

		if (consumed < 0) {
			goto fail;
		}
		if (consumed + i < i || consumed + i > data.length) {
			goto fail;
		}
		i += consumed;
		j++;
		if (j == alloc_length) {
			alloc_length *= 2;
			tokens = talloc_realloc(program,
						tokens,
						struct ace_condition_token,
						alloc_length);
			if (tokens == NULL) {
				goto fail;
			}
		}
	}
	program->length = j;
	program->tokens = talloc_realloc(program,
					 tokens,
					 struct ace_condition_token,
					 program->length + 1);
	if (program->tokens == NULL) {
		goto fail;
	}
	/*
	 * When interpreting the program we will need a stack, which in the
	 * very worst case can be as deep as the program is long.
	 */
	program->stack = talloc_array(program,
				      struct ace_condition_token,
				      program->length + 1);
	if (program->stack == NULL) {
		goto fail;
	}

	return program;
  fail:
	talloc_free(program);
	return NULL;
  }


static bool claim_lookup_internal(
	TALLOC_CTX *mem_ctx,
	struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claim,
	struct ace_condition_token *result)
{
	bool ok = claim_v1_to_ace_token(mem_ctx, claim, result);
	return ok;
}


static bool resource_claim_lookup(
	TALLOC_CTX *mem_ctx,
	const struct ace_condition_token *op,
	const struct security_descriptor *sd,
	struct ace_condition_token *result)
{
	/*
	 * For a @Resource.attr, the claims come from a resource ACE
	 * in the object's SACL. That's why we need a security descriptor.
	 *
	 * If there is no matching resource ACE, a NULL result is returned,
	 * which should compare UNKNOWN to anything. The NULL will have the
	 * CONDITIONAL_ACE_FLAG_NULL_MEANS_ERROR flag set if it seems failure
	 * is not simply due to the sought claim not existing. This is useful for
	 * the Exists and Not_Exists operators.
	 */
	size_t i;
	struct ace_condition_unicode name;

	result->type = CONDITIONAL_ACE_SAMBA_RESULT_NULL;

	if (op->type != CONDITIONAL_ACE_RESOURCE_ATTRIBUTE) {
		/* what are we even doing here? */
		result->type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR;
		return false;
	}

	name = op->data.resource_attr;

	if (sd->sacl == NULL) {
		DBG_NOTICE("Resource attribute ACE '%s' not found, "
			   "because there is no SACL\n",
			   name.value);
		return true;
	}

	for (i = 0; i < sd->sacl->num_aces; i++) {
		struct security_ace *ace = &sd->sacl->aces[i];
		bool ok;

		if (ace->type != SEC_ACE_TYPE_SYSTEM_RESOURCE_ATTRIBUTE) {
			continue;
		}
		if (strcasecmp_m(name.value,
				 ace->coda.claim.name) != 0) {
			continue;
		}
		/* this is the one */
		ok = claim_lookup_internal(mem_ctx, &ace->coda.claim, result);
		if (ok) {
			return true;
		}
	}
	DBG_NOTICE("Resource attribute ACE '%s' not found.\n",
		   name.value);
	return false;
}


static bool token_claim_lookup(
	TALLOC_CTX *mem_ctx,
	const struct security_token *token,
	const struct ace_condition_token *op,
	struct ace_condition_token *result)
{
	/*
	 * The operator has an attribute name; if there is a claim of
	 * the right type with that name, that is returned as the result.
	 *
	 * XXX what happens otherwise? NULL result?
	 */
	struct CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 *claims = NULL;
	size_t num_claims;
	bool ok;
	const struct ace_condition_unicode *name = NULL;
	size_t i;

	result->type = CONDITIONAL_ACE_SAMBA_RESULT_NULL;

	switch (op->type) {
	case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
		claims = token->local_claims;
		num_claims = token->num_local_claims;
		name = &op->data.local_attr;
		break;
	case CONDITIONAL_ACE_USER_ATTRIBUTE:
		claims = token->user_claims;
		num_claims = token->num_user_claims;
		name = &op->data.user_attr;
		break;
	case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
		claims = token->device_claims;
		num_claims = token->num_device_claims;
		name = &op->data.device_attr;
		break;
	default:
		DBG_WARNING("Conditional ACE claim lookup got bad arg type %u\n",
			    op->type);
		result->type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR;
		return false;
	}

	if (num_claims == 0) {
		DBG_NOTICE("There are no type %u claims\n", op->type);
		return false;
	}
	if (claims == NULL) {
		DBG_ERR("Type %u claim list unexpectedly NULL!\n", op->type);
		result->type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR;
		return false;
	}
	/*
	 * Loop backwards: a later claim will override an earlier one with the
	 * same name.
	 */
	for (i = num_claims - 1; i < num_claims; i--) {
		if (claims[i].name == NULL) {
			DBG_ERR("claim %zu has no name!\n", i);
			continue;
		}
		if (strcasecmp_m(claims[i].name, name->value) == 0) {
			/* this is the one */
			ok = claim_lookup_internal(mem_ctx, &claims[i], result);
			return ok;
		}
	}
	DBG_NOTICE("Claim not found\n");
	return false;
}




static bool member_lookup(
	const struct security_token *token,
	const struct ace_condition_token *op,
	const struct ace_condition_token *arg,
	struct ace_condition_token *result)
{
	/*
	 * We need to compare the lists of SIDs in the token with the
	 * SID[s] in the argument. There are 8 combinations of
	 * operation, depending on whether we want to match all or any
	 * of the SIDs, whether we're using the device SIDs or user
	 * SIDs, and whether the operator name starts with "Not_".
	 *
	 * _MEMBER_OF               User has all operand SIDs
	 * _DEVICE_MEMBER_OF        Device has all operand SIDs
	 * _MEMBER_OF_ANY           User has one or more operand SIDs
	 * _DEVICE_MEMBER_OF_ANY    Device has one or more operand SIDs
	 *
	 * NOT_* has the effect of !(the operator without NOT_).
	 *
	 * The operand can either be a composite of SIDs or a single SID.
	 * This adds an additional branch.
	 */
	bool match = false;
	bool it_is_a_not_op;
	bool it_is_an_any_op;
	bool it_is_a_device_op;
	bool arg_is_a_single_sid;
	struct dom_sid *sid_array = NULL;
	size_t num_sids, i, j;
	const struct dom_sid *sid = NULL;

	result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
	result->data.result.value = ACE_CONDITION_UNKNOWN;

	switch (arg->type) {
	case CONDITIONAL_ACE_TOKEN_SID:
		arg_is_a_single_sid = true;
		break;
	case CONDITIONAL_ACE_TOKEN_COMPOSITE:
		arg_is_a_single_sid = false;
		break;
	default:
		DBG_WARNING("Conditional ACE Member_Of got bad arg type %u\n",
			    arg->type);
		return false;
	}

	switch (op->type) {
	case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
	case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
		it_is_a_not_op = true;
		it_is_a_device_op = false;
		break;
	case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
	case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
		it_is_a_not_op = true;
		it_is_a_device_op = true;
		break;
	case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
	case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
		it_is_a_not_op = false;
		it_is_a_device_op = false;
		break;
	case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
	case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
		it_is_a_not_op = false;
		it_is_a_device_op = true;
		break;
	default:
		DBG_WARNING("Conditional ACE Member_Of got bad op type %u\n",
			    op->type);
		return false;
	}

	switch (op->type) {
	case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
	case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
	case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
	case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
		it_is_an_any_op = true;
		break;
	default:
		it_is_an_any_op = false;
	}

	if (it_is_a_device_op) {
		sid_array = token->device_sids;
		num_sids = token->num_device_sids;
	} else {
		sid_array = token->sids;
		num_sids = token->num_sids;
	}

	if (arg_is_a_single_sid) {
		/*
		 * In this case the any and all operations are the
		 * same.
		 */
		sid = &arg->data.sid.sid;
		match = false;
		for (i = 0; i < num_sids; i++) {
			match = dom_sid_equal(sid, &sid_array[i]);
			if (match) {
				break;
			}
		}
		if (it_is_a_not_op) {
			match = ! match;
		}
		if (match) {
			result->data.result.value = ACE_CONDITION_TRUE;
		} else {
			result->data.result.value = ACE_CONDITION_FALSE;
		}
		return true;
	}

	/* This is a composite list (hopefully of SIDs) */
	if (arg->data.composite.n_members == 0) {
		DBG_WARNING("Conditional ACE Member_Of argument is empty\n");
		return false;
	}

	for (j = 0; j < arg->data.composite.n_members; j++) {
		const struct ace_condition_token *member =
			&arg->data.composite.tokens[j];
		if (member->type != CONDITIONAL_ACE_TOKEN_SID) {
			DBG_WARNING("Conditional ACE Member_Of argument contains "
				    "non-sid element [%zu]: %u\n",
				    j, member->type);
			return false;
		}
		sid = &member->data.sid.sid;
		match = false;
		for (i = 0; i < num_sids; i++) {
			match = dom_sid_equal(sid, &sid_array[i]);
			if (match) {
				break;
			}
		}
		if (it_is_an_any_op) {
			if (match) {
				/* we have matched one SID, which is enough */
				goto apply_not;
			}
		} else { /* an all op */
			if (! match) {
				/* failing one is enough */
				goto apply_not;
			}
		}
	}
	/*
	 * Reaching the end of that loop means either:
	 * 1. it was an ALL op and we never failed to find one, or
	 * 2. it was an ANY op, and we didn't find one.
	 */
	match = !it_is_an_any_op;

  apply_not:
	if (it_is_a_not_op) {
		match = ! match;
	}
	if (match) {
		result->data.result.value = ACE_CONDITION_TRUE;
	} else {
		result->data.result.value = ACE_CONDITION_FALSE;
	}

	return true;
}


static bool ternary_value(
	const struct ace_condition_token *arg,
	struct ace_condition_token *result)
{
	/*
	 * Find the truth value of the argument, stored in the result token.
	 *
	 * A return value of false means the operation is invalid, and the
	 * result is undefined.
	 */
	if (arg->type == CONDITIONAL_ACE_SAMBA_RESULT_BOOL) {
		/* pass through */
		*result = *arg;
		return true;
	}

	result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
	result->data.result.value = ACE_CONDITION_UNKNOWN;

	if (IS_INT_TOKEN(arg)) {
		/* zero is false */
		if (arg->data.int64.value == 0) {
			result->data.result.value = ACE_CONDITION_FALSE;
		} else {
			result->data.result.value = ACE_CONDITION_TRUE;
		}
		return true;
	}
	if (arg->type == CONDITIONAL_ACE_TOKEN_UNICODE) {
		/* empty is false */
		if (arg->data.unicode.value[0] == '\0') {
			result->data.result.value = ACE_CONDITION_FALSE;
		} else {
			result->data.result.value = ACE_CONDITION_TRUE;
		}
		return true;
	}

	/*
	 * everything else in UNKNOWN. This includes NULL values (i.e. an
	 * unsuccessful look-up).
	 */
	result->data.result.value = ACE_CONDITION_UNKNOWN;
	return true;
}

static bool not_operator(
	const struct ace_condition_token *arg,
	struct ace_condition_token *result)
{
	bool ok;
	if (IS_LITERAL_TOKEN(arg)) {
		/*
		 * Logic operators don't work on literals.
		 */
		return false;
	}

	ok = ternary_value(arg, result);
	if (! ok) {
		return false;
	}
	if (result->data.result.value == ACE_CONDITION_FALSE) {
		result->data.result.value = ACE_CONDITION_TRUE;
	} else if (result->data.result.value == ACE_CONDITION_TRUE) {
		result->data.result.value = ACE_CONDITION_FALSE;
	}
	/* unknown stays unknown */
	return true;
}


static bool unary_logic_operator(
	TALLOC_CTX *mem_ctx,
	const struct security_token *token,
	const struct ace_condition_token *op,
	const struct ace_condition_token *arg,
	const struct security_descriptor *sd,
	struct ace_condition_token *result)
{

	bool ok;
	bool found;
	struct ace_condition_token claim = {
		.type = CONDITIONAL_ACE_SAMBA_RESULT_ERROR
	};
	if (op->type == CONDITIONAL_ACE_TOKEN_NOT) {
		return not_operator(arg, result);
	}
	result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
	result->data.result.value = ACE_CONDITION_UNKNOWN;

	/*
	 * Not_Exists and Exists require the same work, except we negate the
	 * answer in one case. From [MS-DTYP] 2.4.4.17.7:
	 *
	 *  If the type of the operand is "Local Attribute"
         *    If the value is non-null return TRUE
	 *    Else return FALSE
	 *  Else if the type of the operand is "Resource Attribute"
         *    Return TRUE if value is non-null; FALSE otherwise.
	 *  Else return Error
	 */
	switch (op->type) {
	case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
		ok = token_claim_lookup(mem_ctx, token, arg, &claim);
		/*
		 * "not ok" usually means a failure to find the attribute,
		 * which is the false condition and not an error.
		 *
		 * XXX or do we need an extra flag?
		 */
		break;
	case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
		ok = resource_claim_lookup(mem_ctx, arg, sd, &claim);
		break;
	default:
		return false;
	}

	/*
	 *
	 */

	if (claim.type != CONDITIONAL_ACE_SAMBA_RESULT_NULL) {
		found = true;
	} else if (ok) {
		found = false;
	} else {
		return false;
	}



	if (op->type == CONDITIONAL_ACE_TOKEN_NOT_EXISTS) {
		found = ! found;
	} else if (op->type != CONDITIONAL_ACE_TOKEN_EXISTS) {
		/* should not get here */
		return false;
	}

	result->data.result.value = found ? ACE_CONDITION_TRUE: ACE_CONDITION_FALSE;
	return true;
}



static bool binary_logic_operator(
	const struct security_token *token,
	const struct ace_condition_token *op,
	const struct ace_condition_token *lhs,
	const struct ace_condition_token *rhs,
	struct ace_condition_token *result)
{
	struct ace_condition_token at, bt;
	int a, b;
	bool ok;

	result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
	result->data.result.value = ACE_CONDITION_UNKNOWN;

	if (IS_LITERAL_TOKEN(lhs) || IS_LITERAL_TOKEN(rhs)) {
		/*
		 * Logic operators don't work on literals.
		 */
		return false;
	}

	ok = ternary_value(lhs, &at);
	if (! ok) {
		return false;
	}
	ok = ternary_value(rhs, &bt);
	if (! ok) {
		return false;
	}
	a = at.data.result.value;
	b = bt.data.result.value;

	if (op->type == CONDITIONAL_ACE_TOKEN_AND) {
		/*
		 *   AND   true   false  unknown
		 *  true     T      F      ?
		 *  false    F      F      F
		 *  unknown  ?      F      ?
		 *
		 * unknown unless BOTH true or EITHER false
		 */
		if (a == ACE_CONDITION_TRUE &&
		    b == ACE_CONDITION_TRUE) {
			result->data.result.value = ACE_CONDITION_TRUE;
			return true;
		}
		if (a == ACE_CONDITION_FALSE ||
		    b == ACE_CONDITION_FALSE) {
			result->data.result.value = ACE_CONDITION_FALSE;
			return true;
		}
		/*
		 * Neither value is False, so the result is Unknown,
		 * as set at the start of this function.
		 */
		return true;
	}
	/*
	 *   OR    true   false  unknown
	 *  true     T      T      T
	 *  false    T      F      ?
	 *  unknown  T      ?      ?
	 *
	 * unknown unless EITHER true or BOTH false
	 */
	if (a == ACE_CONDITION_TRUE ||
	    b == ACE_CONDITION_TRUE) {
			result->data.result.value = ACE_CONDITION_TRUE;
			return true;
	}
	if (a == ACE_CONDITION_FALSE &&
	    b == ACE_CONDITION_FALSE) {
		result->data.result.value = ACE_CONDITION_FALSE;
		return true;
	}
	return true;
}


static bool tokens_are_comparable(const struct ace_condition_token *op,
				  const struct ace_condition_token *lhs,
				  const struct ace_condition_token *rhs)
{
	uint64_t n;
	/*
	 * we can't compare different types *unless* they are both
	 * integers, or one is a bool and the other is an integer 0 or
	 * 1, and the operator is == or != (or NULL, which for convenience,
	 * is treated as ==).
	 */
	//XXX actually it says "literal integers", do we need to check flags?
	if (lhs->type == rhs->type) {
		return true;
	}

	if (IS_INT_TOKEN(lhs) && IS_INT_TOKEN(rhs)) {
		/* don't block e.g. comparing an int32 to an int64 */
		return true;
	}

	/* is it == or != */
	if (op != NULL &&
	    op->type != CONDITIONAL_ACE_TOKEN_EQUAL &&
	    op->type != CONDITIONAL_ACE_TOKEN_NOT_EQUAL) {
		return false;
	}
	/* is one a bool and the other an int? */
	if (IS_INT_TOKEN(lhs) && IS_BOOL_TOKEN(rhs)) {
		n = lhs->data.int64.value;
	} else if (IS_INT_TOKEN(rhs) && IS_BOOL_TOKEN(lhs)) {
		n = rhs->data.int64.value;
	} else {
		return false;
	}
	if (n == 0 || n == 1) {
		return true;
	}
	return false;
}


static bool cmp_to_result(const struct ace_condition_token *op,
			  struct ace_condition_token *result,
			  int cmp)
{
	bool answer;
	switch (op->type) {
	case CONDITIONAL_ACE_TOKEN_EQUAL:
		answer = cmp == 0;
		break;
	case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
		answer = cmp != 0;
		break;
	case CONDITIONAL_ACE_TOKEN_LESS_THAN:
		answer = cmp < 0;
		break;
	case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
		answer = cmp <= 0;
		break;
	case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
		answer = cmp > 0;
		break;
	case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
		answer = cmp >= 0;
		break;
	default:
		result->data.result.value = ACE_CONDITION_UNKNOWN;
		return false;
	}
	result->data.result.value = \
		answer ? ACE_CONDITION_TRUE : ACE_CONDITION_FALSE;
	return true;
}



static bool compare_unicode(const struct ace_condition_token *op,
			    const struct ace_condition_token *lhs,
			    const struct ace_condition_token *rhs,
			    int *cmp)
{
	struct ace_condition_unicode a = lhs->data.unicode;
	struct ace_condition_unicode b = rhs->data.unicode;
	/*
	 * Comparison is case-insensitive UNLESS the claim structure
	 * has the case-sensitive flag, which is passed through as a
	 * flag on the token. Usually only the LHS is a claim value,
	 * but in the event that they both are, we allow either to
	 * request case-sensitivity.
	 *
	 * For greater than and less than, the sort order is utf-8 order,
	 * which is not exactly what Windows does, but we don't sort like
	 * Windows does anywhere else either.
	 */
	uint8_t flags = lhs->flags | rhs->flags;
	if (flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE) {
		*cmp = strcmp(a.value, b.value);
	} else {
		*cmp = strcasecmp_m(a.value, b.value);
	}
	return true;
}


static bool compare_bytes(const struct ace_condition_token *op,
			  const struct ace_condition_token *lhs,
			  const struct ace_condition_token *rhs,
			  int *cmp)
{
	DATA_BLOB a = lhs->data.bytes;
	DATA_BLOB b = rhs->data.bytes;
	*cmp = data_blob_cmp(&a, &b);
	return true;
}


static bool compare_sids(const struct ace_condition_token *op,
			 const struct ace_condition_token *lhs,
			 const struct ace_condition_token *rhs,
			 int *cmp)
{
	*cmp = dom_sid_compare(&lhs->data.sid.sid,
			       &rhs->data.sid.sid);
	return true;
}


static bool compare_ints(const struct ace_condition_token *op,
			 const struct ace_condition_token *lhs,
			 const struct ace_condition_token *rhs,
			 int *cmp)
{
	int64_t a = lhs->data.int64.value;
	int64_t b = rhs->data.int64.value;

	if (a < b) {
		*cmp = -1;
	} else if (a == b) {
		*cmp = 0;
	} else {
		*cmp = 1;
	}
	return true;
}


static bool compare_bools(const struct ace_condition_token *op,
			  const struct ace_condition_token *lhs,
			  const struct ace_condition_token *rhs,
			  int *cmp)
{
	bool ok;
	struct ace_condition_token a, b;
	*cmp = -1;

	if (IS_LITERAL_TOKEN(lhs)) {
		/*
		 * we can compare a boolean LHS to a literal RHS, but not
		 * vice versa
		 */
		return false;
	}
	ok = ternary_value(lhs, &a);
	if (! ok) {
		return false;
	}
	ok = ternary_value(rhs, &b);
	if (! ok) {
		return false;
	}
	if (a.data.result.value == ACE_CONDITION_UNKNOWN ||
	    b.data.result.value == ACE_CONDITION_UNKNOWN) {
		return false;
	}

	switch (op->type) {
	case CONDITIONAL_ACE_TOKEN_EQUAL:
	case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
		*cmp = a.data.result.value - b.data.result.value;
		break;
	default:
		/* we are not allowing non-equality comparisons with bools */
		return false;
	}
	return true;
}


static bool simple_relational_operator(const struct ace_condition_token *op,
				       const struct ace_condition_token *lhs,
				       const struct ace_condition_token *rhs,
				       int *cmp);


struct composite_sort_context {
	bool failed;
};

static int composite_sort_cmp(const struct ace_condition_token *lhs,
			      const struct ace_condition_token *rhs,
			      struct composite_sort_context *ctx)
{
	bool ok;
	int cmp = -1;
	/*
	 * simple_relational_operator uses the operator token only to
	 * decide whether the comparison is allowed for the type. In
	 * particular, boolean result and composite arguments can only
	 * be used with equality operators. We want those to fail (we
	 * should not see them here, remembering that claim booleans
	 * become composite integers), so we use a non-equality op.
	 */
	static const struct ace_condition_token op = {
		.type = CONDITIONAL_ACE_TOKEN_LESS_THAN
	};

	ok = simple_relational_operator(&op, lhs, rhs, &cmp);
	if (ok) {
		return cmp;
	}
	/*
	 * This sort isn't going to work out, but the sort function
	 * will only find out at the end.
	 */
	ctx->failed = true;
	return cmp;
}


/*
 * Return a sorted copy of the composite tokens array.
 *
 * The copy is shallow, so the actual string pointers are the same, which is
 * fine for the purposes of comparison.
 */

static struct ace_condition_token *composite_sorted_copy(
	TALLOC_CTX *mem_ctx,
	const struct ace_condition_composite *c,
	bool case_sensitive)
{
	struct ace_condition_token *copy = NULL;
	bool ok;
	size_t  i;
	struct composite_sort_context sort_ctx = {
		.failed = false
	};

	/*
	 * Case sensitivity is a bit tricky. Each token can have a flag saying
	 * it should be sorted case-sensitively and when comparing two tokens,
	 * we should respect this flag on either side. The flag can only come
	 * from claims (including resource attribute ACEs), and as there is only
	 * one flag per claim, it must apply the same to all members (in fact we
	 * don't set it on the members, only the composite). So to be sure we
	 * sort in the way we want, we might need to set the flag on all the
	 * members of the copy *before* sorting it.
	 *
	 * When it comes to comparing two composites, we want to be
	 * case-sensitive if either side has the flag. This can have odd
	 * effects. Think of these RA claims:
	 *
	 *   (RA;;;;;WD;("foo",TS,0,"a","A"))
	 *   (RA;;;;;WD;("bar",TS,2,"a","A"))    <-- 2 is the case-sensitive flag
	 *   (RA;;;;;WD;("baz",TS,0,"a"))
	 *
	 * (@Resource.foo == @Resource.bar) is true
	 * (@Resource.bar == @Resource.foo) is true
	 * (@Resource.bar == @Resource.bar) is true
	 * (@Resource.foo == @Resource.foo) is an error (duplicate values on LHS)
	 * (@Resource.baz == @Resource.foo) is true (RHS case-folds down)
	 * (@Resource.baz == @Resource.bar) is false
	 * (@Resource.bar == {"A", "a"})    is true
	 * (@Resource.baz == {"A", "a"})    is true
	 * (@Resource.foo == {"A", "a"})    is an error
	 */
	copy = talloc_array(mem_ctx, struct ace_condition_token, c->n_members);
	if (copy == NULL) {
		return NULL;
	}
	memcpy(copy, c->tokens, sizeof(struct ace_condition_token) * c->n_members);

	if (case_sensitive) {
		for (i = 0; i < c->n_members; i++) {
			c->tokens[i].flags |= CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
		}
	}

	ok =  stable_sort_talloc_r(mem_ctx,
				   copy,
				   c->n_members,
				   sizeof(struct ace_condition_token),
				   (samba_compare_with_context_fn_t)composite_sort_cmp,
				   &sort_ctx);

	if (!ok || sort_ctx.failed) {
		DBG_NOTICE("composite sort of %"PRIu32" members failed\n",
			   c->n_members);
		TALLOC_FREE(copy);
		return NULL;
	}
	return copy;
}


/*
 * This is a helper for compare composites.
 */
static bool compare_composites_via_sort(const struct ace_condition_token *lhs,
					const struct ace_condition_token *rhs,
					int *cmp)
{
	const struct ace_condition_composite *lc = &lhs->data.composite;
	const struct ace_condition_composite *rc = &rhs->data.composite;
	size_t i;
	TALLOC_CTX *tmp_ctx = NULL;
	bool ok;
	int cmp_pair;
	bool case_sensitive, rhs_case_sensitive;
	bool rhs_sorted;
	struct ace_condition_token *ltok = lc->tokens;
	struct ace_condition_token *rtok = rc->tokens;
	static const struct ace_condition_token eq = {
		.type = CONDITIONAL_ACE_TOKEN_EQUAL
	};
	*cmp = -1;
	if (lc->n_members == 0 ||
	    rc->n_members < lc->n_members) {
		/* we should not have got this far */
		return false;
	}

	tmp_ctx = talloc_new(NULL);
	if (tmp_ctx == NULL) {
		return false;
	}

	case_sensitive = lhs->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
	rhs_case_sensitive = rhs->flags & CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE;
	rhs_sorted = rhs->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED;

	if (lc->tokens[0].type != CONDITIONAL_ACE_TOKEN_UNICODE) {
		/*
		 * All LHS tokens are the same type (because it is a
		 * claim), and that type is not one that cares about
		 * case, so nor do we.
		 */
		case_sensitive = false;
	} else if (case_sensitive == rhs_case_sensitive) {
		/* phew, no extra work */
	} else if (case_sensitive) {
		/* trigger a sorted copy */
		rhs_sorted = false;
	} else if (rhs_case_sensitive) {
		/*
		 * Do we need to rescan for uniqueness, given the new
		 * comparison function? No! The strings were already
		 * unique in the looser comparison, and now they can
		 * only be more so. The number of unique values can't
		 * change, just their order.
		 */
		case_sensitive = true;
		ltok = composite_sorted_copy(tmp_ctx, lc, case_sensitive);
		if (ltok == NULL) {
			DBG_WARNING("sort of LHS failed\n");
			goto error;
		}
	}

	if (! rhs_sorted) {
		/*
		 * we need an RHS sorted copy (it's a literal, or
		 * there was a case sensitivity disagreement).
		 */
		rtok = composite_sorted_copy(tmp_ctx, rc, case_sensitive);
		if (rtok == NULL) {
			DBG_WARNING("sort of RHS failed\n");
			goto error;
		}
	}
	/*
	 * Each member of LHS must match one or more members of RHS.
	 * Each member of RHS must match at least one of LHS.
	 *
	 * If they are the same length we can compare directly, so let's get
	 * rid of duplicates in RHS. This can only happen with literal
	 * composites.
	 */
	if (rc->n_members > lc->n_members) {
		size_t gap = 0;
		for (i = 1; i < rc->n_members; i++) {
			ok = simple_relational_operator(&eq,
							&rtok[i - 1],
							&rtok[i],
							&cmp_pair);
			if (! ok) {
				goto error;
			}
			if (cmp_pair == 0) {
				gap++;
			}
			if (gap != 0) {
				rtok[i - gap] = rtok[i];
			}
		}
		if (rc->n_members - lc->n_members != gap) {
			/*
			 * There were too many or too few duplicates to account
			 * for the difference, and no further comparison is
			 * necessary.
			 */
			goto not_equal;
		}
	}
	/*
	 * OK, now we know LHS and RHS are the same length and sorted in the
	 * same way, so we can just iterate over them and check each pair.
	 */

	for (i = 0; i < lc->n_members; i++) {
		ok = simple_relational_operator(&eq,
						&ltok[i],
						&rtok[i],
						&cmp_pair);
		if (! ok){
			goto error;
		}
		if (cmp_pair != 0) {
			goto not_equal;
		}
	}

	*cmp = 0;

not_equal:
	TALLOC_FREE(tmp_ctx);
	return true;
error:
	TALLOC_FREE(tmp_ctx);
	return false;
}


static bool composite_is_comparable(const struct ace_condition_token *tok,
				    const struct ace_condition_token *comp)
{
	/*
	 * Are all members of the composite comparable to the token?
	 */
	size_t i;
	const struct ace_condition_composite *rc = &comp->data.composite;
	size_t n = rc->n_members;

	if ((comp->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED) &&
	    n > 1) {
		/*
		 * all members are known to be the same type, so we
		 * can just check one.
		 */
		n = 1;
	}

	for (i = 0; i < n; i++) {
		if (! tokens_are_comparable(NULL,
					    tok,
					    &rc->tokens[i])) {
			DBG_NOTICE("token type %u !=  composite type %u\n",
				   tok->type, rc->tokens[i].type);
			return false;
		}
	}
	return true;
}


static bool compare_composites(const struct ace_condition_token *op,
			       const struct ace_condition_token *lhs,
			       const struct ace_condition_token *rhs,
			       int *cmp)
{
	/*
	 * This is for comparing multivalued sets, which includes
	 * conditional ACE composites and claim sets. Because these
	 * are sets, there are no < and > operations, just equality or
	 * otherwise.
	 *
	 * Claims are true sets, while composites are multisets --
	 * duplicate values are allowed -- but these are reduced to
	 * sets in evaluation, and the number of duplicates has no
	 * effect in comparisons. Resource attribute ACEs live in an
	 * intermediate state -- they can contain duplicates on the
	 * wire and as ACE structures, but as soon as they are
	 * evaluated as claims their values must be unique. Windows
	 * will treat RA ACEs with duplicate values as not existing,
	 * rather than as UNKNOWN (This is significant for the Exists
	 * operator). Claims can have a case-sensitive flags set,
	 * meaning they must be compared case-sensitively.
	 *
	 * Some good news is that the LHS of a comparison must always
	 * be a claim. That means we can assume it has unique values
	 * when it comes to pairwise comparisons. Using the magic of
	 * flags, we try to check this only once per claim.
	 *
	 * Conditional ACE composites, which can have duplicates (and
	 * mixed types), can only be on the RHS.
	 *
	 * To summarise:
	 *
	 * {a, b}    vs {a, b}        equal
	 * { }       vs { }           equal
	 * {a, b}    vs {b, a}        equal
	 * {a, b}    vs {a, c}        not equal
	 * {a, b}    vs {a, a, b}     equal
	 * {b, a}    vs {a, b, a}     equal
	 * {a, b}    vs {a, a, b, c}  not equal
	 * {a, b, a} vs {a, b}        should not happen, error
	 * {a, b, a} vs {a, b, a}     should not happen, error
	 *
	 * mixed types:
	 * {1, 2}    vs {1, "2"}      error
	 * {1, "2"}  vs {1, "2"}      should not happen, error
	 *
	 * case sensitivity (*{ }* indicates case-sensitive flag):
	 *
	 *  {"a", "b"}  vs  {"a", "B"}      equal
	 *  {"a", "b"}  vs *{"a", "B"}*     not equal
	 * *{"a", "b"}* vs  {"a", "B"}      not equal
	 * *{"a", "A"}* vs  {"a", "A"}      equal (if RHS is composite)
	 *  {"a", "A"}  vs *{"a", "A"}*     impossible (LHS is not unique)
	 * *{"a"}*      vs  {"a", "A"}      not equal
	 *
	 * The naive approach is of course O(n * m) with an additional O(n²)
	 * if the LHS values are not known to be unique (that is, in resource
	 * attribute claims). We want to avoid that with big sets.
	 */
	const struct ace_condition_composite *lc = &lhs->data.composite;
	const struct ace_condition_composite *rc = &rhs->data.composite;
	bool ok;

	if (!(lhs->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED)) {
		/*
		 * The LHS needs to be a claim, and it should have gone
		 * through claim_v1_check_and_sort() to get here.
		 */
		*cmp = -1;
		return false;
	}

	/* if one or both are empty, the answer is easy */
	if (lc->n_members == 0) {
		if (rc->n_members == 0) {
			*cmp = 0;
			return true;
		}
		*cmp = -1;
		return true;
	}
	if (rc->n_members == 0) {
		*cmp = -1;
		return true;
	}

	/*
	 * LHS must be a claim, so it must be unique, so if there are
	 * fewer members on the RHS, we know they can't be equal.
	 *
	 * If you think about it too much, you might think this is
	 * affected by case sensitivity, but it isn't. One side can be
	 * infected by case-sensitivity by the other, but that can't
	 * shrink the number of elements on the RHS -- it can only
	 * make a literal {"a", "A"} have effective length 2 rather
	 * than 1.
	 *
	 * On the other hand, if the RHS is case sensitive, it must be
	 * a claim and unique in its own terms, and its finer-grained
	 * distinctions can't collapse members of the case sensitive
	 * LHS.
	 */
	if (lc->n_members > rc->n_members) {
		*cmp = -1;
		return composite_is_comparable(&lc->tokens[0], rhs);
	}

	/*
	 * It *could* be that RHS is also unique and we know it. In that
	 * case we can short circuit if RHS has more members. This is
	 * the case when both sides are claims.
	 *
	 * This is also not affected by case-senstivity.
	 */
	if (lc->n_members < rc->n_members &&
	    (rhs->flags & CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED)) {
		*cmp = -1;
		return composite_is_comparable(&lc->tokens[0], rhs);
	}

	ok = compare_composites_via_sort(lhs, rhs, cmp);
	if (! ok) {
		return false;
	}
	return true;
}


static bool simple_relational_operator(const struct ace_condition_token *op,
				       const struct ace_condition_token *lhs,
				       const struct ace_condition_token *rhs,
				       int *cmp)

{
	if (lhs->type != rhs->type) {
		if (! tokens_are_comparable(op, lhs, rhs)) {
			return false;
		}
	}
	switch (lhs->type) {
	case CONDITIONAL_ACE_TOKEN_INT8:
	case CONDITIONAL_ACE_TOKEN_INT16:
	case CONDITIONAL_ACE_TOKEN_INT32:
	case CONDITIONAL_ACE_TOKEN_INT64:
		if (rhs->type == CONDITIONAL_ACE_SAMBA_RESULT_BOOL) {
			return compare_bools(op, lhs, rhs, cmp);
		}
		return compare_ints(op, lhs, rhs, cmp);
	case CONDITIONAL_ACE_SAMBA_RESULT_BOOL:
		return compare_bools(op, lhs, rhs, cmp);
	case CONDITIONAL_ACE_TOKEN_UNICODE:
		return compare_unicode(op, lhs, rhs, cmp);
	case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
		return compare_bytes(op, lhs, rhs, cmp);
	case CONDITIONAL_ACE_TOKEN_SID:
		return compare_sids(op, lhs, rhs, cmp);
	case CONDITIONAL_ACE_TOKEN_COMPOSITE:
		return compare_composites(op, lhs, rhs, cmp);
	case CONDITIONAL_ACE_SAMBA_RESULT_NULL:
		/* leave the result unknown */
		return false;
	default:
		DBG_ERR("did not expect ace type %u\n", lhs->type);
		return false;
	}

	return false;
}


static bool find_in_composite(const struct ace_condition_token *tok,
			      struct ace_condition_composite candidates,
			      bool *answer)
{
	size_t i;
	int cmp;
	bool ok;
	const struct ace_condition_token equals = {
		.type = CONDITIONAL_ACE_TOKEN_EQUAL
	};

	*answer = false;

	for (i = 0; i < candidates.n_members; i++) {
		ok = simple_relational_operator(&equals,
						tok,
						&candidates.tokens[i],
						&cmp);
		if (! ok) {
			return false;
		}
		if (cmp == 0) {
			*answer = true;
			return true;
		}
	}
	return true;
}


static bool contains_operator(const struct ace_condition_token *lhs,
			      const struct ace_condition_token *rhs,
			      bool *answer)
{
	size_t i;
	bool ok;
	int cmp;
	const struct ace_condition_token equals = {
		.type = CONDITIONAL_ACE_TOKEN_EQUAL
	};

	/*
	 * All the required objects must be identical to something in
	 * candidates. But what do we mean by *identical*? We'll use
	 * the equality operator to decide that.
	 *
	 * Both the lhs or rhs can be solitary objects or composites.
	 * This makes it a bit fiddlier.
	 *
	 * NOTE: this operator does not take advantage of the
	 * CLAIM_SECURITY_ATTRIBUTE_UNIQUE_AND_SORTED flag. It could, but it
	 * doesn't.
	 */
	if (lhs->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
		struct ace_condition_composite candidates = lhs->data.composite;
		struct ace_condition_composite required;
		if (rhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
			return find_in_composite(rhs, candidates, answer);
		}
		required = rhs->data.composite;
		if (required.n_members == 0) {
			return false;
		}
		for (i = 0; i < required.n_members; i++) {
			const struct ace_condition_token *t = &required.tokens[i];
			ok = find_in_composite(t, candidates, answer);
			if (! ok) {
				return false;
			}
			if (! *answer) {
				/*
				 * one required item was not there,
				 * *answer is false
				 */
				return true;
			}
		}
		/* all required items are there, *answer will be true */
		return true;
	}
	/* LHS is a single item */
	if (rhs->type == CONDITIONAL_ACE_TOKEN_COMPOSITE) {
		/*
		 * There could be more than one RHS member that is
		 * equal to the single LHS value, so it doesn't help
		 * to compare lengths or anything.
		 */
		struct ace_condition_composite required = rhs->data.composite;
		if (required.n_members == 0) {
			return false;
		}
		for (i = 0; i < required.n_members; i++) {
			ok = simple_relational_operator(&equals,
							lhs,
							&required.tokens[i],
							&cmp);
			if (! ok) {
				return false;
			}
			if (cmp != 0) {
				/*
				 * one required item was not there,
				 * *answer is false
				 */
				*answer = false;
				return true;
			}
		}
		*answer = true;
		return true;
	}
	/* LHS and RHS are both single */
	ok = simple_relational_operator(&equals,
					lhs,
					rhs,
					&cmp);
	if (! ok) {
		return false;
	}
	*answer = (cmp == 0);
	return true;
}


static bool any_of_operator(const struct ace_condition_token *lhs,
			    const struct ace_condition_token *rhs,
			    bool *answer)
{
	size_t i;
	bool ok;
	int cmp;
	const struct ace_condition_token equals = {
		.type = CONDITIONAL_ACE_TOKEN_EQUAL
	};

	/*
	 * There has to be *some* overlap between the LHS and RHS.
	 * Both sides can be solitary objects or composites.
	 *
	 * We can exploit this symmetry.
	 */
	if (lhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
		const struct ace_condition_token *tmp = lhs;
		lhs = rhs;
		rhs = tmp;
	}
	if (lhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
		/* both singles */
		ok = simple_relational_operator(&equals,
						lhs,
						rhs,
						&cmp);
		if (! ok) {
			return false;
		}
		*answer = (cmp == 0);
		return true;
	}
	if (rhs->type != CONDITIONAL_ACE_TOKEN_COMPOSITE) {
		return find_in_composite(rhs, lhs->data.composite, answer);
	}
	/* both are composites */
	if (lhs->data.composite.n_members == 0) {
		return false;
	}
	for (i = 0; i < lhs->data.composite.n_members; i++) {
		ok = find_in_composite(&lhs->data.composite.tokens[i],
				       rhs->data.composite,
				       answer);
		if (! ok) {
			return false;
		}
		if (*answer) {
			/* We have found one match, which is enough. */
			return true;
		}
	}
	return true;
}


static bool composite_relational_operator(const struct ace_condition_token *op,
					  const struct ace_condition_token *lhs,
					  const struct ace_condition_token *rhs,
					  struct ace_condition_token *result)
{
	bool ok, answer;
	switch(op->type) {
	case CONDITIONAL_ACE_TOKEN_CONTAINS:
	case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
		ok = contains_operator(lhs, rhs, &answer);
		break;
	case CONDITIONAL_ACE_TOKEN_ANY_OF:
	case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
		ok = any_of_operator(lhs, rhs, &answer);
		break;
	default:
		return false;
	}
	if (!ok) {
		return false;
	}

	/* negate the NOTs */
	if (op->type == CONDITIONAL_ACE_TOKEN_NOT_CONTAINS ||
	    op->type == CONDITIONAL_ACE_TOKEN_NOT_ANY_OF)
	{
		answer = !answer;
	}

	if (answer) {
		result->data.result.value = ACE_CONDITION_TRUE;
	} else {
		result->data.result.value = ACE_CONDITION_FALSE;
	}
	return true;
}


static bool relational_operator(
	const struct security_token *token,
	const struct ace_condition_token *op,
	const struct ace_condition_token *lhs,
	const struct ace_condition_token *rhs,
	struct ace_condition_token *result)
{
	int cmp;
	bool ok;
	result->type = CONDITIONAL_ACE_SAMBA_RESULT_BOOL;
	result->data.result.value = ACE_CONDITION_UNKNOWN;

	if ((lhs->flags & CONDITIONAL_ACE_FLAG_TOKEN_FROM_ATTR) == 0) {
		/* LHS was not derived from an attribute */
		return false;
	}

	/*
	 * This first nested switch is ensuring that >, >=, <, <= are
	 * not being tried on tokens that are not numbers, strings, or
	 * octet strings. Equality operators are available for all types.
	 */
	switch (lhs->type) {
	case CONDITIONAL_ACE_TOKEN_INT8:
	case CONDITIONAL_ACE_TOKEN_INT16:
	case CONDITIONAL_ACE_TOKEN_INT32:
	case CONDITIONAL_ACE_TOKEN_INT64:
	case CONDITIONAL_ACE_TOKEN_UNICODE:
	case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
		break;
	default:
		switch(op->type) {
		case CONDITIONAL_ACE_TOKEN_LESS_THAN:
		case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
		case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
			return false;
		default:
			break;
		}
	}

	/*
	 * Dispatch according to operator type.
	 */
	switch (op->type) {
	case CONDITIONAL_ACE_TOKEN_EQUAL:
	case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
	case CONDITIONAL_ACE_TOKEN_LESS_THAN:
	case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
	case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
	case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
		ok = simple_relational_operator(op,
						lhs,
						rhs,
						&cmp);
		if (ok) {
			ok = cmp_to_result(op, result, cmp);
		}
		return ok;

	case CONDITIONAL_ACE_TOKEN_CONTAINS:
	case CONDITIONAL_ACE_TOKEN_ANY_OF:
	case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
	case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
		return composite_relational_operator(op,
						     lhs,
						     rhs,
						     result);
	default:
		return false;
	}
}


int run_conditional_ace(TALLOC_CTX *mem_ctx,
			const struct security_token *token,
			struct ace_condition_script *program,
			const struct security_descriptor *sd)
{
	size_t i;
	size_t depth = 0;
	struct ace_condition_token *lhs = NULL;
	struct ace_condition_token *rhs = NULL;
	struct ace_condition_token result = {};
	bool ok;

	for (i = 0; i < program->length; i++) {
		struct ace_condition_token *tok = &program->tokens[i];
		switch (tok->type) {
		case CONDITIONAL_ACE_TOKEN_INT8:
		case CONDITIONAL_ACE_TOKEN_INT16:
		case CONDITIONAL_ACE_TOKEN_INT32:
		case CONDITIONAL_ACE_TOKEN_INT64:
		case CONDITIONAL_ACE_TOKEN_UNICODE:
		case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
		case CONDITIONAL_ACE_TOKEN_SID:
		case CONDITIONAL_ACE_TOKEN_COMPOSITE:
		/* just plonk these literals on the stack */
			program->stack[depth] = *tok;
			depth++;
			break;

		case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
		case CONDITIONAL_ACE_USER_ATTRIBUTE:
		case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
			ok = token_claim_lookup(mem_ctx, token, tok, &result);
			if (! ok) {
				goto error;
			}
			program->stack[depth] = result;
			depth++;
			break;

		case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
			ok = resource_claim_lookup(mem_ctx,
						   tok,
						   sd,
						   &result);
			if (! ok) {
				goto error;
			}
			program->stack[depth] = result;
			depth++;
			break;

		case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
			if (depth == 0) {
				goto error;
			}
			depth--;
			lhs = &program->stack[depth];
			ok = member_lookup(token, tok, lhs, &result);
			if (! ok) {
				goto error;
			}
			program->stack[depth] = result;
			depth++;
			break;
		/* binary relational operators */
		case CONDITIONAL_ACE_TOKEN_EQUAL:
		case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
		case CONDITIONAL_ACE_TOKEN_LESS_THAN:
		case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
		case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_CONTAINS:
		case CONDITIONAL_ACE_TOKEN_ANY_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
		case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
			if (depth < 2) {
				goto error;
			}
			depth--;
			rhs = &program->stack[depth];
			depth--;
			lhs = &program->stack[depth];
			ok = relational_operator(token, tok, lhs, rhs, &result);
			if (! ok) {
				goto error;
			}
			program->stack[depth] = result;
			depth++;
			break;
		/* unary logical operators */
		case CONDITIONAL_ACE_TOKEN_EXISTS:
		case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
		case CONDITIONAL_ACE_TOKEN_NOT:
			if (depth == 0) {
				goto error;
			}
			depth--;
			lhs = &program->stack[depth];
			ok = unary_logic_operator(mem_ctx, token, tok, lhs, sd, &result);
			if (!ok) {
				goto error;
			}
			program->stack[depth] = result;
			depth++;
			break;
		/* binary logical operators */
		case CONDITIONAL_ACE_TOKEN_AND:
		case CONDITIONAL_ACE_TOKEN_OR:
			if (depth < 2) {
				goto error;
			}
			depth--;
			rhs = &program->stack[depth];
			depth--;
			lhs = &program->stack[depth];
			ok = binary_logic_operator(token, tok, lhs, rhs, &result);
			if (! ok) {
				goto error;
			}
			program->stack[depth] = result;
			depth++;
			break;
		default:
			goto error;
		}
	}
	/*
	 * The evaluation should have left a single result value (true, false,
	 * or unknown) on the stack. If not, the expression was malformed.
	 */
	if (depth != 1) {
		goto error;
	}
	result = program->stack[0];
	if (result.type != CONDITIONAL_ACE_SAMBA_RESULT_BOOL) {
		goto error;
	}

	return result.data.result.value;

  error:
	/*
	 * the result of an error is always UNKNOWN, which should be
	 * interpreted pessimistically, not allowing access.
	 */
	return ACE_CONDITION_UNKNOWN;
}


/** access_check_conditional_ace()
 *
 * Run the conditional ACE from the blob form. Return false if it is
 * not a valid conditional ACE, true if it is, even if there is some
 * other error in running it. The *result parameter is set to
 * ACE_CONDITION_FALSE, ACE_CONDITION_TRUE, or ACE_CONDITION_UNKNOWN.
 *
 * ACE_CONDITION_UNKNOWN should be treated pessimistically, as if it were
 * TRUE for deny ACEs, and FALSE for allow ACEs.
 *
 * @param[in] ace - the ACE being processed.
 * @param[in] token - the security token the ACE is processing.
 * @param[out] result - a ternary result value.
 *
 * @return true if it is a valid conditional ACE.
 */

bool access_check_conditional_ace(const struct security_ace *ace,
				  const struct security_token *token,
				  const struct security_descriptor *sd,
				  int *result)
{
	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
	struct ace_condition_script *program = NULL;
	program = parse_conditional_ace(tmp_ctx, ace->coda.conditions);
	if (program == NULL) {
		*result = ACE_CONDITION_UNKNOWN;
		TALLOC_FREE(tmp_ctx);
		return false;
	}

	*result = run_conditional_ace(tmp_ctx, token, program, sd);

	TALLOC_FREE(tmp_ctx);
	return true;
}


bool conditional_ace_encode_binary(TALLOC_CTX *mem_ctx,
				   struct ace_condition_script *program,
				   DATA_BLOB *dest)
{
	size_t i, j, alloc_size, required_size;
	uint8_t *data = NULL;
	uint8_t *new_data = NULL;
	*dest = (DATA_BLOB){NULL, 0};

	alloc_size = CONDITIONAL_ACE_MAX_LENGTH;
	data = talloc_array(mem_ctx,
			    uint8_t,
			    alloc_size);
	if (data == NULL) {
		return false;
	}

	data[0] = 'a';
	data[1] = 'r';
	data[2] = 't';
	data[3] = 'x';

	j = 4;
	for (i = 0; i < program->length; i++) {
		struct ace_condition_token *tok = &program->tokens[i];
		ssize_t consumed;
		bool ok;
		/*
		 * In all cases we write the token type byte.
		 */
		data[j] = tok->type;
		j++;
		if (j >= alloc_size) {
			DBG_ERR("program exceeds %zu bytes\n", alloc_size);
			goto error;
		}

		switch (tok->type) {
		case CONDITIONAL_ACE_TOKEN_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_DEVICE_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_NOT_DEVICE_MEMBER_OF_ANY:
		case CONDITIONAL_ACE_TOKEN_EQUAL:
		case CONDITIONAL_ACE_TOKEN_NOT_EQUAL:
		case CONDITIONAL_ACE_TOKEN_LESS_THAN:
		case CONDITIONAL_ACE_TOKEN_LESS_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_GREATER_THAN:
		case CONDITIONAL_ACE_TOKEN_GREATER_OR_EQUAL:
		case CONDITIONAL_ACE_TOKEN_CONTAINS:
		case CONDITIONAL_ACE_TOKEN_ANY_OF:
		case CONDITIONAL_ACE_TOKEN_NOT_CONTAINS:
		case CONDITIONAL_ACE_TOKEN_NOT_ANY_OF:
		case CONDITIONAL_ACE_TOKEN_EXISTS:
		case CONDITIONAL_ACE_TOKEN_NOT_EXISTS:
		case CONDITIONAL_ACE_TOKEN_NOT:
		case CONDITIONAL_ACE_TOKEN_AND:
		case CONDITIONAL_ACE_TOKEN_OR:
			/*
			 * All of these are simple operators that operate on
			 * the stack. We have already added the tok->type and
			 * there's nothing else to do.
			 */
			continue;

		case CONDITIONAL_ACE_TOKEN_INT8:
		case CONDITIONAL_ACE_TOKEN_INT16:
		case CONDITIONAL_ACE_TOKEN_INT32:
		case CONDITIONAL_ACE_TOKEN_INT64:
			ok = check_integer_range(tok);
			if (! ok) {
				goto error;
			}
			consumed = push_integer(data + j,
						alloc_size - j,
						&tok->data.int64);
			break;
		case CONDITIONAL_ACE_LOCAL_ATTRIBUTE:
		case CONDITIONAL_ACE_USER_ATTRIBUTE:
		case CONDITIONAL_ACE_RESOURCE_ATTRIBUTE:
		case CONDITIONAL_ACE_DEVICE_ATTRIBUTE:
		case CONDITIONAL_ACE_TOKEN_UNICODE:
			consumed = push_unicode(data + j,
						alloc_size - j,
						&tok->data.unicode);
			break;
		case CONDITIONAL_ACE_TOKEN_OCTET_STRING:
			consumed = push_bytes(data + j,
					      alloc_size - j,
					      &tok->data.bytes);
			break;
		case CONDITIONAL_ACE_TOKEN_SID:
			consumed = push_sid(data + j,
					    alloc_size - j,
					    &tok->data.sid);
			break;
		case CONDITIONAL_ACE_TOKEN_COMPOSITE:
			consumed = push_composite(data + j,
						  alloc_size - j,
						  &tok->data.composite);
			break;

		default:
			DBG_ERR("unknown token 0x%02x at position %zu\n",
				tok->type, i);
			goto error;
		}
		if (consumed == -1) {
			DBG_ERR("program exceeds %zu bytes\n", alloc_size);
			goto error;
		}
		j += consumed;
		if (j >= alloc_size) {
			DBG_ERR("program exceeds %zu bytes\n", alloc_size);
			goto error;
		}
	}
	/* align to a 4 byte boundary */
	required_size = (j + 3) & ~((size_t)3);
	if (required_size > alloc_size) {
		DBG_ERR("program exceeds %zu bytes\n", alloc_size);
		goto error;
	}
	while (j < required_size) {
		data[j] = 0;
		j++;
	}
	new_data = talloc_realloc(mem_ctx,
				  data,
				  uint8_t,
				  required_size);
	if (new_data == NULL) {
		goto error;
	}
	data = new_data;

	(*dest).data = data;
	(*dest).length = j;
	return true;
  error:
	TALLOC_FREE(data);
	return false;
}
