diff options
Diffstat (limited to 'FICS')
| -rw-r--r-- | FICS/addgroup.cpp | 198 | ||||
| -rw-r--r-- | FICS/addgroup.h | 17 | ||||
| -rw-r--r-- | FICS/prep_dir_for_privdrop.cpp | 150 | ||||
| -rw-r--r-- | FICS/prep_dir_for_privdrop.h | 11 | 
4 files changed, 376 insertions, 0 deletions
diff --git a/FICS/addgroup.cpp b/FICS/addgroup.cpp new file mode 100644 index 0000000..59547d0 --- /dev/null +++ b/FICS/addgroup.cpp @@ -0,0 +1,198 @@ +// SPDX-FileCopyrightText: 2025 Markus Uhlin <maxxe@rpblc.net> +// SPDX-License-Identifier: ISC + +#include <string> +#include <vector> + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "addgroup.h" + +#define SELF_TEST 0 + +struct group_info { +	std::string	name; +	std::string	password; +	int		gid; +	std::string	members; + +	group_info() +	{ +		this->name.assign(""); +		this->password.assign(""); +		this->gid = 0; +		this->members.assign(""); +	} + +	group_info(const char *p_name, const char *p_password, const int p_gid, +		   const char *p_members) +	{ +		this->name.assign(p_name); +		this->password.assign(p_password); +		this->gid = p_gid; +		this->members.assign(p_members); +	} +}; + +static std::vector<group_info> groups; + +static const int	FIRST_SYSTEM_GID = 100; +static const int	LAST_SYSTEM_GID = 999; + +static bool +is_free_gid(const int gid) +{ +	for (auto it = groups.begin(); it != groups.end(); ++it) { +		if ((*it).gid == gid) +			return false; +	} +	return true; +} + +static int +get_free_gid(void) +{ +	if (groups.empty()) +		return -1; +	for (int gid = FIRST_SYSTEM_GID; gid <= LAST_SYSTEM_GID; gid++) { +		if (is_free_gid(gid)) +			return gid; +	} +	return -1; +} + +int +fics_addgroup(const char *name) +{ +	int	fd; +	int	gid; + +	if (name == nullptr || strcmp(name, "") == 0) +		return -1; +	else if (group_exists(name)) +		return 0; +	else if ((gid = get_free_gid()) == -1) +		return -1; +	fd = open("/etc/group", (O_RDWR|O_APPEND)); +	if (fd < 0) +		return -1; +	struct group_info group(name, "*", gid, ""); +	dprintf(fd, "%s:%s:%d:%s\n", group.name.c_str(), group.password.c_str(), +	    group.gid, group.members.c_str()); +	close(fd); +	groups.push_back(group); +	return 0; +} + +bool +get_group_id(const char *name, int *gid) +{ +	if (name == nullptr || strcmp(name, "") == 0 || gid == nullptr || +	    groups.empty()) { +		if (gid) +			*gid = 0; +		return false; +	} + +	for (auto it = groups.begin(); it != groups.end(); ++it) { +		if (strcmp((*it).name.c_str(), name) == 0) { +			*gid = (*it).gid; +			return true; +		} +	} + +	*gid = 0; +	return false; +} + +bool +get_next_line_from_file(FILE *fp, char **line) +{ +	const int LINE_MAX_LEN = 2048; + +	if (fp == nullptr || line == nullptr) +		errx(1, "%s: invalid argument", __func__); + +	if (*line) { +		delete[] *line; +		*line = nullptr; +	} + +	*line = new char[LINE_MAX_LEN]; + +	return (fgets(*line, LINE_MAX_LEN, fp) ? true : false); +} + +bool +group_exists(const char *name) +{ +	if (name == nullptr || strcmp(name, "") == 0 || groups.empty()) +		return false; +	for (auto it = groups.begin(); it != groups.end(); ++it) { +		if (strcmp((*it).name.c_str(), name) == 0) +			return true; +	} +	return false; +} + +int +read_the_group_permissions_file(const char *path) +{ +	FILE		*fp = nullptr; +	bool		 read_ok = false; +	char		*line = nullptr; +	const char	*token[4]; +	const char	 delim[] = ":"; +	int		 gid = 0; + +	if (!groups.empty()) +		return 0; +	if ((fp = fopen(path, "r")) == nullptr) +		return -1; +	while (get_next_line_from_file(fp, &line)) { +		token[0] = strsep(&line, delim); +		token[1] = strsep(&line, delim); +		token[2] = strsep(&line, delim); +		token[3] = strsep(&line, delim); + +		if (token[0] == nullptr || +		    token[1] == nullptr || +		    token[2] == nullptr || +		    token[3] == nullptr) { +			warnx("%s: too few tokens", __func__); +			continue; +		} else if (sscanf(token[2], "%d", &gid) != 1) { +			warnx("%s: sscanf() error", __func__); +			continue; +		} + +		struct group_info group(token[0], token[1], gid, token[3]); +		groups.push_back(group); +	} + +	if (feof(fp)) +		read_ok = true; + +	fclose(fp); +	delete[] line; + +	return (read_ok ? 0 : -1); +} + +#if SELF_TEST +int +main(void) +{ +	if (read_the_group_permissions_file("/etc/group") == -1) +		errx(1, "failed to read the group permissions file"); +	if (fics_addgroup("chess") == -1) +		errx(1, "failed to add a group"); +	puts("ok"); +	return 0; +} +#endif diff --git a/FICS/addgroup.h b/FICS/addgroup.h new file mode 100644 index 0000000..e5b1906 --- /dev/null +++ b/FICS/addgroup.h @@ -0,0 +1,17 @@ +#ifndef ADDGROUP_H +#define ADDGROUP_H + +#include "common.h" + +#include <stdbool.h> +#include <stdio.h> + +__FICS_BEGIN_DECLS +int	fics_addgroup(const char *); +bool	get_group_id(const char *, int *); +bool	get_next_line_from_file(FILE *, char **); // uses 'new[]' +bool	group_exists(const char *); +int	read_the_group_permissions_file(const char *); +__FICS_END_DECLS + +#endif diff --git a/FICS/prep_dir_for_privdrop.cpp b/FICS/prep_dir_for_privdrop.cpp new file mode 100644 index 0000000..399fdb4 --- /dev/null +++ b/FICS/prep_dir_for_privdrop.cpp @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: 2025 Markus Uhlin <maxxe@rpblc.net> +// SPDX-License-Identifier: ISC + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <fcntl.h> +#include <pwd.h> +#include <string.h> +#include <unistd.h> + +#include <filesystem> +#include <string> + +#include "addgroup.h" +#include "prep_dir_for_privdrop.h" +#include "settings.h" +#include "utils.h" + +namespace fs = std::filesystem; + +static int +get_uid_and_gid(uid_t &uid, gid_t &gid) +{ +	struct passwd *pw = nullptr; +	int i = 0; + +	if ((pw = getpwnam(settings_get("privdrop_user"))) == nullptr || +	    !get_group_id(settings_get("sysgroup"), &i)) { +		uid = 0; +		gid = 0; +		return -1; +	} + +	uid = pw->pw_uid; +	gid = static_cast<gid_t>(i); +	return 0; +} + +static int +check_prep_done(const char *p_path) +{ +	std::string path(p_path); + +	path.append("/").append(".prep_done"); + +	if (file_exists(path.c_str())) +		return 0; +	return -1; +} + +static void +prep_done(const char *p_path) +{ +	int		fd; +	std::string	path(p_path); +	uid_t		uid = 0; +	gid_t		gid = 0; + +	path.append("/").append(".prep_done"); + +	fd = open(path.c_str(), (O_RDWR|O_CREAT|O_TRUNC), (S_IRUSR|S_IWUSR| +	    S_IRGRP|S_IROTH)); +	if (fd < 0) { +		warn("%s: open", __func__); +		return; +	} + +	dprintf(fd, "FICS dir preparation done.\n" +	    "Do not remove this file while FICS is installed.\n"); +	close(fd); + +	if (get_uid_and_gid(uid, gid) == 0) { +		if (chown(path.c_str(), uid, gid) != 0) +			warn("%s: chown", __func__); +	} +} + +int +drop_root_privileges(const char *path) +{ +	struct passwd *pw; + +	if ((pw = getpwnam(settings_get("privdrop_user"))) == nullptr) { +		warnx("%s: password database search failed", __func__); +		return -1; +	} else if (chdir(path) != 0) { +		warn("%s: chdir(%s)", __func__, path); +		return -1; +	} else if (setgid(pw->pw_gid) == -1) { +		warn("%s: setgid", __func__); +		return -1; +	} else if (setegid(pw->pw_gid) == -1) { +		warn("%s: setegid", __func__); +		return -1; +	} else if (setuid(pw->pw_uid) == -1) { +		warn("%s: setuid", __func__); +		return -1; +	} else if (seteuid(pw->pw_uid) == -1) { +		warn("%s: seteuid", __func__); +		return -1; +	} + +	return 0; +} + +int +prep_dir_for_privdrop(const char *path) +{ +	if (path == nullptr || strcmp(path, "") == 0) +		return -1; +	else if (check_prep_done(path) == 0) +		return 0; +	try { +		fs::path		v_path = path; +		fs::directory_iterator	dir_it(v_path); +		uid_t			uid = 0; +		gid_t			gid = 0; +		constexpr mode_t	dir_mode = (S_IRUSR|S_IWUSR| +					    S_IRGRP|S_IWGRP|S_IROTH); +		constexpr mode_t	file_mode = (S_IRUSR|S_IWUSR|S_IRGRP| +					    S_IROTH); + +		if (get_uid_and_gid(uid, gid) == -1) +			throw std::runtime_error("failed to get uid/gid"); +		for (auto const &dir_ent : dir_it) { +			const std::string str(dir_ent.path().string()); +			 +			if (chown(str.c_str(), uid, gid) != 0) { +				warn("%s: chown(%s, ...)", __func__, +				    str.c_str()); +			} + +			if (dir_ent.is_directory()) { +				if (chmod(str.c_str(), dir_mode) != 0) +					warn("%s: chmod", __func__); +			} else if (dir_ent.is_regular_file()) { +				if (chmod(str.c_str(), file_mode) != 0) +					warn("%s: chmod", __func__); +			} +		} +	} catch (const std::exception &ex) { +		warnx("%s: %s", __func__, ex.what()); +		return -1; +	} + +	prep_done(path); +	return 0; +} diff --git a/FICS/prep_dir_for_privdrop.h b/FICS/prep_dir_for_privdrop.h new file mode 100644 index 0000000..f2d8915 --- /dev/null +++ b/FICS/prep_dir_for_privdrop.h @@ -0,0 +1,11 @@ +#ifndef PREP_DIR_FOR_PRIVDROP_H +#define PREP_DIR_FOR_PRIVDROP_H + +#include "common.h" + +__FICS_BEGIN_DECLS +int	drop_root_privileges(const char *); +int	prep_dir_for_privdrop(const char *); +__FICS_END_DECLS + +#endif  | 
