/*
- Unix SMB/Netbios implementation.
- Version 2.0
+ 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
#include "nterr.h"
#include "sids.h"
-extern int DEBUGLEVEL;
+/**********************************************************************************
+ Check if this ACE has a SID in common with the token.
+**********************************************************************************/
-/* Process an access allowed ACE */
-
-static BOOL ace_grant(uint32 mask, uint32 *acc_desired, uint32 *acc_granted)
+static BOOL token_sid_in_ace(const NT_USER_TOKEN *token, const SEC_ACE *ace)
{
- uint32 matches;
-
- /* If there are any matches in the ACE mask and desired access,
- turn them off in the desired access and on in the granted
- mask. */
-
- if (*acc_desired == SEC_RIGHTS_MAXIMUM_ALLOWED) {
- matches = mask;
- *acc_desired = mask;
- } else {
- matches = mask & *acc_desired;
- }
+ size_t i;
- if (matches) {
- *acc_desired = *acc_desired & ~matches;
- *acc_granted = *acc_granted | matches;
+ for (i = 0; i < token->num_sids; i++) {
+ if (sid_equal(&ace->trustee, &token->user_sids[i]))
+ return True;
}
- return *acc_desired == 0;
+ return False;
}
-/* Process an access denied ACE */
+/*********************************************************************************
+ 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 BOOL ace_deny(uint32 mask, uint32 *acc_desired, uint32 *acc_granted)
+static uint32 check_ace(SEC_ACE *ace, NT_USER_TOKEN *token, uint32 acc_desired,
+ NTSTATUS *status)
{
- uint32 matches;
+ uint32 mask = ace->info.mask;
- /* If there are any matches in the ACE mask and the desired access,
- all bits are turned off in the desired and granted mask. */
+ /*
+ * Inherit only is ignored.
+ */
- if (*acc_desired == SEC_RIGHTS_MAXIMUM_ALLOWED) {
- matches = mask;
- } else {
- matches = mask & *acc_desired;
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ return acc_desired;
}
- if (matches) {
- *acc_desired = *acc_granted = 0;
+ /*
+ * 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 == 0;
+ return acc_desired;
}
-/* Check an ACE against a SID. We return true if the ACE clears all the
- permission bits in the access desired mask. This indicates that we have
- make a decision to deny or allow access and the status is updated
- accordingly. */
+/*********************************************************************************
+ Maximum access was requested. Calculate the max possible. Fail if it doesn't
+ include other bits requested.
+**********************************************************************************/
-static BOOL check_ace(SEC_ACE *ace, BOOL is_owner, DOM_SID *sid,
- uint32 *acc_desired, uint32 *acc_granted,
- uint32 *status)
+static BOOL get_max_access( SEC_ACL *the_acl, NT_USER_TOKEN *token, uint32 *granted,
+ uint32 desired,
+ NTSTATUS *status)
{
- uint32 mask = ace->info.mask;
+ 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;
+ }
+ }
- /* Inherit only is ignored */
+ /*
+ * If we were granted no access, or we desired bits that we
+ * didn't get, then deny.
+ */
- if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ if ((acc_granted == 0) || ((acc_granted & desired) != desired)) {
+ *status = NT_STATUS_ACCESS_DENIED;
+ *granted = 0;
return False;
}
- /* Some debugging stuff */
+ /*
+ * Return the access we did get.
+ */
- if (DEBUGLEVEL >= 3) {
- fstring ace_sid_str, sid_str;
- fstring ace_name, ace_name_dom, name, name_dom;
- uint8 name_type;
-
- sid_to_string(sid_str, sid);
- sid_to_string(ace_sid_str, &ace->sid);
+ *granted = acc_granted;
+ *status = NT_STATUS_OK;
+ return True;
+}
- if (!lookup_sid(sid, name_dom, name, &name_type)) {
- fstrcpy(name_dom, "UNKNOWN");
- fstrcpy(name, "UNKNOWN");
- }
+/* 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. */
- if (!lookup_sid(&ace->sid, ace_name_dom, ace_name,
- &name_type)) {
- fstrcpy(ace_name_dom, "UNKNOWN");
- fstrcpy(ace_name, "UNKNOWN");
- }
+void se_map_generic(uint32 *access_mask, struct generic_mapping *mapping)
+{
+ uint32 old_mask = *access_mask;
- DEBUG(3, ("checking %s ACE sid %s (%s%s%s) mask 0x%08x "
- "against sid %s (%s%s%s)\n",
- (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ?
- "allowed" : ((ace->type ==
- SEC_ACE_TYPE_ACCESS_DENIED) ?
- "denied" : "unknown"),
- ace_sid_str, ace_name_dom, lp_winbind_separator(),
- ace_name, mask, sid_str, name_dom,
- lp_winbind_separator(), name));
+ if (*access_mask & GENERIC_READ_ACCESS) {
+ *access_mask &= ~GENERIC_READ_ACCESS;
+ *access_mask |= mapping->generic_read;
}
- /* Only owner allowed write-owner rights */
-
- if (!is_owner) {
- mask &= (~SEC_RIGHTS_WRITE_OWNER);
+ if (*access_mask & GENERIC_WRITE_ACCESS) {
+ *access_mask &= ~GENERIC_WRITE_ACCESS;
+ *access_mask |= mapping->generic_write;
}
- /* Check the ACE value. This updates the access_desired and
- access_granted values appropriately. */
-
- switch (ace->type) {
-
- /* Access allowed ACE */
-
- case SEC_ACE_TYPE_ACCESS_ALLOWED: {
-
- /* Everyone - or us */
-
- if (sid_equal(&ace->sid, global_sid_everyone) ||
- sid_equal(&ace->sid, sid)) {
-
- /* Return true if access has been allowed */
-
- if (ace_grant(mask, acc_desired,
- acc_granted)) {
- *status = NT_STATUS_NO_PROBLEMO;
- DEBUG(3, ("access granted by ace\n"));
- return True;
- }
- }
-
- break;
- }
-
- /* Access denied ACE */
-
- case SEC_ACE_TYPE_ACCESS_DENIED: {
-
- /* Everyone - or us */
-
- if (sid_equal(&ace->sid, global_sid_everyone) ||
- sid_equal(&ace->sid, sid)) {
-
- /* Return false if access has been denied */
-
- if (ace_deny(mask, acc_desired,
- acc_granted)) {
- *status = NT_STATUS_ACCESS_DENIED;
- DEBUG(3, ("access denied by ace\n"));
- return True;
- }
- }
-
- break;
- }
-
- /* Unimplemented ACE types. These are ignored. */
-
- case SEC_ACE_TYPE_SYSTEM_ALARM:
- case SEC_ACE_TYPE_SYSTEM_AUDIT: {
- *status = NT_STATUS_NOT_IMPLEMENTED;
- return False;
- }
-
- /* Unknown ACE type */
-
- default: {
- *status = NT_STATUS_INVALID_PARAMETER;
- return False;
- }
+ if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+ *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+ *access_mask |= mapping->generic_execute;
}
- /* There are still some bits set in the access desired mask that
- haven't been cleared by an ACE. More checking is required. */
+ if (*access_mask & GENERIC_ALL_ACCESS) {
+ *access_mask &= ~GENERIC_ALL_ACCESS;
+ *access_mask |= mapping->generic_all;
+ }
- return False;
+ if (old_mask != *access_mask) {
+ DEBUG(10, ("se_map_generic(): 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, struct current_user *user,
- uint32 acc_desired, uint32 *acc_granted, uint32 *status)
+/*****************************************************************************
+ 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)
{
- DOM_SID user_sid, group_sid;
- DOM_SID owner_sid;
- DOM_SID **group_sids = NULL;
- int i, j;
- uint ngroup_sids = 0;
- SEC_ACL *acl;
- uint8 check_ace_type;
+ 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;
- *status = NT_STATUS_ACCESS_DENIED;
+ 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_NOPROBLEMO;
+ *status = NT_STATUS_OK;
*acc_granted = acc_desired;
- acc_desired = 0;
- DEBUG(3, ("se_access_check: no sd or blank DACL, access allowed\n"));
- goto done;
+ DEBUG(5, ("se_access_check: no sd or blank DACL, access allowed\n"));
+ return True;
}
- /* If desired access mask is empty then no access is allowed */
+ /* The user sid is the first in the token */
- if (acc_desired == 0) {
- *status = NT_STATUS_ACCESS_DENIED;
- *acc_granted = 0;
- goto done;
- }
+ DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[PRIMARY_USER_SID_INDEX]) ));
- /* We must know the owner sid */
+ 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])));
+ }
- if (sd->owner_sid == NULL) {
- DEBUG(1, ("no owner for security descriptor\n"));
- goto done;
+ /* 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;
+ }
+ }
}
- /* Create user sid */
+ the_acl = sd->dacl;
- if (!uid_to_sid(&user_sid, user->uid)) {
- DEBUG(3, ("could not lookup sid for uid %d\n", user->uid));
- goto done;
+ 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);
}
- DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &user_sid) ));
+ for ( i = 0 ; i < the_acl->num_aces && tmp_acc_desired != 0; i++) {
+ SEC_ACE *ace = &the_acl->ace[i];
- /* If we're the owner, then we can do anything */
-
- if (sid_equal(&user_sid, sd->owner_sid)) {
- *status = NT_STATUS_NOPROBLEMO;
- *acc_granted = acc_desired;
- acc_desired = 0;
- DEBUG(3, ("is owner, access allowed\n"));
+ 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 ));
- goto done;
+ 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, get_nt_error_msg(*status)));
+ return False;
+ }
}
- /* Create group sid */
+ /*
+ * If there are no more desired permissions left then
+ * access was allowed.
+ */
- if (!gid_to_sid(&group_sid, user->gid)) {
- DEBUG(3, ("could not lookup sid for gid %d\n", user->gid));
- goto done;
+ 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;
+}
- sid_to_string(sid_str, &group_sid);
- DEBUG(3, ("group sid is %s\n", sid_str));
-
- /* Create array of group sids */
-
- add_sid_to_array(&ngroup_sids, &group_sids, &group_sid);
-
- for (i = 0; i < user->ngroups; i++) {
- if (user->groups[i] != user->gid) {
- if (gid_to_sid(&group_sid, user->groups[i])) {
-
- /* If we're a group member then we can also
- do anything */
-
- if (sid_equal(&group_sid, sd->grp_sid)) {
- *status = NT_STATUS_NOPROBLEMO;
- *acc_granted = acc_desired;
- acc_desired = 0;
- DEBUG(3, ("is group member "
- "access allowed\n"));
-
- goto done;
- }
+/* 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. */
- add_sid_to_array(&ngroup_sids, &group_sids,
- &group_sid);
+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 {
- DEBUG(3, ("could not lookup sid for gid %d\n",
- user->gid));
+ new_flags |= SEC_ACE_FLAG_INHERIT_ONLY;
}
- sid_to_string(sid_str, &group_sid);
- DEBUG(3, ("supplementary group %s\n", sid_str));
+ inherit = True;
}
- }
-
- /* ACL must have something in it */
- acl = sd->dacl;
+ /* The CONAINER_INHERIT_ACE flag means all child container
+ objects will inherit and use the ACE. */
- if (acl == NULL || acl->ace == NULL || acl->num_aces == 0) {
-
- /* Checks against a NULL ACL succeed and return access
- granted = access requested. */
-
- *status = NT_STATUS_NOPROBLEMO;
- *acc_granted = acc_desired;
- acc_desired = 0;
- DEBUG(3, ("null ace, access allowed\n"));
-
- goto done;
- }
-
- /* Check each ACE in ACL. We break out of the loop if an ACE is
- either explicitly denied or explicitly allowed by the
- check_ace2() function. We also check the Access Denied ACEs
- before Access allowed ones as the Platform SDK documentation is
- unclear whether ACEs in a ACL are necessarily always in this
- order. See the discussion on "Order of ACEs in a DACL" in
- MSDN. */
-
- check_ace_type = SEC_ACE_TYPE_ACCESS_DENIED;
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ if (!child_container) {
+ inherit = False;
+ } else {
+ new_flags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
+ }
+ }
- check_aces:
+ /* 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. */
- for (i = 0; i < acl->num_aces; i++) {
- SEC_ACE *ace = &acl->ace[i];
- BOOL is_group_owner;
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ /* Move along, nothing to see here */
+ }
- /* Check user sid */
+ /* 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->type == check_ace_type &&
- check_ace(ace, False, &user_sid, &acc_desired,
- acc_granted, status)) {
- goto done;
- }
+ if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ new_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+ }
- /* Check group sids */
+ /* Add ACE to ACE list */
- for (j = 0; j < ngroup_sids; j++) {
+ if (!inherit)
+ continue;
- is_group_owner = sd->grp_sid ?
- sid_equal(group_sids[j], sd->grp_sid) : False;
+ init_sec_access(&new_ace->info, ace->info.mask);
+ init_sec_ace(new_ace, &ace->trustee, ace->type,
+ new_ace->info, new_flags);
- if (ace->type == check_ace_type &&
- check_ace(ace, is_group_owner, group_sids[j],
- &acc_desired, acc_granted, status)) {
- goto done;
- }
- }
- }
+ sid_to_string(sid_str, &ace->trustee);
- /* Check access allowed ACEs */
+ 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));
- if (check_ace_type == SEC_ACE_TYPE_ACCESS_DENIED) {
- check_ace_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
- goto check_aces;
+ new_ace_list_ndx++;
}
- done:
-
- free_sid_array(ngroup_sids, group_sids);
+ /* Create child security descriptor to return */
- /* If any access desired bits are still on, return access denied
- and turn off any bits already granted. */
+ new_dacl = make_sec_acl(ctx, ACL_REVISION, new_ace_list_ndx, new_ace_list);
- if (acc_desired) {
- *acc_granted = 0;
- *status = NT_STATUS_ACCESS_DENIED;
- }
+ /* 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 *status == NT_STATUS_NOPROBLEMO;
+ return sdb;
}