r6049: Ensure "dos filetime" checks file ACLs correctly. May fix Excel "read-only"
authorJeremy Allison <jra@samba.org>
Thu, 24 Mar 2005 22:34:28 +0000 (22:34 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:56:18 +0000 (10:56 -0500)
issue.
Jeremy.
(This used to be commit 80e788143a6c3d973d3b8e57d91ca5c4a83605b2)

source3/lib/util.c
source3/smbd/dosmode.c
source3/smbd/posix_acls.c

index 265302d2d4f8be86d5260f31a278e3aae6a4715f..8f6a381944293436f4ab106f5ff60fd474f86056 100644 (file)
@@ -273,24 +273,6 @@ const char *tmpdir(void)
        return "/tmp";
 }
 
-/****************************************************************************
- Determine whether we are in the specified group.
-****************************************************************************/
-
-BOOL in_group(gid_t group, gid_t current_gid, int ngroups, const gid_t *groups)
-{
-       int i;
-
-       if (group == current_gid)
-               return(True);
-
-       for (i=0;i<ngroups;i++)
-               if (group == groups[i])
-                       return(True);
-
-       return(False);
-}
-
 /****************************************************************************
  Add a gid to an array of gids if it's not already there.
 ****************************************************************************/
index fefcaca09d5db31742a62a0617cf09559aa9d499..3a0e81e5feffc51c712b607e44ff11d20ee03c34 100644 (file)
@@ -431,10 +431,8 @@ int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode,
  than POSIX.
 *******************************************************************/
 
-int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
+int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times)
 {
-       extern struct current_user current_user;
-       SMB_STRUCT_STAT sb;
        int ret = -1;
 
        errno = 0;
@@ -454,21 +452,12 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
           (as DOS does).
         */
 
-       if(SMB_VFS_STAT(conn,fname,&sb) != 0)
-               return -1;
-
        /* Check if we have write access. */
-       if (CAN_WRITE(conn)) {
-               if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
-                       ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
-                       ((sb.st_mode & S_IWGRP) &&
-                               in_group(sb.st_gid,current_user.gid,
-                                       current_user.ngroups,current_user.groups)))) {
-                       /* We are allowed to become root and change the filetime. */
-                       become_root();
-                       ret = SMB_VFS_UTIME(conn,fname, times);
-                       unbecome_root();
-               }
+       if (can_write_to_file(conn, fname)) {
+               /* We are allowed to become root and change the filetime. */
+               become_root();
+               ret = SMB_VFS_UTIME(conn,fname, times);
+               unbecome_root();
        }
 
        return ret;
@@ -478,7 +467,7 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
  Change a filetime - possibly allowing DOS semantics.
 *******************************************************************/
 
-BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
+BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime)
 {
        struct utimbuf times;
 
index c5fa035c69c52991be1867f61cd03d1eb01d0328..c0ccdcb6d6341fc54d17d256407c59515f7ab023 100644 (file)
@@ -3758,23 +3758,27 @@ BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *
  Check for POSIX group ACLs. If none use stat entry.
 ****************************************************************************/
 
-static int check_posix_acl_group_write(connection_struct *conn, const char *dname, SMB_STRUCT_STAT *psbuf)
+static int check_posix_acl_group_write(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
 {
        extern struct current_user current_user;
        SMB_ACL_T posix_acl = NULL;
        int entry_id = SMB_ACL_FIRST_ENTRY;
        SMB_ACL_ENTRY_T entry;
        int i;
+       BOOL seen_mask = False;
        int ret = -1;
 
-       if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, dname, SMB_ACL_TYPE_ACCESS)) == NULL) {
+       if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS)) == NULL) {
                goto check_stat;
        }
 
        /* First ensure the group mask allows group read. */
+       /* Also check any user entries (these take preference over group). */
+
        while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
                SMB_ACL_TAG_T tagtype;
                SMB_ACL_PERMSET_T permset;
+               int have_write = -1;
 
                /* get_next... */
                if (entry_id == SMB_ACL_FIRST_ENTRY)
@@ -3788,20 +3792,51 @@ static int check_posix_acl_group_write(connection_struct *conn, const char *dnam
                        goto check_stat;
                }
 
+               have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
+               if (have_write == -1) {
+                       goto check_stat;
+               }
+
                switch(tagtype) {
                        case SMB_ACL_MASK:
-                               if (!SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE)) {
-                                       /* We don't have group write permission. */
+                               if (!have_write) {
+                                       /* We don't have any group or explicit user write permission. */
                                        ret = -1; /* Allow caller to check "other" permissions. */
+                                       DEBUG(10,("check_posix_acl_group_write: file %s \
+refusing write due to mask.\n", fname));
                                        goto done;
                                }
+                               seen_mask = True;
+                               break;
+                       case SMB_ACL_USER:
+                       {
+                               /* Check against current_user.uid. */
+                               uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+                               if (puid == NULL) {
+                                       goto check_stat;
+                               }
+                               if (current_user.uid == *puid) {
+                                       /* We have a uid match but we must ensure we have seen the acl mask. */
+                                       ret = have_write;
+                                       DEBUG(10,("check_posix_acl_group_write: file %s \
+match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "cannot write"));
+                                       if (seen_mask) {
+                                               goto done;
+                                       }
+                               }
                                break;
+                       }
                        default:
                                continue;
                }
        }
 
-       /* Now check all group entries. */
+       /* If ret is anything other than -1 we matched on a user entry. */
+       if (ret != -1) {
+               goto done;
+       }
+
+       /* Next check all group entries. */
        entry_id = SMB_ACL_FIRST_ENTRY;
        while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
                SMB_ACL_TAG_T tagtype;
@@ -3826,35 +3861,23 @@ static int check_posix_acl_group_write(connection_struct *conn, const char *dnam
                }
 
                switch(tagtype) {
-                       case SMB_ACL_USER:
-                               {
-                                       /* Check against current_user.uid. */
-                                       uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
-                                       if (puid == NULL) {
-                                               goto check_stat;
-                                       }
-                                       if (current_user.uid == *puid) {
-                                               /* We're done now we have a uid match. */
+                       case SMB_ACL_GROUP:
+                       {
+                               gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
+                               if (pgid == NULL) {
+                                       goto check_stat;
+                               }
+                               for (i = 0; i < current_user.ngroups; i++) {
+                                       if (current_user.groups[i] == *pgid) {
+                                               /* We're done now we have a gid match. */
                                                ret = have_write;
+                                               DEBUG(10,("check_posix_acl_group_write: file %s \
+match on group %u -> %s.\n", fname, (unsigned int)*pgid, ret ? "can write" : "cannot write"));
                                                goto done;
                                        }
                                }
                                break;
-                       case SMB_ACL_GROUP:
-                               {
-                                       gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
-                                       if (pgid == NULL) {
-                                               goto check_stat;
-                                       }
-                                       for (i = 0; i < current_user.ngroups; i++) {
-                                               if (current_user.groups[i] == *pgid) {
-                                                       /* We're done now we have a gid match. */
-                                                       ret = have_write;
-                                                       goto done;
-                                               }
-                                       }
-                               }
-                               break;
+                       }
                        default:
                                continue;
                }
@@ -3877,7 +3900,7 @@ static int check_posix_acl_group_write(connection_struct *conn, const char *dnam
 }
 
 /****************************************************************************
- Actually emulate the in-kernel access checking for write access. We need
+ 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.
 ****************************************************************************/
 
@@ -3888,6 +3911,11 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
        pstring dname;
        int ret;
 
+       if (!CAN_WRITE(conn)) {
+               return False;
+       }
+
+       /* Get the parent directory permission mask and owners. */
        pstrcpy(dname, parent_dirname(fname));
        if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) {
                return False;
@@ -3895,11 +3923,12 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
        if (!S_ISDIR(sbuf.st_mode)) {
                return False;
        }
-       if (current_user.uid == 0) {
+       if (current_user.uid == 0 || conn->admin_user) {
                /* I'm sorry sir, I didn't know you were root... */
                return True;
        }
 
+       /* Check primary owner write access. */
        if (current_user.uid == sbuf.st_uid) {
                return (sbuf.st_mode & S_IWUSR) ? True : False;
        }
@@ -3918,11 +3947,52 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
        }
 #endif
 
-       /* Check group ownership. */
+       /* Check group or explicit user acl entry write access. */
        ret = check_posix_acl_group_write(conn, dname, &sbuf);
        if (ret == 0 || ret == 1) {
                return ret ? True : False;
        }
 
+       /* Finally check other write access. */
+       return (sbuf.st_mode & S_IWOTH) ? True : False;
+}
+
+/****************************************************************************
+ Actually emulate the in-kernel access checking for write access. We need
+ this to successfully check for ability to write for dos filetimes.
+****************************************************************************/
+
+BOOL can_write_to_file(connection_struct *conn, const char *fname)
+{
+       extern struct current_user current_user;
+       SMB_STRUCT_STAT sbuf;  
+       int ret;
+
+       if (!CAN_WRITE(conn)) {
+               return False;
+       }
+
+       if (current_user.uid == 0 || conn->admin_user) {
+               /* I'm sorry sir, I didn't know you were root... */
+               return True;
+       }
+
+       /* Get the file permission mask and owners. */
+       if(SMB_VFS_STAT(conn, fname, &sbuf) != 0) {
+               return False;
+       }
+
+       /* Check primary owner write access. */
+       if (current_user.uid == sbuf.st_uid) {
+               return (sbuf.st_mode & S_IWUSR) ? True : False;
+       }
+
+       /* Check group or explicit user acl entry write access. */
+       ret = check_posix_acl_group_write(conn, fname, &sbuf);
+       if (ret == 0 || ret == 1) {
+               return ret ? True : False;
+       }
+
+       /* Finally check other write access. */
        return (sbuf.st_mode & S_IWOTH) ? True : False;
 }