s4-pvfs: change the handling of access checking on create
authorAndrew Tridgell <tridge@samba.org>
Sat, 17 Oct 2009 10:17:29 +0000 (21:17 +1100)
committerAndrew Tridgell <tridge@samba.org>
Sat, 17 Oct 2009 20:13:47 +0000 (07:13 +1100)
Previously when a file was created, we produces the resulting access
mask based on an ACL check against the parent. This change means we
now calculate the inherited ACL much earlier, and produce the
resulting access mask from that ACL, or the user supplied ACL.

source4/ntvfs/posix/pvfs_acl.c
source4/ntvfs/posix/pvfs_open.c
source4/ntvfs/posix/pvfs_resolve.c
source4/ntvfs/posix/pvfs_setfileinfo.c

index 26515cfe1a6249cfc4a9ea3b462d085196b468ef..842aced6f2f4d673a2bc9de3f311e418eacaed92 100644 (file)
@@ -485,6 +485,8 @@ static bool pvfs_group_member(struct pvfs_state *pvfs, gid_t gid)
   doing this saves on building a full security descriptor
   for the common case of access check on files with no 
   specific NT ACL
+
+  If name is NULL then treat as a new file creation
 */
 NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, 
                                struct ntvfs_request *req,
@@ -499,13 +501,14 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       if (uid == name->st.st_uid) {
+       if (name == NULL || uid == name->st.st_uid) {
                max_bits |= SEC_STD_ALL;
        } else if (security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
                max_bits |= SEC_STD_DELETE;
        }
 
-       if ((name->st.st_mode & S_IWOTH) ||
+       if (name == NULL ||
+           (name->st.st_mode & S_IWOTH) ||
            ((name->st.st_mode & S_IWGRP) && 
             pvfs_group_member(pvfs, name->st.st_gid))) {
                max_bits |= SEC_STD_ALL;
@@ -540,7 +543,7 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
 
        if (*access_mask & ~max_bits) {
                DEBUG(0,(__location__ " denied access to '%s' - wanted 0x%08x but got 0x%08x (missing 0x%08x)\n",
-                        name->full_name, *access_mask, max_bits, *access_mask & ~max_bits));
+                        name?name->full_name:"(new file)", *access_mask, max_bits, *access_mask & ~max_bits));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -643,26 +646,47 @@ NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
 NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs, 
                                  struct ntvfs_request *req,
                                  struct pvfs_filename *name,
-                                 uint32_t *access_mask)
+                                 uint32_t *access_mask,
+                                 bool container,
+                                 struct security_descriptor **sd)
 {
        struct pvfs_filename *parent;
        NTSTATUS status;
+       struct security_token *token = req->session_info->security_token;
+
+       if (pvfs_read_only(pvfs, *access_mask)) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
 
        status = pvfs_resolve_parent(pvfs, req, name, &parent);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       status = pvfs_access_check(pvfs, req, parent, access_mask);
+       status = pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       if (! ((*access_mask) & SEC_DIR_ADD_FILE)) {
-               return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
+       if (*sd == NULL) {
+               status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, sd);
        }
 
-       return status;
+       talloc_free(parent);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* expand the generic access bits to file specific bits */
+       *access_mask = pvfs_translate_mask(*access_mask);
+       if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+               *access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
+       }
+
+       if (*sd == NULL) {
+               return pvfs_access_check_unix(pvfs, req, NULL, access_mask);
+       }
+       return sec_access_check(*sd, token, *access_mask, access_mask);
 }
 
 /*
@@ -790,47 +814,42 @@ static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
 
 
 /*
-  setup an ACL on a new file/directory based on the inherited ACL from
-  the parent. If there is no inherited ACL then we don't set anything,
-  as the default ACL applies anyway
+  calculate the ACL on a new file/directory based on the inherited ACL
+  from the parent. If there is no inherited ACL then return a NULL
+  ACL, which means the default ACL should be used
 */
-NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, 
-                         struct ntvfs_request *req,
-                         struct pvfs_filename *name,
-                         int fd)
+NTSTATUS pvfs_acl_inherited_sd(struct pvfs_state *pvfs, 
+                              TALLOC_CTX *mem_ctx,
+                              struct ntvfs_request *req,
+                              struct pvfs_filename *parent,
+                              bool container,
+                              struct security_descriptor **ret_sd)
 {
        struct xattr_NTACL *acl;
        NTSTATUS status;
-       struct pvfs_filename *parent;
        struct security_descriptor *parent_sd, *sd;
-       bool container;
        struct id_mapping *ids;
        struct composite_context *ctx;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
 
-       /* form the parents path */
-       status = pvfs_resolve_parent(pvfs, req, name, &parent);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       *ret_sd = NULL;
 
        acl = talloc(req, struct xattr_NTACL);
-       if (acl == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(acl, tmp_ctx);
 
        status = pvfs_acl_load(pvfs, parent, -1, acl);
        if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               talloc_free(tmp_ctx);
                return NT_STATUS_OK;
        }
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx);
 
        switch (acl->version) {
        case 1:
                parent_sd = acl->info.sd;
                break;
        default:
+               talloc_free(tmp_ctx);
                return NT_STATUS_INVALID_ACL;
        }
 
@@ -838,61 +857,99 @@ NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs,
            parent_sd->dacl == NULL ||
            parent_sd->dacl->num_aces == 0) {
                /* go with the default ACL */
+               talloc_free(tmp_ctx);
                return NT_STATUS_OK;
        }
 
        /* create the new sd */
        sd = security_descriptor_initialise(req);
-       if (sd == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sd, tmp_ctx);
 
        ids = talloc_array(sd, struct id_mapping, 2);
-       NT_STATUS_HAVE_NO_MEMORY(ids);
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids, tmp_ctx);
 
        ids[0].unixid = talloc(ids, struct unixid);
-       NT_STATUS_HAVE_NO_MEMORY(ids[0].unixid);
-       ids[0].unixid->id = name->st.st_uid;
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids[0].unixid, tmp_ctx);
+       ids[0].unixid->id = geteuid();
        ids[0].unixid->type = ID_TYPE_UID;
        ids[0].sid = NULL;
        ids[0].status = NT_STATUS_NONE_MAPPED;
 
        ids[1].unixid = talloc(ids, struct unixid);
-       NT_STATUS_HAVE_NO_MEMORY(ids[1].unixid);
-       ids[1].unixid->id = name->st.st_gid;
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids[1].unixid, tmp_ctx);
+       ids[1].unixid->id = getegid();
        ids[1].unixid->type = ID_TYPE_GID;
        ids[1].sid = NULL;
        ids[1].status = NT_STATUS_NONE_MAPPED;
 
        ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, ids, 2, ids);
-       NT_STATUS_HAVE_NO_MEMORY(ctx);
+       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ctx, tmp_ctx);
 
        status = wbc_xids_to_sids_recv(ctx, &ids);
-       NT_STATUS_NOT_OK_RETURN(status);
+       NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx);
 
        sd->owner_sid = talloc_steal(sd, ids[0].sid);
        sd->group_sid = talloc_steal(sd, ids[1].sid);
 
        sd->type |= SEC_DESC_DACL_PRESENT;
 
-       container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? true:false;
-
        /* fill in the aces from the parent */
        status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx);
 
        /* if there is nothing to inherit then we fallback to the
           default acl */
        if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
+               talloc_free(tmp_ctx);
                return NT_STATUS_OK;
        }
 
-       acl->info.sd = sd;
+       *ret_sd = talloc_steal(mem_ctx, sd);
+
+       talloc_free(tmp_ctx);
+       return NT_STATUS_OK;
+}
+
+
+/*
+  setup an ACL on a new file/directory based on the inherited ACL from
+  the parent. If there is no inherited ACL then we don't set anything,
+  as the default ACL applies anyway
+*/
+NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, 
+                         struct ntvfs_request *req,
+                         struct pvfs_filename *name,
+                         int fd)
+{
+       struct xattr_NTACL acl;
+       NTSTATUS status;
+       struct security_descriptor *sd;
+       struct pvfs_filename *parent;
+       bool container;
+
+       /* form the parents path */
+       status = pvfs_resolve_parent(pvfs, req, name, &parent);
+       NT_STATUS_NOT_OK_RETURN(status);
+
+       container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? true:false;
+
+       status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, &sd);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(parent);
+               return status;
+       }
+
+       if (sd == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       acl.version = 1;
+       acl.info.sd = sd;
+
+       status = pvfs_acl_save(pvfs, name, fd, &acl);
+       talloc_free(sd);
+       talloc_free(parent);
 
-       status = pvfs_acl_save(pvfs, name, fd, acl);
-       
        return status;
 }
 
index e8f1c0c4c8c7c3154e20795576201a87f62a6f56..59bd67b08d65dd482617cb71d975e3effe5e5049 100644 (file)
@@ -103,10 +103,10 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
                                        struct ntvfs_request *req,
                                        struct pvfs_filename *name,
                                        int fd, struct pvfs_file *f,
-                                       union smb_open *io)
+                                       union smb_open *io,
+                                       struct security_descriptor *sd)
 {
        NTSTATUS status;
-       struct security_descriptor *sd;
 
        /* setup any EAs that were asked for */
        if (io->ntcreatex.in.ea_list) {
@@ -118,7 +118,6 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
                }
        }
 
-       sd = io->ntcreatex.in.sec_desc;
        /* setup an initial sec_desc if requested */
        if (sd && (sd->type & SEC_DESC_DACL_PRESENT)) {
                union smb_setfileinfo set;
@@ -134,9 +133,6 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
                set.set_secdesc.in.sd = sd;
 
                status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
-       } else {
-               /* otherwise setup an inherited acl from the parent */
-               status = pvfs_acl_inherit(pvfs, req, name, fd);
        }
 
        return status;
@@ -185,6 +181,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        uint32_t create_options;
        uint32_t share_access;
        bool forced;
+       struct security_descriptor *sd = NULL;
 
        create_options = io->generic.in.create_options;
        share_access   = io->generic.in.share_access;
@@ -251,8 +248,9 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
        if (name->exists) {
                /* check the security descriptor */
                status = pvfs_access_check(pvfs, req, name, &access_mask);
-       } else {
-               status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+       } else {                
+               sd = io->ntcreatex.in.sec_desc;
+               status = pvfs_access_check_create(pvfs, req, name, &access_mask, true, &sd);
        }
        NT_STATUS_NOT_OK_RETURN(status);
 
@@ -352,7 +350,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
                        goto cleanup_delete;
                }
 
-               status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
+               status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io, sd);
                if (!NT_STATUS_IS_OK(status)) {
                        goto cleanup_delete;
                }
@@ -616,6 +614,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        struct pvfs_filename *parent;
        uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
        bool allow_level_II_oplock = false;
+       struct security_descriptor *sd = NULL;
 
        if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -630,7 +629,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
                return NT_STATUS_CANNOT_DELETE;
        }
 
-       status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+       sd = io->ntcreatex.in.sec_desc;
+       status = pvfs_access_check_create(pvfs, req, name, &access_mask, false, &sd);
        NT_STATUS_NOT_OK_RETURN(status);
 
        /* check that the parent isn't opened with delete on close set */
@@ -698,7 +698,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
        }
 
 
-       status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
+       status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io, sd);
        if (!NT_STATUS_IS_OK(status)) {
                goto cleanup_delete;
        }
index 8e8da72e1f63cf588d4e1df1cd1dc85e4c1bfc67..8c5806d93ffceac1047b268cfbd65622ae702f18 100644 (file)
@@ -498,13 +498,14 @@ static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx,
 
      TODO: ../ collapsing, and outside share checking
 */
-NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, 
+                          struct ntvfs_request *req,
                           const char *cifs_name,
                           uint_t flags, struct pvfs_filename **name)
 {
        NTSTATUS status;
 
-       *name = talloc(mem_ctx, struct pvfs_filename);
+       *name = talloc(req, struct pvfs_filename);
        if (*name == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -516,6 +517,12 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
                flags &= ~PVFS_RESOLVE_STREAMS;
        }
 
+       /* SMB2 doesn't allow a leading slash */
+       if (req->ctx->protocol == PROTOCOL_SMB2 &&
+           *cifs_name == '\\') {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
        /* do the basic conversion to a unix formatted path,
           also checking for allowable characters */
        status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
index 9fe02a8e175eafad6baa7f7ceaaf5355be0b23e2..244548382ce1018c09247f8096d9f01fb10317fa 100644 (file)
@@ -168,7 +168,7 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
        }
 
        /* resolve the new name */
-       status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
+       status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }