Merge commit 'release-4-0-0alpha15' into master4-tmp
[ira/wip.git] / source3 / smbd / file_access.c
index 9c77f9e96163fb3b811e062cda99d9ba9f32295f..74855649ea2fe2b4b971581b2660f3445f0373d7 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 char * fname,
-                               uint32_t access_mask)
+                        const struct smb_filename *smb_fname,
+                        uint32_t access_mask)
 {
        NTSTATUS status;
        uint32_t access_granted;
        struct security_descriptor *secdesc = NULL;
+       bool ret;
 
-       if (conn->server_info->utok.uid == 0 || conn->admin_user) {
+       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, fname,
-                                   (OWNER_SECURITY_INFORMATION |
-                                    GROUP_SECURITY_INFORMATION |
-                                    DACL_SECURITY_INFORMATION),
+       status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
+                                   (SECINFO_OWNER |
+                                    SECINFO_GROUP |
+                                    SECINFO_DACL),
                                    &secdesc);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("Could not get acl: %s\n", nt_errstr(status)));
-               return false;
+               ret = false;
+               goto out;
        }
 
-       status = se_access_check(secdesc, conn->server_info->ptok,
+       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 NT_STATUS_IS_OK(status);
+       return ret;
 }
 
 /****************************************************************************
@@ -60,50 +79,72 @@ bool can_access_file_acl(struct connection_struct *conn,
  this to successfully return ACCESS_DENIED on a file open for delete access.
 ****************************************************************************/
 
-bool can_delete_file_in_directory(connection_struct *conn, const char *fname)
+bool can_delete_file_in_directory(connection_struct *conn,
+                                 struct smb_filename *smb_fname)
 {
-       SMB_STRUCT_STAT sbuf;
        TALLOC_CTX *ctx = talloc_tos();
        char *dname = NULL;
+       struct smb_filename *smb_fname_parent = NULL;
+       NTSTATUS status;
+       bool ret;
 
        if (!CAN_WRITE(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, fname, &dname, NULL)) {
+       if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) {
                return False;
        }
-       if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) {
-               return False;
+
+       status = create_synthetic_smb_fname(ctx, dname, NULL, NULL,
+                                           &smb_fname_parent);
+       if (!NT_STATUS_IS_OK(status)) {
+               ret = false;
+               goto out;
+       }
+
+       if(SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
+               ret = false;
+               goto out;
        }
 
        /* fast paths first */
 
-       if (!S_ISDIR(sbuf.st_ex_mode)) {
-               return False;
+       if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) {
+               ret = false;
+               goto out;
        }
-       if (conn->server_info->utok.uid == 0 || conn->admin_user) {
+       if (get_current_uid(conn) == (uid_t)0) {
                /* I'm sorry sir, I didn't know you were root... */
-               return True;
+               ret = true;
+               goto out;
        }
 
 #ifdef S_ISVTX
        /* sticky bit means delete only by owner of file or by root or
         * by owner of directory. */
-       if (sbuf.st_ex_mode & S_ISVTX) {
-               SMB_STRUCT_STAT sbuf_file;
-               if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) {
+       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. */
-                               return True;
+                               ret = true;
+                               goto out;
                        }
                        DEBUG(10,("can_delete_file_in_directory: can't "
-                               "stat file %s (%s)",
-                               fname, strerror(errno) ));
-                       return False;
+                                 "stat file %s (%s)",
+                                 smb_fname_str_dbg(smb_fname),
+                                 strerror(errno) ));
+                       ret = false;
+                       goto out;
                }
+
                /*
                 * Patch from SATOH Fumiyasu <fumiyas@miraclelinux.com>
                 * for bug #3348. Don't assume owning sticky bit
@@ -112,12 +153,15 @@ bool can_delete_file_in_directory(connection_struct *conn, const char *fname)
                 * or the owner of the directory as we have no possible
                 * chance of deleting. Otherwise, go on and check the ACL.
                 */
-               if ((conn->server_info->utok.uid != sbuf.st_ex_uid) &&
-                               (conn->server_info->utok.uid != sbuf_file.st_ex_uid)) {
+               if ((get_current_uid(conn) !=
+                       smb_fname_parent->st.st_ex_uid) &&
+                   (get_current_uid(conn) != smb_fname->st.st_ex_uid)) {
                        DEBUG(10,("can_delete_file_in_directory: not "
-                               "owner of file %s or directory %s",
-                               fname, dname));
-                       return False;
+                                 "owner of file %s or directory %s",
+                                 smb_fname_str_dbg(smb_fname),
+                                 smb_fname_str_dbg(smb_fname_parent)));
+                       ret = false;
+                       goto out;
                }
        }
 #endif
@@ -133,7 +177,11 @@ bool can_delete_file_in_directory(connection_struct *conn, const char *fname)
         * check the file DELETE permission separately.
         */
 
-       return can_access_file_acl(conn, dname, FILE_DELETE_CHILD);
+       ret = can_access_file_acl(conn, smb_fname_parent, FILE_DELETE_CHILD);
+ out:
+       TALLOC_FREE(dname);
+       TALLOC_FREE(smb_fname_parent);
+       return ret;
 }
 
 /****************************************************************************
@@ -142,7 +190,9 @@ bool can_delete_file_in_directory(connection_struct *conn, const char *fname)
  Note this doesn't take into account share write permissions.
 ****************************************************************************/
 
-bool can_access_file_data(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
+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;
@@ -152,32 +202,31 @@ bool can_access_file_data(connection_struct *conn, const char *fname, SMB_STRUCT
        /* some fast paths first */
 
        DEBUG(10,("can_access_file_data: requesting 0x%x on file %s\n",
-               (unsigned int)access_mask, fname ));
+                 (unsigned int)access_mask, smb_fname_str_dbg(smb_fname)));
 
-       if (conn->server_info->utok.uid == 0 || conn->admin_user) {
+       if (get_current_uid(conn) == (uid_t)0) {
                /* I'm sorry sir, I didn't know you were root... */
                return True;
        }
 
-       if (!VALID_STAT(*psbuf)) {
-               /* Get the file permission mask and owners. */
-               if(SMB_VFS_STAT(conn, fname, psbuf) != 0) {
-                       return False;
-               }
-       }
+       SMB_ASSERT(VALID_STAT(smb_fname->st));
 
        /* Check primary owner access. */
-       if (conn->server_info->utok.uid == psbuf->st_ex_uid) {
+       if (get_current_uid(conn) == smb_fname->st.st_ex_uid) {
                switch (access_mask) {
                        case FILE_READ_DATA:
-                               return (psbuf->st_ex_mode & S_IRUSR) ? True : False;
+                               return (smb_fname->st.st_ex_mode & S_IRUSR) ?
+                                   True : False;
 
                        case FILE_WRITE_DATA:
-                               return (psbuf->st_ex_mode & S_IWUSR) ? True : False;
+                               return (smb_fname->st.st_ex_mode & S_IWUSR) ?
+                                   True : False;
 
                        default: /* FILE_READ_DATA|FILE_WRITE_DATA */
 
-                               if ((psbuf->st_ex_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) {
+                               if ((smb_fname->st.st_ex_mode &
+                                       (S_IWUSR|S_IRUSR)) ==
+                                   (S_IWUSR|S_IRUSR)) {
                                        return True;
                                } else {
                                        return False;
@@ -187,7 +236,7 @@ bool can_access_file_data(connection_struct *conn, const char *fname, SMB_STRUCT
 
        /* now for ACL checks */
 
-       return can_access_file_acl(conn, fname, access_mask);
+       return can_access_file_acl(conn, smb_fname, access_mask);
 }
 
 /****************************************************************************
@@ -195,9 +244,10 @@ bool can_access_file_data(connection_struct *conn, const char *fname, SMB_STRUCT
  Note this doesn't take into account share write permissions.
 ****************************************************************************/
 
-bool can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
+bool can_write_to_file(connection_struct *conn,
+                      const struct smb_filename *smb_fname)
 {
-       return can_access_file_data(conn, fname, psbuf, FILE_WRITE_DATA);
+       return can_access_file_data(conn, smb_fname, FILE_WRITE_DATA);
 }
 
 /****************************************************************************
@@ -210,7 +260,7 @@ 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) {
                return false;