summaryrefslogtreecommitdiff
path: root/source3/lib/util_seaccess.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/lib/util_seaccess.c')
-rw-r--r--source3/lib/util_seaccess.c446
1 files changed, 446 insertions, 0 deletions
diff --git a/source3/lib/util_seaccess.c b/source3/lib/util_seaccess.c
new file mode 100644
index 0000000000..8ed266aced
--- /dev/null
+++ b/source3/lib/util_seaccess.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000.
+ Copyright (C) Tim Potter 2000.
+ Copyright (C) Re-written by Jeremy Allison 2000.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "nterr.h"
+#include "sids.h"
+
+/**********************************************************************************
+ Check if this ACE has a SID in common with the token.
+**********************************************************************************/
+
+static BOOL token_sid_in_ace(const NT_USER_TOKEN *token, const SEC_ACE *ace)
+{
+ size_t i;
+
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&ace->trustee, &token->user_sids[i]))
+ return True;
+ }
+
+ return False;
+}
+
+/*********************************************************************************
+ Check an ACE against a SID. We return the remaining needed permission
+ bits not yet granted. Zero means permission allowed (no more needed bits).
+**********************************************************************************/
+
+static uint32 check_ace(SEC_ACE *ace, NT_USER_TOKEN *token, uint32 acc_desired,
+ NTSTATUS *status)
+{
+ uint32 mask = ace->info.mask;
+
+ /*
+ * Inherit only is ignored.
+ */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ return acc_desired;
+ }
+
+ /*
+ * If this ACE has no SID in common with the token,
+ * ignore it as it cannot be used to make an access
+ * determination.
+ */
+
+ if (!token_sid_in_ace( token, ace))
+ return acc_desired;
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ /*
+ * This is explicitly allowed.
+ * Remove the bits from the remaining
+ * access required. Return the remaining
+ * bits needed.
+ */
+ acc_desired &= ~mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ /*
+ * This is explicitly denied.
+ * If any bits match terminate here,
+ * we are denied.
+ */
+ if (acc_desired & mask) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ return 0xFFFFFFFF;
+ }
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ return 0xFFFFFFFF;
+ default:
+ *status = NT_STATUS_INVALID_PARAMETER;
+ return 0xFFFFFFFF;
+ }
+
+ return acc_desired;
+}
+
+/*********************************************************************************
+ Maximum access was requested. Calculate the max possible. Fail if it doesn't
+ include other bits requested.
+**********************************************************************************/
+
+static BOOL get_max_access( SEC_ACL *the_acl, NT_USER_TOKEN *token, uint32 *granted,
+ uint32 desired,
+ NTSTATUS *status)
+{
+ uint32 acc_denied = 0;
+ uint32 acc_granted = 0;
+ size_t i;
+
+ for ( i = 0 ; i < the_acl->num_aces; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+ uint32 mask = ace->info.mask;
+
+ if (!token_sid_in_ace( token, ace))
+ continue;
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ acc_granted |= (mask & ~acc_denied);
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ acc_denied |= (mask & ~acc_granted);
+ break;
+ case SEC_ACE_TYPE_SYSTEM_ALARM:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT:
+ *status = NT_STATUS_NOT_IMPLEMENTED;
+ *granted = 0;
+ return False;
+ default:
+ *status = NT_STATUS_INVALID_PARAMETER;
+ *granted = 0;
+ return False;
+ }
+ }
+
+ /*
+ * If we were granted no access, or we desired bits that we
+ * didn't get, then deny.
+ */
+
+ if ((acc_granted == 0) || ((acc_granted & desired) != desired)) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ *granted = 0;
+ return False;
+ }
+
+ /*
+ * Return the access we did get.
+ */
+
+ *granted = acc_granted;
+ *status = NT_STATUS_OK;
+ return True;
+}
+
+/* Map generic access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of generic to object
+ specific access rights. */
+
+void se_map_generic(uint32 *access_mask, struct generic_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
+
+ if (*access_mask & GENERIC_READ_ACCESS) {
+ *access_mask &= ~GENERIC_READ_ACCESS;
+ *access_mask |= mapping->generic_read;
+ }
+
+ if (*access_mask & GENERIC_WRITE_ACCESS) {
+ *access_mask &= ~GENERIC_WRITE_ACCESS;
+ *access_mask |= mapping->generic_write;
+ }
+
+ if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+ *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+ *access_mask |= mapping->generic_execute;
+ }
+
+ if (*access_mask & GENERIC_ALL_ACCESS) {
+ *access_mask &= ~GENERIC_ALL_ACCESS;
+ *access_mask |= mapping->generic_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/* Map standard access rights to object specific rights. This technique is
+ used to give meaning to assigning read, write, execute and all access to
+ objects. Each type of object has its own mapping of standard to object
+ specific access rights. */
+
+void se_map_standard(uint32 *access_mask, struct standard_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
+
+ if (*access_mask & READ_CONTROL_ACCESS) {
+ *access_mask &= ~READ_CONTROL_ACCESS;
+ *access_mask |= mapping->std_read;
+ }
+
+ if (*access_mask & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS)) {
+ *access_mask &= ~(DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS);
+ *access_mask |= mapping->std_all;
+ }
+
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n",
+ old_mask, *access_mask));
+ }
+}
+
+/*****************************************************************************
+ Check access rights of a user against a security descriptor. Look at
+ each ACE in the security descriptor until an access denied ACE denies
+ any of the desired rights to the user or any of the users groups, or one
+ or more ACEs explicitly grant all requested access rights. See
+ "Access-Checking" document in MSDN.
+*****************************************************************************/
+
+BOOL se_access_check(SEC_DESC *sd, NT_USER_TOKEN *token,
+ uint32 acc_desired, uint32 *acc_granted,
+ NTSTATUS *status)
+{
+ extern NT_USER_TOKEN anonymous_token;
+ size_t i;
+ SEC_ACL *the_acl;
+ fstring sid_str;
+ uint32 tmp_acc_desired = acc_desired;
+
+ if (!status || !acc_granted)
+ return False;
+
+ if (!token)
+ token = &anonymous_token;
+
+ *status = NT_STATUS_OK;
+ *acc_granted = 0;
+
+ DEBUG(10,("se_access_check: requested access 0x%08x, for NT token with %u entries and first sid %s.\n",
+ (unsigned int)acc_desired, (unsigned int)token->num_sids,
+ sid_to_string(sid_str, &token->user_sids[0])));
+
+ /*
+ * No security descriptor or security descriptor with no DACL
+ * present allows all access.
+ */
+
+ /* ACL must have something in it */
+
+ if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) {
+ *status = NT_STATUS_OK;
+ *acc_granted = acc_desired;
+ DEBUG(5, ("se_access_check: no sd or blank DACL, access allowed\n"));
+ return True;
+ }
+
+ /* The user sid is the first in the token */
+
+ DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[PRIMARY_USER_SID_INDEX]) ));
+
+ for (i = 1; i < token->num_sids; i++) {
+ DEBUG(3, ("se_access_check: also %s\n",
+ sid_to_string(sid_str, &token->user_sids[i])));
+ }
+
+ /* Is the token the owner of the SID ? */
+
+ if (sd->owner_sid) {
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&token->user_sids[i], sd->owner_sid)) {
+ /*
+ * The owner always has SEC_RIGHTS_WRITE_DAC & READ_CONTROL.
+ */
+ if (tmp_acc_desired & WRITE_DAC_ACCESS)
+ tmp_acc_desired &= ~WRITE_DAC_ACCESS;
+ if (tmp_acc_desired & READ_CONTROL_ACCESS)
+ tmp_acc_desired &= ~READ_CONTROL_ACCESS;
+ }
+ }
+ }
+
+ the_acl = sd->dacl;
+
+ if (tmp_acc_desired & MAXIMUM_ALLOWED_ACCESS) {
+ tmp_acc_desired &= ~MAXIMUM_ALLOWED_ACCESS;
+ return get_max_access( the_acl, token, acc_granted, tmp_acc_desired,
+ status);
+ }
+
+ for ( i = 0 ; i < the_acl->num_aces && tmp_acc_desired != 0; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+
+ DEBUG(10,("se_access_check: ACE %u: type %d, flags = 0x%02x, SID = %s mask = %x, current desired = %x\n",
+ (unsigned int)i, ace->type, ace->flags,
+ sid_to_string(sid_str, &ace->trustee),
+ (unsigned int) ace->info.mask,
+ (unsigned int)tmp_acc_desired ));
+
+ tmp_acc_desired = check_ace( ace, token, tmp_acc_desired, status);
+ if (NT_STATUS_V(*status)) {
+ *acc_granted = 0;
+ DEBUG(5,("se_access_check: ACE %u denied with status %s.\n", (unsigned int)i, nt_errstr(*status)));
+ return False;
+ }
+ }
+
+ /*
+ * If there are no more desired permissions left then
+ * access was allowed.
+ */
+
+ if (tmp_acc_desired == 0) {
+ *acc_granted = acc_desired;
+ *status = NT_STATUS_OK;
+ DEBUG(5,("se_access_check: access (%x) granted.\n", (unsigned int)acc_desired ));
+ return True;
+ }
+
+ *acc_granted = 0;
+ *status = NT_STATUS_ACCESS_DENIED;
+ DEBUG(5,("se_access_check: access (%x) denied.\n", (unsigned int)acc_desired ));
+ return False;
+}
+
+/* Create a child security descriptor using another security descriptor as
+ the parent container. This child object can either be a container or
+ non-container object. */
+
+SEC_DESC_BUF *se_create_child_secdesc(TALLOC_CTX *ctx, SEC_DESC *parent_ctr,
+ BOOL child_container)
+{
+ SEC_DESC_BUF *sdb;
+ SEC_DESC *sd;
+ SEC_ACL *new_dacl, *the_acl;
+ SEC_ACE *new_ace_list = NULL;
+ int new_ace_list_ndx = 0, i;
+ size_t size;
+
+ /* Currently we only process the dacl when creating the child. The
+ sacl should also be processed but this is left out as sacls are
+ not implemented in Samba at the moment.*/
+
+ the_acl = parent_ctr->dacl;
+
+ if (!(new_ace_list = talloc(ctx, sizeof(SEC_ACE) * the_acl->num_aces)))
+ return NULL;
+
+ for (i = 0; the_acl && i < the_acl->num_aces; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
+ SEC_ACE *new_ace = &new_ace_list[new_ace_list_ndx];
+ uint8 new_flags = 0;
+ BOOL inherit = False;
+ fstring sid_str;
+
+ /* The OBJECT_INHERIT_ACE flag causes the ACE to be
+ inherited by non-container children objects. Container
+ children objects will inherit it as an INHERIT_ONLY
+ ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) {
+
+ if (!child_container) {
+ new_flags |= SEC_ACE_FLAG_OBJECT_INHERIT;
+ } else {
+ new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+
+ inherit = True;
+ }
+
+ /* The CONAINER_INHERIT_ACE flag means all child container
+ objects will inherit and use the ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ if (!child_container) {
+ inherit = False;
+ } else {
+ new_flags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
+ }
+ }
+
+ /* The INHERIT_ONLY_ACE is not used by the se_access_check()
+ function for the parent container, but is inherited by
+ all child objects as a normal ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ /* Move along, nothing to see here */
+ }
+
+ /* The SEC_ACE_FLAG_NO_PROPAGATE_INHERIT flag means the ACE
+ is inherited by child objects but not grandchildren
+ objects. We clear the object inherit and container
+ inherit flags in the inherited ACE. */
+
+ if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ new_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
+
+ /* Add ACE to ACE list */
+
+ if (!inherit)
+ continue;
+
+ init_sec_access(&new_ace->info, ace->info.mask);
+ init_sec_ace(new_ace, &ace->trustee, ace->type,
+ new_ace->info, new_flags);
+
+ sid_to_string(sid_str, &ace->trustee);
+
+ DEBUG(5, ("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x "
+ " inherited as %s:%d/0x%02x/0x%08x\n", sid_str,
+ ace->type, ace->flags, ace->info.mask,
+ sid_str, new_ace->type, new_ace->flags,
+ new_ace->info.mask));
+
+ new_ace_list_ndx++;
+ }
+
+ /* Create child security descriptor to return */
+
+ new_dacl = make_sec_acl(ctx, ACL_REVISION, new_ace_list_ndx, new_ace_list);
+
+ /* Use the existing user and group sids. I don't think this is
+ correct. Perhaps the user and group should be passed in as
+ parameters by the caller? */
+
+ sd = make_sec_desc(ctx, SEC_DESC_REVISION,
+ parent_ctr->owner_sid,
+ parent_ctr->grp_sid,
+ parent_ctr->sacl,
+ new_dacl, &size);
+
+ sdb = make_sec_desc_buf(ctx, size, sd);
+
+ return sdb;
+}