summaryrefslogtreecommitdiff
path: root/source3/smbd/vfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/vfs.c')
-rw-r--r--source3/smbd/vfs.c67
1 files changed, 65 insertions, 2 deletions
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index de7b90df72..6574a34eaa 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -780,6 +780,63 @@ char *vfs_GetWd(connection_struct *conn, char *path)
return (path);
}
+
+/* check if the file 'nmae' is a symlink, in that case check that it point to
+ a file that reside under the 'dir' tree */
+
+static BOOL readlink_check(connection_struct *conn, char *dir, char *name)
+{
+ BOOL ret = True;
+ pstring flink;
+ pstring cleanlink;
+ pstring savedir;
+ pstring realdir;
+ size_t reallen;
+
+ if (!vfs_GetWd(conn, savedir)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s %s\n", name, dir));
+ return False;
+ }
+
+ if (vfs_ChDir(conn, dir) != 0) {
+ DEBUG(0,("couldn't vfs_ChDir to %s\n", dir));
+ return False;
+ }
+
+ if (!vfs_GetWd(conn, realdir)) {
+ DEBUG(0,("couldn't vfs_GetWd for %s\n", dir));
+ vfs_ChDir(conn, savedir);
+ return(False);
+ }
+
+ reallen = strlen(realdir);
+ if (realdir[reallen -1] == '/') {
+ reallen--;
+ realdir[reallen] = 0;
+ }
+
+ if (conn->vfs_ops.readlink(conn, name, flink, sizeof(pstring) -1) != -1) {
+ DEBUG(3,("reduce_name: file path name %s is a symlink\nChecking it's path\n", name));
+ if (*flink == '/') {
+ pstrcpy(cleanlink, flink);
+ } else {
+ pstrcpy(cleanlink, realdir);
+ pstrcat(cleanlink, "/");
+ pstrcat(cleanlink, flink);
+ }
+ unix_clean_name(cleanlink);
+
+ if (strncmp(cleanlink, realdir, reallen) != 0) {
+ DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n", name, realdir, cleanlink, (int)reallen));
+ ret = False;
+ }
+ }
+
+ vfs_ChDir(conn, savedir);
+
+ return ret;
+}
+
/*******************************************************************
Reduce a file name, removing .. elements and checking that
it is below dir in the heirachy. This uses vfs_GetWd() and so must be run
@@ -824,7 +881,7 @@ BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL wideli
p = strrchr_m(base_name,'/');
if (!p)
- return(True);
+ return readlink_check(conn, dir, s);
if (!vfs_GetWd(conn,wd)) {
DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir));
@@ -858,7 +915,7 @@ BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL wideli
if (!vfs_GetWd(conn,newname)) {
vfs_ChDir(conn,wd);
- DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,dir2));
+ DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name));
return(False);
}
@@ -878,6 +935,11 @@ BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL wideli
return(False);
}
+ if (!readlink_check(conn, dir, newname)) {
+ DEBUG(2, ("Bad access attemt? %s is a symlink outside the share path", s));
+ return(False);
+ }
+
if (relative) {
if (newname[l] == '/')
pstrcpy(s,newname + l + 1);
@@ -896,3 +958,4 @@ BOOL reduce_name(connection_struct *conn, pstring s, const char *dir,BOOL wideli
return(True);
#endif
}
+