ports/net/samba47/files/patch-source3__modules__vfs_streams_xattr.c
Timur I. Bakeyev 3284397fb5 Update Samba ports to address multiple CVE vulnerabilities
Security:	CVE-2018-1139
		CVE-2018-1140
		CVE-2018-10858
		CVE-2018-10918
		CVE-2018-10919
Sponsored by:	iXsystems Inc.
2018-08-14 15:09:36 +00:00

505 lines
15 KiB
C

--- source3/modules/vfs_streams_xattr.c.orig 2018-08-11 23:00:01 UTC
+++ source3/modules/vfs_streams_xattr.c
@@ -1,10 +1,10 @@
/*
* Store streams in xattrs
*
- * Copyright (C) Volker Lendecke, 2008
+ * Copyright (C) Volker Lendecke, 2008
+ * Copyright (C) Timur I. Bakeyev, 2017
*
* Partly based on James Peach's Darwin module, which is
- *
* Copyright (C) James Peach 2006-2007
*
* This program is free software; you can redistribute it and/or modify
@@ -79,25 +79,79 @@ static SMB_INO_T stream_inode(const SMB_
}
static ssize_t get_xattr_size(connection_struct *conn,
- const struct smb_filename *smb_fname,
- const char *xattr_name)
+ const struct smb_filename *smb_fname,
+ const char *xattr_name)
{
- NTSTATUS status;
- struct ea_struct ea;
ssize_t result;
- status = get_ea_value(talloc_tos(), conn, NULL, smb_fname,
- xattr_name, &ea);
+ result = SMB_VFS_GETXATTR(conn, smb_fname, xattr_name, NULL, 0);
+ // ? -1
+ return result;
+}
- if (!NT_STATUS_IS_OK(status)) {
- return -1;
+static NTSTATUS get_xattr_value(TALLOC_CTX *mem_ctx,
+ connection_struct *conn,
+ const struct smb_filename *smb_fname,
+ const char *ea_name,
+ struct ea_struct *pea)
+{
+ ssize_t attr_size;
+
+ attr_size = get_xattr_size(conn, smb_fname, ea_name);
+
+ if (attr_size == -1) {
+ return map_nt_error_from_unix(errno);
}
- result = ea.value.length-1;
- TALLOC_FREE(ea.value.data);
- return result;
+ pea->value = data_blob_talloc(mem_ctx, NULL, attr_size);
+ /* We may have xattr of a 0 size */
+ if(pea->value.data == NULL && attr_size) {
+ DEBUG(5,
+ ("get_xattr_value: for EA '%s' failed to allocate %lu bytes\n",
+ ea_name, (unsigned long)attr_size)
+ );
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ attr_size = SMB_VFS_GETXATTR(conn, smb_fname, ea_name, pea->value.data, pea->value.length);
+
+ if (attr_size == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if(pea->value.length != attr_size) {
+ DEBUG(5,
+ ("get_xattr_value: for EA '%s' requested %lu, read %lu bytes\n",
+ ea_name, (unsigned long)pea->value.length, (unsigned long)attr_size)
+ );
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10,("get_xattr_value: EA '%s' is of length %lu\n", ea_name, (unsigned long)attr_size));
+ /*
+ * This can dump huge amount of data multiple times. For example
+ * for 1Mb ADS and chunk size 64Kb the same 1Mb dump will be
+ * logged 16 times!
+ */
+ dump_data(50, (uint8_t *)pea->value.data, pea->value.length);
+
+ pea->flags = 0;
+ // ? user.
+ if (strnequal(ea_name, "user.", 5)) {
+ pea->name = talloc_strdup(mem_ctx, &ea_name[5]);
+ } else {
+ pea->name = talloc_strdup(mem_ctx, ea_name);
+ }
+
+ if (pea->name == NULL) {
+ data_blob_free(&pea->value);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
}
+
/**
* Given a stream name, populate xattr_name with the xattr name to use for
* accessing the stream.
@@ -114,6 +168,7 @@ static NTSTATUS streams_xattr_get_name(v
SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config,
return NT_STATUS_UNSUCCESSFUL);
+ // stream_name is passed as ':stream', so skip leading ':'
sname = talloc_strdup(ctx, stream_name + 1);
if (sname == NULL) {
return NT_STATUS_NO_MEMORY;
@@ -125,7 +180,7 @@ static NTSTATUS streams_xattr_get_name(v
* characters from their on-the-wire Unicode Private Range
* encoding to their native ASCII representation.
*
- * As as result the name of xattrs storing the streams (via
+ * As a result the name of xattrs storing the streams (via
* vfs_streams_xattr) may contain a colon, so we have to use
* strrchr_m() instead of strchr_m() for matching the stream
* type suffix.
@@ -157,7 +212,7 @@ static NTSTATUS streams_xattr_get_name(v
return NT_STATUS_NO_MEMORY;
}
- DEBUG(10, ("xattr_name: %s, stream_name: %s\n", *xattr_name,
+ DEBUG(10, ("xattr_name: '%s', stream_name: '%s'\n", *xattr_name,
stream_name));
talloc_free(sname);
@@ -265,8 +320,8 @@ static int streams_xattr_fstat(vfs_handl
return -1;
}
- sbuf->st_ex_size = get_xattr_size(handle->conn,
- smb_fname_base, io->xattr_name);
+ sbuf->st_ex_size = get_xattr_size(handle->conn, smb_fname_base,
+ io->xattr_name);
if (sbuf->st_ex_size == -1) {
TALLOC_FREE(smb_fname_base);
SET_STAT_INVALID(*sbuf);
@@ -453,10 +508,10 @@ static int streams_xattr_open(vfs_handle
pipe_fds[1] = -1;
fakefd = pipe_fds[0];
- status = get_ea_value(talloc_tos(), handle->conn, NULL,
- smb_fname, xattr_name, &ea);
+ status = get_xattr_value(talloc_tos(), handle->conn,
+ smb_fname, xattr_name, &ea);
- DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
+ DEBUG(10, ("get_xattr_value returned %s\n", nt_errstr(status)));
if (!NT_STATUS_IS_OK(status)
&& !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
@@ -625,8 +680,8 @@ static int streams_xattr_rename(vfs_hand
}
/* read the old stream */
- status = get_ea_value(talloc_tos(), handle->conn, NULL,
- smb_fname_src, src_xattr_name, &ea);
+ status = get_xattr_value(talloc_tos(), handle->conn,
+ smb_fname_src, src_xattr_name, &ea);
if (!NT_STATUS_IS_OK(status)) {
errno = ENOENT;
goto fail;
@@ -713,14 +768,13 @@ static NTSTATUS walk_xattr_streams(vfs_h
continue;
}
- status = get_ea_value(names,
+ status = get_xattr_value(names,
handle->conn,
- NULL,
smb_fname,
names[i],
&ea);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("Could not get ea %s for file %s: %s\n",
+ DEBUG(10, ("Could not get EA %s for file %s: %s\n",
names[i],
smb_fname->base_name,
nt_errstr(status)));
@@ -782,16 +836,17 @@ struct streaminfo_state {
NTSTATUS status;
};
-static bool collect_one_stream(struct ea_struct *ea, void *private_data)
+static bool collect_one_stream(struct ea_struct *pea, void *private_data)
{
struct streaminfo_state *state =
(struct streaminfo_state *)private_data;
+ // ? -1
if (!add_one_stream(state->mem_ctx,
&state->num_streams, &state->streams,
- ea->name, ea->value.length-1,
+ pea->name, pea->value.length,
smb_roundup(state->handle->conn,
- ea->value.length-1))) {
+ pea->value.length))) {
state->status = NT_STATUS_NO_MEMORY;
return false;
}
@@ -911,14 +966,17 @@ static ssize_t streams_xattr_pwrite(vfs_
files_struct *fsp, const void *data,
size_t n, off_t offset)
{
- struct stream_io *sio =
+ struct stream_io *sio =
(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+ struct smb_filename *smb_fname_base = NULL;
+ TALLOC_CTX *frame = NULL;
+
struct ea_struct ea;
NTSTATUS status;
- struct smb_filename *smb_fname_base = NULL;
int ret;
- DEBUG(10, ("streams_xattr_pwrite called for %d bytes\n", (int)n));
+ DEBUG(10, ("streams_xattr_pwrite: offset=%lu, size=%lu\n",
+ (unsigned long)offset, (unsigned long)n));
if (sio == NULL) {
return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
@@ -928,6 +986,8 @@ static ssize_t streams_xattr_pwrite(vfs_
return -1;
}
+ frame = talloc_stackframe();
+
/* Create an smb_filename with stream_name == NULL. */
smb_fname_base = synthetic_smb_fname(talloc_tos(),
sio->base,
@@ -935,39 +995,55 @@ static ssize_t streams_xattr_pwrite(vfs_
NULL,
fsp->fsp_name->flags);
if (smb_fname_base == NULL) {
+ TALLOC_FREE(frame);
errno = ENOMEM;
return -1;
}
- status = get_ea_value(talloc_tos(), handle->conn, NULL,
- smb_fname_base, sio->xattr_name, &ea);
- if (!NT_STATUS_IS_OK(status)) {
- return -1;
- }
-
- if ((offset + n) > ea.value.length-1) {
- uint8_t *tmp;
+ status = get_xattr_value(talloc_tos(), handle->conn,
+ smb_fname_base, sio->xattr_name, &ea);
- tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t,
- offset + n + 1);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /*
+ * This can happen if we sit behind vfs_fruit:
+ * fruit_ftruncate calls UNLINK on an attribute
+ * truncating the "file" to zero length. A later
+ * pwrite faces a non-existing attribute, we need to
+ * cope with that here.
+ *
+ * This might be not the last word on this.
+ */
- if (tmp == NULL) {
- TALLOC_FREE(ea.value.data);
- errno = ENOMEM;
- return -1;
- }
- ea.value.data = tmp;
- ea.value.length = offset + n + 1;
- ea.value.data[offset+n] = 0;
- }
+ ea = (struct ea_struct) {0};
+ ea.name = talloc_strdup(talloc_tos(), sio->xattr_name);
+ if (ea.name == NULL) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return -1;
+ }
+ status = NT_STATUS_OK;
+ }
- memcpy(ea.value.data + offset, data, n);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ // ? -1
+ if ((offset + n) > ea.value.length) {
+ if(!data_blob_realloc(talloc_tos(), &ea.value, offset + n)) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ memcpy(ea.value.data + offset, data, n);
ret = SMB_VFS_SETXATTR(fsp->conn,
fsp->fsp_name,
sio->xattr_name,
ea.value.data, ea.value.length, 0);
- TALLOC_FREE(ea.value.data);
+
+ TALLOC_FREE(frame);
if (ret == -1) {
return -1;
@@ -980,15 +1056,17 @@ static ssize_t streams_xattr_pread(vfs_h
files_struct *fsp, void *data,
size_t n, off_t offset)
{
- struct stream_io *sio =
+ struct stream_io *sio =
(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+ struct smb_filename *smb_fname_base = NULL;
+ TALLOC_CTX *frame = NULL;
+
struct ea_struct ea;
NTSTATUS status;
- size_t length, overlap;
- struct smb_filename *smb_fname_base = NULL;
+ size_t overlap;
- DEBUG(10, ("streams_xattr_pread: offset=%d, size=%d\n",
- (int)offset, (int)n));
+ DEBUG(10, ("streams_xattr_pread: offset=%lu, size=%lu\n",
+ (unsigned long)offset, (unsigned long)n));
if (sio == NULL) {
return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
@@ -998,6 +1076,8 @@ static ssize_t streams_xattr_pread(vfs_h
return -1;
}
+ frame = talloc_stackframe();
+
/* Create an smb_filename with stream_name == NULL. */
smb_fname_base = synthetic_smb_fname(talloc_tos(),
sio->base,
@@ -1005,31 +1085,35 @@ static ssize_t streams_xattr_pread(vfs_h
NULL,
fsp->fsp_name->flags);
if (smb_fname_base == NULL) {
+ TALLOC_FREE(frame);
errno = ENOMEM;
return -1;
}
- status = get_ea_value(talloc_tos(), handle->conn, NULL,
- smb_fname_base, sio->xattr_name, &ea);
+ status = get_xattr_value(talloc_tos(), handle->conn,
+ smb_fname_base, sio->xattr_name, &ea);
if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
return -1;
}
+ // ? -1
+ //length = ea.value.length-1;
- length = ea.value.length-1;
+ DEBUG(10, ("streams_xattr_pread: get_xattr_value() returned %lu bytes\n",
+ (unsigned long)ea.value.length));
- DEBUG(10, ("streams_xattr_pread: get_ea_value returned %d bytes\n",
- (int)length));
+ /* Attempt to read past EOF. */
+ if (ea.value.length <= offset) {
+ TALLOC_FREE(frame);
+ return 0;
+ }
- /* Attempt to read past EOF. */
- if (length <= offset) {
- return 0;
- }
+ overlap = (offset + n) > ea.value.length ? (ea.value.length - offset) : n;
+ memcpy(data, ea.value.data + offset, overlap);
- overlap = (offset + n) > length ? (length - offset) : n;
- memcpy(data, ea.value.data + offset, overlap);
+ TALLOC_FREE(frame);
- TALLOC_FREE(ea.value.data);
- return overlap;
+ return overlap;
}
struct streams_xattr_pread_state {
@@ -1196,16 +1280,18 @@ static int streams_xattr_ftruncate(struc
struct files_struct *fsp,
off_t offset)
{
- int ret;
- uint8_t *tmp;
- struct ea_struct ea;
- NTSTATUS status;
- struct stream_io *sio =
+ struct stream_io *sio =
(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
struct smb_filename *smb_fname_base = NULL;
+ TALLOC_CTX *frame = NULL;
- DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n",
- fsp_str_dbg(fsp), (double)offset));
+ struct ea_struct ea;
+ NTSTATUS status;
+ size_t orig_length;
+ int ret;
+
+ DEBUG(10, ("streams_xattr_ftruncate: called for file '%s' with offset %lu\n",
+ fsp_str_dbg(fsp), (unsigned long)offset));
if (sio == NULL) {
return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
@@ -1215,6 +1301,8 @@ static int streams_xattr_ftruncate(struc
return -1;
}
+ frame = talloc_stackframe();
+
/* Create an smb_filename with stream_name == NULL. */
smb_fname_base = synthetic_smb_fname(talloc_tos(),
sio->base,
@@ -1222,40 +1310,46 @@ static int streams_xattr_ftruncate(struc
NULL,
fsp->fsp_name->flags);
if (smb_fname_base == NULL) {
+ TALLOC_FREE(frame);
errno = ENOMEM;
return -1;
}
- status = get_ea_value(talloc_tos(), handle->conn, NULL,
- smb_fname_base, sio->xattr_name, &ea);
+ status = get_xattr_value(talloc_tos(), handle->conn,
+ smb_fname_base, sio->xattr_name, &ea);
if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
return -1;
}
+ orig_length = ea.value.length;
- tmp = talloc_realloc(talloc_tos(), ea.value.data, uint8_t,
- offset + 1);
+ /* Requested size matches the original size */
+ if(orig_length == offset) {
+ TALLOC_FREE(frame);
+ return 0;
+ }
- if (tmp == NULL) {
- TALLOC_FREE(ea.value.data);
+ /* That can both shrink and expand */
+ /* XXX: If offset == 0 the result of talloc_realloc is NULL, but still valid */
+ if(offset && !data_blob_realloc(talloc_tos(), &ea.value, offset)) {
+ TALLOC_FREE(frame);
errno = ENOMEM;
return -1;
}
- /* Did we expand ? */
- if (ea.value.length < offset + 1) {
- memset(&tmp[ea.value.length], '\0',
- offset + 1 - ea.value.length);
+ /* If we expanded, fill up extra space with zeros */
+ if (orig_length < offset) {
+ memset(ea.value.data + orig_length, 0,
+ offset - orig_length);
}
- ea.value.data = tmp;
- ea.value.length = offset + 1;
- ea.value.data[offset] = 0;
-
+ /* XXX: We should use ea.value.length here, but when offset == 0
+ it's not reset to 0 in data_blob_realloc() */
ret = SMB_VFS_SETXATTR(fsp->conn,
fsp->fsp_name,
sio->xattr_name,
- ea.value.data, ea.value.length, 0);
- TALLOC_FREE(ea.value.data);
+ ea.value.data, offset, 0);
+ TALLOC_FREE(frame);
if (ret == -1) {
return -1;
@@ -1273,9 +1367,9 @@ static int streams_xattr_fallocate(struc
struct stream_io *sio =
(struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
- DEBUG(10, ("streams_xattr_fallocate called for file %s offset %.0f"
- "len = %.0f\n",
- fsp_str_dbg(fsp), (double)offset, (double)len));
+ DEBUG(10, ("streams_xattr_fallocate: called for file '%s' with offset %lu"
+ "len = %lu\n",
+ fsp_str_dbg(fsp), (unsigned long)offset, (unsigned long)len));
if (sio == NULL) {
return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);