|
1 /* |
|
2 ** openpkg.c -- OpenPKG Frontend |
|
3 ** Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/> |
|
4 ** |
|
5 ** This software is property of the OpenPKG GmbH, DE MUC HRB 160208. |
|
6 ** All rights reserved. Licenses which grant limited permission to use, |
|
7 ** copy, modify and distribute this software are available from the |
|
8 ** OpenPKG GmbH. |
|
9 ** |
|
10 ** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
|
11 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
12 ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
13 ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR |
|
14 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
15 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
16 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
17 ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
18 ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
19 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
20 ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
21 ** SUCH DAMAGE. |
|
22 */ |
|
23 |
|
24 /* program information */ |
|
25 #define LICENSE \ |
|
26 "Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>\n" \ |
|
27 "\n" \ |
|
28 "This software is property of the OpenPKG GmbH, DE MUC HRB 160208.\n" \ |
|
29 "All rights reserved. Licenses which grant limited permission to use,\n" \ |
|
30 "copy, modify and distribute this software are available from the\n" \ |
|
31 "OpenPKG GmbH.\n" \ |
|
32 "\n" \ |
|
33 "THIS SOFTWARE IS PROVIDED \"AS IS\" AND ANY EXPRESSED OR IMPLIED\n" \ |
|
34 "WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n" \ |
|
35 "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" \ |
|
36 "IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR\n" \ |
|
37 "CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" \ |
|
38 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" \ |
|
39 "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\n" \ |
|
40 "USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n" \ |
|
41 "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" \ |
|
42 "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n" \ |
|
43 "OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" \ |
|
44 "SUCH DAMAGE.\n" |
|
45 |
|
46 /* system includes */ |
|
47 #include <stdio.h> |
|
48 #include <stdlib.h> |
|
49 #include <stdarg.h> |
|
50 #include <string.h> |
|
51 #include <sys/types.h> |
|
52 #include <sys/param.h> |
|
53 #include <sys/stat.h> |
|
54 #include <pwd.h> |
|
55 #include <grp.h> |
|
56 #include <unistd.h> |
|
57 #include <errno.h> |
|
58 |
|
59 /* sanity check compilation */ |
|
60 #ifndef OPENPKG_PREFIX |
|
61 #error OpenPKG instance prefix not defined |
|
62 #endif |
|
63 #ifndef OPENPKG_SUSR |
|
64 #error OpenPKG super user not defined |
|
65 #endif |
|
66 #ifndef OPENPKG_MUSR |
|
67 #error OpenPKG management user not defined |
|
68 #endif |
|
69 |
|
70 /* platform specifics */ |
|
71 #if defined(OPENPKG_PLATFORM_FREEBSD) || \ |
|
72 defined(OPENPKG_PLATFORM_NETBSD) || \ |
|
73 defined(OPENPKG_PLATFORM_OPENBSD) || \ |
|
74 defined(OPENPKG_PLATFORM_SUNOS) || \ |
|
75 defined(OPENPKG_PLATFORM_LINUX) || \ |
|
76 defined(OPENPKG_PLATFORM_DARWIN) || \ |
|
77 defined(OPENPKG_PLATFORM_AIX) || \ |
|
78 defined(OPENPKG_PLATFORM_IRIX) || \ |
|
79 defined(OPENPKG_PLATFORM_HPUX) |
|
80 #define HAVE_INITGROUPS |
|
81 #endif |
|
82 |
|
83 /* global debug enable flag */ |
|
84 static int debug_enable = 0; |
|
85 |
|
86 /* helper function: emulate (still less portable) setenv(3) via (more portable) putenv(3) */ |
|
87 static int my_setenv(const char *name, const char *value, int overwrite) |
|
88 { |
|
89 char *pair; |
|
90 |
|
91 if (overwrite == 0 && getenv(name) != NULL) |
|
92 return 0; |
|
93 if ((pair = malloc(strlen(name) + 1 + strlen(value) + 1)) == NULL) |
|
94 return -1; |
|
95 strcpy(pair, name); |
|
96 strcat(pair, "="); |
|
97 strcat(pair, value); |
|
98 putenv(pair); |
|
99 return 0; |
|
100 } |
|
101 |
|
102 /* helper function for printing a warning message */ |
|
103 static void warn(const char *fmt, ...) |
|
104 { |
|
105 va_list ap; |
|
106 |
|
107 va_start(ap, fmt); |
|
108 fprintf(stderr, "openpkg:WARNING: "); |
|
109 vfprintf(stderr, fmt, ap); |
|
110 fprintf(stderr, "\n"); |
|
111 va_end(ap); |
|
112 return; |
|
113 } |
|
114 |
|
115 /* helper function for printing a debug message */ |
|
116 static void debug(const char *fmt, ...) |
|
117 { |
|
118 va_list ap; |
|
119 |
|
120 va_start(ap, fmt); |
|
121 if (debug_enable) { |
|
122 fprintf(stderr, "openpkg:DEBUG: "); |
|
123 vfprintf(stderr, fmt, ap); |
|
124 fprintf(stderr, "\n"); |
|
125 } |
|
126 va_end(ap); |
|
127 return; |
|
128 } |
|
129 |
|
130 /* helper function for printing a fatal message and exit */ |
|
131 static void fatal(const char *fmt, ...) |
|
132 { |
|
133 va_list ap; |
|
134 |
|
135 va_start(ap, fmt); |
|
136 fprintf(stderr, "openpkg:ERROR: "); |
|
137 vfprintf(stderr, fmt, ap); |
|
138 fprintf(stderr, "\n"); |
|
139 va_end(ap); |
|
140 exit(1); |
|
141 return; |
|
142 } |
|
143 |
|
144 /* adjust process privileges */ |
|
145 static void adjust_privileges(uid_t uid, gid_t gid, int login) |
|
146 { |
|
147 struct passwd *pw; |
|
148 char cwd[MAXPATHLEN]; |
|
149 |
|
150 /* optionally emulate a more complete login */ |
|
151 if (login) { |
|
152 /* determine information about user id */ |
|
153 if ((pw = getpwuid(uid)) == NULL) |
|
154 fatal("unable to resolve user id \"%d\": %s", uid, strerror(errno)); |
|
155 |
|
156 /* reset some essential environment variables */ |
|
157 my_setenv("LOGNAME", pw->pw_name, 1); |
|
158 my_setenv("USER", pw->pw_name, 1); |
|
159 my_setenv("SHELL", pw->pw_shell, 1); |
|
160 my_setenv("HOME", pw->pw_dir, 1); |
|
161 |
|
162 #ifdef HAVE_INITGROUPS |
|
163 /* initialize complete group access list */ |
|
164 if (initgroups(pw->pw_name, pw->pw_gid) == -1) |
|
165 fatal("failed to initialize access group list via initgroups(3): %s", strerror(errno)); |
|
166 #endif |
|
167 } |
|
168 |
|
169 /* switch to group id (first) */ |
|
170 if (setgid(gid) == -1) |
|
171 fatal("failed to set group id via setgid(2): %s", strerror(errno)); |
|
172 |
|
173 /* switch to user id (second) */ |
|
174 if (setuid(uid) == -1) |
|
175 fatal("failed to set user id via setuid(2): %s", strerror(errno)); |
|
176 |
|
177 /* in case the current working directory is NO LONGER accessible, |
|
178 try to switch to the home directory of the target identity to |
|
179 prevent failures in subsequently called programs (e.g. GNU bash) */ |
|
180 if (login) { |
|
181 if (getcwd(cwd, sizeof(cwd)) == NULL) { |
|
182 warn("current working directory is no longer accessible -- switching to \"%s\"", pw->pw_dir); |
|
183 if (chdir(pw->pw_dir) == -1) |
|
184 fatal("unable to chdir(2) to \"%s\": %s", pw->pw_dir, strerror(errno)); |
|
185 } |
|
186 } |
|
187 |
|
188 return; |
|
189 } |
|
190 |
|
191 /* check whether caller is an explictly configured management user */ |
|
192 static int check_whether_is_manager(uid_t my_uid, gid_t my_gid, uid_t m_uid, gid_t m_gid) |
|
193 { |
|
194 char buf[1024]; |
|
195 char *username; |
|
196 char *groupname; |
|
197 char *filename; |
|
198 struct stat sb; |
|
199 char *cp; |
|
200 FILE *fp; |
|
201 struct passwd *pw; |
|
202 struct group *gr; |
|
203 int i; |
|
204 int ok_uid; |
|
205 int ok_gid; |
|
206 int is_manager; |
|
207 |
|
208 is_manager = 0; |
|
209 |
|
210 /* path to the managers configuration file */ |
|
211 filename = OPENPKG_PREFIX "/etc/openpkg/managers"; |
|
212 |
|
213 /* check permissions of file */ |
|
214 if (stat(filename, &sb) == -1) { |
|
215 warn("unable to determine information about configuration" |
|
216 " file \"%s\": %s -- ignoring file", filename, strerror(errno)); |
|
217 return 0; |
|
218 } |
|
219 if (sb.st_uid != m_uid) { |
|
220 warn("invalid owner user id %d (expected %d) on configuration" |
|
221 " file \"%s\" -- ignoring file", sb.st_uid, m_uid, filename); |
|
222 return 0; |
|
223 } |
|
224 if (sb.st_gid != m_gid) { |
|
225 warn("invalid owner group id %d (expected %d) on configuration" |
|
226 " file \"%s\" -- ignoring file", sb.st_gid, m_gid, filename); |
|
227 return 0; |
|
228 } |
|
229 if (sb.st_mode != (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) { |
|
230 warn("invalid permissions on configuration" |
|
231 " file \"%s\" -- ignoring file", filename); |
|
232 return 0; |
|
233 } |
|
234 |
|
235 /* parse configuration file */ |
|
236 if ((fp = fopen(filename, "r")) == NULL) { |
|
237 warn("unable to open configuration file \"%s\": %s -- ignoring file", filename, strerror(errno)); |
|
238 return 0; |
|
239 } |
|
240 while ((cp = fgets(buf, sizeof(buf), fp)) != NULL) { |
|
241 /* parse entry as "<username>[:<groupname>]" where both |
|
242 <username> and <groupname> can be set and default to "*" |
|
243 to indicate an arbitrary user or group */ |
|
244 if ((i = strlen(buf)) == 0) { |
|
245 warn("unexpected empty buffer during parsing of configuration file \"%\"", filename); |
|
246 break; |
|
247 } |
|
248 if (i >= sizeof(buf)) { |
|
249 warn("unexpected buffer overflow during parsing of configuration file \"%\"", filename); |
|
250 break; |
|
251 } |
|
252 if (buf[i-1] != '\r' && buf[i-1] != '\n') { |
|
253 warn("unexpected non-newline-terminated line found during parsing of configuration file \"%\"", filename); |
|
254 break; |
|
255 } |
|
256 username = buf + strspn(buf, " \t"); |
|
257 cp = username + strcspn(username, " \t#\r\n"); |
|
258 *cp = '\0'; |
|
259 if (username[0] == '#' || username[0] == '\r' || username[0] == '\n' || username[0] == '\0') |
|
260 continue; |
|
261 groupname = "*"; |
|
262 if ((cp = strchr(username, ':')) != NULL) { |
|
263 *cp++ = '\0'; |
|
264 groupname = cp; |
|
265 } |
|
266 debug("parsing result: username=\"%s\" groupname=\"%s\"", username, groupname); |
|
267 |
|
268 /* check whether UID is ok */ |
|
269 ok_uid = 0; |
|
270 if (strcmp(username, "*") == 0) |
|
271 ok_uid = 1; |
|
272 else { |
|
273 if ((pw = getpwnam(username)) == NULL) { |
|
274 warn("invalid username \"%s\" in \"%s\"", username, filename); |
|
275 continue; |
|
276 } |
|
277 if (pw->pw_uid == my_uid) |
|
278 ok_uid = 1; |
|
279 } |
|
280 |
|
281 /* check whether GID is ok */ |
|
282 ok_gid = 0; |
|
283 if (strcmp(groupname, "*") == 0) |
|
284 ok_gid = 1; |
|
285 else { |
|
286 if ((gr = getgrnam(groupname)) == NULL) { |
|
287 warn("invalid groupname \"%s\" in \"%s\"", groupname, filename); |
|
288 continue; |
|
289 } |
|
290 if (gr->gr_gid == my_gid) |
|
291 ok_gid = 1; |
|
292 } |
|
293 |
|
294 /* if both UID and GID are ok, user is manager */ |
|
295 debug("matching: username ok = %s, groupname ok = %s", ok_uid ? "yes" : "no", ok_gid ? "yes" : "no"); |
|
296 if (ok_uid && ok_gid) { |
|
297 is_manager = 1; |
|
298 break; |
|
299 } |
|
300 } |
|
301 fclose(fp); |
|
302 return is_manager; |
|
303 } |
|
304 |
|
305 /* check whether command requires super-user privileges */ |
|
306 static int check_whether_require_superuser(int argc, char *argv[]) |
|
307 { |
|
308 int require_superuser; |
|
309 int i, j; |
|
310 |
|
311 require_superuser = 0; |
|
312 if (argc > 1 && strcmp(argv[1], "rpm") == 0) { |
|
313 for (i = 2; i < argc; i++) { |
|
314 if (strcmp(argv[i], "--") == 0) |
|
315 break; |
|
316 else if ( strcmp(argv[i], "--erase") == 0 |
|
317 || strcmp(argv[i], "--freshen") == 0 |
|
318 || strcmp(argv[i], "--install") == 0 |
|
319 || strcmp(argv[i], "--upgrade") == 0 |
|
320 || strcmp(argv[i], "--import") == 0 |
|
321 || strcmp(argv[i], "--initdb") == 0 |
|
322 || strcmp(argv[i], "--rebuilddb") == 0 |
|
323 || strcmp(argv[i], "--db-build") == 0 |
|
324 || strcmp(argv[i], "--db-rebuild") == 0 |
|
325 || strcmp(argv[i], "--db-cleanup") == 0 |
|
326 || strcmp(argv[i], "--db-fixate") == 0 |
|
327 || strcmp(argv[i], "--setperms") == 0 |
|
328 || strcmp(argv[i], "--setugids") == 0) { |
|
329 require_superuser = 1; |
|
330 break; |
|
331 } |
|
332 else if ( argv[i][0] == '-' |
|
333 && argv[i][1] != '-' |
|
334 && argv[i][1] != 'b' |
|
335 && argv[i][1] != 't' |
|
336 ) { |
|
337 for (j = 1; argv[i][j] != '\0'; j++) { |
|
338 if ( ( argv[i][j] == 'q' |
|
339 || argv[i][j] == 'K') |
|
340 && argv[i][j+1] != '\0') { |
|
341 j++; |
|
342 continue; |
|
343 } |
|
344 else if ( argv[i][j] == 'i' |
|
345 || argv[i][j] == 'U' |
|
346 || argv[i][j] == 'F' |
|
347 || argv[i][j] == 'V' |
|
348 || argv[i][j] == 'e') { |
|
349 require_superuser = 1; |
|
350 break; |
|
351 } |
|
352 } |
|
353 if (require_superuser) |
|
354 break; |
|
355 } |
|
356 } |
|
357 } |
|
358 else if (argc > 1 && strcmp(argv[1], "rc") == 0) { |
|
359 require_superuser = 1; |
|
360 for (i = 2; i < argc; i++) { |
|
361 if (strcmp(argv[i], "--") == 0) |
|
362 break; |
|
363 else if ( strcmp(argv[i], "-p") == 0 |
|
364 || strcmp(argv[i], "--print") == 0 |
|
365 || strcmp(argv[i], "-e") == 0 |
|
366 || strcmp(argv[i], "--eval") == 0 |
|
367 || strcmp(argv[i], "-q") == 0 |
|
368 || strcmp(argv[i], "--query") == 0 |
|
369 || strcmp(argv[i], "-c") == 0 |
|
370 || strcmp(argv[i], "--config") == 0) { |
|
371 require_superuser = 0; |
|
372 break; |
|
373 } |
|
374 } |
|
375 } |
|
376 return require_superuser; |
|
377 } |
|
378 |
|
379 /* main program */ |
|
380 int main(int argc, char **argv, char **envp) |
|
381 { |
|
382 int keep_original_privileges; |
|
383 int is_manager; |
|
384 int require_superuser; |
|
385 uid_t my_uid, my_euid; |
|
386 gid_t my_gid, my_egid; |
|
387 uid_t m_uid; |
|
388 gid_t m_gid; |
|
389 uid_t s_uid; |
|
390 gid_t s_gid; |
|
391 struct passwd *pw; |
|
392 struct group *gr; |
|
393 int dry_run; |
|
394 int license; |
|
395 |
|
396 /* parse command line options */ |
|
397 license = 0; |
|
398 dry_run = 0; |
|
399 keep_original_privileges = 0; |
|
400 if (argc <= 0) |
|
401 abort(); |
|
402 argv++; argc--; |
|
403 while (argc > 0) { |
|
404 if (argv[0][0] != '-') |
|
405 break; |
|
406 else if (strcmp(argv[0], "--") == 0) { |
|
407 argv++; argc--; |
|
408 break; |
|
409 } |
|
410 else if (strcmp(argv[0], "--license") == 0) { |
|
411 license = 1; |
|
412 argv++; argc--; |
|
413 continue; |
|
414 } |
|
415 else if (strcmp(argv[0], "--debug") == 0) { |
|
416 debug_enable = 1; |
|
417 argv++; argc--; |
|
418 continue; |
|
419 } |
|
420 else if (strcmp(argv[0], "--dry-run") == 0) { |
|
421 dry_run = 1; |
|
422 argv++; argc--; |
|
423 continue; |
|
424 } |
|
425 else if (strcmp(argv[0], "--keep-privileges") == 0) { |
|
426 keep_original_privileges = 1; |
|
427 argv++; argc--; |
|
428 continue; |
|
429 } |
|
430 break; |
|
431 } |
|
432 argv--; argc++; |
|
433 |
|
434 /* display stand-alone license information */ |
|
435 if (license) { |
|
436 fprintf(stdout, "%s", LICENSE); |
|
437 exit(0); |
|
438 } |
|
439 |
|
440 /* determine our current real and effective user/group ids */ |
|
441 my_uid = getuid(); |
|
442 my_gid = getgid(); |
|
443 my_euid = geteuid(); |
|
444 my_egid = getegid(); |
|
445 if ((pw = getpwuid(my_uid)) == NULL) |
|
446 fatal("unable to resolve current user id %d: %s", my_uid, strerror(errno)); |
|
447 if ((gr = getgrgid(my_gid)) == NULL) |
|
448 fatal("unable to resolve current group id %d: %s", my_gid, strerror(errno)); |
|
449 debug("current-user: usr=%s uid=%d euid=%d grp=%s gid=%d egid=%d", |
|
450 pw->pw_name, my_uid, my_euid, gr->gr_name, my_gid, my_egid); |
|
451 |
|
452 /* determine super user/group id */ |
|
453 if ((pw = getpwnam(OPENPKG_SUSR)) == NULL) |
|
454 fatal("unable to resolve OpenPKG superuser username \"%s\": %s", OPENPKG_SUSR, strerror(errno)); |
|
455 s_uid = pw->pw_uid; |
|
456 s_gid = pw->pw_gid; |
|
457 debug("super-user: s_usr=%s s_uid=%d s_gid=%d", OPENPKG_SUSR, s_uid, s_gid); |
|
458 |
|
459 /* determine management user/group id */ |
|
460 if ((pw = getpwnam(OPENPKG_MUSR)) == NULL) |
|
461 fatal("unable to resolve OpenPKG management username \"%s\": %s", OPENPKG_MUSR, strerror(errno)); |
|
462 m_uid = pw->pw_uid; |
|
463 m_gid = pw->pw_gid; |
|
464 debug("management-user: m_grp=%s m_uid=%d m_gid=%d", OPENPKG_MUSR, m_uid, m_gid); |
|
465 |
|
466 /* determine whether caller is explicitly configured as a management user */ |
|
467 is_manager = 0; |
|
468 if (!keep_original_privileges) |
|
469 is_manager = check_whether_is_manager(my_uid, my_gid, m_uid, m_gid); |
|
470 debug("current user is manager: %s", is_manager ? "yes" : "no"); |
|
471 |
|
472 /* determine whether command requires super-user privileges */ |
|
473 require_superuser = check_whether_require_superuser(argc, argv); |
|
474 debug("current command requires super user privileges: %s", require_superuser ? "yes" : "no"); |
|
475 |
|
476 /* adjust privileges according to determined information */ |
|
477 if (!keep_original_privileges && require_superuser && is_manager && my_euid == 0) { |
|
478 /* increase privileges to super user */ |
|
479 debug("increase privileges to super user"); |
|
480 adjust_privileges(s_uid, s_gid, 1); |
|
481 } |
|
482 else if (!keep_original_privileges && !require_superuser && is_manager && my_euid == 0) { |
|
483 /* decrease privileges to management user */ |
|
484 debug("decrease privileges to management user"); |
|
485 adjust_privileges(m_uid, m_gid, 1); |
|
486 } |
|
487 else /* keep_original_privileges || !is_manager */ { |
|
488 /* drop effective privileges for current user*/ |
|
489 debug("drop effective privileges for current user"); |
|
490 adjust_privileges(my_uid, my_gid, 0); |
|
491 } |
|
492 |
|
493 /* pass-through control to real Execution Frontend (shell script) */ |
|
494 argv[0] = OPENPKG_PREFIX "/lib/openpkg/openpkg"; |
|
495 debug("execute \"%s\"", argv[0]); |
|
496 if (!dry_run) { |
|
497 if (execve(argv[0], argv, envp) == -1) |
|
498 fatal("failed to execute \"%s\": %s", argv[0], strerror(errno)); |
|
499 /* NOT REACHED */ |
|
500 fatal("INTERNAL ERROR"); |
|
501 } |
|
502 return 0; |
|
503 } |
|
504 |