|
|
@@ -0,0 +1,451 @@
|
|
|
+/*
|
|
|
+** openpkg -- OpenPKG Tool Chain
|
|
|
+** Copyright (c) 2000-2006 OpenPKG Foundation e.V. <http://openpkg.net/>
|
|
|
+** Copyright (c) 2000-2006 Ralf S. Engelschall <http://engelschall.com/>
|
|
|
+**
|
|
|
+** Permission to use, copy, modify, and distribute this software for
|
|
|
+** any purpose with or without fee is hereby granted, provided that
|
|
|
+** the above copyright notice and this permission notice appear in all
|
|
|
+** copies.
|
|
|
+**
|
|
|
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
|
+** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
+** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
|
|
|
+** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
|
+** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
+** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
+** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
|
+** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
+** SUCH DAMAGE.
|
|
|
+**
|
|
|
+** openpkg.c: Execution Wrapper (Language: C)
|
|
|
+*/
|
|
|
+
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdarg.h>
|
|
|
+#include <string.h>
|
|
|
+#include <sys/types.h>
|
|
|
+#include <sys/param.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <pwd.h>
|
|
|
+#include <grp.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <errno.h>
|
|
|
+
|
|
|
+/* sanity check compilation */
|
|
|
+#ifndef OPENPKG_PREFIX
|
|
|
+#error OpenPKG instance prefix not defined
|
|
|
+#endif
|
|
|
+#ifndef OPENPKG_SUSR
|
|
|
+#error OpenPKG super user not defined
|
|
|
+#endif
|
|
|
+#ifndef OPENPKG_MUSR
|
|
|
+#error OpenPKG management user not defined
|
|
|
+#endif
|
|
|
+
|
|
|
+/* platform specifics */
|
|
|
+#if defined(OPENPKG_PLATFORM_FREEBSD) || \
|
|
|
+ defined(OPENPKG_PLATFORM_NETBSD) || \
|
|
|
+ defined(OPENPKG_PLATFORM_OPENBSD) || \
|
|
|
+ defined(OPENPKG_PLATFORM_SUNOS) || \
|
|
|
+ defined(OPENPKG_PLATFORM_LINUX) || \
|
|
|
+ defined(OPENPKG_PLATFORM_DARWIN) || \
|
|
|
+ defined(OPENPKG_PLATFORM_AIX) || \
|
|
|
+ defined(OPENPKG_PLATFORM_IRIX) || \
|
|
|
+ defined(OPENPKG_PLATFORM_HPUX)
|
|
|
+#define HAVE_INITGROUPS
|
|
|
+#endif
|
|
|
+
|
|
|
+/* global debug enable flag */
|
|
|
+static int debug_enable = 0;
|
|
|
+
|
|
|
+/* helper function: emulate (still less portable) setenv(3) via (more portable) putenv(3) */
|
|
|
+static int my_setenv(const char *name, const char *value, int overwrite)
|
|
|
+{
|
|
|
+ char *pair;
|
|
|
+
|
|
|
+ if (overwrite == 0 && getenv(name) != NULL)
|
|
|
+ return 0;
|
|
|
+ if ((pair = malloc(strlen(name) + 1 + strlen(value) + 1)) == NULL)
|
|
|
+ return -1;
|
|
|
+ strcpy(pair, name);
|
|
|
+ strcat(pair, "=");
|
|
|
+ strcat(pair, value);
|
|
|
+ putenv(pair);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* helper function for printing a warning message */
|
|
|
+static void warn(const char *fmt, ...)
|
|
|
+{
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap, fmt);
|
|
|
+ fprintf(stderr, "openpkg:WARNING: ");
|
|
|
+ vfprintf(stderr, fmt, ap);
|
|
|
+ fprintf(stderr, "\n");
|
|
|
+ va_end(ap);
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/* helper function for printing a debug message */
|
|
|
+static void debug(const char *fmt, ...)
|
|
|
+{
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap, fmt);
|
|
|
+ if (debug_enable) {
|
|
|
+ fprintf(stderr, "openpkg:DEBUG: ");
|
|
|
+ vfprintf(stderr, fmt, ap);
|
|
|
+ fprintf(stderr, "\n");
|
|
|
+ }
|
|
|
+ va_end(ap);
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/* helper function for printing a fatal message and exit */
|
|
|
+static void fatal(const char *fmt, ...)
|
|
|
+{
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap, fmt);
|
|
|
+ fprintf(stderr, "openpkg:ERROR: ");
|
|
|
+ vfprintf(stderr, fmt, ap);
|
|
|
+ fprintf(stderr, "\n");
|
|
|
+ va_end(ap);
|
|
|
+ exit(1);
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/* adjust process privileges */
|
|
|
+static void adjust_privileges(uid_t uid, gid_t gid, int login)
|
|
|
+{
|
|
|
+ struct passwd *pw;
|
|
|
+
|
|
|
+ /* optionally emulate a more complete login */
|
|
|
+ if (login) {
|
|
|
+ /* determine information about user id */
|
|
|
+ if ((pw = getpwuid(uid)) == NULL)
|
|
|
+ fatal("unable to resolve user id \"%d\": %s\n", uid, strerror(errno));
|
|
|
+
|
|
|
+ /* reset some essential environment variables */
|
|
|
+ my_setenv("LOGNAME", pw->pw_name, 1);
|
|
|
+ my_setenv("USER", pw->pw_name, 1);
|
|
|
+ my_setenv("SHELL", pw->pw_shell, 1);
|
|
|
+ my_setenv("HOME", pw->pw_dir, 1);
|
|
|
+
|
|
|
+#ifdef HAVE_INITGROUPS
|
|
|
+ /* initialize complete group access list */
|
|
|
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
|
|
|
+ fatal("failed to initialize access group list via initgroups(3): %s", strerror(errno));
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ /* switch to group id (first) */
|
|
|
+ if (setgid(gid) == -1)
|
|
|
+ fatal("failed to set group id via setgid(2): %s", strerror(errno));
|
|
|
+
|
|
|
+ /* switch to user id (second) */
|
|
|
+ if (setuid(uid) == -1)
|
|
|
+ fatal("failed to set user id via setuid(2): %s", strerror(errno));
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/* check whether caller is an explictly configured management user */
|
|
|
+static int check_whether_is_manager(uid_t my_uid, gid_t my_gid, uid_t m_uid, gid_t m_gid)
|
|
|
+{
|
|
|
+ char buf[1024];
|
|
|
+ char *username;
|
|
|
+ char *groupname;
|
|
|
+ char *filename;
|
|
|
+ struct stat sb;
|
|
|
+ char *cp;
|
|
|
+ FILE *fp;
|
|
|
+ struct passwd *pw;
|
|
|
+ struct group *gr;
|
|
|
+ int i;
|
|
|
+ int ok_uid;
|
|
|
+ int ok_gid;
|
|
|
+ int is_manager;
|
|
|
+
|
|
|
+ is_manager = 0;
|
|
|
+
|
|
|
+ /* path to the managers configuration file */
|
|
|
+ filename = OPENPKG_PREFIX "/etc/openpkg/managers";
|
|
|
+
|
|
|
+ /* check permissions of file */
|
|
|
+ if (stat(filename, &sb) == -1) {
|
|
|
+ warn("unable to determine information about configuration"
|
|
|
+ " file \"%s\": %s -- ignoring file", filename, strerror(errno));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (sb.st_uid != m_uid) {
|
|
|
+ warn("invalid owner user id %d (expected %d) on configuration"
|
|
|
+ " file \"%s\" -- ignoring file", sb.st_uid, m_uid, filename);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (sb.st_gid != m_gid) {
|
|
|
+ warn("invalid owner group id %d (expected %d) on configuration"
|
|
|
+ " file \"%s\" -- ignoring file", sb.st_gid, m_gid, filename);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (sb.st_mode != (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) {
|
|
|
+ warn("invalid permissions on configuration"
|
|
|
+ " file \"%s\" -- ignoring file", filename);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* parse configuration file */
|
|
|
+ if ((fp = fopen(filename, "r")) == NULL) {
|
|
|
+ warn("unable to open configuration file \"%s\": %s -- ignoring file", filename, strerror(errno));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ while ((cp = fgets(buf, sizeof(buf), fp)) != NULL) {
|
|
|
+ /* parse entry as "<username>[:<groupname>]" where both
|
|
|
+ <username> and <groupname> can be set and default to "*"
|
|
|
+ to indicate an arbitrary user or group */
|
|
|
+ if ((i = strlen(buf)) == 0) {
|
|
|
+ warn("unexpected empty buffer during parsing of configuration file \"%\"", filename);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i >= sizeof(buf)) {
|
|
|
+ warn("unexpected buffer overflow during parsing of configuration file \"%\"", filename);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (buf[i-1] != '\r' && buf[i-1] != '\n') {
|
|
|
+ warn("unexpected non-newline-terminated line found during parsing of configuration file \"%\"", filename);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ username = buf + strspn(buf, " \t");
|
|
|
+ cp = username + strcspn(username, " \t#\r\n");
|
|
|
+ *cp = '\0';
|
|
|
+ if (username[0] == '#' || username[0] == '\r' || username[0] == '\n' || username[0] == '\0')
|
|
|
+ continue;
|
|
|
+ groupname = "*";
|
|
|
+ if ((cp = strchr(username, ':')) != NULL) {
|
|
|
+ *cp++ = '\0';
|
|
|
+ groupname = cp;
|
|
|
+ }
|
|
|
+ debug("parsing result: username=\"%s\" groupname=\"%s\"", username, groupname);
|
|
|
+
|
|
|
+ /* check whether UID is ok */
|
|
|
+ ok_uid = 0;
|
|
|
+ if (strcmp(username, "*") == 0)
|
|
|
+ ok_uid = 1;
|
|
|
+ else {
|
|
|
+ if ((pw = getpwnam(username)) == NULL) {
|
|
|
+ warn("invalid username \"%s\" in \"%s\"\n", username, filename);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (pw->pw_uid == my_uid)
|
|
|
+ ok_uid = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* check whether GID is ok */
|
|
|
+ ok_gid = 0;
|
|
|
+ if (strcmp(groupname, "*") == 0)
|
|
|
+ ok_gid = 1;
|
|
|
+ else {
|
|
|
+ if ((gr = getgrnam(groupname)) == NULL) {
|
|
|
+ warn("invalid groupname \"%s\" in \"%s\"\n", groupname, filename);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (gr->gr_gid == my_gid)
|
|
|
+ ok_gid = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if both UID and GID are ok, user is manager */
|
|
|
+ debug("matching: username ok = %s, groupname ok = %s", ok_uid ? "yes" : "no", ok_gid ? "yes" : "no");
|
|
|
+ if (ok_uid && ok_gid) {
|
|
|
+ is_manager = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fclose(fp);
|
|
|
+ return is_manager;
|
|
|
+}
|
|
|
+
|
|
|
+/* check whether command requires super-user privileges */
|
|
|
+static int check_whether_require_superuser(int argc, char *argv[])
|
|
|
+{
|
|
|
+ int require_superuser;
|
|
|
+ int i, j;
|
|
|
+
|
|
|
+ require_superuser = 0;
|
|
|
+ if (argc > 1 && strcmp(argv[1], "rpm") == 0) {
|
|
|
+ for (i = 2; i < argc; i++) {
|
|
|
+ if (strcmp(argv[i], "--") == 0)
|
|
|
+ break;
|
|
|
+ else if ( strcmp(argv[i], "--erase") == 0
|
|
|
+ || strcmp(argv[i], "--freshen") == 0
|
|
|
+ || strcmp(argv[i], "--install") == 0
|
|
|
+ || strcmp(argv[i], "--upgrade") == 0
|
|
|
+ || strcmp(argv[i], "--import") == 0
|
|
|
+ || strcmp(argv[i], "--initdb") == 0
|
|
|
+ || strcmp(argv[i], "--rebuilddb") == 0
|
|
|
+ || strcmp(argv[i], "--db-build") == 0
|
|
|
+ || strcmp(argv[i], "--db-rebuild") == 0
|
|
|
+ || strcmp(argv[i], "--db-cleanup") == 0
|
|
|
+ || strcmp(argv[i], "--db-fixate") == 0
|
|
|
+ || strcmp(argv[i], "--setperms") == 0
|
|
|
+ || strcmp(argv[i], "--setugids") == 0) {
|
|
|
+ require_superuser = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if (argv[i][0] == '-' && argv[i][1] != '-') {
|
|
|
+ for (j = 1; argv[i][j] != '\0'; j++) {
|
|
|
+ if ( ( argv[i][j] == 'q'
|
|
|
+ || argv[i][j] == 'V'
|
|
|
+ || argv[i][j] == 'K')
|
|
|
+ && argv[i][j+1] != '\0') {
|
|
|
+ j++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if ( argv[i][j] == 'i'
|
|
|
+ || argv[i][j] == 'U'
|
|
|
+ || argv[i][j] == 'F'
|
|
|
+ || argv[i][j] == 'e') {
|
|
|
+ require_superuser = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (require_superuser)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (argc > 1 && strcmp(argv[1], "rc") == 0) {
|
|
|
+ require_superuser = 1;
|
|
|
+ for (i = 2; i < argc; i++) {
|
|
|
+ if (strcmp(argv[i], "--") == 0)
|
|
|
+ break;
|
|
|
+ else if ( strcmp(argv[i], "-q") == 0
|
|
|
+ || strcmp(argv[i], "--query") == 0
|
|
|
+ || strcmp(argv[i], "-c") == 0
|
|
|
+ || strcmp(argv[i], "--config") == 0) {
|
|
|
+ require_superuser = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return require_superuser;
|
|
|
+}
|
|
|
+
|
|
|
+/* main program */
|
|
|
+int main(int argc, char **argv, char **envp)
|
|
|
+{
|
|
|
+ int keep_original_privileges;
|
|
|
+ int is_manager;
|
|
|
+ int require_superuser;
|
|
|
+ uid_t my_uid, my_euid;
|
|
|
+ gid_t my_gid, my_egid;
|
|
|
+ uid_t m_uid;
|
|
|
+ gid_t m_gid;
|
|
|
+ uid_t s_uid;
|
|
|
+ gid_t s_gid;
|
|
|
+ struct passwd *pw;
|
|
|
+ struct group *gr;
|
|
|
+ int dry_run;
|
|
|
+
|
|
|
+ /* parse command line options */
|
|
|
+ dry_run = 0;
|
|
|
+ keep_original_privileges = 0;
|
|
|
+ if (argc <= 0)
|
|
|
+ abort();
|
|
|
+ argv++; argc--;
|
|
|
+ while (argc > 0) {
|
|
|
+ if (argv[0][0] != '-')
|
|
|
+ break;
|
|
|
+ else if (strcmp(argv[0], "--") == 0) {
|
|
|
+ argv++; argc--;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if (strcmp(argv[0], "--debug") == 0) {
|
|
|
+ debug_enable = 1;
|
|
|
+ argv++; argc--;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (strcmp(argv[0], "--dry-run") == 0) {
|
|
|
+ dry_run = 1;
|
|
|
+ argv++; argc--;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (strcmp(argv[0], "--keep-privileges") == 0) {
|
|
|
+ keep_original_privileges = 1;
|
|
|
+ argv++; argc--;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ argv--; argc++;
|
|
|
+
|
|
|
+ /* determine our current real and effective user/group ids */
|
|
|
+ my_uid = getuid();
|
|
|
+ my_gid = getgid();
|
|
|
+ my_euid = geteuid();
|
|
|
+ my_egid = getegid();
|
|
|
+ if ((pw = getpwuid(my_uid)) == NULL)
|
|
|
+ fatal("unable to resolve current user id %d: %s\n", my_uid, strerror(errno));
|
|
|
+ if ((gr = getgrgid(my_gid)) == NULL)
|
|
|
+ fatal("unable to resolve current user id %d: %s\n", my_uid, strerror(errno));
|
|
|
+ debug("current-user: usr=%s uid=%d euid=%d grp=%s gid=%d egid=%d",
|
|
|
+ pw->pw_name, my_uid, my_euid, gr->gr_name, my_gid, my_egid);
|
|
|
+
|
|
|
+ /* determine super user/group id */
|
|
|
+ if ((pw = getpwnam(OPENPKG_SUSR)) == NULL)
|
|
|
+ fatal("unable to resolve OpenPKG superuser username \"%s\": %s\n", OPENPKG_SUSR, strerror(errno));
|
|
|
+ s_uid = pw->pw_uid;
|
|
|
+ s_gid = pw->pw_gid;
|
|
|
+ debug("super-user: s_usr=%s s_uid=%d s_gid=%d", OPENPKG_SUSR, s_uid, s_gid);
|
|
|
+
|
|
|
+ /* determine management user/group id */
|
|
|
+ if ((pw = getpwnam(OPENPKG_MUSR)) == NULL)
|
|
|
+ fatal("unable to resolve OpenPKG management username \"%s\": %s\n", OPENPKG_MUSR, strerror(errno));
|
|
|
+ m_uid = pw->pw_uid;
|
|
|
+ m_gid = pw->pw_gid;
|
|
|
+ debug("management-user: m_grp=%s m_uid=%d m_gid=%d", OPENPKG_MUSR, m_uid, m_gid);
|
|
|
+
|
|
|
+ /* determine whether caller is explicitly configured as a management user */
|
|
|
+ is_manager = 0;
|
|
|
+ if (!keep_original_privileges)
|
|
|
+ is_manager = check_whether_is_manager(my_uid, my_gid, m_uid, m_gid);
|
|
|
+ debug("current user is manager: %s", is_manager ? "yes" : "no");
|
|
|
+
|
|
|
+ /* determine whether command requires super-user privileges */
|
|
|
+ require_superuser = check_whether_require_superuser(argc, argv);
|
|
|
+ debug("current command requires super user privileges: %s", require_superuser ? "yes" : "no");
|
|
|
+
|
|
|
+ /* adjust privileges according to determined information */
|
|
|
+ if (!keep_original_privileges && require_superuser && is_manager && my_euid == 0) {
|
|
|
+ /* increase privileges to super user */
|
|
|
+ debug("increase privileges to super user");
|
|
|
+ adjust_privileges(s_uid, s_gid, 1);
|
|
|
+ }
|
|
|
+ else if (!keep_original_privileges && !require_superuser && is_manager && my_euid == 0) {
|
|
|
+ /* decrease privileges to management user */
|
|
|
+ debug("decrease privileges to management user");
|
|
|
+ adjust_privileges(m_uid, m_gid, 1);
|
|
|
+ }
|
|
|
+ else /* keep_original_privileges || !is_manager */ {
|
|
|
+ /* drop effective privileges for current user*/
|
|
|
+ debug("drop effective privileges for current user");
|
|
|
+ adjust_privileges(my_uid, my_gid, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* pass-through control to real Execution Frontend (shell script) */
|
|
|
+ argv[0] = OPENPKG_PREFIX "/lib/openpkg/openpkg";
|
|
|
+ debug("execute \"%s\"", argv[0]);
|
|
|
+ if (!dry_run) {
|
|
|
+ if (execve(argv[0], argv, envp) == -1)
|
|
|
+ fatal("failed to execute \"%s\": %s", argv[0], strerror(errno));
|
|
|
+ /* NOT REACHED */
|
|
|
+ fatal("INTERNAL ERROR");
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|