r4088: Get medieval on our ass about malloc.... :-). Take control of all our allocation
[abartlet/samba.git/.git] / source3 / smbd / vfs.c
index a415e0470e27a7e59fd987169fd6708efcfcefc9..0102739fe3992ce587e3c1896c5e2f4463cabb43 100644 (file)
@@ -62,6 +62,9 @@ static struct vfs_ops default_vfs = {
        
                vfswrap_opendir,
                vfswrap_readdir,
+               vfswrap_seekdir,
+               vfswrap_telldir,
+               vfswrap_rewinddir,
                vfswrap_mkdir,
                vfswrap_rmdir,
                vfswrap_closedir,
@@ -185,7 +188,7 @@ NTSTATUS smb_register_vfs(int version, const char *name, vfs_op_tuple *vfs_op_tu
                return NT_STATUS_OBJECT_NAME_COLLISION;
        }
 
-       entry = smb_xmalloc(sizeof(struct vfs_init_function_entry));
+       entry = SMB_XMALLOC_P(struct vfs_init_function_entry);
        entry->name = smb_xstrdup(name);
        entry->vfs_op_tuples = vfs_op_tuples;
 
@@ -258,7 +261,7 @@ BOOL vfs_init_custom(connection_struct *conn, const char *vfs_object)
                return False;
        }
 
-       handle = (vfs_handle_struct *)talloc_zero(conn->mem_ctx,sizeof(vfs_handle_struct));
+       handle = TALLOC_ZERO_P(conn->mem_ctx,vfs_handle_struct);
        if (!handle) {
                DEBUG(0,("talloc_zero() failed!\n"));
                SAFE_FREE(module_name);
@@ -611,13 +614,13 @@ SMB_OFF_T vfs_transfer_file(files_struct *in, files_struct *out, SMB_OFF_T n)
 
 char *vfs_readdirname(connection_struct *conn, void *p)
 {
-       struct dirent *ptr= NULL;
+       SMB_STRUCT_DIRENT *ptr= NULL;
        char *dname;
 
        if (!p)
                return(NULL);
 
-       ptr = (struct dirent *)SMB_VFS_READDIR(conn,p);
+       ptr = SMB_VFS_READDIR(conn,p);
        if (!ptr)
                return(NULL);
 
@@ -681,7 +684,7 @@ static void array_promote(char *array,int elsize,int element)
        if (element == 0)
                return;
 
-       p = (char *)malloc(elsize);
+       p = (char *)SMB_MALLOC(elsize);
 
        if (!p) {
                DEBUG(5,("array_promote: malloc fail\n"));
@@ -784,12 +787,37 @@ char *vfs_GetWd(connection_struct *conn, char *path)
        return (path);
 }
 
+BOOL canonicalize_path(connection_struct *conn, pstring path)
+{
+#ifdef REALPATH_TAKES_NULL
+       char *resolved_name = SMB_VFS_REALPATH(conn,path,NULL);
+       if (!resolved_name) {
+               return False;
+       }
+       pstrcpy(path, resolved_name);
+       SAFE_FREE(resolved_name);
+       return True;
+#else
+#ifdef PATH_MAX
+        char resolved_name_buf[PATH_MAX+1];
+#else
+        pstring resolved_name_buf;
+#endif
+       char *resolved_name = SMB_VFS_REALPATH(conn,path,resolved_name_buf);
+       if (!resolved_name) {
+               return False;
+       }
+       pstrcpy(path, resolved_name);
+       return True;
+#endif /* REALPATH_TAKES_NULL */
+}
+
 /*******************************************************************
  Reduce a file name, removing .. elements and checking that
  it is below dir in the heirachy. This uses realpath.
 ********************************************************************/
 
-BOOL reduce_name(connection_struct *conn, pstring fname)
+BOOL reduce_name(connection_struct *conn, const pstring fname)
 {
 #ifdef REALPATH_TAKES_NULL
        BOOL free_resolved_name = True;
@@ -804,6 +832,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
        char *resolved_name = NULL;
        size_t con_path_len = strlen(conn->connectpath);
        char *p = NULL;
+       int saved_errno = errno;
 
        DEBUG(3,("reduce_name [%s] [%s]\n", fname, conn->connectpath));
 
@@ -817,6 +846,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
                switch (errno) {
                        case ENOTDIR:
                                DEBUG(3,("reduce_name: Component not a directory in getting realpath for %s\n", fname));
+                               errno = saved_errno;
                                return False;
                        case ENOENT:
                        {
@@ -841,6 +871,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
 #endif
                                if (!resolved_name) {
                                        DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname));
+                                       errno = saved_errno;
                                        return False;
                                }
                                pstrcpy(tmp_fname, resolved_name);
@@ -848,9 +879,10 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
                                pstrcat(tmp_fname, last_component);
 #ifdef REALPATH_TAKES_NULL
                                SAFE_FREE(resolved_name);
-                               resolved_name = strdup(tmp_fname);
+                               resolved_name = SMB_STRDUP(tmp_fname);
                                if (!resolved_name) {
                                        DEBUG(0,("reduce_name: malloc fail for %s\n", tmp_fname));
+                                       errno = saved_errno;
                                        return False;
                                }
 #else
@@ -865,6 +897,8 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
                        }
                        default:
                                DEBUG(1,("reduce_name: couldn't get realpath for %s\n", fname));
+                               /* Don't restore the saved errno. We need to return the error that
+                                  realpath caused here as it was not one of the cases we handle. JRA. */
                                return False;
                }
        }
@@ -875,36 +909,40 @@ BOOL reduce_name(connection_struct *conn, pstring fname)
                DEBUG(0,("reduce_name: realpath doesn't return absolute paths !\n"));
                if (free_resolved_name)
                        SAFE_FREE(resolved_name);
+               errno = saved_errno;
                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));
+       /* Check for widelinks allowed. */
+       if (!lp_widelinks(SNUM(conn)) && (strncmp(conn->connectpath, resolved_name, con_path_len) != 0)) {
+               DEBUG(2, ("reduce_name: Bad access attempt: %s is a symlink outside the share path", fname));
                if (free_resolved_name)
                        SAFE_FREE(resolved_name);
+               errno = EACCES;
                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) {
-               pstrcpy(resolved_name, ".");
-               p = resolved_name;
-       }
-
-       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;
-       }
+        /* Check if we are allowing users to follow symlinks */
+        /* Patch from David Clerc <David.Clerc@cui.unige.ch>
+                University of Geneva */
+                                                                                                                                                    
+#ifdef S_ISLNK
+        if (!lp_symlinks(SNUM(conn))) {
+                SMB_STRUCT_STAT statbuf;
+                if ( (SMB_VFS_LSTAT(conn,fname,&statbuf) != -1) &&
+                                (S_ISLNK(statbuf.st_mode)) ) {
+                       if (free_resolved_name)
+                               SAFE_FREE(resolved_name);
+                        DEBUG(3,("reduce_name: denied: file path name %s is a symlink\n",resolved_name));
+                        errno = EACCES;
+                       return False;
+                }
+        }
+#endif
 
        DEBUG(3,("reduce_name: %s reduced to %s\n", fname, p));
        if (free_resolved_name)
                SAFE_FREE(resolved_name);
+       errno = saved_errno;
        return(True);
 }