openpkg/openpkg.c

Mon, 28 Jan 2013 17:37:18 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Mon, 28 Jan 2013 17:37:18 +0100
changeset 758
a2c6460cfb16
permissions
-rw-r--r--

Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.

     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 */
    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"
    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>
    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
    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
    83 /* global debug enable flag */
    84 static int debug_enable = 0;
    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;
    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 }
   102 /* helper function for printing a warning message */
   103 static void warn(const char *fmt, ...)
   104 {
   105     va_list ap;
   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 }
   115 /* helper function for printing a debug message */
   116 static void debug(const char *fmt, ...)
   117 {
   118     va_list ap;
   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 }
   130 /* helper function for printing a fatal message and exit */
   131 static void fatal(const char *fmt, ...)
   132 {
   133     va_list ap;
   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 }
   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];
   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));
   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);
   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     }
   169     /* switch to group id (first) */
   170     if (setgid(gid) == -1)
   171         fatal("failed to set group id via setgid(2): %s", strerror(errno));
   173     /* switch to user id (second) */
   174     if (setuid(uid) == -1)
   175         fatal("failed to set user id via setuid(2): %s", strerror(errno));
   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     }
   188     return;
   189 }
   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;
   208     is_manager = 0;
   210     /* path to the managers configuration file */
   211     filename = OPENPKG_PREFIX "/etc/openpkg/managers";
   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     }
   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);
   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         }
   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         }
   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 }
   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;
   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 }
   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;
   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++;
   434     /* display stand-alone license information */
   435     if (license) {
   436         fprintf(stdout, "%s", LICENSE);
   437         exit(0);
   438     }
   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);
   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);
   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);
   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");
   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");
   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     }
   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 }

mercurial