diff --git a/autotests/folding/test.cil.fold b/autotests/folding/test.cil.fold new file mode 100644 --- /dev/null +++ b/autotests/folding/test.cil.fold @@ -0,0 +1,144 @@ +; SELinux CIL Policy + +; Tests + +(policycap open_perms) ; Policy config. statement +(mls true) +(handleunknown allow) + +(sid kernel) ; Declaration type statement +(classpermissionset char_w (char (write setattr))) ; Other statements + +(user user) ; Declare identifier 'user' of user type +(role role) +(type type) +(allow allow) (true true) (in in) (xor xor) + +; List of permissions +(class security (compute_av compute_create compute_member check_context load_policy compute_relabel compute_user setenforce setbool setsecparam setcheckreqprot read_policy)) + +; Highlighting permissions only if there is not a statement keyword +(class binder (impersonate call set_context_mgr transfer receive)) +(class binder (classcommon impersonate call set_context_mgr transfer receive)) +(impersonate call set_context_mgr transfer receive) +(tunableif impersonate call set_context_mgr transfer receive) + +; This is allowed by the CIL compiler +( typeattribute;comment + all_fs_type_except_usermodehelper_and_proc_security) +(;comment + typeattribute all_fs_type_except_usermodehelper_and_proc_security) +( ;comment + ;more comments + typeattribute all_fs_type_except_usermodehelper_and_proc_security) + +; Paths +(true true /true true /true/true/ true true/true "true") +; Global namespace +(true true .true true true.true true .true.true true.true.true + .true. true. true.true. ; invalid +) + +; Keywords in some rules + +; filecon +(filecon "/system/bin/run-as" file runas_exec_context) +(filecon "/dev/socket/wpa_wlan[0-9]" any u:object_r:wpa.socket:s0-s0) +(filecon "/data/local/mine" dir ()) +(classcommon file any dir) +(file any dir) +; portcon +(portcon tcp 3333 (unconfined.user object_r unconfined.object levelrange_1)) +(portcon udp 4444 (unconfined.user object_r unconfined.object ((s0) level_2))) +(defaultrole tcp udp) +(tcp udp) +; fsuse +(fsuse xattr ext4 file.labeledfs_context) +(fsuse task pipefs file.pipefs_context) +(fsuse trans tmpfs file.tmpfs_context) +(typemember xattr task trans) +(xattr task trans) + +(allow unconfined.process self (file (read write))) +(allow process httpd.object (file (read write))) + +; Paths +"/system/(foo|bar)/[^/]*/(hi){2,6}(.*)?" +"/pa\12th.*a+b?" +/usr/hi\"esc\032esc\*3es{2,2}ds +"/data/(open " +"/data/[open " + + +; Some rules + +(call macro1("__kmsg__")) +(macro macro1 ((string ARG1)) + (typetransition audit.process device.device chr_file ARG1 device.klog_device) +) + +(allow unconfined.process self (file (read write))) +(auditallow release_app.process secmark_demo.browser_packet (packet (send recv))) +(allowx type_1 type_2 (ioctl tcp_socket (range 0x2000 0x20FF))) +(permissionx ioctl_nodebug (ioctl udp_socket (not (range 0x4000 0x4010)))) +(allowx type_3 type_4 ioctl_nodebug) +(dontauditx type_1 type_2 (ioctl tcp_socket (range 0x3000 0x30FF))) + +(class property_service (set)) +(block av_rules + (type type_1) + (type type_2) + (typeattribute all_types) + (typeattributeset all_types ((all))) + + (neverallow type_2 all_types (property_service (set))) +) +(macro binder_call ((type ARG1) (type ARG2)) + (allow ARG1 ARG2 (binder (transfer call))) +) +(ipaddr netmask_1 255.255.255.0) + +(class dir) +(class foo) +(class bar) +(class baz) +(classorder (dir foo)) +(classorder (unordered bar foo baz)) + +(classpermission zygote_2) +(classpermissionset zygote_2 (zygote + (and + (all) + (not (specifyinvokewith specifyseinfo)) + ) +)) + +(permissionx ioctl_3 (ioctl tcp_socket (and (range 0x8000 0x90FF) (not (range 0x8100 0x82FF))))) +(boolean disableAudioCapture false) +(booleanif (and (not disableAudio) (not disableAudioCapture)) + (true + (allow process mediaserver.audio_capture_device (chr_file_set (rw_file_perms))) + ) +) +(tunable range_trans_rule false) + +(block init + (class process (process)) + (type process) + (tunableif range_trans_rule + (true + (rangetransition process sshd.exec process low_high)))) + +(validatetrans file (eq t1 unconfined.process)) +(block ext_gateway + (optional move_file + (typetransition process msg_filter.move_file.in_queue file msg_filter.move_file.in_file) + (allow process msg_filter.move_file.in_queue (dir (read getattr write search add_name))))) + +(context runas_exec_context (u object_r exec low_low)) +(filecon "/system/bin/run-as" file runas_exec_context) + +(in file + (genfscon rootfs / rootfs_context) + (genfscon selinuxfs / selinuxfs_context) +) diff --git a/autotests/folding/test.fc.fold b/autotests/folding/test.fc.fold new file mode 100644 --- /dev/null +++ b/autotests/folding/test.fc.fold @@ -0,0 +1,104 @@ +# Sample SELinux Labeling Policy File + +# Syntax of 'file_contexts' file and other SELinux configuration files: + +/usr/lib/.*/program/foo\.so -- user:role:type:s0:c0 +/.* system_u:object_r:default_t:s0 +/sys(/.*)? system_u:object_r:sysfs_t:s0 +/xen(/.*)? system_u:object_r:xen_image_t:s1 +/mnt(/[^/]*)? -d system_u:object_r:mnt_t:s1-5 +/mnt(/[^/]*)? -l system_u:object_r:mnt_t:s0.s2 +/tmp/.* <> +/root(/.*)? system_u:object_r:admin_home_t:s0 +/dev/[0-9].* -c system_u:object_r:usb_device_t:s0 +/run/.*\.*pid <> +/mnt/[^/]*/.* <> +/etc/[mg]dm(/.*)? system_u:object_r:xdm_etc_t:s5-s6:c0 +/dev/(misc/)?psaux -c system_u:object_r:mouse_device_t:s0-s3:c0.c5 + +HOME_DIR/.+ system_u:object_r:user_home_t:s0 +HOME_DIR/((www)|(web)|(public_html))(/.+)? system_u:object_r:httpd_user_content_t:s0 +HOME_DIR/\.cache/google-chrome(/.*)? system_u:object_r:chrome_sandbox_home_t:s0 + +/dev/(misc/)?rtc[0-9]* -c system_u:object_r:clock_device_t:s0-s2:c1 +/var/(db|adm)/sudo(/.*)? system_u:object_r:pam_var_run_t:s0 +/dev/pcd[0-3] -b system_u:object_r:removable_device_t:s0 +/etc/ppp(/.*)? -- system_u:object_r:pppd_etc_rw_t:s0 +/var/www(/.*)? system_u:object_r:httpd_sys_content_t:s0 +/usr/lib(.*/)?bin(/.*)? system_u:object_r:bin_t:s0 +/dev/shm/.* <> +/usr/lib/(sse2/)?hello-.*\.so.* -- system_u:object_r:textrel_shlib_t:s0 +/sbin/grub.* -- system_u:object_r:bootloader_exec_t:s0.s3 +/sbin/lilo.* -- system_u:object_r:bootloader_exec_t:s0 +/etc/group[-\+]? -- system_u:object_r:passwd_file_t:s0:c1-c5 +/etc/rc\.d/init\.d/mpd -- system_u:object_r:mpd_initrc_exec_t:s0 + + +# Syntax of *.fc files, from the SELinux reference policy: + +/run/sudo/ts/%{USERNAME} gen_context(system_u:object_r:pam_var_run_t,s0,c0) +/etc/aiccu\.conf -- gen_context(system_u:object_r:aiccu_etc_t,s0-s2,c1.c5) +HOME_DIR/\.mtpz-data -- gen_context(system_u:object_r:libmtp_home_t,s0) +/var/log/mariadb(/.*)? gen_context(system_u:object_r:mysqld_log_t,s0) +/dev/dasd[^/]* -b gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh) +/dev/dasd[^/]* -c gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh) +HOME_ROOT -d gen_context(system_u:object_r:home_root_t,s0-mls_systemhigh,c1) +HOME_ROOT -l gen_context(system_u:object_r:home_root_t,s0) + +ifdef(`distro_debian',` + /run/shm -d gen_context(system_u:object_r:tmpfs_t,s0) + /run/shm/.* <> +') +ifdef(`distro_suse',` + /success -- gen_context(system_u:object_r:etc_runtime_t,s0) +') +ifdef(`init_systemd',` + /run/tmpfiles\.d/kmod\.conf -- gen_context(system_u:object_r:kmod_tmpfiles_conf_t,s0) +') + +# Tests + +# Variables +HOME_DIR/path +HOME_ROOT/path +/path/HOME_DIR/HOME_ROOT + +# Open brackets +/hello(world +/hello[wo + +/path[^0-8]+ +/path(hello|bye) +/path.*a+b? +/path\wa\Wa\sa\da\ba\Ba\(a +/usr/hi\"esc\sesc\032esc\*3esds + +# Security contexts +user:role +user:role: +user:role:type +user:role:type:level_sensitivity +user:role:type:level_sensitivity:level_category +user:role:type:level_sensitivity:level_category:other:other + +(user:role:type,) +(user:role:type,level_s,) +(user:role:type,level_s,level_c) +(user:role:type,level_s,level_c,other,other,other) +(user:role:type:level_s:level_c,other,other) +(user:role:type:level_s:level_c:other,other,other) + +us er:role:type:level_s:level_c +user:ro le:type:level_s:level_c +user:role:ty pe:level_s:level_c +user:role:type:lev el_s:level_c +user:role:type:level_s:lev el_c + +(u ser:role:type,level_s,level_c,other,other) +(user:ro le:type,level_s,level_c,other,other) +(user:role:ty pe,level_s,level_c,other,other) +(user:role:type,le vel_s,level_c,other,other) +(user:role:type,level_s,le vel_c,other,other) + +( user :role:type, level_s , level_c , other ) +( user:role:type, level_s , level_c , other ) diff --git a/autotests/html/test.cil.html b/autotests/html/test.cil.html new file mode 100644 --- /dev/null +++ b/autotests/html/test.cil.html @@ -0,0 +1,151 @@ + + + +test.cil + +
+; SELinux CIL Policy
+
+; Tests
+
+(policycap open_perms)  ; Policy config. statement
+(mls true)
+(handleunknown allow)
+
+(sid kernel)  ; Declaration type statement
+(classpermissionset char_w (char (write setattr)))  ; Other statements
+
+(user user) ; Declare identifier 'user' of user type
+(role role)
+(type type)
+(allow allow) (true true) (in in) (xor xor)
+
+; List of permissions
+(class security (compute_av compute_create compute_member check_context load_policy compute_relabel compute_user setenforce setbool setsecparam setcheckreqprot read_policy))
+
+; Highlighting permissions only if there is not a statement keyword
+(class binder (impersonate call set_context_mgr transfer receive))
+(class binder (classcommon impersonate call set_context_mgr transfer receive))
+(impersonate call set_context_mgr transfer receive)
+(tunableif impersonate call set_context_mgr transfer receive)
+
+; This is allowed by the CIL compiler
+( typeattribute;comment
+	all_fs_type_except_usermodehelper_and_proc_security)
+(;comment
+	typeattribute all_fs_type_except_usermodehelper_and_proc_security)
+(  ;comment
+ ;more comments
+	typeattribute all_fs_type_except_usermodehelper_and_proc_security)
+	
+; Paths
+(true true /true true /true/true/ true true/true "true")
+; Global namespace
+(true true .true true true.true true .true.true true.true.true
+	.true. true. true.true. ; invalid
+)
+
+; Keywords in some rules
+
+; filecon
+(filecon "/system/bin/run-as" file runas_exec_context)
+(filecon "/dev/socket/wpa_wlan[0-9]" any u:object_r:wpa.socket:s0-s0)
+(filecon "/data/local/mine" dir ())
+(classcommon file any dir)
+(file any dir)
+; portcon
+(portcon tcp 3333 (unconfined.user object_r unconfined.object levelrange_1))
+(portcon udp 4444 (unconfined.user object_r unconfined.object ((s0) level_2)))
+(defaultrole tcp udp)
+(tcp udp)
+; fsuse
+(fsuse xattr ext4 file.labeledfs_context)
+(fsuse task pipefs file.pipefs_context)
+(fsuse trans tmpfs file.tmpfs_context)
+(typemember xattr task trans)
+(xattr task trans)
+	
+(allow unconfined.process self (file (read write)))
+(allow process httpd.object (file (read write)))
+
+; Paths
+"/system/(foo|bar)/[^/]*/(hi){2,6}(.*)?"
+"/pa\12th.*a+b?"
+/usr/hi\"esc\032esc\*3es{2,2}ds
+"/data/(open "
+"/data/[open "
+
+
+; Some rules
+
+(call macro1("__kmsg__"))
+(macro macro1 ((string ARG1))
+    (typetransition audit.process device.device chr_file ARG1 device.klog_device)
+)
+
+(allow unconfined.process self (file (read write)))
+(auditallow release_app.process secmark_demo.browser_packet (packet (send recv)))
+(allowx type_1 type_2 (ioctl tcp_socket (range 0x2000 0x20FF)))
+(permissionx ioctl_nodebug (ioctl udp_socket (not (range 0x4000 0x4010))))
+(allowx type_3 type_4 ioctl_nodebug)
+(dontauditx type_1 type_2 (ioctl tcp_socket (range 0x3000 0x30FF)))
+
+(class property_service (set))
+(block av_rules
+    (type type_1)
+    (type type_2)
+    (typeattribute all_types)
+    (typeattributeset all_types ((all)))
+
+    (neverallow type_2 all_types (property_service (set)))
+)
+(macro binder_call ((type ARG1) (type ARG2))
+    (allow ARG1 ARG2 (binder (transfer call)))
+)
+(ipaddr netmask_1 255.255.255.0)
+
+(class dir)
+(class foo)
+(class bar)
+(class baz)
+(classorder (dir foo))
+(classorder (unordered bar foo baz))
+
+(classpermission zygote_2)
+(classpermissionset zygote_2 (zygote
+    (and
+        (all)
+        (not (specifyinvokewith specifyseinfo))
+    )
+))
+
+(permissionx ioctl_3 (ioctl tcp_socket (and (range 0x8000 0x90FF) (not (range 0x8100 0x82FF)))))
+(boolean disableAudioCapture false)
+(booleanif (and (not disableAudio) (not disableAudioCapture))
+    (true
+        (allow process mediaserver.audio_capture_device (chr_file_set (rw_file_perms)))
+    )
+)
+(tunable range_trans_rule false)
+
+(block init
+    (class process (process))
+    (type process)
+    (tunableif range_trans_rule
+        (true
+            (rangetransition process sshd.exec process low_high))))
+
+(validatetrans file (eq t1 unconfined.process))
+(block ext_gateway
+    (optional move_file
+        (typetransition process msg_filter.move_file.in_queue file msg_filter.move_file.in_file)
+        (allow process msg_filter.move_file.in_queue (dir (read getattr write search add_name)))))
+
+(context runas_exec_context (u object_r exec low_low))
+(filecon "/system/bin/run-as" file runas_exec_context)
+
+(in file
+    (genfscon rootfs / rootfs_context)
+    (genfscon selinuxfs / selinuxfs_context)
+)
+
diff --git a/autotests/html/test.fc.html b/autotests/html/test.fc.html new file mode 100644 --- /dev/null +++ b/autotests/html/test.fc.html @@ -0,0 +1,111 @@ + + + +test.fc + +
+# Sample SELinux Labeling Policy File
+
+# Syntax of 'file_contexts' file and other SELinux configuration files:
+
+/usr/lib/.*/program/foo\.so  --  user:role:type:s0:c0
+/.*                system_u:object_r:default_t:s0
+/sys(/.*)?         system_u:object_r:sysfs_t:s0
+/xen(/.*)?         system_u:object_r:xen_image_t:s1
+/mnt(/[^/]*)?  -d  system_u:object_r:mnt_t:s1-5
+/mnt(/[^/]*)?  -l  system_u:object_r:mnt_t:s0.s2
+/tmp/.*            <<none>>
+/root(/.*)?        system_u:object_r:admin_home_t:s0
+/dev/[0-9].*   -c  system_u:object_r:usb_device_t:s0
+/run/.*\.*pid      <<none>>
+/mnt/[^/]*/.*      <<none>>
+/etc/[mg]dm(/.*)?  system_u:object_r:xdm_etc_t:s5-s6:c0
+/dev/(misc/)?psaux  -c  system_u:object_r:mouse_device_t:s0-s3:c0.c5
+
+HOME_DIR/.+                                 system_u:object_r:user_home_t:s0
+HOME_DIR/((www)|(web)|(public_html))(/.+)?  system_u:object_r:httpd_user_content_t:s0
+HOME_DIR/\.cache/google-chrome(/.*)?        system_u:object_r:chrome_sandbox_home_t:s0
+
+/dev/(misc/)?rtc[0-9]*           -c  system_u:object_r:clock_device_t:s0-s2:c1
+/var/(db|adm)/sudo(/.*)?             system_u:object_r:pam_var_run_t:s0
+/dev/pcd[0-3]                    -b  system_u:object_r:removable_device_t:s0
+/etc/ppp(/.*)?                   --  system_u:object_r:pppd_etc_rw_t:s0
+/var/www(/.*)?                       system_u:object_r:httpd_sys_content_t:s0
+/usr/lib(.*/)?bin(/.*)?              system_u:object_r:bin_t:s0
+/dev/shm/.*                          <<none>>
+/usr/lib/(sse2/)?hello-.*\.so.*  --  system_u:object_r:textrel_shlib_t:s0
+/sbin/grub.*                     --  system_u:object_r:bootloader_exec_t:s0.s3
+/sbin/lilo.*                     --  system_u:object_r:bootloader_exec_t:s0
+/etc/group[-\+]?                 --  system_u:object_r:passwd_file_t:s0:c1-c5
+/etc/rc\.d/init\.d/mpd           --  system_u:object_r:mpd_initrc_exec_t:s0
+
+
+# Syntax of *.fc files, from the SELinux reference policy:
+
+/run/sudo/ts/%{USERNAME}     gen_context(system_u:object_r:pam_var_run_t,s0,c0)
+/etc/aiccu\.conf         --  gen_context(system_u:object_r:aiccu_etc_t,s0-s2,c1.c5)
+HOME_DIR/\.mtpz-data     --  gen_context(system_u:object_r:libmtp_home_t,s0)
+/var/log/mariadb(/.*)?       gen_context(system_u:object_r:mysqld_log_t,s0)
+/dev/dasd[^/]*           -b  gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh)
+/dev/dasd[^/]*           -c  gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh)
+HOME_ROOT                -d  gen_context(system_u:object_r:home_root_t,s0-mls_systemhigh,c1)
+HOME_ROOT                -l  gen_context(system_u:object_r:home_root_t,s0)
+
+ifdef(`distro_debian',`
+	/run/shm  -d  gen_context(system_u:object_r:tmpfs_t,s0)
+	/run/shm/.*  <<none>>
+')
+ifdef(`distro_suse',`
+	/success  --  gen_context(system_u:object_r:etc_runtime_t,s0)
+')
+ifdef(`init_systemd',`
+	/run/tmpfiles\.d/kmod\.conf  --  gen_context(system_u:object_r:kmod_tmpfiles_conf_t,s0)
+')
+
+# Tests
+
+# Variables
+HOME_DIR/path
+HOME_ROOT/path
+/path/HOME_DIR/HOME_ROOT
+
+# Open brackets
+/hello(world
+/hello[wo
+
+/path[^0-8]+
+/path(hello|bye)
+/path.*a+b?
+/path\wa\Wa\sa\da\ba\Ba\(a
+/usr/hi\"esc\sesc\032esc\*3esds
+
+# Security contexts
+user:role
+user:role:
+user:role:type
+user:role:type:level_sensitivity
+user:role:type:level_sensitivity:level_category
+user:role:type:level_sensitivity:level_category:other:other
+
+(user:role:type,)
+(user:role:type,level_s,)
+(user:role:type,level_s,level_c)
+(user:role:type,level_s,level_c,other,other,other)
+(user:role:type:level_s:level_c,other,other)
+(user:role:type:level_s:level_c:other,other,other) 
+
+us  er:role:type:level_s:level_c
+user:ro le:type:level_s:level_c
+user:role:ty  pe:level_s:level_c
+user:role:type:lev el_s:level_c
+user:role:type:level_s:lev el_c
+
+(u ser:role:type,level_s,level_c,other,other)
+(user:ro le:type,level_s,level_c,other,other)
+(user:role:ty pe,level_s,level_c,other,other)
+(user:role:type,le vel_s,level_c,other,other)
+(user:role:type,level_s,le vel_c,other,other)
+
+( user :role:type,  level_s ,  level_c , other )
+( user:role:type,  level_s ,  level_c , other )
+
diff --git a/autotests/input/test.cil b/autotests/input/test.cil new file mode 100644 --- /dev/null +++ b/autotests/input/test.cil @@ -0,0 +1,144 @@ +; SELinux CIL Policy + +; Tests + +(policycap open_perms) ; Policy config. statement +(mls true) +(handleunknown allow) + +(sid kernel) ; Declaration type statement +(classpermissionset char_w (char (write setattr))) ; Other statements + +(user user) ; Declare identifier 'user' of user type +(role role) +(type type) +(allow allow) (true true) (in in) (xor xor) + +; List of permissions +(class security (compute_av compute_create compute_member check_context load_policy compute_relabel compute_user setenforce setbool setsecparam setcheckreqprot read_policy)) + +; Highlighting permissions only if there is not a statement keyword +(class binder (impersonate call set_context_mgr transfer receive)) +(class binder (classcommon impersonate call set_context_mgr transfer receive)) +(impersonate call set_context_mgr transfer receive) +(tunableif impersonate call set_context_mgr transfer receive) + +; This is allowed by the CIL compiler +( typeattribute;comment + all_fs_type_except_usermodehelper_and_proc_security) +(;comment + typeattribute all_fs_type_except_usermodehelper_and_proc_security) +( ;comment + ;more comments + typeattribute all_fs_type_except_usermodehelper_and_proc_security) + +; Paths +(true true /true true /true/true/ true true/true "true") +; Global namespace +(true true .true true true.true true .true.true true.true.true + .true. true. true.true. ; invalid +) + +; Keywords in some rules + +; filecon +(filecon "/system/bin/run-as" file runas_exec_context) +(filecon "/dev/socket/wpa_wlan[0-9]" any u:object_r:wpa.socket:s0-s0) +(filecon "/data/local/mine" dir ()) +(classcommon file any dir) +(file any dir) +; portcon +(portcon tcp 3333 (unconfined.user object_r unconfined.object levelrange_1)) +(portcon udp 4444 (unconfined.user object_r unconfined.object ((s0) level_2))) +(defaultrole tcp udp) +(tcp udp) +; fsuse +(fsuse xattr ext4 file.labeledfs_context) +(fsuse task pipefs file.pipefs_context) +(fsuse trans tmpfs file.tmpfs_context) +(typemember xattr task trans) +(xattr task trans) + +(allow unconfined.process self (file (read write))) +(allow process httpd.object (file (read write))) + +; Paths +"/system/(foo|bar)/[^/]*/(hi){2,6}(.*)?" +"/pa\12th.*a+b?" +/usr/hi\"esc\032esc\*3es{2,2}ds +"/data/(open " +"/data/[open " + + +; Some rules + +(call macro1("__kmsg__")) +(macro macro1 ((string ARG1)) + (typetransition audit.process device.device chr_file ARG1 device.klog_device) +) + +(allow unconfined.process self (file (read write))) +(auditallow release_app.process secmark_demo.browser_packet (packet (send recv))) +(allowx type_1 type_2 (ioctl tcp_socket (range 0x2000 0x20FF))) +(permissionx ioctl_nodebug (ioctl udp_socket (not (range 0x4000 0x4010)))) +(allowx type_3 type_4 ioctl_nodebug) +(dontauditx type_1 type_2 (ioctl tcp_socket (range 0x3000 0x30FF))) + +(class property_service (set)) +(block av_rules + (type type_1) + (type type_2) + (typeattribute all_types) + (typeattributeset all_types ((all))) + + (neverallow type_2 all_types (property_service (set))) +) +(macro binder_call ((type ARG1) (type ARG2)) + (allow ARG1 ARG2 (binder (transfer call))) +) +(ipaddr netmask_1 255.255.255.0) + +(class dir) +(class foo) +(class bar) +(class baz) +(classorder (dir foo)) +(classorder (unordered bar foo baz)) + +(classpermission zygote_2) +(classpermissionset zygote_2 (zygote + (and + (all) + (not (specifyinvokewith specifyseinfo)) + ) +)) + +(permissionx ioctl_3 (ioctl tcp_socket (and (range 0x8000 0x90FF) (not (range 0x8100 0x82FF))))) +(boolean disableAudioCapture false) +(booleanif (and (not disableAudio) (not disableAudioCapture)) + (true + (allow process mediaserver.audio_capture_device (chr_file_set (rw_file_perms))) + ) +) +(tunable range_trans_rule false) + +(block init + (class process (process)) + (type process) + (tunableif range_trans_rule + (true + (rangetransition process sshd.exec process low_high)))) + +(validatetrans file (eq t1 unconfined.process)) +(block ext_gateway + (optional move_file + (typetransition process msg_filter.move_file.in_queue file msg_filter.move_file.in_file) + (allow process msg_filter.move_file.in_queue (dir (read getattr write search add_name))))) + +(context runas_exec_context (u object_r exec low_low)) +(filecon "/system/bin/run-as" file runas_exec_context) + +(in file + (genfscon rootfs / rootfs_context) + (genfscon selinuxfs / selinuxfs_context) +) diff --git a/autotests/input/test.fc b/autotests/input/test.fc new file mode 100644 --- /dev/null +++ b/autotests/input/test.fc @@ -0,0 +1,104 @@ +# Sample SELinux Labeling Policy File + +# Syntax of 'file_contexts' file and other SELinux configuration files: + +/usr/lib/.*/program/foo\.so -- user:role:type:s0:c0 +/.* system_u:object_r:default_t:s0 +/sys(/.*)? system_u:object_r:sysfs_t:s0 +/xen(/.*)? system_u:object_r:xen_image_t:s1 +/mnt(/[^/]*)? -d system_u:object_r:mnt_t:s1-5 +/mnt(/[^/]*)? -l system_u:object_r:mnt_t:s0.s2 +/tmp/.* <> +/root(/.*)? system_u:object_r:admin_home_t:s0 +/dev/[0-9].* -c system_u:object_r:usb_device_t:s0 +/run/.*\.*pid <> +/mnt/[^/]*/.* <> +/etc/[mg]dm(/.*)? system_u:object_r:xdm_etc_t:s5-s6:c0 +/dev/(misc/)?psaux -c system_u:object_r:mouse_device_t:s0-s3:c0.c5 + +HOME_DIR/.+ system_u:object_r:user_home_t:s0 +HOME_DIR/((www)|(web)|(public_html))(/.+)? system_u:object_r:httpd_user_content_t:s0 +HOME_DIR/\.cache/google-chrome(/.*)? system_u:object_r:chrome_sandbox_home_t:s0 + +/dev/(misc/)?rtc[0-9]* -c system_u:object_r:clock_device_t:s0-s2:c1 +/var/(db|adm)/sudo(/.*)? system_u:object_r:pam_var_run_t:s0 +/dev/pcd[0-3] -b system_u:object_r:removable_device_t:s0 +/etc/ppp(/.*)? -- system_u:object_r:pppd_etc_rw_t:s0 +/var/www(/.*)? system_u:object_r:httpd_sys_content_t:s0 +/usr/lib(.*/)?bin(/.*)? system_u:object_r:bin_t:s0 +/dev/shm/.* <> +/usr/lib/(sse2/)?hello-.*\.so.* -- system_u:object_r:textrel_shlib_t:s0 +/sbin/grub.* -- system_u:object_r:bootloader_exec_t:s0.s3 +/sbin/lilo.* -- system_u:object_r:bootloader_exec_t:s0 +/etc/group[-\+]? -- system_u:object_r:passwd_file_t:s0:c1-c5 +/etc/rc\.d/init\.d/mpd -- system_u:object_r:mpd_initrc_exec_t:s0 + + +# Syntax of *.fc files, from the SELinux reference policy: + +/run/sudo/ts/%{USERNAME} gen_context(system_u:object_r:pam_var_run_t,s0,c0) +/etc/aiccu\.conf -- gen_context(system_u:object_r:aiccu_etc_t,s0-s2,c1.c5) +HOME_DIR/\.mtpz-data -- gen_context(system_u:object_r:libmtp_home_t,s0) +/var/log/mariadb(/.*)? gen_context(system_u:object_r:mysqld_log_t,s0) +/dev/dasd[^/]* -b gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh) +/dev/dasd[^/]* -c gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh) +HOME_ROOT -d gen_context(system_u:object_r:home_root_t,s0-mls_systemhigh,c1) +HOME_ROOT -l gen_context(system_u:object_r:home_root_t,s0) + +ifdef(`distro_debian',` + /run/shm -d gen_context(system_u:object_r:tmpfs_t,s0) + /run/shm/.* <> +') +ifdef(`distro_suse',` + /success -- gen_context(system_u:object_r:etc_runtime_t,s0) +') +ifdef(`init_systemd',` + /run/tmpfiles\.d/kmod\.conf -- gen_context(system_u:object_r:kmod_tmpfiles_conf_t,s0) +') + +# Tests + +# Variables +HOME_DIR/path +HOME_ROOT/path +/path/HOME_DIR/HOME_ROOT + +# Open brackets +/hello(world +/hello[wo + +/path[^0-8]+ +/path(hello|bye) +/path.*a+b? +/path\wa\Wa\sa\da\ba\Ba\(a +/usr/hi\"esc\sesc\032esc\*3esds + +# Security contexts +user:role +user:role: +user:role:type +user:role:type:level_sensitivity +user:role:type:level_sensitivity:level_category +user:role:type:level_sensitivity:level_category:other:other + +(user:role:type,) +(user:role:type,level_s,) +(user:role:type,level_s,level_c) +(user:role:type,level_s,level_c,other,other,other) +(user:role:type:level_s:level_c,other,other) +(user:role:type:level_s:level_c:other,other,other) + +us er:role:type:level_s:level_c +user:ro le:type:level_s:level_c +user:role:ty pe:level_s:level_c +user:role:type:lev el_s:level_c +user:role:type:level_s:lev el_c + +(u ser:role:type,level_s,level_c,other,other) +(user:ro le:type,level_s,level_c,other,other) +(user:role:ty pe,level_s,level_c,other,other) +(user:role:type,le vel_s,level_c,other,other) +(user:role:type,level_s,le vel_c,other,other) + +( user :role:type, level_s , level_c , other ) +( user:role:type, level_s , level_c , other ) diff --git a/autotests/reference/test.cil.ref b/autotests/reference/test.cil.ref new file mode 100644 --- /dev/null +++ b/autotests/reference/test.cil.ref @@ -0,0 +1,144 @@ +; SELinux CIL Policy
+
+; Tests
+
+(policycap open_perms) ; Policy config. statement
+(mls true)
+(handleunknown allow)
+
+(sid kernel) ; Declaration type statement
+(classpermissionset char_w (char (write setattr))) ; Other statements
+
+(user user) ; Declare identifier 'user' of user type
+(role role)
+(type type)
+(allow allow) (true true) (in in) (xor xor)
+
+; List of permissions
+(class security (compute_av compute_create compute_member check_context load_policy compute_relabel compute_user setenforce setbool setsecparam setcheckreqprot read_policy))
+
+; Highlighting permissions only if there is not a statement keyword
+(class binder (impersonate call set_context_mgr transfer receive))
+(class binder (classcommon impersonate call set_context_mgr transfer receive))
+(impersonate call set_context_mgr transfer receive)
+(tunableif impersonate call set_context_mgr transfer receive)
+
+; This is allowed by the CIL compiler
+( typeattribute;comment
+ all_fs_type_except_usermodehelper_and_proc_security)
+(;comment
+ typeattribute all_fs_type_except_usermodehelper_and_proc_security)
+( ;comment
+ ;more comments
+ typeattribute all_fs_type_except_usermodehelper_and_proc_security)
+
+; Paths
+(true true /true true /true/true/ true true/true "true")
+; Global namespace
+(true true .true true true.true true .true.true true.true.true
+ .true. true. true.true. ; invalid
+)
+
+; Keywords in some rules
+
+; filecon
+(filecon "/system/bin/run-as" file runas_exec_context)
+(filecon "/dev/socket/wpa_wlan[0-9]" any u:object_r:wpa.socket:s0-s0)
+(filecon "/data/local/mine" dir ())
+(classcommon file any dir)
+(file any dir)
+; portcon
+(portcon tcp 3333 (unconfined.user object_r unconfined.object levelrange_1))
+(portcon udp 4444 (unconfined.user object_r unconfined.object ((s0) level_2)))
+(defaultrole tcp udp)
+(tcp udp)
+; fsuse
+(fsuse xattr ext4 file.labeledfs_context)
+(fsuse task pipefs file.pipefs_context)
+(fsuse trans tmpfs file.tmpfs_context)
+(typemember xattr task trans)
+(xattr task trans)
+
+(allow unconfined.process self (file (read write)))
+(allow process httpd.object (file (read write)))
+
+; Paths
+"/system/(foo|bar)/[^/]*/(hi){2,6}(.*)?"
+"/pa\12th.*a+b?"
+/usr/hi\"esc\032esc\*3es{2,2}ds
+"/data/(open "
+"/data/[open "
+
+
+; Some rules
+
+(call macro1("__kmsg__"))
+(macro macro1 ((string ARG1))
+ (typetransition audit.process device.device chr_file ARG1 device.klog_device)
+)
+
+(allow unconfined.process self (file (read write)))
+(auditallow release_app.process secmark_demo.browser_packet (packet (send recv)))
+(allowx type_1 type_2 (ioctl tcp_socket (range 0x2000 0x20FF)))
+(permissionx ioctl_nodebug (ioctl udp_socket (not (range 0x4000 0x4010))))
+(allowx type_3 type_4 ioctl_nodebug)
+(dontauditx type_1 type_2 (ioctl tcp_socket (range 0x3000 0x30FF)))
+
+(class property_service (set))
+(block av_rules
+ (type type_1)
+ (type type_2)
+ (typeattribute all_types)
+ (typeattributeset all_types ((all)))
+
+ (neverallow type_2 all_types (property_service (set)))
+)
+(macro binder_call ((type ARG1) (type ARG2))
+ (allow ARG1 ARG2 (binder (transfer call)))
+)
+(ipaddr netmask_1 255.255.255.0)
+
+(class dir)
+(class foo)
+(class bar)
+(class baz)
+(classorder (dir foo))
+(classorder (unordered bar foo baz))
+
+(classpermission zygote_2)
+(classpermissionset zygote_2 (zygote
+ (and
+ (all)
+ (not (specifyinvokewith specifyseinfo))
+ )
+))
+
+(permissionx ioctl_3 (ioctl tcp_socket (and (range 0x8000 0x90FF) (not (range 0x8100 0x82FF)))))
+(boolean disableAudioCapture false)
+(booleanif (and (not disableAudio) (not disableAudioCapture))
+ (true
+ (allow process mediaserver.audio_capture_device (chr_file_set (rw_file_perms)))
+ )
+)
+(tunable range_trans_rule false)
+
+(block init
+ (class process (process))
+ (type process)
+ (tunableif range_trans_rule
+ (true
+ (rangetransition process sshd.exec process low_high))))
+
+(validatetrans file (eq t1 unconfined.process))
+(block ext_gateway
+ (optional move_file
+ (typetransition process msg_filter.move_file.in_queue file msg_filter.move_file.in_file)
+ (allow process msg_filter.move_file.in_queue (dir (read getattr write search add_name)))))
+
+(context runas_exec_context (u object_r exec low_low))
+(filecon "/system/bin/run-as" file runas_exec_context)
+
+(in file
+ (genfscon rootfs / rootfs_context)
+ (genfscon selinuxfs / selinuxfs_context)
+)
diff --git a/autotests/reference/test.fc.ref b/autotests/reference/test.fc.ref new file mode 100644 --- /dev/null +++ b/autotests/reference/test.fc.ref @@ -0,0 +1,104 @@ +# Sample SELinux Labeling Policy File
+
+# Syntax of 'file_contexts' file and other SELinux configuration files:
+
+/usr/lib/.*/program/foo\.so -- user:role:type:s0:c0
+/.* system_u:object_r:default_t:s0
+/sys(/.*)? system_u:object_r:sysfs_t:s0
+/xen(/.*)? system_u:object_r:xen_image_t:s1
+/mnt(/[^/]*)? -d system_u:object_r:mnt_t:s1-5
+/mnt(/[^/]*)? -l system_u:object_r:mnt_t:s0.s2
+/tmp/.* <>
+/root(/.*)? system_u:object_r:admin_home_t:s0
+/dev/[0-9].* -c system_u:object_r:usb_device_t:s0
+/run/.*\.*pid <>
+/mnt/[^/]*/.* <>
+/etc/[mg]dm(/.*)? system_u:object_r:xdm_etc_t:s5-s6:c0
+/dev/(misc/)?psaux -c system_u:object_r:mouse_device_t:s0-s3:c0.c5
+
+HOME_DIR/.+ system_u:object_r:user_home_t:s0
+HOME_DIR/((www)|(web)|(public_html))(/.+)? system_u:object_r:httpd_user_content_t:s0
+HOME_DIR/\.cache/google-chrome(/.*)? system_u:object_r:chrome_sandbox_home_t:s0
+
+/dev/(misc/)?rtc[0-9]* -c system_u:object_r:clock_device_t:s0-s2:c1
+/var/(db|adm)/sudo(/.*)? system_u:object_r:pam_var_run_t:s0
+/dev/pcd[0-3] -b system_u:object_r:removable_device_t:s0
+/etc/ppp(/.*)? -- system_u:object_r:pppd_etc_rw_t:s0
+/var/www(/.*)? system_u:object_r:httpd_sys_content_t:s0
+/usr/lib(.*/)?bin(/.*)? system_u:object_r:bin_t:s0
+/dev/shm/.* <>
+/usr/lib/(sse2/)?hello-.*\.so.* -- system_u:object_r:textrel_shlib_t:s0
+/sbin/grub.* -- system_u:object_r:bootloader_exec_t:s0.s3
+/sbin/lilo.* -- system_u:object_r:bootloader_exec_t:s0
+/etc/group[-\+]? -- system_u:object_r:passwd_file_t:s0:c1-c5
+/etc/rc\.d/init\.d/mpd -- system_u:object_r:mpd_initrc_exec_t:s0
+
+
+# Syntax of *.fc files, from the SELinux reference policy:
+
+/run/sudo/ts/%{USERNAME} gen_context(system_u:object_r:pam_var_run_t,s0,c0)
+/etc/aiccu\.conf -- gen_context(system_u:object_r:aiccu_etc_t,s0-s2,c1.c5)
+HOME_DIR/\.mtpz-data -- gen_context(system_u:object_r:libmtp_home_t,s0)
+/var/log/mariadb(/.*)? gen_context(system_u:object_r:mysqld_log_t,s0)
+/dev/dasd[^/]* -b gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh)
+/dev/dasd[^/]* -c gen_context(system_u:object_r:fixed_disk_device_t,mls_systemhigh)
+HOME_ROOT -d gen_context(system_u:object_r:home_root_t,s0-mls_systemhigh,c1)
+HOME_ROOT -l gen_context(system_u:object_r:home_root_t,s0)
+
+ifdef(`distro_debian',`
+ /run/shm -d gen_context(system_u:object_r:tmpfs_t,s0)
+ /run/shm/.* <>
+')
+ifdef(`distro_suse',`
+ /success -- gen_context(system_u:object_r:etc_runtime_t,s0)
+')
+ifdef(`init_systemd',`
+ /run/tmpfiles\.d/kmod\.conf -- gen_context(system_u:object_r:kmod_tmpfiles_conf_t,s0)
+')
+
+# Tests
+
+# Variables
+HOME_DIR/path
+HOME_ROOT/path
+/path/HOME_DIR/HOME_ROOT
+
+# Open brackets
+/hello(world
+/hello[wo
+
+/path[^0-8]+
+/path(hello|bye)
+/path.*a+b?
+/path\wa\Wa\sa\da\ba\Ba\(a
+/usr/hi\"esc\sesc\032esc\*3esds
+
+# Security contexts
+user:role
+user:role:
+user:role:type
+user:role:type:level_sensitivity
+user:role:type:level_sensitivity:level_category
+user:role:type:level_sensitivity:level_category:other:other
+
+(user:role:type,)
+(user:role:type,level_s,)
+(user:role:type,level_s,level_c)
+(user:role:type,level_s,level_c,other,other,other)
+(user:role:type:level_s:level_c,other,other)
+(user:role:type:level_s:level_c:other,other,other)
+
+us er:role:type:level_s:level_c
+user:ro le:type:level_s:level_c
+user:role:ty pe:level_s:level_c
+user:role:type:lev el_s:level_c
+user:role:type:level_s:lev el_c
+
+(u ser:role:type,level_s,level_c,other,other)
+(user:ro le:type,level_s,level_c,other,other)
+(user:role:ty pe,level_s,level_c,other,other)
+(user:role:type,le vel_s,level_c,other,other)
+(user:role:type,level_s,le vel_c,other,other)
+
+( user :role:type, level_s , level_c , other )
+( user:role:type, level_s , level_c , other )
diff --git a/data/syntax/selinux-cil.xml b/data/syntax/selinux-cil.xml new file mode 100644 --- /dev/null +++ b/data/syntax/selinux-cil.xml @@ -0,0 +1,974 @@ + + + + +]> + + + + + + + + + and + or + xor + not + all + eq + ne + neq + dom + domby + incomp + range + + + + + allow + auditallow + dontaudit + neverallow + auditdeny + allowx + auditallowx + dontauditx + neverallowx + + + + true + false + + + + file + dir + char + block + socket + pipe + symlink + any + + + task + trans + xattr + + + tcp + udp + dccp + + + + self + + + unordered + + + allow + deny + reject + + + + block + optional + common + class + classmap + classmapping + sid + user + role + roleattribute + type + classpermission + typeattribute + typealias + tunable + sensitivity + sensitivityalias + category + categoryalias + categoryset + level + levelrange + context + ipaddr + macro + boolean + + + + policycap + mls + handleunknown + + + + blockabstract + blockinherit + in + call + + + defaultuser + defaultrole + defaulttype + defaultrange + + + userrole + userattribute + userattributeset + userlevel + userrange + userbounds + userprefix + selinuxuser + selinuxuserdefault + + + roletype + roleattributeset + roleallow + roletransition + rolebounds + + + typealiasactual + typeattributeset + typebounds + typechange + typemember + typetransition + typepermissive + attributetype + + expandtypeattribute + nametypetransition + + + classcommon + classorder + permission + permissionset + classpermissionset + permissionx + + + booleanif + tunableif + + + constrain + validatetrans + mlsconstrain + mlsvalidatetrans + + + sensitivityaliasactual + sensitivityorder + categoryaliasactual + categoryorder + sensitivitycategory + rangetransition + categoryrange + + + + sidorder + sidcontext + + + filecon + fsuse + genfscon + fscon + fsusexattr + fsusetask + fsusetrans + + + netifcon + nodecon + portcon + + + + iomemcon + ioportcon + pcidevicecon + pirqcon + devicetreecon + + + ibpkeycon + ibendportcon + + + dominance + allowxperm + auditallowxperm + dontauditxperm + neverallowxperm + + + + string + name + ioctl + + + + source + target + low + high + low-high + + perm + object_r + t1 + t2 + t3 + r1 + r2 + r3 + u1 + u2 + u3 + l1 + l2 + h1 + h2 + + + + + accept + acceptfrom + access + acquire_svc + add + add_child + add_color + add_glyph + add_name + admin + append + associate + attach_queue + audit_access + audit_control + audit_read + audit_write + bell + bind + blend + block_suspend + call + check_context + chfn + chown + chsh + compute_av + compute_create + compute_member + compute_relabel + compute_user + connect + connectto + contains + copy + create + create_files_as + crontab + dac_override + dac_read_search + dccp_recv + dccp_send + debug + delete + destroy + disable + drop + dyntransition + egress + enable + enforce_dest + enqueue + entrypoint + execheap + execmem + execmod + execstack + execute + execute_no_trans + expand + export + flow_in + flow_out + force_cursor + fork + forward_in + forward_out + fowner + freeze + fsetid + get_param + get_property + get_value + getattr + getcap + getfocus + getgrp + gethost + getopt + getpgid + getpwd + getrlimit + getsched + getserv + getsession + getstat + grab + halt + hide + hide_cursor + impersonate + implement + import + ingress + insert + install + install_module + ioctl + ipc_info + ipc_lock + ipc_owner + kill + lease + link + linux_immutable + list_child + list_property + listen + load_module + load_policy + lock + mac_admin + mac_override + manage + manage_subnet + map + mknod + mmap_zero + module_load + module_request + mount + mounton + name_bind + name_connect + net_admin + net_bind_service + net_broadcast + net_raw + newconn + next_value + nlmsg_read + nlmsg_readpriv + nlmsg_relay + nlmsg_tty_audit + nlmsg_write + nnp_transition + noatsecure + node_bind + nosuid_transition + open + override + passwd + paste + paste_after_confirm + polmatch + ptrace + query + quotaget + quotamod + quotaon + rawip_recv + rawip_send + read + read_policy + reboot + receive + record + recv + recv_msg + recvfrom + relabelfrom + relabelto + reload + remount + remove + remove_child + remove_color + remove_glyph + remove_name + rename + reparent + rlimitinh + rmdir + rootok + saver_getattr + saver_hide + saver_setattr + saver_show + search + select + send + send_msg + sendto + set_context_mgr + set_param + set_property + set_value + setattr + setbool + setcap + setcheckreqprot + setcontext + setcurrent + setenforce + setexec + setfcap + setfocus + setfscreate + setgid + setkeycreate + setopt + setpcap + setpgid + setrlimit + setsched + setsecparam + setsockcreate + setuid + share + shmemgrp + shmemhost + shmempwd + shmemserv + show + show_cursor + shutdown + sigchld + siginh + sigkill + signal + signull + sigstop + start + status + stop + swapon + sys_admin + sys_boot + sys_chroot + sys_module + sys_nice + sys_pacct + sys_ptrace + sys_rawio + sys_resource + sys_time + sys_tty_config + syslog + syslog_console + syslog_mod + syslog_read + tcp_recv + tcp_send + transfer + transition + translate + udp_recv + udp_send + uninstall + unix_read + unix_write + unlink + unmount + update + use + use_as_override + validate_trans + view + wake_alarm + write + + + + + autofs + bdev + bpf + cachefs + cgroup + cgroup2 + cifs + coherent + configfs + cpuset + cramfs + debugfs + devfs + devpts + devtmpfs + ecryptfs + efs + fuse + fuseblk + fusectl + hugetlbfs + iso9660 + kernfs + mqueue + pipefs + proc + procfs + pstore + ramfs + romfs + rootfs + securityfs + selinuxfs + sockfs + specfs + squashfs + swapfs + sysfs + sysv + tmpfs + usbfs + vfat + + adfs + affs + apfs + btrfs + coda + exfat + ext2 + ext3 + ext4 + f2fs + fatx + hfs + hfsplus + hpfs + jfs + lvm2 + minix + msdos + ncpfs + nilfs + nilfs2 + nfs + nfs4 + ntfs-3g + qnx4 + qnx6 + reiser4 + reiserfs + smbfs + swap + tracefs + ubifs + udf + ufs + umsdos + urefs + xenix + xfs + zfs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/syntax/selinux-fc.xml b/data/syntax/selinux-fc.xml new file mode 100644 --- /dev/null +++ b/data/syntax/selinux-fc.xml @@ -0,0 +1,223 @@ + + +]> + + + + + + + + + gen_context + ifdef + define + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +