summaryrefslogtreecommitdiff
path: root/src/core/privilege.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/privilege.c')
-rw-r--r--src/core/privilege.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/core/privilege.c b/src/core/privilege.c
new file mode 100644
index 0000000..254dbc6
--- /dev/null
+++ b/src/core/privilege.c
@@ -0,0 +1,233 @@
+#if defined(__linux__)
+#define _DEFAULT_SOURCE
+#endif
+
+#include "hybbx/privilege.h"
+#include "hybbx/config.h"
+#include "hybbx/log.h"
+#include "hybbx/types.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(_WIN32)
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#if !defined(_WIN32)
+
+static int parse_optional_uid(const char *raw, uid_t *out)
+{
+ char *end;
+ unsigned long value;
+
+ if (raw == NULL || raw[0] == '\0') {
+ return 0;
+ }
+ errno = 0;
+ value = strtoul(raw, &end, 10);
+ if (end == raw || *end != '\0' || errno != 0) {
+ return -1;
+ }
+ *out = (uid_t)value;
+ return 1;
+}
+
+static int parse_optional_gid(const char *raw, gid_t *out)
+{
+ char *end;
+ unsigned long value;
+
+ if (raw == NULL || raw[0] == '\0') {
+ return 0;
+ }
+ errno = 0;
+ value = strtoul(raw, &end, 10);
+ if (end == raw || *end != '\0' || errno != 0) {
+ return -1;
+ }
+ *out = (gid_t)value;
+ return 1;
+}
+
+static hybbx_result_t resolve_target(
+ const hybbx_config_t *config,
+ uid_t *uid_out,
+ gid_t *gid_out,
+ char name_buf[64],
+ size_t name_buf_len)
+{
+ const char *user_name;
+ const char *group_name;
+ const char *uid_raw;
+ const char *gid_raw;
+ struct passwd *pw = NULL;
+ struct group *gr = NULL;
+ uid_t uid_override = 0;
+ gid_t gid_override = 0;
+ int have_uid = 0;
+ int have_gid = 0;
+
+ user_name = hybbx_config_get(config, "service", "user", NULL);
+ group_name = hybbx_config_get(config, "service", "group", NULL);
+ uid_raw = hybbx_config_get(config, "service", "uid", NULL);
+ gid_raw = hybbx_config_get(config, "service", "gid", NULL);
+
+ have_uid = parse_optional_uid(uid_raw, &uid_override);
+ if (have_uid < 0) {
+ hybbx_log_warn("[service] invalid uid=%s", uid_raw);
+ return HYBBX_ERR_INVALID;
+ }
+ have_gid = parse_optional_gid(gid_raw, &gid_override);
+ if (have_gid < 0) {
+ hybbx_log_warn("[service] invalid gid=%s", gid_raw);
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (user_name != NULL && user_name[0] != '\0') {
+ pw = getpwnam(user_name);
+ if (pw == NULL) {
+ hybbx_log_warn("[service] unknown user=%s", user_name);
+ return HYBBX_ERR_INVALID;
+ }
+ *uid_out = pw->pw_uid;
+ *gid_out = pw->pw_gid;
+ if (name_buf_len > 0) {
+ snprintf(name_buf, name_buf_len, "%s", pw->pw_name);
+ }
+ } else if (have_uid) {
+ pw = getpwuid(uid_override);
+ if (pw == NULL) {
+ hybbx_log_warn("[service] unknown uid=%s", uid_raw);
+ return HYBBX_ERR_INVALID;
+ }
+ *uid_out = pw->pw_uid;
+ *gid_out = pw->pw_gid;
+ if (name_buf_len > 0) {
+ snprintf(name_buf, name_buf_len, "%s", pw->pw_name);
+ }
+ } else {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (have_uid) {
+ *uid_out = uid_override;
+ }
+ if (group_name != NULL && group_name[0] != '\0') {
+ gr = getgrnam(group_name);
+ if (gr == NULL) {
+ hybbx_log_warn("[service] unknown group=%s", group_name);
+ return HYBBX_ERR_INVALID;
+ }
+ *gid_out = gr->gr_gid;
+ } else if (have_gid) {
+ if (getgrgid(gid_override) == NULL) {
+ hybbx_log_warn("[service] unknown gid=%s", gid_raw);
+ return HYBBX_ERR_INVALID;
+ }
+ *gid_out = gid_override;
+ }
+
+ return HYBBX_OK;
+}
+
+static hybbx_result_t drop_to(uid_t uid, gid_t gid, const char *name)
+{
+ if (getuid() != 0) {
+ hybbx_log_warn("[service] privilege drop requires root start (euid=0)");
+ return HYBBX_ERR_DENIED;
+ }
+
+ if (name != NULL && name[0] != '\0') {
+ if (initgroups(name, gid) != 0) {
+ hybbx_log_warn("[service] initgroups(%s) failed: %s",
+ name, strerror(errno));
+ return HYBBX_ERR_IO;
+ }
+ }
+
+ if (setgid(gid) != 0) {
+ hybbx_log_warn("[service] setgid(%u) failed: %s",
+ (unsigned)gid, strerror(errno));
+ return HYBBX_ERR_IO;
+ }
+
+ if (setuid(uid) != 0) {
+ hybbx_log_warn("[service] setuid(%u) failed: %s",
+ (unsigned)uid, strerror(errno));
+ return HYBBX_ERR_IO;
+ }
+
+ if (setuid(0) == 0 || seteuid(0) == 0) {
+ hybbx_log_warn("[service] privilege drop incomplete — still root");
+ return HYBBX_ERR_IO;
+ }
+
+ hybbx_log_info("[service] dropped privileges to uid=%u gid=%u (%s)",
+ (unsigned)uid, (unsigned)gid,
+ name != NULL && name[0] != '\0' ? name : "?");
+ return HYBBX_OK;
+}
+
+#endif /* !_WIN32 */
+
+hybbx_result_t hybbx_privilege_apply_from_config(const hybbx_config_t *config)
+{
+#if defined(_WIN32)
+ (void)config;
+ return HYBBX_OK;
+#else
+ const char *user_name;
+ uid_t target_uid = 0;
+ gid_t target_gid = 0;
+ char resolved_name[64];
+ hybbx_result_t rc;
+
+ if (config == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ resolved_name[0] = '\0';
+ user_name = hybbx_config_get(config, "service", "user", NULL);
+
+ if (geteuid() == 0) {
+ if ((user_name == NULL || user_name[0] == '\0') &&
+ hybbx_config_get(config, "service", "uid", NULL) == NULL) {
+ hybbx_log_warn(
+ "[service] refusing to run as root — set [service] user= "
+ "(and optional group=)");
+ return HYBBX_ERR_DENIED;
+ }
+
+ rc = resolve_target(config, &target_uid, &target_gid,
+ resolved_name, sizeof(resolved_name));
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ return drop_to(target_uid, target_gid,
+ resolved_name[0] != '\0' ? resolved_name : user_name);
+ }
+
+ if (user_name != NULL && user_name[0] != '\0') {
+ struct passwd *pw = getpwnam(user_name);
+
+ if (pw == NULL) {
+ hybbx_log_warn("[service] unknown configured user=%s", user_name);
+ return HYBBX_OK;
+ }
+ if ((uid_t)geteuid() != pw->pw_uid) {
+ hybbx_log_warn(
+ "[service] running as uid=%u but [service] user=%s (uid=%u)",
+ (unsigned)geteuid(), user_name, (unsigned)pw->pw_uid);
+ }
+ }
+
+ return HYBBX_OK;
+#endif
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com