/*
- Unix SMB/Netbios implementation.
- Version 1.9.
+ Unix SMB/CIFS implementation.
uid/user handling
Copyright (C) Tim Potter 2000
-
+
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,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "libcli/security/security_token.h"
+#include "auth.h"
-extern int DEBUGLEVEL;
extern struct current_user current_user;
-struct sec_ctx {
- uid_t uid;
- uid_t gid;
- int ngroups;
- gid_t *groups;
- NT_USER_TOKEN *token;
-};
-
-/* A stack of security contexts. We include the current context as being
- the first one, so there is room for another MAX_SEC_CTX_DEPTH more. */
+/****************************************************************************
+ Are two UNIX tokens equal ?
+****************************************************************************/
-static struct sec_ctx sec_ctx_stack[MAX_SEC_CTX_DEPTH + 1];
-static int sec_ctx_stack_ndx;
+bool unix_token_equal(const struct security_unix_token *t1, const struct security_unix_token *t2)
+{
+ if (t1->uid != t2->uid || t1->gid != t2->gid ||
+ t1->ngroups != t2->ngroups) {
+ return false;
+ }
+ if (memcmp(t1->groups, t2->groups,
+ t1->ngroups*sizeof(gid_t)) != 0) {
+ return false;
+ }
+ return true;
+}
/****************************************************************************
Become the specified uid.
****************************************************************************/
-static BOOL become_uid(uid_t uid)
+static bool become_uid(uid_t uid)
{
/* Check for dodgy uid values */
if (uid == (uid_t)-1 ||
((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
- static int done;
-
- if (!done) {
+ if (!become_uid_done) {
DEBUG(1,("WARNING: using uid %d is a security risk\n",
(int)uid));
- done = 1;
+ become_uid_done = true;
}
}
/* Set effective user id */
set_effective_uid(uid);
- current_user.uid = uid;
-
-#ifdef WITH_PROFILE
- profile_p->uid_changes++;
-#endif
+ DO_PROFILE_INC(uid_changes);
return True;
}
Become the specified gid.
****************************************************************************/
-static BOOL become_gid(gid_t gid)
+static bool become_gid(gid_t gid)
{
/* Check for dodgy gid values */
if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) &&
(gid == (gid_t)65535))) {
- static int done;
-
- if (!done) {
+ if (!become_gid_done) {
DEBUG(1,("WARNING: using gid %d is a security risk\n",
(int)gid));
- done = 1;
+ become_gid_done = true;
}
}
-
+
/* Set effective group id */
set_effective_gid(gid);
- current_user.gid = gid;
-
return True;
}
Become the specified uid and gid.
****************************************************************************/
-static BOOL become_id(uid_t uid, gid_t gid)
+static bool become_id(uid_t uid, gid_t gid)
{
return become_gid(gid) && become_uid(uid);
}
static void gain_root(void)
{
+ if (non_root_mode()) {
+ return;
+ }
+
if (geteuid() != 0) {
set_effective_uid(0);
Get the list of current groups.
****************************************************************************/
-int get_current_groups(int *p_ngroups, gid_t **p_groups)
+static int get_current_groups(gid_t gid, uint32_t *p_ngroups, gid_t **p_groups)
{
int i;
gid_t grp;
- int ngroups = sys_getgroups(0,&grp);
- gid_t *groups;
+ int ngroups;
+ gid_t *groups = NULL;
(*p_ngroups) = 0;
(*p_groups) = NULL;
- if (ngroups <= 0)
- return -1;
+ /* this looks a little strange, but is needed to cope with
+ systems that put the current egid in the group list
+ returned from getgroups() (tridge) */
+ save_re_gid();
+ set_effective_gid(gid);
+ setgid(gid);
+
+ ngroups = sys_getgroups(0,&grp);
+ if (ngroups <= 0) {
+ goto fail;
+ }
- if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL) {
+ if((groups = SMB_MALLOC_ARRAY(gid_t, ngroups+1)) == NULL) {
DEBUG(0,("setup_groups malloc fail !\n"));
- return -1;
+ goto fail;
+ }
+
+ if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
+ goto fail;
}
- if ((ngroups = sys_getgroups(ngroups,groups)) == -1)
- return -1;
+ restore_re_gid();
(*p_ngroups) = ngroups;
(*p_groups) = groups;
- DEBUG( 3, ( "get_current_groups: uid %u is in %u groups: ", (unsigned int)getuid() , ngroups ) );
+ DEBUG( 4, ( "get_current_groups: user is in %u groups: ", ngroups));
for (i = 0; i < ngroups; i++ ) {
- DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
+ DEBUG( 4, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
}
- DEBUG( 3, ( "\n" ) );
+ DEBUG( 4, ( "\n" ) );
- return ngroups;
-}
+ return ngroups;
-/****************************************************************************
- Delete a SID token.
-****************************************************************************/
-
-void delete_nt_token(NT_USER_TOKEN **pptoken)
-{
- if (*pptoken) {
- NT_USER_TOKEN *ptoken = *pptoken;
- safe_free( ptoken->user_sids );
- ZERO_STRUCTP(ptoken);
- }
- safe_free(*pptoken);
- *pptoken = NULL;
+fail:
+ SAFE_FREE(groups);
+ restore_re_gid();
+ return -1;
}
/****************************************************************************
- Duplicate a SID token.
+ Create a new security context on the stack. It is the same as the old
+ one. User changes are done using the set_sec_ctx() function.
****************************************************************************/
-NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken)
+bool push_sec_ctx(void)
{
- NT_USER_TOKEN *token;
-
- if (!ptoken)
- return NULL;
+ struct sec_ctx *ctx_p;
- if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
- return NULL;
+ /* Check we don't overflow our stack */
- ZERO_STRUCTP(token);
+ if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
+ DEBUG(0, ("Security context stack overflow!\n"));
+ smb_panic("Security context stack overflow!");
+ }
- if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
- free(token);
- return NULL;
- }
+ /* Store previous user context */
- token->num_sids = ptoken->num_sids;
+ sec_ctx_stack_ndx++;
- return token;
-}
+ ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
-/****************************************************************************
- Initialize the groups a user belongs to.
-****************************************************************************/
+ ctx_p->ut.uid = geteuid();
+ ctx_p->ut.gid = getegid();
-BOOL initialise_groups(char *user, uid_t uid, gid_t gid)
-{
- struct sec_ctx *prev_ctx_p;
- BOOL result = True;
+ DEBUG(4, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n",
+ (unsigned int)ctx_p->ut.uid, (unsigned int)ctx_p->ut.gid, sec_ctx_stack_ndx ));
- become_root();
+ ctx_p->token = dup_nt_token(NULL,
+ sec_ctx_stack[sec_ctx_stack_ndx-1].token);
- /* Call initgroups() to get user groups */
+ ctx_p->ut.ngroups = sys_getgroups(0, NULL);
- if (initgroups(user,gid) == -1) {
- DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) ));
- if (getuid() == 0) {
- if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) {
- DEBUG(0,("This is probably a problem with the account %s\n", user));
- }
+ if (ctx_p->ut.ngroups != 0) {
+ if (!(ctx_p->ut.groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ut.ngroups))) {
+ DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
+ TALLOC_FREE(ctx_p->token);
+ return False;
}
- result = False;
- goto done;
- }
-
- /* Store groups in previous user's security context. This will
- always work as the become_root() call increments the stack
- pointer. */
-
- prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx - 1];
-
- safe_free(prev_ctx_p->groups);
- prev_ctx_p->groups = NULL;
- prev_ctx_p->ngroups = 0;
- get_current_groups(&prev_ctx_p->ngroups, &prev_ctx_p->groups);
-
- done:
- unbecome_root();
+ sys_getgroups(ctx_p->ut.ngroups, ctx_p->ut.groups);
+ } else {
+ ctx_p->ut.groups = NULL;
+ }
- return result;
+ return True;
}
/****************************************************************************
- Create a new security context on the stack. It is the same as the old
- one. User changes are done using the set_sec_ctx() function.
+ Change UNIX security context. Calls panic if not successful so no return value.
****************************************************************************/
-BOOL push_sec_ctx(void)
-{
- struct sec_ctx *ctx_p;
+#ifndef HAVE_DARWIN_INITGROUPS
- /* Check we don't overflow our stack */
+/* Normal credential switch path. */
- if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
- DEBUG(0, ("Security context stack overflow!\n"));
- smb_panic("Security context stack overflow!\n");
+static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
+{
+ /* Start context switch */
+ gain_root();
+#ifdef HAVE_SETGROUPS
+ if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) {
+ smb_panic("sys_setgroups failed");
}
+#endif
+ become_id(uid, gid);
+ /* end context switch */
+}
- /* Store previous user context */
-
- sec_ctx_stack_ndx++;
+#else /* HAVE_DARWIN_INITGROUPS */
+
+/* The Darwin groups implementation is a little unusual. The list of
+* groups in the kernel credential is not exhaustive, but more like
+* a cache. The full group list is held in userspace and checked
+* dynamically.
+*
+* This is an optional mechanism, and setgroups(2) opts out
+* of it. That is, if you call setgroups, then the list of groups you
+* set are the only groups that are ever checked. This is not what we
+* want. We want to opt in to the dynamic resolution mechanism, so we
+* need to specify the uid of the user whose group list (cache) we are
+* setting.
+*
+* The Darwin rules are:
+* 1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
+* 2. Thou shalt not pass more that NGROUPS_MAX to initgroups
+* 3. Thou shalt leave the first entry in the groups list well alone
+*/
- ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
+#include <sys/syscall.h>
- ctx_p->uid = geteuid();
- ctx_p->gid = getegid();
+static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
+{
+ int max = groups_max();
- DEBUG(3, ("push_sec_ctx(%d, %d) : sec_ctx_stack_ndx = %d\n",
- ctx_p->uid, ctx_p->gid, sec_ctx_stack_ndx ));
+ /* Start context switch */
+ gain_root();
- ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
+ become_gid(gid);
- ctx_p->ngroups = sys_getgroups(0, NULL);
- if (ctx_p->ngroups != 0) {
- if (!(ctx_p->groups = malloc(ctx_p->ngroups * sizeof(gid_t)))) {
- DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
- delete_nt_token(&ctx_p->token);
- return False;
- }
-
- sys_getgroups(ctx_p->ngroups, ctx_p->groups);
- } else {
- ctx_p->groups = NULL;
+ if (syscall(SYS_initgroups, (ngroups > max) ? max : ngroups,
+ groups, uid) == -1 && !non_root_mode()) {
+ DEBUG(0, ("WARNING: failed to set group list "
+ "(%d groups) for UID %ld: %s\n",
+ ngroups, uid, strerror(errno)));
+ smb_panic("sys_setgroups failed");
}
- return True;
+ become_uid(uid);
+ /* end context switch */
}
+#endif /* HAVE_DARWIN_INITGROUPS */
+
/****************************************************************************
Set the current security context to a given user.
****************************************************************************/
-void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
+void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, struct security_token *token)
{
struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
/* Set the security context */
- DEBUG(3, ("setting sec ctx (%d, %d) - sec_ctx_stack_ndx = %d\n", uid, gid, sec_ctx_stack_ndx));
+ DEBUG(4, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
- gain_root();
+ security_token_debug(DBGC_CLASS, 5, token);
+ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
-#ifdef HAVE_SETGROUPS
- sys_setgroups(ngroups, groups);
-#endif
+ /* Change uid, gid and supplementary group list. */
+ set_unix_security_ctx(uid, gid, ngroups, groups);
- ctx_p->ngroups = ngroups;
+ ctx_p->ut.ngroups = ngroups;
- safe_free(ctx_p->groups);
-#if 1 /* JRATEST */
- if (token && (token == ctx_p->token))
+ SAFE_FREE(ctx_p->ut.groups);
+ if (token && (token == ctx_p->token)) {
smb_panic("DUPLICATE_TOKEN");
-#endif
+ }
- delete_nt_token(&ctx_p->token);
-
- ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
- ctx_p->token = dup_nt_token(token);
+ TALLOC_FREE(ctx_p->token);
- become_id(uid, gid);
+ if (ngroups) {
+ ctx_p->ut.groups = (gid_t *)memdup(groups,
+ sizeof(gid_t) * ngroups);
+ if (!ctx_p->ut.groups) {
+ smb_panic("memdup failed");
+ }
+ } else {
+ ctx_p->ut.groups = NULL;
+ }
+
+ if (token) {
+ ctx_p->token = dup_nt_token(NULL, token);
+ if (!ctx_p->token) {
+ smb_panic("dup_nt_token failed");
+ }
+ } else {
+ ctx_p->token = NULL;
+ }
- ctx_p->uid = uid;
- ctx_p->gid = gid;
+ ctx_p->ut.uid = uid;
+ ctx_p->ut.gid = gid;
/* Update current_user stuff */
- current_user.uid = uid;
- current_user.gid = gid;
- current_user.ngroups = ngroups;
- current_user.groups = groups;
- current_user.nt_user_token = token;
+ current_user.ut.uid = uid;
+ current_user.ut.gid = gid;
+ current_user.ut.ngroups = ngroups;
+ current_user.ut.groups = groups;
+ current_user.nt_user_token = ctx_p->token;
}
/****************************************************************************
Pop a security context from the stack.
****************************************************************************/
-BOOL pop_sec_ctx(void)
+bool pop_sec_ctx(void)
{
struct sec_ctx *ctx_p;
struct sec_ctx *prev_ctx_p;
if (sec_ctx_stack_ndx == 0) {
DEBUG(0, ("Security context stack underflow!\n"));
- smb_panic("Security context stack underflow!\n");
+ smb_panic("Security context stack underflow!");
}
ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
/* Clear previous user info */
- ctx_p->uid = (uid_t)-1;
- ctx_p->gid = (gid_t)-1;
+ ctx_p->ut.uid = (uid_t)-1;
+ ctx_p->ut.gid = (gid_t)-1;
- safe_free(ctx_p->groups);
- ctx_p->ngroups = 0;
+ SAFE_FREE(ctx_p->ut.groups);
+ ctx_p->ut.ngroups = 0;
- delete_nt_token(&ctx_p->token);
+ TALLOC_FREE(ctx_p->token);
/* Pop back previous user */
sec_ctx_stack_ndx--;
- gain_root();
-
prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
-#ifdef HAVE_SETGROUPS
- sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups);
-#endif
-
- become_id(prev_ctx_p->uid, prev_ctx_p->gid);
+ /* Change uid, gid and supplementary group list. */
+ set_unix_security_ctx(prev_ctx_p->ut.uid,
+ prev_ctx_p->ut.gid,
+ prev_ctx_p->ut.ngroups,
+ prev_ctx_p->ut.groups);
/* Update current_user stuff */
- current_user.uid = prev_ctx_p->uid;
- current_user.gid = prev_ctx_p->gid;
- current_user.ngroups = prev_ctx_p->ngroups;
- current_user.groups = prev_ctx_p->groups;
+ current_user.ut.uid = prev_ctx_p->ut.uid;
+ current_user.ut.gid = prev_ctx_p->ut.gid;
+ current_user.ut.ngroups = prev_ctx_p->ut.ngroups;
+ current_user.ut.groups = prev_ctx_p->ut.groups;
current_user.nt_user_token = prev_ctx_p->token;
- DEBUG(3, ("pop_sec_ctx (%d, %d) - sec_ctx_stack_ndx = %d\n", geteuid(), getegid(), sec_ctx_stack_ndx));
+ DEBUG(4, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
return True;
}
memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
- sec_ctx_stack[i].uid = (uid_t)-1;
- sec_ctx_stack[i].gid = (gid_t)-1;
+ sec_ctx_stack[i].ut.uid = (uid_t)-1;
+ sec_ctx_stack[i].ut.gid = (gid_t)-1;
}
/* Initialise first level of stack. It is the current context */
ctx_p = &sec_ctx_stack[0];
- ctx_p->uid = geteuid();
- ctx_p->gid = getegid();
+ ctx_p->ut.uid = geteuid();
+ ctx_p->ut.gid = getegid();
- get_current_groups(&ctx_p->ngroups, &ctx_p->groups);
+ get_current_groups(ctx_p->ut.gid, &ctx_p->ut.ngroups, &ctx_p->ut.groups);
ctx_p->token = NULL; /* Maps to guest user. */
/* Initialise current_user global */
- current_user.uid = ctx_p->uid;
- current_user.gid = ctx_p->gid;
- current_user.ngroups = ctx_p->ngroups;
- current_user.groups = ctx_p->groups;
+ current_user.ut.uid = ctx_p->ut.uid;
+ current_user.ut.gid = ctx_p->ut.gid;
+ current_user.ut.ngroups = ctx_p->ut.ngroups;
+ current_user.ut.groups = ctx_p->ut.groups;
/* The conn and vuid are usually taken care of by other modules.
We initialise them here. */