This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "system/passwd.h"
#include "auth/auth.h"
#include "ntvfs/ntvfs.h"
-#include "dsdb/samdb/samdb.h"
+#include "libcli/wbclient/wbclient.h"
+#define TEVENT_DEPRECATED
+#include <tevent.h>
+#include "../lib/util/setid.h"
+
+NTSTATUS ntvfs_unixuid_init(void);
struct unixuid_private {
- struct sidmap_context *sidmap;
- struct unix_sec_ctx *last_sec_ctx;
+ struct security_unix_token *last_sec_ctx;
struct security_token *last_token;
};
-
-struct unix_sec_ctx {
- uid_t uid;
- gid_t gid;
- uint_t ngroups;
- gid_t *groups;
-};
-
/*
- pull the current security context into a unix_sec_ctx
+ pull the current security context into a security_unix_token
*/
-static struct unix_sec_ctx *save_unix_security(TALLOC_CTX *mem_ctx)
+static struct security_unix_token *save_unix_security(TALLOC_CTX *mem_ctx)
{
- struct unix_sec_ctx *sec = talloc(mem_ctx, struct unix_sec_ctx);
+ struct security_unix_token *sec = talloc(mem_ctx, struct security_unix_token);
if (sec == NULL) {
return NULL;
}
}
/*
- set the current security context from a unix_sec_ctx
+ set the current security context from a security_unix_token
*/
-static NTSTATUS set_unix_security(struct unix_sec_ctx *sec)
+static NTSTATUS set_unix_security(struct security_unix_token *sec)
{
- seteuid(0);
+ samba_seteuid(0);
- if (setgroups(sec->ngroups, sec->groups) != 0) {
+ if (samba_setgroups(sec->ngroups, sec->groups) != 0) {
return NT_STATUS_ACCESS_DENIED;
}
- if (setegid(sec->gid) != 0) {
+ if (samba_setegid(sec->gid) != 0) {
return NT_STATUS_ACCESS_DENIED;
}
- if (seteuid(sec->uid) != 0) {
+ if (samba_seteuid(sec->uid) != 0) {
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
-/*
- form a unix_sec_ctx from the current security_token
-*/
-static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req,
- struct security_token *token,
- struct unix_sec_ctx **sec)
-{
- struct unixuid_private *private = ntvfs->private_data;
- int i;
- NTSTATUS status;
- *sec = talloc(req, struct unix_sec_ctx);
-
- /* we can't do unix security without a user and group */
- if (token->num_sids < 2) {
- return NT_STATUS_ACCESS_DENIED;
- }
-
- status = sidmap_sid_to_unixuid(private->sidmap,
- token->user_sid, &(*sec)->uid);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+static int unixuid_nesting_level;
- status = sidmap_sid_to_unixgid(private->sidmap,
- token->group_sid, &(*sec)->gid);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+/*
+ called at the start and end of a tevent nesting loop. Needs to save/restore
+ unix security context
+ */
+static int unixuid_event_nesting_hook(struct tevent_context *ev,
+ void *private_data,
+ uint32_t level,
+ bool begin,
+ void *stack_ptr,
+ const char *location)
+{
+ struct security_unix_token *sec_ctx;
+
+ if (unixuid_nesting_level == 0) {
+ /* we don't need to do anything unless we are nested
+ inside of a call in this module */
+ return 0;
}
- (*sec)->ngroups = token->num_sids - 2;
- (*sec)->groups = talloc_array(*sec, gid_t, (*sec)->ngroups);
- if ((*sec)->groups == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
+ if (begin) {
+ sec_ctx = save_unix_security(ev);
+ if (sec_ctx == NULL) {
+ DEBUG(0,("%s: Failed to save security context\n", location));
+ return -1;
+ }
+ *(struct security_unix_token **)stack_ptr = sec_ctx;
+ if (samba_seteuid(0) != 0 || samba_setegid(0) != 0) {
+ DEBUG(0,("%s: Failed to change to root\n", location));
+ return -1;
+ }
+ } else {
+ /* called when we come out of a nesting level */
+ NTSTATUS status;
+
+ sec_ctx = *(struct security_unix_token **)stack_ptr;
+ if (sec_ctx == NULL) {
+ /* this happens the first time this function
+ is called, as we install the hook while
+ inside an event in unixuid_connect() */
+ return 0;
+ }
- for (i=0;i<(*sec)->ngroups;i++) {
- status = sidmap_sid_to_unixgid(private->sidmap,
- token->sids[i+2], &(*sec)->groups[i]);
+ sec_ctx = talloc_get_type_abort(sec_ctx, struct security_unix_token);
+ status = set_unix_security(sec_ctx);
+ talloc_free(sec_ctx);
if (!NT_STATUS_IS_OK(status)) {
- return status;
+ DEBUG(0,("%s: Failed to revert security context (%s)\n",
+ location, nt_errstr(status)));
+ return -1;
}
}
- return NT_STATUS_OK;
+ return 0;
+}
+
+
+/*
+ form a security_unix_token from the current security_token
+*/
+static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct security_token *token,
+ struct security_unix_token **sec)
+{
+ return security_token_to_unix_token(req,
+ ntvfs->ctx->event_ctx,
+ token, sec);
}
/*
setup our unix security context according to the session authentication info
*/
static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, struct unix_sec_ctx **sec)
+ struct ntvfs_request *req, struct security_unix_token **sec)
{
- struct unixuid_private *private = ntvfs->private_data;
+ struct unixuid_private *priv = ntvfs->private_data;
struct security_token *token;
- struct unix_sec_ctx *newsec;
+ struct security_unix_token *newsec;
NTSTATUS status;
- if (req->session_info == NULL) {
+ /* If we are asked to set up, but have not had a successful
+ * session setup or tree connect, then these may not be filled
+ * in. ACCESS_DENIED is the right error code here */
+ if (req->session_info == NULL || priv == NULL) {
return NT_STATUS_ACCESS_DENIED;
}
token = req->session_info->security_token;
- *sec = save_unix_security(req);
+ *sec = save_unix_security(ntvfs);
if (*sec == NULL) {
return NT_STATUS_NO_MEMORY;
}
- if (token == private->last_token) {
- newsec = private->last_sec_ctx;
+ if (token == priv->last_token) {
+ newsec = priv->last_sec_ctx;
} else {
status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*sec);
return status;
}
- if (private->last_sec_ctx) {
- talloc_free(private->last_sec_ctx);
+ if (priv->last_sec_ctx) {
+ talloc_free(priv->last_sec_ctx);
}
- private->last_sec_ctx = newsec;
- private->last_token = token;
- talloc_steal(private, newsec);
+ priv->last_sec_ctx = newsec;
+ priv->last_token = token;
+ talloc_steal(priv, newsec);
}
status = set_unix_security(newsec);
if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*sec);
return status;
}
*/
#define PASS_THRU_REQ(ntvfs, req, op, args) do { \
NTSTATUS status2; \
- struct unix_sec_ctx *sec; \
+ struct security_unix_token *sec; \
status = unixuid_setup_security(ntvfs, req, &sec); \
- if (NT_STATUS_IS_OK(status)) status = ntvfs_next_##op args; \
+ NT_STATUS_NOT_OK_RETURN(status); \
+ unixuid_nesting_level++; \
+ status = ntvfs_next_##op args; \
+ unixuid_nesting_level--; \
status2 = set_unix_security(sec); \
+ talloc_free(sec); \
if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
} while (0)
connect to a share - used when a tree_connect operation comes in.
*/
static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, const char *sharename)
+ struct ntvfs_request *req, union smb_tcon *tcon)
{
- struct unixuid_private *private;
+ struct unixuid_private *priv;
NTSTATUS status;
- private = talloc(ntvfs, struct unixuid_private);
- if (!private) {
+ priv = talloc(ntvfs, struct unixuid_private);
+ if (!priv) {
return NT_STATUS_NO_MEMORY;
}
- private->sidmap = sidmap_open(private);
- if (private->sidmap == NULL) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
+ priv->last_sec_ctx = NULL;
+ priv->last_token = NULL;
+ ntvfs->private_data = priv;
- ntvfs->private_data = private;
- private->last_sec_ctx = NULL;
- private->last_token = NULL;
+ tevent_loop_set_nesting_hook(ntvfs->ctx->event_ctx,
+ unixuid_event_nesting_hook,
+ &unixuid_nesting_level);
/* we don't use PASS_THRU_REQ here, as the connect operation runs with
root privileges. This allows the backends to setup any database
links they might need during the connect. */
- status = ntvfs_next_connect(ntvfs, req, sharename);
+ status = ntvfs_next_connect(ntvfs, req, tcon);
return status;
}
*/
static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
{
- struct unixuid_private *private = ntvfs->private_data;
+ struct unixuid_private *priv = ntvfs->private_data;
NTSTATUS status;
- talloc_free(private);
+ talloc_free(priv);
ntvfs->private_data = NULL;
status = ntvfs_next_disconnect(ntvfs);
static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req)
{
- struct unixuid_private *private = ntvfs->private_data;
+ struct unixuid_private *priv = ntvfs->private_data;
NTSTATUS status;
PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
- private->last_token = NULL;
+ priv->last_token = NULL;
return status;
}
*/
static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
- void *private)
+ void *private_data)
{
NTSTATUS status;
- PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private));
+ PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private_data));
return status;
}
change notify
*/
static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, struct smb_notify *info)
+ struct ntvfs_request *req, union smb_notify *info)
{
NTSTATUS status;
static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
- BOOL (*callback)(void *, union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
NTSTATUS status;
static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
- BOOL (*callback)(void *, union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
NTSTATUS status;
{
NTSTATUS ret;
struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
ZERO_STRUCT(ops);
/* fill in all the operations */
- ops.connect = unixuid_connect;
- ops.disconnect = unixuid_disconnect;
- ops.unlink = unixuid_unlink;
- ops.chkpath = unixuid_chkpath;
- ops.qpathinfo = unixuid_qpathinfo;
- ops.setpathinfo = unixuid_setpathinfo;
- ops.open = unixuid_open;
- ops.mkdir = unixuid_mkdir;
- ops.rmdir = unixuid_rmdir;
- ops.rename = unixuid_rename;
- ops.copy = unixuid_copy;
- ops.ioctl = unixuid_ioctl;
- ops.read = unixuid_read;
- ops.write = unixuid_write;
- ops.seek = unixuid_seek;
- ops.flush = unixuid_flush;
- ops.close = unixuid_close;
- ops.exit = unixuid_exit;
- ops.lock = unixuid_lock;
- ops.setfileinfo = unixuid_setfileinfo;
- ops.qfileinfo = unixuid_qfileinfo;
- ops.fsinfo = unixuid_fsinfo;
- ops.lpq = unixuid_lpq;
- ops.search_first = unixuid_search_first;
- ops.search_next = unixuid_search_next;
- ops.search_close = unixuid_search_close;
- ops.trans = unixuid_trans;
- ops.logoff = unixuid_logoff;
- ops.async_setup = unixuid_async_setup;
- ops.cancel = unixuid_cancel;
- ops.notify = unixuid_notify;
+ ops.connect_fn = unixuid_connect;
+ ops.disconnect_fn = unixuid_disconnect;
+ ops.unlink_fn = unixuid_unlink;
+ ops.chkpath_fn = unixuid_chkpath;
+ ops.qpathinfo_fn = unixuid_qpathinfo;
+ ops.setpathinfo_fn = unixuid_setpathinfo;
+ ops.open_fn = unixuid_open;
+ ops.mkdir_fn = unixuid_mkdir;
+ ops.rmdir_fn = unixuid_rmdir;
+ ops.rename_fn = unixuid_rename;
+ ops.copy_fn = unixuid_copy;
+ ops.ioctl_fn = unixuid_ioctl;
+ ops.read_fn = unixuid_read;
+ ops.write_fn = unixuid_write;
+ ops.seek_fn = unixuid_seek;
+ ops.flush_fn = unixuid_flush;
+ ops.close_fn = unixuid_close;
+ ops.exit_fn = unixuid_exit;
+ ops.lock_fn = unixuid_lock;
+ ops.setfileinfo_fn = unixuid_setfileinfo;
+ ops.qfileinfo_fn = unixuid_qfileinfo;
+ ops.fsinfo_fn = unixuid_fsinfo;
+ ops.lpq_fn = unixuid_lpq;
+ ops.search_first_fn = unixuid_search_first;
+ ops.search_next_fn = unixuid_search_next;
+ ops.search_close_fn = unixuid_search_close;
+ ops.trans_fn = unixuid_trans;
+ ops.logoff_fn = unixuid_logoff;
+ ops.async_setup_fn = unixuid_async_setup;
+ ops.cancel_fn = unixuid_cancel;
+ ops.notify_fn = unixuid_notify;
ops.name = "unixuid";
/* we register under all 3 backend types, as we are not type specific */
ops.type = NTVFS_DISK;
- ret = ntvfs_register(&ops);
+ ret = ntvfs_register(&ops, &vers);
if (!NT_STATUS_IS_OK(ret)) goto failed;
ops.type = NTVFS_PRINT;
- ret = ntvfs_register(&ops);
+ ret = ntvfs_register(&ops, &vers);
if (!NT_STATUS_IS_OK(ret)) goto failed;
ops.type = NTVFS_IPC;
- ret = ntvfs_register(&ops);
+ ret = ntvfs_register(&ops, &vers);
if (!NT_STATUS_IS_OK(ret)) goto failed;
failed: