diff options
author | Jeremy Allison <jra@samba.org> | 2002-01-14 23:12:45 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2002-01-14 23:12:45 +0000 |
commit | 73ee41adc64658e9d89e557de686fde4ef9365a2 (patch) | |
tree | 54a98ec456e3c7d4d1d225d3aea302afc7c49aa0 | |
parent | 1239feecf50ca4ab746c4fbeddd446989bf05721 (diff) | |
download | samba-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-x | source/configure | 2 | ||||
-rw-r--r-- | source/configure.in | 2 | ||||
-rw-r--r-- | source/include/config.h.in | 3 | ||||
-rw-r--r-- | source/include/profile.h | 2 | ||||
-rw-r--r-- | source/include/proto.h | 2 | ||||
-rw-r--r-- | source/include/trans2.h | 2 | ||||
-rw-r--r-- | source/include/vfs.h | 1 | ||||
-rw-r--r-- | source/lib/system.c | 15 | ||||
-rw-r--r-- | source/smbd/trans2.c | 133 | ||||
-rw-r--r-- | source/smbd/vfs-wrap.c | 15 | ||||
-rw-r--r-- | source/smbd/vfs.c | 1 |
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, |