LCOV - code coverage report
Current view: top level - basic - selinux-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 103 256 40.2 %
Date: 2019-08-22 15:41:25 Functions: 14 19 73.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <malloc.h>
       6             : #include <stddef.h>
       7             : #include <string.h>
       8             : #include <sys/stat.h>
       9             : #include <sys/time.h>
      10             : #include <sys/types.h>
      11             : #include <sys/un.h>
      12             : #include <syslog.h>
      13             : 
      14             : #if HAVE_SELINUX
      15             : #include <selinux/context.h>
      16             : #include <selinux/label.h>
      17             : #include <selinux/selinux.h>
      18             : #endif
      19             : 
      20             : #include "alloc-util.h"
      21             : #include "errno-util.h"
      22             : #include "fd-util.h"
      23             : #include "log.h"
      24             : #include "macro.h"
      25             : #include "path-util.h"
      26             : #include "selinux-util.h"
      27             : #include "stdio-util.h"
      28             : #include "time-util.h"
      29             : 
      30             : #if HAVE_SELINUX
      31          62 : DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
      32           2 : DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
      33             : 
      34             : #define _cleanup_freecon_ _cleanup_(freeconp)
      35             : #define _cleanup_context_free_ _cleanup_(context_freep)
      36             : 
      37             : static int cached_use = -1;
      38             : static struct selabel_handle *label_hnd = NULL;
      39             : 
      40             : #define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
      41             : #define log_enforcing_errno(r, ...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, r, __VA_ARGS__)
      42             : #endif
      43             : 
      44         250 : bool mac_selinux_use(void) {
      45             : #if HAVE_SELINUX
      46         250 :         if (cached_use < 0)
      47         103 :                 cached_use = is_selinux_enabled() > 0;
      48             : 
      49         250 :         return cached_use;
      50             : #else
      51             :         return false;
      52             : #endif
      53             : }
      54             : 
      55           1 : void mac_selinux_retest(void) {
      56             : #if HAVE_SELINUX
      57           1 :         cached_use = -1;
      58             : #endif
      59           1 : }
      60             : 
      61          85 : int mac_selinux_init(void) {
      62          85 :         int r = 0;
      63             : 
      64             : #if HAVE_SELINUX
      65             :         usec_t before_timestamp, after_timestamp;
      66             :         struct mallinfo before_mallinfo, after_mallinfo;
      67             : 
      68          85 :         if (label_hnd)
      69           0 :                 return 0;
      70             : 
      71          85 :         if (!mac_selinux_use())
      72           0 :                 return 0;
      73             : 
      74          85 :         before_mallinfo = mallinfo();
      75          85 :         before_timestamp = now(CLOCK_MONOTONIC);
      76             : 
      77          85 :         label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
      78          85 :         if (!label_hnd) {
      79           0 :                 log_enforcing_errno(errno, "Failed to initialize SELinux context: %m");
      80           0 :                 r = security_getenforce() == 1 ? -errno : 0;
      81             :         } else  {
      82             :                 char timespan[FORMAT_TIMESPAN_MAX];
      83             :                 int l;
      84             : 
      85          85 :                 after_timestamp = now(CLOCK_MONOTONIC);
      86          85 :                 after_mallinfo = mallinfo();
      87             : 
      88          85 :                 l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
      89             : 
      90          85 :                 log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
      91             :                           format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
      92             :                           (l+1023)/1024);
      93             :         }
      94             : #endif
      95             : 
      96          85 :         return r;
      97             : }
      98             : 
      99         282 : void mac_selinux_finish(void) {
     100             : 
     101             : #if HAVE_SELINUX
     102         282 :         if (!label_hnd)
     103         197 :                 return;
     104             : 
     105          85 :         selabel_close(label_hnd);
     106          85 :         label_hnd = NULL;
     107             : #endif
     108             : }
     109             : 
     110          29 : int mac_selinux_fix(const char *path, LabelFixFlags flags) {
     111             : 
     112             : #if HAVE_SELINUX
     113             :         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
     114          29 :         _cleanup_freecon_ char* fcon = NULL;
     115          29 :         _cleanup_close_ int fd = -1;
     116             :         struct stat st;
     117             :         int r;
     118             : 
     119          29 :         assert(path);
     120             : 
     121             :         /* if mac_selinux_init() wasn't called before we are a NOOP */
     122          29 :         if (!label_hnd)
     123           0 :                 return 0;
     124             : 
     125             :         /* Open the file as O_PATH, to pin it while we determine and adjust the label */
     126          29 :         fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
     127          29 :         if (fd < 0) {
     128           0 :                 if ((flags & LABEL_IGNORE_ENOENT) && errno == ENOENT)
     129           0 :                         return 0;
     130             : 
     131           0 :                 return -errno;
     132             :         }
     133             : 
     134          29 :         if (fstat(fd, &st) < 0)
     135           0 :                 return -errno;
     136             : 
     137          29 :         if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) {
     138          29 :                 r = -errno;
     139             : 
     140             :                 /* If there's no label to set, then exit without warning */
     141          29 :                 if (r == -ENOENT)
     142          29 :                         return 0;
     143             : 
     144           0 :                 goto fail;
     145             :         }
     146             : 
     147           0 :         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
     148           0 :         if (setfilecon_raw(procfs_path, fcon) < 0) {
     149           0 :                 _cleanup_freecon_ char *oldcon = NULL;
     150             : 
     151           0 :                 r = -errno;
     152             : 
     153             :                 /* If the FS doesn't support labels, then exit without warning */
     154           0 :                 if (r == -EOPNOTSUPP)
     155           0 :                         return 0;
     156             : 
     157             :                 /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */
     158           0 :                 if (r == -EROFS && (flags & LABEL_IGNORE_EROFS))
     159           0 :                         return 0;
     160             : 
     161             :                 /* If the old label is identical to the new one, suppress any kind of error */
     162           0 :                 if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon))
     163           0 :                         return 0;
     164             : 
     165           0 :                 goto fail;
     166             :         }
     167             : 
     168           0 :         return 0;
     169             : 
     170           0 : fail:
     171           0 :         log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", path);
     172           0 :         if (security_getenforce() == 1)
     173           0 :                 return r;
     174             : #endif
     175             : 
     176           0 :         return 0;
     177             : }
     178             : 
     179           0 : int mac_selinux_apply(const char *path, const char *label) {
     180             : 
     181             : #if HAVE_SELINUX
     182           0 :         if (!mac_selinux_use())
     183           0 :                 return 0;
     184             : 
     185           0 :         assert(path);
     186           0 :         assert(label);
     187             : 
     188           0 :         if (setfilecon(path, label) < 0) {
     189           0 :                 log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
     190           0 :                 if (security_getenforce() > 0)
     191           0 :                         return -errno;
     192             :         }
     193             : #endif
     194           0 :         return 0;
     195             : }
     196             : 
     197           1 : int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
     198           1 :         int r = -EOPNOTSUPP;
     199             : 
     200             : #if HAVE_SELINUX
     201           1 :         _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
     202             :         security_class_t sclass;
     203             : 
     204           1 :         assert(exe);
     205           1 :         assert(label);
     206             : 
     207           1 :         if (!mac_selinux_use())
     208           0 :                 return -EOPNOTSUPP;
     209             : 
     210           1 :         r = getcon_raw(&mycon);
     211           1 :         if (r < 0)
     212           0 :                 return -errno;
     213             : 
     214           1 :         r = getfilecon_raw(exe, &fcon);
     215           1 :         if (r < 0)
     216           0 :                 return -errno;
     217             : 
     218           1 :         sclass = string_to_security_class("process");
     219           1 :         r = security_compute_create_raw(mycon, fcon, sclass, label);
     220           1 :         if (r < 0)
     221           0 :                 return -errno;
     222             : #endif
     223             : 
     224           1 :         return r;
     225             : }
     226             : 
     227           1 : int mac_selinux_get_our_label(char **label) {
     228           1 :         int r = -EOPNOTSUPP;
     229             : 
     230           1 :         assert(label);
     231             : 
     232             : #if HAVE_SELINUX
     233           1 :         if (!mac_selinux_use())
     234           0 :                 return -EOPNOTSUPP;
     235             : 
     236           1 :         r = getcon_raw(label);
     237           1 :         if (r < 0)
     238           0 :                 return -errno;
     239             : #endif
     240             : 
     241           1 :         return r;
     242             : }
     243             : 
     244           1 : int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
     245           1 :         int r = -EOPNOTSUPP;
     246             : 
     247             : #if HAVE_SELINUX
     248           1 :         _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
     249           1 :         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
     250             :         security_class_t sclass;
     251           1 :         const char *range = NULL;
     252             : 
     253           1 :         assert(socket_fd >= 0);
     254           1 :         assert(exe);
     255           1 :         assert(label);
     256             : 
     257           1 :         if (!mac_selinux_use())
     258           0 :                 return -EOPNOTSUPP;
     259             : 
     260           1 :         r = getcon_raw(&mycon);
     261           1 :         if (r < 0)
     262           0 :                 return -errno;
     263             : 
     264           1 :         r = getpeercon_raw(socket_fd, &peercon);
     265           1 :         if (r < 0)
     266           1 :                 return -errno;
     267             : 
     268           0 :         if (!exec_label) {
     269             :                 /* If there is no context set for next exec let's use context
     270             :                    of target executable */
     271           0 :                 r = getfilecon_raw(exe, &fcon);
     272           0 :                 if (r < 0)
     273           0 :                         return -errno;
     274             :         }
     275             : 
     276           0 :         bcon = context_new(mycon);
     277           0 :         if (!bcon)
     278           0 :                 return -ENOMEM;
     279             : 
     280           0 :         pcon = context_new(peercon);
     281           0 :         if (!pcon)
     282           0 :                 return -ENOMEM;
     283             : 
     284           0 :         range = context_range_get(pcon);
     285           0 :         if (!range)
     286           0 :                 return -errno;
     287             : 
     288           0 :         r = context_range_set(bcon, range);
     289           0 :         if (r)
     290           0 :                 return -errno;
     291             : 
     292           0 :         freecon(mycon);
     293           0 :         mycon = strdup(context_str(bcon));
     294           0 :         if (!mycon)
     295           0 :                 return -ENOMEM;
     296             : 
     297           0 :         sclass = string_to_security_class("process");
     298           0 :         r = security_compute_create_raw(mycon, fcon, sclass, label);
     299           0 :         if (r < 0)
     300           0 :                 return -errno;
     301             : #endif
     302             : 
     303           0 :         return r;
     304             : }
     305             : 
     306           2 : char* mac_selinux_free(char *label) {
     307             : 
     308             : #if HAVE_SELINUX
     309           2 :         if (!label)
     310           0 :                 return NULL;
     311             : 
     312           2 :         if (!mac_selinux_use())
     313           0 :                 return NULL;
     314             : 
     315           2 :         freecon(label);
     316             : #endif
     317             : 
     318           2 :         return NULL;
     319             : }
     320             : 
     321             : #if HAVE_SELINUX
     322          28 : static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
     323          28 :         _cleanup_freecon_ char *filecon = NULL;
     324             :         int r;
     325             : 
     326          28 :         assert(abspath);
     327          28 :         assert(path_is_absolute(abspath));
     328             : 
     329          28 :         r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
     330          28 :         if (r < 0) {
     331             :                 /* No context specified by the policy? Proceed without setting it. */
     332          27 :                 if (errno == ENOENT)
     333          27 :                         return 0;
     334             : 
     335           0 :                 log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
     336             :         } else {
     337           1 :                 if (setfscreatecon_raw(filecon) >= 0)
     338           1 :                         return 0; /* Success! */
     339             : 
     340           0 :                 log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
     341             :         }
     342             : 
     343           0 :         if (security_getenforce() > 0)
     344           0 :                 return -errno;
     345             : 
     346           0 :         return 0;
     347             : }
     348             : #endif
     349             : 
     350           0 : int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
     351           0 :         int r = 0;
     352             : 
     353             : #if HAVE_SELINUX
     354           0 :         _cleanup_free_ char *abspath = NULL;
     355             : 
     356           0 :         assert(path);
     357             : 
     358           0 :         if (!label_hnd)
     359           0 :                 return 0;
     360             : 
     361           0 :         if (!path_is_absolute(path)) {
     362           0 :                 _cleanup_free_ char *p = NULL;
     363             : 
     364           0 :                 if (dirfd == AT_FDCWD)
     365           0 :                         r = safe_getcwd(&p);
     366             :                 else
     367           0 :                         r = fd_get_path(dirfd, &p);
     368           0 :                 if (r < 0)
     369           0 :                         return r;
     370             : 
     371           0 :                 path = abspath = path_join(p, path);
     372           0 :                 if (!path)
     373           0 :                         return -ENOMEM;
     374             :         }
     375             : 
     376           0 :         r = selinux_create_file_prepare_abspath(path, mode);
     377             : #endif
     378           0 :         return r;
     379             : }
     380             : 
     381         153 : int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
     382         153 :         int r = 0;
     383             : 
     384             : #if HAVE_SELINUX
     385         153 :         _cleanup_free_ char *abspath = NULL;
     386             : 
     387         153 :         assert(path);
     388             : 
     389         153 :         if (!label_hnd)
     390         125 :                 return 0;
     391             : 
     392          28 :         r = path_make_absolute_cwd(path, &abspath);
     393          28 :         if (r < 0)
     394           0 :                 return r;
     395             : 
     396          28 :         r = selinux_create_file_prepare_abspath(abspath, mode);
     397             : #endif
     398          28 :         return r;
     399             : }
     400             : 
     401         153 : void mac_selinux_create_file_clear(void) {
     402             : 
     403             : #if HAVE_SELINUX
     404         153 :         PROTECT_ERRNO;
     405             : 
     406         153 :         if (!mac_selinux_use())
     407           0 :                 return;
     408             : 
     409         153 :         setfscreatecon_raw(NULL);
     410             : #endif
     411             : }
     412             : 
     413           0 : int mac_selinux_create_socket_prepare(const char *label) {
     414             : 
     415             : #if HAVE_SELINUX
     416           0 :         if (!mac_selinux_use())
     417           0 :                 return 0;
     418             : 
     419           0 :         assert(label);
     420             : 
     421           0 :         if (setsockcreatecon(label) < 0) {
     422           0 :                 log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
     423             : 
     424           0 :                 if (security_getenforce() == 1)
     425           0 :                         return -errno;
     426             :         }
     427             : #endif
     428             : 
     429           0 :         return 0;
     430             : }
     431             : 
     432           0 : void mac_selinux_create_socket_clear(void) {
     433             : 
     434             : #if HAVE_SELINUX
     435           0 :         PROTECT_ERRNO;
     436             : 
     437           0 :         if (!mac_selinux_use())
     438           0 :                 return;
     439             : 
     440           0 :         setsockcreatecon_raw(NULL);
     441             : #endif
     442             : }
     443             : 
     444           0 : int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
     445             : 
     446             :         /* Binds a socket and label its file system object according to the SELinux policy */
     447             : 
     448             : #if HAVE_SELINUX
     449           0 :         _cleanup_freecon_ char *fcon = NULL;
     450             :         const struct sockaddr_un *un;
     451           0 :         bool context_changed = false;
     452             :         char *path;
     453             :         int r;
     454             : 
     455           0 :         assert(fd >= 0);
     456           0 :         assert(addr);
     457           0 :         assert(addrlen >= sizeof(sa_family_t));
     458             : 
     459           0 :         if (!label_hnd)
     460           0 :                 goto skipped;
     461             : 
     462             :         /* Filter out non-local sockets */
     463           0 :         if (addr->sa_family != AF_UNIX)
     464           0 :                 goto skipped;
     465             : 
     466             :         /* Filter out anonymous sockets */
     467           0 :         if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1)
     468           0 :                 goto skipped;
     469             : 
     470             :         /* Filter out abstract namespace sockets */
     471           0 :         un = (const struct sockaddr_un*) addr;
     472           0 :         if (un->sun_path[0] == 0)
     473           0 :                 goto skipped;
     474             : 
     475           0 :         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
     476             : 
     477           0 :         if (path_is_absolute(path))
     478           0 :                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
     479             :         else {
     480           0 :                 _cleanup_free_ char *newpath = NULL;
     481             : 
     482           0 :                 r = path_make_absolute_cwd(path, &newpath);
     483           0 :                 if (r < 0)
     484           0 :                         return r;
     485             : 
     486           0 :                 r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
     487             :         }
     488             : 
     489           0 :         if (r < 0) {
     490             :                 /* No context specified by the policy? Proceed without setting it */
     491           0 :                 if (errno == ENOENT)
     492           0 :                         goto skipped;
     493             : 
     494           0 :                 log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
     495           0 :                 if (security_getenforce() > 0)
     496           0 :                         return -errno;
     497             : 
     498             :         } else {
     499           0 :                 if (setfscreatecon_raw(fcon) < 0) {
     500           0 :                         log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
     501           0 :                         if (security_getenforce() > 0)
     502           0 :                                 return -errno;
     503             :                 } else
     504           0 :                         context_changed = true;
     505             :         }
     506             : 
     507           0 :         r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
     508             : 
     509           0 :         if (context_changed)
     510           0 :                 setfscreatecon_raw(NULL);
     511             : 
     512           0 :         return r;
     513             : 
     514           0 : skipped:
     515             : #endif
     516           0 :         if (bind(fd, addr, addrlen) < 0)
     517           0 :                 return -errno;
     518             : 
     519           0 :         return 0;
     520             : }

Generated by: LCOV version 1.14