2012-09-19 17:00:34 +04:00
/*
2013-02-12 02:47:16 +04:00
* Copyright ( C ) 2011 - 2013 Red Hat , Inc .
2012-09-19 17:00:34 +04:00
*
* This library 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 .
*
* This library 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 this library ; if not , write to the Free Software
* License along with this library ; If not , see
* < http : //www.gnu.org/licenses/>.
*
*/
# include <config.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include <selinux/selinux.h>
# include <selinux/context.h>
# include "internal.h"
# include "testutils.h"
# include "testutilsqemu.h"
# include "qemu/qemu_domain.h"
# include "viralloc.h"
# include "virerror.h"
# include "virfile.h"
# include "virlog.h"
# include "security/security_manager.h"
2013-04-03 14:36:23 +04:00
# include "virstring.h"
2012-09-19 17:00:34 +04:00
# define VIR_FROM_THIS VIR_FROM_NONE
static virCapsPtr caps ;
2013-03-31 22:03:42 +04:00
static virDomainXMLOptionPtr xmlopt ;
2012-09-19 17:00:34 +04:00
static virSecurityManagerPtr mgr ;
typedef struct testSELinuxFile testSELinuxFile ;
struct testSELinuxFile {
char * file ;
char * context ;
} ;
static int
testSELinuxMungePath ( char * * path )
{
char * tmp ;
if ( virAsprintf ( & tmp , " %s/securityselinuxlabeldata%s " ,
2013-07-04 14:20:21 +04:00
abs_builddir , * path ) < 0 )
2012-09-19 17:00:34 +04:00
return - 1 ;
VIR_FREE ( * path ) ;
* path = tmp ;
return 0 ;
}
static int
testSELinuxLoadFileList ( const char * testname ,
testSELinuxFile * * files ,
size_t * nfiles )
{
int ret = - 1 ;
char * path = NULL ;
FILE * fp = NULL ;
2013-07-03 17:14:33 +04:00
char * line = NULL ;
2012-09-19 17:00:34 +04:00
* files = NULL ;
* nfiles = 0 ;
if ( virAsprintf ( & path , " %s/securityselinuxlabeldata/%s.txt " ,
2013-07-04 14:20:21 +04:00
abs_srcdir , testname ) < 0 )
2012-09-19 17:00:34 +04:00
goto cleanup ;
if ( ! ( fp = fopen ( path , " r " ) ) ) {
goto cleanup ;
}
2013-07-04 14:20:21 +04:00
if ( VIR_ALLOC_N ( line , 1024 ) < 0 )
2013-07-03 17:14:33 +04:00
goto cleanup ;
2012-09-19 17:00:34 +04:00
while ( ! feof ( fp ) ) {
2013-07-03 17:14:33 +04:00
char * file , * context , * tmp ;
2012-09-19 17:00:34 +04:00
if ( ! fgets ( line , 1024 , fp ) ) {
if ( ! feof ( fp ) )
goto cleanup ;
break ;
}
2013-07-03 17:14:33 +04:00
tmp = strchr ( line , ' ; ' ) ;
if ( ! tmp ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
" unexpected format for line '%s' " ,
line ) ;
goto cleanup ;
}
2012-09-19 17:00:34 +04:00
* tmp = ' \0 ' ;
tmp + + ;
2013-07-03 17:14:33 +04:00
if ( virAsprintf ( & file , " %s/securityselinuxlabeldata%s " ,
2013-07-04 14:20:21 +04:00
abs_builddir , line ) < 0 )
2012-09-19 17:00:34 +04:00
goto cleanup ;
if ( * tmp ! = ' \0 ' & & * tmp ! = ' \n ' ) {
2013-05-03 16:52:21 +04:00
if ( VIR_STRDUP ( context , tmp ) < 0 ) {
2012-09-19 17:00:34 +04:00
VIR_FREE ( file ) ;
goto cleanup ;
}
tmp = strchr ( context , ' \n ' ) ;
2013-07-03 17:14:33 +04:00
if ( tmp )
* tmp = ' \0 ' ;
2012-09-19 17:00:34 +04:00
} else {
context = NULL ;
}
2013-07-04 14:20:21 +04:00
if ( VIR_EXPAND_N ( * files , * nfiles , 1 ) < 0 )
2012-09-19 17:00:34 +04:00
goto cleanup ;
( * files ) [ ( * nfiles ) - 1 ] . file = file ;
( * files ) [ ( * nfiles ) - 1 ] . context = context ;
}
ret = 0 ;
cleanup :
VIR_FORCE_FCLOSE ( fp ) ;
VIR_FREE ( path ) ;
2013-07-03 17:14:33 +04:00
VIR_FREE ( line ) ;
2012-09-19 17:00:34 +04:00
return ret ;
}
static virDomainDefPtr
testSELinuxLoadDef ( const char * testname )
{
char * xmlfile = NULL ;
char * xmlstr = NULL ;
virDomainDefPtr def = NULL ;
size_t i ;
if ( virAsprintf ( & xmlfile , " %s/securityselinuxlabeldata/%s.xml " ,
2013-07-04 14:20:21 +04:00
abs_srcdir , testname ) < 0 )
2012-09-19 17:00:34 +04:00
goto cleanup ;
if ( virFileReadAll ( xmlfile , 1024 * 1024 , & xmlstr ) < 0 ) {
goto cleanup ;
}
2013-03-28 17:55:55 +04:00
if ( ! ( def = virDomainDefParseString ( xmlstr , caps , xmlopt ,
2012-09-19 17:00:34 +04:00
QEMU_EXPECTED_VIRT_TYPES ,
0 ) ) )
goto cleanup ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < def - > ndisks ; i + + ) {
2012-09-19 17:00:34 +04:00
if ( def - > disks [ i ] - > type ! = VIR_DOMAIN_DISK_TYPE_FILE & &
def - > disks [ i ] - > type ! = VIR_DOMAIN_DISK_TYPE_BLOCK )
continue ;
if ( testSELinuxMungePath ( & def - > disks [ i ] - > src ) < 0 )
goto cleanup ;
}
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < def - > nserials ; i + + ) {
2012-09-19 17:00:34 +04:00
if ( def - > serials [ i ] - > source . type ! = VIR_DOMAIN_CHR_TYPE_FILE & &
def - > serials [ i ] - > source . type ! = VIR_DOMAIN_CHR_TYPE_PIPE & &
def - > serials [ i ] - > source . type ! = VIR_DOMAIN_CHR_TYPE_DEV & &
def - > serials [ i ] - > source . type ! = VIR_DOMAIN_CHR_TYPE_UNIX )
continue ;
if ( def - > serials [ i ] - > source . type = = VIR_DOMAIN_CHR_TYPE_UNIX ) {
if ( testSELinuxMungePath ( & def - > serials [ i ] - > source . data . nix . path ) < 0 )
goto cleanup ;
} else {
if ( testSELinuxMungePath ( & def - > serials [ i ] - > source . data . file . path ) < 0 )
goto cleanup ;
}
}
if ( def - > os . kernel & &
testSELinuxMungePath ( & def - > os . kernel ) < 0 )
goto cleanup ;
if ( def - > os . initrd & &
testSELinuxMungePath ( & def - > os . initrd ) < 0 )
goto cleanup ;
cleanup :
VIR_FREE ( xmlfile ) ;
VIR_FREE ( xmlstr ) ;
return def ;
}
static int
testSELinuxCreateDisks ( testSELinuxFile * files , size_t nfiles )
{
size_t i ;
2013-08-14 00:19:14 +04:00
if ( virFileMakePath ( abs_builddir " /securityselinuxlabeldata/nfs " ) < 0 )
2012-09-19 17:00:34 +04:00
return - 1 ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < nfiles ; i + + ) {
2012-09-19 17:00:34 +04:00
if ( virFileTouch ( files [ i ] . file , 0600 ) < 0 )
return - 1 ;
}
return 0 ;
}
static int
testSELinuxDeleteDisks ( testSELinuxFile * files , size_t nfiles )
{
size_t i ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < nfiles ; i + + ) {
2012-09-19 17:00:34 +04:00
if ( unlink ( files [ i ] . file ) < 0 )
return - 1 ;
}
2013-08-14 00:19:14 +04:00
if ( rmdir ( abs_builddir " /securityselinuxlabeldata/nfs " ) < 0 )
return - 1 ;
/* Ignore failure to remove non-empty directory with in-tree build */
rmdir ( abs_builddir " /securityselinuxlabeldata " ) ;
2012-09-19 17:00:34 +04:00
return 0 ;
}
static int
testSELinuxCheckLabels ( testSELinuxFile * files , size_t nfiles )
{
size_t i ;
security_context_t ctx ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < nfiles ; i + + ) {
2013-08-14 00:19:14 +04:00
ctx = NULL ;
2012-09-19 17:00:34 +04:00
if ( getfilecon ( files [ i ] . file , & ctx ) < 0 ) {
if ( errno = = ENODATA ) {
2013-08-14 00:19:14 +04:00
/* nothing to do */
} else if ( errno = = EOPNOTSUPP ) {
if ( VIR_STRDUP ( ctx , " EOPNOTSUPP " ) < 0 )
return - 1 ;
2012-09-19 17:00:34 +04:00
} else {
virReportSystemError ( errno ,
" Cannot read label on %s " ,
files [ i ] . file ) ;
return - 1 ;
}
}
if ( ! STREQ_NULLABLE ( files [ i ] . context , ctx ) ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
" File %s context '%s' did not match epected '%s' " ,
files [ i ] . file , ctx , files [ i ] . context ) ;
2013-08-14 00:19:14 +04:00
VIR_FREE ( ctx ) ;
2012-09-19 17:00:34 +04:00
return - 1 ;
}
2013-08-14 00:19:14 +04:00
VIR_FREE ( ctx ) ;
2012-09-19 17:00:34 +04:00
}
return 0 ;
}
static int
testSELinuxLabeling ( const void * opaque )
{
const char * testname = opaque ;
int ret = - 1 ;
testSELinuxFile * files = NULL ;
size_t nfiles = 0 ;
size_t i ;
virDomainDefPtr def = NULL ;
if ( testSELinuxLoadFileList ( testname , & files , & nfiles ) < 0 )
goto cleanup ;
if ( testSELinuxCreateDisks ( files , nfiles ) < 0 )
goto cleanup ;
if ( ! ( def = testSELinuxLoadDef ( testname ) ) )
goto cleanup ;
if ( virSecurityManagerSetAllLabel ( mgr , def , NULL ) < 0 )
goto cleanup ;
if ( testSELinuxCheckLabels ( files , nfiles ) < 0 )
goto cleanup ;
ret = 0 ;
cleanup :
if ( testSELinuxDeleteDisks ( files , nfiles ) < 0 )
2013-08-14 00:19:14 +04:00
VIR_WARN ( " unable to fully clean up " ) ;
2012-09-19 17:00:34 +04:00
virDomainDefFree ( def ) ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < nfiles ; i + + ) {
2012-09-19 17:00:34 +04:00
VIR_FREE ( files [ i ] . file ) ;
VIR_FREE ( files [ i ] . context ) ;
}
VIR_FREE ( files ) ;
2013-01-14 22:33:44 +04:00
if ( ret < 0 & & virTestGetVerbose ( ) ) {
virErrorPtr err = virGetLastError ( ) ;
fprintf ( stderr , " %s \n " , err ? err - > message : " <unknown> " ) ;
}
2012-09-19 17:00:34 +04:00
return ret ;
}
static int
mymain ( void )
{
int ret = 0 ;
if ( ! ( mgr = virSecurityManagerNew ( " selinux " , " QEMU " , false , true , false ) ) ) {
virErrorPtr err = virGetLastError ( ) ;
if ( err - > code = = VIR_ERR_CONFIG_UNSUPPORTED )
2013-02-23 02:42:39 +04:00
return EXIT_AM_SKIP ;
2012-09-19 17:00:34 +04:00
fprintf ( stderr , " Unable to initialize security driver: %s \n " ,
err - > message ) ;
2013-02-23 02:42:39 +04:00
return EXIT_FAILURE ;
2012-09-19 17:00:34 +04:00
}
if ( ( caps = testQemuCapsInit ( ) ) = = NULL )
2013-02-23 02:42:39 +04:00
return EXIT_FAILURE ;
2012-09-19 17:00:34 +04:00
2013-03-11 13:24:29 +04:00
if ( ! ( xmlopt = virQEMUDriverCreateXMLConf ( NULL ) ) )
2013-03-05 19:17:24 +04:00
return EXIT_FAILURE ;
2012-09-19 17:00:34 +04:00
# define DO_TEST_LABELING(name) \
if ( virtTestRun ( " Labelling " # name , 1 , testSELinuxLabeling , name ) < 0 ) \
ret = - 1 ; \
setcon ( ( security_context_t ) " system_r:system_u:libvirtd_t:s0:c0.c1023 " ) ;
DO_TEST_LABELING ( " disks " ) ;
DO_TEST_LABELING ( " kernel " ) ;
DO_TEST_LABELING ( " chardev " ) ;
2013-08-14 00:19:14 +04:00
DO_TEST_LABELING ( " nfs " ) ;
2012-09-19 17:00:34 +04:00
return ( ret = = 0 ) ? EXIT_SUCCESS : EXIT_FAILURE ;
}
VIRT_TEST_MAIN_PRELOAD ( mymain , abs_builddir " /.libs/libsecurityselinuxhelper.so " )