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 "system/passwd.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "libcli/security/security_token.h"
+#include "auth.h"
+#include "smbprofile.h"
extern struct current_user current_user;
-struct sec_ctx {
- UNIX_USER_TOKEN ut;
- 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;
}
}
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);
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);
}
Get the list of current groups.
****************************************************************************/
-static int get_current_groups(gid_t gid, 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;
(*p_ngroups) = ngroups;
(*p_groups) = groups;
- DEBUG( 3, ( "get_current_groups: user is in %u groups: ", 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;
one. User changes are done using the set_sec_ctx() function.
****************************************************************************/
-BOOL push_sec_ctx(void)
+bool push_sec_ctx(void)
{
struct sec_ctx *ctx_p;
if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
DEBUG(0, ("Security context stack overflow!\n"));
- smb_panic("Security context stack overflow!\n");
+ smb_panic("Security context stack overflow!");
}
/* Store previous user context */
ctx_p->ut.uid = geteuid();
ctx_p->ut.gid = getegid();
- DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n",
+ 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 ));
ctx_p->token = dup_nt_token(NULL,
return True;
}
+/****************************************************************************
+ Change UNIX security context. Calls panic if not successful so no return value.
+****************************************************************************/
+
+#ifndef HAVE_DARWIN_INITGROUPS
+
+/* Normal credential switch path. */
+
+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 */
+}
+
+#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
+*/
+
+#include <sys/syscall.h>
+
+static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
+{
+ int max = groups_max();
+
+ /* Start context switch */
+ gain_root();
+
+ become_gid(gid);
+
+
+ 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");
+ }
+
+ 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, const 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 (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ DEBUG(4, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
(unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
- debug_nt_user_token(DBGC_CLASS, 5, token);
+ security_token_debug(DBGC_CLASS, 5, token);
debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
- gain_root();
-
-#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->ut.ngroups = ngroups;
}
TALLOC_FREE(ctx_p->token);
-
+
if (ngroups) {
- ctx_p->ut.groups = memdup(groups, sizeof(gid_t) * ngroups);
+ ctx_p->ut.groups = (gid_t *)memdup(groups,
+ sizeof(gid_t) * ngroups);
if (!ctx_p->ut.groups) {
smb_panic("memdup failed");
}
ctx_p->token = NULL;
}
- become_id(uid, gid);
-
ctx_p->ut.uid = uid;
ctx_p->ut.gid = gid;
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];
sec_ctx_stack_ndx--;
- gain_root();
-
prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
-#ifdef HAVE_SETGROUPS
- sys_setgroups(prev_ctx_p->ut.ngroups, prev_ctx_p->ut.groups);
-#endif
-
- become_id(prev_ctx_p->ut.uid, prev_ctx_p->ut.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.ut.groups = prev_ctx_p->ut.groups;
current_user.nt_user_token = prev_ctx_p->token;
- DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
+ 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;