diff options
author | Sergey Poznyakoff <gray@nxc.no> | 2017-08-15 09:58:21 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@nxc.no> | 2017-08-15 09:58:21 +0300 |
commit | 4d1f42638d16cf6f26983cee38a4c36e4b64b68a (patch) | |
tree | 0718ed6117fc8c021a2a68294f3a74fb7eb1e495 | |
parent | 03a9d11a59cec2047ec1282e0dfa76ef1f248a18 (diff) | |
download | nssync-4d1f42638d16cf6f26983cee38a4c36e4b64b68a.tar.gz nssync-4d1f42638d16cf6f26983cee38a4c36e4b64b68a.tar.bz2 |
Switch to user's privileges, if required
* src/runas.c: New file.
* src/Makefile.am: Add runas.c
* src/config.c: Move mysql configuration to a separate block.
New configuration statements: "user" and "group".
* src/nssync.c (main): Optionally switch to user privileges
before starting operation.
* src/nssync.h (runas_user, runas_group): New externs.
(DEFAULT_NSSYNC_ADDR): Change to a safer value.
(runas): New proto.
* src/server.c (nssync_resources): Change endpoint.
(nssync_mhd_handler): Remove debugging print.
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/config.c | 21 | ||||
-rw-r--r-- | src/nssync.c | 2 | ||||
-rw-r--r-- | src/nssync.h | 6 | ||||
-rw-r--r-- | src/runas.c | 179 | ||||
-rw-r--r-- | src/server.c | 7 |
6 files changed, 207 insertions, 9 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e2e3783..5706ab7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,12 +19,13 @@ nssync_SOURCES = \ bindcf.c\ config.c\ iflist.c\ nssync.c\ nssync.h\ output.c\ + runas.c\ sqlop.c if COND_MICROHTTPD nssync_SOURCES += server.c else nssync_SOURCES += noserver.c diff --git a/src/config.c b/src/config.c index 0481dfe..b8f062e 100644 --- a/src/config.c +++ b/src/config.c @@ -115,18 +115,18 @@ cb_sync(enum grecs_callback_command cmd, case grecs_callback_set_value: grecs_error(locus, 0, "invalid use of block statement"); } return err; } - -static struct grecs_keyword nssync_kw[] = { - { "sql-config-file", + +static struct grecs_keyword mysql_kw[] = { + { "config-file", "file", "Read MySQL configuration from <file>", grecs_type_string, GRECS_DFLT, &sql_config_file }, - { "sql-config-group", + { "config-group", "name", "Read the named group from the SQL configuration file", grecs_type_string, GRECS_DFLT, &sql_config_group }, { "host", "host", "Set SQL server hostname or IP address", grecs_type_string, GRECS_DFLT, &sql_host }, { "database", @@ -142,18 +142,28 @@ static struct grecs_keyword nssync_kw[] = { "file", "File name of the Certificate Authority (CA) certificate", grecs_type_string, GRECS_DFLT, &sql_cacert }, { "slave-status-file", NULL, "Check slave status and save it in the given file", grecs_type_string, GRECS_DFLT, &slave_status_file }, + { NULL } +}; +static struct grecs_keyword nssync_kw[] = { { "pidfile", "file", "At startup, check if <file> already exists and is " "owned by an existing process. Exit if so.", grecs_type_string, GRECS_DFLT, &pidfile }, + + { "user", + "name", "Run as this user", + grecs_type_string, GRECS_DFLT, &runas_user }, + { "group", + "name", "Run as this group", + grecs_type_string, GRECS_DFLT, &runas_group }, { "tempdir", NULL, "Name for the temporary directory (must exist)", grecs_type_string, GRECS_DFLT, &tempdir }, { "named-conf", @@ -181,12 +191,15 @@ static struct grecs_keyword nssync_kw[] = { { "sync", "tag: string", "Define a synchronization block", grecs_type_section, GRECS_DFLT, NULL, 0, cb_sync, NULL, sync_kw }, { "server", NULL, "Configure HTTP server", grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, server_kw }, + + { "mysql", NULL, "Configure MySQL access", + grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, mysql_kw }, { NULL } }; void config_help() diff --git a/src/nssync.c b/src/nssync.c index e18100c..41a03af 100644 --- a/src/nssync.c +++ b/src/nssync.c @@ -614,12 +614,14 @@ main(int argc, char **argv) config_parse(); if (check_ns) get_host_addresses(); check_pidfile(); + + runas(); if (server_mode) nssync_server(); else if (nssync(NULL)) { error("exiting due to errors"); exit(EX_UNAVAILABLE); diff --git a/src/nssync.h b/src/nssync.h index 063b33a..588f28e 100644 --- a/src/nssync.h +++ b/src/nssync.h @@ -35,19 +35,21 @@ extern int debug_level; extern char *config_file; extern char *slave_status_file; extern char *pidfile; extern char *tempdir; extern char *reload_command; extern int check_ns; +extern char *runas_user; +extern char *runas_group; extern struct json_value *changed_zones; extern struct json_value *error_list; extern struct grecs_sockaddr *server_addr; #ifndef DEFAULT_NSSYNC_ADDR -# define DEFAULT_NSSYNC_ADDR "0.0.0.0:8080" +# define DEFAULT_NSSYNC_ADDR "127.0.0.1:8080" #endif extern char *sql_config_file; extern char *sql_config_group; extern char *database_name; extern char *sql_host; @@ -83,12 +85,14 @@ void config_parse(void); void config_help(void); void verror(const char *fmt, va_list ap); void error(const char *fmt, ...) __PRINTFLIKE(1,2); void debug_printf(const char *fmt, ...) __PRINTFLIKE(1,2); +void runas(void); + void dlz_error(char const *zone, char const *fmt, ...); void dlz_success(char const *zone); void sql_connect(void); void sql_disconnect(void); int sql_do_query(const char *query, diff --git a/src/runas.c b/src/runas.c new file mode 100644 index 0000000..8d86468 --- /dev/null +++ b/src/runas.c @@ -0,0 +1,179 @@ +/* This file is part of NSsync + Copyright (C) 2017 Sergey Poznyakoff + + NSsync is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + NSsync is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with NSsync. If not, see <http://www.gnu.org/licenses/>. */ + +#include "nssync.h" +#include <pwd.h> +#include <grp.h> + +char *runas_user; +char *runas_group; + +#ifndef SIZE_T_MAX +# define SIZE_T_MAX ((size_t)-1) +#endif + +static void +addgid(gid_t **pgv, size_t *pgc, size_t *pgi, gid_t gid) +{ + gid_t *gv = *pgv; + size_t gc = *pgc; + size_t gi = *pgi; + + if (gi == gc) { + if (gc == 0) { + gc = 16; + gv = grecs_calloc(gc, sizeof(*gv)); + } else if (gc <= SIZE_T_MAX / 2 / sizeof(*gv)) { + gc *= 2; + gv = grecs_realloc(gv, gc * sizeof(*gv)); + } + } + gv[gi++] = gid; + *pgv = gv; + *pgc = gc; + *pgi = gi; +} + +static int +member(gid_t *gv, size_t gc, gid_t gid) +{ + size_t i; + + for (i = 0; i < gc; i++) + if (gv[i] == gid) + return 1; + return 0; +} + +static size_t +get_user_groups(const char *user, gid_t **pgv, size_t *pgc) +{ + struct group *gr; + size_t gi = 0; + + setgrent(); + while ((gr = getgrent())) { + char **p; + for (p = gr->gr_mem; *p; p++) + if (strcmp(*p, user) == 0) + addgid(pgv, pgc, &gi, gr->gr_gid); + } + endgrent(); + return gi; +} + +void +runas(void) +{ + struct passwd *pw; + struct group *gr; + uid_t uid; + gid_t gid; + char const *user_name; + gid_t *gv; + size_t gc, gn; + + if (!(runas_user || runas_group)) + return; + if (getuid() != 0) { + error("not root: can't switch to user privileges"); + exit(EX_USAGE); + } + + user_name = runas_user; + if (!user_name) { + pw = getpwuid(0); + user_name = "root"; + } else if (user_name[0] == '+') { + char *end; + unsigned long n; + + errno = 0; + n = strtoul(user_name + 1, &end, 10); + if (errno || *end) { + error("invalid user name %s", user_name); + exit(EX_USAGE); + } + + pw = getpwuid(n); + } else + pw = getpwnam(user_name); + + if (!pw) { + error("%s: no such user", runas_user); + exit(EX_USAGE); + } + user_name = pw->pw_name; + + uid = pw->pw_uid; + gid = pw->pw_gid; + + if (runas_group) { + if (runas_group[0] == '+') { + char *end; + unsigned long n; + + errno = 0; + n = strtoul(runas_group + 1, &end, 10); + if (errno || *end) { + error("invalid group name %s", runas_group); + exit(EX_USAGE); + } + + gr = getgrgid(n); + } else + gr = getgrnam(runas_group); + + if (!gr) { + error("%s: no such group", runas_user); + exit(EX_USAGE); + } + + gid = gr->gr_gid; + } + + gv = NULL; + gc = 0; + gn = get_user_groups(user_name, &gv, &gc); + if (!member(gv, gn, gid)) + addgid(&gv, &gc, &gn, gid); + + /* Reset group permissions */ + if (setgroups(gc, gv)) { + error("setgroups failed: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + free(gv); + + + if (gid) { + /* Switch to the user's gid. */ + if (setgid(gid)) { + error("setgid(%lu) failed: %s", + (unsigned long) gid, strerror(errno)); + exit(EX_UNAVAILABLE); + } + } + + /* Now reset uid */ + if (uid) { + if (setuid(uid)) { + error("setuid(%lu) failed: %s", + (unsigned long) uid, strerror(errno)); + exit(EX_UNAVAILABLE); + } + } +} diff --git a/src/server.c b/src/server.c index ae0d755..574c34e 100644 --- a/src/server.c +++ b/src/server.c @@ -177,13 +177,13 @@ struct nssync_resource { MHD_AccessHandlerCallback cbf; int (*predicate)(void); }; static struct nssync_resource nssync_resources[] = { #define S(s) #s, (sizeof(#s) - 1) - { S(sync), do_sync }, + { S(nssync), do_sync }, #undef S { NULL } }; static MHD_AccessHandlerCallback find_callback(const char **puri) @@ -216,14 +216,13 @@ nssync_mhd_handler(void *cls, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { MHD_AccessHandlerCallback cb; - error("METHOD: %s",method); - error("URL: %s",url); + cb = find_callback(&url); return cb(cls, conn, url, method, version, upload_data, upload_data_size, con_cls); } void @@ -231,13 +230,13 @@ nssync_server(void) { int fd; struct MHD_Daemon *mhd; if (!server_addr) { if (grecs_str_to_sockaddr(&server_addr, DEFAULT_NSSYNC_ADDR, - NULL, NULL)) + grecs_sockaddr_hints, NULL)) exit(EX_UNAVAILABLE); } fd = open_socket(server_addr); if (fd == -1) exit(EX_UNAVAILABLE); |