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