summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/proto.h1
-rw-r--r--source3/param/loadparm.c4
-rw-r--r--source3/smbd/dosmode.c106
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 );
}