diff options
Diffstat (limited to 'source3/smbd/vfs.c')
-rw-r--r-- | source3/smbd/vfs.c | 67 |
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 } + |