2010-08-14 21:59:25 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2010-08-12 00:58:34 +04:00
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 02:20:58 +04:00
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
2010-08-12 00:58:34 +04:00
( 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
2012-04-12 02:20:58 +04:00
Lesser General Public License for more details .
2010-08-12 00:58:34 +04:00
2012-04-12 02:20:58 +04:00
You should have received a copy of the GNU Lesser General Public License
2010-08-12 00:58:34 +04:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <sys/stat.h>
# include <unistd.h>
2011-07-27 23:47:25 +04:00
# include <malloc.h>
2011-08-31 03:02:00 +04:00
# include <sys/socket.h>
# include <sys/un.h>
2010-08-12 00:58:34 +04:00
# include "label.h"
# include "util.h"
2012-05-07 23:36:12 +04:00
# include "path-util.h"
2010-08-12 00:58:34 +04:00
# ifdef HAVE_SELINUX
# include <selinux/selinux.h>
# include <selinux/label.h>
static struct selabel_handle * label_hnd = NULL ;
2011-08-30 01:36:10 +04:00
static int use_selinux_cached = - 1 ;
2010-08-12 00:58:34 +04:00
static inline bool use_selinux ( void ) {
2011-07-29 03:48:18 +04:00
if ( use_selinux_cached < 0 )
use_selinux_cached = is_selinux_enabled ( ) > 0 ;
2010-08-12 00:58:34 +04:00
2011-07-29 03:48:18 +04:00
return use_selinux_cached ;
2010-08-12 00:58:34 +04:00
}
2011-08-30 01:36:10 +04:00
void label_retest_selinux ( void ) {
use_selinux_cached = - 1 ;
}
2010-08-12 00:58:34 +04:00
# endif
2012-05-31 00:13:48 +04:00
int label_init ( const char * prefixes [ ] ) {
2010-08-12 00:58:34 +04:00
int r = 0 ;
# ifdef HAVE_SELINUX
2011-07-29 01:39:29 +04:00
usec_t before_timestamp , after_timestamp ;
struct mallinfo before_mallinfo , after_mallinfo ;
2010-08-12 00:58:34 +04:00
if ( ! use_selinux ( ) )
return 0 ;
2010-10-27 07:47:02 +04:00
if ( label_hnd )
return 0 ;
2011-07-29 01:39:29 +04:00
before_mallinfo = mallinfo ( ) ;
before_timestamp = now ( CLOCK_MONOTONIC ) ;
2011-07-27 23:47:25 +04:00
2012-05-31 00:13:48 +04:00
if ( prefixes ) {
2012-04-17 18:05:03 +04:00
struct selinux_opt options [ ] = {
2012-05-31 00:13:48 +04:00
{ . type = SELABEL_OPT_SUBSET , . values = prefixes } ,
2012-04-17 18:05:03 +04:00
} ;
label_hnd = selabel_open ( SELABEL_CTX_FILE , options , ELEMENTSOF ( options ) ) ;
} else
label_hnd = selabel_open ( SELABEL_CTX_FILE , NULL , 0 ) ;
2010-08-12 00:58:34 +04:00
if ( ! label_hnd ) {
log_full ( security_getenforce ( ) = = 1 ? LOG_ERR : LOG_DEBUG ,
" Failed to initialize SELinux context: %m " ) ;
2010-08-20 04:27:05 +04:00
r = security_getenforce ( ) = = 1 ? - errno : 0 ;
2011-07-25 23:22:57 +04:00
} else {
2011-07-29 01:39:29 +04:00
char timespan [ FORMAT_TIMESPAN_MAX ] ;
2011-07-27 23:47:25 +04:00
int l ;
2011-07-25 23:22:57 +04:00
2011-07-29 01:39:29 +04:00
after_timestamp = now ( CLOCK_MONOTONIC ) ;
after_mallinfo = mallinfo ( ) ;
2011-07-25 23:22:57 +04:00
2011-07-29 01:39:29 +04:00
l = after_mallinfo . uordblks > before_mallinfo . uordblks ? after_mallinfo . uordblks - before_mallinfo . uordblks : 0 ;
2011-07-27 23:47:25 +04:00
2012-05-31 03:10:44 +04:00
log_debug ( " Successfully loaded SELinux database in %s, size on heap is %iK. " ,
format_timespan ( timespan , sizeof ( timespan ) , after_timestamp - before_timestamp ) ,
( l + 1023 ) / 1024 ) ;
2011-07-27 23:47:25 +04:00
}
2010-08-12 00:58:34 +04:00
# endif
return r ;
}
2011-02-25 03:47:31 +03:00
int label_fix ( const char * path , bool ignore_enoent ) {
2010-08-12 00:58:34 +04:00
int r = 0 ;
# ifdef HAVE_SELINUX
struct stat st ;
security_context_t fcon ;
if ( ! use_selinux ( ) | | ! label_hnd )
return 0 ;
r = lstat ( path , & st ) ;
if ( r = = 0 ) {
r = selabel_lookup_raw ( label_hnd , & fcon , path , st . st_mode ) ;
2010-10-19 00:47:57 +04:00
/* If there's no label to set, then exit without warning */
if ( r < 0 & & errno = = ENOENT )
return 0 ;
2010-08-12 00:58:34 +04:00
if ( r = = 0 ) {
2011-12-20 02:55:29 +04:00
r = lsetfilecon ( path , fcon ) ;
2010-08-12 00:58:34 +04:00
freecon ( fcon ) ;
2010-10-19 21:35:35 +04:00
/* If the FS doesn't support labels, then exit without warning */
if ( r < 0 & & errno = = ENOTSUP )
return 0 ;
2010-08-12 00:58:34 +04:00
}
}
2010-10-19 00:47:57 +04:00
2010-08-12 00:58:34 +04:00
if ( r < 0 ) {
2011-04-28 07:06:48 +04:00
/* Ignore ENOENT in some cases */
if ( ignore_enoent & & errno = = ENOENT )
return 0 ;
2010-08-12 00:58:34 +04:00
log_full ( security_getenforce ( ) = = 1 ? LOG_ERR : LOG_DEBUG ,
" Unable to fix label of %s: %m " , path ) ;
2010-08-20 04:27:05 +04:00
r = security_getenforce ( ) = = 1 ? - errno : 0 ;
2010-08-12 00:58:34 +04:00
}
# endif
return r ;
}
void label_finish ( void ) {
# ifdef HAVE_SELINUX
if ( use_selinux ( ) & & label_hnd )
selabel_close ( label_hnd ) ;
# endif
}
2011-07-29 01:39:29 +04:00
int label_get_create_label_from_exe ( const char * exe , char * * label ) {
2010-08-12 00:58:34 +04:00
int r = 0 ;
# ifdef HAVE_SELINUX
security_context_t mycon = NULL , fcon = NULL ;
security_class_t sclass ;
if ( ! use_selinux ( ) ) {
* label = NULL ;
return 0 ;
}
r = getcon ( & mycon ) ;
if ( r < 0 )
goto fail ;
r = getfilecon ( exe , & fcon ) ;
if ( r < 0 )
goto fail ;
sclass = string_to_security_class ( " process " ) ;
r = security_compute_create ( mycon , fcon , sclass , ( security_context_t * ) label ) ;
if ( r = = 0 )
log_debug ( " SELinux Socket context for %s will be set to %s " , exe , * label ) ;
fail :
if ( r < 0 & & security_getenforce ( ) = = 1 )
r = - errno ;
freecon ( mycon ) ;
freecon ( fcon ) ;
# endif
return r ;
}
2012-04-17 18:05:03 +04:00
int label_context_set ( const char * path , mode_t mode ) {
2010-10-27 07:47:48 +04:00
int r = 0 ;
# ifdef HAVE_SELINUX
security_context_t filecon = NULL ;
if ( ! use_selinux ( ) | | ! label_hnd )
return 0 ;
2012-04-17 18:05:03 +04:00
r = selabel_lookup_raw ( label_hnd , & filecon , path , mode ) ;
2011-08-31 03:21:02 +04:00
if ( r < 0 )
r = - errno ;
else if ( r = = 0 ) {
r = setfscreatecon ( filecon ) ;
if ( r < 0 ) {
2010-10-27 07:47:48 +04:00
log_error ( " Failed to set SELinux file context on %s: %m " , path ) ;
r = - errno ;
}
freecon ( filecon ) ;
}
if ( r < 0 & & security_getenforce ( ) = = 0 )
r = 0 ;
# endif
return r ;
}
2010-08-12 00:58:34 +04:00
int label_socket_set ( const char * label ) {
# ifdef HAVE_SELINUX
if ( ! use_selinux ( ) )
return 0 ;
if ( setsockcreatecon ( ( security_context_t ) label ) < 0 ) {
log_full ( security_getenforce ( ) = = 1 ? LOG_ERR : LOG_DEBUG ,
" Failed to set SELinux context (%s) on socket: %m " , label ) ;
if ( security_getenforce ( ) = = 1 )
return - errno ;
}
# endif
return 0 ;
}
2012-04-17 18:05:03 +04:00
void label_context_clear ( void ) {
2010-08-12 00:58:34 +04:00
# ifdef HAVE_SELINUX
if ( ! use_selinux ( ) )
return ;
setfscreatecon ( NULL ) ;
# endif
}
void label_socket_clear ( void ) {
# ifdef HAVE_SELINUX
if ( ! use_selinux ( ) )
return ;
setsockcreatecon ( NULL ) ;
# endif
}
void label_free ( const char * label ) {
# ifdef HAVE_SELINUX
if ( ! use_selinux ( ) )
return ;
freecon ( ( security_context_t ) label ) ;
# endif
}
2011-08-31 03:02:00 +04:00
int label_mkdir ( const char * path , mode_t mode ) {
2010-08-12 00:58:34 +04:00
/* Creates a directory and labels it according to the SELinux policy */
# ifdef HAVE_SELINUX
int r ;
security_context_t fcon = NULL ;
2011-09-01 03:16:55 +04:00
if ( ! use_selinux ( ) | | ! label_hnd )
2011-08-31 03:02:00 +04:00
goto skipped ;
2010-08-12 00:58:34 +04:00
2011-08-31 03:02:00 +04:00
if ( path_is_absolute ( path ) )
2011-08-31 03:21:02 +04:00
r = selabel_lookup_raw ( label_hnd , & fcon , path , S_IFDIR ) ;
2011-08-31 03:02:00 +04:00
else {
char * newpath ;
2010-08-12 00:58:34 +04:00
2011-08-31 03:02:00 +04:00
newpath = path_make_absolute_cwd ( path ) ;
if ( ! newpath )
return - ENOMEM ;
2010-08-12 00:58:34 +04:00
2011-08-31 03:21:02 +04:00
r = selabel_lookup_raw ( label_hnd , & fcon , newpath , S_IFDIR ) ;
2011-08-31 03:02:00 +04:00
free ( newpath ) ;
}
if ( r = = 0 )
r = setfscreatecon ( fcon ) ;
2010-08-12 00:58:34 +04:00
2011-08-31 03:02:00 +04:00
if ( r < 0 & & errno ! = ENOENT ) {
log_error ( " Failed to set security context %s for %s: %m " , fcon , path ) ;
2010-08-12 00:58:34 +04:00
2011-08-31 03:02:00 +04:00
if ( security_getenforce ( ) = = 1 ) {
2010-08-12 00:58:34 +04:00
r = - errno ;
2011-08-31 03:02:00 +04:00
goto finish ;
2010-08-12 00:58:34 +04:00
}
}
2011-08-31 03:02:00 +04:00
r = mkdir ( path , mode ) ;
if ( r < 0 )
2010-08-12 00:58:34 +04:00
r = - errno ;
finish :
2011-08-31 03:02:00 +04:00
setfscreatecon ( NULL ) ;
freecon ( fcon ) ;
return r ;
skipped :
# endif
return mkdir ( path , mode ) < 0 ? - errno : 0 ;
}
int label_bind ( int fd , const struct sockaddr * addr , socklen_t addrlen ) {
/* Binds a socket and label its file system object according to the SELinux policy */
# ifdef HAVE_SELINUX
int r ;
security_context_t fcon = NULL ;
const struct sockaddr_un * un ;
char * path = NULL ;
assert ( fd > = 0 ) ;
assert ( addr ) ;
assert ( addrlen > = sizeof ( sa_family_t ) ) ;
if ( ! use_selinux ( ) | | ! label_hnd )
goto skipped ;
/* Filter out non-local sockets */
if ( addr - > sa_family ! = AF_UNIX )
goto skipped ;
/* Filter out anonymous sockets */
if ( addrlen < sizeof ( sa_family_t ) + 1 )
goto skipped ;
/* Filter out abstract namespace sockets */
un = ( const struct sockaddr_un * ) addr ;
if ( un - > sun_path [ 0 ] = = 0 )
goto skipped ;
path = strndup ( un - > sun_path , addrlen - offsetof ( struct sockaddr_un , sun_path ) ) ;
if ( ! path )
return - ENOMEM ;
if ( path_is_absolute ( path ) )
2011-08-31 03:21:02 +04:00
r = selabel_lookup_raw ( label_hnd , & fcon , path , S_IFSOCK ) ;
2011-08-31 03:02:00 +04:00
else {
char * newpath ;
newpath = path_make_absolute_cwd ( path ) ;
if ( ! newpath ) {
free ( path ) ;
return - ENOMEM ;
}
2011-08-31 03:21:02 +04:00
r = selabel_lookup_raw ( label_hnd , & fcon , newpath , S_IFSOCK ) ;
2011-08-31 03:02:00 +04:00
free ( newpath ) ;
2010-08-12 00:58:34 +04:00
}
2011-08-31 03:02:00 +04:00
if ( r = = 0 )
r = setfscreatecon ( fcon ) ;
if ( r < 0 & & errno ! = ENOENT ) {
log_error ( " Failed to set security context %s for %s: %m " , fcon , path ) ;
if ( security_getenforce ( ) = = 1 ) {
r = - errno ;
goto finish ;
}
}
r = bind ( fd , addr , addrlen ) ;
if ( r < 0 )
r = - errno ;
finish :
setfscreatecon ( NULL ) ;
freecon ( fcon ) ;
free ( path ) ;
2010-08-12 00:58:34 +04:00
return r ;
2011-08-31 03:02:00 +04:00
skipped :
2010-08-12 00:58:34 +04:00
# endif
2011-08-31 03:02:00 +04:00
return bind ( fd , addr , addrlen ) < 0 ? - errno : 0 ;
2010-08-12 00:58:34 +04:00
}