#include "smbd/globals.h"
#include "libcli/security/security.h"
#include "lib/util/bitmap.h"
+#include "../lib/util/memcache.h"
+#include "../librpc/gen_ndr/open_files.h"
/*
This module implements directory related functions for Samba.
#define START_OF_DIRECTORY_OFFSET ((long)0)
#define DOT_DOT_DIRECTORY_OFFSET ((long)0x80000000)
+/* "Special" directory offsets in 32-bit wire format. */
+#define WIRE_END_OF_DIRECTORY_OFFSET ((uint32_t)0xFFFFFFFF)
+#define WIRE_START_OF_DIRECTORY_OFFSET ((uint32_t)0)
+#define WIRE_DOT_DOT_DIRECTORY_OFFSET ((uint32_t)0x80000000)
+
/* Make directory handle internals available. */
struct name_cache_entry {
struct smb_Dir {
connection_struct *conn;
- SMB_STRUCT_DIR *dir;
+ DIR *dir;
long offset;
char *dir_path;
size_t name_cache_size;
struct name_cache_entry *name_cache;
unsigned int name_cache_index;
unsigned int file_number;
+ files_struct *fsp; /* Back pointer to containing fsp, only
+ set from OpenDir_fsp(). */
};
struct dptr_struct {
struct dptr_struct *next, *prev;
int dnum;
- uint16 spid;
+ uint16_t spid;
struct connection_struct *conn;
struct smb_Dir *dir_hnd;
bool expect_close;
char *wcard;
- uint32 attr;
+ uint32_t attr;
char *path;
bool has_wild; /* Set to true if the wcard entry has MS wildcard characters in it. */
bool did_stat; /* Optimisation for non-wcard searches. */
+ bool priv; /* Directory handle opened with privilege. */
+ uint32_t counter;
+ struct memcache *dptr_cache;
};
static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
files_struct *fsp,
const char *mask,
- uint32 attr);
-
-#define INVALID_DPTR_KEY (-3)
-
-/****************************************************************************
- Make a dir struct.
-****************************************************************************/
-
-bool make_dir_struct(TALLOC_CTX *ctx,
- char *buf,
- const char *mask,
- const char *fname,
- SMB_OFF_T size,
- uint32 mode,
- time_t date,
- bool uc)
-{
- char *p;
- char *mask2 = talloc_strdup(ctx, mask);
+ uint32_t attr);
- if (!mask2) {
- return False;
- }
+static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset);
- if ((mode & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- size = 0;
- }
-
- memset(buf+1,' ',11);
- if ((p = strchr_m(mask2,'.')) != NULL) {
- *p = 0;
- push_ascii(buf+1,mask2,8, 0);
- push_ascii(buf+9,p+1,3, 0);
- *p = '.';
- } else {
- push_ascii(buf+1,mask2,11, 0);
- }
-
- memset(buf+21,'\0',DIR_STRUCT_SIZE-21);
- SCVAL(buf,21,mode);
- srv_put_dos_date(buf,22,date);
- SSVAL(buf,26,size & 0xFFFF);
- SSVAL(buf,28,(size >> 16)&0xFFFF);
- /* We only uppercase if FLAGS2_LONG_PATH_COMPONENTS is zero in the input buf.
- Strange, but verified on W2K3. Needed for OS/2. JRA. */
- push_ascii(buf+30,fname,12, uc ? STR_UPPER : 0);
- DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname));
- return True;
-}
+#define INVALID_DPTR_KEY (-3)
/****************************************************************************
Initialise the dir bitmap.
if (dptr->dir_hnd) {
DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum));
TALLOC_FREE(dptr->dir_hnd);
+ TALLOC_FREE(dptr->dptr_cache);
+ dptr->counter = 0;
}
}
dptr->wcard, dptr->attr))) {
DEBUG(4,("dptr_get: Failed to open %s (%s)\n",dptr->path,
strerror(errno)));
- return False;
+ return NULL;
}
}
DLIST_PROMOTE(sconn->searches.dirptrs,dptr);
Get the dir path for a dir index.
****************************************************************************/
-char *dptr_path(struct smbd_server_connection *sconn, int key)
+const char *dptr_path(struct smbd_server_connection *sconn, int key)
{
struct dptr_struct *dptr = dptr_get(sconn, key, false);
if (dptr)
Get the dir wcard for a dir index.
****************************************************************************/
-char *dptr_wcard(struct smbd_server_connection *sconn, int key)
+const char *dptr_wcard(struct smbd_server_connection *sconn, int key)
{
struct dptr_struct *dptr = dptr_get(sconn, key, false);
if (dptr)
Get the dir attrib for a dir index.
****************************************************************************/
-uint16 dptr_attr(struct smbd_server_connection *sconn, int key)
+uint16_t dptr_attr(struct smbd_server_connection *sconn, int key)
{
struct dptr_struct *dptr = dptr_get(sconn, key, false);
if (dptr)
goto done;
}
+ if (sconn->using_smb2) {
+ goto done;
+ }
+
DLIST_REMOVE(sconn->searches.dirptrs, dptr);
/*
done:
TALLOC_FREE(dptr->dir_hnd);
-
- /* Lanman 2 specific code */
- SAFE_FREE(dptr->wcard);
- string_set(&dptr->path,"");
- SAFE_FREE(dptr);
+ TALLOC_FREE(dptr);
}
/****************************************************************************
****************************************************************************/
void dptr_closepath(struct smbd_server_connection *sconn,
- char *path,uint16 spid)
+ char *path,uint16_t spid)
{
struct dptr_struct *dptr, *next;
for(dptr = sconn->searches.dirptrs; dptr; dptr = next) {
}
}
+/****************************************************************************
+ Safely do an OpenDir as root, ensuring we're in the right place.
+****************************************************************************/
+
+static struct smb_Dir *open_dir_with_privilege(connection_struct *conn,
+ struct smb_request *req,
+ const char *path,
+ const char *wcard,
+ uint32_t attr)
+{
+ struct smb_Dir *dir_hnd = NULL;
+ struct smb_filename *smb_fname_cwd;
+ char *saved_dir = vfs_GetWd(talloc_tos(), conn);
+ struct privilege_paths *priv_paths = req->priv_paths;
+ int ret;
+
+ if (saved_dir == NULL) {
+ return NULL;
+ }
+
+ if (vfs_ChDir(conn, path) == -1) {
+ return NULL;
+ }
+
+ /* Now check the stat value is the same. */
+ smb_fname_cwd = synthetic_smb_fname(talloc_tos(), ".", NULL, NULL);
+
+ if (smb_fname_cwd == NULL) {
+ goto out;
+ }
+ ret = SMB_VFS_STAT(conn, smb_fname_cwd);
+ if (ret != 0) {
+ goto out;
+ }
+
+ if (!check_same_stat(&smb_fname_cwd->st, &priv_paths->parent_name.st)) {
+ DEBUG(0,("open_dir_with_privilege: stat mismatch between %s "
+ "and %s\n",
+ path,
+ smb_fname_str_dbg(&priv_paths->parent_name)));
+ goto out;
+ }
+
+ dir_hnd = OpenDir(NULL, conn, ".", wcard, attr);
+
+ out:
+
+ vfs_ChDir(conn, saved_dir);
+ return dir_hnd;
+}
+
/****************************************************************************
Create a new dir ptr. If the flag old_handle is true then we must allocate
from the bitmap range 0 - 255 as old SMBsearch directory handles are only
wcard must not be zero.
****************************************************************************/
-NTSTATUS dptr_create(connection_struct *conn, files_struct *fsp,
- const char *path, bool old_handle, bool expect_close,uint16 spid,
- const char *wcard, bool wcard_has_wild, uint32 attr, struct dptr_struct **dptr_ret)
+NTSTATUS dptr_create(connection_struct *conn,
+ struct smb_request *req,
+ files_struct *fsp,
+ const char *path, bool old_handle, bool expect_close,uint16_t spid,
+ const char *wcard, bool wcard_has_wild, uint32_t attr, struct dptr_struct **dptr_ret)
{
struct smbd_server_connection *sconn = conn->sconn;
struct dptr_struct *dptr = NULL;
struct smb_Dir *dir_hnd;
- NTSTATUS status;
if (fsp && fsp->is_directory && fsp->fh->fd != -1) {
path = fsp->fsp_name->base_name;
}
if (fsp) {
+ if (!(fsp->access_mask & SEC_DIR_LIST)) {
+ DEBUG(5,("dptr_create: directory %s "
+ "not open for LIST access\n",
+ path));
+ return NT_STATUS_ACCESS_DENIED;
+ }
dir_hnd = OpenDir_fsp(NULL, conn, fsp, wcard, attr);
} else {
- status = check_name(conn,path);
+ int ret;
+ bool backup_intent = (req && req->priv_paths);
+ struct smb_filename *smb_dname;
+ NTSTATUS status;
+
+ smb_dname = synthetic_smb_fname(talloc_tos(), path,
+ NULL, NULL);
+ if (smb_dname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (req != NULL && req->posix_pathnames) {
+ ret = SMB_VFS_LSTAT(conn, smb_dname);
+ } else {
+ ret = SMB_VFS_STAT(conn, smb_dname);
+ }
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (!S_ISDIR(smb_dname->st.st_ex_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+ status = smbd_check_access_rights(conn,
+ smb_dname,
+ backup_intent,
+ SEC_DIR_LIST);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
- dir_hnd = OpenDir(NULL, conn, path, wcard, attr);
+ if (backup_intent) {
+ dir_hnd = open_dir_with_privilege(conn,
+ req,
+ path,
+ wcard,
+ attr);
+ } else {
+ dir_hnd = OpenDir(NULL, conn, path, wcard, attr);
+ }
}
if (!dir_hnd) {
dptr_idleoldest(sconn);
}
- dptr = SMB_MALLOC_P(struct dptr_struct);
+ dptr = talloc_zero(NULL, struct dptr_struct);
if(!dptr) {
- DEBUG(0,("malloc fail in dptr_create.\n"));
+ DEBUG(0,("talloc fail in dptr_create.\n"));
TALLOC_FREE(dir_hnd);
return NT_STATUS_NO_MEMORY;
}
- ZERO_STRUCTP(dptr);
+ dptr->path = talloc_strdup(dptr, path);
+ if (!dptr->path) {
+ TALLOC_FREE(dptr);
+ TALLOC_FREE(dir_hnd);
+ return NT_STATUS_NO_MEMORY;
+ }
+ dptr->conn = conn;
+ dptr->dir_hnd = dir_hnd;
+ dptr->spid = spid;
+ dptr->expect_close = expect_close;
+ dptr->wcard = talloc_strdup(dptr, wcard);
+ if (!dptr->wcard) {
+ TALLOC_FREE(dptr);
+ TALLOC_FREE(dir_hnd);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if ((req != NULL && req->posix_pathnames) ||
+ (wcard[0] == '.' && wcard[1] == 0)) {
+ dptr->has_wild = True;
+ } else {
+ dptr->has_wild = wcard_has_wild;
+ }
+
+ dptr->attr = attr;
+
+ if (sconn->using_smb2) {
+ goto done;
+ }
if(old_handle) {
dptr->dnum = bitmap_find(sconn->searches.dptr_bmap, 0);
if(dptr->dnum == -1 || dptr->dnum > 254) {
DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum));
- SAFE_FREE(dptr);
+ TALLOC_FREE(dptr);
TALLOC_FREE(dir_hnd);
return NT_STATUS_TOO_MANY_OPENED_FILES;
}
if(dptr->dnum == -1 || dptr->dnum < 255) {
DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum));
- SAFE_FREE(dptr);
+ TALLOC_FREE(dptr);
TALLOC_FREE(dir_hnd);
return NT_STATUS_TOO_MANY_OPENED_FILES;
}
dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */
- string_set(&dptr->path,path);
- dptr->conn = conn;
- dptr->dir_hnd = dir_hnd;
- dptr->spid = spid;
- dptr->expect_close = expect_close;
- dptr->wcard = SMB_STRDUP(wcard);
- if (!dptr->wcard) {
- bitmap_clear(sconn->searches.dptr_bmap, dptr->dnum - 1);
- SAFE_FREE(dptr);
- TALLOC_FREE(dir_hnd);
- return NT_STATUS_NO_MEMORY;
- }
- if (lp_posix_pathnames() || (wcard[0] == '.' && wcard[1] == 0)) {
- dptr->has_wild = True;
- } else {
- dptr->has_wild = wcard_has_wild;
- }
-
- dptr->attr = attr;
-
DLIST_ADD(sconn->searches.dirptrs, dptr);
+done:
DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n",
dptr->dnum,path,expect_close));
void dptr_CloseDir(files_struct *fsp)
{
if (fsp->dptr) {
-/*
- * Ugly hack. We have defined fdopendir to return ENOSYS if dirfd also isn't
- * present. I hate Solaris. JRA.
- */
-#ifdef HAVE_DIRFD
- if (fsp->fh->fd != -1 &&
- fsp->dptr->dir_hnd &&
- dirfd(fsp->dptr->dir_hnd->dir)) {
- /* The call below closes the underlying fd. */
- fsp->fh->fd = -1;
- }
-#endif
+ /*
+ * The destructor for the struct smb_Dir
+ * (fsp->dptr->dir_hnd) now handles
+ * all resource deallocation.
+ */
dptr_close_internal(fsp->dptr);
fsp->dptr = NULL;
}
return dptr->dnum;
}
+bool dptr_get_priv(struct dptr_struct *dptr)
+{
+ return dptr->priv;
+}
+
+void dptr_set_priv(struct dptr_struct *dptr)
+{
+ dptr->priv = true;
+}
+
/****************************************************************************
Return the next visible file name, skipping veto'd and invisible files.
****************************************************************************/
Return the next visible file name, skipping veto'd and invisible files.
****************************************************************************/
-char *dptr_ReadDirName(TALLOC_CTX *ctx,
- struct dptr_struct *dptr,
- long *poffset,
- SMB_STRUCT_STAT *pst)
+static char *dptr_ReadDirName(TALLOC_CTX *ctx,
+ struct dptr_struct *dptr,
+ long *poffset,
+ SMB_STRUCT_STAT *pst)
{
struct smb_filename smb_fname_base;
char *name = NULL;
return NULL;
/* Create an smb_filename with stream_name == NULL. */
- ZERO_STRUCT(smb_fname_base);
- smb_fname_base.base_name = pathreal;
+ smb_fname_base = (struct smb_filename) { .base_name = pathreal };
if (SMB_VFS_STAT(dptr->conn, &smb_fname_base) == 0) {
*pst = smb_fname_base.st;
}
/****************************************************************************
- Add the name we're returning into the underlying cache.
+ Initialize variables & state data at the beginning of all search SMB requests.
****************************************************************************/
-
-void dptr_DirCacheAdd(struct dptr_struct *dptr, const char *name, long offset)
+void dptr_init_search_op(struct dptr_struct *dptr)
{
- DirCacheAdd(dptr->dir_hnd, name, offset);
+ SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir);
}
/****************************************************************************
- Initialize variables & state data at the beginning of all search SMB requests.
+ Map a native directory offset to a 32-bit cookie.
****************************************************************************/
-void dptr_init_search_op(struct dptr_struct *dptr)
+
+static uint32_t map_dir_offset_to_wire(struct dptr_struct *dptr, long offset)
{
- SMB_VFS_INIT_SEARCH_OP(dptr->conn, dptr->dir_hnd->dir);
+ DATA_BLOB key;
+ DATA_BLOB val;
+
+ if (offset == END_OF_DIRECTORY_OFFSET) {
+ return WIRE_END_OF_DIRECTORY_OFFSET;
+ } else if(offset == START_OF_DIRECTORY_OFFSET) {
+ return WIRE_START_OF_DIRECTORY_OFFSET;
+ } else if (offset == DOT_DOT_DIRECTORY_OFFSET) {
+ return WIRE_DOT_DOT_DIRECTORY_OFFSET;
+ }
+ if (sizeof(long) == 4) {
+ /* 32-bit machine. We can cheat... */
+ return (uint32_t)offset;
+ }
+ if (dptr->dptr_cache == NULL) {
+ /* Lazy initialize cache. */
+ dptr->dptr_cache = memcache_init(dptr, 0);
+ if (dptr->dptr_cache == NULL) {
+ return WIRE_END_OF_DIRECTORY_OFFSET;
+ }
+ } else {
+ /* Have we seen this offset before ? */
+ key.data = (void *)&offset;
+ key.length = sizeof(offset);
+ if (memcache_lookup(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ key,
+ &val)) {
+ uint32_t wire_offset;
+ SMB_ASSERT(val.length == sizeof(wire_offset));
+ memcpy(&wire_offset, val.data, sizeof(wire_offset));
+ DEBUG(10,("found wire %u <-> offset %ld\n",
+ (unsigned int)wire_offset,
+ (long)offset));
+ return wire_offset;
+ }
+ }
+ /* Allocate a new wire cookie. */
+ do {
+ dptr->counter++;
+ } while (dptr->counter == WIRE_START_OF_DIRECTORY_OFFSET ||
+ dptr->counter == WIRE_END_OF_DIRECTORY_OFFSET ||
+ dptr->counter == WIRE_DOT_DOT_DIRECTORY_OFFSET);
+ /* Store it in the cache. */
+ key.data = (void *)&offset;
+ key.length = sizeof(offset);
+ val.data = (void *)&dptr->counter;
+ val.length = sizeof(dptr->counter); /* MUST BE uint32_t ! */
+ memcache_add(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ key,
+ val);
+ /* And the reverse mapping for lookup from
+ map_wire_to_dir_offset(). */
+ memcache_add(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ val,
+ key);
+ DEBUG(10,("stored wire %u <-> offset %ld\n",
+ (unsigned int)dptr->counter,
+ (long)offset));
+ return dptr->counter;
}
/****************************************************************************
{
unsigned char *buf = (unsigned char *)buf1;
struct dptr_struct *dptr = dptr_get(sconn, key, false);
- uint32 offset;
+ uint32_t wire_offset;
if (!dptr) {
DEBUG(1,("filling null dirptr %d\n",key));
return(False);
}
- offset = (uint32)TellDir(dptr->dir_hnd);
+ wire_offset = map_dir_offset_to_wire(dptr,TellDir(dptr->dir_hnd));
DEBUG(6,("fill on key %u dirptr 0x%lx now at %d\n",key,
- (long)dptr->dir_hnd,(int)offset));
+ (long)dptr->dir_hnd,(int)wire_offset));
buf[0] = key;
- SIVAL(buf,1,offset);
+ SIVAL(buf,1,wire_offset);
return(True);
}
+/****************************************************************************
+ Map a 32-bit wire cookie to a native directory offset.
+****************************************************************************/
+
+static long map_wire_to_dir_offset(struct dptr_struct *dptr, uint32_t wire_offset)
+{
+ DATA_BLOB key;
+ DATA_BLOB val;
+
+ if (wire_offset == WIRE_END_OF_DIRECTORY_OFFSET) {
+ return END_OF_DIRECTORY_OFFSET;
+ } else if(wire_offset == WIRE_START_OF_DIRECTORY_OFFSET) {
+ return START_OF_DIRECTORY_OFFSET;
+ } else if (wire_offset == WIRE_DOT_DOT_DIRECTORY_OFFSET) {
+ return DOT_DOT_DIRECTORY_OFFSET;
+ }
+ if (sizeof(long) == 4) {
+ /* 32-bit machine. We can cheat... */
+ return (long)wire_offset;
+ }
+ if (dptr->dptr_cache == NULL) {
+ /* Logic error, cache should be initialized. */
+ return END_OF_DIRECTORY_OFFSET;
+ }
+ key.data = (void *)&wire_offset;
+ key.length = sizeof(wire_offset);
+ if (memcache_lookup(dptr->dptr_cache,
+ SMB1_SEARCH_OFFSET_MAP,
+ key,
+ &val)) {
+ /* Found mapping. */
+ long offset;
+ SMB_ASSERT(val.length == sizeof(offset));
+ memcpy(&offset, val.data, sizeof(offset));
+ DEBUG(10,("lookup wire %u <-> offset %ld\n",
+ (unsigned int)wire_offset,
+ (long)offset));
+ return offset;
+ }
+ return END_OF_DIRECTORY_OFFSET;
+}
+
/****************************************************************************
Fetch the dir ptr and seek it given the 5 byte server field.
****************************************************************************/
{
unsigned int key = *(unsigned char *)buf;
struct dptr_struct *dptr = dptr_get(sconn, key, false);
- uint32 offset;
+ uint32_t wire_offset;
long seekoff;
if (!dptr) {
return(NULL);
}
*num = key;
- offset = IVAL(buf,1);
- if (offset == (uint32)-1) {
- seekoff = END_OF_DIRECTORY_OFFSET;
- } else {
- seekoff = (long)offset;
- }
+ wire_offset = IVAL(buf,1);
+ seekoff = map_wire_to_dir_offset(dptr, wire_offset);
SeekDir(dptr->dir_hnd,seekoff);
DEBUG(3,("fetching dirptr %d for path %s at offset %d\n",
key, dptr->path, (int)seekoff));
return(dptr);
}
-/****************************************************************************
- Check that a file matches a particular file type.
-****************************************************************************/
-
-bool dir_check_ftype(connection_struct *conn, uint32 mode, uint32 dirtype)
-{
- uint32 mask;
-
- /* Check the "may have" search bits. */
- if (((mode & ~dirtype) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)) != 0)
- return False;
-
- /* Check the "must have" bits, which are the may have bits shifted eight */
- /* If must have bit is set, the file/dir can not be returned in search unless the matching
- file attribute is set */
- mask = ((dirtype >> 8) & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)); /* & 0x37 */
- if(mask) {
- if((mask & (mode & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))) == mask) /* check if matching attribute present */
- return True;
- else
- return False;
- }
-
- return True;
-}
-
static bool mangle_mask_match(connection_struct *conn,
const char *filename,
const char *mask)
long *_prev_offset)
{
connection_struct *conn = dirptr->conn;
- bool needslash;
+ size_t slashlen;
+ size_t pathlen;
+ bool dirptr_path_is_dot = ISDOT(dirptr->path);
*_smb_fname = NULL;
*_mode = 0;
- needslash = ( dirptr->path[strlen(dirptr->path) -1] != '/');
+ pathlen = strlen(dirptr->path);
+ slashlen = ( dirptr->path[pathlen-1] != '/') ? 1 : 0;
while (true) {
long cur_offset;
long prev_offset;
- SMB_STRUCT_STAT sbuf;
+ SMB_STRUCT_STAT sbuf = { 0 };
char *dname = NULL;
bool isdots;
char *fname = NULL;
struct smb_filename smb_fname;
uint32_t mode = 0;
bool ok;
- NTSTATUS status;
cur_offset = dptr_TellDir(dirptr);
prev_offset = cur_offset;
continue;
}
- pathreal = talloc_asprintf(ctx, "%s%s%s",
- dirptr->path,
- needslash?"/":"",
- dname);
+ /*
+ * This used to be
+ * pathreal = talloc_asprintf(ctx, "%s%s%s", dirptr->path,
+ * needslash?"/":"", dname);
+ * but this was measurably slower than doing the memcpy.
+ */
+
+ pathreal = talloc_array(
+ ctx, char,
+ pathlen + slashlen + talloc_get_size(dname));
if (!pathreal) {
TALLOC_FREE(dname);
TALLOC_FREE(fname);
return false;
}
+ /*
+ * We don't want to pass ./xxx to modules below us so don't
+ * add the path if it is just . by itself.
+ */
+ if (dirptr_path_is_dot) {
+ memcpy(pathreal, dname, talloc_get_size(dname));
+ } else {
+ memcpy(pathreal, dirptr->path, pathlen);
+ pathreal[pathlen] = '/';
+ memcpy(pathreal + slashlen + pathlen, dname,
+ talloc_get_size(dname));
+ }
+
/* Create smb_fname with NULL stream_name. */
- ZERO_STRUCT(smb_fname);
- smb_fname.base_name = pathreal;
- smb_fname.st = sbuf;
+ smb_fname = (struct smb_filename) {
+ .base_name = pathreal, .st = sbuf
+ };
ok = mode_fn(ctx, private_data, &smb_fname, &mode);
if (!ok) {
continue;
}
- if (!dir_check_ftype(conn, mode, dirtype)) {
+ if (!dir_check_ftype(mode, dirtype)) {
DEBUG(5,("[%s] attribs 0x%x didn't match 0x%x\n",
fname, (unsigned int)mode, (unsigned int)dirtype));
TALLOC_FREE(dname);
TALLOC_FREE(dname);
- status = copy_smb_filename(ctx, &smb_fname, _smb_fname);
+ *_smb_fname = cp_smb_filename(ctx, &smb_fname);
TALLOC_FREE(pathreal);
- if (!NT_STATUS_IS_OK(status)) {
+ if (*_smb_fname == NULL) {
return false;
}
*_fname = fname;
mangle_mask_match(conn, dname, mask)) {
char mname[13];
const char *fname;
+ /*
+ * Ensure we can push the original name as UCS2. If
+ * not, then just don't return this name.
+ */
+ NTSTATUS status;
+ size_t ret_len = 0;
+ size_t len = (strlen(dname) + 2) * 4; /* Allow enough space. */
+ uint8_t *tmp = talloc_array(talloc_tos(),
+ uint8_t,
+ len);
+
+ status = srvstr_push(NULL,
+ FLAGS2_UNICODE_STRINGS,
+ tmp,
+ dname,
+ len,
+ STR_TERMINATE,
+ &ret_len);
+
+ TALLOC_FREE(tmp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
if (!mangle_is_8_3(dname, false, conn->params)) {
bool ok = name_to_8_3(dname, mname, false,
const char *mask,
uint32_t dirtype,
char **_fname,
- SMB_OFF_T *_size,
+ off_t *_size,
uint32_t *_mode,
struct timespec *_date,
bool check_descend,
static bool user_can_read_file(connection_struct *conn,
struct smb_filename *smb_fname)
{
+ NTSTATUS status;
+ uint32_t rejected_share_access = 0;
+ uint32_t rejected_mask = 0;
+ struct security_descriptor *sd = NULL;
+ uint32_t access_mask = FILE_READ_DATA|
+ FILE_READ_EA|
+ FILE_READ_ATTRIBUTES|
+ SEC_STD_READ_CONTROL;
+
/*
* Never hide files from the root user.
* We use (uid_t)0 here not sec_initial_uid()
return True;
}
- return can_access_file_acl(conn, smb_fname, FILE_READ_DATA);
+ /*
+ * We can't directly use smbd_check_access_rights()
+ * here, as this implicitly grants FILE_READ_ATTRIBUTES
+ * which the Windows access-based-enumeration code
+ * explicitly checks for on the file security descriptor.
+ * See bug:
+ *
+ * https://bugzilla.samba.org/show_bug.cgi?id=10252
+ *
+ * and the smb2.acl2.ACCESSBASED test for details.
+ */
+
+ rejected_share_access = access_mask & ~(conn->share_access);
+ if (rejected_share_access) {
+ DEBUG(10, ("rejected share access 0x%x "
+ "on %s (0x%x)\n",
+ (unsigned int)access_mask,
+ smb_fname_str_dbg(smb_fname),
+ (unsigned int)rejected_share_access ));
+ return false;
+ }
+
+ status = SMB_VFS_GET_NT_ACL(conn,
+ smb_fname->base_name,
+ (SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL),
+ talloc_tos(),
+ &sd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not get acl "
+ "on %s: %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status)));
+ return false;
+ }
+
+ status = se_file_access_check(sd,
+ get_current_nttok(conn),
+ false,
+ access_mask,
+ &rejected_mask);
+
+ TALLOC_FREE(sd);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ DEBUG(10,("rejected bits 0x%x read access for %s\n",
+ (unsigned int)rejected_mask,
+ smb_fname_str_dbg(smb_fname) ));
+ return false;
+ }
+ return true;
}
/*******************************************************************
bool is_visible_file(connection_struct *conn, const char *dir_path,
const char *name, SMB_STRUCT_STAT *pst, bool use_veto)
{
- bool hide_unreadable = lp_hideunreadable(SNUM(conn));
- bool hide_unwriteable = lp_hideunwriteable_files(SNUM(conn));
+ bool hide_unreadable = lp_hide_unreadable(SNUM(conn));
+ bool hide_unwriteable = lp_hide_unwriteable_files(SNUM(conn));
bool hide_special = lp_hide_special_files(SNUM(conn));
char *entry = NULL;
struct smb_filename *smb_fname_base = NULL;
- NTSTATUS status;
bool ret = false;
if ((strcmp(".",name) == 0) || (strcmp("..",name) == 0)) {
}
/* Create an smb_filename with stream_name == NULL. */
- status = create_synthetic_smb_fname(talloc_tos(), entry, NULL,
- pst, &smb_fname_base);
- if (!NT_STATUS_IS_OK(status)) {
+ smb_fname_base = synthetic_smb_fname(talloc_tos(), entry, NULL,
+ pst);
+ if (smb_fname_base == NULL) {
ret = false;
goto out;
}
if (SMB_VFS_STAT(conn, smb_fname_base) != 0) {
ret = true;
goto out;
- } else {
- *pst = smb_fname_base->st;
}
+ *pst = smb_fname_base->st;
}
/* Honour _hide unreadable_ option */
static int smb_Dir_destructor(struct smb_Dir *dirp)
{
- if (dirp->dir) {
-#ifdef HAVE_DIRFD
- if (dirp->conn->sconn) {
- files_struct *fsp = file_find_fd(dirp->conn->sconn,
- dirfd(dirp->dir));
- if (fsp) {
- /* The call below closes the underlying fd. */
- fsp->fh->fd = -1;
+ if (dirp->dir != NULL) {
+ SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir);
+ if (dirp->fsp != NULL) {
+ /*
+ * The SMB_VFS_CLOSEDIR above
+ * closes the underlying fd inside
+ * dirp->fsp.
+ */
+ dirp->fsp->fh->fd = -1;
+ if (dirp->fsp->dptr != NULL) {
+ SMB_ASSERT(dirp->fsp->dptr->dir_hnd == dirp);
+ dirp->fsp->dptr->dir_hnd = NULL;
}
+ dirp->fsp = NULL;
}
-#endif
- SMB_VFS_CLOSEDIR(dirp->conn,dirp->dir);
}
- if (dirp->conn->sconn) {
+ if (dirp->conn->sconn && !dirp->conn->sconn->using_smb2) {
dirp->conn->sconn->searches.dirhandles_open--;
}
return 0;
struct smb_Dir *OpenDir(TALLOC_CTX *mem_ctx, connection_struct *conn,
const char *name,
const char *mask,
- uint32 attr)
+ uint32_t attr)
{
struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir);
struct smbd_server_connection *sconn = conn->sconn;
goto fail;
}
- if (sconn) {
+ if (sconn && !sconn->using_smb2) {
sconn->searches.dirhandles_open++;
}
talloc_set_destructor(dirp, smb_Dir_destructor);
static struct smb_Dir *OpenDir_fsp(TALLOC_CTX *mem_ctx, connection_struct *conn,
files_struct *fsp,
const char *mask,
- uint32 attr)
+ uint32_t attr)
{
struct smb_Dir *dirp = talloc_zero(mem_ctx, struct smb_Dir);
struct smbd_server_connection *sconn = conn->sconn;
goto fail;
}
- if (sconn) {
+ if (sconn && !sconn->using_smb2) {
sconn->searches.dirhandles_open++;
}
talloc_set_destructor(dirp, smb_Dir_destructor);
if (fsp->is_directory && fsp->fh->fd != -1) {
dirp->dir = SMB_VFS_FDOPENDIR(fsp, mask, attr);
- if (dirp->dir == NULL) {
+ if (dirp->dir != NULL) {
+ dirp->fsp = fsp;
+ } else {
DEBUG(10,("OpenDir_fsp: SMB_VFS_FDOPENDIR on %s returned "
"NULL (%s)\n",
dirp->dir_path,
dirp->file_number++;
*ptalloced = NULL;
return n;
- } else if (*poffset == END_OF_DIRECTORY_OFFSET) {
+ }
+
+ if (*poffset == END_OF_DIRECTORY_OFFSET) {
*poffset = dirp->offset = END_OF_DIRECTORY_OFFSET;
return NULL;
- } else {
- /* A real offset, seek to it. */
- SeekDir(dirp, *poffset);
}
+ /* A real offset, seek to it. */
+ SeekDir(dirp, *poffset);
+
while ((n = vfs_readdirname(conn, dirp->dir, sbuf, &talloced))) {
/* Ignore . and .. - we've already returned them. */
if (*n == '.') {
Add an entry into the dcache.
********************************************************************/
-void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
+static void DirCacheAdd(struct smb_Dir *dirp, const char *name, long offset)
{
struct name_cache_entry *e;
return False;
}
+struct files_below_forall_state {
+ char *dirpath;
+ size_t dirpath_len;
+ int (*fn)(struct file_id fid, const struct share_mode_data *data,
+ void *private_data);
+ void *private_data;
+};
+
+static int files_below_forall_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct files_below_forall_state *state = private_data;
+ char tmpbuf[PATH_MAX];
+ char *fullpath, *to_free;
+ size_t len;
+
+ len = full_path_tos(data->servicepath, data->base_name,
+ tmpbuf, sizeof(tmpbuf),
+ &fullpath, &to_free);
+ if (len == -1) {
+ return 0;
+ }
+ if (state->dirpath_len >= len) {
+ /*
+ * Filter files above dirpath
+ */
+ return 0;
+ }
+ if (fullpath[state->dirpath_len] != '/') {
+ /*
+ * Filter file that don't have a path separator at the end of
+ * dirpath's length
+ */
+ return 0;
+ }
+
+ if (memcmp(state->dirpath, fullpath, state->dirpath_len) != 0) {
+ /*
+ * Not a parent
+ */
+ return 0;
+ }
+
+ return state->fn(fid, data, state->private_data);
+}
+
+static int files_below_forall(connection_struct *conn,
+ const struct smb_filename *dir_name,
+ int (*fn)(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data),
+ void *private_data)
+{
+ struct files_below_forall_state state = {
+ .fn = fn,
+ .private_data = private_data,
+ };
+ int ret;
+ char tmpbuf[PATH_MAX];
+ char *to_free;
+
+ state.dirpath_len = full_path_tos(conn->connectpath,
+ dir_name->base_name,
+ tmpbuf, sizeof(tmpbuf),
+ &state.dirpath, &to_free);
+ if (state.dirpath_len == -1) {
+ return -1;
+ }
+
+ ret = share_mode_forall(files_below_forall_fn, &state);
+ TALLOC_FREE(to_free);
+ return ret;
+}
+
+struct have_file_open_below_state {
+ bool found_one;
+};
+
+static int have_file_open_below_fn(struct file_id fid,
+ const struct share_mode_data *data,
+ void *private_data)
+{
+ struct have_file_open_below_state *state = private_data;
+ state->found_one = true;
+ return 1;
+}
+
+bool have_file_open_below(connection_struct *conn,
+ const struct smb_filename *name)
+{
+ struct have_file_open_below_state state = {
+ .found_one = false,
+ };
+ int ret;
+
+ if (!VALID_STAT(name->st)) {
+ return false;
+ }
+ if (!S_ISDIR(name->st.st_ex_mode)) {
+ return false;
+ }
+
+ ret = files_below_forall(conn, name, have_file_open_below_fn, &state);
+ if (ret == -1) {
+ return false;
+ }
+
+ return state.found_one;
+}
+
/*****************************************************************
Is this directory empty ?
*****************************************************************/
-NTSTATUS smbd_can_delete_directory(struct connection_struct *conn,
- const char *dirname)
+NTSTATUS can_delete_directory_fsp(files_struct *fsp)
{
NTSTATUS status = NT_STATUS_OK;
long dirpos = 0;
const char *dname = NULL;
+ const char *dirname = fsp->fsp_name->base_name;
char *talloced = NULL;
SMB_STRUCT_STAT st;
- struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn,
- dirname, NULL, 0);
+ struct connection_struct *conn = fsp->conn;
+ struct smb_Dir *dir_hnd = OpenDir_fsp(talloc_tos(),
+ conn,
+ fsp,
+ NULL,
+ 0);
if (!dir_hnd) {
return map_nt_error_from_unix(errno);
continue;
}
- DEBUG(10,("can_delete_directory: got name %s - can't delete\n",
+ DEBUG(10,("got name %s - can't delete\n",
dname ));
status = NT_STATUS_DIRECTORY_NOT_EMPTY;
break;
TALLOC_FREE(talloced);
TALLOC_FREE(dir_hnd);
- return status;
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!(fsp->posix_flags & FSP_POSIX_FLAGS_RENAME) &&
+ lp_strict_rename(SNUM(conn)) &&
+ have_file_open_below(fsp->conn, fsp->fsp_name))
+ {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
}