Fix bug #6082 - smbd_gpfs_getacl failed: Windows client canĀ“t rename or delete file
[samba.git] / source / lib / util_seaccess.c
index 52696d2d306eef8e7349b247f5d3b15b4d534538..0da7442d1953748c3e594999748c5c2ce4aea198 100644 (file)
@@ -1,12 +1,14 @@
 /*
-   Unix SMB/Netbios implementation.
-   Version 2.0
-   Copyright (C) Luke Kenneth Casson Leighton 1996-2000.
-   Copyright (C) Tim Potter 2000.
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Gerald Carter 2005
+   Copyright (C) Volker Lendecke 2007
+   Copyright (C) Jeremy Allison 2008
 
    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
+   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,
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
-#include "nterr.h"
-#include "sids.h"
 
-extern int DEBUGLEVEL;
+extern NT_USER_TOKEN anonymous_token;
 
-/* Process an access allowed ACE */
+/* 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. */
 
-static BOOL ace_grant(uint32 mask, uint32 *acc_desired, uint32 *acc_granted)
+void se_map_generic(uint32 *access_mask, const struct generic_mapping *mapping)
 {
-       uint32 matches;
+       uint32 old_mask = *access_mask;
+
+       if (*access_mask & GENERIC_READ_ACCESS) {
+               *access_mask &= ~GENERIC_READ_ACCESS;
+               *access_mask |= mapping->generic_read;
+       }
 
-       /* 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 (*access_mask & GENERIC_WRITE_ACCESS) {
+               *access_mask &= ~GENERIC_WRITE_ACCESS;
+               *access_mask |= mapping->generic_write;
+       }
 
-       if (*acc_desired == SEC_RIGHTS_MAXIMUM_ALLOWED) {
-               matches = mask;
-               *acc_desired = mask;
-       } else {
-               matches = mask & *acc_desired;
+       if (*access_mask & GENERIC_EXECUTE_ACCESS) {
+               *access_mask &= ~GENERIC_EXECUTE_ACCESS;
+               *access_mask |= mapping->generic_execute;
        }
 
-       if (matches) {
-               *acc_desired = *acc_desired & ~matches;
-               *acc_granted = *acc_granted | matches;
+       if (*access_mask & GENERIC_ALL_ACCESS) {
+               *access_mask &= ~GENERIC_ALL_ACCESS;
+               *access_mask |= mapping->generic_all;
        }
 
-       return *acc_desired == 0;
+       if (old_mask != *access_mask) {
+               DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n",
+                          old_mask, *access_mask));
+       }
 }
 
-/* Process an access denied ACE */
+/* Map generic access rights to object specific rights for all the ACE's
+ * in a security_acl.
+ */
 
-static BOOL ace_deny(uint32 mask, uint32 *acc_desired, uint32 *acc_granted)
+void security_acl_map_generic(struct security_acl *sa,
+                               const struct generic_mapping *mapping)
 {
-       uint32 matches;
-
-       /* If there are any matches in the ACE mask and the desired access,
-          all bits are turned off in the desired and granted mask. */
+       unsigned int i;
 
-       if (*acc_desired == SEC_RIGHTS_MAXIMUM_ALLOWED) {
-               matches = mask;
-       } else {
-               matches = mask & *acc_desired;
+       if (!sa) {
+               return;
        }
 
-       if (matches) {
-               *acc_desired = *acc_granted = 0;
+       for (i = 0; i < sa->num_aces; i++) {
+               se_map_generic(&sa->aces[i].access_mask, mapping);
        }
-
-       return *acc_desired == 0;
 }
 
-/* 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. */
+/* 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. */
 
-static BOOL check_ace(SEC_ACE *ace, BOOL is_owner, DOM_SID *sid, 
-                     uint32 *acc_desired, uint32 *acc_granted, 
-                     uint32 *status)
+void se_map_standard(uint32 *access_mask, struct standard_mapping *mapping)
 {
-       uint32 mask = ace->info.mask;
-
-       /* Inherit only is ignored */
+       uint32 old_mask = *access_mask;
 
-       if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
-               return False;
+       if (*access_mask & SEC_STD_READ_CONTROL) {
+               *access_mask &= ~SEC_STD_READ_CONTROL;
+               *access_mask |= mapping->std_read;
        }
 
-       /* Some debugging stuff */
-
-       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);
-
-               if (!lookup_sid(sid, name_dom, name, &name_type)) {
-                       fstrcpy(name_dom, "UNKNOWN");
-                       fstrcpy(name, "UNKNOWN");
-               }
-
-               if (!lookup_sid(&ace->sid, ace_name_dom, ace_name, 
-                                       &name_type)) {
-                       fstrcpy(ace_name_dom, "UNKNOWN");
-                       fstrcpy(ace_name, "UNKNOWN");
-               }
-
-               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 & (SEC_STD_DELETE|SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|SEC_STD_SYNCHRONIZE)) {
+               *access_mask &= ~(SEC_STD_DELETE|SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|SEC_STD_SYNCHRONIZE);
+               *access_mask |= mapping->std_all;
        }
 
-       /* Only owner allowed write-owner rights */
-
-       if (!is_owner) {
-               mask &= (~SEC_RIGHTS_WRITE_OWNER);
+       if (old_mask != *access_mask) {
+               DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n",
+                          old_mask, *access_mask));
        }
+}
 
-       /* 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: {
+/*
+  perform a SEC_FLAG_MAXIMUM_ALLOWED access check
+*/
+static uint32_t access_check_max_allowed(const struct security_descriptor *sd, 
+                                       const NT_USER_TOKEN *token)
+{
+       uint32_t denied = 0, granted = 0;
+       unsigned i;
 
-                       /* Everyone - or us */
+       if (is_sid_in_token(token, sd->owner_sid)) {
+               granted |= SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL | SEC_STD_DELETE;
+       } else if (user_has_privileges(token, &se_restore)) {
+               granted |= SEC_STD_DELETE;
+       }
 
-                       if (sid_equal(&ace->sid, global_sid_everyone) ||
-                           sid_equal(&ace->sid, sid)) {
-                               
-                               /* Return false if access has been denied */
+       if (sd->dacl == NULL) {
+               return granted & ~denied;
+       }
 
-                               if (ace_deny(mask, acc_desired, 
-                                            acc_granted)) {
-                                       *status = NT_STATUS_ACCESS_DENIED;
-                                       DEBUG(3, ("access denied by ace\n"));
-                                       return True;
-                               }
-                       }
+       for (i = 0;i<sd->dacl->num_aces; i++) {
+               struct security_ace *ace = &sd->dacl->aces[i];
 
-                       break;
+               if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+                       continue;
                }
 
-               /* 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;
+               if (!is_sid_in_token(token, &ace->trustee)) {
+                       continue;
                }
 
-               /* Unknown ACE type */
-
-               default: {
-                       *status = NT_STATUS_INVALID_PARAMETER;
-                       return False;
+               switch (ace->type) {
+               case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                       granted |= ace->access_mask;
+                       break;
+               case SEC_ACE_TYPE_ACCESS_DENIED:
+               case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+                       denied |= ace->access_mask;
+                       break;
+               default:        /* Other ACE types not handled/supported */
+                       break;
                }
        }
 
-       /* There are still some bits set in the access desired mask that
-          haven't been cleared by an ACE.  More checking is required. */
-
-       return False;
+       return granted & ~denied;
 }
 
-/* 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)
+/*
+  The main entry point for access checking. If returning ACCESS_DENIED
+  this function returns the denied bits in the uint32_t pointed
+  to by the access_granted pointer.
+*/
+NTSTATUS se_access_check(const struct security_descriptor *sd, 
+                         const NT_USER_TOKEN *token,
+                         uint32_t access_desired,
+                         uint32_t *access_granted)
 {
-       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;
-       fstring sid_str;
-
-       if (!status || !acc_granted)
-               return False;
-
-       *status = NT_STATUS_ACCESS_DENIED;
-       *acc_granted = 0;
-
-       /*
-        * No security descriptor or security descriptor with no DACL
-        * present allows all access.
-        */
-
-       if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) {
-               *status = NT_STATUS_NOPROBLEMO;
-               *acc_granted = acc_desired;
-               acc_desired = 0;
-               DEBUG(3, ("se_access_check: no sd or blank DACL, access allowed\n"));
-               goto done;
-       }
+       int i;
+       uint32_t bits_remaining;
 
-       /* If desired access mask is empty then no access is allowed */
+       *access_granted = access_desired;
+       bits_remaining = access_desired;
 
-       if (acc_desired == 0) {
-               *status = NT_STATUS_ACCESS_DENIED;
-               *acc_granted = 0;
-               goto done;
-       }
+       /* handle the maximum allowed flag */
+       if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+               uint32_t orig_access_desired = access_desired;
 
-       /* We must know the owner sid */
+               access_desired |= access_check_max_allowed(sd, token);
+               access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+               *access_granted = access_desired;
+               bits_remaining = access_desired & ~SEC_STD_DELETE;
 
-       if (sd->owner_sid == NULL) {
-               DEBUG(1, ("no owner for security descriptor\n"));
-               goto done;
+               DEBUG(10,("se_access_check: MAX desired = 0x%x, granted = 0x%x, remaining = 0x%x\n",
+                       orig_access_desired,
+                       *access_granted,
+                       bits_remaining));
        }
 
-       /* Create user sid */
+#if 0
+       /* We need to support SeSecurityPrivilege for this. */
 
-       if (!uid_to_sid(&user_sid, user->uid)) {
-               DEBUG(3, ("could not lookup sid for uid %d\n", user->uid));
-               goto done;
+       if (access_desired & SEC_FLAG_SYSTEM_SECURITY) {
+               if (user_has_privileges(token, &sec_security)) {
+                       bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY;
+               } else {
+                       return NT_STATUS_PRIVILEGE_NOT_HELD;
+               }
        }
+#endif
 
-       DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &user_sid) ));
-
-       /* 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"));
-
-                goto done;
+       /* a NULL dacl allows access */
+       if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) {
+               *access_granted = access_desired;
+               return NT_STATUS_OK;
        }
 
-       /* Create group sid */
+       /* the owner always gets SEC_STD_WRITE_DAC, SEC_STD_READ_CONTROL and SEC_STD_DELETE */
+       if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE)) &&
+           is_sid_in_token(token, sd->owner_sid)) {
+               bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE);
+       }
+       if ((bits_remaining & SEC_STD_DELETE) &&
+           user_has_privileges(token, &se_restore)) {
+               bits_remaining &= ~SEC_STD_DELETE;
+       }
 
-       if (!gid_to_sid(&group_sid, user->gid)) {
-               DEBUG(3, ("could not lookup sid for gid %d\n", user->gid));
+       if (sd->dacl == NULL) {
                goto done;
        }
 
-       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 */
+       /* check each ace in turn. */
+       for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+               struct security_ace *ace = &sd->dacl->aces[i];
 
-                               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"));
+               if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+                       continue;
+               }
 
-                                       goto done;
-                               }
+               if (!is_sid_in_token(token, &ace->trustee)) {
+                       continue;
+               }
 
-                               add_sid_to_array(&ngroup_sids, &group_sids, 
-                                                &group_sid);
-                       } else {
-                               DEBUG(3, ("could not lookup sid for gid %d\n", 
-                                         user->gid));
+               switch (ace->type) {
+               case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                       bits_remaining &= ~ace->access_mask;
+                       break;
+               case SEC_ACE_TYPE_ACCESS_DENIED:
+               case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+                       if (bits_remaining & ace->access_mask) {
+                               return NT_STATUS_ACCESS_DENIED;
                        }
-
-                       sid_to_string(sid_str, &group_sid);
-                       DEBUG(3, ("supplementary group %s\n", sid_str));
+                       break;
+               default:        /* Other ACE types not handled/supported */
+                       break;
                }
        }
 
-        /* ACL must have something in it */
-
-       acl = sd->dacl;
-
-       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;
+done:
+       if (bits_remaining != 0) {
+               *access_granted = bits_remaining;
+               return NT_STATUS_ACCESS_DENIED;
        }
 
-       /* 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;
-
-    check_aces:
-
-        for (i = 0; i < acl->num_aces; i++) {
-                SEC_ACE *ace = &acl->ace[i];
-               BOOL is_group_owner;
+       return NT_STATUS_OK;
+}
 
-               /* Check user sid */
+/*******************************************************************
+ samr_make_sam_obj_sd
+ ********************************************************************/
 
-                if (ace->type == check_ace_type &&
-                   check_ace(ace, False, &user_sid, &acc_desired,
-                             acc_granted, status)) {
-                       goto done;
-                }
+NTSTATUS samr_make_sam_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size)
+{
+       DOM_SID adm_sid;
+       DOM_SID act_sid;
 
-                /* Check group sids */
+       SEC_ACE ace[3];
 
-                for (j = 0; j < ngroup_sids; j++) {
+       SEC_ACL *psa = NULL;
 
-                       is_group_owner = sd->grp_sid ? 
-                               sid_equal(group_sids[j], sd->grp_sid) : False;
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
 
-                        if (ace->type == check_ace_type &&
-                           check_ace(ace, is_group_owner, group_sids[j], 
-                                     &acc_desired, acc_granted, status)) {
-                               goto done;
-                        }
-                }
-        }
+       sid_copy(&act_sid, &global_sid_Builtin);
+       sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS);
 
-       /* Check access allowed ACEs */
+       /*basic access for every one*/
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+               GENERIC_RIGHTS_SAM_EXECUTE | GENERIC_RIGHTS_SAM_READ, 0);
 
-       if (check_ace_type == SEC_ACE_TYPE_ACCESS_DENIED) {
-               check_ace_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
-               goto check_aces;
-       }
+       /*full access for builtin aliases Administrators and Account Operators*/
+       init_sec_ace(&ace[1], &adm_sid,
+               SEC_ACE_TYPE_ACCESS_ALLOWED, GENERIC_RIGHTS_SAM_ALL_ACCESS, 0);
+       init_sec_ace(&ace[2], &act_sid,
+               SEC_ACE_TYPE_ACCESS_ALLOWED, GENERIC_RIGHTS_SAM_ALL_ACCESS, 0);
 
- done:
+       if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL)
+               return NT_STATUS_NO_MEMORY;
 
-       free_sid_array(ngroup_sids, group_sids);
-       
-       /* If any access desired bits are still on, return access denied
-          and turn off any bits already granted. */
-
-       if (acc_desired) {
-               *acc_granted = 0;
-               *status = NT_STATUS_ACCESS_DENIED;
-       }
+       if ((*psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
+                                 SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL,
+                                 psa, sd_size)) == NULL)
+               return NT_STATUS_NO_MEMORY;
 
-       return *status == NT_STATUS_NOPROBLEMO;
+       return NT_STATUS_OK;
 }