diff options
author | Jeremy Allison <jra@samba.org> | 2012-02-29 17:04:08 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2012-02-29 17:04:08 -0800 |
commit | 89c55485c3cc6eefdd7af20f79b1d219cace5066 (patch) | |
tree | 66b0de8b952da922cf5c4c6659a2bad5b3f43bff | |
parent | 442e79efbdc9dfaf5774c67edb3460603d63d2a5 (diff) | |
download | samba-89c55485c3cc6eefdd7af20f79b1d219cace5066.tar.gz samba-89c55485c3cc6eefdd7af20f79b1d219cace5066.tar.bz2 samba-89c55485c3cc6eefdd7af20f79b1d219cace5066.zip |
Add the implementation of check_reduced_name_with_privilege(). Now to plumb into
SMB1 requests.
-rw-r--r-- | source3/include/smb.h | 16 | ||||
-rw-r--r-- | source3/smbd/filename.c | 5 | ||||
-rw-r--r-- | source3/smbd/process.c | 1 | ||||
-rw-r--r-- | source3/smbd/proto.h | 3 | ||||
-rw-r--r-- | source3/smbd/vfs.c | 160 |
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; } /******************************************************************* |