2006-11-06 17:05:00 +01:00
'''
This is the web interface for a fileserver managing encrypted filesystems .
'''
# check python version
import sys
( ver_major , ver_minor , ver_sub , ver_desc , ver_subsub ) = sys . version_info
if ( ver_major < 2 ) or ( ( ver_major == 2 ) and ( ver_minor < 4 ) ) :
sys . stderr . write ( " You need a python version >= 2.4 \n Current version is: \n %s \n " % sys . version )
sys . exit ( 1 )
2006-11-23 20:13:08 +01:00
import cryptobox . core . container as cbxContainer
from cryptobox . core . exceptions import *
2006-11-06 17:05:00 +01:00
import re
import os
2006-11-23 20:13:08 +01:00
import cryptobox . core . tools as cbxTools
2006-11-06 17:05:00 +01:00
import subprocess
2006-11-23 20:13:08 +01:00
VERSION = " 0.3~1 "
2006-11-06 17:05:00 +01:00
class CryptoBox :
''' this class rules them all!
2006-11-24 12:02:02 +01:00
put things like logging , conf and other stuff in here ,
2006-11-06 17:05:00 +01:00
that might be used by more classes , it will be passed on to them '''
def __init__ ( self , config_file = None ) :
2006-11-23 20:13:08 +01:00
import cryptobox . core . settings as cbxSettings
2006-11-06 17:05:00 +01:00
self . log = self . __getStartupLogger ( )
2006-11-23 20:13:08 +01:00
self . prefs = cbxSettings . CryptoBoxSettings ( config_file )
2006-11-06 17:05:00 +01:00
self . __runTests ( )
def __getStartupLogger ( self ) :
import logging
''' initialises the logging system
use it with : ' self.log.[debug|info|warning|error|critical](logmessage) '
all classes should get the logging instance during __init__ :
self . log = logging . getLogger ( " CryptoBox " )
first we output all warnings / errors to stderr
as soon as we opened the config file successfully , we redirect debug output
to the configured destination '''
## basicConfig(...) needs python >= 2.4
try :
log_handler = logging . getLogger ( " CryptoBox " )
logging . basicConfig (
format = ' %(asctime)s CryptoBox %(levelname)s : %(message)s ' ,
stderr = sys . stderr )
log_handler . setLevel ( logging . ERROR )
log_handler . info ( " loggingsystem is up ' n running " )
## from now on everything can be logged via self.log...
except :
raise CBEnvironmentError ( " couldn ' t initialise the loggingsystem. I give up. " )
return log_handler
# do some initial checks
def __runTests ( self ) :
self . __runTestUID ( )
self . __runTestRootPriv ( )
def __runTestUID ( self ) :
if os . geteuid ( ) == 0 :
raise CBEnvironmentError ( " you may not run the cryptobox as root " )
def __runTestRootPriv ( self ) :
""" try to run ' super ' with ' CryptoBoxRootActions ' """
try :
devnull = open ( os . devnull , " w " )
except IOError :
raise CBEnvironmentError ( " could not open %s for writing! " % os . devnull )
try :
prog_super = self . prefs [ " Programs " ] [ " super " ]
except KeyError :
raise CBConfigUndefinedError ( " Programs " , " super " )
try :
prog_rootactions = self . prefs [ " Programs " ] [ " CryptoBoxRootActions " ]
except KeyError :
raise CBConfigUndefinedError ( " Programs " , " CryptoBoxRootActions " )
try :
proc = subprocess . Popen (
shell = False ,
stdout = devnull ,
stderr = devnull ,
args = [ prog_super , prog_rootactions , " check " ] )
except OSError :
raise CBEnvironmentError ( " failed to execute ' super ' ( %s ) " % self . prefs [ " Programs " ] [ " super " ] )
proc . wait ( )
if proc . returncode != 0 :
raise CBEnvironmentError ( " failed to call CryptoBoxRootActions ( %s ) via ' super ' - maybe you did not add the appropriate line to /etc/super.tab? " % prog_rootactions )
# this method just demonstrates inheritance effects - may be removed
def cbx_inheritance_test ( self , string = " you lucky widow " ) :
self . log . info ( string )
# RFC: why should CryptoBoxProps inherit CryptoBox? [l]
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
class CryptoBoxProps ( CryptoBox ) :
''' Get and set the properties of a CryptoBox
This class contains all available devices that may be accessed .
All properties of the cryptobox can be accessed by this class .
'''
def __init__ ( self , config_file = None ) :
''' read config and fill class variables '''
CryptoBox . __init__ ( self , config_file )
self . reReadContainerList ( )
def reReadContainerList ( self ) :
self . log . debug ( " rereading container list " )
self . containers = [ ]
2006-11-23 20:13:08 +01:00
for device in cbxTools . getAvailablePartitions ( ) :
2006-11-06 17:05:00 +01:00
if self . isDeviceAllowed ( device ) and not self . isConfigPartition ( device ) :
2006-11-23 20:13:08 +01:00
self . containers . append ( cbxContainer . CryptoBoxContainer ( device , self ) )
2006-11-06 17:05:00 +01:00
## sort by container name
self . containers . sort ( cmp = lambda x , y : x . getName ( ) < y . getName ( ) and - 1 or 1 )
def isConfigPartition ( self , device ) :
proc = subprocess . Popen (
shell = False ,
stdout = subprocess . PIPE ,
args = [
self . prefs [ " Programs " ] [ " blkid " ] ,
" -c " , os . path . devnull ,
" -o " , " value " ,
" -s " , " LABEL " ,
device ] )
( output , error ) = proc . communicate ( )
return output . strip ( ) == self . prefs [ " Main " ] [ " ConfigVolumeLabel " ]
def isDeviceAllowed ( self , devicename ) :
" check if a device is white-listed for being used as cryptobox containers "
import types
allowed = self . prefs [ " Main " ] [ " AllowedDevices " ]
if type ( allowed ) == types . StringType : allowed = [ allowed ]
for a_dev in allowed :
" remove double dots and so on ... "
real_device = os . path . realpath ( devicename )
if a_dev and re . search ( ' ^ ' + a_dev , real_device ) : return True
return False
def getLogData ( self , lines = None , maxSize = None ) :
""" get the most recent log entries of the cryptobox
the maximum number and size of these entries can be limited by ' lines ' and ' maxSize '
"""
# return nothing if the currently selected log output is not a file
try :
if self . prefs [ " Log " ] [ " Destination " ] . upper ( ) != " FILE " : return [ ]
log_file = self . prefs [ " Log " ] [ " Details " ]
except KeyError :
self . log . error ( " could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details " )
return [ ]
try :
fd = open ( log_file , " r " )
if maxSize : fd . seek ( - maxSize , 2 ) # seek relative to the end of the file
content = fd . readlines ( )
fd . close ( )
except IOError :
self . log . warn ( " failed to read the log file ( %s ) " % log_file )
return [ ]
if lines : content = content [ - lines : ]
content . reverse ( )
return content
def getContainerList ( self , filterType = None , filterName = None ) :
" retrieve the list of all containers of this cryptobox "
try :
result = self . containers [ : ]
if filterType != None :
2006-11-24 12:02:02 +01:00
if filterType in range ( len ( cbxContainer . ContainerTypes ) ) :
2006-11-06 17:05:00 +01:00
return [ e for e in self . containers if e . getType ( ) == filterType ]
else :
self . log . info ( " invalid filterType ( %d ) " % filterType )
result . clear ( )
if filterName != None :
result = [ e for e in self . containers if e . getName ( ) == filterName ]
return result
except AttributeError :
return [ ]
def getContainer ( self , device ) :
" retrieve the container element for this device "
all = [ e for e in self . getContainerList ( ) if e . device == device ]
if all :
return all [ 0 ]
else :
return None
2006-11-09 13:00:52 +01:00
def sendEventNotification ( self , event , event_infos ) :
2006-11-23 20:13:08 +01:00
""" call all available scripts in the event directory with some event information """
event_dir = self . prefs [ " Locations " ] [ " EventDir " ]
for fname in os . listdir ( event_dir ) :
real_fname = os . path . join ( event_dir , fname )
2006-11-09 13:00:52 +01:00
if os . path . isfile ( real_fname ) and os . access ( real_fname , os . X_OK ) :
cmd_args = [ self . prefs [ " Programs " ] [ " super " ] ,
self . prefs [ " Programs " ] [ " CryptoBoxRootActions " ] ,
2006-11-23 20:13:08 +01:00
" event " , real_fname , event ]
2006-11-09 13:00:52 +01:00
cmd_args . extend ( event_infos )
proc = subprocess . Popen (
shell = False ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
args = cmd_args )
( stdout , stderr ) = proc . communicate ( )
if proc . returncode != 0 :
2006-11-23 20:13:08 +01:00
self . log . warn ( " an event script ( %s ) failed (exitcode= %d ) to handle an event ( %s ): %s " % ( real_fname , proc . returncode , event , stderr . strip ( ) ) )
2006-11-09 13:00:52 +01:00
else :
self . log . info ( " event handler ( %s ) finished successfully: %s " % ( real_fname , event ) )
2006-11-06 17:05:00 +01:00
if __name__ == " __main__ " :
cb = CryptoBoxProps ( )