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