LCOV - code coverage report
Current view: top level - shared - condition.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 170 239 71.1 %
Date: 2015-07-29 18:47:03 Functions: 27 31 87.1 %

          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 2010 Lennart Poettering
       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 <stdlib.h>
      23             : #include <errno.h>
      24             : #include <string.h>
      25             : #include <unistd.h>
      26             : #include <fnmatch.h>
      27             : 
      28             : #include "sd-id128.h"
      29             : #include "util.h"
      30             : #include "virt.h"
      31             : #include "path-util.h"
      32             : #include "architecture.h"
      33             : #include "smack-util.h"
      34             : #include "apparmor-util.h"
      35             : #include "ima-util.h"
      36             : #include "selinux-util.h"
      37             : #include "audit.h"
      38             : #include "cap-list.h"
      39             : #include "hostname-util.h"
      40             : #include "condition.h"
      41             : 
      42          37 : Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
      43             :         Condition *c;
      44             :         int r;
      45             : 
      46          37 :         assert(type >= 0);
      47          37 :         assert(type < _CONDITION_TYPE_MAX);
      48          37 :         assert((!parameter) == (type == CONDITION_NULL));
      49             : 
      50          37 :         c = new0(Condition, 1);
      51          37 :         if (!c)
      52           0 :                 return NULL;
      53             : 
      54          37 :         c->type = type;
      55          37 :         c->trigger = trigger;
      56          37 :         c->negate = negate;
      57             : 
      58          37 :         r = free_and_strdup(&c->parameter, parameter);
      59          37 :         if (r < 0) {
      60           0 :                 free(c);
      61           0 :                 return NULL;
      62             :         }
      63             : 
      64          37 :         return c;
      65             : }
      66             : 
      67          37 : void condition_free(Condition *c) {
      68          37 :         assert(c);
      69             : 
      70          37 :         free(c->parameter);
      71          37 :         free(c);
      72          37 : }
      73             : 
      74        1580 : Condition* condition_free_list(Condition *first) {
      75             :         Condition *c, *n;
      76             : 
      77        1581 :         LIST_FOREACH_SAFE(conditions, c, n, first)
      78           1 :                 condition_free(c);
      79             : 
      80        1580 :         return NULL;
      81             : }
      82             : 
      83           2 : static int condition_test_kernel_command_line(Condition *c) {
      84           4 :         _cleanup_free_ char *line = NULL;
      85             :         const char *p;
      86             :         bool equal;
      87             :         int r;
      88             : 
      89           2 :         assert(c);
      90           2 :         assert(c->parameter);
      91           2 :         assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
      92             : 
      93           2 :         r = proc_cmdline(&line);
      94           2 :         if (r < 0)
      95           0 :                 return r;
      96             : 
      97           2 :         equal = !!strchr(c->parameter, '=');
      98           2 :         p = line;
      99             : 
     100             :         for (;;) {
     101          32 :                 _cleanup_free_ char *word = NULL;
     102             :                 bool found;
     103             : 
     104          16 :                 r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
     105          16 :                 if (r < 0)
     106           0 :                         return r;
     107          16 :                 if (r == 0)
     108           2 :                         break;
     109             : 
     110          14 :                 if (equal)
     111           7 :                         found = streq(word, c->parameter);
     112             :                 else {
     113             :                         const char *f;
     114             : 
     115           7 :                         f = startswith(word, c->parameter);
     116           7 :                         found = f && (*f == '=' || *f == 0);
     117             :                 }
     118             : 
     119          14 :                 if (found)
     120           0 :                         return true;
     121          14 :         }
     122             : 
     123           2 :         return false;
     124             : }
     125             : 
     126           1 : static int condition_test_virtualization(Condition *c) {
     127             :         int b, v;
     128             :         const char *id;
     129             : 
     130           1 :         assert(c);
     131           1 :         assert(c->parameter);
     132           1 :         assert(c->type == CONDITION_VIRTUALIZATION);
     133             : 
     134           1 :         v = detect_virtualization(&id);
     135           1 :         if (v < 0)
     136           0 :                 return v;
     137             : 
     138             :         /* First, compare with yes/no */
     139           1 :         b = parse_boolean(c->parameter);
     140             : 
     141           1 :         if (v > 0 && b > 0)
     142           0 :                 return true;
     143             : 
     144           1 :         if (v == 0 && b == 0)
     145           0 :                 return true;
     146             : 
     147             :         /* Then, compare categorization */
     148           1 :         if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm"))
     149           0 :                 return true;
     150             : 
     151           1 :         if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
     152           0 :                 return true;
     153             : 
     154             :         /* Finally compare id */
     155           1 :         return v > 0 && streq(c->parameter, id);
     156             : }
     157             : 
     158           3 : static int condition_test_architecture(Condition *c) {
     159             :         int a, b;
     160             : 
     161           3 :         assert(c);
     162           3 :         assert(c->parameter);
     163           3 :         assert(c->type == CONDITION_ARCHITECTURE);
     164             : 
     165           3 :         a = uname_architecture();
     166           3 :         if (a < 0)
     167           0 :                 return a;
     168             : 
     169           3 :         if (streq(c->parameter, "native"))
     170           0 :                 b = native_architecture();
     171             :         else
     172           3 :                 b = architecture_from_string(c->parameter);
     173           3 :         if (b < 0)
     174           1 :                 return b;
     175             : 
     176           2 :         return a == b;
     177             : }
     178             : 
     179           4 : static int condition_test_host(Condition *c) {
     180           8 :         _cleanup_free_ char *h = NULL;
     181             :         sd_id128_t x, y;
     182             :         int r;
     183             : 
     184           4 :         assert(c);
     185           4 :         assert(c->parameter);
     186           4 :         assert(c->type == CONDITION_HOST);
     187             : 
     188           4 :         if (sd_id128_from_string(c->parameter, &x) >= 0) {
     189             : 
     190           2 :                 r = sd_id128_get_machine(&y);
     191           2 :                 if (r < 0)
     192           0 :                         return r;
     193             : 
     194           2 :                 return sd_id128_equal(x, y);
     195             :         }
     196             : 
     197           2 :         h = gethostname_malloc();
     198           2 :         if (!h)
     199           0 :                 return -ENOMEM;
     200             : 
     201           2 :         return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
     202             : }
     203             : 
     204           3 : static int condition_test_ac_power(Condition *c) {
     205             :         int r;
     206             : 
     207           3 :         assert(c);
     208           3 :         assert(c->parameter);
     209           3 :         assert(c->type == CONDITION_AC_POWER);
     210             : 
     211           3 :         r = parse_boolean(c->parameter);
     212           3 :         if (r < 0)
     213           0 :                 return r;
     214             : 
     215           3 :         return (on_ac_power() != 0) == !!r;
     216             : }
     217             : 
     218           6 : static int condition_test_security(Condition *c) {
     219           6 :         assert(c);
     220           6 :         assert(c->parameter);
     221           6 :         assert(c->type == CONDITION_SECURITY);
     222             : 
     223           6 :         if (streq(c->parameter, "selinux"))
     224           1 :                 return mac_selinux_use();
     225           5 :         if (streq(c->parameter, "smack"))
     226           1 :                 return mac_smack_use();
     227           4 :         if (streq(c->parameter, "apparmor"))
     228           1 :                 return mac_apparmor_use();
     229           3 :         if (streq(c->parameter, "audit"))
     230           1 :                 return use_audit();
     231           2 :         if (streq(c->parameter, "ima"))
     232           1 :                 return use_ima();
     233             : 
     234           1 :         return false;
     235             : }
     236             : 
     237           0 : static int condition_test_capability(Condition *c) {
     238           0 :         _cleanup_fclose_ FILE *f = NULL;
     239             :         int value;
     240             :         char line[LINE_MAX];
     241           0 :         unsigned long long capabilities = -1;
     242             : 
     243           0 :         assert(c);
     244           0 :         assert(c->parameter);
     245           0 :         assert(c->type == CONDITION_CAPABILITY);
     246             : 
     247             :         /* If it's an invalid capability, we don't have it */
     248           0 :         value = capability_from_name(c->parameter);
     249           0 :         if (value < 0)
     250           0 :                 return -EINVAL;
     251             : 
     252             :         /* If it's a valid capability we default to assume
     253             :          * that we have it */
     254             : 
     255           0 :         f = fopen("/proc/self/status", "re");
     256           0 :         if (!f)
     257           0 :                 return -errno;
     258             : 
     259           0 :         while (fgets(line, sizeof(line), f)) {
     260           0 :                 truncate_nl(line);
     261             : 
     262           0 :                 if (startswith(line, "CapBnd:")) {
     263           0 :                         (void) sscanf(line+7, "%llx", &capabilities);
     264           0 :                         break;
     265             :                 }
     266             :         }
     267             : 
     268           0 :         return !!(capabilities & (1ULL << value));
     269             : }
     270             : 
     271           0 : static int condition_test_needs_update(Condition *c) {
     272             :         const char *p;
     273             :         struct stat usr, other;
     274             : 
     275           0 :         assert(c);
     276           0 :         assert(c->parameter);
     277           0 :         assert(c->type == CONDITION_NEEDS_UPDATE);
     278             : 
     279             :         /* If the file system is read-only we shouldn't suggest an update */
     280           0 :         if (path_is_read_only_fs(c->parameter) > 0)
     281           0 :                 return false;
     282             : 
     283             :         /* Any other failure means we should allow the condition to be true,
     284             :          * so that we rather invoke too many update tools then too
     285             :          * few. */
     286             : 
     287           0 :         if (!path_is_absolute(c->parameter))
     288           0 :                 return true;
     289             : 
     290           0 :         p = strjoina(c->parameter, "/.updated");
     291           0 :         if (lstat(p, &other) < 0)
     292           0 :                 return true;
     293             : 
     294           0 :         if (lstat("/usr/", &usr) < 0)
     295           0 :                 return true;
     296             : 
     297           0 :         return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
     298           0 :                 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
     299             : }
     300             : 
     301           0 : static int condition_test_first_boot(Condition *c) {
     302             :         int r;
     303             : 
     304           0 :         assert(c);
     305           0 :         assert(c->parameter);
     306           0 :         assert(c->type == CONDITION_FIRST_BOOT);
     307             : 
     308           0 :         r = parse_boolean(c->parameter);
     309           0 :         if (r < 0)
     310           0 :                 return r;
     311             : 
     312           0 :         return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
     313             : }
     314             : 
     315           4 : static int condition_test_path_exists(Condition *c) {
     316           4 :         assert(c);
     317           4 :         assert(c->parameter);
     318           4 :         assert(c->type == CONDITION_PATH_EXISTS);
     319             : 
     320           4 :         return access(c->parameter, F_OK) >= 0;
     321             : }
     322             : 
     323           2 : static int condition_test_path_exists_glob(Condition *c) {
     324           2 :         assert(c);
     325           2 :         assert(c->parameter);
     326           2 :         assert(c->type == CONDITION_PATH_EXISTS_GLOB);
     327             : 
     328           2 :         return glob_exists(c->parameter) > 0;
     329             : }
     330             : 
     331           1 : static int condition_test_path_is_directory(Condition *c) {
     332           1 :         assert(c);
     333           1 :         assert(c->parameter);
     334           1 :         assert(c->type == CONDITION_PATH_IS_DIRECTORY);
     335             : 
     336           1 :         return is_dir(c->parameter, true) > 0;
     337             : }
     338             : 
     339           1 : static int condition_test_path_is_symbolic_link(Condition *c) {
     340           1 :         assert(c);
     341           1 :         assert(c->parameter);
     342           1 :         assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
     343             : 
     344           1 :         return is_symlink(c->parameter) > 0;
     345             : }
     346             : 
     347           3 : static int condition_test_path_is_mount_point(Condition *c) {
     348           3 :         assert(c);
     349           3 :         assert(c->parameter);
     350           3 :         assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
     351             : 
     352           3 :         return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
     353             : }
     354             : 
     355           1 : static int condition_test_path_is_read_write(Condition *c) {
     356           1 :         assert(c);
     357           1 :         assert(c->parameter);
     358           1 :         assert(c->type == CONDITION_PATH_IS_READ_WRITE);
     359             : 
     360           1 :         return path_is_read_only_fs(c->parameter) <= 0;
     361             : }
     362             : 
     363           1 : static int condition_test_directory_not_empty(Condition *c) {
     364             :         int r;
     365             : 
     366           1 :         assert(c);
     367           1 :         assert(c->parameter);
     368           1 :         assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
     369             : 
     370           1 :         r = dir_is_empty(c->parameter);
     371           1 :         return r <= 0 && r != -ENOENT;
     372             : }
     373             : 
     374           1 : static int condition_test_file_not_empty(Condition *c) {
     375             :         struct stat st;
     376             : 
     377           1 :         assert(c);
     378           1 :         assert(c->parameter);
     379           1 :         assert(c->type == CONDITION_FILE_NOT_EMPTY);
     380             : 
     381           3 :         return (stat(c->parameter, &st) >= 0 &&
     382           2 :                 S_ISREG(st.st_mode) &&
     383           1 :                 st.st_size > 0);
     384             : }
     385             : 
     386           2 : static int condition_test_file_is_executable(Condition *c) {
     387             :         struct stat st;
     388             : 
     389           2 :         assert(c);
     390           2 :         assert(c->parameter);
     391           2 :         assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
     392             : 
     393           6 :         return (stat(c->parameter, &st) >= 0 &&
     394           4 :                 S_ISREG(st.st_mode) &&
     395           2 :                 (st.st_mode & 0111));
     396             : }
     397             : 
     398           2 : static int condition_test_null(Condition *c) {
     399           2 :         assert(c);
     400           2 :         assert(c->type == CONDITION_NULL);
     401             : 
     402             :         /* Note that during parsing we already evaluate the string and
     403             :          * store it in c->negate */
     404           2 :         return true;
     405             : }
     406             : 
     407          37 : int condition_test(Condition *c) {
     408             : 
     409             :         static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
     410             :                 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
     411             :                 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
     412             :                 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
     413             :                 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
     414             :                 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
     415             :                 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
     416             :                 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
     417             :                 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
     418             :                 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
     419             :                 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
     420             :                 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
     421             :                 [CONDITION_SECURITY] = condition_test_security,
     422             :                 [CONDITION_CAPABILITY] = condition_test_capability,
     423             :                 [CONDITION_HOST] = condition_test_host,
     424             :                 [CONDITION_AC_POWER] = condition_test_ac_power,
     425             :                 [CONDITION_ARCHITECTURE] = condition_test_architecture,
     426             :                 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
     427             :                 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
     428             :                 [CONDITION_NULL] = condition_test_null,
     429             :         };
     430             : 
     431             :         int r, b;
     432             : 
     433          37 :         assert(c);
     434          37 :         assert(c->type >= 0);
     435          37 :         assert(c->type < _CONDITION_TYPE_MAX);
     436             : 
     437          37 :         r = condition_tests[c->type](c);
     438          37 :         if (r < 0) {
     439           1 :                 c->result = CONDITION_ERROR;
     440           1 :                 return r;
     441             :         }
     442             : 
     443          36 :         b = (r > 0) == !c->negate;
     444          36 :         c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
     445          36 :         return b;
     446             : }
     447             : 
     448           0 : void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
     449           0 :         assert(c);
     450           0 :         assert(f);
     451             : 
     452           0 :         if (!prefix)
     453           0 :                 prefix = "";
     454             : 
     455           0 :         fprintf(f,
     456             :                 "%s\t%s: %s%s%s %s\n",
     457             :                 prefix,
     458           0 :                 to_string(c->type),
     459           0 :                 c->trigger ? "|" : "",
     460           0 :                 c->negate ? "!" : "",
     461             :                 c->parameter,
     462           0 :                 condition_result_to_string(c->result));
     463           0 : }
     464             : 
     465         650 : void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
     466             :         Condition *c;
     467             : 
     468         650 :         LIST_FOREACH(conditions, c, first)
     469           0 :                 condition_dump(c, f, prefix, to_string);
     470         650 : }
     471             : 
     472             : static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
     473             :         [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
     474             :         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
     475             :         [CONDITION_HOST] = "ConditionHost",
     476             :         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
     477             :         [CONDITION_SECURITY] = "ConditionSecurity",
     478             :         [CONDITION_CAPABILITY] = "ConditionCapability",
     479             :         [CONDITION_AC_POWER] = "ConditionACPower",
     480             :         [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
     481             :         [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
     482             :         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
     483             :         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
     484             :         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
     485             :         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
     486             :         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
     487             :         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
     488             :         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
     489             :         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
     490             :         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
     491             :         [CONDITION_NULL] = "ConditionNull"
     492             : };
     493             : 
     494          42 : DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
     495             : 
     496             : static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
     497             :         [CONDITION_ARCHITECTURE] = "AssertArchitecture",
     498             :         [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
     499             :         [CONDITION_HOST] = "AssertHost",
     500             :         [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
     501             :         [CONDITION_SECURITY] = "AssertSecurity",
     502             :         [CONDITION_CAPABILITY] = "AssertCapability",
     503             :         [CONDITION_AC_POWER] = "AssertACPower",
     504             :         [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
     505             :         [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
     506             :         [CONDITION_PATH_EXISTS] = "AssertPathExists",
     507             :         [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
     508             :         [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
     509             :         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
     510             :         [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
     511             :         [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
     512             :         [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
     513             :         [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
     514             :         [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
     515             :         [CONDITION_NULL] = "AssertNull"
     516             : };
     517             : 
     518          42 : DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
     519             : 
     520             : static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
     521             :         [CONDITION_UNTESTED] = "untested",
     522             :         [CONDITION_SUCCEEDED] = "succeeded",
     523             :         [CONDITION_FAILED] = "failed",
     524             :         [CONDITION_ERROR] = "error",
     525             : };
     526             : 
     527          12 : DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);

Generated by: LCOV version 1.11