Added acl.grant and acl.revoke functions.
[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                   'authCapability' , 'authEffectiveCapability' , 'authFsCapability' ,
344                   'authGroupCapability' , 'authGroupEffectiveCapability' , 'authGroupFsCapability' )
345     type = headers.T_FD
346     def __new__( cls , id ) :
347         if id is None :
348             instance = _g_defaultFd.get( cls.type )
349             if instance is None :
350                 instance = ObjectWithAttributes.__new__( cls )
351                 instance.__init_singleton__( id )
352                 _g_defaultFd[ cls.type ] = instance
353         else :
354             instance = ObjectWithAttributes.__new__( cls )
355             instance.__init_singleton__( id )
356         return instance
357     def __init_singleton__( self , id ) :
358         if not isinstance( id , str ) and id is not None :
359             raise TypeError , 'Invalid id %r' % ( id , )
360         self._id = id
361         self.acl = acl.AclByName( self )
362         self.eff_acl = acl.EffectiveAclByName( self )
363         self.authCapability = \
364             auth.AuthCapDictProxyFd( headers.ACT_real , id )
365         self.authEffectiveCapability = \
366             auth.AuthCapDictProxyFd( headers.ACT_eff , id )
367         self.authFsCapability = \
368             auth.AuthCapDictProxyFd( headers.ACT_fs , id )
369         self.authGroupCapability = \
370             auth.AuthCapDictProxyFd( headers.ACT_group_real , id )
371         self.authGroupEffectiveCapability = \
372             auth.AuthCapDictProxyFd( headers.ACT_group_eff , id )
373         self.authGroupFsCapability = \
374             auth.AuthCapDictProxyFd( headers.ACT_group_fs , id )
375     def __eq__( self , other ) :
376         return self is other or ( isinstance( other , FDBase )
377                                   and self.type == other.type
378                                   and self._id == other._id )
379     def __str__( self ) :
380         return self._id
381     def __repr__( self ) :
382         if self._id is None :
383             return '<Default%s>' % self.__class__.__name__
384         else :
385             return '<%s %r>' % ( self.__class__.__name__ , self._id )
386
387 FD = \
388     buildObjectClass( 'FD' , ( FDBase , ) ,
389                       headers.T_FD , True )
390 File = \
391     buildObjectClass( 'File' , ( FDBase , ) ,
392                       ( headers.T_FILE , headers.T_FD ) , True )
393 Fifo = \
394     buildObjectClass( 'Fifo' , ( FDBase , ) ,
395                       ( headers.T_FIFO , headers.T_FD ) , True )
396 Symlink = \
397     buildObjectClass( 'Symlink' , ( FDBase , ) ,
398                       ( headers.T_SYMLINK , headers.T_FD ) , True )
399 Directory = \
400     buildObjectClass( 'Directory' , ( FDBase , ) ,
401                       ( headers.T_DIR , headers.T_FD ) , True )
402 Device = \
403     buildObjectClass( 'Device' , ( FDBase , ) ,
404                       ( headers.T_DEV , headers.T_FD ) , True )
405
406 UnixSocket = \
407     buildObjectClass( 'UnixSocket' , ( FDBase , ) ,
408                       ( headers.T_UNIXSOCK , headers.T_FD ) , True )
409
410 #--[ Device ]-----------------------------------------------------------------
411
412 class DeviceBase( ObjectWithAttributes ) :
413     __slots__ = ()
414     type = headers.T_DEV # Needed for RcTypeDict
415
416 class BlockDeviceBase( DeviceBase ) :
417     __slots__ = ( '_dev' , '_id' , 'acl' , 'eff_acl' )
418     def __init__( self , major , minor = None ) :
419         super( BlockDeviceBase , self ).__init__()
420         self._dev = ( major , minor )
421         id = headers.rsbac_target_id_t()
422         if minor is not None :
423             id.dev.type = headers.D_block
424             id.dev.major = major
425             id.dev.minor = minor
426         else :
427             id.dev.type = headers.D_block_major
428             id.dev.major = major
429         self._id = byref( id )
430         self.acl = acl.AclById( self )
431         self.eff_acl = acl.EffectiveAclById( self )
432     def __eq__( self , other ) :
433         return self is other or ( isinstance( other , BlockDeviceBase )
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 '<BlockDevice %s>' % dev
441
442 BlockDevice = buildObjectClass( 'BlockDevice' ,
443                                 ( BlockDeviceBase , ) ,
444                                 headers.T_DEV , False )
445
446 class CharacterDeviceBase( DeviceBase ) :
447     __slots__ = ( '_dev' , '_id' , 'acl' , 'eff_acl' )
448     def __init__( self , major , minor = None ) :
449         super( CharacterDeviceBase , self ).__init__()
450         self._dev = ( major , minor )
451         id = headers.rsbac_target_id_t()
452         if minor is not None :
453             id.dev.type = headers.D_char
454             id.dev.major = major
455             id.dev.minor = minor
456         else :
457             id.dev.type = headers.D_char_major
458             id.dev.major = major
459         self._id = byref( id )
460         self.acl = acl.AclById( self )
461         self.eff_acl = acl.EffectiveAclById( self )
462     def __eq__( self , other ) :
463         return self is other or ( isinstance( other , CharacterDeviceBase )
464                                   and self._dev == other._dev )
465     def __repr__( self ) :
466         if self._dev[ 1 ] is None :
467             dev = self._dev[ 0 ]
468         else :
469             dev = '%d:%d' % ( self._dev )
470         return '<CharacterDevice %s>' % dev
471
472 CharacterDevice = buildObjectClass( 'CharacterDevice' ,
473                                     ( CharacterDeviceBase , ) ,
474                                     headers.T_DEV , False )
475
476 #--[ User ]-------------------------------------------------------------------
477
478 _g_user_mod = headers.rsbac_uid_t( -1 ).value + 1
479 assert _g_user_mod > 0
480 _g_user_max_value = headers.rsbac_uid_t( -32 ).value # FIXME: -32?
481 def _nuser( n ) :
482     n %= _g_user_mod
483     if n >= _g_user_max_value :
484         return n - _g_user_mod
485     else :
486         return n
487 _g_users = {}
488 _g_pseudoUsers = {
489     -3 : 'no_user' ,
490     -4 : 'all_users' ,
491     }
492
493 class UserBase( ObjectWithAttributes ) :
494     __slots__ = ( 'uid' , '_id' , 'acl' , 'eff_acl' )
495     def __new__( cls , user ) :
496         user = _nuser( user )
497         if user < 0 :
498             instance = _g_users.get( user )
499             if instance is None :
500                 instance = ObjectWithAttributes.__new__( cls )
501                 instance.__init_singleton__( user )
502                 _g_users[ user ] = instance
503             return instance
504         else :
505             instance = ObjectWithAttributes.__new__( cls )
506             instance.__init_singleton__( user )
507             return instance
508     def __init_singleton__( self , user ) :
509         ObjectWithAttributes.__init__( self )
510         id = lib.rsbac_target_id_t()
511         if isinstance( user , ( int , long ) ) :
512             uid = user
513         elif isinstance( user , ( str , unicode ) ) :
514             uid = pwd.getpwnam( user )[ 2 ]
515         else :
516             raise RuntimeError , 'Unexpected user id %r' % ( user , )
517         id.user = uid
518         self.uid = uid
519         self._id = byref( id )
520         self.acl = acl.AclById( self )
521         self.eff_acl = acl.EffectiveAclById( self )
522     def __int__( self ) :
523         return int( self.uid )
524     def __long__( self ) :
525         return long( self.uid )
526     def __eq__( self , other ) :
527         return self is other or ( isinstance( other , UserBase )
528                                   and self.uid == other.uid )
529     def __repr__( self ) :
530         if self.uid < 0 :
531             return '<PseudoUser %s>' % ( _g_pseudoUsers.get( self.uid , self.uid ) , )
532         else :
533             return '<User %d>' % self.uid
534     def removeFromAcl( self ) :
535         return acl.aclRemoveUser( self.uid )
536
537 User = buildObjectClass( 'User' , ( UserBase , ) , headers.T_USER , False )
538
539 #--[ Group ]------------------------------------------------------------------
540
541 _g_group_mod = headers.rsbac_gid_t( -1 ).value + 1
542 assert _g_group_mod > 0
543 _g_group_max_value = headers.rsbac_gid_t( -32 ).value # FIXME: -32?
544 def _ngroup( n ) :
545     n %= _g_group_mod
546     if n >= _g_group_max_value :
547         return n - _g_group_mod
548     else :
549         return n
550 _g_groups = {}
551 _g_pseudoGroups = {
552     -3 : 'no_group' ,
553     -4 : 'all_groups' ,
554     }
555
556 class GroupBase( ObjectWithAttributes ) :
557     __slots__ = ( 'gid' , '_id' , 'acl' , 'eff_acl' )
558     def __new__( cls , group ) :
559         group = _ngroup( group )
560         if group < 0 :
561             instance = _g_groups.get( group )
562             if instance is None :
563                 instance = ObjectWithAttributes.__new__( cls )
564                 instance.__init_singleton__( group )
565                 _g_groups[ group ] = instance
566             return instance
567         else :
568             instance = ObjectWithAttributes.__new__( cls )
569             instance.__init_singleton__( group )
570             return instance
571     def __init_singleton__( self , group ) :
572         ObjectWithAttributes.__init__( group )
573         id = headers.rsbac_target_id_t()
574         if isinstance( group , ( int , long ) ) :
575             gid = group
576         elif isinstance( group , ( str , unicode ) ) :
577             gid = grp.getgrnam( group )[ 2 ]
578         else :
579             raise RuntimeError , 'Unexpected group id %r' % ( group , )
580         id.group = gid
581         self.gid = gid
582         self._id = byref( id )
583         self.acl = acl.AclById( self )
584         self.eff_acl = acl.EffectiveAclById( self )
585     def __int__( self ) :
586         return int( self.gid )
587     def __long__( self ) :
588         return long( self.gid )
589     def __eq__( self , other ) :
590         return self is other or ( isinstance( other , GroupBase )
591                                   and self.gid == other.gid )
592     def __repr__( self ) :
593         if self.gid < 0 :
594             return '<PseudoGroup %s>' % ( _g_pseudoGroups.get( self.gid , self.gid ) , )
595         else :
596             return '<Group %d>' % self.gid
597
598 Group = buildObjectClass( 'Group' , ( GroupBase , ) , headers.T_GROUP , False )
599
600 #--[ Process ]----------------------------------------------------------------
601
602 class ProcessBase( ObjectWithAttributes ) :
603     __slots__ = ( 'pid' , '_id' , 'acl' , 'eff_acl' ,
604                   'authCapability' , 'authEffectiveCapability' , 'authFsCapability' ,
605                   'authGroupCapability' , 'authGroupEffectiveCapability' , 'authGroupFsCapability' )
606     def __init__( self , pid ) :
607         super( ProcessBase , self ).__init__()
608         id = headers.rsbac_target_id_t()
609         id.process = pid
610         self.pid = pid
611         self._id = byref( id )
612         self.acl = acl.AclById( self )
613         self.eff_acl = acl.EffectiveAclById( self )
614         self.authCapability = \
615             auth.AuthCapDictProxyProcess( headers.ACT_real , pid )
616         self.authEffectiveCapability = \
617             auth.AuthCapDictProxyProcess( headers.ACT_eff , pid )
618         self.authFsCapability = \
619             auth.AuthCapDictProxyProcess( headers.ACT_fs , pid )
620         self.authGroupCapability = \
621             auth.AuthCapDictProxyProcess( headers.ACT_group_real , pid )
622         self.authGroupEffectiveCapability = \
623             auth.AuthCapDictProxyProcess( headers.ACT_group_eff , pid )
624         self.authGroupFsCapability = \
625             auth.AuthCapDictProxyProcess( headers.ACT_group_fs , pid )
626     def __int__( self ) :
627         return int( self.pid )
628     def __long__( self ) :
629         return long( self.pid )
630     def __eq__( self , other ) :
631         return self is other or ( isinstance( other , ProcessBase )
632                                   and self.pid == other.pid )
633     def __repr__( self ) :
634         if self.pid == 0 :
635             return '<DefaultProcess>'
636         else :
637             return '<Process %d %r>' % ( self.pid , processName( self.pid ) )
638
639 Process = buildObjectClass( 'Process' , ( ProcessBase , ) , headers.T_PROCESS , False )
640
641 #--[ IPC ]--------------------------------------------------------------------
642
643 class IpcBase( ObjectWithAttributes ) :
644     __slots__ = ( '_id' , 'id' , 'acl' , 'eff_acl' )
645     ipc = headers.I_none
646     def __init__( self , ipcId ) :
647         super( IpcBase , self ).__init__()
648         id = headers.rsbac_target_id_t()
649         id.ipc.type = self.ipc
650         id.ipc.id.id_nr = ipcId
651         self._id = byref( id )
652         self.id = ipcId
653         self.acl = acl.AclById( self )
654         self.eff_acl = acl.EffectiveAclById( self )
655     def __int__( self ) :
656         return int( self.id )
657     def __long__( self ) :
658         return long( self.id )
659     def __repr__( self ) :
660         if self.ipc == headers.I_none and self.id == 0 :
661             return '<DefaultIpc>'
662         else :
663             return '<%s %d>' % ( self.__class__.__name__ , self.id )
664
665 Ipc = buildObjectClass( 'Ipc' , ( IpcBase , ) , headers.T_IPC , False )
666
667 class IpcSem( Ipc ) :
668     __slots__ = ()
669     ipc = headers.I_sem
670
671 class IpcMsg( Ipc ) :
672     __slots__ = ()
673     ipc = headers.I_msg
674
675 class IpcShm( Ipc ) :
676     __slots__ = ()
677     ipc = headers.I_shm
678
679 class IpcAnonPipe( Ipc ) :
680     __slots__ = ()
681     ipc = headers.I_anonpipe
682
683 class IpcMqueue( Ipc ) :
684     __slots__ = ()
685     ipc = headers.I_mqueue
686
687 class IpcAnonUnix( Ipc ) :
688     __slots__ = ()
689     ipc = headers.I_anonunix
690
691 #--[ SCD ]--------------------------------------------------------------------
692
693 g_scd = {}
694
695 class Scd( Object ) :
696     __slots__ = ( '_id' , 'id' , 'name' , 'acl' , 'eff_acl' )
697     type = headers.T_SCD
698     def __new__( cls , target , *args , **kwargs ) :
699         instance = g_scd.get( target )
700         if instance is None :
701             instance = object.__new__( cls )
702             instance.__init_singleton__( target , *args , **kwargs )
703             g_scd[ target ] = instance
704         return instance
705     def __init_singleton__( self , target , name ) :
706         super( Scd , self ).__init__()
707         id = lib.rsbac_target_id_t()
708         id.scd = target
709         self._id = byref( id )
710         self.id = target
711         self.name = name
712         self.acl = acl.AclById( self )
713         self.eff_acl = acl.EffectiveAclById( self )
714     def getRcType( self ) :
715         return Type( self.type , self.id )
716     rc_type = property( getRcType )
717     def __repr__( self ) :
718         if self.name is None :
719             return '<DefaultSCD>'
720         else :
721             return '<SCD %s>' % self.name
722
723 #--[ NetworkObject ]----------------------------------------------------------
724
725 _g_networkObject = None
726
727 class NetworkObject( Object ) :
728     __slots__ = ( '_id' , 'acl' , 'eff_acl' )
729     type = headers.T_NETOBJ
730     def __new__( cls ) :
731         global _g_networkObject
732         if _g_networkObject is None :
733             _g_networkObject = Object.__new__( cls )
734             _g_networkObject.__init_singleton__()
735         return _g_networkObject
736     def __init_singleton__( self ) :
737         id = headers.rsbac_target_id_t()
738         id.netobj.sock_p = None
739         id.netobj.local_addr = None
740         id.netobj.local_len = 0
741         id.netobj.remote_addr = None
742         id.netobj.remote_len = 0
743         self._id = byref( id )
744         self.acl = acl.AclById( self )
745         self.eff_acl = acl.EffectiveAclById( self )
746     def __repr__( self ) :
747         return '<NetworkObject>'
748
749 #--[ NetworkDevice ]----------------------------------------------------------
750
751 # Not exported. Included in NetworkDevice class.
752 def getAllNetworkDevice() :
753     """*Return a list of all network devices involved with RSBAC rules."""
754     arr = fetch( headers.rsbac_netdev_id_t ,
755                  lambda n , a : lib.rsbac_net_list_all_netdev( transaction._t , a , n ) )
756     return sorted( map( byteArrayToString , arr ) )
757
758 class NetworkDeviceBase( ObjectWithAttributes ) :
759     __slots__ = ( '_name' , '_id' , 'acl' , 'eff_acl' )
760     def __init__( self , name ) :
761         super( NetworkDeviceBase , self ).__init__()
762         id = headers.rsbac_target_id_t()
763         stringToByteArray( id.netdev , name , minPadding = 1 )
764         self._name = name
765         self._id = byref( id )
766         self.acl = acl.AclById( self )
767         self.eff_acl = acl.EffectiveAclById( self )
768     def __eq__( self , other ) :
769         return self is other or ( isinstance( other , NetworkDeviceBase )
770                                   and self._name == other._name )
771     def __str__( self ) :
772         return self._name
773     def __repr__( self ) :
774         if self._name == '' :
775             return '<DefaultNetworkDevice>'
776         else :
777             return '<NetworkDevice %r>' % ( self._name , )
778     all = staticmethod( getAllNetworkDevice )
779
780 NetworkDevice = buildObjectClass( 'NetworkDeviceBase' ,
781                                   ( NetworkDeviceBase , ) ,
782                                   headers.T_NETDEV , False )
783
784 #--[ NetworkTemplate ]--------------------------------------------------------
785
786 def intToIpv4( n ) :
787     #assert 0 <= n < 2**32
788     n , a = divmod( n , 256 )
789     n , b = divmod( n , 256 )
790     n , c = divmod( n , 256 )
791     d = n
792     return '.'.join( map( str , ( a , b , c , d ) ) )
793
794 g_reDigitsOnly = re.compile( r'^\d+$' )
795
796 def ipv4ToInt( s ) :
797     ns = s.split( '.' )
798     if len( ns ) != 4 :
799         raise RuntimeError , 'invalid IP'
800     a = 0
801     for n in reversed( ns ) :
802         if not g_reDigitsOnly.match( n ) :
803             raise RuntimeError , 'invalid IP'
804         n = int( n )
805         if not 0 <= n <= 255 :
806             raise RuntimeError , 'invalid IP'
807         a = 256 * a + n
808     return a
809
810 def ipv4AndPrefixToInt( s ) :
811     if '/' not in s :
812         ip , prefix = s , 32
813     else :
814         ip , prefix = s.split( '/' , 1 )
815         if not g_reDigitsOnly.match( prefix ) :
816             raise RuntimeError, 'invalid prefix'
817         prefix = int( prefix )
818     ip = ipv4ToInt( ip )
819     if not 0 <= prefix <= 32 :
820         raise RuntimeError , 'invalid prefix'
821     return ip , prefix
822
823 # Not exported. Included in NetworkTemplate class.
824 def createNetworkTemplate( id , name ) :
825     data = headers.rsbac_net_temp_syscall_data_t()
826     slowButCorrectStringAssignation( data , 'name' , name )
827     raiseIfError( lib.rsbac_net_template( transaction._t ,
828                                           headers.NTS_new_template ,
829                                           id , byref( data ) ) )
830     return NetworkTemplate( id )
831
832 def findUndefinedNetworkTemplate( start = 1 ) :
833     """Find an undefined network template.
834
835     start -- Minimal Network Template ID to use, as integer.
836
837     Return an integer.
838
839     """
840     data = byref( headers.rsbac_net_temp_syscall_data_t() )
841     fun = lib.rsbac_net_template
842     call = headers.NTS_check_id
843     i = start
844     while fun( transaction._t , call , i , data ) >= 0 :
845         i += 1
846     return i
847
848 def findUndefinedNetworkTemplates( n = 1 , start = 1 ) :
849     """Find a set of undefined network template.
850
851     n -- Number of unnnamed types to find.
852     start -- Minimal RC type id to use, as integer.
853
854     Return a list of integers.
855
856     """
857     r = []
858     i = start
859     for j in range( n ) :
860         nt = findUndefinedNetworkTemplate( i )
861         r.append( nt )
862         i = nt + 1
863     return r
864
865 def newNetworkTemplate( name , start = 1 ) :
866     """Create a new network template.
867
868     name -- Name for the new network template.
869
870     Return a NetworkTemplate.
871
872     """
873     return createNetworkTemplate( findUndefinedNetworkTemplate( start ) , name )
874
875 # Not exported. Included in NetworkTemplate class.
876 def copyNetworkTemplate( source , dest ) :
877     data = headers.rsbac_net_temp_syscall_data_t()
878     data.id = source
879     raiseIfError( lib.rsbac_net_template( transaction._t ,
880                                           headers.NTS_copy_template ,
881                                           dest , byref( data ) ) )
882     return NetworkTemplate( dest )
883
884 class _NetworkTemplateSelf( object ) :
885     __slots__ = ( '_id' , 'nt' , 'acl' , 'eff_acl' )
886     type = headers.T_NETTEMP_NT
887     def __init__( self , nt ) :
888         id = headers.rsbac_target_id_t()
889         id.nettemp = nt
890         self._id = byref( id )
891         self.nt = nt
892         self.acl = acl.AclById( self )
893         self.eff_acl = acl.EffectiveAclById( self )
894     def __repr__( self ) :
895         return '<NetworkTemplate[Self] %d>' % self.nt
896
897 class NetworkTemplateBase( ObjectWithAttributes ) :
898     __slots__ = ( '_id' , 'nt' , 'acl' , 'eff_acl' , 'selfAcl' , 'eff_selfAcl' )
899     def __init__( self , nt ) :
900         super( NetworkTemplateBase , self ).__init__()
901         id = headers.rsbac_target_id_t()
902         id.nettemp = nt
903         self._id = byref( id )
904         self.nt = nt
905         self.acl = acl.AclById( self )
906         self.eff_acl = acl.EffectiveAclByName( self )
907         ntSelf = _NetworkTemplateSelf( nt )
908         self.selfAcl = acl.AclById( ntSelf )
909         self.eff_selfAcl = acl.EffectiveAclById( ntSelf )
910     def __repr__( self ) :
911         try :
912             name = `self.getName()`
913         except Error :
914             name = 'undefined'
915         return '<%s [%d] %s>' % ( self.__class__.__name__ , self.nt , name )
916     def __syscall( self , syscall , data ) :
917         raiseIfError( lib.rsbac_net_template( transaction._t , syscall ,
918                                               self.nt , byref( data ) ) )
919         return data
920     def __syscallGet( self , syscall ) :
921         # FIXME: Use a shared 'data' to prevent building this
922         # structure too much times?
923         data = headers.rsbac_net_temp_syscall_data_t()
924         return self.__syscall( syscall , data )
925     #
926     # addresses
927     #
928     def getAddresses( self ) :
929         family = self.__syscallGet( headers.NTS_get_address_family ).address_family
930         addr = self.__syscallGet( headers.NTS_get_address ).address
931         def translate( inet ) :
932             result = []
933             for i in range( inet.nr_addr ) :
934                 result.append( '%s/%s' % ( intToIpv4( inet.addr[ i ] ) ,
935                                            inet.valid_bits[ i ] ) )
936             return result
937         if family == socket.AF_INET :
938             return ( family , translate( addr.inet ) )
939         elif family == socket.AF_UNIX :
940             return ( family , addr.other.addr[ : addr.other.valid_len ] )
941         else :
942             if addr.other.addr :
943                 name = str( family )
944                 #name = rsbac_get_net_family_name( family )
945                 raise RuntimeError , 'address for family %s not supported' % name
946             return ( family , )
947     def setAddresses( self , addresses ) :
948         if isinstance( addresses , ( int , long ) ) :
949             addresses = ( addresses , () )
950         if not isinstance( addresses , ( tuple , list ) ) \
951                 or len( addresses ) != 2 :
952             raise RuntimeError
953         family , addrs = addresses
954         if not isinstance( family , ( int , long ) ) :
955             raise RuntimeError
956         data = headers.rsbac_net_temp_syscall_data_t()
957         data.address_family = family
958         self.__syscall( headers.NTS_set_address_family , data )
959         if family == socket.AF_INET :
960             for i , addr in enumerate( addrs ) :
961                 a , n = ipv4AndPrefixToInt( addr )
962                 try :
963                     data.address.inet.addr[ i ] = a
964                     data.address.inet.valid_bits[ i ] = n
965                 except IndexError :
966                     raise IndexError , \
967                         'only %d addresses allowed, got %d' \
968                         % ( i , len( addrs ) )
969             data.address.inet.nr_addr = len( addrs )
970             self.__syscall( headers.NTS_set_address , data )
971         elif family == socket.AF_UNIX :
972             data.address.other.addr = addrs
973             data.address.other.valid_len = len( addrs )
974             self.__syscall( headers.NTS_set_address , data )
975         else :
976             if addrs :
977                 raise RuntimeError , \
978                     'address family %s doesn\'t allows addresses (or is not yet supported)' \
979                     % family
980     addresses = property( getAddresses , setAddresses )
981     #
982     # type
983     #
984     def getSocketType( self ) :
985         return self.__syscallGet( headers.NTS_get_type ).type
986     def setSocketType( self , type ) :
987         data = headers.rsbac_net_temp_syscall_data_t()
988         data.type = type
989         self.__syscall( headers.NTS_set_type , data )
990     socketType = property( getSocketType , setSocketType )
991     #
992     # protocol
993     #
994     def getProtocol( self ) :
995         return self.__syscallGet( headers.NTS_get_protocol ).protocol
996     def setProtocol( self , protocol ) :
997         data = headers.rsbac_net_temp_syscall_data_t()
998         data.protocol = protocol
999         self.__syscall( headers.NTS_set_protocol , data )
1000     protocol = property( getProtocol , setProtocol )
1001     #
1002     # netdev
1003     #
1004     def getNetworkDevice( self ) :
1005         return byteArrayToString( self.__syscallGet( headers.NTS_get_netdev ).netdev )
1006     def setNetworkDevice( self , value ) :
1007         data = headers.rsbac_net_temp_syscall_data_t()
1008         stringToByteArray( data.netdev , str( value ) )
1009         self.__syscall( headers.NTS_set_netdev , data )
1010     def delNetworkDevice( self ) : # FIXME
1011         self.setNetworkDevice( '' )
1012     networkDevice = property( getNetworkDevice , setNetworkDevice , delNetworkDevice )
1013     #
1014     # ports
1015     #
1016     def getPorts( self ) :
1017         ports = self.__syscallGet( headers.NTS_get_ports ).ports
1018         p = ports.ports
1019         def mkPortRange( p ) :
1020             if p.min == p.max :
1021                 return p.min
1022             else :
1023                 return ( p.min , p.max )
1024         return map( mkPortRange , p[ : ports.nr_ports ] )
1025     def setPorts( self , value ) :
1026         data = headers.rsbac_net_temp_syscall_data_t()
1027         p = data.ports.ports
1028         try :
1029             for i , range in enumerate( value ) :
1030                 if not isinstance( range , ( tuple , list ) ) :
1031                     min , max = range , range
1032                 else :
1033                     min , max = range
1034                 p[ i ].min = min
1035                 p[ i ].max = max
1036         except IndexError :
1037             raise IndexError , 'only %d port ranges allowed, got %d' % ( i , len( value ) )
1038         data.ports.nr_ports = len( value )
1039         self.__syscall( headers.NTS_set_ports , data )
1040     def delPorts( self ) : # FIXME
1041         self.setPorts( () )
1042     ports = property( getPorts , setPorts , delPorts )
1043     #
1044     # name
1045     #
1046     def getName( self ) :
1047         return self.__syscallGet( headers.NTS_get_name ).name
1048     def setName( self , value ) :
1049         data = headers.rsbac_net_temp_syscall_data_t()
1050         slowButCorrectStringAssignation( data , 'name' , value )
1051         try :
1052             raiseIfError( lib.rsbac_net_template( transaction._t ,
1053                                                   headers.NTS_set_name ,
1054                                                   self.nt , byref( data ) ) )
1055         except Error , e :
1056             if e[ 0 ] != headers.RSBAC_ENOTFOUND :
1057                 raise
1058             self.create( self.nt , value )
1059     name = property( getName , setName )
1060     #
1061     # misc
1062     #
1063     create = staticmethod( createNetworkTemplate )
1064     def checkId( self ) :
1065         data = headers.rsbac_net_temp_syscall_data_t()
1066         return self.__syscall( headers.NTS_check_id , data ).id
1067     def copyTo( self , target ) :
1068         return copyNetworkTemplate( self.nt , target )
1069     def delete( self ) :
1070         data = headers.rsbac_net_temp_syscall_data_t()
1071         self.__syscall( headers.NTS_delete_template , data )
1072
1073 NetworkTemplate = buildObjectClass( 'NetworkTemplate' ,
1074                                     ( NetworkTemplateBase , ) ,
1075                                     headers.T_NETTEMP , False )
1076
1077 def getAllNetworkTemplate() :
1078     arr = fetch( headers.rsbac_net_temp_id_t ,
1079                  lambda n , a : lib.rsbac_net_list_all_template( transaction._t , a , n ) )
1080     return sorted( map( int , arr ) )
1081
1082 class NetworkTemplateDict( object ) :
1083     __slots__ = ()
1084     def __repr__( self ) :
1085         return '{' + ', '.join( [ ': '.join( map( str , item ) ) for item in self.items() ] ) + '}'
1086     def __getitem__( self , nt ) :
1087         return NetworkTemplate( nt )
1088     def __delitem__( self , nt ) :
1089         NetworkTemplate( nt ).delete()
1090     def keys( self ) :
1091         return getAllNetworkTemplate()
1092     def values( self ) :
1093         return map( NetworkTemplate , self.keys() )
1094     def items( self ) :
1095         return [ ( i , NetworkTemplate( i ) ) for i in self.keys() ]
1096
1097 networkTemplates = NetworkTemplateDict()
1098
1099 #-----------------------------------------------------------------------------
1100
1101 for target in ( FDBase , User , Group , Process ,
1102                 Ipc , Scd , DeviceBase ,
1103                 NetworkDevice , NetworkTemplate , NetworkObject ) :
1104     target.rcTypes = RcTypeDict( target.type )
1105
1106 def listAllDevices() :
1107     arr = fetch( headers.rsbac_dev_desc_t ,
1108                  lambda n , a : lib.rsbac_list_all_dev( transaction._t , a , n ) )
1109     r = []
1110     def cmp( a , b ) :
1111         return ( a.type < b.type
1112                  or ( a.type == b.type
1113                       and ( a.major < b.major
1114                             or ( a.major == b.major
1115                                  and a.minor < b.minor ) ) ) )
1116     for item in sorted( arr , cmp ) :
1117         if item.type == headers.D_block :
1118             dev = BlockDevice( item.major , item.minor )
1119         elif item.type == headers.D_block_major :
1120             dev = BlockDevice( item.major )
1121         elif item.type == headers.D_char :
1122             dev = CharacterDevice( item.major , item.minor )
1123         elif item.type == headers.D_char_major :
1124             dev = CharacterDevice( item.major )
1125         else :
1126             raise NotReached
1127         r.append( dev )
1128     return r
1129
1130 def listAllUsers() :
1131     arr = fetch( headers.rsbac_uid_t ,
1132                  lambda n , a : lib.rsbac_list_all_user( transaction._t , a , n ) )
1133     return map( User , sorted( arr ) )
1134
1135 def listAllGroups() :
1136     arr = fetch( headers.rsbac_gid_t ,
1137                  lambda n , a : lib.rsbac_list_all_group( transaction._t , a , n ) )
1138     return map( User , sorted( arr ) )
1139
1140 from rsbac import acl, auth
1141
1142 defaultFd = FD( None )
1143 defaultFile = File( None )
1144 defaultFifo = Fifo( None )
1145 defaultSymlink = Symlink( None )
1146 defaultDirectory = Directory( None )
1147 defaultDevice = Device( None )
1148 defaultUnixSocket = UnixSocket( None )
1149
1150 defaultProcess = Process( 0 )
1151
1152 defaultIpc = Ipc( 0 )
1153
1154 pseudoUsers = new.module( 'pseudoUsers' )
1155 for k , v in _g_pseudoUsers.items() :
1156     setattr( pseudoUsers , v , User( k ) )
1157
1158 defaultUser = pseudoUsers.no_user
1159
1160 pseudoGroups = new.module( 'pseudoGroups' )
1161 for k , v in _g_pseudoGroups.items() :
1162     setattr( pseudoGroups , v , Group( k ) )
1163
1164 defaultGroup = pseudoGroups.no_group
1165
1166 defaultNetworkDevice = NetworkDevice( '' )
1167
1168 defaultScd = Scd( 0 , None )
1169
1170 system = new.module( 'system' )
1171
1172 def createScdObject() :
1173     prefix = 'ST_'
1174     for k , v in headers.__dict__.items() :
1175         if k.startswith( prefix ) :
1176             k = k[ len( prefix ) : ]
1177             scd = Scd( v , k )
1178             setattr( system , k , scd )
1179
1180 # Create SystemClock, SystemSysctl, SystemQuota,..
1181 createScdObject()
1182
1183 defaultNetworkObject = NetworkObject()
1184
1185 # Local Variables:
1186 # indent-tabs-mode: nil
1187 # python-indent: 4
1188 # End: