diff options
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/com_start.c | 1 | ||||
-rw-r--r-- | src/genrc.8 | 25 | ||||
-rw-r--r-- | src/genrc.c | 10 | ||||
-rw-r--r-- | src/genrc.h | 2 | ||||
-rw-r--r-- | src/runas.c | 191 | ||||
-rw-r--r-- | src/sentinel.c | 2 |
7 files changed, 232 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index f78a979..b565303 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
@@ -38,3 +38,4 @@ genrc_SOURCES = \ | |||
38 | match_regex.c\ | 38 | match_regex.c\ |
39 | sentinel.c | 39 | sentinel.c\ |
40 | runas.c | ||
40 | 41 | ||
diff --git a/src/com_start.c b/src/com_start.c index 65c1775..5744e39 100644 --- a/src/com_start.c +++ b/src/com_start.c | |||
@@ -114,2 +114,3 @@ com_start(void) | |||
114 | argv[2] = genrc_command; | 114 | argv[2] = genrc_command; |
115 | runas(); | ||
115 | execvp(SHELL, argv); | 116 | execvp(SHELL, argv); |
diff --git a/src/genrc.8 b/src/genrc.8 index f900639..00522ee 100644 --- a/src/genrc.8 +++ b/src/genrc.8 | |||
@@ -20,2 +20,3 @@ genrc \- generic system initialization script helper | |||
20 | .nh | 20 | .nh |
21 | .na | ||
21 | \fBgenrc\fR\ | 22 | \fBgenrc\fR\ |
@@ -25,6 +26,9 @@ genrc \- generic system initialization script helper | |||
25 | [\fB\-c\fR \fICOMMAND\fR]\ | 26 | [\fB\-c\fR \fICOMMAND\fR]\ |
27 | [\fB\-g\fR \fIGROUP\fR[,\fIGROUP\fR...]]\ | ||
26 | [\fB\-p\fR \fIPROGRAM\fR]\ | 28 | [\fB\-p\fR \fIPROGRAM\fR]\ |
27 | [\fB\-t\fR \fISECONDS\fR]\ | 29 | [\fB\-t\fR \fISECONDS\fR]\ |
30 | [\fB\-u\fR \fIUSER\fR]\ | ||
28 | [\fB\-\-command=\fICOMMAND\fR]\ | 31 | [\fB\-\-command=\fICOMMAND\fR]\ |
29 | [\fB\-\-create\-pidfile=\fIPIDFILE\fR]\ | 32 | [\fB\-\-create\-pidfile=\fIPIDFILE\fR]\ |
33 | [\fB\-\-group=\fIGROUP\fR[,\fIGROUP\fR...]]\ | ||
30 | [\fB\-\-help\fR]\ | 34 | [\fB\-\-help\fR]\ |
@@ -38,2 +42,3 @@ genrc \- generic system initialization script helper | |||
38 | [\fB\-\-timeout=\fISECONDS\fR]\ | 42 | [\fB\-\-timeout=\fISECONDS\fR]\ |
43 | [\fB\-\-user=\fIUSER\fR]\ | ||
39 | [\fB\-\-usage\fR]\ | 44 | [\fB\-\-usage\fR]\ |
@@ -167,2 +172,11 @@ Display a short help list. | |||
167 | .TP | 172 | .TP |
173 | \fB\-g\fR, \fB\-\-group=\fIGROUP\fR[,\fIGROUP\fR...] | ||
174 | Run program with this \fIGROUP\fR privileges. If the argument is a | ||
175 | list of groups, the first group becomes the principal, and the | ||
176 | rest of them supplementary groups. Each \fIGROUP\fR is either a group | ||
177 | name or a numeric group number prefixed with a plus sign. Whatever | ||
178 | notation is used, it must exist in the system group database. | ||
179 | |||
180 | See also the \fB\-\-user\fR option. | ||
181 | .TP | ||
168 | \fB\-\-no\-reload\fR | 182 | \fB\-\-no\-reload\fR |
@@ -194,2 +208,11 @@ Display a short usage summary. | |||
194 | .TP | 208 | .TP |
209 | \fB\-u\fR, \fB\-\-user=\fIUSER\fR | ||
210 | Run with this user privileges. The argument is either a login | ||
211 | name or a numeric UID prefixed with the plus sign. Whatever form is | ||
212 | used, it must correspond to a valid user from the system user | ||
213 | database. | ||
214 | |||
215 | Unless \fB\-\-group\fR option is also given, the primary and | ||
216 | supplementary groups of \fIUSER\fR will be used. | ||
217 | .TP | ||
195 | \fB\-\-version\fR | 218 | \fB\-\-version\fR |
@@ -284,2 +307,4 @@ Influential environment variables and corresponding options: | |||
284 | \fBGENRC_CREATE_PIDFILE=\fINAME\fR \fB\-\-create\-pidfile=\fINAME\fR | 307 | \fBGENRC_CREATE_PIDFILE=\fINAME\fR \fB\-\-create\-pidfile=\fINAME\fR |
308 | \fBGENRC_USER=\fINAME\fR \fB\-\-user=\fINAME\fR | ||
309 | \fBGENRC_GROUP=\fIGROUPS\fR \fB\-\-group=\fIGROUPS\fR | ||
285 | .fi | 310 | .fi |
diff --git a/src/genrc.c b/src/genrc.c index 92b0fac..d4904b6 100644 --- a/src/genrc.c +++ b/src/genrc.c | |||
@@ -45,5 +45,7 @@ struct option longopts[] = { | |||
45 | { "verbose", no_argument, 0, 'v' }, | 45 | { "verbose", no_argument, 0, 'v' }, |
46 | { "user", required_argument, 0, 'u' }, | ||
47 | { "group", required_argument, 0, 'g' }, | ||
46 | { NULL } | 48 | { NULL } |
47 | }; | 49 | }; |
48 | char shortopts[] = "c:hF:P:p:St:v"; | 50 | char shortopts[] = "c:hF:g:P:p:St:u:v"; |
49 | 51 | ||
@@ -388,2 +390,5 @@ main(int argc, char **argv) | |||
388 | break; | 390 | break; |
391 | case 'g': | ||
392 | setenv("GENRC_GROUP", optarg, 1); | ||
393 | break; | ||
389 | case OPT_CREATE_PIDFILE: | 394 | case OPT_CREATE_PIDFILE: |
@@ -406,2 +411,5 @@ main(int argc, char **argv) | |||
406 | break; | 411 | break; |
412 | case 'u': | ||
413 | setenv("GENRC_USER", optarg, 1); | ||
414 | break; | ||
407 | case 'v': | 415 | case 'v': |
diff --git a/src/genrc.h b/src/genrc.h index a6e81e8..9842016 100644 --- a/src/genrc.h +++ b/src/genrc.h | |||
@@ -33,2 +33,3 @@ void system_error(int ec, char const *fmt, ...); | |||
33 | #define xstrdup grecs_strdup | 33 | #define xstrdup grecs_strdup |
34 | void *x2nrealloc(void *p, size_t *pn, size_t s); | ||
34 | 35 | ||
@@ -63,2 +64,3 @@ int pid_is_running(pid_t pid); | |||
63 | 64 | ||
65 | void runas(void); | ||
64 | 66 | ||
diff --git a/src/runas.c b/src/runas.c new file mode 100644 index 0000000..9d2a6c1 --- /dev/null +++ b/src/runas.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* This file is part of genrc | ||
2 | Copyryght (C) 2018 Sergey Poznyakoff | ||
3 | License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> | ||
4 | This is free software: you are free to change and redistribute it. | ||
5 | There is NO WARRANTY, to the extent permitted by law. | ||
6 | */ | ||
7 | #include "genrc.h" | ||
8 | #include <pwd.h> | ||
9 | #include <grp.h> | ||
10 | |||
11 | static void | ||
12 | addgid(gid_t **pgv, size_t *pgc, size_t *pgi, gid_t gid) | ||
13 | { | ||
14 | gid_t *gv = *pgv; | ||
15 | size_t gc = *pgc; | ||
16 | size_t gi = *pgi; | ||
17 | |||
18 | if (gi == gc) | ||
19 | gv = x2nrealloc(gv, &gc, sizeof(gv[0])); | ||
20 | |||
21 | gv[gi++] = gid; | ||
22 | *pgv = gv; | ||
23 | *pgc = gc; | ||
24 | *pgi = gi; | ||
25 | } | ||
26 | |||
27 | static int | ||
28 | member(gid_t *gv, size_t gc, gid_t gid) | ||
29 | { | ||
30 | size_t i; | ||
31 | |||
32 | for (i = 0; i < gc; i++) | ||
33 | if (gv[i] == gid) | ||
34 | return 1; | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static size_t | ||
39 | get_user_groups(const char *user, gid_t **pgv, size_t *pgc) | ||
40 | { | ||
41 | struct group *gr; | ||
42 | size_t gi = 0; | ||
43 | |||
44 | setgrent(); | ||
45 | while ((gr = getgrent())) { | ||
46 | char **p; | ||
47 | for (p = gr->gr_mem; *p; p++) | ||
48 | if (strcmp(*p, user) == 0) | ||
49 | addgid(pgv, pgc, &gi, gr->gr_gid); | ||
50 | } | ||
51 | endgrent(); | ||
52 | return gi; | ||
53 | } | ||
54 | |||
55 | static gid_t | ||
56 | strtogid(char const *str) | ||
57 | { | ||
58 | struct group *gr; | ||
59 | |||
60 | if (str[0] == '+') { | ||
61 | char *end; | ||
62 | unsigned long n; | ||
63 | |||
64 | errno = 0; | ||
65 | n = strtoul(str, &end, 10); | ||
66 | if (errno || *end) { | ||
67 | genrc_error("invalid group name %s", str); | ||
68 | exit(1); | ||
69 | } | ||
70 | |||
71 | gr = getgrgid(n); | ||
72 | } else | ||
73 | gr = getgrnam(str); | ||
74 | |||
75 | if (!gr) { | ||
76 | genrc_error("%s: no such group", str); | ||
77 | exit(1); | ||
78 | } | ||
79 | |||
80 | return gr->gr_gid; | ||
81 | } | ||
82 | |||
83 | void | ||
84 | runas(void) | ||
85 | { | ||
86 | struct passwd *pw; | ||
87 | uid_t uid; | ||
88 | gid_t gid; | ||
89 | gid_t *gv; | ||
90 | size_t gc, gn; | ||
91 | char const *runas_user; | ||
92 | char const *runas_groups; | ||
93 | |||
94 | runas_user = getenv("GENRC_USER"); | ||
95 | runas_groups = getenv("GENRC_GROUP"); | ||
96 | |||
97 | if (!(runas_user || runas_groups)) | ||
98 | return; | ||
99 | if (getuid() != 0) { | ||
100 | genrc_error("not root: can't switch to user privileges"); | ||
101 | exit(1); | ||
102 | } | ||
103 | |||
104 | if (!runas_user) { | ||
105 | pw = getpwuid(0); | ||
106 | runas_user = "root"; | ||
107 | } else if (runas_user[0] == '+') { | ||
108 | char *end; | ||
109 | unsigned long n; | ||
110 | |||
111 | errno = 0; | ||
112 | n = strtoul(runas_user + 1, &end, 10); | ||
113 | if (errno || *end) { | ||
114 | genrc_error("invalid user name %s", runas_user); | ||
115 | exit(1); | ||
116 | } | ||
117 | |||
118 | pw = getpwuid(n); | ||
119 | } else | ||
120 | pw = getpwnam(runas_user); | ||
121 | |||
122 | if (!pw) { | ||
123 | genrc_error("%s: no such user", runas_user); | ||
124 | exit(1); | ||
125 | } | ||
126 | runas_user = pw->pw_name; | ||
127 | |||
128 | uid = pw->pw_uid; | ||
129 | gid = pw->pw_gid; | ||
130 | |||
131 | gv = NULL; | ||
132 | gc = 0; | ||
133 | |||
134 | if (runas_groups && runas_groups[0]) { | ||
135 | struct wordsplit ws; | ||
136 | size_t i; | ||
137 | |||
138 | ws.ws_delim = ","; | ||
139 | ws.ws_error = genrc_error; | ||
140 | if (wordsplit(runas_groups, &ws, | ||
141 | WRDSF_NOCMD | ||
142 | | WRDSF_NOVAR | ||
143 | | WRDSF_DELIM | ||