656 lines
14 KiB
C
656 lines
14 KiB
C
#include "plugin.h"
|
|
|
|
#include <errno.h>
|
|
#include <selinux/selinux.h>
|
|
#include <semanage/semanage.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <rpm/rpmpol.h>
|
|
#include <rpm/rpmfileutil.h>
|
|
#include <rpm/rpmmacro.h>
|
|
#include <rpm/rpmbase64.h>
|
|
|
|
#include "lib/rpmte_internal.h"
|
|
#include "lib/rpmts_internal.h" /* rpmtsSELabelFoo() */
|
|
|
|
rpmPluginHook PLUGIN_HOOKS = \
|
|
PLUGINHOOK_INIT | \
|
|
PLUGINHOOK_CLEANUP | \
|
|
PLUGINHOOK_OPENTE | \
|
|
PLUGINHOOK_COLL_POST_ADD | \
|
|
PLUGINHOOK_COLL_PRE_REMOVE;
|
|
|
|
typedef enum sepolAction {
|
|
SEPOL_ACTION_IGNORE,
|
|
SEPOL_ACTION_INSTALL,
|
|
SEPOL_ACTION_REMOVE
|
|
} sepolAction;
|
|
|
|
typedef struct sepol {
|
|
char *data; /*!< policy data */
|
|
char *name; /*!< policy names */
|
|
ARGV_t types; /*!< policy types */
|
|
uint32_t flags; /*!< policy flags */
|
|
sepolAction action; /*!< install/remove/ignore */
|
|
struct sepol *next; /*!< next in linked list */
|
|
} sepol;
|
|
|
|
typedef struct sepoltrans {
|
|
int execsemodule; /*!< 0 = use libsemanage to install policy; non-zero = use semodule */
|
|
semanage_handle_t *sh; /*!< handle to libsemanage, only used when execsemodule is zero */
|
|
char *semodulepath; /*!< path to semodule binary */
|
|
ARGV_t semodargs; /*!< argument list to pass to semodule, only used when execsemodule is non-zero */
|
|
ARGV_t filelist; /*!< list of temporary files that have been written to disk during the transaction */
|
|
int changes; /*!< number of changes made during the transaction */
|
|
} sepoltrans;
|
|
|
|
|
|
static char * name;
|
|
static rpmts ts;
|
|
|
|
static sepol * policiesHead;
|
|
static sepol * policiesTail;
|
|
|
|
|
|
static sepol *sepolNew(rpmte te);
|
|
static sepol *sepolFree(sepol * pol);
|
|
static int sepolHasType(const sepol * pol, const char *type);
|
|
|
|
static rpmRC sepolPreparePolicies(sepol * pols, const char *policytype);
|
|
static rpmRC sepolWritePolicy(const sepol * pol, char **path);
|
|
static rpmRC sepolLoadPolicies(const sepol * pols);
|
|
|
|
static sepoltrans *sepoltransNew(void);
|
|
static sepoltrans *sepoltransFree(sepoltrans * pt);
|
|
|
|
static rpmRC sepoltransInstall(sepoltrans * pt, const sepol * pol);
|
|
static rpmRC sepoltransRemove(sepoltrans * pt, const sepol * pol);
|
|
static rpmRC sepoltransCommit(sepoltrans * pt);
|
|
|
|
|
|
static sepol *sepolNew(rpmte te)
|
|
{
|
|
sepol *head = NULL;
|
|
sepol *ret = NULL;
|
|
sepolAction action;
|
|
Header h;
|
|
struct rpmtd_s policies, names, types, typesidx, flags;
|
|
int i, j;
|
|
int count;
|
|
|
|
rpmtdReset(&policies);
|
|
rpmtdReset(&names);
|
|
rpmtdReset(&types);
|
|
rpmtdReset(&typesidx);
|
|
rpmtdReset(&flags);
|
|
|
|
h = rpmteHeader(te);
|
|
if (!h) {
|
|
goto exit;
|
|
}
|
|
|
|
if (!headerIsEntry(h, RPMTAG_POLICIES)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (!headerGet(h, RPMTAG_POLICIES, &policies, HEADERGET_MINMEM)) {
|
|
goto exit;
|
|
}
|
|
|
|
count = rpmtdCount(&policies);
|
|
if (count <= 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (!headerGet(h, RPMTAG_POLICYNAMES, &names, HEADERGET_MINMEM)
|
|
|| rpmtdCount(&names) != count) {
|
|
goto exit;
|
|
}
|
|
|
|
if (!headerGet(h, RPMTAG_POLICYFLAGS, &flags, HEADERGET_MINMEM)
|
|
|| rpmtdCount(&flags) != count) {
|
|
goto exit;
|
|
}
|
|
|
|
if (!headerGet(h, RPMTAG_POLICYTYPES, &types, HEADERGET_MINMEM)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (!headerGet(h, RPMTAG_POLICYTYPESINDEXES, &typesidx, HEADERGET_MINMEM)
|
|
|| rpmtdCount(&types) != rpmtdCount(&typesidx)) {
|
|
goto exit;
|
|
}
|
|
|
|
action = (rpmteType(te) == TR_ADDED) ? SEPOL_ACTION_INSTALL : SEPOL_ACTION_REMOVE;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
sepol *pol = xcalloc(1, sizeof(*pol));
|
|
pol->next = head;
|
|
head = pol;
|
|
|
|
pol->data = xstrdup(rpmtdNextString(&policies));
|
|
pol->name = xstrdup(rpmtdNextString(&names));
|
|
pol->flags = *rpmtdNextUint32(&flags);
|
|
pol->action = action;
|
|
|
|
for (j = 0; j < rpmtdCount(&types); j++) {
|
|
uint32_t index = ((uint32_t *) typesidx.data)[j];
|
|
if (index < 0 || index >= count) {
|
|
goto exit;
|
|
}
|
|
if (index != i) {
|
|
continue;
|
|
}
|
|
argvAdd(&pol->types, rpmtdNextString(&types));
|
|
}
|
|
argvSort(pol->types, NULL);
|
|
}
|
|
|
|
ret = head;
|
|
|
|
exit:
|
|
headerFree(h);
|
|
|
|
rpmtdFreeData(&policies);
|
|
rpmtdFreeData(&names);
|
|
rpmtdFreeData(&types);
|
|
rpmtdFreeData(&typesidx);
|
|
rpmtdFreeData(&flags);
|
|
|
|
if (!ret) {
|
|
sepolFree(head);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static sepol *sepolFree(sepol * pol)
|
|
{
|
|
while (pol) {
|
|
sepol *next = pol->next;
|
|
|
|
pol->data = _free(pol->data);
|
|
pol->name = _free(pol->name);
|
|
pol->types = argvFree(pol->types);
|
|
pol->next = NULL;
|
|
_free(pol);
|
|
|
|
pol = next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int sepolHasType(const sepol * pol, const char *type)
|
|
{
|
|
if (!pol || !type) {
|
|
return 0;
|
|
}
|
|
|
|
return (argvSearch(pol->types, type, NULL) != NULL) ||
|
|
(argvSearch(pol->types, RPMPOL_TYPE_DEFAULT, NULL) != NULL);
|
|
}
|
|
|
|
static rpmRC sepolPreparePolicies(sepol * pols, const char *policytype)
|
|
{
|
|
sepol *pol;
|
|
rpmRC rc = RPMRC_OK;
|
|
|
|
for (pol = pols; pol; pol = pol->next) {
|
|
if (!sepolHasType(pol, policytype)) {
|
|
pol->action = SEPOL_ACTION_IGNORE;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepolWritePolicy(const sepol * pol, char **path)
|
|
{
|
|
char *tmppath = NULL;
|
|
FD_t fd = NULL;
|
|
char *policy = NULL;
|
|
size_t policylen;
|
|
rpmRC rc = RPMRC_FAIL;
|
|
|
|
if (rpmBase64Decode(pol->data, (void **) &policy, &policylen) != 0) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to decode policy for %s\n"),
|
|
pol->name);
|
|
goto exit;
|
|
}
|
|
|
|
fd = rpmMkTempFile(NULL, &tmppath);
|
|
if (fd == NULL || Ferror(fd)) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to create temporary file for %s: %s\n"),
|
|
pol->name, strerror(errno));
|
|
goto exit;
|
|
}
|
|
|
|
if (!Fwrite(policy, sizeof(*policy), policylen, fd)) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to write %s policy to file %s\n"),
|
|
pol->name, tmppath);
|
|
goto exit;
|
|
}
|
|
|
|
*path = tmppath;
|
|
rc = RPMRC_OK;
|
|
|
|
exit:
|
|
if (fd)
|
|
Fclose(fd);
|
|
_free(policy);
|
|
if (rc != RPMRC_OK)
|
|
_free(tmppath);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepolLoadPolicies(const sepol * pols)
|
|
{
|
|
const sepol *pol;
|
|
rpmRC rc = RPMRC_FAIL;
|
|
sepoltrans *pt = sepoltransNew();
|
|
|
|
if (pt == NULL)
|
|
goto exit;
|
|
|
|
for (pol = pols; pol; pol = pol->next) {
|
|
switch (pol->action) {
|
|
case SEPOL_ACTION_REMOVE:
|
|
rc = sepoltransRemove(pt, pol);
|
|
break;
|
|
case SEPOL_ACTION_INSTALL:
|
|
rc = sepoltransInstall(pt, pol);
|
|
break;
|
|
case SEPOL_ACTION_IGNORE:
|
|
default:
|
|
rc = RPMRC_OK;
|
|
break;
|
|
}
|
|
|
|
if (rc != RPMRC_OK)
|
|
goto exit;
|
|
}
|
|
|
|
rc = sepoltransCommit(pt);
|
|
|
|
exit:
|
|
sepoltransFree(pt);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static sepoltrans *sepoltransNew(void)
|
|
{
|
|
sepoltrans *pt = xcalloc(1, sizeof(*pt));
|
|
pt->semodulepath = rpmExpand("%{__semodule}", NULL);
|
|
pt->execsemodule = (!rpmChrootDone() && access(pt->semodulepath, X_OK) == 0);
|
|
pt->changes = 0;
|
|
|
|
if (pt->execsemodule) {
|
|
argvAdd(&pt->semodargs, "semodule");
|
|
} else {
|
|
pt->sh = semanage_handle_create();
|
|
if (!pt->sh) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to create semanage handle\n"));
|
|
goto err;
|
|
}
|
|
semanage_set_create_store(pt->sh, 1);
|
|
semanage_set_check_contexts(pt->sh, 0);
|
|
if (semanage_connect(pt->sh) < 0) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to connect to policy handler\n"));
|
|
goto err;
|
|
}
|
|
if (semanage_begin_transaction(pt->sh) < 0) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to begin policy transaction: %s\n"),
|
|
errno ? strerror(errno) : "");
|
|
goto err;
|
|
}
|
|
semanage_set_reload(pt->sh, !rpmChrootDone());
|
|
}
|
|
|
|
return pt;
|
|
|
|
err:
|
|
if (pt->sh) {
|
|
if (semanage_is_connected(pt->sh)) {
|
|
semanage_disconnect(pt->sh);
|
|
}
|
|
semanage_handle_destroy(pt->sh);
|
|
}
|
|
free(pt);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static sepoltrans *sepoltransFree(sepoltrans * pt)
|
|
{
|
|
ARGV_t file;
|
|
|
|
if (!pt) {
|
|
return NULL;
|
|
}
|
|
|
|
for (file = pt->filelist; file && *file; file++) {
|
|
if (unlink(*file) < 0) {
|
|
rpmlog(RPMLOG_WARNING, _("Failed to remove temporary policy file %s: %s\n"),
|
|
*file, strerror(errno));
|
|
}
|
|
}
|
|
argvFree(pt->filelist);
|
|
|
|
if (pt->execsemodule) {
|
|
argvFree(pt->semodargs);
|
|
} else {
|
|
semanage_disconnect(pt->sh);
|
|
semanage_handle_destroy(pt->sh);
|
|
}
|
|
|
|
free(pt->semodulepath);
|
|
memset(pt, 0, sizeof(*pt)); /* trash and burn */
|
|
|
|
free(pt);
|
|
return NULL;
|
|
}
|
|
|
|
static rpmRC sepoltransInstall(sepoltrans * pt, const sepol * pol)
|
|
{
|
|
rpmRC rc = RPMRC_OK;
|
|
char *path = NULL;
|
|
|
|
rc = sepolWritePolicy(pol, &path);
|
|
if (rc != RPMRC_OK) {
|
|
return rc;
|
|
}
|
|
argvAdd(&pt->filelist, path);
|
|
|
|
if (pt->execsemodule) {
|
|
const char *flag = (pol->flags & RPMPOL_FLAG_BASE) ? "-b" : "-i";
|
|
if (argvAdd(&pt->semodargs, flag) < 0 || argvAdd(&pt->semodargs, path) < 0) {
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
} else {
|
|
if (pol->flags & RPMPOL_FLAG_BASE) {
|
|
if (semanage_module_install_base_file(pt->sh, path) < 0) {
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
} else {
|
|
if (semanage_module_install_file(pt->sh, path) < 0) {
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rc != RPMRC_OK) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to install policy module: %s (%s)\n"),
|
|
pol->name, path);
|
|
} else {
|
|
pt->changes++;
|
|
}
|
|
|
|
_free(path);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepoltransRemove(sepoltrans * pt, const sepol * pol)
|
|
{
|
|
rpmRC rc = RPMRC_OK;
|
|
|
|
if (pol->flags & RPMPOL_FLAG_BASE) {
|
|
return RPMRC_FAIL;
|
|
}
|
|
|
|
if (pt->execsemodule) {
|
|
if (argvAdd(&pt->semodargs, "-r") < 0 || argvAdd(&pt->semodargs, pol->name) < 0) {
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
} else {
|
|
if (semanage_module_remove(pt->sh, (char *) pol->name) < 0) {
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
}
|
|
|
|
if (rc != RPMRC_OK) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to remove policy module: %s\n"),
|
|
pol->name);
|
|
} else {
|
|
pt->changes++;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepoltransCommit(sepoltrans * pt)
|
|
{
|
|
rpmRC rc = RPMRC_OK;
|
|
|
|
if (pt->changes == 0) {
|
|
return rc;
|
|
}
|
|
|
|
if (pt->execsemodule) {
|
|
int status;
|
|
pid_t pid = fork();
|
|
int fd;
|
|
|
|
switch (pid) {
|
|
case -1:
|
|
rpmlog(RPMLOG_ERR, _("Failed to fork process: %s\n"),
|
|
strerror(errno));
|
|
rc = RPMRC_FAIL;
|
|
break;
|
|
case 0:
|
|
fd = open("/dev/null", O_RDWR);
|
|
dup2(fd, STDIN_FILENO);
|
|
dup2(fd, STDOUT_FILENO);
|
|
dup2(fd, STDERR_FILENO);
|
|
execv(pt->semodulepath, pt->semodargs);
|
|
rpmlog(RPMLOG_ERR, _("Failed to execute %s: %s\n"),
|
|
pt->semodulepath, strerror(errno));
|
|
exit(1);
|
|
default:
|
|
waitpid(pid, &status, 0);
|
|
if (!WIFEXITED(status)) {
|
|
rpmlog(RPMLOG_ERR, _("%s terminated abnormally\n"),
|
|
pt->semodulepath);
|
|
rc = RPMRC_FAIL;
|
|
} else if (WEXITSTATUS(status)) {
|
|
rpmlog(RPMLOG_ERR, _("%s failed with exit code %i\n"),
|
|
pt->semodulepath, WEXITSTATUS(status));
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
}
|
|
} else {
|
|
if (semanage_commit(pt->sh) < 0) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to commit policy changes\n"));
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepolRelabelFiles(void)
|
|
{
|
|
rpmRC rc = RPMRC_OK;
|
|
pid_t pid;
|
|
int fd;
|
|
int status;
|
|
char *restoreconPath = rpmExpand("%{__restorecon}", NULL);
|
|
|
|
if (!restoreconPath) {
|
|
rpmlog(RPMLOG_ERR, _("Failed to expand restorecon path"));
|
|
return RPMRC_FAIL;
|
|
}
|
|
|
|
/* execute restorecon -R / */
|
|
pid = fork();
|
|
switch (pid) {
|
|
case -1:
|
|
rpmlog(RPMLOG_ERR, _("Failed to fork process: %s\n"),
|
|
strerror(errno));
|
|
rc = RPMRC_FAIL;
|
|
break;
|
|
case 0:
|
|
fd = open("/dev/null", O_RDWR);
|
|
dup2(fd, STDIN_FILENO);
|
|
dup2(fd, STDOUT_FILENO);
|
|
dup2(fd, STDERR_FILENO);
|
|
execl(restoreconPath, "restorecon", "-R", "/", NULL);
|
|
rpmlog(RPMLOG_ERR, _("Failed to execute %s: %s\n"), restoreconPath,
|
|
strerror(errno));
|
|
exit(1);
|
|
default:
|
|
waitpid(pid, &status, 0);
|
|
if (!WIFEXITED(status)) {
|
|
rpmlog(RPMLOG_ERR, _("%s terminated abnormally\n"),
|
|
restoreconPath);
|
|
rc = RPMRC_FAIL;
|
|
} else if (WEXITSTATUS(status)) {
|
|
rpmlog(RPMLOG_ERR, _("%s failed with exit code %i\n"),
|
|
restoreconPath, WEXITSTATUS(status));
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
}
|
|
|
|
_free(restoreconPath);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepolGo(void)
|
|
{
|
|
semanage_handle_t *sh;
|
|
int existingPolicy;
|
|
char *policytype = NULL;
|
|
rpmRC rc = RPMRC_FAIL;
|
|
|
|
static int performed = 0;
|
|
if (performed) {
|
|
return RPMRC_OK;
|
|
}
|
|
performed = 1;
|
|
|
|
if (rpmChrootIn()) {
|
|
goto exit;
|
|
}
|
|
|
|
if (selinux_getpolicytype(&policytype) < 0) {
|
|
goto exit;
|
|
}
|
|
|
|
sepolPreparePolicies(policiesHead, policytype);
|
|
|
|
/* determine if this is the first time installing policy */
|
|
sh = semanage_handle_create();
|
|
existingPolicy = (semanage_is_managed(sh) == 1);
|
|
semanage_handle_destroy(sh);
|
|
|
|
/* now load the policies */
|
|
rc = sepolLoadPolicies(policiesHead);
|
|
|
|
/* re-init selinux and re-read the files contexts, since things may have changed */
|
|
selinux_reset_config();
|
|
if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
|
|
if (rpmtsSELabelInit(ts, 0) == RPMRC_OK) {
|
|
/* if this was the first time installing policy, every package before
|
|
* policy was installed will be mislabeled (e.g. semodule). So, relabel
|
|
* the entire filesystem if this is the case */
|
|
if (!existingPolicy) {
|
|
if (sepolRelabelFiles() != RPMRC_OK) {
|
|
rpmlog(RPMLOG_WARNING, _("Failed to relabel filesystem. Files may be mislabeled\n"));
|
|
}
|
|
}
|
|
} else {
|
|
rpmlog(RPMLOG_WARNING, _("Failed to reload file contexts. Files may be mislabeled\n"));
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (rpmChrootOut()) {
|
|
rc = RPMRC_FAIL;
|
|
}
|
|
|
|
_free(policytype);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC sepolAddTE(rpmte te)
|
|
{
|
|
sepol *pol;
|
|
sepol *polTail;
|
|
|
|
if (!rpmteHasCollection(te, name)) {
|
|
return RPMRC_OK;
|
|
}
|
|
|
|
pol = sepolNew(te);
|
|
if (!pol) {
|
|
/* something's wrong with the policy information, either missing or
|
|
* corrupt. abort */
|
|
rpmlog(RPMLOG_ERR, _("Failed to extract policy from %s\n"),
|
|
rpmteNEVRA(te));
|
|
return RPMRC_FAIL;
|
|
}
|
|
|
|
/* find the tail of pol */
|
|
polTail = pol;
|
|
while (polTail->next) {
|
|
polTail = polTail->next;
|
|
}
|
|
|
|
/* add the new policy to the list */
|
|
if (!policiesHead) {
|
|
policiesHead = pol;
|
|
policiesTail = polTail;
|
|
} else {
|
|
if (rpmteType(te) == TR_ADDED) {
|
|
/* add to the end of the list */
|
|
policiesTail->next = pol;
|
|
policiesTail = polTail;
|
|
} else {
|
|
/* add to the beginning of the list */
|
|
polTail->next = policiesHead;
|
|
policiesHead = pol;
|
|
}
|
|
}
|
|
|
|
return RPMRC_OK;
|
|
}
|
|
|
|
|
|
|
|
rpmRC PLUGINHOOK_INIT_FUNC(rpmts _ts, const char *_name, const char *_opts)
|
|
{
|
|
ts = _ts;
|
|
name = strdup(_name);
|
|
policiesHead = policiesTail = NULL;
|
|
return RPMRC_OK;
|
|
}
|
|
|
|
rpmRC PLUGINHOOK_CLEANUP_FUNC(void)
|
|
{
|
|
_free(name);
|
|
ts = NULL;
|
|
policiesHead = policiesTail = sepolFree(policiesHead);
|
|
return RPMRC_OK;
|
|
}
|
|
|
|
rpmRC PLUGINHOOK_OPENTE_FUNC(rpmte te)
|
|
{
|
|
return sepolAddTE(te);
|
|
}
|
|
|
|
rpmRC PLUGINHOOK_COLL_POST_ADD_FUNC(void)
|
|
{
|
|
return sepolGo();
|
|
}
|
|
|
|
rpmRC PLUGINHOOK_COLL_PRE_REMOVE_FUNC(void)
|
|
{
|
|
return sepolGo();
|
|
}
|