selinux/libsemanage/include/semanage/handle.h
Ondrej Mosnacek 286a679fad libsemanage: optionally rebuild policy when modules are changed externally
In Fedora/RHEL's selinux-policy package we ship a pre-built SELinux
policy store in the RPMs. When updating the main policy RPM, care must
be taken to rebuild the policy using `semodule -B` if there are any
other SELinux modules installed (whether shipped via another RPM or
manually installed locally).

However, this way of shipping/managing the policy creates complications
on systems, where system files are managed by rpm-ostree (such as Fedora
CoreOS or Red Hat CoreOS), where the "package update" process is more
sophisticated.

(Disclaimer: The following is written according to my current limited
understanding of rpm-ostree and may not be entirely accurate, but the
gist of it should match the reality.)

Basically, one can think of rpm-ostree as a kind of Git for system
files. The package content is provided on a "branch", where each
"commit" represents a set of package updates layered on top of the
previous commit (i.e. it is a rolling release with some defined
package content snapshots). The user can then maintain their own branch
with additional package updates/installations/... and "rebase" it on top
of the main branch as needed. On top of that, the user can also have
additional configuration files (or modifications to existing files) in
/etc, which represent an additional layer on top of the package content.

When updating the system (i.e. rebasing on a new "commit" of the "main
branch"), the files on the running system are not touched and the new
system state is prepared under a new root directory, which is chrooted
into on the next reboot.

When an rpm-ostree system is updated, there are three moments when the
SELinux module store needs to be rebuilt to ensure that all modules are
included in the binary policy:
1. When the local RPM installations are applied on top of the base
   system snapshot.
2. When local user configuartion is applied on top of that.
3. On system shutdown, to ensure that any changes in local configuration
   performed since (2.) are reflected in the final new system image.

Forcing a full rebuild at each step is not optimal and in many cases is
not necessary, as the user may not have any custom modules installed.

Thus, this patch extends libsemanage to compute a checksum of the
content of all enabled modules, which is stored in the store, and adds a
flag to the libsemanage handle that instructs it to check the module
content checksum against the one from the last successful transaction
and force a full policy rebuild if they don't match.

This will allow rpm-ostree systems to potentially reduce delays when
reconciling the module store when applying updates.

I wasn't able to measure any noticeable overhead of the hash
computation, which is now added for every transaction (both before and
after this change a full policy rebuild took about 7 seconds on my test
x86 VM). With the new option check_ext_changes enabled, rebuilding a
policy store with unchanged modules took only about 0.96 seconds.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
2022-02-18 11:08:39 -05:00

178 lines
7.3 KiB
C

/* Authors: Joshua Brindle <jbrindle@tresys.com>
* Jason Tang <jtang@tresys.com>
*
* Copyright (C) 2005 Tresys Technology, LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SEMANAGE_HANDLE_H_
#define _SEMANAGE_HANDLE_H_
#include <stdint.h>
/* All accesses with semanage are through a "semanage_handle". The
* handle may ultimately reference local config files,
* the binary policy file, a module store, or a policy management server.
*/
struct semanage_handle;
typedef struct semanage_handle semanage_handle_t;
/* Create and return a semanage handle.
The handle is initially in the disconnected state. */
extern semanage_handle_t *semanage_handle_create(void);
/* Deallocate all space associated with a semanage_handle_t, including
* the pointer itself. CAUTION: this function does not disconnect
* from the backend; be sure that a semanage_disconnect() was
* previously called if the handle was connected. */
extern void semanage_handle_destroy(semanage_handle_t *);
/* This is the type of connection to the store, for now only
* direct is supported */
enum semanage_connect_type {
SEMANAGE_CON_INVALID = 0, SEMANAGE_CON_DIRECT,
SEMANAGE_CON_POLSERV_LOCAL, SEMANAGE_CON_POLSERV_REMOTE
};
/* This function allows you to specify the store to connect to.
* It must be called after semanage_handle_create but before
* semanage_connect. The argument should be the full path to the store.
*/
extern void semanage_select_store(semanage_handle_t * handle, char *path,
enum semanage_connect_type storetype);
/* Just reload the policy */
extern int semanage_reload_policy(semanage_handle_t * handle);
/* set whether to reload the policy or not after a commit,
* 1 for yes (default), 0 for no */
extern void semanage_set_reload(semanage_handle_t * handle, int do_reload);
/* set whether to rebuild the policy on commit, even if no
* changes were performed.
* 1 for yes, 0 for no (default) */
extern void semanage_set_rebuild(semanage_handle_t * handle, int do_rebuild);
/* set whether to rebuild the policy on commit when potential changes
* to module files since last rebuild are detected,
* 1 for yes (default), 0 for no */
extern void semanage_set_check_ext_changes(semanage_handle_t * handle, int do_check);
/* Fills *compiler_path with the location of the hll compiler sh->conf->compiler_directory_path
* corresponding to lang_ext.
* Upon success returns 0, -1 on error. */
extern int semanage_get_hll_compiler_path(semanage_handle_t *sh, char *lang_ext, char **compiler_path);
/* create the store if it does not exist, this only has an effect on
* direct connections and must be called before semanage_connect
* 1 for yes, 0 for no (default) */
extern void semanage_set_create_store(semanage_handle_t * handle, int create_store);
/*Get whether or not dontaudits will be disabled upon commit */
extern int semanage_get_disable_dontaudit(semanage_handle_t * handle);
/* Set whether or not to disable dontaudits upon commit */
extern void semanage_set_disable_dontaudit(semanage_handle_t * handle, int disable_dontaudit);
/* Set whether or not to execute setfiles to check file contexts upon commit */
extern void semanage_set_check_contexts(semanage_handle_t * sh, int do_check_contexts);
/* Get the default priority. */
extern uint16_t semanage_get_default_priority(semanage_handle_t *sh);
/* Set the default priority. */
extern int semanage_set_default_priority(semanage_handle_t *sh, uint16_t priority);
/* Check whether policy is managed via libsemanage on this system.
* Must be called prior to trying to connect.
* Return 1 if policy is managed via libsemanage on this system,
* 0 if policy is not managed, or -1 on error.
*/
extern int semanage_is_managed(semanage_handle_t *);
/* "Connect" to a manager based on the configuration and
* associate the provided handle with the connection.
* If the connect fails then this function returns a negative value,
* else it returns zero.
*/
extern int semanage_connect(semanage_handle_t *);
/* Disconnect from the manager given by the handle. If already
* disconnected then this function does nothing. Return 0 if
* disconnected properly or already disconnected, negative value on
* error. */
extern int semanage_disconnect(semanage_handle_t *);
/* Attempt to obtain a transaction lock on the manager. If another
* process has the lock then this function may block, depending upon
* the timeout value in the handle.
*
* Note that if the semanage_handle has not yet obtained a transaction
* lock whenever a writer function is called, there will be an
* implicit call to this function. */
extern int semanage_begin_transaction(semanage_handle_t *);
/* Attempt to commit all changes since this transaction began. If the
* commit is successful then increment the "policy sequence number"
* and then release the transaction lock. Return that policy number
* afterwards, or -1 on error.
*/
extern int semanage_commit(semanage_handle_t *);
#define SEMANAGE_CAN_READ 1
#define SEMANAGE_CAN_WRITE 2
/* returns SEMANAGE_CAN_READ or SEMANAGE_CAN_WRITE if the store is readable
* or writable, respectively. <0 if an error occurred */
extern int semanage_access_check(semanage_handle_t * sh);
/* returns 0 if not connected, 1 if connected */
extern int semanage_is_connected(semanage_handle_t * sh);
/* returns 1 if policy is MLS, 0 otherwise. */
extern int semanage_mls_enabled(semanage_handle_t *sh);
/* Change to alternate semanage root path */
extern int semanage_set_root(const char *path);
/* Get the current semanage root path */
extern const char * semanage_root(void);
/* Get whether or not needless unused branch of tunables would be preserved */
extern int semanage_get_preserve_tunables(semanage_handle_t * handle);
/* Set whether or not to preserve the needless unused branch of tunables */
extern void semanage_set_preserve_tunables(semanage_handle_t * handle, int preserve_tunables);
/* Get the flag value for whether or not caching is ignored for compiled CIL modules from HLL files */
extern int semanage_get_ignore_module_cache(semanage_handle_t *handle);
/* Set semanage_handle flag for whether or not to ignore caching of compiled CIL modules from HLL files */
extern void semanage_set_ignore_module_cache(semanage_handle_t *handle, int ignore_module_cache);
/* set the store root path for semanage output files */
extern void semanage_set_store_root(semanage_handle_t *sh, const char *store_root);
/* META NOTES
*
* For all functions a non-negative number indicates success. For some
* functions a >=0 returned value is the "policy sequence number". This
* number keeps tracks of policy revisions and is used to detect if
* one semanage client has committed policy changes while another is
* still connected.
*/
#endif