* 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:
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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
|
||||
@@ -42,6 +42,7 @@ KernelMergeObject kernel_core.o :
|
||||
team.cpp
|
||||
thread.cpp
|
||||
timer.c
|
||||
usergroup.cpp
|
||||
wait_for_objects.cpp
|
||||
|
||||
: $(TARGET_KERNEL_PIC_CCFLAGS)
|
||||
|
||||
@@ -80,7 +80,7 @@ KernelMergeObject kernel_posix.o :
|
||||
read.c
|
||||
sync.c
|
||||
truncate.c
|
||||
usergroup.c
|
||||
usergroup.cpp
|
||||
write.c
|
||||
# string
|
||||
memchr.c
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
Reference in New Issue
Block a user