Blame src/plugins/abrt-action-install-debuginfo-to-abrt-cache.c

Packit 8ea169
/*
Packit 8ea169
    Copyright (C) 2011  ABRT Team
Packit 8ea169
    Copyright (C) 2011  RedHat inc.
Packit 8ea169
Packit 8ea169
    This program is free software; you can redistribute it and/or modify
Packit 8ea169
    it under the terms of the GNU General Public License as published by
Packit 8ea169
    the Free Software Foundation; either version 2 of the License, or
Packit 8ea169
    (at your option) any later version.
Packit 8ea169
Packit 8ea169
    This program is distributed in the hope that it will be useful,
Packit 8ea169
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ea169
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8ea169
    GNU General Public License for more details.
Packit 8ea169
Packit 8ea169
    You should have received a copy of the GNU General Public License along
Packit 8ea169
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 8ea169
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 8ea169
*/
Packit 8ea169
Packit 8ea169
#include "libabrt.h"
Packit 8ea169
Packit 8ea169
#define EXECUTABLE "abrt-action-install-debuginfo"
Packit 8ea169
#define IGNORE_RESULT(func_call) do { if (func_call) /* nothing */; } while (0)
Packit 8ea169
Packit 8ea169
/* A binary wrapper is needed around python scripts if we want
Packit 8ea169
 * to run them in sgid/suid mode.
Packit 8ea169
 *
Packit 8ea169
 * This is such a wrapper.
Packit 8ea169
 */
Packit 8ea169
int main(int argc, char **argv)
Packit 8ea169
{
Packit 8ea169
    /* I18n */
Packit 8ea169
    setlocale(LC_ALL, "");
Packit 8ea169
#if ENABLE_NLS
Packit 8ea169
    bindtextdomain(PACKAGE, LOCALEDIR);
Packit 8ea169
    textdomain(PACKAGE);
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
    abrt_init(argv);
Packit 8ea169
Packit 8ea169
    /* Can't keep these strings/structs static: _() doesn't support that */
Packit 8ea169
    const char *program_usage_string = _(
Packit 8ea169
        "& [-y] [-i BUILD_IDS_FILE|-i -] [-e PATH[:PATH]...]\n"
Packit 8ea169
        "\t[-r REPO]\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "Installs debuginfo packages for all build-ids listed in BUILD_IDS_FILE to\n"
Packit 8ea169
        "ABRT system cache."
Packit 8ea169
    );
Packit 8ea169
Packit 8ea169
    enum {
Packit 8ea169
        OPT_v = 1 << 0,
Packit 8ea169
        OPT_y = 1 << 1,
Packit 8ea169
        OPT_i = 1 << 2,
Packit 8ea169
        OPT_e = 1 << 3,
Packit 8ea169
        OPT_r = 1 << 4,
Packit 8ea169
        OPT_s = 1 << 5,
Packit 8ea169
        OPT_R = 1 << 6,
Packit 8ea169
    };
Packit 8ea169
Packit 8ea169
    const char *build_ids = "build_ids";
Packit 8ea169
    const char *exact = NULL;
Packit 8ea169
    const char *repo = NULL;
Packit 8ea169
    const char *size_mb = NULL;
Packit 8ea169
    const char *releasever = NULL;
Packit 8ea169
Packit 8ea169
    struct options program_options[] = {
Packit 8ea169
        OPT__VERBOSE(&g_verbose),
Packit 8ea169
        OPT_BOOL  ('y', "yes",         NULL,                   _("Noninteractive, assume 'Yes' to all questions")),
Packit 8ea169
        OPT_STRING('i', "ids",   &build_ids, "BUILD_IDS_FILE", _("- means STDIN, default: build_ids")),
Packit 8ea169
        OPT_STRING('e', "exact",     &exact, "EXACT",          _("Download only specified files")),
Packit 8ea169
        OPT_STRING('r', "repo",       &repo, "REPO",           _("Pattern to use when searching for repos, default: *debug*")),
Packit 8ea169
        OPT_STRING('s', "size_mb", &size_mb, "SIZE_MB",        _("Ignored option")),
Packit 8ea169
        OPT_STRING('R', "releasever", &releasever, "RELEASEVER", _("OS release version")),
Packit 8ea169
        OPT_END()
Packit 8ea169
    };
Packit 8ea169
    const unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
Packit 8ea169
Packit 8ea169
    const gid_t egid = getegid();
Packit 8ea169
    const gid_t rgid = getgid();
Packit 8ea169
    const uid_t euid = geteuid();
Packit 8ea169
Packit 8ea169
    /* We need to open the build ids file under the caller's UID/GID to avoid
Packit 8ea169
     * information disclosures when reading files with changed UID.
Packit 8ea169
     * Unfortunately, we cannot replace STDIN with the new fd because ABRT uses
Packit 8ea169
     * STDIN to communicate with the caller. So, the following code opens a
Packit 8ea169
     * dummy file descriptor to the build ids file and passes the new fd's proc
Packit 8ea169
     * path to the wrapped program in the ids argument.
Packit 8ea169
     * The new fd remains opened, the OS will close it for us. */
Packit 8ea169
    char *build_ids_self_fd = NULL;
Packit 8ea169
    if (strcmp("-", build_ids) != 0)
Packit 8ea169
    {
Packit 8ea169
        if (setregid(egid, rgid) < 0)
Packit 8ea169
            perror_msg_and_die("setregid(egid, rgid)");
Packit 8ea169
Packit 8ea169
        const int build_ids_fd = open(build_ids, O_RDONLY);
Packit 8ea169
Packit 8ea169
        if (setregid(rgid, egid) < 0)
Packit 8ea169
            perror_msg_and_die("setregid(rgid, egid)");
Packit 8ea169
Packit 8ea169
        if (build_ids_fd < 0)
Packit 8ea169
            perror_msg_and_die("Failed to open file '%s'", build_ids);
Packit 8ea169
Packit 8ea169
        /* We are not going to free this memory. There is no place to do so. */
Packit 8ea169
        build_ids_self_fd = xasprintf("/proc/self/fd/%d", build_ids_fd);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char tmp_directory[] = LARGE_DATA_TMP_DIR"/abrt-tmp-debuginfo.XXXXXX";
Packit 8ea169
    if (mkdtemp(tmp_directory) == NULL)
Packit 8ea169
        perror_msg_and_die("Failed to create working directory");
Packit 8ea169
Packit 8ea169
    log_info("Created working directory: %s", tmp_directory);
Packit 8ea169
Packit 8ea169
    /* name, -v, --ids, -, -y, -e, EXACT, -r, REPO, -t, PATH, --releaseve, VER, --, NULL */
Packit 8ea169
    const char *args[15];
Packit 8ea169
    {
Packit 8ea169
        const char *verbs[] = { "", "-v", "-vv", "-vvv" };
Packit 8ea169
        unsigned i = 0;
Packit 8ea169
        args[i++] = EXECUTABLE;
Packit 8ea169
        args[i++] = "--ids";
Packit 8ea169
        args[i++] = (build_ids_self_fd != NULL) ? build_ids_self_fd : "-";
Packit 8ea169
        if (g_verbose > 0)
Packit 8ea169
            args[i++] = verbs[g_verbose <= 3 ? g_verbose : 3];
Packit 8ea169
        if ((opts & OPT_y))
Packit 8ea169
            args[i++] = "-y";
Packit 8ea169
        if ((opts & OPT_e))
Packit 8ea169
        {
Packit 8ea169
            args[i++] = "--exact";
Packit 8ea169
            args[i++] = exact;
Packit 8ea169
        }
Packit 8ea169
        if ((opts & OPT_r))
Packit 8ea169
        {
Packit 8ea169
            args[i++] = "--repo";
Packit 8ea169
            args[i++] = repo;
Packit 8ea169
        }
Packit 8ea169
        if ((opts & OPT_R))
Packit 8ea169
        {
Packit 8ea169
            args[i++] = "--releasever";
Packit 8ea169
            args[i++] = releasever;
Packit 8ea169
        }
Packit 8ea169
        args[i++] = "--tmpdir";
Packit 8ea169
        args[i++] = tmp_directory;
Packit 8ea169
        args[i++] = "--";
Packit 8ea169
        args[i] = NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* Switch real user/group to effective ones.
Packit 8ea169
     * Otherwise yum library gets confused - gets EPERM (why??).
Packit 8ea169
     */
Packit 8ea169
    /* do setregid only if we have to, to not upset selinux needlessly */
Packit 8ea169
    if (egid != rgid)
Packit 8ea169
    {
Packit 8ea169
        IGNORE_RESULT(setregid(egid, egid));
Packit 8ea169
        /* We are sgid'ed! */
Packit 8ea169
        /* Prevent malicious user from messing up with sgid'ed process: */
Packit 8ea169
#if 1
Packit 8ea169
// We forgot to sanitize PYTHONPATH. And who knows what else we forgot
Packit 8ea169
// (especially considering *future* new variables of this kind).
Packit 8ea169
// We switched to clearing entire environment instead:
Packit 8ea169
Packit 8ea169
        // However since we communicate through environment variables
Packit 8ea169
        // we have to keep a whitelist of variables to keep.
Packit 8ea169
        static const char *whitelist[] = {
Packit 8ea169
            "REPORT_CLIENT_SLAVE", //  Check if the app is being run as a slave
Packit 8ea169
            "LANG",
Packit 8ea169
        };
Packit 8ea169
        const size_t wlsize = sizeof(whitelist)/sizeof(char*);
Packit 8ea169
        char *setlist[sizeof(whitelist)/sizeof(char*)] = { 0 };
Packit 8ea169
        char *p = NULL;
Packit 8ea169
        for (size_t i = 0; i < wlsize; i++)
Packit 8ea169
            if ((p = getenv(whitelist[i])) != NULL)
Packit 8ea169
                setlist[i] = xstrdup(p);
Packit 8ea169
Packit 8ea169
        // Now we can clear the environment
Packit 8ea169
        clearenv();
Packit 8ea169
Packit 8ea169
        // And once again set whitelisted variables
Packit 8ea169
        for (size_t i = 0; i < wlsize; i++)
Packit 8ea169
            if (setlist[i] != NULL)
Packit 8ea169
            {
Packit 8ea169
                xsetenv(whitelist[i], setlist[i]);
Packit 8ea169
                free(setlist[i]);
Packit 8ea169
            }
Packit 8ea169
#else
Packit 8ea169
        /* Clear dangerous stuff from env */
Packit 8ea169
        static const char forbid[] =
Packit 8ea169
            "LD_LIBRARY_PATH" "\0"
Packit 8ea169
            "LD_PRELOAD" "\0"
Packit 8ea169
            "LD_TRACE_LOADED_OBJECTS" "\0"
Packit 8ea169
            "LD_BIND_NOW" "\0"
Packit 8ea169
            "LD_AOUT_LIBRARY_PATH" "\0"
Packit 8ea169
            "LD_AOUT_PRELOAD" "\0"
Packit 8ea169
            "LD_NOWARN" "\0"
Packit 8ea169
            "LD_KEEPDIR" "\0"
Packit 8ea169
        ;
Packit 8ea169
        const char *p = forbid;
Packit 8ea169
        do {
Packit 8ea169
            unsetenv(p);
Packit 8ea169
            p += strlen(p) + 1;
Packit 8ea169
        } while (*p);
Packit 8ea169
#endif
Packit 8ea169
        /* Set safe PATH */
Packit 8ea169
        // Adding configure --bindir and --sbindir to the PATH so that
Packit 8ea169
        // abrt-action-install-debuginfo doesn't fail when spawning
Packit 8ea169
        // abrt-action-trim-files
Packit 8ea169
        char path_env[] = "PATH=/usr/sbin:/sbin:/usr/bin:/bin:"BIN_DIR":"SBIN_DIR;
Packit 8ea169
        if (euid != 0)
Packit 8ea169
            strcpy(path_env, "PATH=/usr/bin:/bin:"BIN_DIR);
Packit 8ea169
        putenv(path_env);
Packit 8ea169
Packit 8ea169
        /* Use safe umask */
Packit 8ea169
        umask(0022);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    pid_t pid = fork();
Packit 8ea169
    if (pid < 0)
Packit 8ea169
        perror_msg_and_die("fork");
Packit 8ea169
Packit 8ea169
    if (pid == 0)
Packit 8ea169
    {
Packit 8ea169
        execvp(EXECUTABLE, (char **)args);
Packit 8ea169
        error_msg_and_die("Can't execute %s", EXECUTABLE);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    int status;
Packit 8ea169
    if (safe_waitpid(pid, &status, 0) < 0)
Packit 8ea169
        perror_msg_and_die("waitpid");
Packit 8ea169
Packit 8ea169
    if (rmdir(tmp_directory) >= 0)
Packit 8ea169
        log_info("Removed working directory: %s", tmp_directory);
Packit 8ea169
    else if (errno != ENOENT)
Packit 8ea169
        perror_msg("Failed to remove working directory");
Packit 8ea169
Packit 8ea169
    /* Normal execution should exit here. */
Packit 8ea169
    if (WIFEXITED(status))
Packit 8ea169
        return WEXITSTATUS(status);
Packit 8ea169
Packit 8ea169
    if (WIFSIGNALED(status))
Packit 8ea169
        error_msg_and_die("Child terminated with signal %d", WTERMSIG(status));
Packit 8ea169
Packit 8ea169
    error_msg_and_die("Child exit failed");
Packit 8ea169
}