#include "includes.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
+#include "../libcli/security/security.h"
#include "dbwrap/dbwrap.h"
#include "dbwrap/dbwrap_rbt.h"
#include "dbwrap/dbwrap_open.h"
return 0;
}
+struct vfs_offload_token_db_store_fsp_state {
+ const struct files_struct *fsp;
+ const DATA_BLOB *token_blob;
+ NTSTATUS status;
+};
+
+static void vfs_offload_token_db_store_fsp_fn(
+ struct db_record *rec, TDB_DATA value, void *private_data)
+{
+ struct vfs_offload_token_db_store_fsp_state *state = private_data;
+ const struct files_struct *fsp = state->fsp;
+ const DATA_BLOB *token_blob = state->token_blob;
+ files_struct *token_db_fsp = NULL;
+ void *ptr = NULL;
+
+ if (value.dsize == 0) {
+ value = make_tdb_data((uint8_t *)&fsp, sizeof(files_struct *));
+ state->status = dbwrap_record_store(rec, value, 0);
+ return;
+ }
+
+ if (value.dsize != sizeof(ptr)) {
+ DBG_ERR("Bad db entry for token:\n");
+ dump_data(1, token_blob->data, token_blob->length);
+ state->status = NT_STATUS_INTERNAL_ERROR;
+ return;
+ }
+ memcpy(&ptr, value.dptr, value.dsize);
+
+ token_db_fsp = talloc_get_type_abort(ptr, struct files_struct);
+ if (token_db_fsp != fsp) {
+ DBG_ERR("token for fsp [%s] matches already known "
+ "but different fsp [%s]:\n",
+ fsp_str_dbg(fsp),
+ fsp_str_dbg(token_db_fsp));
+ dump_data(1, token_blob->data, token_blob->length);
+ state->status = NT_STATUS_INTERNAL_ERROR;
+ return;
+ }
+}
+
NTSTATUS vfs_offload_token_db_store_fsp(struct vfs_offload_ctx *ctx,
const files_struct *fsp,
const DATA_BLOB *token_blob)
{
- struct db_record *rec = NULL;
+ struct vfs_offload_token_db_store_fsp_state state = {
+ .fsp = fsp, .token_blob = token_blob,
+ };
struct fsp_token_link *link = NULL;
TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
- TDB_DATA value;
NTSTATUS status;
- rec = dbwrap_fetch_locked(ctx->db_ctx, talloc_tos(), key);
- if (rec == NULL) {
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- value = dbwrap_record_get_value(rec);
- if (value.dsize != 0) {
- void *ptr = NULL;
- files_struct *token_db_fsp = NULL;
-
- if (value.dsize != sizeof(ptr)) {
- DBG_ERR("Bad db entry for token:\n");
- dump_data(1, token_blob->data, token_blob->length);
- TALLOC_FREE(rec);
- return NT_STATUS_INTERNAL_ERROR;
- }
- memcpy(&ptr, value.dptr, value.dsize);
- TALLOC_FREE(rec);
-
- token_db_fsp = talloc_get_type_abort(ptr, struct files_struct);
- if (token_db_fsp != fsp) {
- DBG_ERR("token for fsp [%s] matches already known "
- "but different fsp [%s]:\n",
- fsp_str_dbg(fsp), fsp_str_dbg(token_db_fsp));
- dump_data(1, token_blob->data, token_blob->length);
- return NT_STATUS_INTERNAL_ERROR;
- }
-
- return NT_STATUS_OK;
- }
-
- link = talloc_zero(fsp, struct fsp_token_link);
+ link = talloc(fsp, struct fsp_token_link);
if (link == NULL) {
return NT_STATUS_NO_MEMORY;
}
- link->ctx = ctx;
- link->token_blob = data_blob_talloc(link, token_blob->data,
- token_blob->length);
+ *link = (struct fsp_token_link) {
+ .ctx = ctx,
+ .token_blob = data_blob_dup_talloc(link, *token_blob),
+ };
if (link->token_blob.data == NULL) {
TALLOC_FREE(link);
return NT_STATUS_NO_MEMORY;
}
- talloc_set_destructor(link, fsp_token_link_destructor);
-
- value = make_tdb_data((uint8_t *)&fsp, sizeof(files_struct *));
- status = dbwrap_record_store(rec, value, 0);
+ status = dbwrap_do_locked(
+ ctx->db_ctx,
+ key,
+ vfs_offload_token_db_store_fsp_fn,
+ &state);
if (!NT_STATUS_IS_OK(status)) {
- DBG_ERR("dbwrap_record_store for [%s] failed: %s. Token\n",
- fsp_str_dbg(fsp), nt_errstr(status));
- dump_data(0, token_blob->data, token_blob->length);
+ DBG_DEBUG("dbwrap_do_locked failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(link);
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ DBG_DEBUG("vfs_offload_token_db_store_fsp_fn failed: %s\n",
+ nt_errstr(status));
TALLOC_FREE(link);
- TALLOC_FREE(rec);
return status;
}
- TALLOC_FREE(rec);
+ talloc_set_destructor(link, fsp_token_link_destructor);
return NT_STATUS_OK;
}
+struct vfs_offload_token_db_fetch_fsp_state {
+ struct files_struct **fsp;
+ NTSTATUS status;
+};
+
+static void vfs_offload_token_db_fetch_fsp_fn(
+ TDB_DATA key, TDB_DATA value, void *private_data)
+{
+ struct vfs_offload_token_db_fetch_fsp_state *state = private_data;
+ void *ptr;
+
+ if (value.dsize != sizeof(ptr)) {
+ DBG_ERR("Bad db entry for token:\n");
+ dump_data(1, key.dptr, key.dsize);
+ state->status = NT_STATUS_INTERNAL_ERROR;
+ return;
+ }
+
+ memcpy(&ptr, value.dptr, value.dsize);
+ *state->fsp = talloc_get_type_abort(ptr, struct files_struct);
+}
+
NTSTATUS vfs_offload_token_db_fetch_fsp(struct vfs_offload_ctx *ctx,
const DATA_BLOB *token_blob,
files_struct **fsp)
{
- struct db_record *rec = NULL;
+ struct vfs_offload_token_db_fetch_fsp_state state = { .fsp = fsp };
TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
- TDB_DATA value;
- void *ptr = NULL;
+ NTSTATUS status;
- rec = dbwrap_fetch_locked(ctx->db_ctx, talloc_tos(), key);
- if (rec == NULL) {
- return NT_STATUS_INTERNAL_ERROR;
+ status = dbwrap_parse_record(
+ ctx->db_ctx,
+ key,
+ vfs_offload_token_db_fetch_fsp_fn,
+ &state);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
-
- value = dbwrap_record_get_value(rec);
- if (value.dsize == 0) {
+ if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("Unknown token:\n");
dump_data(10, token_blob->data, token_blob->length);
- TALLOC_FREE(rec);
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
- }
-
- if (value.dsize != sizeof(ptr)) {
- DBG_ERR("Bad db entry for token:\n");
- dump_data(1, token_blob->data, token_blob->length);
- TALLOC_FREE(rec);
- return NT_STATUS_INTERNAL_ERROR;
+ return status;
}
-
- memcpy(&ptr, value.dptr, value.dsize);
- TALLOC_FREE(rec);
-
- *fsp = talloc_get_type_abort(ptr, struct files_struct);
- return NT_STATUS_OK;
+ return state.status;
}
NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
}
/* combine persistent and volatile handles for the resume key */
- SBVAL(token_blob->data, 0, fsp->op->global->open_persistent_id);
- SBVAL(token_blob->data, 8, fsp->op->global->open_volatile_id);
- SIVAL(token_blob->data, 16, fsctl);
+ SBVAL(token_blob->data,
+ SMB_VFS_ODX_TOKEN_OFFSET_PFID,
+ fsp->op->global->open_persistent_id);
+ SBVAL(token_blob->data,
+ SMB_VFS_ODX_TOKEN_OFFSET_VFID,
+ fsp->op->global->open_volatile_id);
+ SIVAL(token_blob->data,
+ SMB_VFS_ODX_TOKEN_OFFSET_FSCTL,
+ fsctl);
return NT_STATUS_OK;
}
+
+NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
+ files_struct *src_fsp,
+ files_struct *dst_fsp)
+{
+ NTSTATUS status;
+
+ if (src_fsp->vuid != dst_fsp->vuid) {
+ DBG_INFO("copy chunk handles not in the same session.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!NT_STATUS_IS_OK(src_fsp->op->status)) {
+ DBG_INFO("copy chunk source handle invalid: %s\n",
+ nt_errstr(src_fsp->op->status));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!NT_STATUS_IS_OK(dst_fsp->op->status)) {
+ DBG_INFO("copy chunk destination handle invalid: %s\n",
+ nt_errstr(dst_fsp->op->status));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (src_fsp->fsp_flags.closing) {
+ DBG_INFO("copy chunk src handle with closing in progress.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dst_fsp->fsp_flags.closing) {
+ DBG_INFO("copy chunk dst handle with closing in progress.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (src_fsp->fsp_flags.is_directory) {
+ DBG_INFO("copy chunk no read on src directory handle (%s).\n",
+ smb_fname_str_dbg(src_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (dst_fsp->fsp_flags.is_directory) {
+ DBG_INFO("copy chunk no read on dst directory handle (%s).\n",
+ smb_fname_str_dbg(dst_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
+ DBG_INFO("copy chunk no access on IPC$ handle.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
+ DBG_INFO("copy chunk no access on PRINT handle.\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
+ * The server MUST fail the request with STATUS_ACCESS_DENIED if any of
+ * the following are true:
+ * - The Open.GrantedAccess of the destination file does not include
+ * FILE_WRITE_DATA or FILE_APPEND_DATA.
+ *
+ * A non writable dst handle also doesn't make sense for other fsctls.
+ */
+ status = check_any_access_fsp(dst_fsp, FILE_WRITE_DATA|FILE_APPEND_DATA);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_INFO("dest handle not writable (%s).\n",
+ smb_fname_str_dbg(dst_fsp->fsp_name));
+ return status;
+ }
+ /*
+ * - The Open.GrantedAccess of the destination file does not include
+ * FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
+ */
+ if ((fsctl == FSCTL_SRV_COPYCHUNK) && !CHECK_READ_IOCTL(dst_fsp)) {
+ DBG_INFO("copy chunk no read on dest handle (%s).\n",
+ smb_fname_str_dbg(dst_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /*
+ * - The Open.GrantedAccess of the source file does not include
+ * FILE_READ_DATA access.
+ */
+ if (!CHECK_READ_SMB2(src_fsp)) {
+ DBG_INFO("src handle not readable (%s).\n",
+ smb_fname_str_dbg(src_fsp->fsp_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}