summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/smb.h16
-rw-r--r--source3/smbd/filename.c5
-rw-r--r--source3/smbd/process.c1
-rw-r--r--source3/smbd/proto.h3
-rw-r--r--source3/smbd/vfs.c160
5 files changed, 177 insertions, 8 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 10e47984a8..a54d2069a8 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -442,6 +442,7 @@ struct current_user {
};
struct smbd_smb2_request;
+struct privilege_paths;
struct smb_request {
uint8_t cmd;
@@ -495,6 +496,12 @@ struct smb_request {
* Back pointer to smb2 request.
*/
struct smbd_smb2_request *smb2req;
+
+ /*
+ * Pathnames used if request done
+ * under privilege.
+ */
+ struct privilege_paths *priv_paths;
};
/* Defines for the sent_oplock_break field above. */
@@ -1349,6 +1356,15 @@ struct smb_filename {
SMB_STRUCT_STAT st;
};
+/*
+ * Pathnames used if request done
+ * under privilege.
+ */
+struct privilege_paths {
+ struct smb_filename parent_name;
+ struct smb_filename file_name;
+};
+
/* Used to keep track of deferred opens. */
struct deferred_open_record;
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
index b3e132b7ea..95e8c14409 100644
--- a/source3/smbd/filename.c
+++ b/source3/smbd/filename.c
@@ -1021,7 +1021,7 @@ NTSTATUS check_name(connection_struct *conn, const char *name)
}
/****************************************************************************
- Must be called as root. Creates the struct priv_backup_restore_paths structure
+ Must be called as root. Creates the struct privilege_paths
attached to the struct smb_request if this call is successful.
****************************************************************************/
@@ -1036,8 +1036,7 @@ static NTSTATUS check_name_with_privilege(connection_struct *conn,
}
return check_reduced_name_with_privilege(conn,
name,
- NULL,
- NULL);
+ smbreq);
}
/****************************************************************************
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index fc18f5e869..6ffc06700f 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -540,6 +540,7 @@ static bool init_smb_request(struct smb_request *req,
req->chain_outbuf = NULL;
req->done = false;
req->smb2req = NULL;
+ req->priv_paths = NULL;
smb_init_perfcount_data(&req->pcd);
/* Ensure we have at least wct words and 2 bytes of bcc. */
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 7101041195..48ecfe52d5 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -1168,8 +1168,7 @@ char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn);
NTSTATUS check_reduced_name(connection_struct *conn, const char *fname);
NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
const char *fname,
- struct smb_filename **pp_parent_name,
- struct smb_filename **pp_file_name);
+ struct smb_request *smbreq);
int vfs_stat_smb_fname(struct connection_struct *conn, const char *fname,
SMB_STRUCT_STAT *psbuf);
int vfs_lstat_smb_fname(struct connection_struct *conn, const char *fname,
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 65c2535629..7da3881828 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -901,10 +901,164 @@ char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
const char *fname,
- struct smb_filename **pp_parent_name,
- struct smb_filename **pp_file_name)
+ struct smb_request *smbreq)
{
- return NT_STATUS_NOT_IMPLEMENTED;
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_tos();
+ const char *conn_rootdir;
+ size_t rootdir_len;
+ char *dir_name = NULL;
+ const char *last_component = NULL;
+ char *resolved_name = NULL;
+ char *saved_dir = NULL;
+ struct smb_filename *smb_fname_cwd = NULL;
+ struct privilege_paths *priv_paths = NULL;
+ int ret;
+
+ DEBUG(3,("check_reduced_name_with_privilege [%s] [%s]\n",
+ fname,
+ conn->connectpath));
+
+
+ priv_paths = talloc_zero(smbreq, struct privilege_paths);
+ if (!priv_paths) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err;
+ }
+
+ if (!parent_dirname(ctx, fname, &dir_name, &last_component)) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err;
+ }
+
+ priv_paths->parent_name.base_name = talloc_strdup(priv_paths, dir_name);
+ priv_paths->file_name.base_name = talloc_strdup(priv_paths, last_component);
+
+ if (priv_paths->parent_name.base_name == NULL ||
+ priv_paths->file_name.base_name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err;
+ }
+
+ if (SMB_VFS_STAT(conn, &priv_paths->parent_name) != 0) {
+ status = map_nt_error_from_unix(errno);
+ goto err;
+ }
+ /* Remember where we were. */
+ saved_dir = vfs_GetWd(ctx, conn);
+ if (!saved_dir) {
+ status = map_nt_error_from_unix(errno);
+ goto err;
+ }
+
+ /* Go to the parent directory to lock in memory. */
+ if (vfs_ChDir(conn, priv_paths->parent_name.base_name) == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto err;
+ }
+
+ /* Get the absolute path of the parent directory. */
+ resolved_name = SMB_VFS_REALPATH(conn,".");
+ if (!resolved_name) {
+ status = map_nt_error_from_unix(errno);
+ goto err;
+ }
+
+ if (*resolved_name != '/') {
+ DEBUG(0,("check_reduced_name_with_privilege: realpath "
+ "doesn't return absolute paths !\n"));
+ status = NT_STATUS_OBJECT_NAME_INVALID;
+ goto err;
+ }
+
+ DEBUG(10,("check_reduced_name_with_privilege: realpath [%s] -> [%s]\n",
+ priv_paths->parent_name.base_name,
+ resolved_name));
+
+ /* Now check the stat value is the same. */
+ status = create_synthetic_smb_fname(talloc_tos(), ".",
+ NULL, NULL,
+ &smb_fname_cwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+
+ if (SMB_VFS_LSTAT(conn, smb_fname_cwd) != 0) {
+ status = map_nt_error_from_unix(errno);
+ goto err;
+ }
+
+ /* Ensure we're pointing at the same place. */
+ if (!check_same_stat(&smb_fname_cwd->st, &priv_paths->parent_name.st)) {
+ DEBUG(0,("check_reduced_name_with_privilege: "
+ "device/inode/uid/gid on directory %s changed. "
+ "Denying access !\n",
+ priv_paths->parent_name.base_name));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err;
+ }
+
+ /* Ensure we're below the connect path. */
+
+ conn_rootdir = SMB_VFS_CONNECTPATH(conn, fname);
+ if (conn_rootdir == NULL) {
+ DEBUG(2, ("check_reduced_name_with_privilege: Could not get "
+ "conn_rootdir\n"));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err;
+ }
+
+ rootdir_len = strlen(conn_rootdir);
+ if (strncmp(conn_rootdir, resolved_name, rootdir_len) != 0) {
+ DEBUG(2, ("check_reduced_name_with_privilege: Bad access "
+ "attempt: %s is a symlink outside the "
+ "share path\n",
+ dir_name));
+ DEBUGADD(2, ("conn_rootdir =%s\n", conn_rootdir));
+ DEBUGADD(2, ("resolved_name=%s\n", resolved_name));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err;
+ }
+
+ /* Now ensure that the last component either doesn't
+ exist, or is *NOT* a symlink. */
+
+ ret = SMB_VFS_LSTAT(conn, &priv_paths->file_name);
+ if (ret == -1) {
+ /* Errno must be ENOENT for this be ok. */
+ if (errno != ENOENT) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(2, ("check_reduced_name_with_privilege: "
+ "LSTAT on %s failed with %s\n",
+ priv_paths->file_name.base_name,
+ nt_errstr(status)));
+ goto err;
+ }
+ }
+
+ if (VALID_STAT(priv_paths->file_name.st) &&
+ S_ISLNK(priv_paths->file_name.st.st_ex_mode)) {
+ DEBUG(2, ("check_reduced_name_with_privilege: "
+ "Last component %s is a symlink. Denying"
+ "access.\n",
+ priv_paths->file_name.base_name));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err;
+ }
+
+ smbreq->priv_paths = priv_paths;
+ status = NT_STATUS_OK;
+
+ err:
+
+ if (saved_dir) {
+ vfs_ChDir(conn, saved_dir);
+ }
+ SAFE_FREE(resolved_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(priv_paths);
+ }
+ return status;
}
/*******************************************************************