1
0
mirror of https://github.com/falk-werner/webfused synced 2024-10-27 20:44:08 +00:00

Merge pull request #2 from falk-werner/change_user

switch user and group when started as root
This commit is contained in:
Falk Werner 2020-03-19 12:50:20 +01:00 committed by GitHub
commit 38f5c60e7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 369 additions and 7 deletions

View File

@ -51,6 +51,7 @@ target_compile_options(userdb PUBLIC ${OPENSSL_CFLAGS_OTHER})
add_library(webfused-static STATIC
src/webfused/daemon.c
src/webfused/mountpoint_factory.c
src/webfused/change_user.c
src/webfused/config/config.c
src/webfused/config/factory.c
src/webfused/config/builder.c

View File

@ -63,6 +63,12 @@ log:
log_pid = true
}
}
user:
{
name = "webfused"
group = "webfused"
}
```
### Version
@ -154,6 +160,18 @@ This logger does not provide any settings.
| facility | string | daemon | Syslog facility (see syslog documentation) |
| log_pid | bool | false | Add process ID to log messages |
### User
| Setting | Type | Default value | Description |
| ------- | ------ | ------------- | ------------------------------- |
| name | string | *-required-* | Name of the user to switch to. |
| group | string | *-required-* | Name of the group to switch to. |
Webfuse daemon will not run as root. If started as root, webfuse daemon tries to
switch to *user* and *group* provided in config file.
*Note*: user and group are not switched, when webfuse daemon is not started as root.
## Dependencies
- [webfuse](https://github.com/falk-werner/webfuse)

View File

@ -43,3 +43,9 @@ log:
log_pid = true
}
}
#user:
#{
# name = "webfused"
# group = "webfused"
#}

116
src/webfused/change_user.c Normal file
View File

@ -0,0 +1,116 @@
#include "webfused/change_user.h"
#include "webfused/log/log.h"
#include <unistd.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
static bool
wfd_switch_group(
char const * group_name)
{
struct group * group = getgrnam(group_name);
bool result = (NULL != group);
if (!result)
{
WFD_ERROR("failed to switch group: unknown group \'%s\'", group_name);
}
if (result)
{
result = (0 != group->gr_gid);
if (!result)
{
WFD_ERROR("failed to switch group: switch to root (gid 0) is not allowed");
}
}
if (result)
{
result = (0 == setgid(group->gr_gid));
if (!result)
{
WFD_ERROR("failed to set group id: %s", strerror(errno));
}
}
if (result)
{
result = (0 == setgroups(0, NULL));
if (!result)
{
WFD_ERROR("failed to release supplemenatary groups (setgroups): %s", strerror(errno));
}
}
return result;
}
static bool
wfd_switch_user(
char const * user_name)
{
struct passwd * user = getpwnam(user_name);
bool result = (NULL != user);
if (!result)
{
WFD_ERROR("failed to switch user: unknown user \'%s\'", user_name);
}
if (result)
{
result = (0 != user->pw_uid);
if (!result)
{
WFD_ERROR("failed to switch user: switch to root (uid 0) is not allowed");
}
}
if (result)
{
result = (0 == setuid(user->pw_uid));
if (!result)
{
WFD_ERROR("failed to switch user (setuid): %s", strerror(errno));
}
}
return result;
}
bool
wfd_change_user(
char const * user,
char const * group)
{
bool result = true;
bool const is_root = (0 == getuid());
if (is_root)
{
result = ((NULL != user) || (NULL != group));
if (!result)
{
WFD_ERROR("webfuse daemon cannot be run as root: specify user and group in config");
}
if (result)
{
result = wfd_switch_group(group);
}
if (result)
{
result = wfd_switch_user(user);
}
}
return result;
}

View File

@ -0,0 +1,22 @@
#ifndef WFD_CHANGE_USER_H
#define WFD_CHANGE_USER_H
#ifndef __cplusplus
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
extern bool
wfd_change_user(
char const * user,
char const * group);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -68,5 +68,14 @@ wfd_config_builder_set_logger(
return builder.vtable->set_logger(builder.data, provider, level, settings);
}
void
wfd_config_builder_set_user(
struct wfd_config_builder builder,
char const * user,
char const * group)
{
return builder.vtable->set_user(builder.data, user, group);
}

View File

@ -56,6 +56,12 @@ wfd_config_builder_set_logger_fn(
int level,
struct wfd_settings * settings);
typedef void
wfd_config_builder_set_user_fn(
void * data,
char const * user,
char const * group);
struct wfd_config_builder_vtable
{
wfd_config_builder_set_server_vhostname_fn * set_server_vhostname;
@ -66,6 +72,7 @@ struct wfd_config_builder_vtable
wfd_config_builder_add_auth_provider_fn * add_auth_provider;
wfd_config_builder_add_filesystem_fn * add_filesystem;
wfd_config_builder_set_logger_fn * set_logger;
wfd_config_builder_set_user_fn * set_user;
};
struct wfd_config_builder
@ -118,6 +125,12 @@ wfd_config_builder_set_logger(
int level,
struct wfd_settings * settings);
extern void
wfd_config_builder_set_user(
struct wfd_config_builder builder,
char const * user,
char const * group);
#ifdef __cplusplus
}
#endif

View File

@ -6,6 +6,7 @@
#include "webfused/log/manager.h"
#include <stdlib.h>
#include <string.h>
#define WFD_CONFIG_DEFAULT_PORT (8080)
#define WFD_CONFIG_DEFAULT_VHOSTNAME ("localhost")
@ -16,6 +17,8 @@ struct wfd_config
bool has_authenticator;
struct wfd_authenticator authenticator;
struct wfd_mountpoint_factory * mountpoint_factory;
char * user;
char * group;
};
static void
@ -112,6 +115,17 @@ wfd_config_set_logger(
return wfd_log_manager_set_logger(provider, level, settings);
}
static void
wfd_config_set_user(
void * data,
char const * user,
char const * group)
{
struct wfd_config * config = data;
config->user = strdup(user);
config->group = strdup(group);
}
static const struct wfd_config_builder_vtable
wfd_config_vtable_config_builder =
{
@ -122,7 +136,8 @@ wfd_config_vtable_config_builder =
.set_server_document_root = &wfd_config_set_server_document_root,
.add_auth_provider = &wfd_config_add_auth_provider,
.add_filesystem = &wfd_config_add_filesystem,
.set_logger = &wfd_config_set_logger
.set_logger = &wfd_config_set_logger,
.set_user = &wfd_config_set_user
};
struct wfd_config *
@ -138,6 +153,8 @@ wfd_config_create(void)
wf_server_config_set_mountpoint_factory(config->server,
wfd_mountpoint_factory_create_mountpoint,
config->mountpoint_factory);
config->user = NULL;
config->group = NULL;
return config;
}
@ -153,6 +170,8 @@ wfd_config_dispose(
}
wfd_mountpoint_factory_dispose(config->mountpoint_factory);
free(config->user);
free(config->group);
free(config);
}
@ -176,3 +195,17 @@ wfd_config_get_server_config(
return config->server;
}
char const *
wfd_config_get_user(
struct wfd_config * config)
{
return config->user;
}
char const *
wfd_config_get_group(
struct wfd_config * config)
{
return config->group;
}

View File

@ -33,6 +33,14 @@ extern struct wf_server_config *
wfd_config_get_server_config(
struct wfd_config * config);
extern char const *
wfd_config_get_user(
struct wfd_config * config);
extern char const *
wfd_config_get_group(
struct wfd_config * config);
#ifdef __cplusplus
}
#endif

View File

@ -262,6 +262,46 @@ wfd_config_read_filesystems(
return result;
}
static bool
wfd_config_read_user(
config_t * config,
struct wfd_config_builder builder)
{
bool result = true;
bool has_user = (NULL != config_lookup(config, "user"));
if (has_user)
{
char const * user;
{
int rc = config_lookup_string(config, "user.name", &user);
if (CONFIG_TRUE != rc)
{
WFD_ERROR("failed to load config: missing required user propert: \'name\'");
result = false;
}
}
char const * group;
if (result)
{
int rc = config_lookup_string(config, "user.group", &group);
if (CONFIG_TRUE != rc)
{
WFD_ERROR("failed to load config: missing required user propert: \'group\'");
result = false;
}
}
if (result)
{
wfd_config_builder_set_user(builder, user, group);
}
}
return result;
}
static bool
wfd_config_load(
struct wfd_config_builder builder,
@ -273,6 +313,7 @@ wfd_config_load(
&& wfd_config_read_server(config, builder)
&& wfd_config_read_authentication(config, builder)
&& wfd_config_read_filesystems(config, builder)
&& wfd_config_read_user(config, builder)
;
return result;

View File

@ -16,6 +16,7 @@
#include "webfused/log/log.h"
#include "webfused/log/logger.h"
#include "webfused/log/stderr_logger.h"
#include "webfused/change_user.h"
#define WFD_SERVICE_TIMEOUT (1 * 1000)
#define WFD_DEFAULT_CONFIG_FILE ("/etc/webfuse.conf")
@ -111,6 +112,19 @@ int wfd_daemon_run(int argc, char * argv[])
struct wfd_config * config = wfd_config_create();
struct wfd_config_builder builder = wfd_config_get_builder(config);
bool success = wfd_config_load_file(builder, args.config_file);
if (!success)
{
fprintf(stderr, "fatal: failed to load server config\n");
result = EXIT_FAILURE;
}
if (success)
{
success = wfd_change_user(
wfd_config_get_user(config),
wfd_config_get_group(config));
}
if (success)
{
struct wf_server_config * server_config = wfd_config_get_server_config(config);
@ -130,11 +144,6 @@ int wfd_daemon_run(int argc, char * argv[])
result = EXIT_FAILURE;
}
}
else
{
fprintf(stderr, "fatal: failed to load server config\n");
result = EXIT_FAILURE;
}
wfd_config_dispose(config);
}

View File

@ -80,6 +80,15 @@ wfd_MockConfigBuilder_set_logger(
return builder->setLogger(provider, level, settings);
}
static void
wfd_MockConfigBuilder_set_user(
void * data,
char const * user,
char const * group)
{
auto * builder = reinterpret_cast<IConfigBuilder*>(data);
return builder->setUser(user, group);
}
static const wfd_config_builder_vtable wfd_MockConfigBuilder_vtable =
{
@ -90,7 +99,8 @@ static const wfd_config_builder_vtable wfd_MockConfigBuilder_vtable =
&wfd_MockConfigBuilder_set_server_document_root,
&wfd_MockConfigBuilder_add_auth_provider,
&wfd_MockConfigBuilder_add_filesystem,
&wfd_MockConfigBuilder_set_logger
&wfd_MockConfigBuilder_set_logger,
&wfd_MockConfigBuilder_set_user
};
}

View File

@ -19,6 +19,7 @@ public:
virtual bool addAuthProvider(char const * provider, wfd_settings * settings) = 0;
virtual bool addFilesystem(char const * name, char const * mountpoint) = 0;
virtual bool setLogger(char const * provider, int level, wfd_settings * settings) = 0;
virtual void setUser(char const * user, char const * group) = 0;
};
class MockConfigBuilder: public IConfigBuilder
@ -33,6 +34,7 @@ public:
MOCK_METHOD2(addAuthProvider, bool (char const * provider, wfd_settings * settings));
MOCK_METHOD2(addFilesystem, bool (char const * name, char const * mountpoint));
MOCK_METHOD3(setLogger, bool (char const * provider, int level, wfd_settings * settings));
MOCK_METHOD2(setUser, void (char const * user, char const * group));
struct wfd_config_builder getBuilder();
};

View File

@ -103,3 +103,17 @@ TEST(config, set_logger)
wfd_config_dispose(config);
}
TEST(config, do_set_user)
{
wfd_config * config = wfd_config_create();
ASSERT_NE(nullptr, config);
wfd_config_builder builder = wfd_config_get_builder(config);
wfd_config_builder_set_user(builder, "some.user", "some.group");
ASSERT_STREQ("some.user", wfd_config_get_user(config));
ASSERT_STREQ("some.group", wfd_config_get_group(config));
wfd_config_dispose(config);
}

View File

@ -546,3 +546,63 @@ TEST(config, log_fail_invalid_level)
ASSERT_FALSE(result);
}
TEST(config, set_user)
{
MockLogger logger;
EXPECT_CALL(logger, log(_, _, _)).Times(0);
EXPECT_CALL(logger, onclose()).Times(1);
StrictMock<MockConfigBuilder> builder;
EXPECT_CALL(builder, setUser(_, _)).Times(1);
char const config_text[] =
"version = { major = 1, minor = 0 }\n"
"user:\n"
"{\n"
" name = \"webfused\"\n"
" group = \"webfused\"\n"
"}\n"
;
bool result = wfd_config_load_string(builder.getBuilder(), config_text);
ASSERT_TRUE(result);
}
TEST(config, set_user_fail_missing_name)
{
MockLogger logger;
EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1);
EXPECT_CALL(logger, onclose()).Times(1);
StrictMock<MockConfigBuilder> builder;
EXPECT_CALL(builder, setUser(_, _)).Times(0);
char const config_text[] =
"version = { major = 1, minor = 0 }\n"
"user:\n"
"{\n"
" group = \"webfused\"\n"
"}\n"
;
bool result = wfd_config_load_string(builder.getBuilder(), config_text);
ASSERT_FALSE(result);
}
TEST(config, set_user_fail_missing_group)
{
MockLogger logger;
EXPECT_CALL(logger, log(WFD_LOGLEVEL_ERROR, _, _)).Times(1);
EXPECT_CALL(logger, onclose()).Times(1);
StrictMock<MockConfigBuilder> builder;
EXPECT_CALL(builder, setUser(_, _)).Times(0);
char const config_text[] =
"version = { major = 1, minor = 0 }\n"
"user:\n"
"{\n"
" name = \"webfused\"\n"
"}\n"
;
bool result = wfd_config_load_string(builder.getBuilder(), config_text);
ASSERT_FALSE(result);
}