* Implemented some basics for supplementary groups support:
- The kernel stores the group IDs in the team structure. They are correctly inherited on fork() and load_image_etc(). - Implemented getgroups() for real, i.e. it retrieves the groups associated with the process. - Implemented setgroups(), initgroups() and (the BSDish) getgrouplist(). The latter two read the group information from the "group database" /etc/group (if existing). - Change the BIND port config, since we do have getgrouplist() now. * The set-uid feature was broken when the path to the executable was relative, since we used stat(), which, in the kernel, uses the kernel IO context. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24669 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
f244a07d99
commit
290946ce80
@ -160,7 +160,6 @@ extern gid_t getegid(void);
|
||||
extern uid_t geteuid(void);
|
||||
extern gid_t getgid(void);
|
||||
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);
|
||||
@ -169,6 +168,12 @@ 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 int getgrouplist(const char* user, gid_t baseGroup,
|
||||
gid_t* groupList, int* groupCount);
|
||||
extern int getgroups(int groupCount, gid_t groupList[]);
|
||||
extern int initgroups(const char* user, gid_t baseGroup);
|
||||
extern int setgroups(int groupCount, const gid_t* groupList);
|
||||
|
||||
extern char *getlogin(void);
|
||||
extern int getlogin_r(char *name, size_t nameSize);
|
||||
|
||||
|
@ -116,11 +116,12 @@ extern status_t _kern_get_team_usage_info(team_id team, int32 who, team_usage_i
|
||||
// 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);
|
||||
extern ssize_t _kern_getgroups(int groupCount, gid_t* groupList);
|
||||
extern status_t _kern_setgroups(int groupCount, const gid_t* groupList);
|
||||
|
||||
// signal functions
|
||||
extern status_t _kern_send_signal(pid_t tid, uint sig);
|
||||
|
@ -196,6 +196,8 @@ struct team {
|
||||
gid_t saved_set_gid;
|
||||
gid_t real_gid;
|
||||
gid_t effective_gid;
|
||||
gid_t* supplementary_groups;
|
||||
int supplementary_group_count;
|
||||
};
|
||||
|
||||
typedef int32 (*thread_entry_func)(thread_func, void *);
|
||||
|
@ -27,9 +27,10 @@ status_t update_set_id_user_and_group(struct team* team, const char* file);
|
||||
|
||||
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);
|
||||
ssize_t _user_getgroups(int groupCount, gid_t* groupList);
|
||||
ssize_t _user_setgroups(int groupCount, const gid_t* groupList);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
@ -11,7 +11,7 @@
|
||||
/* #undef POSIX_GETGRNAM_R */
|
||||
|
||||
#define NEED_SETGROUPENT 1
|
||||
#define NEED_GETGROUPLIST 1
|
||||
/* #undef NEED_GETGROUPLIST */
|
||||
|
||||
/* define if prototype for getgrnam_r() is required */
|
||||
#define NEED_GETGRNAM_R 1
|
||||
|
@ -829,6 +829,9 @@ create_team_struct(const char *name, bool kernel)
|
||||
team->flags = 0;
|
||||
team->death_sem = -1;
|
||||
|
||||
team->supplementary_groups = NULL;
|
||||
team->supplementary_group_count = 0;
|
||||
|
||||
team->dead_threads_kernel_time = 0;
|
||||
team->dead_threads_user_time = 0;
|
||||
|
||||
@ -913,6 +916,8 @@ delete_team_struct(struct team *team)
|
||||
while (job_control_entry* entry = team->dead_children->entries.RemoveHead())
|
||||
delete entry;
|
||||
|
||||
malloc_referenced_release(team->supplementary_groups);
|
||||
|
||||
delete team->job_control_entry;
|
||||
// usually already NULL and transferred to the parent
|
||||
delete team->continued_children;
|
||||
@ -1959,6 +1964,8 @@ team_init(kernel_args *args)
|
||||
sKernelTeam->saved_set_gid = 0;
|
||||
sKernelTeam->real_gid = 0;
|
||||
sKernelTeam->effective_gid = 0;
|
||||
sKernelTeam->supplementary_groups = NULL;
|
||||
sKernelTeam->supplementary_group_count = 0;
|
||||
|
||||
insert_team_into_group(group, sKernelTeam);
|
||||
|
||||
|
@ -6,16 +6,19 @@
|
||||
#include <usergroup.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <new.h>
|
||||
#include <new>
|
||||
|
||||
#include <heap.h>
|
||||
#include <kernel.h>
|
||||
#include <syscalls.h>
|
||||
#include <team.h>
|
||||
#include <thread.h>
|
||||
#include <thread_types.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <vfs.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
@ -137,6 +140,81 @@ common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel)
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
common_getgroups(int groupCount, gid_t* groupList, bool kernel)
|
||||
{
|
||||
struct team* team = thread_get_current_thread()->team;
|
||||
|
||||
InterruptsSpinLocker _(team_spinlock);
|
||||
|
||||
const gid_t* groups = team->supplementary_groups;
|
||||
int actualCount = team->supplementary_group_count;
|
||||
|
||||
// follow the specification and return always at least one group
|
||||
if (actualCount == 0) {
|
||||
groups = &team->effective_gid;
|
||||
actualCount = 1;
|
||||
}
|
||||
|
||||
// check for sufficient space
|
||||
if (groupCount < actualCount)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// copy
|
||||
if (kernel) {
|
||||
memcpy(groupList, groups, actualCount);
|
||||
} else {
|
||||
if (!IS_USER_ADDRESS(groupList)
|
||||
|| user_memcpy(groupList, groups,
|
||||
actualCount * sizeof(gid_t)) != B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
}
|
||||
|
||||
return actualCount;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
common_setgroups(int groupCount, const gid_t* groupList, bool kernel)
|
||||
{
|
||||
if (groupCount < 0 || groupCount > NGROUPS_MAX)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
gid_t* newGroups = NULL;
|
||||
if (groupCount > 0) {
|
||||
newGroups = (gid_t*)malloc_referenced(sizeof(gid_t) * groupCount);
|
||||
if (newGroups == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
if (kernel) {
|
||||
memcpy(newGroups, groupList, sizeof(gid_t) * groupCount);
|
||||
} else {
|
||||
if (!IS_USER_ADDRESS(groupList)
|
||||
|| user_memcpy(newGroups, groupList,
|
||||
sizeof(gid_t) * groupCount) != B_OK) {
|
||||
free(newGroups);
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InterruptsSpinLocker locker(team_spinlock);
|
||||
|
||||
struct team* team = thread_get_current_thread()->team;
|
||||
|
||||
gid_t* toFree = team->supplementary_groups;
|
||||
team->supplementary_groups = newGroups;
|
||||
team->supplementary_group_count = groupCount;
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
malloc_referenced_release(toFree);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Kernel Private
|
||||
|
||||
|
||||
@ -151,6 +229,10 @@ inherit_parent_user_and_group(struct team* team, struct team* parent)
|
||||
team->saved_set_gid = parent->saved_set_gid;
|
||||
team->real_gid = parent->real_gid;
|
||||
team->effective_gid = parent->effective_gid;
|
||||
|
||||
malloc_referenced_acquire(parent->supplementary_groups);
|
||||
team->supplementary_groups = parent->supplementary_groups;
|
||||
team->supplementary_group_count = parent->supplementary_group_count;
|
||||
}
|
||||
|
||||
|
||||
@ -158,8 +240,9 @@ status_t
|
||||
update_set_id_user_and_group(struct team* team, const char* file)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(file, &st) < 0)
|
||||
return errno;
|
||||
status_t status = vfs_read_stat(-1, file, true, &st, false);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
InterruptsSpinLocker _(team_spinlock);
|
||||
|
||||
@ -195,19 +278,6 @@ _kern_getuid(bool effective)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@ -222,6 +292,20 @@ _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
_kern_getgroups(int groupCount, gid_t* groupList)
|
||||
{
|
||||
return common_getgroups(groupCount, groupList, true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_kern_setgroups(int groupCount, const gid_t* groupList)
|
||||
{
|
||||
return common_setgroups(groupCount, groupList, true);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Syscalls
|
||||
|
||||
|
||||
@ -243,42 +327,6 @@ _user_getuid(bool effective)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@ -291,3 +339,20 @@ _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
|
||||
{
|
||||
return common_setreuid(ruid, euid, setAllIfPrivileged, false);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
_user_getgroups(int groupCount, gid_t* groupList)
|
||||
{
|
||||
return common_getgroups(groupCount, groupList, false);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
_user_setgroups(int groupCount, const gid_t* groupList)
|
||||
{
|
||||
if (!is_privileged(thread_get_current_thread()->team))
|
||||
return EPERM;
|
||||
|
||||
return common_setgroups(groupCount, groupList, false);
|
||||
}
|
||||
|
@ -7,8 +7,12 @@
|
||||
|
||||
#include <syscalls.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
@ -27,6 +31,134 @@ set_errno_if_necessary(const T& result)
|
||||
}
|
||||
|
||||
|
||||
class FileLineReader {
|
||||
public:
|
||||
FileLineReader(int fd)
|
||||
: fFD(fd),
|
||||
fSize(0),
|
||||
fOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
char* NextLine()
|
||||
{
|
||||
char* eol;
|
||||
if (fOffset >= fSize
|
||||
|| (eol = strchr(fBuffer + fOffset, '\n')) == NULL) {
|
||||
_ReadBuffer();
|
||||
if (fOffset >= fSize)
|
||||
return NULL;
|
||||
|
||||
eol = strchr(fBuffer + fOffset, '\n');
|
||||
if (eol == NULL)
|
||||
eol = fBuffer + fSize;
|
||||
}
|
||||
|
||||
char* result = fBuffer + fOffset;
|
||||
*eol = '\0';
|
||||
fOffset = eol + 1 - fBuffer;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* NextNonEmptyLine()
|
||||
{
|
||||
while (char* line = NextLine()) {
|
||||
while (*line != '\0' && isspace(*line))
|
||||
line++;
|
||||
|
||||
if (*line != '\0' && *line != '#')
|
||||
return line;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
void _ReadBuffer()
|
||||
{
|
||||
// catch special cases: full buffer or already done with the file
|
||||
if (fSize == LINE_MAX || fFD < 0)
|
||||
return;
|
||||
|
||||
// move buffered bytes to the beginning of the buffer
|
||||
int leftBytes = 0;
|
||||
if (fOffset < fSize) {
|
||||
leftBytes = fSize - fOffset;
|
||||
memmove(fBuffer, fBuffer + fOffset, leftBytes);
|
||||
}
|
||||
|
||||
fOffset = 0;
|
||||
fSize = leftBytes;
|
||||
|
||||
// read
|
||||
ssize_t bytesRead = read(fFD, fBuffer + leftBytes,
|
||||
LINE_MAX - leftBytes);
|
||||
if (bytesRead > 0)
|
||||
fSize += bytesRead;
|
||||
else
|
||||
fFD = -1;
|
||||
|
||||
// null-terminate
|
||||
fBuffer[fSize] = '\0';
|
||||
}
|
||||
|
||||
private:
|
||||
int fFD;
|
||||
char fBuffer[LINE_MAX + 1];
|
||||
int fSize;
|
||||
int fOffset;
|
||||
};
|
||||
|
||||
|
||||
class Tokenizer {
|
||||
public:
|
||||
Tokenizer(char* string)
|
||||
: fString(string)
|
||||
{
|
||||
}
|
||||
|
||||
char* NextToken(char separator)
|
||||
{
|
||||
if (fString == NULL)
|
||||
return NULL;
|
||||
|
||||
char* token = fString;
|
||||
fString = strchr(fString, separator);
|
||||
if (fString != NULL) {
|
||||
*fString = '\0';
|
||||
fString++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
char* NextTrimmedToken(char separator)
|
||||
{
|
||||
char* token = NextToken(separator);
|
||||
if (token == NULL)
|
||||
return NULL;
|
||||
|
||||
// skip spaces at the beginning
|
||||
while (*token != '\0' && isspace(*token))
|
||||
token++;
|
||||
|
||||
// cut off spaces at the end
|
||||
char* end = token + strlen(token);
|
||||
while (end != token && isspace(end[-1]))
|
||||
end--;
|
||||
*end = '\0';
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private:
|
||||
char* fString;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
gid_t
|
||||
getegid(void)
|
||||
{
|
||||
@ -55,13 +187,6 @@ getuid(void)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
getgroups(int groupSize, gid_t groupList[])
|
||||
{
|
||||
return set_errno_if_necessary(_kern_getgroups(groupSize, groupList));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
setgid(gid_t gid)
|
||||
{
|
||||
@ -102,3 +227,74 @@ setreuid(uid_t ruid, uid_t euid)
|
||||
{
|
||||
return set_errno_if_necessary(_kern_setreuid(ruid, euid, false));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
getgrouplist(const char* user, gid_t baseGroup, gid_t* groupList,
|
||||
int* groupCount)
|
||||
{
|
||||
int maxGroupCount = *groupCount;
|
||||
*groupCount = 0;
|
||||
|
||||
// read group file
|
||||
int fd = open("/etc/group", O_RDONLY);
|
||||
FileLineReader reader(fd);
|
||||
|
||||
while (char* line = reader.NextNonEmptyLine()) {
|
||||
Tokenizer lineTokenizer(line);
|
||||
lineTokenizer.NextTrimmedToken(':'); // group name
|
||||
lineTokenizer.NextTrimmedToken(':'); // group password
|
||||
char* groupID = lineTokenizer.NextTrimmedToken(':');
|
||||
|
||||
if (groupID == NULL || !isdigit(*groupID))
|
||||
continue;
|
||||
|
||||
gid_t gid = atol(groupID);
|
||||
if (gid == baseGroup)
|
||||
continue;
|
||||
|
||||
while (char* groupUser = lineTokenizer.NextTrimmedToken(',')) {
|
||||
if (*groupUser != '\0' && strcmp(groupUser, user) == 0) {
|
||||
if (*groupCount < maxGroupCount)
|
||||
groupList[*groupCount] = gid;
|
||||
++*groupCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
// put in the base group
|
||||
if (*groupCount < maxGroupCount)
|
||||
groupList[*groupCount] = baseGroup;
|
||||
++*groupCount;
|
||||
|
||||
return *groupCount <= maxGroupCount ? *groupCount : -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
getgroups(int groupCount, gid_t groupList[])
|
||||
{
|
||||
return set_errno_if_necessary(_kern_getgroups(groupCount, groupList));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
initgroups(const char* user, gid_t baseGroup)
|
||||
{
|
||||
gid_t groups[NGROUPS_MAX + 1];
|
||||
int groupCount = NGROUPS_MAX + 1;
|
||||
if (getgrouplist(user, baseGroup, groups, &groupCount) < 0)
|
||||
return -1;
|
||||
|
||||
return setgroups(groupCount, groups);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
setgroups(int groupCount, const gid_t* groupList)
|
||||
{
|
||||
return set_errno_if_necessary(_kern_setgroups(groupCount, groupList));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user