Add bool use_privs parameter to smbd_check_access_rights()
[kai/samba.git] / source3 / smbd / file_access.c
index 065f2b6c75b3caeacdd12d26a11495e9f712f2fa..015679deb0bc2a625ce176a7d486861117bef0cb 100644 (file)
 */
 
 #include "includes.h"
+#include "system/filesys.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "smbd/smbd.h"
 
 #undef  DBGC_CLASS
 #define DBGC_CLASS DBGC_ACLS
 
-/**
- * Security descriptor / NT Token level access check function.
- */
-bool can_access_file_acl(struct connection_struct *conn,
-                        const struct smb_filename *smb_fname,
-                        uint32_t access_mask)
-{
-       NTSTATUS status;
-       uint32_t access_granted;
-       struct security_descriptor *secdesc = NULL;
-       bool ret;
-
-       if (get_current_uid(conn) == (uid_t)0) {
-               /* I'm sorry sir, I didn't know you were root... */
-               return true;
-       }
-
-       status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
-                                   (OWNER_SECURITY_INFORMATION |
-                                    GROUP_SECURITY_INFORMATION |
-                                    DACL_SECURITY_INFORMATION),
-                                   &secdesc);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(5, ("Could not get acl: %s\n", nt_errstr(status)));
-               ret = false;
-               goto out;
-       }
-
-       status = se_access_check(secdesc, get_current_nttok(conn),
-                                access_mask, &access_granted);
-       ret = NT_STATUS_IS_OK(status);
-
-       if (DEBUGLEVEL >= 10) {
-               DEBUG(10,("can_access_file_acl for file %s "
-                       "access_mask 0x%x, access_granted 0x%x "
-                       "access %s\n",
-                       smb_fname_str_dbg(smb_fname),
-                       (unsigned int)access_mask,
-                       (unsigned int)access_granted,
-                       ret ? "ALLOWED" : "DENIED" ));
-               NDR_PRINT_DEBUG(security_descriptor, secdesc);
-       }
- out:
-       TALLOC_FREE(secdesc);
-       return ret;
-}
-
 /****************************************************************************
  Actually emulate the in-kernel access checking for delete access. We need
  this to successfully return ACCESS_DENIED on a file open for delete access.
 ****************************************************************************/
 
 bool can_delete_file_in_directory(connection_struct *conn,
-                                 struct smb_filename *smb_fname)
+                                 const struct smb_filename *smb_fname)
 {
        TALLOC_CTX *ctx = talloc_tos();
        char *dname = NULL;
@@ -88,6 +45,11 @@ bool can_delete_file_in_directory(connection_struct *conn,
                return False;
        }
 
+       if (!lp_acl_check_permissions(SNUM(conn))) {
+               /* This option means don't check. */
+               return true;
+       }
+
        /* Get the parent directory permission mask and owners. */
        if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) {
                return False;
@@ -121,18 +83,10 @@ bool can_delete_file_in_directory(connection_struct *conn,
        /* sticky bit means delete only by owner of file or by root or
         * by owner of directory. */
        if (smb_fname_parent->st.st_ex_mode & S_ISVTX) {
-               if(SMB_VFS_STAT(conn, smb_fname) != 0) {
-                       if (errno == ENOENT) {
-                               /* If the file doesn't already exist then
-                                * yes we'll be able to delete it. */
-                               ret = true;
-                               goto out;
-                       }
-                       DEBUG(10,("can_delete_file_in_directory: can't "
-                                 "stat file %s (%s)",
-                                 smb_fname_str_dbg(smb_fname),
-                                 strerror(errno) ));
-                       ret = false;
+               if (!VALID_STAT(smb_fname->st)) {
+                       /* If the file doesn't already exist then
+                        * yes we'll be able to delete it. */
+                       ret = true;
                        goto out;
                }
 
@@ -168,77 +122,27 @@ bool can_delete_file_in_directory(connection_struct *conn,
         * check the file DELETE permission separately.
         */
 
-       ret = can_access_file_acl(conn, smb_fname_parent, FILE_DELETE_CHILD);
+       ret = NT_STATUS_IS_OK(smbd_check_access_rights(conn,
+                               smb_fname_parent,
+                               false,
+                               FILE_DELETE_CHILD));
  out:
        TALLOC_FREE(dname);
        TALLOC_FREE(smb_fname_parent);
        return ret;
 }
 
-/****************************************************************************
- Actually emulate the in-kernel access checking for read/write access. We need
- this to successfully check for ability to write for dos filetimes.
- Note this doesn't take into account share write permissions.
-****************************************************************************/
-
-bool can_access_file_data(connection_struct *conn,
-                         const struct smb_filename *smb_fname,
-                         uint32 access_mask)
-{
-       if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) {
-               return False;
-       }
-       access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA);
-
-       /* some fast paths first */
-
-       DEBUG(10,("can_access_file_data: requesting 0x%x on file %s\n",
-                 (unsigned int)access_mask, smb_fname_str_dbg(smb_fname)));
-
-       if (get_current_uid(conn) == (uid_t)0) {
-               /* I'm sorry sir, I didn't know you were root... */
-               return True;
-       }
-
-       SMB_ASSERT(VALID_STAT(smb_fname->st));
-
-       /* Check primary owner access. */
-       if (get_current_uid(conn) == smb_fname->st.st_ex_uid) {
-               switch (access_mask) {
-                       case FILE_READ_DATA:
-                               return (smb_fname->st.st_ex_mode & S_IRUSR) ?
-                                   True : False;
-
-                       case FILE_WRITE_DATA:
-                               return (smb_fname->st.st_ex_mode & S_IWUSR) ?
-                                   True : False;
-
-                       default: /* FILE_READ_DATA|FILE_WRITE_DATA */
-
-                               if ((smb_fname->st.st_ex_mode &
-                                       (S_IWUSR|S_IRUSR)) ==
-                                   (S_IWUSR|S_IRUSR)) {
-                                       return True;
-                               } else {
-                                       return False;
-                               }
-               }
-       }
-
-       /* now for ACL checks */
-
-       return can_access_file_acl(conn, smb_fname, access_mask);
-}
-
 /****************************************************************************
  Userspace check for write access.
- Note this doesn't take into account share write permissions.
 ****************************************************************************/
 
 bool can_write_to_file(connection_struct *conn,
                       const struct smb_filename *smb_fname)
 {
-       return can_access_file_data(conn, smb_fname, FILE_WRITE_DATA);
+       return NT_STATUS_IS_OK(smbd_check_access_rights(conn,
+                               smb_fname,
+                               false,
+                               FILE_WRITE_DATA));
 }
 
 /****************************************************************************
@@ -251,9 +155,12 @@ bool directory_has_default_acl(connection_struct *conn, const char *fname)
        struct security_descriptor *secdesc = NULL;
        unsigned int i;
        NTSTATUS status = SMB_VFS_GET_NT_ACL(conn, fname,
-                               DACL_SECURITY_INFORMATION, &secdesc);
+                               SECINFO_DACL, &secdesc);
 
-       if (!NT_STATUS_IS_OK(status) || secdesc == NULL) {
+       if (!NT_STATUS_IS_OK(status) ||
+                       secdesc == NULL ||
+                       secdesc->dacl == NULL) {
+               TALLOC_FREE(secdesc);
                return false;
        }
 
@@ -268,3 +175,62 @@ bool directory_has_default_acl(connection_struct *conn, const char *fname)
        TALLOC_FREE(secdesc);
        return false;
 }
+
+/****************************************************************************
+ Check if setting delete on close is allowed on this fsp.
+****************************************************************************/
+
+NTSTATUS can_set_delete_on_close(files_struct *fsp, uint32 dosmode)
+{
+       /*
+        * Only allow delete on close for writable files.
+        */
+
+       if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
+           !lp_delete_readonly(SNUM(fsp->conn))) {
+               DEBUG(10,("can_set_delete_on_close: file %s delete on close "
+                         "flag set but file attribute is readonly.\n",
+                         fsp_str_dbg(fsp)));
+               return NT_STATUS_CANNOT_DELETE;
+       }
+
+       /*
+        * Only allow delete on close for writable shares.
+        */
+
+       if (!CAN_WRITE(fsp->conn)) {
+               DEBUG(10,("can_set_delete_on_close: file %s delete on "
+                         "close flag set but write access denied on share.\n",
+                         fsp_str_dbg(fsp)));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       /*
+        * Only allow delete on close for files/directories opened with delete
+        * intent.
+        */
+
+       if (!(fsp->access_mask & DELETE_ACCESS)) {
+               DEBUG(10,("can_set_delete_on_close: file %s delete on "
+                         "close flag set but delete access denied.\n",
+                         fsp_str_dbg(fsp)));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       /* Don't allow delete on close for non-empty directories. */
+       if (fsp->is_directory) {
+               SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name));
+
+               /* Or the root of a share. */
+               if (ISDOT(fsp->fsp_name->base_name)) {
+                       DEBUG(10,("can_set_delete_on_close: can't set delete on "
+                                 "close for the root of a share.\n"));
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+
+               return can_delete_directory(fsp->conn,
+                                           fsp->fsp_name->base_name);
+       }
+
+       return NT_STATUS_OK;
+}