1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/openpkg/openpkg.c Tue Jul 31 12:23:42 2012 +0200 1.3 @@ -0,0 +1,504 @@ 1.4 +/* 1.5 +** openpkg.c -- OpenPKG Frontend 1.6 +** Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/> 1.7 +** 1.8 +** This software is property of the OpenPKG GmbH, DE MUC HRB 160208. 1.9 +** All rights reserved. Licenses which grant limited permission to use, 1.10 +** copy, modify and distribute this software are available from the 1.11 +** OpenPKG GmbH. 1.12 +** 1.13 +** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 1.14 +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1.15 +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1.16 +** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR 1.17 +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.18 +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.19 +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 1.20 +** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1.21 +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 1.22 +** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 1.23 +** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1.24 +** SUCH DAMAGE. 1.25 +*/ 1.26 + 1.27 +/* program information */ 1.28 +#define LICENSE \ 1.29 + "Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>\n" \ 1.30 + "\n" \ 1.31 + "This software is property of the OpenPKG GmbH, DE MUC HRB 160208.\n" \ 1.32 + "All rights reserved. Licenses which grant limited permission to use,\n" \ 1.33 + "copy, modify and distribute this software are available from the\n" \ 1.34 + "OpenPKG GmbH.\n" \ 1.35 + "\n" \ 1.36 + "THIS SOFTWARE IS PROVIDED \"AS IS\" AND ANY EXPRESSED OR IMPLIED\n" \ 1.37 + "WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n" \ 1.38 + "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" \ 1.39 + "IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR\n" \ 1.40 + "CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" \ 1.41 + "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" \ 1.42 + "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\n" \ 1.43 + "USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n" \ 1.44 + "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" \ 1.45 + "OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n" \ 1.46 + "OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" \ 1.47 + "SUCH DAMAGE.\n" 1.48 + 1.49 +/* system includes */ 1.50 +#include <stdio.h> 1.51 +#include <stdlib.h> 1.52 +#include <stdarg.h> 1.53 +#include <string.h> 1.54 +#include <sys/types.h> 1.55 +#include <sys/param.h> 1.56 +#include <sys/stat.h> 1.57 +#include <pwd.h> 1.58 +#include <grp.h> 1.59 +#include <unistd.h> 1.60 +#include <errno.h> 1.61 + 1.62 +/* sanity check compilation */ 1.63 +#ifndef OPENPKG_PREFIX 1.64 +#error OpenPKG instance prefix not defined 1.65 +#endif 1.66 +#ifndef OPENPKG_SUSR 1.67 +#error OpenPKG super user not defined 1.68 +#endif 1.69 +#ifndef OPENPKG_MUSR 1.70 +#error OpenPKG management user not defined 1.71 +#endif 1.72 + 1.73 +/* platform specifics */ 1.74 +#if defined(OPENPKG_PLATFORM_FREEBSD) || \ 1.75 + defined(OPENPKG_PLATFORM_NETBSD) || \ 1.76 + defined(OPENPKG_PLATFORM_OPENBSD) || \ 1.77 + defined(OPENPKG_PLATFORM_SUNOS) || \ 1.78 + defined(OPENPKG_PLATFORM_LINUX) || \ 1.79 + defined(OPENPKG_PLATFORM_DARWIN) || \ 1.80 + defined(OPENPKG_PLATFORM_AIX) || \ 1.81 + defined(OPENPKG_PLATFORM_IRIX) || \ 1.82 + defined(OPENPKG_PLATFORM_HPUX) 1.83 +#define HAVE_INITGROUPS 1.84 +#endif 1.85 + 1.86 +/* global debug enable flag */ 1.87 +static int debug_enable = 0; 1.88 + 1.89 +/* helper function: emulate (still less portable) setenv(3) via (more portable) putenv(3) */ 1.90 +static int my_setenv(const char *name, const char *value, int overwrite) 1.91 +{ 1.92 + char *pair; 1.93 + 1.94 + if (overwrite == 0 && getenv(name) != NULL) 1.95 + return 0; 1.96 + if ((pair = malloc(strlen(name) + 1 + strlen(value) + 1)) == NULL) 1.97 + return -1; 1.98 + strcpy(pair, name); 1.99 + strcat(pair, "="); 1.100 + strcat(pair, value); 1.101 + putenv(pair); 1.102 + return 0; 1.103 +} 1.104 + 1.105 +/* helper function for printing a warning message */ 1.106 +static void warn(const char *fmt, ...) 1.107 +{ 1.108 + va_list ap; 1.109 + 1.110 + va_start(ap, fmt); 1.111 + fprintf(stderr, "openpkg:WARNING: "); 1.112 + vfprintf(stderr, fmt, ap); 1.113 + fprintf(stderr, "\n"); 1.114 + va_end(ap); 1.115 + return; 1.116 +} 1.117 + 1.118 +/* helper function for printing a debug message */ 1.119 +static void debug(const char *fmt, ...) 1.120 +{ 1.121 + va_list ap; 1.122 + 1.123 + va_start(ap, fmt); 1.124 + if (debug_enable) { 1.125 + fprintf(stderr, "openpkg:DEBUG: "); 1.126 + vfprintf(stderr, fmt, ap); 1.127 + fprintf(stderr, "\n"); 1.128 + } 1.129 + va_end(ap); 1.130 + return; 1.131 +} 1.132 + 1.133 +/* helper function for printing a fatal message and exit */ 1.134 +static void fatal(const char *fmt, ...) 1.135 +{ 1.136 + va_list ap; 1.137 + 1.138 + va_start(ap, fmt); 1.139 + fprintf(stderr, "openpkg:ERROR: "); 1.140 + vfprintf(stderr, fmt, ap); 1.141 + fprintf(stderr, "\n"); 1.142 + va_end(ap); 1.143 + exit(1); 1.144 + return; 1.145 +} 1.146 + 1.147 +/* adjust process privileges */ 1.148 +static void adjust_privileges(uid_t uid, gid_t gid, int login) 1.149 +{ 1.150 + struct passwd *pw; 1.151 + char cwd[MAXPATHLEN]; 1.152 + 1.153 + /* optionally emulate a more complete login */ 1.154 + if (login) { 1.155 + /* determine information about user id */ 1.156 + if ((pw = getpwuid(uid)) == NULL) 1.157 + fatal("unable to resolve user id \"%d\": %s", uid, strerror(errno)); 1.158 + 1.159 + /* reset some essential environment variables */ 1.160 + my_setenv("LOGNAME", pw->pw_name, 1); 1.161 + my_setenv("USER", pw->pw_name, 1); 1.162 + my_setenv("SHELL", pw->pw_shell, 1); 1.163 + my_setenv("HOME", pw->pw_dir, 1); 1.164 + 1.165 +#ifdef HAVE_INITGROUPS 1.166 + /* initialize complete group access list */ 1.167 + if (initgroups(pw->pw_name, pw->pw_gid) == -1) 1.168 + fatal("failed to initialize access group list via initgroups(3): %s", strerror(errno)); 1.169 +#endif 1.170 + } 1.171 + 1.172 + /* switch to group id (first) */ 1.173 + if (setgid(gid) == -1) 1.174 + fatal("failed to set group id via setgid(2): %s", strerror(errno)); 1.175 + 1.176 + /* switch to user id (second) */ 1.177 + if (setuid(uid) == -1) 1.178 + fatal("failed to set user id via setuid(2): %s", strerror(errno)); 1.179 + 1.180 + /* in case the current working directory is NO LONGER accessible, 1.181 + try to switch to the home directory of the target identity to 1.182 + prevent failures in subsequently called programs (e.g. GNU bash) */ 1.183 + if (login) { 1.184 + if (getcwd(cwd, sizeof(cwd)) == NULL) { 1.185 + warn("current working directory is no longer accessible -- switching to \"%s\"", pw->pw_dir); 1.186 + if (chdir(pw->pw_dir) == -1) 1.187 + fatal("unable to chdir(2) to \"%s\": %s", pw->pw_dir, strerror(errno)); 1.188 + } 1.189 + } 1.190 + 1.191 + return; 1.192 +} 1.193 + 1.194 +/* check whether caller is an explictly configured management user */ 1.195 +static int check_whether_is_manager(uid_t my_uid, gid_t my_gid, uid_t m_uid, gid_t m_gid) 1.196 +{ 1.197 + char buf[1024]; 1.198 + char *username; 1.199 + char *groupname; 1.200 + char *filename; 1.201 + struct stat sb; 1.202 + char *cp; 1.203 + FILE *fp; 1.204 + struct passwd *pw; 1.205 + struct group *gr; 1.206 + int i; 1.207 + int ok_uid; 1.208 + int ok_gid; 1.209 + int is_manager; 1.210 + 1.211 + is_manager = 0; 1.212 + 1.213 + /* path to the managers configuration file */ 1.214 + filename = OPENPKG_PREFIX "/etc/openpkg/managers"; 1.215 + 1.216 + /* check permissions of file */ 1.217 + if (stat(filename, &sb) == -1) { 1.218 + warn("unable to determine information about configuration" 1.219 + " file \"%s\": %s -- ignoring file", filename, strerror(errno)); 1.220 + return 0; 1.221 + } 1.222 + if (sb.st_uid != m_uid) { 1.223 + warn("invalid owner user id %d (expected %d) on configuration" 1.224 + " file \"%s\" -- ignoring file", sb.st_uid, m_uid, filename); 1.225 + return 0; 1.226 + } 1.227 + if (sb.st_gid != m_gid) { 1.228 + warn("invalid owner group id %d (expected %d) on configuration" 1.229 + " file \"%s\" -- ignoring file", sb.st_gid, m_gid, filename); 1.230 + return 0; 1.231 + } 1.232 + if (sb.st_mode != (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) { 1.233 + warn("invalid permissions on configuration" 1.234 + " file \"%s\" -- ignoring file", filename); 1.235 + return 0; 1.236 + } 1.237 + 1.238 + /* parse configuration file */ 1.239 + if ((fp = fopen(filename, "r")) == NULL) { 1.240 + warn("unable to open configuration file \"%s\": %s -- ignoring file", filename, strerror(errno)); 1.241 + return 0; 1.242 + } 1.243 + while ((cp = fgets(buf, sizeof(buf), fp)) != NULL) { 1.244 + /* parse entry as "<username>[:<groupname>]" where both 1.245 + <username> and <groupname> can be set and default to "*" 1.246 + to indicate an arbitrary user or group */ 1.247 + if ((i = strlen(buf)) == 0) { 1.248 + warn("unexpected empty buffer during parsing of configuration file \"%\"", filename); 1.249 + break; 1.250 + } 1.251 + if (i >= sizeof(buf)) { 1.252 + warn("unexpected buffer overflow during parsing of configuration file \"%\"", filename); 1.253 + break; 1.254 + } 1.255 + if (buf[i-1] != '\r' && buf[i-1] != '\n') { 1.256 + warn("unexpected non-newline-terminated line found during parsing of configuration file \"%\"", filename); 1.257 + break; 1.258 + } 1.259 + username = buf + strspn(buf, " \t"); 1.260 + cp = username + strcspn(username, " \t#\r\n"); 1.261 + *cp = '\0'; 1.262 + if (username[0] == '#' || username[0] == '\r' || username[0] == '\n' || username[0] == '\0') 1.263 + continue; 1.264 + groupname = "*"; 1.265 + if ((cp = strchr(username, ':')) != NULL) { 1.266 + *cp++ = '\0'; 1.267 + groupname = cp; 1.268 + } 1.269 + debug("parsing result: username=\"%s\" groupname=\"%s\"", username, groupname); 1.270 + 1.271 + /* check whether UID is ok */ 1.272 + ok_uid = 0; 1.273 + if (strcmp(username, "*") == 0) 1.274 + ok_uid = 1; 1.275 + else { 1.276 + if ((pw = getpwnam(username)) == NULL) { 1.277 + warn("invalid username \"%s\" in \"%s\"", username, filename); 1.278 + continue; 1.279 + } 1.280 + if (pw->pw_uid == my_uid) 1.281 + ok_uid = 1; 1.282 + } 1.283 + 1.284 + /* check whether GID is ok */ 1.285 + ok_gid = 0; 1.286 + if (strcmp(groupname, "*") == 0) 1.287 + ok_gid = 1; 1.288 + else { 1.289 + if ((gr = getgrnam(groupname)) == NULL) { 1.290 + warn("invalid groupname \"%s\" in \"%s\"", groupname, filename); 1.291 + continue; 1.292 + } 1.293 + if (gr->gr_gid == my_gid) 1.294 + ok_gid = 1; 1.295 + } 1.296 + 1.297 + /* if both UID and GID are ok, user is manager */ 1.298 + debug("matching: username ok = %s, groupname ok = %s", ok_uid ? "yes" : "no", ok_gid ? "yes" : "no"); 1.299 + if (ok_uid && ok_gid) { 1.300 + is_manager = 1; 1.301 + break; 1.302 + } 1.303 + } 1.304 + fclose(fp); 1.305 + return is_manager; 1.306 +} 1.307 + 1.308 +/* check whether command requires super-user privileges */ 1.309 +static int check_whether_require_superuser(int argc, char *argv[]) 1.310 +{ 1.311 + int require_superuser; 1.312 + int i, j; 1.313 + 1.314 + require_superuser = 0; 1.315 + if (argc > 1 && strcmp(argv[1], "rpm") == 0) { 1.316 + for (i = 2; i < argc; i++) { 1.317 + if (strcmp(argv[i], "--") == 0) 1.318 + break; 1.319 + else if ( strcmp(argv[i], "--erase") == 0 1.320 + || strcmp(argv[i], "--freshen") == 0 1.321 + || strcmp(argv[i], "--install") == 0 1.322 + || strcmp(argv[i], "--upgrade") == 0 1.323 + || strcmp(argv[i], "--import") == 0 1.324 + || strcmp(argv[i], "--initdb") == 0 1.325 + || strcmp(argv[i], "--rebuilddb") == 0 1.326 + || strcmp(argv[i], "--db-build") == 0 1.327 + || strcmp(argv[i], "--db-rebuild") == 0 1.328 + || strcmp(argv[i], "--db-cleanup") == 0 1.329 + || strcmp(argv[i], "--db-fixate") == 0 1.330 + || strcmp(argv[i], "--setperms") == 0 1.331 + || strcmp(argv[i], "--setugids") == 0) { 1.332 + require_superuser = 1; 1.333 + break; 1.334 + } 1.335 + else if ( argv[i][0] == '-' 1.336 + && argv[i][1] != '-' 1.337 + && argv[i][1] != 'b' 1.338 + && argv[i][1] != 't' 1.339 + ) { 1.340 + for (j = 1; argv[i][j] != '\0'; j++) { 1.341 + if ( ( argv[i][j] == 'q' 1.342 + || argv[i][j] == 'K') 1.343 + && argv[i][j+1] != '\0') { 1.344 + j++; 1.345 + continue; 1.346 + } 1.347 + else if ( argv[i][j] == 'i' 1.348 + || argv[i][j] == 'U' 1.349 + || argv[i][j] == 'F' 1.350 + || argv[i][j] == 'V' 1.351 + || argv[i][j] == 'e') { 1.352 + require_superuser = 1; 1.353 + break; 1.354 + } 1.355 + } 1.356 + if (require_superuser) 1.357 + break; 1.358 + } 1.359 + } 1.360 + } 1.361 + else if (argc > 1 && strcmp(argv[1], "rc") == 0) { 1.362 + require_superuser = 1; 1.363 + for (i = 2; i < argc; i++) { 1.364 + if (strcmp(argv[i], "--") == 0) 1.365 + break; 1.366 + else if ( strcmp(argv[i], "-p") == 0 1.367 + || strcmp(argv[i], "--print") == 0 1.368 + || strcmp(argv[i], "-e") == 0 1.369 + || strcmp(argv[i], "--eval") == 0 1.370 + || strcmp(argv[i], "-q") == 0 1.371 + || strcmp(argv[i], "--query") == 0 1.372 + || strcmp(argv[i], "-c") == 0 1.373 + || strcmp(argv[i], "--config") == 0) { 1.374 + require_superuser = 0; 1.375 + break; 1.376 + } 1.377 + } 1.378 + } 1.379 + return require_superuser; 1.380 +} 1.381 + 1.382 +/* main program */ 1.383 +int main(int argc, char **argv, char **envp) 1.384 +{ 1.385 + int keep_original_privileges; 1.386 + int is_manager; 1.387 + int require_superuser; 1.388 + uid_t my_uid, my_euid; 1.389 + gid_t my_gid, my_egid; 1.390 + uid_t m_uid; 1.391 + gid_t m_gid; 1.392 + uid_t s_uid; 1.393 + gid_t s_gid; 1.394 + struct passwd *pw; 1.395 + struct group *gr; 1.396 + int dry_run; 1.397 + int license; 1.398 + 1.399 + /* parse command line options */ 1.400 + license = 0; 1.401 + dry_run = 0; 1.402 + keep_original_privileges = 0; 1.403 + if (argc <= 0) 1.404 + abort(); 1.405 + argv++; argc--; 1.406 + while (argc > 0) { 1.407 + if (argv[0][0] != '-') 1.408 + break; 1.409 + else if (strcmp(argv[0], "--") == 0) { 1.410 + argv++; argc--; 1.411 + break; 1.412 + } 1.413 + else if (strcmp(argv[0], "--license") == 0) { 1.414 + license = 1; 1.415 + argv++; argc--; 1.416 + continue; 1.417 + } 1.418 + else if (strcmp(argv[0], "--debug") == 0) { 1.419 + debug_enable = 1; 1.420 + argv++; argc--; 1.421 + continue; 1.422 + } 1.423 + else if (strcmp(argv[0], "--dry-run") == 0) { 1.424 + dry_run = 1; 1.425 + argv++; argc--; 1.426 + continue; 1.427 + } 1.428 + else if (strcmp(argv[0], "--keep-privileges") == 0) { 1.429 + keep_original_privileges = 1; 1.430 + argv++; argc--; 1.431 + continue; 1.432 + } 1.433 + break; 1.434 + } 1.435 + argv--; argc++; 1.436 + 1.437 + /* display stand-alone license information */ 1.438 + if (license) { 1.439 + fprintf(stdout, "%s", LICENSE); 1.440 + exit(0); 1.441 + } 1.442 + 1.443 + /* determine our current real and effective user/group ids */ 1.444 + my_uid = getuid(); 1.445 + my_gid = getgid(); 1.446 + my_euid = geteuid(); 1.447 + my_egid = getegid(); 1.448 + if ((pw = getpwuid(my_uid)) == NULL) 1.449 + fatal("unable to resolve current user id %d: %s", my_uid, strerror(errno)); 1.450 + if ((gr = getgrgid(my_gid)) == NULL) 1.451 + fatal("unable to resolve current group id %d: %s", my_gid, strerror(errno)); 1.452 + debug("current-user: usr=%s uid=%d euid=%d grp=%s gid=%d egid=%d", 1.453 + pw->pw_name, my_uid, my_euid, gr->gr_name, my_gid, my_egid); 1.454 + 1.455 + /* determine super user/group id */ 1.456 + if ((pw = getpwnam(OPENPKG_SUSR)) == NULL) 1.457 + fatal("unable to resolve OpenPKG superuser username \"%s\": %s", OPENPKG_SUSR, strerror(errno)); 1.458 + s_uid = pw->pw_uid; 1.459 + s_gid = pw->pw_gid; 1.460 + debug("super-user: s_usr=%s s_uid=%d s_gid=%d", OPENPKG_SUSR, s_uid, s_gid); 1.461 + 1.462 + /* determine management user/group id */ 1.463 + if ((pw = getpwnam(OPENPKG_MUSR)) == NULL) 1.464 + fatal("unable to resolve OpenPKG management username \"%s\": %s", OPENPKG_MUSR, strerror(errno)); 1.465 + m_uid = pw->pw_uid; 1.466 + m_gid = pw->pw_gid; 1.467 + debug("management-user: m_grp=%s m_uid=%d m_gid=%d", OPENPKG_MUSR, m_uid, m_gid); 1.468 + 1.469 + /* determine whether caller is explicitly configured as a management user */ 1.470 + is_manager = 0; 1.471 + if (!keep_original_privileges) 1.472 + is_manager = check_whether_is_manager(my_uid, my_gid, m_uid, m_gid); 1.473 + debug("current user is manager: %s", is_manager ? "yes" : "no"); 1.474 + 1.475 + /* determine whether command requires super-user privileges */ 1.476 + require_superuser = check_whether_require_superuser(argc, argv); 1.477 + debug("current command requires super user privileges: %s", require_superuser ? "yes" : "no"); 1.478 + 1.479 + /* adjust privileges according to determined information */ 1.480 + if (!keep_original_privileges && require_superuser && is_manager && my_euid == 0) { 1.481 + /* increase privileges to super user */ 1.482 + debug("increase privileges to super user"); 1.483 + adjust_privileges(s_uid, s_gid, 1); 1.484 + } 1.485 + else if (!keep_original_privileges && !require_superuser && is_manager && my_euid == 0) { 1.486 + /* decrease privileges to management user */ 1.487 + debug("decrease privileges to management user"); 1.488 + adjust_privileges(m_uid, m_gid, 1); 1.489 + } 1.490 + else /* keep_original_privileges || !is_manager */ { 1.491 + /* drop effective privileges for current user*/ 1.492 + debug("drop effective privileges for current user"); 1.493 + adjust_privileges(my_uid, my_gid, 0); 1.494 + } 1.495 + 1.496 + /* pass-through control to real Execution Frontend (shell script) */ 1.497 + argv[0] = OPENPKG_PREFIX "/lib/openpkg/openpkg"; 1.498 + debug("execute \"%s\"", argv[0]); 1.499 + if (!dry_run) { 1.500 + if (execve(argv[0], argv, envp) == -1) 1.501 + fatal("failed to execute \"%s\": %s", argv[0], strerror(errno)); 1.502 + /* NOT REACHED */ 1.503 + fatal("INTERNAL ERROR"); 1.504 + } 1.505 + return 0; 1.506 +} 1.507 +