From 89db7e100193c3b895180730f49061424a806c8e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 13 May 2004 00:20:50 +0000 Subject: r656: Make widelinks use realpath(). Tidy up cases where we need to become a service. Jeremy. (This used to be commit a03b6a05e02ec8415efc0e8ceade102e06f8fffe) --- source3/configure.in | 14 +++ source3/smbd/conn.c | 1 + source3/smbd/filename.c | 2 +- source3/smbd/process.c | 7 +- source3/smbd/service.c | 6 +- source3/smbd/vfs.c | 237 ++++++++++++++++++++---------------------------- 6 files changed, 119 insertions(+), 148 deletions(-) diff --git a/source3/configure.in b/source3/configure.in index f4383ea337..7d6cfee111 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -2277,7 +2277,21 @@ if test x"$samba_cv_BROKEN_NISPLUS_INCLUDE_FILES" = x"yes"; then AC_DEFINE(BROKEN_NISPLUS_INCLUDE_FILES,1,[Whether the nisplus include files are broken]) fi +AC_CACHE_CHECK([if the realpath function allows a NULL argument],samba_cv_REALPATH_TAKES_NULL,[ +AC_TRY_RUN([ +#include +#include +main() { + char *newpath = realpath("/tmp", NULL); + exit ((newpath != NULL) ? 0 : 1); +} +], +samba_cv_REALPATH_TAKES_NULL=yes,samba_cv_REALPATH_TAKES_NULL=no,samba_cv_REALPATH_TAKES_NULL=cross)]) +if test x"$samba_cv_REALPATH_TAKES_NULL" = x"yes"; then + AC_DEFINE(REALPATH_TAKES_NULL,1,[Whether the realpath function allows NULL]) +fi +A ################################################# # check for smbwrapper support AC_MSG_CHECKING(whether to use smbwrapper) diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c index 9bac0acdb9..e083e14426 100644 --- a/source3/smbd/conn.c +++ b/source3/smbd/conn.c @@ -161,6 +161,7 @@ void conn_close_all(void) connection_struct *conn, *next; for (conn=Connections;conn;conn=next) { next=conn->next; + set_current_service(conn, True); close_cnum(conn, conn->vuid); } } diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 692c7f7610..a7223e7527 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -399,7 +399,7 @@ BOOL check_name(pstring name,connection_struct *conn) } if (!lp_widelinks(SNUM(conn))) { - ret = reduce_name(conn,name,conn->connectpath); + ret = reduce_name(conn,name); } /* Check if we are allowing users to follow symlinks */ diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 966bb63c1e..d0dfc6dd7d 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -344,6 +344,7 @@ force write permissions on print services. #define CAN_IPC (1<<3) #define AS_GUEST (1<<5) #define QUEUE_IN_OPLOCK (1<<6) +#define DO_CHDIR (1<<7) /* define a list of possible SMB messages and their corresponding @@ -373,7 +374,7 @@ static const struct smb_message_struct { /* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER | QUEUE_IN_OPLOCK }, /* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, /* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER}, -/* 0x11 */ { "SMBexit",reply_exit,0}, +/* 0x11 */ { "SMBexit",reply_exit,DO_CHDIR}, /* 0x12 */ { "SMBlseek",reply_lseek,AS_USER}, /* 0x13 */ { "SMBlockread",reply_lockread,AS_USER}, /* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER}, @@ -469,7 +470,7 @@ static const struct smb_message_struct { /* 0x6e */ { NULL, NULL, 0 }, /* 0x6f */ { NULL, NULL, 0 }, /* 0x70 */ { "SMBtcon",reply_tcon,0}, -/* 0x71 */ { "SMBtdis",reply_tdis,0}, +/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR}, /* 0x72 */ { "SMBnegprot",reply_negprot,0}, /* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0}, /* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ @@ -754,7 +755,7 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize return(ERROR_DOS(ERRSRV,ERRaccess)); /* load service specific parameters */ - if (conn && !set_current_service(conn,(flags & AS_USER)?True:False)) + if (conn && !set_current_service(conn,(flags & (AS_USER|DO_CHDIR)?True:False))) return(ERROR_DOS(ERRSRV,ERRaccess)); /* does this protocol need to be run as guest? */ diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 93b017e94c..04cade9577 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -788,6 +788,9 @@ void close_cnum(connection_struct *conn, uint16 vuid) { DirCacheFlush(SNUM(conn)); + file_close_conn(conn); + dptr_closecnum(conn); + change_to_root_user(); DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n", @@ -799,9 +802,6 @@ void close_cnum(connection_struct *conn, uint16 vuid) yield_connection(conn, lp_servicename(SNUM(conn))); - file_close_conn(conn); - dptr_closecnum(conn); - /* make sure we leave the directory available for unmount */ vfs_ChDir(conn, "/"); diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 533220e7df..0f3d591ebb 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -784,168 +784,123 @@ 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, const 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 (SMB_VFS_READLINK(conn, name, flink, sizeof(pstring) -1) != -1) { - DEBUG(3,("readlink_check: 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 - on the system that has the referenced file system. + it is below dir in the heirachy. This uses realpath. ********************************************************************/ -BOOL reduce_name(connection_struct *conn, pstring s, const char *dir) +BOOL reduce_name(connection_struct *conn, pstring fname) { -#ifndef REDUCE_PATHS - return True; +#ifdef REALPATH_TAKES_NULL + BOOL free_resolved_name = True; #else - pstring dir2; - pstring wd; - pstring base_name; - pstring newname; - char *p=NULL; - BOOL relative = (*s != '/'); - - *dir2 = *wd = *base_name = *newname = 0; - - DEBUG(3,("reduce_name [%s] [%s]\n",s,dir)); - - /* We know there are no double slashes as this comes from srvstr_get_path(). - and has gone through check_path_syntax(). JRA */ - - pstrcpy(base_name,s); - p = strrchr_m(base_name,'/'); +#ifdef PATH_MAX + char resolved_name_buf[PATH_MAX+1]; +#else + pstring resolved_name_buf; +#endif + BOOL free_resolved_name = False; +#endif + char *resolved_name = NULL; + size_t con_path_len = strlen(conn->connectpath); + char *p = NULL; - if (!p) - return readlink_check(conn, dir, s); + DEBUG(3,("reduce_name [%s] [%s]\n", fname, conn->connectpath)); - if (!vfs_GetWd(conn,wd)) { - DEBUG(0,("couldn't vfs_GetWd for %s %s\n",s,dir)); - return(False); - } +#ifdef REALPATH_TAKES_NULL + resolved_name = SMB_VFS_REALPATH(conn,fname,NULL); +#else + resolved_name = SMB_VFS_REALPATH(conn,fname,resolved_name_buf); +#endif - if (vfs_ChDir(conn,dir) != 0) { - DEBUG(0,("couldn't vfs_ChDir to %s\n",dir)); - return(False); + if (!resolved_name) { + switch (errno) { + case ENOTDIR: + DEBUG(3,("reduce_name: Component not a directory in getting realpath for %s\n", fname)); + return False; + case ENOENT: + { + pstring tmp_fname; + fstring last_component; + /* Last component didn't exist. Remove it and try and canonicalise the directory. */ + + pstrcpy(tmp_fname, fname); + p = strrchr_m(tmp_fname, '/'); + if (p) { + *p++ = '\0'; + fstrcpy(last_component, p); + } +#ifdef REALPATH_TAKES_NULL + resolved_name = SMB_VFS_REALPATH(conn,tmp_fname,NULL); +#else + resolved_name = SMB_VFS_REALPATH(conn,tmp_fname,resolved_name_buf); +#endif + if (!resolved_name) { + DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname)); + return False; + } + pstrcpy(tmp_fname, resolved_name); + pstrcat(tmp_fname, "/"); + pstrcat(tmp_fname, last_component); +#ifdef REALPATH_TAKES_NULL + SAFE_FREE(resolved_name); + resolved_name = strdup(tmp_fname); + if (!resolved_name) { + DEBUG(0,("reduce_name: malloc fail for %s\n", tmp_fname)); + return False; + } +#else +#ifdef PATH_MAX + safe_strcpy(resolved_name_buf, tmp_fname, PATH_MAX); +#else + pstrcpy(pstring resolved_name_buf, tmp_fname); +#endif + resolved_name = resolved_name_buf; +#endif + break; + } + default: + DEBUG(1,("reduce_name: couldn't get realpath for %s\n", fname)); + return False; + } } - if (!vfs_GetWd(conn,dir2)) { - DEBUG(0,("couldn't vfs_GetWd for %s\n",dir)); - vfs_ChDir(conn,wd); - return(False); - } + DEBUG(10,("reduce_name realpath [%s] -> [%s]\n", fname, resolved_name)); - if (p && (p != base_name)) { - *p = 0; - if (strcmp(p+1,".")==0) - p[1]=0; - if (strcmp(p+1,"..")==0) - *p = '/'; + if (*resolved_name != '/') { + DEBUG(0,("reduce_name: realpath doesn't return absolute paths !\n")); + if (free_resolved_name) + SAFE_FREE(resolved_name); + return False; } - if (vfs_ChDir(conn,base_name) != 0) { - vfs_ChDir(conn,wd); - DEBUG(3,("couldn't vfs_ChDir for %s %s basename=%s\n",s,dir,base_name)); - return(False); + if (strncmp(conn->connectpath, resolved_name, con_path_len) != 0) { + DEBUG(2, ("reduce_name: Bad access attemt: %s is a symlink outside the share path", fname)); + if (free_resolved_name) + SAFE_FREE(resolved_name); + return False; } - if (!vfs_GetWd(conn,newname)) { - vfs_ChDir(conn,wd); - DEBUG(2,("couldn't get vfs_GetWd for %s %s\n",s,base_name)); - return(False); + /* Move path the connect path to the last part of the filename. */ + p = resolved_name + con_path_len; + if (*p == '/') { + p++; } - if (p && (p != base_name)) { - pstrcat(newname,"/"); - pstrcat(newname,p+1); + if (!*p) { + pstrcpy(resolved_name, "."); + p = resolved_name; } - { - size_t l = strlen(dir2); - char *last_slash = strrchr_m(dir2, '/'); - - if (last_slash && (last_slash[1] == '\0')) - l--; - - if (strncmp(newname,dir2,l) != 0) { - vfs_ChDir(conn,wd); - DEBUG(2,("Bad access attempt: s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,(int)l)); - 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); - else - pstrcpy(s,newname+l); - } else - pstrcpy(s,newname); + if (!lp_symlinks(SNUM(conn)) && (strcmp(fname, p)!=0)) { + DEBUG(3,("reduce_name: denied: file path name %s is a symlink\n",fname)); + if (free_resolved_name) + SAFE_FREE(resolved_name); + return False; } - vfs_ChDir(conn,wd); - - if (strlen(s) == 0) - pstrcpy(s,"./"); - - DEBUG(3,("reduced to %s\n",s)); + DEBUG(3,("reduce_name: %s reduced to %s\n", fname, p)); + if (free_resolved_name) + SAFE_FREE(resolved_name); return(True); -#endif } -- cgit