2014-02-20 22:26:54 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd 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 .
systemd 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 systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <stdlib.h>
# include <errno.h>
# include <string.h>
# include <unistd.h>
# include <sys/statvfs.h>
# include <fnmatch.h>
2014-11-06 03:40:37 +03:00
# include "sd-id128.h"
2014-02-20 22:26:54 +04:00
# include "util.h"
# include "virt.h"
# include "path-util.h"
# include "fileio.h"
# include "unit.h"
2014-02-21 05:06:04 +04:00
# include "architecture.h"
2014-11-06 03:40:37 +03:00
# include "smack-util.h"
# include "apparmor-util.h"
# include "ima-util.h"
# include "selinux-util.h"
# include "audit.h"
2014-11-06 16:19:05 +03:00
# include "condition.h"
2014-02-20 22:26:54 +04:00
Condition * condition_new ( ConditionType type , const char * parameter , bool trigger , bool negate ) {
Condition * c ;
2014-11-06 03:40:37 +03:00
int r ;
2014-02-20 22:26:54 +04:00
2014-11-06 04:11:08 +03:00
assert ( type > = 0 ) ;
2014-02-20 22:26:54 +04:00
assert ( type < _CONDITION_TYPE_MAX ) ;
2014-11-06 04:11:08 +03:00
assert ( ! parameter = = ( type = = CONDITION_NULL ) ) ;
2014-02-20 22:26:54 +04:00
c = new0 ( Condition , 1 ) ;
if ( ! c )
return NULL ;
c - > type = type ;
c - > trigger = trigger ;
c - > negate = negate ;
2014-11-06 03:40:37 +03:00
r = free_and_strdup ( & c - > parameter , parameter ) ;
if ( r < 0 ) {
free ( c ) ;
return NULL ;
2014-02-20 22:26:54 +04:00
}
return c ;
}
void condition_free ( Condition * c ) {
assert ( c ) ;
free ( c - > parameter ) ;
free ( c ) ;
}
void condition_free_list ( Condition * first ) {
Condition * c , * n ;
LIST_FOREACH_SAFE ( conditions , c , n , first )
condition_free ( c ) ;
}
2014-11-06 04:02:13 +03:00
static int condition_test_kernel_command_line ( Condition * c ) {
2014-11-05 21:43:55 +03:00
_cleanup_free_ char * line = NULL ;
const char * p ;
2014-02-20 22:26:54 +04:00
bool equal ;
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_KERNEL_COMMAND_LINE ) ;
r = proc_cmdline ( & line ) ;
if ( r < 0 )
2014-11-06 02:49:44 +03:00
return r ;
2014-02-20 22:26:54 +04:00
equal = ! ! strchr ( c - > parameter , ' = ' ) ;
2014-11-05 21:43:55 +03:00
p = line ;
for ( ; ; ) {
_cleanup_free_ char * word = NULL ;
bool found ;
2014-11-07 02:10:24 +03:00
r = unquote_first_word ( & p , & word , true ) ;
2014-11-06 02:49:44 +03:00
if ( r < 0 )
return r ;
if ( r = = 0 )
2014-11-06 04:02:13 +03:00
break ;
2014-11-05 21:43:55 +03:00
if ( equal )
found = streq ( word , c - > parameter ) ;
else {
const char * f ;
f = startswith ( word , c - > parameter ) ;
found = f & & ( * f = = ' = ' | | * f = = 0 ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-05 21:43:55 +03:00
if ( found )
2014-11-06 04:02:13 +03:00
return true ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
return false ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
static int condition_test_virtualization ( Condition * c ) {
2014-02-24 01:05:18 +04:00
int b , v ;
2014-02-20 22:26:54 +04:00
const char * id ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_VIRTUALIZATION ) ;
v = detect_virtualization ( & id ) ;
2014-11-06 02:49:44 +03:00
if ( v < 0 )
return v ;
2014-02-20 22:26:54 +04:00
/* First, compare with yes/no */
b = parse_boolean ( c - > parameter ) ;
if ( v > 0 & & b > 0 )
2014-11-06 04:02:13 +03:00
return true ;
2014-02-20 22:26:54 +04:00
if ( v = = 0 & & b = = 0 )
2014-11-06 04:02:13 +03:00
return true ;
2014-02-20 22:26:54 +04:00
/* Then, compare categorization */
if ( v = = VIRTUALIZATION_VM & & streq ( c - > parameter , " vm " ) )
2014-11-06 04:02:13 +03:00
return true ;
2014-02-20 22:26:54 +04:00
if ( v = = VIRTUALIZATION_CONTAINER & & streq ( c - > parameter , " container " ) )
2014-11-06 04:02:13 +03:00
return true ;
2014-02-20 22:26:54 +04:00
/* Finally compare id */
2014-11-06 04:02:13 +03:00
return v > 0 & & streq ( c - > parameter , id ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
static int condition_test_architecture ( Condition * c ) {
2014-11-06 02:49:44 +03:00
int a , b ;
2014-02-21 05:06:04 +04:00
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_ARCHITECTURE ) ;
a = uname_architecture ( ) ;
if ( a < 0 )
2014-11-06 02:49:44 +03:00
return a ;
2014-02-21 05:06:04 +04:00
if ( streq ( c - > parameter , " native " ) )
b = native_architecture ( ) ;
else
b = architecture_from_string ( c - > parameter ) ;
if ( b < 0 )
2014-11-06 02:49:44 +03:00
return b ;
2014-02-21 05:06:04 +04:00
2014-11-06 04:02:13 +03:00
return a = = b ;
2014-02-21 05:06:04 +04:00
}
2014-11-06 04:02:13 +03:00
static int condition_test_host ( Condition * c ) {
2014-06-13 14:39:58 +04:00
_cleanup_free_ char * h = NULL ;
2014-02-20 22:26:54 +04:00
sd_id128_t x , y ;
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_HOST ) ;
if ( sd_id128_from_string ( c - > parameter , & x ) > = 0 ) {
r = sd_id128_get_machine ( & y ) ;
if ( r < 0 )
2014-11-06 02:49:44 +03:00
return r ;
2014-02-20 22:26:54 +04:00
2014-11-06 04:02:13 +03:00
return sd_id128_equal ( x , y ) ;
2014-02-20 22:26:54 +04:00
}
h = gethostname_malloc ( ) ;
if ( ! h )
2014-11-06 02:49:44 +03:00
return - ENOMEM ;
2014-02-20 22:26:54 +04:00
2014-11-06 04:02:13 +03:00
return fnmatch ( c - > parameter , h , FNM_CASEFOLD ) = = 0 ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 04:02:13 +03:00
static int condition_test_ac_power ( Condition * c ) {
2014-02-20 22:26:54 +04:00
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_AC_POWER ) ;
r = parse_boolean ( c - > parameter ) ;
if ( r < 0 )
2014-11-06 02:49:44 +03:00
return r ;
2014-02-20 22:26:54 +04:00
2014-11-06 04:02:13 +03:00
return ( on_ac_power ( ) ! = 0 ) = = ! ! r ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 03:40:37 +03:00
static int condition_test_security ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_SECURITY ) ;
if ( streq ( c - > parameter , " selinux " ) )
2014-11-06 04:02:13 +03:00
return mac_selinux_use ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " smack " ) )
2014-11-06 04:02:13 +03:00
return mac_smack_use ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " apparmor " ) )
2014-11-06 04:02:13 +03:00
return mac_apparmor_use ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " audit " ) )
2014-11-06 04:02:13 +03:00
return use_audit ( ) ;
2014-11-06 03:40:37 +03:00
if ( streq ( c - > parameter , " ima " ) )
2014-11-06 04:02:13 +03:00
return use_ima ( ) ;
2014-11-06 03:40:37 +03:00
2014-11-06 04:02:13 +03:00
return false ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_capability ( Condition * c ) {
_cleanup_fclose_ FILE * f = NULL ;
cap_value_t value ;
char line [ LINE_MAX ] ;
unsigned long long capabilities = - 1 ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_CAPABILITY ) ;
/* If it's an invalid capability, we don't have it */
if ( cap_from_name ( c - > parameter , & value ) < 0 )
return - EINVAL ;
/* If it's a valid capability we default to assume
* that we have it */
f = fopen ( " /proc/self/status " , " re " ) ;
if ( ! f )
return - errno ;
while ( fgets ( line , sizeof ( line ) , f ) ) {
truncate_nl ( line ) ;
if ( startswith ( line , " CapBnd: " ) ) {
( void ) sscanf ( line + 7 , " %llx " , & capabilities ) ;
break ;
}
}
2014-11-06 04:02:13 +03:00
return ! ! ( capabilities & ( 1ULL < < value ) ) ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_needs_update ( Condition * c ) {
const char * p ;
struct stat usr , other ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_NEEDS_UPDATE ) ;
/* If the file system is read-only we shouldn't suggest an update */
if ( path_is_read_only_fs ( c - > parameter ) > 0 )
2014-11-06 04:02:13 +03:00
return false ;
2014-11-06 03:40:37 +03:00
/* Any other failure means we should allow the condition to be true,
* so that we rather invoke too many update tools then too
* few . */
if ( ! path_is_absolute ( c - > parameter ) )
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
p = strappenda ( c - > parameter , " /.updated " ) ;
if ( lstat ( p , & other ) < 0 )
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
if ( lstat ( " /usr/ " , & usr ) < 0 )
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
2014-11-06 04:02:13 +03:00
return usr . st_mtim . tv_sec > other . st_mtim . tv_sec | |
( usr . st_mtim . tv_sec = = other . st_mtim . tv_sec & & usr . st_mtim . tv_nsec > other . st_mtim . tv_nsec ) ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_first_boot ( Condition * c ) {
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_FIRST_BOOT ) ;
r = parse_boolean ( c - > parameter ) ;
if ( r < 0 )
return r ;
2014-11-06 04:02:13 +03:00
return ( access ( " /run/systemd/first-boot " , F_OK ) > = 0 ) = = ! ! r ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_exists ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_EXISTS ) ;
2014-11-06 04:02:13 +03:00
return access ( c - > parameter , F_OK ) > = 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_exists_glob ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_EXISTS_GLOB ) ;
2014-11-06 04:02:13 +03:00
return glob_exists ( c - > parameter ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_directory ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_DIRECTORY ) ;
2014-11-06 04:02:13 +03:00
return is_dir ( c - > parameter , true ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_symbolic_link ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_SYMBOLIC_LINK ) ;
2014-11-06 04:02:13 +03:00
return is_symlink ( c - > parameter ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_mount_point ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_MOUNT_POINT ) ;
2014-11-06 04:02:13 +03:00
return path_is_mount_point ( c - > parameter , true ) > 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_path_is_read_write ( Condition * c ) {
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_PATH_IS_READ_WRITE ) ;
2014-11-06 04:02:13 +03:00
return path_is_read_only_fs ( c - > parameter ) < = 0 ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_directory_not_empty ( Condition * c ) {
int r ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_DIRECTORY_NOT_EMPTY ) ;
r = dir_is_empty ( c - > parameter ) ;
2014-11-06 04:02:13 +03:00
return r < = 0 & & r ! = - ENOENT ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_file_not_empty ( Condition * c ) {
struct stat st ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_FILE_NOT_EMPTY ) ;
return ( stat ( c - > parameter , & st ) > = 0 & &
S_ISREG ( st . st_mode ) & &
2014-11-06 04:02:13 +03:00
st . st_size > 0 ) ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_file_is_executable ( Condition * c ) {
struct stat st ;
assert ( c ) ;
assert ( c - > parameter ) ;
assert ( c - > type = = CONDITION_FILE_IS_EXECUTABLE ) ;
return ( stat ( c - > parameter , & st ) > = 0 & &
S_ISREG ( st . st_mode ) & &
2014-11-06 04:02:13 +03:00
( st . st_mode & 0111 ) ) ;
2014-11-06 03:40:37 +03:00
}
static int condition_test_null ( Condition * c ) {
assert ( c ) ;
assert ( c - > type = = CONDITION_NULL ) ;
/* Note that during parsing we already evaluate the string and
* store it in c - > negate */
2014-11-06 04:02:13 +03:00
return true ;
2014-11-06 03:40:37 +03:00
}
int condition_test ( Condition * c ) {
static int ( * const condition_tests [ _CONDITION_TYPE_MAX ] ) ( Condition * c ) = {
[ CONDITION_PATH_EXISTS ] = condition_test_path_exists ,
[ CONDITION_PATH_EXISTS_GLOB ] = condition_test_path_exists_glob ,
[ CONDITION_PATH_IS_DIRECTORY ] = condition_test_path_is_directory ,
[ CONDITION_PATH_IS_SYMBOLIC_LINK ] = condition_test_path_is_symbolic_link ,
[ CONDITION_PATH_IS_MOUNT_POINT ] = condition_test_path_is_mount_point ,
[ CONDITION_PATH_IS_READ_WRITE ] = condition_test_path_is_read_write ,
[ CONDITION_DIRECTORY_NOT_EMPTY ] = condition_test_directory_not_empty ,
[ CONDITION_FILE_NOT_EMPTY ] = condition_test_file_not_empty ,
[ CONDITION_FILE_IS_EXECUTABLE ] = condition_test_file_is_executable ,
[ CONDITION_KERNEL_COMMAND_LINE ] = condition_test_kernel_command_line ,
[ CONDITION_VIRTUALIZATION ] = condition_test_virtualization ,
[ CONDITION_SECURITY ] = condition_test_security ,
[ CONDITION_CAPABILITY ] = condition_test_capability ,
[ CONDITION_HOST ] = condition_test_host ,
[ CONDITION_AC_POWER ] = condition_test_ac_power ,
[ CONDITION_ARCHITECTURE ] = condition_test_architecture ,
[ CONDITION_NEEDS_UPDATE ] = condition_test_needs_update ,
[ CONDITION_FIRST_BOOT ] = condition_test_first_boot ,
[ CONDITION_NULL ] = condition_test_null ,
} ;
2014-11-06 04:27:10 +03:00
int r , b ;
2014-11-06 03:40:37 +03:00
assert ( c ) ;
assert ( c - > type > = 0 ) ;
assert ( c - > type < _CONDITION_TYPE_MAX ) ;
2014-11-06 04:02:13 +03:00
r = condition_tests [ c - > type ] ( c ) ;
2014-11-06 04:27:10 +03:00
if ( r < 0 ) {
c - > result = CONDITION_ERROR ;
2014-11-06 04:02:13 +03:00
return r ;
2014-11-06 04:27:10 +03:00
}
2014-11-06 04:02:13 +03:00
2014-11-06 04:27:10 +03:00
b = ( r > 0 ) = = ! c - > negate ;
c - > result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED ;
return b ;
2014-11-06 03:40:37 +03:00
}
2014-11-06 15:43:45 +03:00
void condition_dump ( Condition * c , FILE * f , const char * prefix , const char * ( * to_string ) ( ConditionType t ) ) {
2014-02-20 22:26:54 +04:00
assert ( c ) ;
assert ( f ) ;
if ( ! prefix )
prefix = " " ;
fprintf ( f ,
" %s \t %s: %s%s%s %s \n " ,
prefix ,
2014-11-06 15:43:45 +03:00
to_string ( c - > type ) ,
2014-02-20 22:26:54 +04:00
c - > trigger ? " | " : " " ,
c - > negate ? " ! " : " " ,
c - > parameter ,
2014-11-06 04:27:10 +03:00
condition_result_to_string ( c - > result ) ) ;
2014-02-20 22:26:54 +04:00
}
2014-11-06 15:43:45 +03:00
void condition_dump_list ( Condition * first , FILE * f , const char * prefix , const char * ( * to_string ) ( ConditionType t ) ) {
2014-02-20 22:26:54 +04:00
Condition * c ;
LIST_FOREACH ( conditions , c , first )
2014-11-06 15:43:45 +03:00
condition_dump ( c , f , prefix , to_string ) ;
2014-02-20 22:26:54 +04:00
}
static const char * const condition_type_table [ _CONDITION_TYPE_MAX ] = {
2014-11-06 15:56:22 +03:00
[ CONDITION_ARCHITECTURE ] = " ConditionArchitecture " ,
[ CONDITION_VIRTUALIZATION ] = " ConditionVirtualization " ,
[ CONDITION_HOST ] = " ConditionHost " ,
[ CONDITION_KERNEL_COMMAND_LINE ] = " ConditionKernelCommandLine " ,
[ CONDITION_SECURITY ] = " ConditionSecurity " ,
[ CONDITION_CAPABILITY ] = " ConditionCapability " ,
[ CONDITION_AC_POWER ] = " ConditionACPower " ,
[ CONDITION_NEEDS_UPDATE ] = " ConditionNeedsUpdate " ,
[ CONDITION_FIRST_BOOT ] = " ConditionFirstBoot " ,
2014-02-20 22:26:54 +04:00
[ CONDITION_PATH_EXISTS ] = " ConditionPathExists " ,
[ CONDITION_PATH_EXISTS_GLOB ] = " ConditionPathExistsGlob " ,
[ CONDITION_PATH_IS_DIRECTORY ] = " ConditionPathIsDirectory " ,
[ CONDITION_PATH_IS_SYMBOLIC_LINK ] = " ConditionPathIsSymbolicLink " ,
[ CONDITION_PATH_IS_MOUNT_POINT ] = " ConditionPathIsMountPoint " ,
[ CONDITION_PATH_IS_READ_WRITE ] = " ConditionPathIsReadWrite " ,
[ CONDITION_DIRECTORY_NOT_EMPTY ] = " ConditionDirectoryNotEmpty " ,
[ CONDITION_FILE_NOT_EMPTY ] = " ConditionFileNotEmpty " ,
[ CONDITION_FILE_IS_EXECUTABLE ] = " ConditionFileIsExecutable " ,
[ CONDITION_NULL ] = " ConditionNull "
} ;
DEFINE_STRING_TABLE_LOOKUP ( condition_type , ConditionType ) ;
2014-11-06 04:27:10 +03:00
2014-11-06 15:43:45 +03:00
static const char * const assert_type_table [ _CONDITION_TYPE_MAX ] = {
2014-11-06 15:56:22 +03:00
[ CONDITION_ARCHITECTURE ] = " AssertArchitecture " ,
[ CONDITION_VIRTUALIZATION ] = " AssertVirtualization " ,
[ CONDITION_HOST ] = " AssertHost " ,
[ CONDITION_KERNEL_COMMAND_LINE ] = " AssertKernelCommandLine " ,
[ CONDITION_SECURITY ] = " AssertSecurity " ,
[ CONDITION_CAPABILITY ] = " AssertCapability " ,
[ CONDITION_AC_POWER ] = " AssertACPower " ,
[ CONDITION_NEEDS_UPDATE ] = " AssertNeedsUpdate " ,
[ CONDITION_FIRST_BOOT ] = " AssertFirstBoot " ,
2014-11-06 15:43:45 +03:00
[ CONDITION_PATH_EXISTS ] = " AssertPathExists " ,
[ CONDITION_PATH_EXISTS_GLOB ] = " AssertPathExistsGlob " ,
[ CONDITION_PATH_IS_DIRECTORY ] = " AssertPathIsDirectory " ,
[ CONDITION_PATH_IS_SYMBOLIC_LINK ] = " AssertPathIsSymbolicLink " ,
[ CONDITION_PATH_IS_MOUNT_POINT ] = " AssertPathIsMountPoint " ,
[ CONDITION_PATH_IS_READ_WRITE ] = " AssertPathIsReadWrite " ,
[ CONDITION_DIRECTORY_NOT_EMPTY ] = " AssertDirectoryNotEmpty " ,
[ CONDITION_FILE_NOT_EMPTY ] = " AssertFileNotEmpty " ,
[ CONDITION_FILE_IS_EXECUTABLE ] = " AssertFileIsExecutable " ,
[ CONDITION_NULL ] = " AssertNull "
} ;
DEFINE_STRING_TABLE_LOOKUP ( assert_type , ConditionType ) ;
2014-11-06 04:27:10 +03:00
static const char * const condition_result_table [ _CONDITION_RESULT_MAX ] = {
[ CONDITION_UNTESTED ] = " untested " ,
[ CONDITION_SUCCEEDED ] = " succeeded " ,
[ CONDITION_FAILED ] = " failed " ,
[ CONDITION_ERROR ] = " error " ,
} ;
DEFINE_STRING_TABLE_LOOKUP ( condition_result , ConditionResult ) ;