summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2002-01-14 23:12:45 +0000
committerJeremy Allison <jra@samba.org>2002-01-14 23:12:45 +0000
commit73ee41adc64658e9d89e557de686fde4ef9365a2 (patch)
tree54a98ec456e3c7d4d1d225d3aea302afc7c49aa0
parent1239feecf50ca4ab746c4fbeddd446989bf05721 (diff)
downloadsamba-73ee41adc64658e9d89e557de686fde4ef9365a2.tar.gz
samba-73ee41adc64658e9d89e557de686fde4ef9365a2.tar.xz
samba-73ee41adc64658e9d89e557de686fde4ef9365a2.zip
Finished UNIX extensions - including realpath checks on link creation.
Now to add tests.... Jeremy.
-rwxr-xr-xsource/configure2
-rw-r--r--source/configure.in2
-rw-r--r--source/include/config.h.in3
-rw-r--r--source/include/profile.h2
-rw-r--r--source/include/proto.h2
-rw-r--r--source/include/trans2.h2
-rw-r--r--source/include/vfs.h1
-rw-r--r--source/lib/system.c15
-rw-r--r--source/smbd/trans2.c133
-rw-r--r--source/smbd/vfs-wrap.c15
-rw-r--r--source/smbd/vfs.c1
11 files changed, 153 insertions, 25 deletions
diff --git a/source/configure b/source/configure
index 3f2fa8fcd38..44025f7c7bc 100755
--- a/source/configure
+++ b/source/configure
@@ -5322,7 +5322,7 @@ else
fi
done
-for ac_func in initgroups select poll rdchk getgrnam getgrent pathconf
+for ac_func in initgroups select poll rdchk getgrnam getgrent pathconf realpath
do
echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
echo "configure:5329: checking for $ac_func" >&5
diff --git a/source/configure.in b/source/configure.in
index f50d16c4af3..d0a76030960 100644
--- a/source/configure.in
+++ b/source/configure.in
@@ -661,7 +661,7 @@ AC_CHECK_FUNCS(waitpid getcwd strdup strtoul strerror chown fchown chmod fchmod
AC_CHECK_FUNCS(fstat strchr utime utimes getrlimit fsync bzero memset setpgid mknod mknod64)
AC_CHECK_FUNCS(memmove vsnprintf snprintf asprintf vasprintf setsid glob strpbrk pipe crypt16 getauthuid)
AC_CHECK_FUNCS(strftime sigprocmask sigblock sigaction sigset innetgr setnetgrent getnetgrent endnetgrent)
-AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf)
+AC_CHECK_FUNCS(initgroups select poll rdchk getgrnam getgrent pathconf realpath)
AC_CHECK_FUNCS(setpriv setgidx setuidx setgroups sysconf mktime rename ftruncate stat64 fstat64)
AC_CHECK_FUNCS(lstat64 fopen64 atexit grantpt dup2 lseek64 ftruncate64 readdir64)
AC_CHECK_FUNCS(fseek64 fseeko64 ftell64 ftello64 setluid getpwanam setlinebuf)
diff --git a/source/include/config.h.in b/source/include/config.h.in
index c7fb34a2383..006e36f16c8 100644
--- a/source/include/config.h.in
+++ b/source/include/config.h.in
@@ -660,6 +660,9 @@
/* Define if you have the readlink function. */
#undef HAVE_READLINK
+/* Define if you have the realpath function. */
+#undef HAVE_REALPATH
+
/* Define if you have the rename function. */
#undef HAVE_RENAME
diff --git a/source/include/profile.h b/source/include/profile.h
index 719318195e0..149c0a73157 100644
--- a/source/include/profile.h
+++ b/source/include/profile.h
@@ -104,6 +104,8 @@ struct profile_stats {
unsigned syscall_link_time;
unsigned syscall_mknod_count;
unsigned syscall_mknod_time;
+ unsigned syscall_realpath_count;
+ unsigned syscall_realpath_time;
/* stat cache counters */
unsigned statcache_lookups;
unsigned statcache_misses;
diff --git a/source/include/proto.h b/source/include/proto.h
index 9d35c0e41bb..92f88301aff 100644
--- a/source/include/proto.h
+++ b/source/include/proto.h
@@ -946,6 +946,7 @@ int sys_open(const char *path, int oflag, mode_t mode);
FILE *sys_fopen(const char *path, const char *type);
SMB_STRUCT_DIRENT *sys_readdir(DIR *dirp);
int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev);
+char *sys_realpath(const char *path, char *resolved_path);
int sys_waitpid(pid_t pid,int *status,int options);
char *sys_getwd(char *s);
int sys_symlink(const char *oldpath, const char *newpath);
@@ -4586,6 +4587,7 @@ int vfswrap_symlink(connection_struct *conn, const char *oldpath, const char *ne
int vfswrap_readlink(connection_struct *conn, const char *path, char *buf, size_t bufsiz);
int vfswrap_link(connection_struct *conn, const char *oldpath, const char *newpath);
int vfswrap_mknod(connection_struct *conn, const char *pathname, mode_t mode, SMB_DEV_T dev);
+char *vfswrap_realpath(connection_struct *conn, const char *path, char *resolved_path);
size_t vfswrap_fget_nt_acl(files_struct *fsp, int fd, SEC_DESC **ppdesc);
size_t vfswrap_get_nt_acl(files_struct *fsp, char *name, SEC_DESC **ppdesc);
BOOL vfswrap_fset_nt_acl(files_struct *fsp, int fd, uint32 security_info_sent, SEC_DESC *psd);
diff --git a/source/include/trans2.h b/source/include/trans2.h
index ffd90ae7229..9b655e8b938 100644
--- a/source/include/trans2.h
+++ b/source/include/trans2.h
@@ -369,7 +369,7 @@ Byte offset Type name description
* Oh this is fun. "Standard UNIX permissions" has no
* meaning in POSIX. We need to define the mapping onto
* and off the wire as this was not done in the original HP
- * code. JRA.
+ * spec. JRA.
*/
#define UNIX_X_OTH 0000001
diff --git a/source/include/vfs.h b/source/include/vfs.h
index b0dcd7ba84d..ee4bdea6022 100644
--- a/source/include/vfs.h
+++ b/source/include/vfs.h
@@ -91,6 +91,7 @@ struct vfs_ops {
int (*readlink)(struct connection_struct *conn, const char *path, char *buf, size_t bufsiz);
int (*link)(struct connection_struct *conn, const char *oldpath, const char *newpath);
int (*mknod)(struct connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev);
+ char *(*realpath)(struct connection_struct *conn, const char *path, char *resolved_path);
/* NT ACL operations. */
diff --git a/source/lib/system.c b/source/lib/system.c
index 3685ce444f8..a4420f5c6c2 100644
--- a/source/lib/system.c
+++ b/source/lib/system.c
@@ -256,6 +256,21 @@ int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev)
}
/*******************************************************************
+ Wrapper for realpath.
+********************************************************************/
+
+char *sys_realpath(const char *path, char *resolved_path)
+{
+#if defined(HAVE_REALPATH)
+ return realpath(path, resolved_path);
+#else
+ /* As realpath is not a system call we can't return ENOSYS. */
+ errno = EINVAL;
+ return NULL;
+#endif
+}
+
+/*******************************************************************
The wait() calls vary between systems
********************************************************************/
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
index 4b7f2a147db..a72498f4fee 100644
--- a/source/smbd/trans2.c
+++ b/source/smbd/trans2.c
@@ -411,10 +411,13 @@ static uint32 unix_perms_to_wire(mode_t perms)
Map wire perms onto standard UNIX permissions. Obey share restrictions.
****************************************************************************/
-static mode_t unix_perms_from_wire( connection_struct *conn, uint32 perms)
+static mode_t unix_perms_from_wire( connection_struct *conn, SMB_STRUCT_STAT *pst, uint32 perms)
{
mode_t ret = 0;
+ if (perms == MODE_NO_CHANGE)
+ return pst->st_mode;
+
ret |= ((perms & UNIX_X_OTH ) ? S_IXOTH : 0);
ret |= ((perms & UNIX_W_OTH ) ? S_IWOTH : 0);
ret |= ((perms & UNIX_R_OTH ) ? S_IROTH : 0);
@@ -434,10 +437,16 @@ static mode_t unix_perms_from_wire( connection_struct *conn, uint32 perms)
ret |= ((perms & UNIX_SET_UID ) ? S_ISVTX : 0);
#endif
- /* Apply mode mask */
- ret &= lp_create_mask(SNUM(conn));
- /* Add in force bits */
- ret |= lp_force_create_mode(SNUM(conn));
+ if (VALID_STAT(*pst) && S_ISDIR(pst->st_mode)) {
+ ret &= lp_dir_mask(SNUM(conn));
+ /* Add in force bits */
+ ret |= lp_force_dir_mode(SNUM(conn));
+ } else {
+ /* Apply mode mask */
+ ret &= lp_create_mask(SNUM(conn));
+ /* Add in force bits */
+ ret |= lp_force_create_mode(SNUM(conn));
+ }
return ret;
}
@@ -2156,9 +2165,36 @@ NTSTATUS set_delete_on_close_internal(files_struct *fsp, BOOL delete_on_close)
Returns true if this pathname is within the share, and thus safe.
****************************************************************************/
-static BOOL ensure_link_is_safe(connection_struct *conn, char *link_dest)
+static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in, char *link_dest_out)
{
- return False;
+#ifdef PATH_MAX
+ char resolved_name[PATH_MAX+1];
+#else
+ pstring resolved_name;
+#endif
+ pstring link_dest;
+
+ pstrcpy(link_dest, link_dest_in);
+
+ if (*link_dest != '/') {
+ pstrcpy(link_dest, conn->connectpath);
+ pstrcat(link_dest, link_dest_in);
+ }
+
+ if (conn->vfs_ops.realpath(conn,dos_to_unix(link_dest,False),resolved_name) == NULL)
+ return -1;
+
+ pstrcpy(link_dest_out, unix_to_dos(resolved_name,False));
+
+ /*
+ * Check if the link is within the share.
+ */
+
+ if (strncmp(conn->connectpath, link_dest_out, strlen(conn->connectpath))) {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
}
/****************************************************************************
@@ -2172,7 +2208,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
char *pdata = *ppdata;
uint16 tran_call = SVAL(inbuf, smb_setup0);
uint16 info_level;
- int mode=0;
+ int dosmode=0;
SMB_OFF_T size=0;
struct utimbuf tvs;
SMB_STRUCT_STAT sbuf;
@@ -2276,6 +2312,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions())
return ERROR_DOS(ERRDOS,ERRunknownlevel);
+ if (VALID_STAT(sbuf))
+ unixmode = sbuf.st_mode;
+
DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n",
tran_call,fname,info_level,total_data));
@@ -2290,7 +2329,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
size = sbuf.st_size;
tvs.modtime = sbuf.st_mtime;
tvs.actime = sbuf.st_atime;
- mode = dos_mode(conn,fname,&sbuf);
+ dosmode = dos_mode(conn,fname,&sbuf);
set_owner = VALID_STAT(sbuf) ? sbuf.st_uid : (uid_t)UID_NO_CHANGE;
set_grp = VALID_STAT(sbuf) ? sbuf.st_gid : (gid_t)GID_NO_CHANGE;
@@ -2313,7 +2352,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
/* write time */
tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite);
- mode = SVAL(pdata,l1_attrFile);
+ dosmode = SVAL(pdata,l1_attrFile);
size = IVAL(pdata,l1_cbFile);
break;
}
@@ -2328,7 +2367,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
tvs.actime = make_unix_date2(pdata+8);
tvs.modtime = make_unix_date2(pdata+12);
size = IVAL(pdata,16);
- mode = IVAL(pdata,24);
+ dosmode = IVAL(pdata,24);
break;
/* XXXX nor this. not in cifs6.txt, either. */
@@ -2339,7 +2378,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
tvs.actime = make_unix_date2(pdata+8);
tvs.modtime = make_unix_date2(pdata+12);
size = IVAL(pdata,16);
- mode = IVAL(pdata,24);
+ dosmode = IVAL(pdata,24);
break;
case SMB_SET_FILE_BASIC_INFO:
@@ -2369,7 +2408,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
: write_time);
/* attributes */
- mode = IVAL(pdata,32);
+ dosmode = IVAL(pdata,32);
break;
}
@@ -2490,6 +2529,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
*/
case SMB_SET_FILE_UNIX_BASIC:
+ {
+ uint32 raw_unixmode;
if (total_data < 100)
return(ERROR_DOS(ERRDOS,ERRinvalidparam));
@@ -2509,7 +2550,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
pdata += 8;
set_grp = (gid_t)IVAL(pdata,0);
pdata += 8;
- unixmode = unix_perms_from_wire(conn, IVAL(pdata,20));
+ raw_unixmode = IVAL(pdata,20);
+ unixmode = unix_perms_from_wire(conn, &sbuf, raw_unixmode);
if (!VALID_STAT(sbuf)) {
@@ -2533,6 +2575,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
if (tran_call == TRANSACT2_SETFILEINFO)
return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ if (raw_unixmode == MODE_NO_CHANGE)
+ return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
dev = makedev(dev_major, dev_minor);
/* We can only create as the owner/group we are. */
@@ -2546,6 +2591,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
file_type != UNIX_TYPE_FIFO)
return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \
+0%o for file %s\n", (double)dev, unixmode, fname ));
+
/* Ok - do the mknod. */
if (conn->vfs_ops.mknod(conn,dos_to_unix(fname,False), unixmode, dev) != 0)
return(UNIXERROR(ERRDOS,ERRnoaccess));
@@ -2557,7 +2605,40 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
}
+ /*
+ * Deal with the UNIX specific mode set.
+ */
+
+ if (raw_unixmode != MODE_NO_CHANGE) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC setting mode 0%o for file %s\n",
+ unixmode, fname ));
+ if (vfs_chmod(conn,fname,unixmode) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Deal with the UNIX specific uid set.
+ */
+
+ if ((set_owner != (uid_t)UID_NO_CHANGE) && (sbuf.st_uid != set_owner)) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing owner %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (vfs_chown(conn,fname,set_owner, (gid_t)-1) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
+
+ /*
+ * Deal with the UNIX specific gid set.
+ */
+
+ if ((set_grp != (uid_t)GID_NO_CHANGE) && (sbuf.st_gid != set_grp)) {
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC changing group %u for file %s\n",
+ (unsigned int)set_owner, fname ));
+ if (vfs_chown(conn,fname,(uid_t)-1, set_grp) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
+ }
break;
+ }
case SMB_SET_FILE_UNIX_LINK:
{
@@ -2574,10 +2655,14 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
pstrcpy(link_dest, pdata);
- if (!ensure_link_is_safe(conn, link_dest))
- return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ if (ensure_link_is_safe(conn, link_dest, link_dest) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
dos_to_unix(link_dest, True);
dos_to_unix(fname, True);
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
+ fname, link_dest ));
+
if (conn->vfs_ops.symlink(conn,link_dest,fname) != 0)
return(UNIXERROR(ERRDOS,ERRnoaccess));
SSVAL(params,0,0);
@@ -2597,11 +2682,15 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
pstrcpy(link_dest, pdata);
- if (!ensure_link_is_safe(conn, link_dest))
- return(ERROR_DOS(ERRDOS,ERRnoaccess));
+ if (ensure_link_is_safe(conn, link_dest, link_dest) != 0)
+ return(UNIXERROR(ERRDOS,ERRnoaccess));
dos_to_unix(link_dest, True);
dos_to_unix(fname, True);
+
+ DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n",
+ fname, link_dest ));
+
if (conn->vfs_ops.link(conn,link_dest,fname) != 0)
return(UNIXERROR(ERRDOS,ERRnoaccess));
SSVAL(params,0,0);
@@ -2623,7 +2712,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
DEBUG(6,("actime: %s " , ctime(&tvs.actime)));
DEBUG(6,("modtime: %s ", ctime(&tvs.modtime)));
DEBUG(6,("size: %.0f ", (double)size));
- DEBUG(6,("mode: %x\n" , mode));
+ DEBUG(6,("dosmode: %x\n" , dosmode));
if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) ||
(info_level == SMB_SET_FILE_ALLOCATION_INFO) ||
@@ -2668,12 +2757,12 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
}
/* check the mode isn't different, before changing it */
- if ((mode != 0) && (mode != dos_mode(conn, fname, &sbuf))) {
+ if ((dosmode != 0) && (dosmode != dos_mode(conn, fname, &sbuf))) {
DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n",
- fname, mode ));
+ fname, dosmode ));
- if(file_chmod(conn, fname, mode, NULL)) {
+ if(file_chmod(conn, fname, dosmode, NULL)) {
DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno)));
return(UNIXERROR(ERRDOS,ERRnoaccess));
}
diff --git a/source/smbd/vfs-wrap.c b/source/smbd/vfs-wrap.c
index 5e04d9af584..372dddf589f 100644
--- a/source/smbd/vfs-wrap.c
+++ b/source/smbd/vfs-wrap.c
@@ -702,6 +702,21 @@ int vfswrap_mknod(connection_struct *conn, const char *pathname, mode_t mode, SM
return result;
}
+char *vfswrap_realpath(connection_struct *conn, const char *path, char *resolved_path)
+{
+ char *result;
+
+ START_PROFILE(syscall_realpath);
+
+#ifdef VFS_CHECK_NULL
+ if ((path == NULL) || (resolved_path == NULL))
+ smb_panic("NULL pointer passed to vfswrap_realpath()\n");
+#endif
+ result = sys_realpath(path, resolved_path);
+ END_PROFILE(syscall_realpath);
+ return result;
+}
+
size_t vfswrap_fget_nt_acl(files_struct *fsp, int fd, SEC_DESC **ppdesc)
{
size_t result;
diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c
index f7eed62fe81..68eb8d77bb2 100644
--- a/source/smbd/vfs.c
+++ b/source/smbd/vfs.c
@@ -74,6 +74,7 @@ struct vfs_ops default_vfs_ops = {
vfswrap_readlink,
vfswrap_link,
vfswrap_mknod,
+ vfswrap_realpath,
vfswrap_fget_nt_acl,
vfswrap_get_nt_acl,