* Added real, effective and saved set- user and group IDs to the team

structure. They are properly inherited and updated on
  fork(), load_image(), and exec().
* Implemented the get[e]{u,g}id(), set[[r]e]{u,g}id() family for real.
* getgroups() also calls the kernel now, but only returns the effective
  group ID. Supplementary groups support is still missing.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24359 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-03-11 17:12:02 +00:00
parent 1149e982f0
commit 4eb3560949
12 changed files with 479 additions and 95 deletions
+3 -1
View File
@@ -159,13 +159,15 @@ extern pid_t setpgrp(void);
extern gid_t getegid(void);
extern uid_t geteuid(void);
extern gid_t getgid(void);
extern int getgroups(int groupSize, gid_t groupList[]);
extern uid_t getuid(void);
extern int getgroups(int groupSize, gid_t groupList[]);
extern int setgid(gid_t gid);
extern int setuid(uid_t uid);
extern int setegid(gid_t gid);
extern int seteuid(uid_t uid);
extern int setregid(gid_t rgid, gid_t egid);
extern int setreuid(uid_t ruid, uid_t euid);
extern char *getlogin(void);
extern int getlogin_r(char *name, size_t nameSize);
+9
View File
@@ -113,6 +113,15 @@ extern status_t _kern_get_team_info(team_id id, team_info *info);
extern status_t _kern_get_next_team_info(int32 *cookie, team_info *info);
extern status_t _kern_get_team_usage_info(team_id team, int32 who, team_usage_info *info, size_t size);
// user/group functions
extern gid_t _kern_getgid(bool effective);
extern uid_t _kern_getuid(bool effective);
extern ssize_t _kern_getgroups(int groupSize, gid_t* groupList);
extern status_t _kern_setregid(gid_t rgid, gid_t egid,
bool setAllIfPrivileged);
extern status_t _kern_setreuid(uid_t ruid, uid_t euid,
bool setAllIfPrivileged);
// signal functions
extern status_t _kern_send_signal(pid_t tid, uint sig);
extern status_t _kern_sigprocmask(int how, const sigset_t *set,
+7
View File
@@ -189,6 +189,13 @@ struct team {
bigtime_t dead_threads_kernel_time;
bigtime_t dead_threads_user_time;
uid_t saved_set_uid;
uid_t real_uid;
uid_t effective_uid;
gid_t saved_set_gid;
gid_t real_gid;
gid_t effective_gid;
};
typedef int32 (*thread_entry_func)(thread_func, void *);
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_USERGROUP_H
#define _KERNEL_USERGROUP_H
#include <unistd.h>
#include <SupportDefs.h>
struct team;
#ifdef __cplusplus
extern "C" {
#endif
// kernel private functions
void inherit_parent_user_and_group(struct team* team,
struct team* parent);
status_t update_set_id_user_and_group(struct team* team, const char* file);
// syscalls
gid_t _user_getgid(bool effective);
uid_t _user_getuid(bool effective);
ssize_t _user_getgroups(int groupSize, gid_t* groupList);
status_t _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged);
status_t _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _KERNEL_USERGROUP_H
+1
View File
@@ -42,6 +42,7 @@ KernelMergeObject kernel_core.o :
team.cpp
thread.cpp
timer.c
usergroup.cpp
wait_for_objects.cpp
: $(TARGET_KERNEL_PIC_CCFLAGS)
+1 -1
View File
@@ -80,7 +80,7 @@ KernelMergeObject kernel_posix.o :
read.c
sync.c
truncate.c
usergroup.c
usergroup.cpp
write.c
# string
memchr.c
+1
View File
@@ -33,6 +33,7 @@
#include <arch/system_info.h>
#include <messaging.h>
#include <frame_buffer_console.h>
#include <usergroup.h>
#include <wait_for_objects.h>
#include <malloc.h>
+20
View File
@@ -34,6 +34,7 @@
#include <tls.h>
#include <tracing.h>
#include <user_runtime.h>
#include <usergroup.h>
#include <vfs.h>
#include <vm.h>
#include <vm_address_space.h>
@@ -1171,6 +1172,11 @@ load_image_etc(int32 argCount, char * const *args, int32 envCount,
team->loading_info = &loadingInfo;
}
// Inherit the parent's user/group, but also check the executable's
// set-user/group-id permission
inherit_parent_user_and_group(team, parent);
update_set_id_user_and_group(team, args[0]);
state = disable_interrupts();
GRAB_TEAM_LOCK();
@@ -1377,6 +1383,10 @@ exec_team(const char *path, int32 argCount, char * const *args,
atomic_or(&team->flags, TEAM_FLAG_EXEC_DONE);
// Update user/group according to the executable's set-user/group-id
// permission.
update_set_id_user_and_group(team, path);
status = team_create_thread_start(teamArgs);
// this one usually doesn't return...
@@ -1449,6 +1459,9 @@ fork_team(void)
strlcpy(team->args, parentTeam->args, sizeof(team->args));
// Inherit the parent's user/group.
inherit_parent_user_and_group(team, parentTeam);
state = disable_interrupts();
GRAB_TEAM_LOCK();
@@ -1940,6 +1953,13 @@ team_init(kernel_args *args)
strcpy(sKernelTeam->args, sKernelTeam->name);
sKernelTeam->state = TEAM_STATE_NORMAL;
sKernelTeam->saved_set_uid = 0;
sKernelTeam->real_uid = 0;
sKernelTeam->effective_uid = 0;
sKernelTeam->saved_set_gid = 0;
sKernelTeam->real_gid = 0;
sKernelTeam->effective_gid = 0;
insert_team_into_group(group, sKernelTeam);
sKernelTeam->io_context = vfs_new_io_context(NULL);
+293
View File
@@ -0,0 +1,293 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <usergroup.h>
#include <errno.h>
#include <sys/stat.h>
#include <new.h>
#include <kernel.h>
#include <syscalls.h>
#include <team.h>
#include <thread.h>
#include <thread_types.h>
#include <util/AutoLock.h>
#include <AutoDeleter.h>
// #pragma mark - Implementation Private
static bool
is_privileged(struct team* team)
{
// currently only the root user is privileged
return team->effective_uid == 0;
}
static status_t
common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel)
{
struct team* team = thread_get_current_thread()->team;
InterruptsSpinLocker _(team_spinlock);
bool privileged = kernel || is_privileged(team);
// real gid
if (rgid == (gid_t)-1) {
rgid = team->real_gid;
} else {
if (setAllIfPrivileged) {
// setgid() semantics: If privileged set both, real, effective and
// saved set-gid, otherwise set the effective gid.
if (privileged) {
team->saved_set_gid = rgid;
team->real_gid = rgid;
team->effective_gid = rgid;
return B_OK;
}
// not privileged -- set only the effective gid
egid = rgid;
rgid = team->real_gid;
} else {
// setregid() semantics: set the real gid, if allowed to
if (!privileged && rgid != team->real_gid)
return EPERM;
}
}
// effective gid
if (egid == (gid_t)-1) {
egid = team->effective_gid;
} else {
if (!privileged && egid != team->effective_gid
&& egid != team->real_gid && egid != team->saved_set_gid) {
return EPERM;
}
}
// Getting here means all checks were successful -- set the gids.
team->real_gid = rgid;
team->effective_gid = egid;
return B_OK;
}
static status_t
common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel)
{
struct team* team = thread_get_current_thread()->team;
InterruptsSpinLocker _(team_spinlock);
bool privileged = kernel || is_privileged(team);
// real uid
if (ruid == (uid_t)-1) {
ruid = team->real_uid;
} else {
if (setAllIfPrivileged) {
// setuid() semantics: If privileged set both, real, effective and
// saved set-uid, otherwise set the effective uid.
if (privileged) {
team->saved_set_uid = ruid;
team->real_uid = ruid;
team->effective_uid = ruid;
return B_OK;
}
// not privileged -- set only the effective uid
euid = ruid;
ruid = team->real_uid;
} else {
// setreuid() semantics: set the real uid, if allowed to
// Note: We allow setting the real uid to the effective uid. This
// is unspecified by the specs, but is common practice.
if (!privileged && ruid != team->real_uid
&& ruid != team->effective_uid) {
return EPERM;
}
}
}
// effective uid
if (euid == (uid_t)-1) {
euid = team->effective_uid;
} else {
if (!privileged && euid != team->effective_uid
&& euid != team->real_uid && euid != team->saved_set_uid) {
return EPERM;
}
}
// Getting here means all checks were successful -- set the uids.
team->real_uid = ruid;
team->effective_uid = euid;
return B_OK;
}
// #pragma mark - Kernel Private
void
inherit_parent_user_and_group(struct team* team, struct team* parent)
{
InterruptsSpinLocker _(team_spinlock);
team->saved_set_uid = parent->saved_set_uid;
team->real_uid = parent->real_uid;
team->effective_uid = parent->effective_uid;
team->saved_set_gid = parent->saved_set_gid;
team->real_gid = parent->real_gid;
team->effective_gid = parent->effective_gid;
}
status_t
update_set_id_user_and_group(struct team* team, const char* file)
{
struct stat st;
if (stat(file, &st) < 0)
return errno;
InterruptsSpinLocker _(team_spinlock);
if ((st.st_mode & S_ISUID) != 0) {
team->saved_set_uid = st.st_uid;
team->effective_uid = st.st_uid;
}
if ((st.st_mode & S_ISGID) != 0) {
team->saved_set_gid = st.st_gid;
team->effective_gid = st.st_gid;
}
return B_OK;
}
gid_t
_kern_getgid(bool effective)
{
struct team* team = thread_get_current_thread()->team;
return effective ? team->effective_gid : team->real_gid;
}
uid_t
_kern_getuid(bool effective)
{
struct team* team = thread_get_current_thread()->team;
return effective ? team->effective_uid : team->real_uid;
}
ssize_t
_kern_getgroups(int groupSize, gid_t* groupList)
{
// TODO: Implement proper supplementary group support!
// For now only return the effective group.
if (groupSize > 0)
groupList[0] = getegid();
return 1;
}
status_t
_kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
{
return common_setregid(rgid, egid, setAllIfPrivileged, true);
}
status_t
_kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
{
return common_setreuid(ruid, euid, setAllIfPrivileged, true);
}
// #pragma mark - Syscalls
gid_t
_user_getgid(bool effective)
{
struct team* team = thread_get_current_thread()->team;
return effective ? team->effective_gid : team->real_gid;
}
uid_t
_user_getuid(bool effective)
{
struct team* team = thread_get_current_thread()->team;
return effective ? team->effective_uid : team->real_uid;
}
ssize_t
_user_getgroups(int groupSize, gid_t* userGroupList)
{
gid_t* groupList = NULL;
if (groupSize < 0)
return B_BAD_VALUE;
if (groupSize > NGROUPS_MAX + 1)
groupSize = NGROUPS_MAX + 1;
if (groupSize > 0) {
if (userGroupList == NULL || !IS_USER_ADDRESS(userGroupList))
return B_BAD_VALUE;
groupList = new(nothrow) gid_t[groupSize];
if (groupList == NULL)
return B_NO_MEMORY;
}
ArrayDeleter<gid_t> _(groupList);
ssize_t result = _kern_getgroups(groupSize, groupList);
if (result < 0)
return result;
if (groupSize > 0) {
if (user_memcpy(userGroupList, groupList, sizeof(gid_t) * result)
!= B_OK) {
return B_BAD_ADDRESS;
}
}
return result;
}
status_t
_user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
{
return common_setregid(rgid, egid, setAllIfPrivileged, false);
}
status_t
_user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
{
return common_setreuid(ruid, euid, setAllIfPrivileged, false);
}
+1 -1
View File
@@ -35,7 +35,7 @@ MergeObject posix_unistd.o :
truncate.c
ttyname.c
ualarm.c
usergroup.c
usergroup.cpp
usleep.c
write.c
;
@@ -1,92 +0,0 @@
/*
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <syscalls.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
// ToDo: implement the user/group functions for real!
gid_t
getegid(void)
{
return 0;
}
uid_t
geteuid(void)
{
return 0;
}
gid_t
getgid(void)
{
return 0;
}
int
getgroups(int groupSize, gid_t groupList[])
{
return 0;
}
uid_t
getuid(void)
{
return 0;
}
int
setgid(gid_t gid)
{
if (gid == 0)
return 0;
return EPERM;
}
int
setuid(uid_t uid)
{
if (uid == 0)
return 0;
return EPERM;
}
int
setegid(gid_t gid)
{
if (gid == 0)
return 0;
return EPERM;
}
int
seteuid(uid_t uid)
{
if (uid == 0)
return 0;
return EPERM;
}
@@ -0,0 +1,104 @@
/*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <syscalls.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
template<typename T>
static inline T
set_errno_if_necessary(const T& result)
{
if (result < 0) {
errno = result;
return -1;
}
return result;
}
gid_t
getegid(void)
{
return _kern_getgid(true);
}
uid_t
geteuid(void)
{
return _kern_getuid(true);
}
gid_t
getgid(void)
{
return _kern_getgid(false);
}
uid_t
getuid(void)
{
return _kern_getuid(false);
}
int
getgroups(int groupSize, gid_t groupList[])
{
return set_errno_if_necessary(_kern_getgroups(groupSize, groupList));
}
int
setgid(gid_t gid)
{
return set_errno_if_necessary(_kern_setregid(gid, (gid_t)-1, true));
}
int
setuid(uid_t uid)
{
return set_errno_if_necessary(_kern_setreuid(uid, (uid_t)-1, true));
}
int
setegid(gid_t gid)
{
return set_errno_if_necessary(_kern_setregid((gid_t)-1, gid, false));
}
int
seteuid(uid_t uid)
{
return set_errno_if_necessary(_kern_setreuid((uid_t)-1, uid, false));
}
int
setregid(gid_t rgid, gid_t egid)
{
return set_errno_if_necessary(_kern_setregid(rgid, egid, false));
}
int
setreuid(uid_t ruid, uid_t euid)
{
return set_errno_if_necessary(_kern_setreuid(ruid, euid, false));
}