* 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:
Ingo Weinhold 2008-03-30 00:08:13 +00:00
parent f244a07d99
commit 290946ce80
8 changed files with 340 additions and 63 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 *);

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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));
}