Hi,
On Mon, 6 Jul 2009, Stephen Lawrence wrote:
RPM currently has support for security policies to be stored in an rpm
header but it doesn't currently do anything with the policies. We'd like
to get some feedback on a prototype implementation that adds support for
using those policies in an SELinux environment.
First of all thanks for looking into this!
First, a bit of background. SELinux policy is currently installed
through %post scripts. This presents several problems. First, this means
that policy for a given application may not be loaded at the time the
files are written to disk, preventing those files from being labeled
properly, because the symbols used to label files need to be in the
policy loaded into the kernel. Secondly, this means that if multiple
packages install policy, each of their %post scripts will reload the
policy, which is a very expensive operation. Consequently, policy is
generally kept in a single package to avoid this, despite containing
many application specific policy modules that would be more suited to be
included in their application package.
So, what we would like to do is to start including SELinux policy as
part of the rpm and have rpm install all policies together before files
start to hit the disk. To do this, we would like to use the already
supported %policy directive, which stores the policy in the archive
header.
We would then install the policy before pretrans. This policy load would
involve gathering all the policies to be installed from all packages,
writing them to a temporary location, and calling out to semodule to
install the SELinux policy modules.
Obviously I'm glossing over many implementation details that would need
to be worked out. The point of this email is strictly to get feedback on
our approach. Below is a patch that implements the beginnings of what I
describe above. Any and all feedback is appreciated.
Loading the policies at pre-trans stage is how it needs to be done, but
calling out to semodule is a no-go. It'd work for upgrades more or less,
but on initial installation (to an empty chroot) the pre-trans stage
happens in a complete void, there's just nothing there, not even /bin/sh.
It needs to be done through API calls, no way around it. On the surface it
doesn't look that bad, skipping over details like error handling,
rpmtsLoadPolicy() might be something like:
static int rpmtsLoadPolicies(rpmts ts)
{
int rc;
rpmte p;
semanage_handle_t *sh = NULL;
rpmtsi pi = rpmtsiInit(ts);
while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
/* If no pre/post-transaction script, then don't bother. */
if (!rpmteHavePolicies(p, stag))
continue;
/* only set up semanage transaction if we have policies */
if (sh == NULL) {
sh = semanage_handle_create();
semanage_connect(sh);
semanage_begin_transaction(sh);
}
if (rpmteOpen(p, ts, 0)) {
/* ... fish the policies from te header, b64decode ... */
semanage_module_install(sh, ...);
rpmteClose(p, ts, 0);
}
}
if (sh) {
semanage_commit(sh);
semanage_disconnect(sh);
semanage_handle_destroy(sh);
}
pi = rpmtsiFree(pi);
return rc;
}
...but I've a feeling there's more than one devil in the details. The
"base" policy seems to be a bit special as it even has a separate loader
function, which I suppose would have to be loaded first if one is present,
but how would rpm know what's a base policy and what's not? Are there
other order dependencies in the modules? I guess not but dunno.
Another open question is upgrade/remove semantics. Maybe it's sufficient
just to semanage_module_install() on install+upgrade, and
semanage_module_remove() on non-upgrade removal (direct erase or
obsoletion), all in the pre-trans stage. I'm just wondering could there be
cases where successful removal requires the package's policy to be loaded?
If so, the module removals would either have to be done at the middle of
transaction individually per package (costly) or do them all at post-trans
stage, which would seem more symmetric in a sense. And actually quite
similar to how %pre- and %posttrans happen, might even be
possible/reasonable to lump their processing to a single function (the
actual "do stuff" part obviously differs but the transaction set iteration
and header loading is the same)
And of course it must be possible to disable this functionality. Might be
sufficient to just hang it on RPMTRANS_FLAG_NOCONTEXTS, or can you think
of a case where one would want to load policies without actually touching
the contexts (or the other way around)?
- Panu -
_______________________________________________
Rpm-maint mailing list
Rpm-maint@lists.rpm.org
http://lists.rpm.org/mailman/listinfo/rpm-maint