2006-08-23 15:27:25 +02:00
#!/usr/bin/env python2.4
2006-08-16 13:07:57 +02:00
'''
2006-08-17 12:38:05 +02:00
This is the web interface for a fileserver managing encrypted filesystems .
2006-08-16 20:17:16 +02:00
2006-08-16 13:07:57 +02:00
It was originally written in bash / perl . Now a complete rewrite is in
progress . So things might be confusing here . Hopefully not for long .
: )
'''
2006-08-16 20:17:16 +02:00
2006-08-23 15:27:25 +02:00
# 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-08-16 13:07:57 +02:00
import CryptoBoxContainer
2006-09-08 13:02:27 +02:00
from CryptoBoxExceptions import *
2006-08-16 20:17:16 +02:00
import re
import os
2006-08-17 12:38:05 +02:00
2006-08-21 09:41:03 +02:00
2006-08-16 09:17:44 +02:00
2006-08-20 18:33:52 +02:00
class CryptoBox :
''' this class rules them all!
put things like logging , conf and oter stuff in here ,
that might be used by more classes , it will be passed on to them '''
2006-08-21 09:41:03 +02:00
def __init__ ( self , config_file = None ) :
2006-09-08 13:02:27 +02:00
import CryptoBoxSettings
self . log = self . __getStartupLogger ( )
self . prefs = CryptoBoxSettings . CryptoBoxSettings ( config_file )
2006-08-23 15:27:25 +02:00
self . __runTests ( )
2006-08-21 09:41:03 +02:00
2006-08-20 18:33:52 +02:00
2006-09-08 13:02:27 +02:00
def __getStartupLogger ( self ) :
2006-08-20 20:23:35 +02:00
import logging
2006-08-20 18:33:52 +02:00
''' initialises the logging system
use it with : ' self.log.[debug|info|warning|error|critical](logmessage) '
2006-08-22 08:55:07 +02:00
all classes should get the logging instance during __init__ :
self . log = logging . getLogger ( " CryptoBox " )
2006-08-20 18:33:52 +02:00
2006-08-22 08:55:07 +02:00
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 '''
2006-08-20 18:33:52 +02:00
## basicConfig(...) needs python >= 2.4
2006-08-20 20:23:35 +02:00
try :
2006-09-08 13:02:27 +02:00
log_handler = logging . getLogger ( " CryptoBox " )
2006-08-24 09:36:47 +02:00
logging . basicConfig (
format = ' %(asctime)s %(module)s %(levelname)s %(message)s ' ,
stderr = sys . stderr )
2006-09-08 13:02:27 +02:00
log_handler . setLevel ( logging . ERROR )
log_handler . info ( " loggingsystem is up ' n running " )
2006-08-20 20:23:35 +02:00
## from now on everything can be logged via self.log...
except :
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError ( " couldn ' t initialise the loggingsystem. I give up. " )
2006-09-08 13:02:27 +02:00
return log_handler
2006-08-20 18:33:52 +02:00
2006-08-21 09:41:03 +02:00
2006-08-23 15:27:25 +02:00
# do some initial checks
def __runTests ( self ) :
2006-08-27 10:38:48 +02:00
self . __runTestRootPriv ( )
2006-09-05 17:03:16 +02:00
2006-08-27 10:38:48 +02:00
def __runTestRootPriv ( self ) :
""" try to run ' super ' with ' CryptoBoxRootActions ' """
2006-09-08 13:02:27 +02:00
import subprocess
2006-08-23 15:27:25 +02:00
try :
devnull = open ( os . devnull , " w " )
except IOError :
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError ( " could not open %s for writing! " % os . devnull )
try :
2006-09-08 13:02:27 +02:00
prog_super = self . prefs [ " Programs " ] [ " super " ]
2006-08-27 10:38:48 +02:00
except KeyError :
raise CBConfigUndefinedError ( " Programs " , " super " )
try :
2006-09-08 13:02:27 +02:00
prog_rootactions = self . prefs [ " Programs " ] [ " CryptoBoxRootActions " ]
2006-08-27 10:38:48 +02:00
except KeyError :
raise CBConfigUndefinedError ( " Programs " , " CryptoBoxRootActions " )
2006-08-23 18:44:14 +02:00
try :
proc = subprocess . Popen (
shell = False ,
stdout = devnull ,
stderr = devnull ,
2006-08-27 10:38:48 +02:00
args = [ prog_super , prog_rootactions , " check " ] )
2006-08-23 18:44:14 +02:00
except OSError :
2006-09-08 13:02:27 +02:00
raise CBEnvironmentError ( " failed to execute ' super ' ( %s ) " % self . prefs [ " Programs " ] [ " super " ] )
2006-08-23 15:27:25 +02:00
proc . wait ( )
if proc . returncode != 0 :
2006-08-27 10:38:48 +02:00
raise CBEnvironmentError ( " failed to call CryptoBoxRootActions ( %s ) via ' super ' - maybe you did not add the appropriate line to /etc/super.tab? " % prog_rootactions )
2006-08-23 15:27:25 +02:00
# this method just demonstrates inheritance effects - may be removed
2006-08-23 20:19:37 +02:00
def cbx_inheritance_test ( self , string = " you lucky widow " ) :
self . log . info ( string )
2006-08-24 09:36:47 +02:00
2006-08-20 18:33:52 +02:00
2006-08-21 09:41:03 +02:00
# RFC: why should CryptoBoxProps inherit CryptoBox? [l]
# RFC: shouldn't we move all useful functions of CryptoBoxProps to CryptoBox? [l]
2006-08-20 18:33:52 +02:00
class CryptoBoxProps ( CryptoBox ) :
2006-08-16 13:07:57 +02:00
''' Get and set the properties of a CryptoBox
2006-08-17 12:38:05 +02:00
This class contains all available devices that may be accessed .
All properties of the cryptobox can be accessed by this class .
2006-08-16 13:07:57 +02:00
'''
2006-08-16 09:17:44 +02:00
2006-08-24 09:36:47 +02:00
def __init__ ( self , config_file = None ) :
2006-08-16 13:07:57 +02:00
''' read config and fill class variables '''
2006-08-24 09:36:47 +02:00
CryptoBox . __init__ ( self , config_file )
2006-08-16 20:17:16 +02:00
self . containers = [ ]
for device in self . __getAvailablePartitions ( ) :
if self . isDeviceAllowed ( device ) :
2006-08-17 12:38:05 +02:00
self . containers . append ( CryptoBoxContainer . CryptoBoxContainer ( device , self ) )
2006-08-16 20:17:16 +02:00
2006-08-24 09:36:47 +02:00
2006-08-16 20:17:16 +02:00
def isDeviceAllowed ( self , devicename ) :
" check if a device is white-listed for being used as cryptobox containers "
2006-09-08 13:02:27 +02:00
import types
allowed = self . prefs [ " Main " ] [ " AllowedDevices " ]
2006-08-17 12:38:05 +02:00
if type ( allowed ) == types . StringType : allowed = [ allowed ]
for a_dev in allowed :
2006-08-18 16:00:49 +02:00
" remove double dots and so on ... "
real_device = os . path . realpath ( devicename )
if a_dev and re . search ( ' ^ ' + a_dev , real_device ) : return True
2006-08-16 20:17:16 +02:00
return False
2006-08-25 09:37:52 +02:00
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 :
2006-09-08 13:02:27 +02:00
if self . prefs [ " Log " ] [ " Destination " ] . upper ( ) != " FILE " : return [ ]
log_file = self . prefs [ " Log " ] [ " Details " ]
2006-08-25 09:37:52 +02:00
except KeyError :
self . log . error ( " could not evaluate one of the following config settings: [Log]->Destination or [Log]->Details " )
2006-08-27 10:38:48 +02:00
return [ ]
2006-08-25 09:37:52 +02:00
try :
fd = open ( log_file , " r " )
if maxSize : content = fd . readlines ( maxSize )
else : content = fd . readlines ( )
fd . close ( )
except IOError :
self . log . warn ( " failed to read the log file ( %s ) " % log_file )
2006-08-27 10:38:48 +02:00
return [ ]
2006-08-25 09:37:52 +02:00
if lines : content = content [ - lines : ]
return content
2006-08-16 20:17:16 +02:00
def getContainerList ( self , filterType = None , filterName = None ) :
" retrieve the list of all containers of this cryptobox "
try :
result = self . containers [ : ]
if filterType != None :
if filterType in range ( len ( CryptoBoxContainer . Types ) ) :
return [ e for e in self . containers if e . getType ( ) == filterType ]
else :
2006-08-21 12:10:40 +02:00
self . log . info ( " invalid filterType ( %d ) " % filterType )
2006-08-16 20:17:16 +02:00
result . clear ( )
if filterName != None :
result = [ e for e in self . containers if e . getName ( ) == filterName ]
return result
except AttributeError :
return [ ]
2006-09-07 13:21:56 +02:00
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-08-16 20:17:16 +02:00
def setNameForUUID ( self , uuid , name ) :
" assign a name to a uuid in the ContainerNameDatabase "
used_uuid = self . getUUIDForName ( name )
" first remove potential conflicting uuid/name combination "
2006-09-08 13:02:27 +02:00
if used_uuid : del self . prefs . nameDB [ used_uuid ]
self . prefs . nameDB [ uuid ] = name
self . prefs . nameDB . write ( )
2006-08-16 20:17:16 +02:00
def getNameForUUID ( self , uuid ) :
" get the name belonging to a specified key (usually the UUID of a fs) "
try :
2006-09-08 13:02:27 +02:00
return self . prefs . nameDB [ uuid ]
2006-08-16 20:17:16 +02:00
except KeyError :
return None
def getUUIDForName ( self , name ) :
""" get the key belonging to a value in the ContainerNameDatabase
this is the reverse action of ' getNameForUUID ' """
2006-09-08 13:02:27 +02:00
for key in self . prefs . nameDB . keys ( ) :
if self . prefs . nameDB [ key ] == name : return key
2006-08-16 20:17:16 +02:00
" the uuid was not found "
return None
2006-09-05 17:03:16 +02:00
2006-08-30 21:56:37 +02:00
def getAvailableLanguages ( self ) :
''' reads all files in path LangDir and returns a list of
basenames from existing hdf files , that should are all available
languages '''
languages = [ ]
2006-09-08 13:02:27 +02:00
for file in os . listdir ( self . prefs [ " Locations " ] [ " LangDir " ] ) :
2006-08-30 21:56:37 +02:00
if file . endswith ( " .hdf " ) : languages . append ( file . rstrip ( " .hdf " ) )
if len ( languages ) < 1 :
self . log . warn ( " No .hdf files found! The website won ' t render properly. " )
return languages
2006-09-05 17:03:16 +02:00
2006-08-30 21:56:37 +02:00
def getAvailableDocLanguages ( self ) :
''' reads all dirs in path DocDir and returns a list of
available documentation languages , this list might be empty . '''
doclangs = [ ]
regpat = re . compile ( r " ^ \ w+$ " )
try :
2006-09-08 13:02:27 +02:00
doc_dir = self . prefs [ " Locations " ] [ " DocDir " ]
2006-08-30 21:56:37 +02:00
except KeyError :
self . log . error ( " Could not find a configuration setting: [Locations]->DocDir - please check the config file " )
return [ ]
if not os . path . exists ( doc_dir ) :
self . log . error ( " The configured documentation directory ( %s ) does not exist " % ( doc_dir , ) )
return [ ]
try :
for e in os . listdir ( doc_dir ) :
if regpat . search ( e ) and os . path . isdir ( os . path . join ( doc_dir , e ) ) :
doclangs . append ( e )
if len ( doclangs ) < 1 :
self . log . warn ( " Didn ' t find any documentation files. " )
return doclangs
except OSError :
self . log . error ( " Could not access the documentations directory ( %s ) " % ( doc_dir , ) )
return [ ]
2006-08-16 20:17:16 +02:00
""" ************ internal stuff starts here *********** """
def __getAvailablePartitions ( self ) :
" retrieve a list of all available containers "
2006-08-16 09:17:44 +02:00
ret_list = [ ]
try :
2006-08-16 20:17:16 +02:00
" the following reads all lines of /proc/partitions and adds the mentioned devices "
2006-08-16 09:17:44 +02:00
fpart = open ( " /proc/partitions " , " r " )
try :
line = fpart . readline ( )
while line :
p_details = line . split ( )
if ( len ( p_details ) == 4 ) :
2006-08-16 20:17:16 +02:00
" the following code prevents double entries like /dev/hda and /dev/hda1 "
2006-08-16 09:17:44 +02:00
( p_major , p_minor , p_size , p_device ) = p_details
if re . search ( ' ^[0-9]*$ ' , p_major ) and re . search ( ' ^[0-9]*$ ' , p_minor ) :
p_parent = re . sub ( ' [1-9]?[0-9]$ ' , ' ' , p_device )
if p_parent == p_device :
if [ e for e in ret_list if re . search ( ' ^ ' + p_parent + ' [1-9]?[0-9]$ ' , e ) ] :
" major partition - its children are already in the list "
pass
else :
" major partition - but there are no children for now "
ret_list . append ( p_device )
else :
" minor partition - remove parent if necessary "
if p_parent in ret_list : ret_list . remove ( p_parent )
ret_list . append ( p_device )
line = fpart . readline ( )
finally :
fpart . close ( )
2006-08-16 20:17:16 +02:00
return [ self . __getAbsoluteDeviceName ( e ) for e in ret_list ]
2006-08-16 09:17:44 +02:00
except IOError :
2006-08-21 12:10:40 +02:00
self . log . warning ( " Could not read /proc/partitions " )
2006-08-16 09:17:44 +02:00
return [ ]
2006-08-16 20:17:16 +02:00
def __getAbsoluteDeviceName ( self , shortname ) :
""" returns the absolute file name of a device (e.g.: " hda1 " -> " /dev/hda1 " )
this does also work for device mapper devices
if the result is non - unique , one arbitrary value is returned """
if re . search ( ' ^/ ' , shortname ) : return shortname
default = os . path . join ( " /dev " , shortname )
if os . path . exists ( default ) : return default
result = self . __findMajorMinorOfDevice ( shortname )
" if no valid major/minor was found -> exit "
if not result : return default
( major , minor ) = result
" for device-mapper devices (major == 254) ... "
if major == 254 :
result = self . __findMajorMinorDeviceName ( " /dev/mapper " , major , minor )
if result : return result [ 0 ]
" now check all files in /dev "
result = self . __findMajorMinorDeviceName ( " /dev " , major , minor )
if result : return result [ 0 ]
return default
def __findMajorMinorOfDevice ( self , device ) :
" return the major/minor numbers of a block device by querying /sys/block/?/dev "
if not os . path . exists ( os . path . join ( " /sys/block " , device ) ) : return None
blockdev_info_file = os . path . join ( os . path . join ( " /sys/block " , device ) , " dev " )
2006-08-16 09:17:44 +02:00
try :
2006-08-16 20:17:16 +02:00
f_blockdev_info = open ( blockdev_info_file , " r " )
blockdev_info = f_blockdev_info . read ( )
f_blockdev_info . close ( )
( str_major , str_minor ) = blockdev_info . split ( " : " )
" numeric conversion "
try :
major = int ( str_major )
minor = int ( str_minor )
return ( major , minor )
except ValueError :
" unknown device numbers -> stop guessing "
return None
except IOError :
pass
2006-08-16 09:17:44 +02:00
2006-08-16 20:17:16 +02:00
def __findMajorMinorDeviceName ( self , dir , major , minor ) :
" returns the names of devices with the specified major and minor number "
collected = [ ]
try :
subdirs = [ os . path . join ( dir , e ) for e in os . listdir ( dir ) if ( not os . path . islink ( os . path . join ( dir , e ) ) ) and os . path . isdir ( os . path . join ( dir , e ) ) ]
" do a recursive call to parse the directory tree "
for dirs in subdirs :
collected . extend ( self . __findMajorMinorDeviceName ( dirs , major , minor ) )
" filter all device inodes in this directory "
collected . extend ( [ os . path . realpath ( os . path . join ( dir , e ) ) for e in os . listdir ( dir ) if ( os . major ( os . stat ( os . path . join ( dir , e ) ) . st_rdev ) == major ) and ( os . minor ( os . stat ( os . path . join ( dir , e ) ) . st_rdev ) == minor ) ] )
result = [ ]
for e in collected :
if e not in result : result . append ( e )
return collected
except OSError :
return [ ]
2006-08-18 16:00:49 +02:00
if __name__ == " __main__ " :
2006-08-22 06:29:53 +02:00
cb = CryptoBox ( )
2006-08-18 16:00:49 +02:00