LCOV - code coverage report
Current view: top level - core - selinux-access.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 6 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 3 0.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2012 Dan Walsh
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include "selinux-access.h"
      23             : 
      24             : #ifdef HAVE_SELINUX
      25             : 
      26             : #include <stdio.h>
      27             : #include <errno.h>
      28             : #include <selinux/selinux.h>
      29             : #include <selinux/avc.h>
      30             : #ifdef HAVE_AUDIT
      31             : #include <libaudit.h>
      32             : #endif
      33             : 
      34             : #include "sd-bus.h"
      35             : #include "bus-util.h"
      36             : #include "util.h"
      37             : #include "log.h"
      38             : #include "selinux-util.h"
      39             : #include "audit-fd.h"
      40             : #include "strv.h"
      41             : 
      42             : static bool initialized = false;
      43             : 
      44             : struct audit_info {
      45             :         sd_bus_creds *creds;
      46             :         const char *path;
      47             :         const char *cmdline;
      48             : };
      49             : 
      50             : /*
      51             :    Any time an access gets denied this callback will be called
      52             :    with the audit data.  We then need to just copy the audit data into the msgbuf.
      53             : */
      54             : static int audit_callback(
      55             :                 void *auditdata,
      56             :                 security_class_t cls,
      57             :                 char *msgbuf,
      58             :                 size_t msgbufsize) {
      59             : 
      60             :         const struct audit_info *audit = auditdata;
      61             :         uid_t uid = 0, login_uid = 0;
      62             :         gid_t gid = 0;
      63             :         char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
      64             :         char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a";
      65             :         char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a";
      66             : 
      67             :         if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0)
      68             :                 xsprintf(login_uid_buf, UID_FMT, login_uid);
      69             :         if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0)
      70             :                 xsprintf(uid_buf, UID_FMT, uid);
      71             :         if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
      72             :                 xsprintf(gid_buf, GID_FMT, gid);
      73             : 
      74             :         snprintf(msgbuf, msgbufsize,
      75             :                  "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
      76             :                  login_uid_buf, uid_buf, gid_buf,
      77             :                  audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
      78             :                  audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
      79             : 
      80             :         return 0;
      81             : }
      82             : 
      83             : static int callback_type_to_priority(int type) {
      84             :         switch(type) {
      85             : 
      86             :         case SELINUX_ERROR:
      87             :                 return LOG_ERR;
      88             : 
      89             :         case SELINUX_WARNING:
      90             :                 return LOG_WARNING;
      91             : 
      92             :         case SELINUX_INFO:
      93             :                 return LOG_INFO;
      94             : 
      95             :         case SELINUX_AVC:
      96             :         default:
      97             :                 return LOG_NOTICE;
      98             :         }
      99             : }
     100             : 
     101             : /*
     102             :    libselinux uses this callback when access gets denied or other
     103             :    events happen. If audit is turned on, messages will be reported
     104             :    using audit netlink, otherwise they will be logged using the usual
     105             :    channels.
     106             : 
     107             :    Code copied from dbus and modified.
     108             : */
     109             : _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
     110             :         va_list ap;
     111             : 
     112             : #ifdef HAVE_AUDIT
     113             :         int fd;
     114             : 
     115             :         fd = get_audit_fd();
     116             : 
     117             :         if (fd >= 0) {
     118             :                 _cleanup_free_ char *buf = NULL;
     119             :                 int r;
     120             : 
     121             :                 va_start(ap, fmt);
     122             :                 r = vasprintf(&buf, fmt, ap);
     123             :                 va_end(ap);
     124             : 
     125             :                 if (r >= 0) {
     126             :                         audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
     127             :                         return 0;
     128             :                 }
     129             :         }
     130             : #endif
     131             : 
     132             :         va_start(ap, fmt);
     133             :         log_internalv(LOG_AUTH | callback_type_to_priority(type),
     134             :                       0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
     135             :         va_end(ap);
     136             : 
     137             :         return 0;
     138             : }
     139             : 
     140             : /*
     141             :    Function must be called once to initialize the SELinux AVC environment.
     142             :    Sets up callbacks.
     143             :    If you want to cleanup memory you should need to call selinux_access_finish.
     144             : */
     145             : static int access_init(void) {
     146             :         int r = 0;
     147             : 
     148             :         if (avc_open(NULL, 0))
     149             :                 return log_error_errno(errno, "avc_open() failed: %m");
     150             : 
     151             :         selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
     152             :         selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
     153             : 
     154             :         if (security_getenforce() < 0){
     155             :                 r = -errno;
     156             :                 avc_destroy();
     157             :         }
     158             : 
     159             :         return r;
     160             : }
     161             : 
     162             : static int mac_selinux_access_init(sd_bus_error *error) {
     163             :         int r;
     164             : 
     165             :         if (initialized)
     166             :                 return 0;
     167             : 
     168             :         if (!mac_selinux_use())
     169             :                 return 0;
     170             : 
     171             :         r = access_init();
     172             :         if (r < 0)
     173             :                 return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
     174             : 
     175             :         initialized = true;
     176             :         return 0;
     177             : }
     178             : #endif
     179             : 
     180           0 : void mac_selinux_access_free(void) {
     181             : 
     182             : #ifdef HAVE_SELINUX
     183             :         if (!initialized)
     184             :                 return;
     185             : 
     186             :         avc_destroy();
     187             :         initialized = false;
     188             : #endif
     189           0 : }
     190             : 
     191             : /*
     192             :    This function communicates with the kernel to check whether or not it should
     193             :    allow the access.
     194             :    If the machine is in permissive mode it will return ok.  Audit messages will
     195             :    still be generated if the access would be denied in enforcing mode.
     196             : */
     197           0 : int mac_selinux_generic_access_check(
     198             :                 sd_bus_message *message,
     199             :                 const char *path,
     200             :                 const char *permission,
     201             :                 sd_bus_error *error) {
     202             : 
     203             : #ifdef HAVE_SELINUX
     204             :         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
     205             :         const char *tclass = NULL, *scon = NULL;
     206             :         struct audit_info audit_info = {};
     207             :         _cleanup_free_ char *cl = NULL;
     208             :         security_context_t fcon = NULL;
     209             :         char **cmdline = NULL;
     210             :         int r = 0;
     211             : 
     212             :         assert(message);
     213             :         assert(permission);
     214             :         assert(error);
     215             : 
     216             :         if (!mac_selinux_use())
     217             :                 return 0;
     218             : 
     219             :         r = mac_selinux_access_init(error);
     220             :         if (r < 0)
     221             :                 return r;
     222             : 
     223             :         r = sd_bus_query_sender_creds(
     224             :                         message,
     225             :                         SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
     226             :                         SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID|
     227             :                         SD_BUS_CREDS_SELINUX_CONTEXT|
     228             :                         SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
     229             :                         &creds);
     230             :         if (r < 0)
     231             :                 goto finish;
     232             : 
     233             :         /* The SELinux context is something we really should have
     234             :          * gotten directly from the message or sender, and not be an
     235             :          * augmented field. If it was augmented we cannot use it for
     236             :          * authorization, since this is racy and vulnerable. Let's add
     237             :          * an extra check, just in case, even though this really
     238             :          * shouldn't be possible. */
     239             :         assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
     240             : 
     241             :         r = sd_bus_creds_get_selinux_context(creds, &scon);
     242             :         if (r < 0)
     243             :                 goto finish;
     244             : 
     245             :         if (path) {
     246             :                 /* Get the file context of the unit file */
     247             : 
     248             :                 r = getfilecon(path, &fcon);
     249             :                 if (r < 0) {
     250             :                         r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
     251             :                         goto finish;
     252             :                 }
     253             : 
     254             :                 tclass = "service";
     255             :         } else {
     256             :                 r = getcon(&fcon);
     257             :                 if (r < 0) {
     258             :                         r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
     259             :                         goto finish;
     260             :                 }
     261             : 
     262             :                 tclass = "system";
     263             :         }
     264             : 
     265             :         sd_bus_creds_get_cmdline(creds, &cmdline);
     266             :         cl = strv_join(cmdline, " ");
     267             : 
     268             :         audit_info.creds = creds;
     269             :         audit_info.path = path;
     270             :         audit_info.cmdline = cl;
     271             : 
     272             :         r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
     273             :         if (r < 0)
     274             :                 r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
     275             : 
     276             :         log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r);
     277             : 
     278             : finish:
     279             :         freecon(fcon);
     280             : 
     281             :         if (r < 0 && security_getenforce() != 1) {
     282             :                 sd_bus_error_free(error);
     283             :                 r = 0;
     284             :         }
     285             : 
     286             :         return r;
     287             : #else
     288           0 :         return 0;
     289             : #endif
     290             : }
     291             : 
     292           0 : int mac_selinux_unit_access_check_strv(
     293             :                 char **units,
     294             :                 sd_bus_message *message,
     295             :                 Manager *m,
     296             :                 const char *permission,
     297             :                 sd_bus_error *error) {
     298             : 
     299             : #ifdef HAVE_SELINUX
     300             :         char **i;
     301             :         Unit *u;
     302             :         int r;
     303             : 
     304             :         STRV_FOREACH(i, units) {
     305             :                 r = manager_load_unit(m, *i, NULL, error, &u);
     306             :                 if (r < 0)
     307             :                         return r;
     308             :                 r = mac_selinux_unit_access_check(u, message, permission, error);
     309             :                 if (r < 0)
     310             :                         return r;
     311             :         }
     312             : #endif
     313           0 :         return 0;
     314             : }

Generated by: LCOV version 1.11