diff options
Diffstat (limited to 'src/common/selinux.c')
-rw-r--r-- | src/common/selinux.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/common/selinux.c b/src/common/selinux.c new file mode 100644 index 0000000..b5d29d8 --- /dev/null +++ b/src/common/selinux.c @@ -0,0 +1,156 @@ +/* + * dpkg - main program for package management + * selinux.c - SELinux support + * + * Copyright © 2007-2015 Guillem Jover <guillem@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +#ifdef WITH_LIBSELINUX +#include <selinux/selinux.h> +#include <selinux/avc.h> +#include <selinux/label.h> +#endif + +#include "force.h" +#include "security-mac.h" + +#ifdef WITH_LIBSELINUX +static struct selabel_handle *sehandle; +#endif + +#ifdef WITH_LIBSELINUX +static int DPKG_ATTR_PRINTF(2) +log_callback(int type, const char *fmt, ...) +{ + va_list ap; + char *msg; + + switch (type) { + case SELINUX_ERROR: + case SELINUX_WARNING: + case SELINUX_AVC: + break; + default: + return 0; + } + + va_start(ap, fmt); + m_vasprintf(&msg, fmt, ap); + va_end(ap); + + warning("selinux: %s", msg); + free(msg); + + return 0; +} +#endif + +void +dpkg_selabel_load(void) +{ +#ifdef WITH_LIBSELINUX + static int selinux_enabled = -1; + + if (selinux_enabled < 0) { + int rc; + + /* Set selinux_enabled if it is not already set (singleton). */ + selinux_enabled = (in_force(FORCE_SECURITY_MAC) && + is_selinux_enabled() > 0); + if (!selinux_enabled) + return; + + /* Open the SELinux status notification channel, with fallback + * enabled for older kernels. */ + rc = selinux_status_open(1); + if (rc < 0) + ohshit(_("cannot open security status notification channel")); + + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) { + .func_log = log_callback, + }); + } else if (selinux_enabled && selinux_status_updated()) { + /* The SELinux policy got updated in the kernel, usually after + * upgrading the package shipping it, we need to reload. */ + selabel_close(sehandle); + } else { + /* SELinux is either disabled or it does not need a reload. */ + return; + } + + sehandle = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (sehandle == NULL && security_getenforce() == 1) + ohshite(_("cannot get security labeling handle")); +#endif +} + +void +dpkg_selabel_set_context(const char *matchpath, const char *path, mode_t mode) +{ +#ifdef WITH_LIBSELINUX + char *scontext = NULL; + int ret; + + /* If SELinux is not enabled just do nothing. */ + if (sehandle == NULL) + return; + + /* + * We use the _raw function variants here so that no translation + * happens from computer to human readable forms, to avoid issues + * when mcstransd has disappeared during the unpack process. + */ + + /* Do nothing if we can't figure out what the context is, or if it has + * no context; in which case the default context shall be applied. */ + ret = selabel_lookup_raw(sehandle, &scontext, matchpath, mode & S_IFMT); + if (ret == -1 || (ret == 0 && scontext == NULL)) + return; + + ret = lsetfilecon_raw(path, scontext); + if (ret < 0 && errno != ENOTSUP) + ohshite(_("cannot set security context for file object '%s'"), + path); + + freecon(scontext); +#endif /* WITH_LIBSELINUX */ +} + +void +dpkg_selabel_close(void) +{ +#ifdef WITH_LIBSELINUX + if (sehandle == NULL) + return; + + selinux_status_close(); + selabel_close(sehandle); + sehandle = NULL; +#endif +} |