|
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 |
}
|