#if defined(__linux__) #define _DEFAULT_SOURCE #endif #include "hybbx/privilege.h" #include "hybbx/config.h" #include "hybbx/log.h" #include "hybbx/types.h" #include #include #include #if !defined(_WIN32) #include #include #include #include #include #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 }