Initial import
[py-rsbac] / rsbac / objects.py
1 # -*- coding:utf-8 -*-
2
3 # py-rsbac - RSBAC Python bindings
4 # Copyright (C) 2006  Frederic Jolliton <pyrsbac@tuxee.net>
5
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 __all__ = [
21     'Object' ,
22     'FD' , 'File' , 'Fifo' , 'Symlink' , 'Directory' , 'Device' ,
23     'User' , 'Group' , 'Process' ,
24     'Ipc' , 'IpcSem' , 'IpcMsg' , 'IpcShm' ,
25     'IpcAnonPipe' , 'IpcMqueue' , 'IpcAnonUnix' ,
26     'NetworkDevice' , 'NetworkTemplate'
27     ]
28
29 import sys
30 import new
31 import pwd, grp
32 import socket
33 import re
34 import operator
35 from ctypes import byref
36
37 from rsbac import headers, lib, transaction
38 from rsbac._data import RequestVector, AclRequestVector, JailScdVector
39 from rsbac._data import FileFlags, PaxFlags, CapsFlags, JailFlags
40 from rsbac.errors import Error, raiseIfError
41 from rsbac._utils import aptr, slowButCorrectStringAssignation
42 from rsbac._utils import stringToByteArray, byteArrayToString, fetch
43 from rsbac._utils import ipToInt, intToIp, ttlToInt, intToTtl
44 from rsbac._utils import processName, digits
45
46 # Singleton. Obviously bad for MT. But good for performance..
47 g_attributeValue = headers.rsbac_attribute_value_t()
48 g_attributeValueRef = byref( g_attributeValue )
49
50 from rsbac_auto import g_attributes
51
52 from rsbac.rc import RcTypeDict, Role, Type
53
54 def buildObjectClass( className , parents , target , byName ) :
55     if isinstance( target , tuple ) :
56         target , realTarget = target
57     else :
58         target , realTarget = target , target
59     attrs = {}
60     if byName :
61         fget = lib.rsbac_get_attr_n
62         fset = lib.rsbac_set_attr_n
63         frem = lib.rsbac_remove_target_n
64         amget = lib.rsbac_acl_get_mask_n
65         amset = lib.rsbac_acl_n
66     else :
67         fget = lib.rsbac_get_attr
68         fset = lib.rsbac_set_attr
69         frem = lib.rsbac_remove_target
70         amget = lib.rsbac_acl_get_mask
71         amset = lib.rsbac_acl
72     def remover( id ) :
73         raiseIfError( frem( transaction._t , target , id ) )
74     def mremove( self ) :
75         return remover( self._id )
76     attrs[ 'remover' ] = staticmethod( remover )
77     attrs[ 'remove' ] = mremove
78     def addAclMaskAttribute() :
79         rights = headers.rsbac_acl_rights_vector_t()
80         rightsRef = byref( rights )
81         sm = headers.ACLC_set_mask
82         def getter( id ) :
83             raiseIfError( amget( transaction._t , target , id , rightsRef ) )
84             return rights.value
85         if byName :
86             arg = headers.rsbac_acl_syscall_n_arg_t()
87         else :
88             arg = headers.rsbac_acl_syscall_arg_t()
89         arg.type = target
90         argRef = byref( arg )
91         if byName :
92             def setter( id , mask ) :
93                 arg.name = id
94                 arg.rights = mask
95                 raiseIfError( amset( transaction._t , sm , argRef ) )
96         else :
97             def setter( id , mask ) :
98                 arg.tid = id
99                 arg.rights = mask
100                 raiseIfError( amset( transaction._t , sm , argRef ) )
101         def mget( self ) :
102             return AclRequestVector( getter( self._id ) )
103         def mset( self , value ) :
104             return setter( self._id , int( value ) )
105         attrs[ 'getter_acl_mask' ] = staticmethod( getter )
106         attrs[ 'setter_acl_mask' ] = staticmethod( setter )
107         attrs[ 'get_acl_mask' ] = mget
108         attrs[ 'set_acl_mask' ] = mset
109         attrs[ 'acl_mask' ] = property( mget , mset )
110     def addLogArrayAttribute() :
111         module = headers.SW_GEN
112         attr1 = headers.A_log_array_low
113         attr2 = headers.A_log_array_high
114         def getter( id ) :
115             raiseIfError( fget( transaction._t , module , target , id , attr1 , g_attributeValueRef , 0 ) )
116             v1 = g_attributeValue.log_array_low
117             raiseIfError( fget( transaction._t , module , target , id , attr2 , g_attributeValueRef , 0 ) )
118             v2 = g_attributeValue.log_array_high
119             return tuple( [ int( item[ 0 ] + 2 * item[ 1 ] )
120                             for item in zip( digits( v1 , 2 , 64 ) ,
121                                              digits( v2 , 2 , 64 ) ) ] )
122         def getterEffective( id ) :
123             raiseIfError( fget( transaction._t , module , target ,
124                                 id , attr1 , g_attributeValueRef , 1 ) )
125             v1 = g_attributeValue.log_array_low
126             raiseIfError( fget( transaction._t , module , target ,
127                                 id , attr2 , g_attributeValueRef , 1 ) )
128             v2 = g_attributeValue.log_array_high
129             return tuple( [ int( item[ 0 ] + 2 * item[ 1 ] )
130                             for item in zip( digits( v1 , 2 , 64 ) ,
131                                              digits( v2 , 2 , 64 ) ) ] )
132         def setter( id , value ) :
133             if not isinstance( value , ( tuple , list ) ) or len( value ) != 64 :
134                 raise RuntimeError , 'expected a sequence of 64 elements'
135             v1 , v2 = 0 , 0
136             mask = 1 << 63
137             for item in value :
138                 if item & 1 :
139                     v1 |= mask
140                 if item & 2 :
141                     v2 |= mask
142                 mask >>= 1
143             g_attributeValue.log_array_low = v1
144             raiseIfError( fset( transaction._t , module , target ,
145                                 id , attr1 , g_attributeValueRef , 0 ) )
146             g_attributeValue.log_array_high = v2
147             raiseIfError( fset( transaction._t , module , target ,
148                                 id , attr2 , g_attributeValueRef , 0 ) )
149         def mget( self ) :
150             return getter( self._id )
151         def mgetEffective( self ) :
152             return getterEffective( self._id )
153         def mset( self , value ) :
154             setter( self._id , value )
155         attrs[ 'getter_log_array' ] = staticmethod( getter )
156         attrs[ 'getter_eff_log_array' ] = staticmethod( getterEffective )
157         attrs[ 'setter_log_array' ] = staticmethod( setter )
158         attrs[ 'get_log_array' ] = mget
159         attrs[ 'get_eff_log_array' ] = mgetEffective
160         attrs[ 'set_log_array' ] = mset
161         attrs[ 'log_array' ] = property( mget , mset )
162         attrs[ 'eff_log_array' ] = property( mgetEffective )
163     def addAttribute( module , vid , name , field ,
164                       pre = None , post = None ,
165                       mpre = None , mpost = None ) :
166         #
167         # getter for real value
168         #
169         if post is None :
170             def getter( id ) :
171                 raiseIfError( fget( transaction._t , module , target ,
172                                     id , vid , g_attributeValueRef , 0 ) )
173                 return getattr( g_attributeValue , field )
174         else :
175             def getter( id ) :
176                 raiseIfError( fget( transaction._t , module , target ,
177                                     id , vid , g_attributeValueRef , 0 ) )
178                 return post( getattr( g_attributeValue , field ) )
179         #
180         # getter for effective value
181         #
182         if post is None :
183             def getterEffective( id ) :
184                 raiseIfError( fget( transaction._t , module , target ,
185                                     id , vid , g_attributeValueRef , 1 ) )
186                 return getattr( g_attributeValue , field )
187         else :
188             def getterEffective( id ) :
189                 raiseIfError( fget( transaction._t , module , target ,
190                                     id , vid , g_attributeValueRef , 1 ) )
191                 return post( getattr( g_attributeValue , field ) )
192         #
193         # setter
194         #
195         if pre is None :
196             def setter( id , value ) :
197                 setattr( g_attributeValue , field , value )
198                 raiseIfError( fset( transaction._t , module , target ,
199                                     id , vid , g_attributeValueRef ) )
200         else :
201             def setter( id , value ) :
202                 setattr( g_attributeValue , field , pre( value ) )
203                 raiseIfError( fset( transaction._t , module , target ,
204                                     id , vid , g_attributeValueRef ) )
205         #
206         # member functions
207         #
208         if mpost is None :
209             def mget( self ) :
210                 return getter( self._id )
211             def mgetEffective( self ) :
212                 return getterEffective( self._id )
213         else :
214             if isinstance( mpost , tuple ) :
215                 mpost , withSelf = mpost
216             else :
217                 withSelf = False
218             if not withSelf :
219                 def mget( self ) :
220                     return mpost( getter( self._id ) )
221                 def mgetEffective( self ) :
222                     return mpost( getterEffective( self._id ) )
223             else :
224                 def mget( self ) :
225                     return mpost( getter( self._id ) , self )
226                 def mgetEffective( self ) :
227                     return mpost( getterEffective( self._id ) , self )
228         if mpre is None :
229             def mset( self , value ) :
230                 return setter( self._id , value )
231         else :
232             if isinstance( mpre , tuple ) :
233                 mpre , withSelf = mpost
234             else :
235                 withSelf = False
236             if not withSelf :
237                 def mset( self , value ) :
238                     return setter( self._id , mpre( value ) )
239             else :
240                 def mset( self , value ) :
241                     return setter( self._id , mpre( value , self ) )
242         #
243         attrs[ 'getter_' + name ] = staticmethod( getter )
244         attrs[ 'setter_' + name ] = staticmethod( setter )
245         attrs[ 'getter_eff_' + name ] = staticmethod( getterEffective )
246         #
247         attrs[ 'get_' + name ] = mget
248         attrs[ 'set_' + name ] = mset
249         attrs[ name ] = property( mget , mset )
250         #
251         attrs[ 'get_eff_' + name ] = mgetEffective
252         attrs[ 'eff_' + name ] = property( mgetEffective )
253     attributes = []
254     def raUnpack( value ) :
255         return tuple( map( int , value ) )
256     def raPack( value ) :
257         t = headers.rsbac_res_array_t()
258         t[ : ] = value
259         return t
260     fields = set()
261     for attribute , module , field , ctype , targets in g_attributes :
262         module = getattr( headers , 'SW_' + module )
263         attributeId = getattr( headers , 'A_' + attribute )
264         targets = [ getattr( headers , 'T_' + n ) for n in targets ]
265         if ( target in targets or realTarget in targets ) \
266                 and attribute not in [ 'log_array_low' , 'log_array_high' ] :
267             attributes.append( attribute )
268             if ctype == 'rsbac_jail_ip_t' or field == 'remote_ip' :
269                 mpre , mpost = ipToInt , intToIp
270             elif ctype == 'rsbac_jail_scd_vector_t' :
271                 mpre , mpost = int , JailScdVector
272             elif ctype == 'rsbac_jail_id_t' :
273                 mpre , mpost = int , int
274             elif ctype == 'rsbac_jail_flags_t' :
275                 mpre , mpost = int , JailFlags
276             elif ctype == 'rsbac_boolean_t' :
277                 mpre , mpost = int , bool
278             elif ctype == 'rsbac_rc_role_id_t' :
279                 mpre , mpost = int , Role
280             elif ctype == 'rsbac_rc_type_id_t' :
281                 mpre , mpost = int , lambda n : Type( realTarget , n )
282             elif ctype == 'rsbac_request_vector_t' :
283                 mpre , mpost = int , RequestVector
284             elif ctype == 'rsbac_ff_flags_t' :
285                 mpre , mpost = int , FileFlags
286             elif ctype == 'rsbac_uid_t' :
287                 mpre , mpost = int , lambda n : User( n ) # defer, since User is not yet defined
288             elif ctype == 'rsbac_pax_flags_t' :
289                 mpre , mpost = int , PaxFlags
290             elif ctype == 'rsbac_cap_vector_t' :
291                 mpre , mpost = int , CapsFlags
292             else :
293                 mpre , mpost = None , None
294             if ctype == 'rsbac_res_array_t' :
295                 pre , post = raPack , raUnpack
296             else :
297                 pre , post = None , None
298             addAttribute( module , attributeId , attribute , field ,
299                           pre , post , mpre , mpost )
300         fields.add( field )
301     if 'log_array_low' in fields and 'log_array_high' in fields :
302         addLogArrayAttribute()
303         attributes.append( 'log_array' )
304     addAclMaskAttribute()
305     attrs[ 'type' ] = target
306     attrs[ 'attributes' ] = sorted( attributes )
307     attrs[ '_byName' ] = byName
308     return type( className , parents , attrs )
309
310 class Object( object ) :
311     """Base class for all RSBAC objects.
312
313     """
314     pass
315
316 class ObjectWithAttributes( Object ) :
317     """Base class for objects with RSBAC attributes.
318
319     """
320     def getAllAttributes( self ) :
321         r = {}
322         for name in self.attributes :
323             try :
324                 r[ name ] = getattr( self , name )
325             except Error :
326                 pass
327         return r
328
329 #--[ FD ]---------------------------------------------------------------------
330
331 _g_defaultFd = {}
332
333 class FDBase( ObjectWithAttributes ) :
334     """Base class for FD hierarchy.
335
336     """
337     type = headers.T_FD
338     def __new__( cls , id ) :
339         if id is None :
340             instance = _g_defaultFd.get( cls.type )
341             if instance is None :
342                 instance = ObjectWithAttributes.__new__( cls )
343                 instance.__init_singleton__( id )
344                 _g_defaultFd[ cls.type ] = instance
345         else :
346             instance = ObjectWithAttributes.__new__( cls )
347             instance.__init_singleton__( id )
348         return instance
349     def __init_singleton__( self , id ) :
350         if not isinstance( id , str ) and id is not None :
351             raise TypeError , 'Invalid id %r' % ( id , )
352         self._id = id
353         self.acl = acl.AclByName( self )
354     def __eq__( self , other ) :
355         return self is other or ( isinstance( other , FDBase )
356                                   and self.type == other.type
357                                   and self._id == other._id )
358     def __str__( self ) :
359         return self._id
360     def __repr__( self ) :
361         if self._id is None :
362             return '<Default%s>' % self.__class__.__name__
363         else :
364             return '<%s %r>' % ( self.__class__.__name__ , self._id )
365
366 FD = \
367     buildObjectClass( 'FD' , ( FDBase , ) ,
368                       headers.T_FD , True )
369 File = \
370     buildObjectClass( 'File' , ( FDBase , ) ,
371                       ( headers.T_FILE , headers.T_FD ) , True )
372 Fifo = \
373     buildObjectClass( 'Fifo' , ( FDBase , ) ,
374                       ( headers.T_FIFO , headers.T_FD ) , True )
375 Symlink = \
376     buildObjectClass( 'Symlink' , ( FDBase , ) ,
377                       ( headers.T_SYMLINK , headers.T_FD ) , True )
378 Directory = \
379     buildObjectClass( 'Directory' , ( FDBase , ) ,
380                       ( headers.T_DIR , headers.T_FD ) , True )
381 Device = \
382     buildObjectClass( 'Device' , ( FDBase , ) ,
383                       ( headers.T_DEV , headers.T_FD ) , True )
384
385 #--[ Device ]-----------------------------------------------------------------
386
387 class DeviceBase( ObjectWithAttributes ) :
388     type = headers.T_DEV # Needed for RcTypeDict
389
390 class BlockDeviceBase( DeviceBase ) :
391     def __init__( self , major , minor = None ) :
392         super( BlockDeviceBase , self ).__init__()
393         self._dev = ( major , minor )
394         id = headers.rsbac_target_id_t()
395         if minor is not None :
396             id.dev.type = headers.D_block
397             id.dev.major = major
398             id.dev.minor = minor
399         else :
400             id.dev.type = headers.D_block_major
401             id.dev.major = major
402         self._id = byref( id )
403         self.acl = acl.AclById( self )
404     def __eq__( self , other ) :
405         return self is other or ( isinstance( other , BlockDeviceBase )
406                                   and self._dev == other._dev )
407     def __repr__( self ) :
408         if self._dev[ 1 ] is None :
409             dev = self._dev[ 0 ]
410         else :
411             dev = '%d:%d' % ( self._dev )
412         return '<BlockDevice %s>' % dev
413
414 BlockDevice = buildObjectClass( 'BlockDevice' ,
415                                 ( BlockDeviceBase , ) ,
416                                 headers.T_DEV , False )
417
418 class CharacterDeviceBase( DeviceBase ) :
419     def __init__( self , major , minor = None ) :
420         super( CharacterDeviceBase , self ).__init__()
421         self._dev = ( major , minor )
422         id = headers.rsbac_target_id_t()
423         if minor is not None :
424             id.dev.type = headers.D_char
425             id.dev.major = major
426             id.dev.minor = minor
427         else :
428             id.dev.type = headers.D_char_major
429             id.dev.major = major
430         self._id = byref( id )
431         self.acl = acl.AclById( self )
432     def __eq__( self , other ) :
433         return self is other or ( isinstance( other , CharacterDeviceBase )
434                                   and self._dev == other._dev )
435     def __repr__( self ) :
436         if self._dev[ 1 ] is None :
437             dev = self._dev[ 0 ]
438         else :
439             dev = '%d:%d' % ( self._dev )
440         return '<CharacterDevice %s>' % dev
441
442 CharacterDevice = buildObjectClass( 'CharacterDevice' ,
443                                     ( CharacterDeviceBase , ) ,
444                                     headers.T_DEV , False )
445
446 #--[ User ]-------------------------------------------------------------------
447
448 _g_user_mod = headers.rsbac_uid_t( -1 ).value + 1
449 assert _g_user_mod > 0
450 _g_user_max_value = headers.rsbac_uid_t( -32 ).value # FIXME: -32?
451 def _nuser( n ) :
452     n %= _g_user_mod
453     if n >= _g_user_max_value :
454         return n - _g_user_mod
455     else :
456         return n
457 _g_users = {}
458 _g_pseudoUsers = {
459     -3 : 'no_user' ,
460     -4 : 'all_users' ,
461     }
462
463 class UserBase( ObjectWithAttributes ) :
464     def __new__( cls , user ) :
465         user = _nuser( user )
466         if user < 0 :
467             instance = _g_users.get( user )
468             if instance is None :
469                 instance = ObjectWithAttributes.__new__( cls )
470                 instance.__init_singleton__( user )
471                 _g_users[ user ] = instance
472             return instance
473         else :
474             instance = ObjectWithAttributes.__new__( cls )
475             instance.__init_singleton__( user )
476             return instance
477     def __init_singleton__( self , user ) :
478         ObjectWithAttributes.__init__( self )
479         id = lib.rsbac_target_id_t()
480         if isinstance( user , ( int , long ) ) :
481             uid = user
482         elif isinstance( user , ( str , unicode ) ) :
483             uid = pwd.getpwnam( user )[ 2 ]
484         else :
485             raise RuntimeError , 'Unexpected user id %r' % ( user , )
486         id.user = uid
487         self.uid = uid
488         self._id = byref( id )
489         self.acl = acl.AclById( self )
490     def __int__( self ) :
491         return int( self.uid )
492     def __long__( self ) :
493         return long( self.uid )
494     def __eq__( self , other ) :
495         return self is other or ( isinstance( other , UserBase )
496                                   and self.uid == other.uid )
497     def __repr__( self ) :
498         if self.uid < 0 :
499             return '<PseudoUser %s>' % ( _g_pseudoUsers.get( self.uid , self.uid ) , )
500         else :
501             return '<User %d>' % self.uid
502     def removeFromAcl( self ) :
503         return acl.aclRemoveUser( self.uid )
504
505 User = buildObjectClass( 'User' , ( UserBase , ) , headers.T_USER , False )
506
507 #--[ Group ]------------------------------------------------------------------
508
509 _g_group_mod = headers.rsbac_gid_t( -1 ).value + 1
510 assert _g_group_mod > 0
511 _g_group_max_value = headers.rsbac_gid_t( -32 ).value # FIXME: -32?
512 def _ngroup( n ) :
513     n %= _g_group_mod
514     if n >= _g_group_max_value :
515         return n - _g_group_mod
516     else :
517         return n
518 _g_groups = {}
519 _g_pseudoGroups = {
520     -3 : 'no_group' ,
521     -4 : 'all_groups' ,
522     }
523
524 class GroupBase( ObjectWithAttributes ) :
525     def __new__( cls , group ) :
526         group = _ngroup( group )
527         if group < 0 :
528             instance = _g_groups.get( group )
529             if instance is None :
530                 instance = ObjectWithAttributes.__new__( cls )
531                 instance.__init_singleton__( group )
532                 _g_groups[ group ] = instance
533             return instance
534         else :
535             instance = ObjectWithAttributes.__new__( cls )
536             instance.__init_singleton__( group )
537             return instance
538     def __init_singleton__( self , group ) :
539         ObjectWithAttributes.__init__( group )
540         id = headers.rsbac_target_id_t()
541         if isinstance( group , ( int , long ) ) :
542             gid = group
543         elif isinstance( group , ( str , unicode ) ) :
544             gid = grp.getgrnam( group )[ 2 ]
545         else :
546             raise RuntimeError , 'Unexpected group id %r' % ( group , )
547         id.group = gid
548         self.gid = gid
549         self._id = byref( id )
550         self.acl = acl.AclById( self )
551     def __int__( self ) :
552         return int( self.gid )
553     def __long__( self ) :
554         return long( self.gid )
555     def __eq__( self , other ) :
556         return self is other or ( isinstance( other , GroupBase )
557                                   and self.gid == other.gid )
558     def __repr__( self ) :
559         if self.gid < 0 :
560             return '<PseudoGroup %s>' % ( _g_pseudoGroups.get( self.gid , self.gid ) , )
561         else :
562             return '<Group %d>' % self.gid
563
564 Group = buildObjectClass( 'Group' , ( GroupBase , ) , headers.T_GROUP , False )
565
566 #--[ Process ]----------------------------------------------------------------
567
568 class ProcessBase( ObjectWithAttributes ) :
569     def __init__( self , process ) :
570         super( ProcessBase , self ).__init__()
571         id = headers.rsbac_target_id_t()
572         id.process = process
573         self.pid = process
574         self._id = byref( id )
575     def __int__( self ) :
576         return int( self.pid )
577     def __long__( self ) :
578         return long( self.pid )
579     def __eq__( self , other ) :
580         return self is other or ( isinstance( other , ProcessBase )
581                                   and self.pid == other.pid )
582     def __repr__( self ) :
583         return '<Process %d %r>' % ( self.pid , processName( self.pid ) )
584
585 Process = buildObjectClass( 'Process' , ( ProcessBase , ) , headers.T_PROCESS , False )
586
587 #--[ IPC ]--------------------------------------------------------------------
588
589 class IpcBase( ObjectWithAttributes ) :
590     ipc = headers.I_none
591     def __init__( self , ipcId ) :
592         super( IpcBase , self ).__init__()
593         id = headers.rsbac_target_id_t()
594         id.ipc.type = self.ipc
595         id.ipc.id.id_nr = ipcId
596         self._id = byref( id )
597         self.id = ipcId
598     def __int__( self ) :
599         return int( self.id )
600     def __long__( self ) :
601         return long( self.id )
602     def __repr__( self ) :
603         return '<%s %d>' % ( self.__class__.__name__ , self.id )
604
605 Ipc = buildObjectClass( 'Ipc' , ( IpcBase , ) , headers.T_IPC , False )
606
607 class IpcSem( Ipc ) :
608     ipc = headers.I_sem
609
610 class IpcMsg( Ipc ) :
611     ipc = headers.I_msg
612
613 class IpcShm( Ipc ) :
614     ipc = headers.I_shm
615
616 class IpcAnonPipe( Ipc ) :
617     ipc = headers.I_anonpipe
618
619 class IpcMqueue( Ipc ) :
620     ipc = headers.I_mqueue
621
622 class IpcAnonUnix( Ipc ) :
623     ipc = headers.I_anonunix
624
625 #--[ SCD ]--------------------------------------------------------------------
626
627 g_scd = {}
628
629 class Scd( Object ) :
630     type = headers.T_SCD
631     def __new__( cls , target , *args , **kwargs ) :
632         instance = g_scd.get( target )
633         if instance is None :
634             instance = object.__new__( cls )
635             instance.__init_singleton__( target , *args , **kwargs )
636             g_scd[ target ] = instance
637         return instance
638     def __init_singleton__( self , target , name ) :
639         super( Scd , self ).__init__()
640         id = lib.rsbac_target_id_t()
641         id.scd = target
642         self._id = byref( id )
643         self.id = target
644         self.name = name
645     def getRcType( self ) :
646         return Type( self.type , self.id )
647     rc_type = property( getRcType )
648     def __repr__( self ) :
649         return '<SCD %s>' % self.name
650
651 system = new.module( 'system' )
652
653 def createScdObject() :
654     prefix = 'ST_'
655     for k , v in headers.__dict__.items() :
656         if k.startswith( prefix ) :
657             k = k[ len( prefix ) : ]
658             scd = Scd( v , k )
659             setattr( system , k , scd )
660
661 # Create SystemClock, SystemSysctl, SystemQuota,..
662 createScdObject()
663
664 #--[ NetworkObject ]----------------------------------------------------------
665
666 class NetworkObject( Object ) :
667     type = headers.T_NETOBJ
668     def __init__( self , *args , **kwargs ) : raise NotImplementedError
669     def __repr__( self ) :
670         return '<NetworkObject>'
671
672 #--[ NetworkDevice ]----------------------------------------------------------
673
674 # Not exported. Included in NetworkDevice class.
675 def getAllNetworkDevice() :
676     """*Return a list of all network devices involved with RSBAC rules."""
677     arr = fetch( headers.rsbac_netdev_id_t ,
678                  lambda n , a : lib.rsbac_net_list_all_netdev( transaction._t , a , n ) )
679     return sorted( map( byteArrayToString , arr ) )
680
681 class NetworkDeviceBase( ObjectWithAttributes ) :
682     def __init__( self , name ) :
683         super( NetworkDeviceBase , self ).__init__()
684         id = headers.rsbac_target_id_t()
685         stringToByteArray( id.netdev , name , minPadding = 1 )
686         self._name = name
687         self._id = byref( id )
688         self.acl = acl.AclById( self )
689     def __eq__( self , other ) :
690         return self is other or ( isinstance( other , NetworkDeviceBase )
691                                   and self._name == other._name )
692     def __str__( self ) :
693         return self._name
694     def __repr__( self ) :
695         return '<NetworkDevice %r>' % ( self._name , )
696     all = staticmethod( getAllNetworkDevice )
697
698 NetworkDevice = buildObjectClass( 'NetworkDeviceBase' ,
699                                   ( NetworkDeviceBase , ) ,
700                                   headers.T_NETDEV , False )
701
702 #--[ NetworkTemplate ]--------------------------------------------------------
703
704 def intToIpv4( n ) :
705     #assert 0 <= n < 2**32
706     n , a = divmod( n , 256 )
707     n , b = divmod( n , 256 )
708     n , c = divmod( n , 256 )
709     d = n
710     return '.'.join( map( str , ( a , b , c , d ) ) )
711
712 g_reDigitsOnly = re.compile( r'^\d+$' )
713
714 def ipv4ToInt( s ) :
715     ns = s.split( '.' )
716     if len( ns ) != 4 :
717         raise RuntimeError , 'invalid IP'
718     a = 0
719     for n in reversed( ns ) :
720         if not g_reDigitsOnly.match( n ) :
721             raise RuntimeError , 'invalid IP'
722         n = int( n )
723         if not 0 <= n <= 255 :
724             raise RuntimeError , 'invalid IP'
725         a = 256 * a + n
726     return a
727
728 def ipv4AndPrefixToInt( s ) :
729     if '/' not in s :
730         ip , prefix = s , 32
731     else :
732         ip , prefix = s.split( '/' , 1 )
733         if not g_reDigitsOnly.match( prefix ) :
734             raise RuntimeError, 'invalid prefix'
735         prefix = int( prefix )
736     ip = ipv4ToInt( ip )
737     if not 0 <= prefix <= 32 :
738         raise RuntimeError , 'invalid prefix'
739     return ip , prefix
740
741 # Not exported. Included in NetworkTemplate class.
742 def newNetworkTemplate( id , name ) :
743     data = headers.rsbac_net_temp_syscall_data_t()
744     data.name = name
745     raiseIfError( lib.rsbac_net_template( transaction._t ,
746                                           headers.NTS_new_template ,
747                                           id , byref( data ) ) )
748     nt = NetworkTemplate( id )
749     if nt.name != name :
750         raise RuntimeError , \
751             'error while constructing network template with name %r (got back %r instead)' \
752             % ( name , nt.name )
753     return nt
754
755 # Not exported. Included in NetworkTemplate class.
756 def copyNetworkTemplate( source , dest ) :
757     data = headers.rsbac_net_temp_syscall_data_t()
758     data.id = source
759     raiseIfError( lib.rsbac_net_template( transaction._t ,
760                                           headers.NTS_copy_template ,
761                                           dest , byref( data ) ) )
762     return NetworkTemplate( dest )
763
764 class NetworkTemplateBase( ObjectWithAttributes ) :
765     def __init__( self , nt ) :
766         super( NetworkTemplateBase , self ).__init__()
767         id = headers.rsbac_target_id_t()
768         id.nettemp = nt
769         self._id = byref( id )
770         self.nt = nt
771         self.acl = acl.AclById( self )
772     def __repr__( self ) :
773         try :
774             name = `self.getName()`
775         except Error :
776             name = 'undefined'
777         return '<%s [%d] %s>' % ( self.__class__.__name__ , self.nt , name )
778     def __syscall( self , syscall , data ) :
779         raiseIfError( lib.rsbac_net_template( transaction._t , syscall ,
780                                               self.nt , byref( data ) ) )
781         return data
782     def __syscallGet( self , syscall ) :
783         # FIXME: Use a shared 'data' to prevent building this
784         # structure too much times?
785         data = headers.rsbac_net_temp_syscall_data_t()
786         return self.__syscall( syscall , data )
787     #
788     # addresses
789     #
790     def getAddresses( self ) :
791         family = self.__syscallGet( headers.NTS_get_address_family ).address_family
792         addr = self.__syscallGet( headers.NTS_get_address ).address
793         def translate( inet ) :
794             result = []
795             for i in range( inet.nr_addr ) :
796                 result.append( '%s/%s' % ( intToIpv4( inet.addr[ i ] ) ,
797                                            inet.valid_bits[ i ] ) )
798             return result
799         if family == socket.AF_INET :
800             return ( family , translate( addr.inet ) )
801         elif family == socket.AF_UNIX :
802             return ( family , addr.other.addr[ : addr.other.valid_len ] )
803         else :
804             if addr.other.addr :
805                 name = str( family )
806                 #name = rsbac_get_net_family_name( family )
807                 raise RuntimeError , 'address for family %s not supported' % name
808             return ( family , )
809     def setAddresses( self , addresses ) :
810         if isinstance( addresses , ( int , long ) ) :
811             addresses = ( addresses , () )
812         if not isinstance( addresses , ( tuple , list ) ) \
813                 or len( addresses ) != 2 :
814             raise RuntimeError
815         family , addrs = addresses
816         if not isinstance( family , ( int , long ) ) :
817             raise RuntimeError
818         data = headers.rsbac_net_temp_syscall_data_t()
819         data.address_family = family
820         self.__syscall( headers.NTS_set_address_family , data )
821         if family == socket.AF_INET :
822             for i , addr in enumerate( addrs ) :
823                 a , n = ipv4AndPrefixToInt( addr )
824                 try :
825                     data.address.inet.addr[ i ] = a
826                     data.address.inet.valid_bits[ i ] = n
827                 except IndexError :
828                     raise IndexError , \
829                         'only %d addresses allowed, got %d' \
830                         % ( i , len( addrs ) )
831             data.address.inet.nr_addr = len( addrs )
832             self.__syscall( headers.NTS_set_address , data )
833         elif family == socket.AF_UNIX :
834             data.address.other.addr = addrs
835             data.address.other.valid_len = len( addrs )
836             self.__syscall( headers.NTS_set_address , data )
837         else :
838             if addrs :
839                 raise RuntimeError , \
840                     'address family %s doesn\'t allows addresses (or is not yet supported)' \
841                     % family
842     addresses = property( getAddresses , setAddresses )
843     #
844     # type
845     #
846     def getSocketType( self ) :
847         return self.__syscallGet( headers.NTS_get_type ).type
848     def setSocketType( self , type ) :
849         data = headers.rsbac_net_temp_syscall_data_t()
850         data.type = type
851         self.__syscall( headers.NTS_set_type , data )
852     socketType = property( getSocketType , setSocketType )
853     #
854     # protocol
855     #
856     def getProtocol( self ) :
857         return self.__syscallGet( headers.NTS_get_protocol ).protocol
858     def setProtocol( self , protocol ) :
859         data = headers.rsbac_net_temp_syscall_data_t()
860         data.protocol = protocol
861         self.__syscall( headers.NTS_set_protocol , data )
862     protocol = property( getProtocol , setProtocol )
863     #
864     # netdev
865     #
866     def getNetworkDevice( self ) :
867         return byteArrayToString( self.__syscallGet( headers.NTS_get_netdev ).netdev )
868     def setNetworkDevice( self , value ) :
869         data = headers.rsbac_net_temp_syscall_data_t()
870         stringToByteArray( data.netdev , str( value ) )
871         self.__syscall( headers.NTS_set_netdev , data )
872     def delNetworkDevice( self ) : # FIXME
873         self.setNetworkDevice( '' )
874     networkDevice = property( getNetworkDevice , setNetworkDevice , delNetworkDevice )
875     #
876     # ports
877     #
878     def getPorts( self ) :
879         ports = self.__syscallGet( headers.NTS_get_ports ).ports
880         p = ports.ports
881         def mkPortRange( p ) :
882             if p.min == p.max :
883                 return p.min
884             else :
885                 return ( p.min , p.max )
886         return map( mkPortRange , p[ : ports.nr_ports ] )
887     def setPorts( self , value ) :
888         data = headers.rsbac_net_temp_syscall_data_t()
889         p = data.ports.ports
890         try :
891             for i , range in enumerate( value ) :
892                 if not isinstance( range , ( tuple , list ) ) :
893                     min , max = range , range
894                 else :
895                     min , max = range
896                 p[ i ].min = min
897                 p[ i ].max = max
898         except IndexError :
899             raise IndexError , 'only %d port ranges allowed, got %d' % ( i , len( value ) )
900         data.ports.nr_ports = len( value )
901         self.__syscall( headers.NTS_set_ports , data )
902     def delPorts( self ) : # FIXME
903         self.setPorts( () )
904     ports = property( getPorts , setPorts , delPorts )
905     #
906     # name
907     #
908     def getName( self ) :
909         return self.__syscallGet( headers.NTS_get_name ).name
910     def setName( self , value ) :
911         data = headers.rsbac_net_temp_syscall_data_t()
912         slowButCorrectStringAssignation( data , 'name' , value )
913         try :
914             raiseIfError( lib.rsbac_net_template( transaction._t ,
915                                                   headers.NTS_set_name ,
916                                                   self.nt , byref( data ) ) )
917         except Error , e :
918             if e[ 0 ] != headers.RSBAC_ENOTFOUND :
919                 raise
920             newNetworkTemplate( self.nt , value )
921     name = property( getName , setName )
922     #
923     # misc
924     #
925     create = staticmethod( newNetworkTemplate )
926     def checkId( self ) :
927         data = headers.rsbac_net_temp_syscall_data_t()
928         return self.__syscall( headers.NTS_check_id , data ).id
929     def copyTo( self , target ) :
930         return copyNetworkTemplate( self.nt , target )
931     def delete( self ) :
932         data = headers.rsbac_net_temp_syscall_data_t()
933         self.__syscall( headers.NTS_delete_template , data )
934
935 NetworkTemplate = buildObjectClass( 'NetworkTemplate' ,
936                                     ( NetworkTemplateBase , ) ,
937                                     headers.T_NETTEMP , False )
938
939 def getAllNetworkTemplate() :
940     arr = fetch( headers.rsbac_net_temp_id_t ,
941                  lambda n , a : lib.rsbac_net_list_all_template( transaction._t , a , n ) )
942     return sorted( map( int , arr ) )
943
944 class NetworkTemplateDict( object ) :
945     def __repr__( self ) :
946         return '{' + ', '.join( [ ': '.join( map( str , item ) ) for item in self.items() ] ) + '}'
947     def __getitem__( self , nt ) :
948         return NetworkTemplate( nt )
949     def __delitem__( self , nt ) :
950         NetworkTemplate( nt ).delete()
951     def keys( self ) :
952         return getAllNetworkTemplate()
953     def values( self ) :
954         return map( NetworkTemplate , self.keys() )
955     def items( self ) :
956         return [ ( i , NetworkTemplate( i ) ) for i in self.keys() ]
957
958 networkTemplates = NetworkTemplateDict()
959
960 #--[ UnixSocket ]-------------------------------------------------------------
961
962 class UnixSocket( Object ) :
963     type = headers.T_UNIXSOCK
964     def __init__( self , path ) :
965         self._id = path
966     def __repr__( self ) :
967         return '<UnixSocket %r>' % ( self._id , )
968
969 #-----------------------------------------------------------------------------
970
971 for target in ( FDBase , User , Group , Process ,
972                 Ipc , Scd , DeviceBase ,
973                 NetworkDevice , NetworkTemplate , NetworkObject ) :
974     target.rcTypes = RcTypeDict( target.type )
975
976 def listAllDevices() :
977     arr = fetch( headers.rsbac_dev_desc_t ,
978                  lambda n , a : lib.rsbac_list_all_dev( transaction._t , a , n ) )
979     r = []
980     def cmp( a , b ) :
981         return ( a.type < b.type
982                  or ( a.type == b.type
983                       and ( a.major < b.major
984                             or ( a.major == b.major
985                                  and a.minor < b.minor ) ) ) )
986     for item in sorted( arr , cmp ) :
987         if item.type == headers.D_block :
988             dev = BlockDevice( item.major , item.minor )
989         elif item.type == headers.D_block_major :
990             dev = BlockDevice( item.major )
991         elif item.type == headers.D_char :
992             dev = CharacterDevice( item.major , item.minor )
993         elif item.type == headers.D_char_major :
994             dev = CharacterDevice( item.major )
995         else :
996             raise NotReached
997         r.append( dev )
998     return r
999
1000 def listAllUsers() :
1001     arr = fetch( headers.rsbac_uid_t ,
1002                  lambda n , a : lib.rsbac_list_all_user( transaction._t , a , n ) )
1003     return map( User , sorted( arr ) )
1004
1005 def listAllGroups() :
1006     arr = fetch( headers.rsbac_gid_t ,
1007                  lambda n , a : lib.rsbac_list_all_group( transaction._t , a , n ) )
1008     return map( User , sorted( arr ) )
1009
1010 from rsbac import acl
1011
1012 defaultFd = FD( None )
1013 defaultFile = File( None )
1014 defaultFifo = Fifo( None )
1015 defaultSymlink = Symlink( None )
1016 defaultDirectory = Directory( None )
1017 defaultDevice = Device( None )
1018
1019 pseudoUsers = new.module( 'pseudoUsers' )
1020 for k , v in _g_pseudoUsers.items() :
1021     setattr( pseudoUsers , v , User( k ) )
1022
1023 pseudoGroups = new.module( 'pseudoGroups' )
1024 for k , v in _g_pseudoGroups.items() :
1025     setattr( pseudoGroups , v , Group( k ) )
1026
1027 # Local Variables:
1028 # indent-tabs-mode: nil
1029 # python-indent: 4
1030 # End: