diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/proto.h | 1 | ||||
-rw-r--r-- | source3/param/loadparm.c | 4 | ||||
-rw-r--r-- | source3/smbd/dosmode.c | 106 |
3 files changed, 75 insertions, 36 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index 8c0ff6f86c..9cd9296c41 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1609,6 +1609,7 @@ BOOL lp_map_system(int ); BOOL lp_delete_readonly(int ); BOOL lp_fake_oplocks(int ); BOOL lp_recursive_veto_delete(int ); +BOOL lp_dos_filemode(int ); BOOL lp_dos_filetimes(int ); BOOL lp_dos_filetime_resolution(int ); BOOL lp_fake_dir_create_times(int ); diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index d5a032c26b..716511cb8b 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -375,6 +375,7 @@ typedef struct BOOL bDeleteReadonly; BOOL bFakeOplocks; BOOL bDeleteVetoFiles; + BOOL bDosFilemode; BOOL bDosFiletimes; BOOL bDosFiletimeResolution; BOOL bFakeDirCreateTimes; @@ -489,6 +490,7 @@ static service sDefault = { False, /* bDeleteReadonly */ False, /* bFakeOplocks */ False, /* bDeleteVetoFiles */ + False, /* bDosFilemode */ False, /* bDosFiletimes */ False, /* bDosFiletimeResolution */ False, /* bFakeDirCreateTimes */ @@ -984,6 +986,7 @@ static struct parm_struct parm_table[] = { {"magic script", P_STRING, P_LOCAL, &sDefault.szMagicScript, NULL, NULL, FLAG_SHARE}, {"magic output", P_STRING, P_LOCAL, &sDefault.szMagicOutput, NULL, NULL, FLAG_SHARE}, {"delete readonly", P_BOOL, P_LOCAL, &sDefault.bDeleteReadonly, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"dos filemode", P_BOOL, P_LOCAL, &sDefault.bDosFilemode, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, {"dos filetimes", P_BOOL, P_LOCAL, &sDefault.bDosFiletimes, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, {"dos filetime resolution", P_BOOL, P_LOCAL, &sDefault.bDosFiletimeResolution, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, @@ -1603,6 +1606,7 @@ FN_LOCAL_BOOL(lp_map_system, bMap_system) FN_LOCAL_BOOL(lp_delete_readonly, bDeleteReadonly) FN_LOCAL_BOOL(lp_fake_oplocks, bFakeOplocks) FN_LOCAL_BOOL(lp_recursive_veto_delete, bDeleteVetoFiles) +FN_LOCAL_BOOL(lp_dos_filemode, bDosFilemode) FN_LOCAL_BOOL(lp_dos_filetimes, bDosFiletimes) FN_LOCAL_BOOL(lp_dos_filetime_resolution, bDosFiletimeResolution) FN_LOCAL_BOOL(lp_fake_dir_create_times, bFakeDirCreateTimes) diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c index 377cfbb7cf..89e7c6b766 100644 --- a/source3/smbd/dosmode.c +++ b/source3/smbd/dosmode.c @@ -184,50 +184,84 @@ chmod a file - but preserve some bits ********************************************************************/ int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st) { - SMB_STRUCT_STAT st1; - int mask=0; - mode_t tmp; - mode_t unixmode; - - if (!st) { - st = &st1; - if (vfs_stat(conn,fname,st)) return(-1); - } + extern struct current_user current_user; + SMB_STRUCT_STAT st1; + int mask=0; + mode_t tmp; + mode_t unixmode; + int ret = -1; + + if (!st) { + st = &st1; + if (vfs_stat(conn,fname,st)) + return(-1); + } - if (S_ISDIR(st->st_mode)) dosmode |= aDIR; + if (S_ISDIR(st->st_mode)) + dosmode |= aDIR; - if (dos_mode(conn,fname,st) == dosmode) return(0); + if (dos_mode(conn,fname,st) == dosmode) + return(0); - unixmode = unix_mode(conn,dosmode,fname); + unixmode = unix_mode(conn,dosmode,fname); - /* preserve the s bits */ - mask |= (S_ISUID | S_ISGID); + /* preserve the s bits */ + mask |= (S_ISUID | S_ISGID); - /* preserve the t bit */ + /* preserve the t bit */ #ifdef S_ISVTX - mask |= S_ISVTX; + mask |= S_ISVTX; #endif - /* possibly preserve the x bits */ - if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR; - if (!MAP_SYSTEM(conn)) mask |= S_IXGRP; - if (!MAP_HIDDEN(conn)) mask |= S_IXOTH; - - unixmode |= (st->st_mode & mask); - - /* if we previously had any r bits set then leave them alone */ - if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { - unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); - unixmode |= tmp; - } - - /* if we previously had any w bits set then leave them alone - whilst adding in the new w bits, if the new mode is not rdonly */ - if (!IS_DOS_READONLY(dosmode)) { - unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); - } - - return(vfs_chmod(conn,fname,unixmode)); + /* possibly preserve the x bits */ + if (!MAP_ARCHIVE(conn)) + mask |= S_IXUSR; + if (!MAP_SYSTEM(conn)) + mask |= S_IXGRP; + if (!MAP_HIDDEN(conn)) + mask |= S_IXOTH; + + unixmode |= (st->st_mode & mask); + + /* if we previously had any r bits set then leave them alone */ + if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { + unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); + unixmode |= tmp; + } + + /* if we previously had any w bits set then leave them alone + whilst adding in the new w bits, if the new mode is not rdonly */ + if (!IS_DOS_READONLY(dosmode)) { + unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); + } + + ret = vfs_chmod(conn,fname,unixmode); + + if((errno != EPERM) && (errno != EACCES)) + return -1; + + if(!lp_dos_filemode(SNUM(conn))) + return -1; + + /* We want DOS semantics, ie allow non owner with write permission to change the + bits on a file. Just like file_utime below. + */ + + /* Check if we have write access. */ + if (CAN_WRITE(conn)) { + if (((st->st_mode & S_IWOTH) || + conn->admin_user || + ((st->st_mode & S_IWUSR) && current_user.uid==st->st_uid) || + ((st->st_mode & S_IWGRP) && + in_group(st->st_gid,current_user.gid, current_user.ngroups,current_user.groups)))) { + /* We are allowed to become root and change the file mode. */ + become_root(); + ret = vfs_chmod(conn,fname,unixmode); + unbecome_root(); + } + } + + return( ret ); } |