summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiroslav Grepl <mgrepl@redhat.com>2014-04-11 09:37:53 +0200
committerMiroslav Grepl <mgrepl@redhat.com>2014-04-11 09:37:53 +0200
commit47be9ff57e72906660bb62a515222f482131e1fb (patch)
tree2cb0ef0ba48d73b1df7cc0915754a17e19464bb6
downloadsetools-47be9ff57e72906660bb62a515222f482131e1fb.tar.gz
setools-47be9ff57e72906660bb62a515222f482131e1fb.tar.xz
setools-47be9ff57e72906660bb62a515222f482131e1fb.zip
Create setools-3.3.7 git repomaster
-rw-r--r--AUTHORS30
-rw-r--r--COPYING5
-rw-r--r--COPYING.GPL339
-rw-r--r--COPYING.LGPL504
-rw-r--r--ChangeLog1155
-rw-r--r--KNOWN-BUGS73
-rw-r--r--Makefile.am116
-rw-r--r--NEWS1018
-rw-r--r--README456
-rw-r--r--TODO29
-rw-r--r--VERSION.in1
-rw-r--r--apol/Makefile.am131
-rw-r--r--apol/analysis_tab.tcl326
-rw-r--r--apol/apol.gifbin0 -> 1305 bytes
-rw-r--r--apol/apol.pngbin0 -> 1408 bytes
-rw-r--r--apol/apol.xcfbin0 -> 9291 bytes
-rw-r--r--apol/apol_help.txt482
-rw-r--r--apol/apol_tcl.cc142
-rw-r--r--apol/apol_tcl.i451
-rw-r--r--apol/classes_perms_tab.tcl484
-rw-r--r--apol/common_widgets.tcl688
-rw-r--r--apol/cond_bools_tab.tcl266
-rw-r--r--apol/cond_rules_tab.tcl281
-rw-r--r--apol/context_dialog.tcl340
-rw-r--r--apol/context_selector.tcl150
-rw-r--r--apol/directflow_module.tcl582
-rw-r--r--apol/domaintrans_help.txt141
-rw-r--r--apol/domaintrans_module.tcl999
-rw-r--r--apol/file_contexts_tab.tcl504
-rw-r--r--apol/file_relabel_help.txt68
-rw-r--r--apol/find.tcl134
-rw-r--r--apol/foo_module.tcl143
-rw-r--r--apol/fscontexts_tab.tcl472
-rw-r--r--apol/goto.tcl78
-rw-r--r--apol/head.tcl29
-rw-r--r--apol/infoflow_help.txt321
-rw-r--r--apol/initial_sids_tab.tcl142
-rw-r--r--apol/level_dialog.tcl82
-rw-r--r--apol/mls_tab.tcl332
-rw-r--r--apol/netcontexts_tab.tcl878
-rw-r--r--apol/open_policy_dialog.tcl388
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver12575
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver15580
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver16560
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver17561
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver18922
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver19952
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver20993
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver21998
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver22998
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver23998
-rw-r--r--apol/perm_maps/apol_perm_mapping_ver241227
-rw-r--r--apol/perms_map.tcl410
-rw-r--r--apol/policyconf.tcl102
-rw-r--r--apol/progress_dialog.tcl74
-rw-r--r--apol/range_dialog.tcl132
-rw-r--r--apol/range_selector.tcl156
-rw-r--r--apol/range_trans.tcl204
-rw-r--r--apol/rbac_tab.tcl490
-rw-r--r--apol/relabel_module.tcl898
-rw-r--r--apol/roles_tab.tcl196
-rw-r--r--apol/terules_tab.tcl1034
-rw-r--r--apol/top.tcl1228
-rw-r--r--apol/transflow_module.tcl1156
-rw-r--r--apol/types_relation_help.txt53
-rw-r--r--apol/types_relation_module.tcl770
-rw-r--r--apol/types_tab.tcl411
-rw-r--r--apol/users_tab.tcl313
-rw-r--r--apol/util.tcl312
-rw-r--r--configure.ac896
-rw-r--r--debian/Makefile.am24
-rw-r--r--debian/changelog362
-rw-r--r--debian/compat1
-rw-r--r--debian/control183
-rw-r--r--debian/copyright48
-rw-r--r--debian/docs5
-rw-r--r--debian/libsetools-dev.install7
-rw-r--r--debian/libsetools-java.install2
-rw-r--r--debian/libsetools-jni.install1
-rw-r--r--debian/libsetools-jni.postinst41
-rw-r--r--debian/libsetools-jni.postrm39
-rw-r--r--debian/libsetools-python.install2
-rw-r--r--debian/libsetools-python.postinst41
-rw-r--r--debian/libsetools-tcl.install5
-rw-r--r--debian/libsetools.install5
-rw-r--r--debian/libsetools.postinst41
-rw-r--r--debian/libsetools.postrm39
-rwxr-xr-xdebian/rules25
-rw-r--r--debian/setools-console.install24
-rw-r--r--debian/setools.install19
-rw-r--r--debian/setools.menu6
-rw-r--r--debian/setools.postinst41
-rw-r--r--debian/setools.postrm39
-rw-r--r--debian/watch11
-rw-r--r--libapol/Makefile.am8
-rw-r--r--libapol/include/Makefile.am1
-rw-r--r--libapol/include/apol/Makefile.am35
-rw-r--r--libapol/include/apol/avrule-query.h373
-rw-r--r--libapol/include/apol/bool-query.h105
-rw-r--r--libapol/include/apol/bst.h178
-rw-r--r--libapol/include/apol/class-perm-query.h255
-rw-r--r--libapol/include/apol/condrule-query.h119
-rw-r--r--libapol/include/apol/constraint-query.h188
-rw-r--r--libapol/include/apol/context-query.h261
-rw-r--r--libapol/include/apol/domain-trans-analysis.h427
-rw-r--r--libapol/include/apol/fscon-query.h249
-rw-r--r--libapol/include/apol/infoflow-analysis.h387
-rw-r--r--libapol/include/apol/isid-query.h111
-rw-r--r--libapol/include/apol/mls-query.h228
-rw-r--r--libapol/include/apol/mls_level.h261
-rw-r--r--libapol/include/apol/mls_range.h270
-rw-r--r--libapol/include/apol/netcon-query.h364
-rw-r--r--libapol/include/apol/perm-map.h137
-rw-r--r--libapol/include/apol/permissive-query.h104
-rw-r--r--libapol/include/apol/polcap-query.h103
-rw-r--r--libapol/include/apol/policy-path.h194
-rw-r--r--libapol/include/apol/policy-query.h86
-rw-r--r--libapol/include/apol/policy.h166
-rw-r--r--libapol/include/apol/range_trans-query.h198
-rw-r--r--libapol/include/apol/rbacrule-query.h279
-rw-r--r--libapol/include/apol/relabel-analysis.h258
-rw-r--r--libapol/include/apol/render.h82
-rw-r--r--libapol/include/apol/role-query.h127
-rw-r--r--libapol/include/apol/terule-query.h321
-rw-r--r--libapol/include/apol/type-query.h172
-rw-r--r--libapol/include/apol/types-relation-analysis.h380
-rw-r--r--libapol/include/apol/user-query.h150
-rw-r--r--libapol/include/apol/util.h336
-rw-r--r--libapol/include/apol/vector.h335
-rw-r--r--libapol/src/Makefile.am75
-rw-r--r--libapol/src/avrule-query.c1209
-rw-r--r--libapol/src/bool-query.c111
-rw-r--r--libapol/src/bst.c352
-rw-r--r--libapol/src/class-perm-query.c327
-rw-r--r--libapol/src/condrule-query.c182
-rw-r--r--libapol/src/constraint-query.c217
-rw-r--r--libapol/src/context-query.c477
-rw-r--r--libapol/src/domain-trans-analysis-internal.h36
-rw-r--r--libapol/src/domain-trans-analysis.c2076
-rw-r--r--libapol/src/fscon-query.c437
-rw-r--r--libapol/src/infoflow-analysis-internal.h49
-rw-r--r--libapol/src/infoflow-analysis.c2245
-rw-r--r--libapol/src/isid-query.c123
-rw-r--r--libapol/src/libapol.map86
-rw-r--r--libapol/src/mls-query.c248
-rw-r--r--libapol/src/mls_level.c771
-rw-r--r--libapol/src/mls_range.c641
-rw-r--r--libapol/src/netcon-query.c592
-rw-r--r--libapol/src/perm-map.c697
-rw-r--r--libapol/src/permissive-query.c107
-rw-r--r--libapol/src/polcap-query.c107
-rw-r--r--libapol/src/policy-path.c409
-rw-r--r--libapol/src/policy-query-internal.h511
-rw-r--r--libapol/src/policy-query.c903
-rw-r--r--libapol/src/policy.c217
-rw-r--r--libapol/src/queue.c125
-rw-r--r--libapol/src/queue.h86
-rw-r--r--libapol/src/range_trans-query.c318
-rw-r--r--libapol/src/rbacrule-query.c417
-rw-r--r--libapol/src/relabel-analysis.c813
-rw-r--r--libapol/src/render.c158
-rw-r--r--libapol/src/role-query.c167
-rw-r--r--libapol/src/terule-query.c1049
-rw-r--r--libapol/src/type-query.c202
-rw-r--r--libapol/src/types-relation-analysis.c1143
-rw-r--r--libapol/src/user-query.c198
-rw-r--r--libapol/src/util.c659
-rw-r--r--libapol/src/vector-internal.h36
-rw-r--r--libapol/src/vector.c457
-rw-r--r--libapol/swig/Makefile.am15
-rw-r--r--libapol/swig/apol.i3220
-rw-r--r--libapol/swig/java/MANIFEST.MF.in12
-rw-r--r--libapol/swig/java/Makefile.am118
-rw-r--r--libapol/swig/python/Makefile.am36
-rw-r--r--libapol/swig/tcl/Makefile.am36
-rw-r--r--libapol/tests/Makefile.am23
-rw-r--r--libapol/tests/avrule-tests.c161
-rw-r--r--libapol/tests/avrule-tests.h35
-rw-r--r--libapol/tests/constrain-tests.c523
-rw-r--r--libapol/tests/constrain-tests.h33
-rw-r--r--libapol/tests/dta-tests.c529
-rw-r--r--libapol/tests/dta-tests.h35
-rw-r--r--libapol/tests/infoflow-tests.c127
-rw-r--r--libapol/tests/infoflow-tests.h36
-rw-r--r--libapol/tests/libapol-tests.c64
-rw-r--r--libapol/tests/policy-21-tests.c181
-rw-r--r--libapol/tests/policy-21-tests.h35
-rw-r--r--libapol/tests/role-tests.c154
-rw-r--r--libapol/tests/role-tests.h35
-rw-r--r--libapol/tests/terule-tests.c130
-rw-r--r--libapol/tests/terule-tests.h35
-rw-r--r--libapol/tests/user-tests.c159
-rw-r--r--libapol/tests/user-tests.h35
-rw-r--r--libpoldiff/Makefile.am8
-rw-r--r--libpoldiff/include/Makefile.am1
-rw-r--r--libpoldiff/include/poldiff/Makefile.am20
-rw-r--r--libpoldiff/include/poldiff/attrib_diff.h130
-rw-r--r--libpoldiff/include/poldiff/avrule_diff.h361
-rw-r--r--libpoldiff/include/poldiff/bool_diff.h146
-rw-r--r--libpoldiff/include/poldiff/cat_diff.h103
-rw-r--r--libpoldiff/include/poldiff/class_diff.h222
-rw-r--r--libpoldiff/include/poldiff/component_record.h159
-rw-r--r--libpoldiff/include/poldiff/level_diff.h159
-rw-r--r--libpoldiff/include/poldiff/poldiff.h218
-rw-r--r--libpoldiff/include/poldiff/range_diff.h129
-rw-r--r--libpoldiff/include/poldiff/range_trans_diff.h140
-rw-r--r--libpoldiff/include/poldiff/rbac_diff.h251
-rw-r--r--libpoldiff/include/poldiff/role_diff.h127
-rw-r--r--libpoldiff/include/poldiff/terule_diff.h262
-rw-r--r--libpoldiff/include/poldiff/type_diff.h132
-rw-r--r--libpoldiff/include/poldiff/type_map.h153
-rw-r--r--libpoldiff/include/poldiff/user_diff.h191
-rw-r--r--libpoldiff/include/poldiff/util.h45
-rw-r--r--libpoldiff/src/Makefile.am57
-rw-r--r--libpoldiff/src/attrib_diff.c544
-rw-r--r--libpoldiff/src/attrib_internal.h121
-rw-r--r--libpoldiff/src/avrule_diff.c1636
-rw-r--r--libpoldiff/src/avrule_internal.h296
-rw-r--r--libpoldiff/src/bool_diff.c333
-rw-r--r--libpoldiff/src/bool_internal.h122
-rw-r--r--libpoldiff/src/cat_diff.c289
-rw-r--r--libpoldiff/src/cat_internal.h120
-rw-r--r--libpoldiff/src/class_diff.c990
-rw-r--r--libpoldiff/src/class_internal.h212
-rw-r--r--libpoldiff/src/level_diff.c769
-rw-r--r--libpoldiff/src/level_internal.h208
-rw-r--r--libpoldiff/src/libpoldiff.map50
-rw-r--r--libpoldiff/src/poldiff.c814
-rw-r--r--libpoldiff/src/poldiff_internal.h239
-rw-r--r--libpoldiff/src/range_diff.c420
-rw-r--r--libpoldiff/src/range_internal.h80
-rw-r--r--libpoldiff/src/range_trans_diff.c520
-rw-r--r--libpoldiff/src/range_trans_internal.h124
-rw-r--r--libpoldiff/src/rbac_diff.c1052
-rw-r--r--libpoldiff/src/rbac_internal.h209
-rw-r--r--libpoldiff/src/role_diff.c543
-rw-r--r--libpoldiff/src/role_internal.h121
-rw-r--r--libpoldiff/src/terule_diff.c1329
-rw-r--r--libpoldiff/src/terule_internal.h244
-rw-r--r--libpoldiff/src/type_diff.c662
-rw-r--r--libpoldiff/src/type_internal.h125
-rw-r--r--libpoldiff/src/type_map.c985
-rw-r--r--libpoldiff/src/type_map_internal.h171
-rw-r--r--libpoldiff/src/user_diff.c789
-rw-r--r--libpoldiff/src/user_internal.h121
-rw-r--r--libpoldiff/src/util.c32
-rw-r--r--libpoldiff/src/writing-diffs-HOWTO351
-rw-r--r--libpoldiff/swig/Makefile.am15
-rw-r--r--libpoldiff/swig/java/MANIFEST.MF.in14
-rw-r--r--libpoldiff/swig/java/Makefile.am93
-rw-r--r--libpoldiff/swig/poldiff.i1335
-rw-r--r--libpoldiff/swig/python/Makefile.am39
-rw-r--r--libpoldiff/swig/tcl/Makefile.am37
-rw-r--r--libpoldiff/tests/Makefile.am19
-rw-r--r--libpoldiff/tests/components-tests.c544
-rw-r--r--libpoldiff/tests/components-tests.h49
-rw-r--r--libpoldiff/tests/libpoldiff-tests.c364
-rw-r--r--libpoldiff/tests/libpoldiff-tests.h85
-rw-r--r--libpoldiff/tests/mls-tests.c635
-rw-r--r--libpoldiff/tests/mls-tests.h39
-rw-r--r--libpoldiff/tests/nomls-tests.c139
-rw-r--r--libpoldiff/tests/nomls-tests.h33
-rw-r--r--libpoldiff/tests/policy-defs.h44
-rw-r--r--libpoldiff/tests/rules-tests.c914
-rw-r--r--libpoldiff/tests/rules-tests.h40
-rw-r--r--libqpol/Makefile.am8
-rw-r--r--libqpol/include/Makefile.am1
-rw-r--r--libqpol/include/qpol/Makefile.am30
-rw-r--r--libqpol/include/qpol/avrule_query.h167
-rw-r--r--libqpol/include/qpol/bool_query.h127
-rw-r--r--libqpol/include/qpol/class_perm_query.h209
-rw-r--r--libqpol/include/qpol/cond_query.h198
-rw-r--r--libqpol/include/qpol/constraint_query.h262
-rw-r--r--libqpol/include/qpol/context_query.h93
-rw-r--r--libqpol/include/qpol/fs_use_query.h115
-rw-r--r--libqpol/include/qpol/genfscon_query.h128
-rw-r--r--libqpol/include/qpol/isid_query.h91
-rw-r--r--libqpol/include/qpol/iterator.h95
-rw-r--r--libqpol/include/qpol/mls_query.h260
-rw-r--r--libqpol/include/qpol/mlsrule_query.h104
-rw-r--r--libqpol/include/qpol/module.h125
-rw-r--r--libqpol/include/qpol/netifcon_query.h104
-rw-r--r--libqpol/include/qpol/nodecon_query.h132
-rw-r--r--libqpol/include/qpol/permissive_query.h68
-rw-r--r--libqpol/include/qpol/polcap_query.h68
-rw-r--r--libqpol/include/qpol/policy.h261
-rw-r--r--libqpol/include/qpol/policy_extend.h86
-rw-r--r--libqpol/include/qpol/portcon_query.h118
-rw-r--r--libqpol/include/qpol/rbacrule_query.h130
-rw-r--r--libqpol/include/qpol/role_query.h119
-rw-r--r--libqpol/include/qpol/syn_rule_query.h314
-rw-r--r--libqpol/include/qpol/terule_query.h159
-rw-r--r--libqpol/include/qpol/type_query.h175
-rw-r--r--libqpol/include/qpol/user_query.h130
-rw-r--r--libqpol/include/qpol/util.h61
-rw-r--r--libqpol/src/Makefile.am89
-rw-r--r--libqpol/src/avrule_query.c288
-rw-r--r--libqpol/src/bool_query.c193
-rw-r--r--libqpol/src/class_perm_query.c653
-rw-r--r--libqpol/src/cond_query.c620
-rw-r--r--libqpol/src/constraint_query.c983
-rw-r--r--libqpol/src/context_query.c123
-rw-r--r--libqpol/src/expand.c190
-rw-r--r--libqpol/src/expand.h51
-rw-r--r--libqpol/src/fs_use_query.c167
-rw-r--r--libqpol/src/genfscon_query.c294
-rw-r--r--libqpol/src/isid_query.c139
-rw-r--r--libqpol/src/iterator.c797
-rw-r--r--libqpol/src/iterator_internal.h123
-rw-r--r--libqpol/src/libqpol.map73
-rw-r--r--libqpol/src/mls_query.c639
-rw-r--r--libqpol/src/mlsrule_query.c230
-rw-r--r--libqpol/src/module.c236
-rw-r--r--libqpol/src/module_compiler.c1440
-rw-r--r--libqpol/src/module_compiler.h115
-rw-r--r--libqpol/src/netifcon_query.c159
-rw-r--r--libqpol/src/nodecon_query.c329
-rw-r--r--libqpol/src/permissive_query.c95
-rw-r--r--libqpol/src/polcap_query.c92
-rw-r--r--libqpol/src/policy.c1563
-rw-r--r--libqpol/src/policy_define.c4319
-rw-r--r--libqpol/src/policy_define.h75
-rw-r--r--libqpol/src/policy_extend.c1397
-rw-r--r--libqpol/src/policy_parse.y834
-rw-r--r--libqpol/src/policy_scan.l320
-rw-r--r--libqpol/src/portcon_query.c183
-rw-r--r--libqpol/src/qpol_internal.h122
-rw-r--r--libqpol/src/queue.c183
-rw-r--r--libqpol/src/queue.h67
-rw-r--r--libqpol/src/rbacrule_query.c358
-rw-r--r--libqpol/src/role_query.c238
-rw-r--r--libqpol/src/syn_rule_internal.h47
-rw-r--r--libqpol/src/syn_rule_query.c782
-rw-r--r--libqpol/src/terule_query.c264
-rw-r--r--libqpol/src/type_query.c468
-rw-r--r--libqpol/src/user_query.c224
-rw-r--r--libqpol/src/util.c231
-rw-r--r--libqpol/swig/Makefile.am15
-rw-r--r--libqpol/swig/java/MANIFEST.MF.in9
-rw-r--r--libqpol/swig/java/Makefile.am97
-rw-r--r--libqpol/swig/python/Makefile.am34
-rw-r--r--libqpol/swig/python/__init__.py1
-rw-r--r--libqpol/swig/qpol.i2879
-rw-r--r--libqpol/swig/tcl/Makefile.am35
-rw-r--r--libqpol/tests/Makefile.am17
-rw-r--r--libqpol/tests/capabilities-tests.c542
-rw-r--r--libqpol/tests/capabilities-tests.h35
-rw-r--r--libqpol/tests/iterators-tests.c87
-rw-r--r--libqpol/tests/iterators-tests.h35
-rw-r--r--libqpol/tests/libqpol-tests.c57
-rw-r--r--libqpol/tests/policy-features-tests.c145
-rw-r--r--libqpol/tests/policy-features-tests.h35
-rw-r--r--libseaudit/Makefile.am8
-rw-r--r--libseaudit/include/Makefile.am1
-rw-r--r--libseaudit/include/seaudit/Makefile.am14
-rw-r--r--libseaudit/include/seaudit/avc_message.h374
-rw-r--r--libseaudit/include/seaudit/bool_message.h43
-rw-r--r--libseaudit/include/seaudit/filter.h1025
-rw-r--r--libseaudit/include/seaudit/load_message.h41
-rw-r--r--libseaudit/include/seaudit/log.h162
-rw-r--r--libseaudit/include/seaudit/message.h133
-rw-r--r--libseaudit/include/seaudit/model.h362
-rw-r--r--libseaudit/include/seaudit/parse.h72
-rw-r--r--libseaudit/include/seaudit/report.h140
-rw-r--r--libseaudit/include/seaudit/sort.h491
-rw-r--r--libseaudit/include/seaudit/util.h44
-rw-r--r--libseaudit/src/Makefile.am52
-rw-r--r--libseaudit/src/avc_message.c630
-rw-r--r--libseaudit/src/bool_message.c153
-rw-r--r--libseaudit/src/filter-internal.c1526
-rw-r--r--libseaudit/src/filter-internal.h109
-rw-r--r--libseaudit/src/filter.c1124
-rw-r--r--libseaudit/src/libseaudit.map88
-rw-r--r--libseaudit/src/load_message.c91
-rw-r--r--libseaudit/src/log.c253
-rw-r--r--libseaudit/src/message.c204
-rw-r--r--libseaudit/src/model.c808
-rw-r--r--libseaudit/src/parse.c1513
-rw-r--r--libseaudit/src/report.c1060
-rw-r--r--libseaudit/src/seaudit_internal.h664
-rw-r--r--libseaudit/src/sort.c744
-rw-r--r--libseaudit/src/util.c32
-rw-r--r--libseaudit/swig/Makefile.am15
-rw-r--r--libseaudit/swig/java/MANIFEST.MF.in14
-rw-r--r--libseaudit/swig/java/Makefile.am90
-rw-r--r--libseaudit/swig/python/Makefile.am39
-rw-r--r--libseaudit/swig/seaudit.i1373
-rw-r--r--libseaudit/swig/tcl/Makefile.am37
-rw-r--r--libseaudit/tests/Makefile.am16
-rw-r--r--libseaudit/tests/filters.c114
-rw-r--r--libseaudit/tests/filters.h35
-rw-r--r--libseaudit/tests/libseaudit-tests.c54
-rw-r--r--libseaudit/tests/parse_file.c113
-rw-r--r--libseaudit/tests/parse_file.h35
-rw-r--r--libsefs/Makefile.am8
-rw-r--r--libsefs/include/Makefile.am1
-rw-r--r--libsefs/include/sefs/Makefile.am10
-rw-r--r--libsefs/include/sefs/db.hh213
-rw-r--r--libsefs/include/sefs/entry.hh222
-rw-r--r--libsefs/include/sefs/fcfile.hh261
-rw-r--r--libsefs/include/sefs/fclist.hh323
-rw-r--r--libsefs/include/sefs/filesystem.hh181
-rw-r--r--libsefs/include/sefs/query.hh329
-rw-r--r--libsefs/include/sefs/util.h56
-rw-r--r--libsefs/src/Makefile.am52
-rw-r--r--libsefs/src/db.cc1304
-rw-r--r--libsefs/src/entry.cc213
-rw-r--r--libsefs/src/fcfile.cc691
-rw-r--r--libsefs/src/fclist.cc766
-rw-r--r--libsefs/src/filesystem.cc733
-rw-r--r--libsefs/src/libsefs.map29
-rw-r--r--libsefs/src/new_ftw.c749
-rw-r--r--libsefs/src/new_ftw.h183
-rw-r--r--libsefs/src/query.cc431
-rw-r--r--libsefs/src/sefs_internal.hh78
-rw-r--r--libsefs/src/util.c46
-rw-r--r--libsefs/swig/Makefile.am15
-rw-r--r--libsefs/swig/java/MANIFEST.MF.in14
-rw-r--r--libsefs/swig/java/Makefile.am85
-rw-r--r--libsefs/swig/python/Makefile.am39
-rw-r--r--libsefs/swig/sefs.i162
-rw-r--r--libsefs/swig/tcl/Makefile.am37
-rw-r--r--libsefs/tests/Makefile.am17
-rw-r--r--libsefs/tests/attic/fuse_non_mls.c195
-rwxr-xr-xlibsefs/tests/attic/launch-libsefs-tests.sh12
-rw-r--r--libsefs/tests/fcfile-tests.cc333
-rw-r--r--libsefs/tests/fcfile-tests.hh35
-rw-r--r--libsefs/tests/file_contexts.broken3
-rw-r--r--libsefs/tests/file_contexts.confed24
-rw-r--r--libsefs/tests/file_contexts.union16
-rw-r--r--libsefs/tests/libsefs-tests.cc52
-rw-r--r--m4/ac_check_classpath.m463
-rw-r--r--m4/ac_java_options.m446
-rw-r--r--m4/ac_pkg_swig.m4125
-rw-r--r--m4/ac_prog_jar.m452
-rw-r--r--m4/ac_prog_java.m4122
-rw-r--r--m4/ac_prog_java_works.m4137
-rw-r--r--m4/ac_prog_javac.m484
-rw-r--r--m4/ac_prog_javac_works.m475
-rw-r--r--m4/ac_python_devel.m4269
-rw-r--r--m4/c.m4265
-rw-r--r--m4/swig_python.m467
-rw-r--r--m4/tcl.m43960
-rw-r--r--man/Makefile.am19
-rw-r--r--man/apol.143
-rw-r--r--man/findcon.1119
-rw-r--r--man/indexcon.136
-rw-r--r--man/replcon.1102
-rw-r--r--man/seaudit-report.8.in38
-rw-r--r--man/seaudit.849
-rw-r--r--man/sechecker.1136
-rw-r--r--man/sediff.1118
-rw-r--r--man/sediffx.175
-rw-r--r--man/seinfo.1109
-rw-r--r--man/sesearch.1113
-rw-r--r--packages/BWidget-1.8.0.tar.bz2bin0 -> 164532 bytes
-rw-r--r--packages/Doxyfile1237
-rw-r--r--packages/Makefile.am38
-rw-r--r--packages/combobox.tcl764
-rw-r--r--packages/libapol.pc.in12
-rw-r--r--packages/libpoldiff.pc.in12
-rw-r--r--packages/libqpol.pc.in12
-rw-r--r--packages/libseaudit.pc.in12
-rw-r--r--packages/libsefs.pc.in12
-rw-r--r--packages/mainframe.tcl133
-rw-r--r--packages/notebook.tcl9
-rw-r--r--packages/rpm/Makefile.am11
-rw-r--r--packages/rpm/apol.desktop.in12
-rw-r--r--packages/rpm/fc9-compile.patch13
-rw-r--r--packages/rpm/seaudit.console.in3
-rw-r--r--packages/rpm/seaudit.desktop.in12
-rw-r--r--packages/rpm/seaudit.pam4
-rw-r--r--packages/rpm/sediffx.desktop.in12
-rw-r--r--packages/rpm/setools.spec661
-rw-r--r--seaudit/Makefile.am90
-rw-r--r--seaudit/dot_seaudit.in12
-rw-r--r--seaudit/filter_view.c1127
-rw-r--r--seaudit/filter_view.h42
-rw-r--r--seaudit/message_view.c1341
-rw-r--r--seaudit/message_view.h178
-rw-r--r--seaudit/modify_view.c327
-rw-r--r--seaudit/modify_view.h41
-rw-r--r--seaudit/open_policy_window.c469
-rw-r--r--seaudit/open_policy_window.h46
-rw-r--r--seaudit/policy_components_view.c373
-rw-r--r--seaudit/policy_components_view.h56
-rw-r--r--seaudit/policy_view.c573
-rw-r--r--seaudit/policy_view.h77
-rw-r--r--seaudit/preferences.c585
-rw-r--r--seaudit/preferences.h274
-rw-r--r--seaudit/preferences_view.c310
-rw-r--r--seaudit/preferences_view.h44
-rw-r--r--seaudit/progress.c200
-rw-r--r--seaudit/progress.h139
-rw-r--r--seaudit/report_window.c261
-rw-r--r--seaudit/report_window.h41
-rw-r--r--seaudit/seaudit-report-group.conf3
-rw-r--r--seaudit/seaudit-report-service.conf5
-rw-r--r--seaudit/seaudit-report-service.in24
-rw-r--r--seaudit/seaudit-report.c246
-rw-r--r--seaudit/seaudit-report.conf62
-rw-r--r--seaudit/seaudit-report.css178
-rw-r--r--seaudit/seaudit-small.pngbin0 -> 336 bytes
-rw-r--r--seaudit/seaudit.c418
-rw-r--r--seaudit/seaudit.glade7713
-rw-r--r--seaudit/seaudit.gladep7
-rw-r--r--seaudit/seaudit.h231
-rw-r--r--seaudit/seaudit.pngbin0 -> 992 bytes
-rw-r--r--seaudit/seaudit.xcfbin0 -> 7462 bytes
-rw-r--r--seaudit/seaudit_help.txt293
-rw-r--r--seaudit/toplevel.c1179
-rw-r--r--seaudit/toplevel.h263
-rw-r--r--seaudit/utilgui.c134
-rw-r--r--seaudit/utilgui.h106
-rw-r--r--sechecker/Makefile.am67
-rw-r--r--sechecker/modules/attribs_wo_rules.c520
-rw-r--r--sechecker/modules/attribs_wo_rules.h63
-rw-r--r--sechecker/modules/attribs_wo_types.c399
-rw-r--r--sechecker/modules/attribs_wo_types.h48
-rw-r--r--sechecker/modules/domain_and_file.c398
-rw-r--r--sechecker/modules/domain_and_file.h46
-rw-r--r--sechecker/modules/domains_wo_roles.c386
-rw-r--r--sechecker/modules/domains_wo_roles.h47
-rw-r--r--sechecker/modules/find_assoc_types.c407
-rw-r--r--sechecker/modules/find_assoc_types.h51
-rw-r--r--sechecker/modules/find_domains.c633
-rw-r--r--sechecker/modules/find_domains.h60
-rw-r--r--sechecker/modules/find_file_types.c636
-rw-r--r--sechecker/modules/find_file_types.h60
-rw-r--r--sechecker/modules/find_net_domains.c501
-rw-r--r--sechecker/modules/find_net_domains.h59
-rw-r--r--sechecker/modules/find_netif_types.c489
-rw-r--r--sechecker/modules/find_netif_types.h53
-rw-r--r--sechecker/modules/find_node_types.c479
-rw-r--r--sechecker/modules/find_node_types.h53
-rw-r--r--sechecker/modules/find_port_types.c513
-rw-r--r--sechecker/modules/find_port_types.h50
-rw-r--r--sechecker/modules/imp_range_trans.c513
-rw-r--r--sechecker/modules/imp_range_trans.h52
-rw-r--r--sechecker/modules/inc_dom_trans.c913
-rw-r--r--sechecker/modules/inc_dom_trans.h56
-rw-r--r--sechecker/modules/inc_mount.c520
-rw-r--r--sechecker/modules/inc_mount.h54
-rw-r--r--sechecker/modules/inc_net_access.c1852
-rw-r--r--sechecker/modules/inc_net_access.h51
-rw-r--r--sechecker/modules/roles_wo_allow.c387
-rw-r--r--sechecker/modules/roles_wo_allow.h49
-rw-r--r--sechecker/modules/roles_wo_types.c330
-rw-r--r--sechecker/modules/roles_wo_types.h47
-rw-r--r--sechecker/modules/roles_wo_users.c392
-rw-r--r--sechecker/modules/roles_wo_users.h53
-rw-r--r--sechecker/modules/spurious_audit.c778
-rw-r--r--sechecker/modules/spurious_audit.h53
-rw-r--r--sechecker/modules/template/profiles.readme142
-rw-r--r--sechecker/modules/template/template.howto13
-rw-r--r--sechecker/modules/template/xx.c393
-rw-r--r--sechecker/modules/template/xx.h72
-rw-r--r--sechecker/modules/types_wo_allow.c442
-rw-r--r--sechecker/modules/types_wo_allow.h49
-rw-r--r--sechecker/modules/unreachable_doms.c1053
-rw-r--r--sechecker/modules/unreachable_doms.h73
-rw-r--r--sechecker/modules/users_wo_roles.c321
-rw-r--r--sechecker/modules/users_wo_roles.h47
-rw-r--r--sechecker/profiles/all-checks-no-mls.sechecker98
-rw-r--r--sechecker/profiles/all-checks.sechecker102
-rw-r--r--sechecker/profiles/analysis-checks.sechecker78
-rw-r--r--sechecker/profiles/devel-checks.sechecker74
-rw-r--r--sechecker/profiles/sechecker.dtd19
-rw-r--r--sechecker/register_list.c102
-rw-r--r--sechecker/register_list.h64
-rw-r--r--sechecker/sechecker.c1230
-rw-r--r--sechecker/sechecker.h695
-rw-r--r--sechecker/sechecker_cli.c361
-rw-r--r--sechecker/sechecker_help.txt86
-rw-r--r--sechecker/sechk_parse.c285
-rw-r--r--sechecker/sechk_parse.h44
-rw-r--r--secmds/Makefile.am40
-rw-r--r--secmds/findcon.cc245
-rw-r--r--secmds/indexcon.cc121
-rw-r--r--secmds/replcon.cc358
-rw-r--r--secmds/seinfo.c2337
-rw-r--r--secmds/sesearch.c1173
-rw-r--r--sediff/Makefile.am54
-rw-r--r--sediff/find_dialog.c184
-rw-r--r--sediff/find_dialog.h63
-rw-r--r--sediff/open_policies_dialog.c527
-rw-r--r--sediff/open_policies_dialog.h45
-rw-r--r--sediff/policy_view.c391
-rw-r--r--sediff/policy_view.h84
-rw-r--r--sediff/progress.c202
-rw-r--r--sediff/progress.h139
-rw-r--r--sediff/remap_types_dialog.c564
-rw-r--r--sediff/remap_types_dialog.h56
-rw-r--r--sediff/result_item.c1361
-rw-r--r--sediff/result_item.h251
-rw-r--r--sediff/result_item_render.c421
-rw-r--r--sediff/result_item_render.h79
-rw-r--r--sediff/results.c708
-rw-r--r--sediff/results.h121
-rw-r--r--sediff/sediff.c653
-rw-r--r--sediff/sediff_help.txt259
-rw-r--r--sediff/sediffx-small.pngbin0 -> 281 bytes
-rw-r--r--sediff/sediffx-small.xcfbin0 -> 1639 bytes
-rw-r--r--sediff/sediffx.c317
-rw-r--r--sediff/sediffx.glade3619
-rw-r--r--sediff/sediffx.h108
-rw-r--r--sediff/sediffx.pngbin0 -> 607 bytes
-rw-r--r--sediff/sediffx.xcfbin0 -> 5882 bytes
-rw-r--r--sediff/select_diff_dialog.c134
-rw-r--r--sediff/select_diff_dialog.h42
-rw-r--r--sediff/toplevel.c728
-rw-r--r--sediff/toplevel.h202
-rw-r--r--sediff/utilgui.c168
-rw-r--r--sediff/utilgui.h125
614 files changed, 196158 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9c5cb5e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,30 @@
+SETools - Policy analysis tools for SELinux (C) 2001-2010
+Tresys Technology
+setools@tresys.com, http://oss.tresys.com/projects/setools
+
+Contributors:
+
+Alex Alberg
+James Athey
+Jeff Bankert
+Kevin Carr
+Alessandro Ferrucci
+Mark Goldman
+Ryan Jordan
+Karl MacMillan
+Frank Mayer
+Jeremy A. Mowery
+John Oliver
+Anand Patel
+Don Patterson
+Chris PeBenito
+Paul Rosenfeld
+Spencer Shimko
+Jeremy Solt
+David Sugar
+Jason Tang
+Karen Vance
+Brandon Whalen
+Meggan Whalen
+Randy Wicks
+David Windsor
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..25b8d0c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,5 @@
+The SETools package contains files under two licences -
+the GNU General Public License and the GNU Lesser General Public License.
+See COPYING.GPL and COPYING.LGPL respectively for the full text of
+these licences. All files distributed with this package indicate the
+appropriate license to use with that file.
diff --git a/COPYING.GPL b/COPYING.GPL
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING.GPL
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LGPL b/COPYING.LGPL
new file mode 100644
index 0000000..5ab7695
--- /dev/null
+++ b/COPYING.LGPL
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e492df4
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1155 @@
+2010-05-07 Spencer Shimko <sshimko@tresys.com>
+
+ * SETools 3.3.7.
+
+2010-04-30 Spencer Shimko <sshimko@tresys.com>
+
+ * Update the spec file for release.
+ * Update versions for release.
+
+2010-04-28 John Oliver <joliver@tresys.com>
+
+ * Restore close button in transitive info flow.
+ * Wrapped new policy db components so we can build against
+ new and old SELinux userspace.
+
+2010-04-28 Spencer Shimko <sshimko@tresys.com>
+
+ * Switch to a non-modal advanced filtering window.
+
+2010-04-23 Spencer Shimko <sshimko@tresys.com>
+
+ * Rebase module loading code on upstream userspace.
+
+ * Automatically detect base module instead of asking
+ user to identify the base.
+
+ * Support for bzipped policy packages (.pp) found in
+ Fedora 12 and later.
+
+2010-04-20 Chris PeBenito <cpebenito@tresys.com>
+
+ * Fix seinfo to correctly handle cases where there are
+ no nodecon statements in the policy.
+
+2010-04-20 Spencer Shimko <sshimko@tresys.com>
+
+ * Fixes error conditions that prevent apol from exiting. If
+ window/tab was already destroyed on exit
+ then apol threw an error and refused to exit.
+
+2010-04-15 Spencer Shimko <sshimko@tresys.com>
+
+ * Add support for displaying constraints to seinfo.
+ * Add constraint CUnit tests.
+
+2010-03-12 John Oliver <joliver@tresys.com>
+
+ * Ensure architecture independent parts of python SWIG wrapper
+ get installed into the same directory as the architecture
+ dependent parts.
+
+2010-03-12 Chris PeBenito <cpebenito@tresys.com>
+
+ * Fix seaudit/libseaudit headers.
+
+2010-03-02 John Oliver <joliver@tresys.com>
+
+ * Fix error message when Tk can not be loaded by apol. Make it
+ clear that it is Tk, not an SETools library that connot be loaded.
+
+2010-03-01 John Oliver <joliver@tresys.com>
+
+ * Fix error in Apol which would disable new and updated search
+ buttons in rule search.
+
+2009-12-09 John Oliver <joliver@tresys.com>
+
+ * Fix seaudit-report to correctly handle -c and --config
+ options.
+
+2009-07-22 Chris PeBenito <cpebenito@tresys.com>
+
+ * SETools 3.3.6.
+
+2009-07-16 Chris PeBenito <cpebenito@tresys.com>
+
+ * Change default log for seaudit to /var/log/audit/audit.log.
+
+2009-07-16 Chris PeBenito <cpebenito@tresys.com>
+
+ * Change context parsing in libseaudit to use libselinux functions.
+
+2009-06-04 Jeremy Solt <jsolt@tresys.com>
+
+ * Add support for display of level and clearance of contexts to seaudit.
+ * Add support for filterting by level and clearance to seaudit.
+
+2009-05-26 Jeremy Solt <jsolt@tresys.com>
+
+ * Modified seaudit to show the raw audit message when doubleclicking
+ an entry.
+
+2009-05-19 Steve Lawrence <slawrence@tresys.com>
+
+ * Add libqpol and libapol support for policy capabilites and
+ permissive domains.
+ * Add policy capabilities and permissive domains querying to seinfo.
+
+2009-05-15 Jeremy Solt <jsolt@tresys.com>
+
+ * Add support for using attribute names available in version 24
+ binary policies.
+
+2008-09-11 J. Tang <jtang@tresys.com>
+
+ * Fixed error in qpol_policy_rebuild_opt() where a policy's
+ neverallow rules are expanded when the rebuild option is
+ QPOL_POLICY_OPTION_NO_RULES.
+
+2008-08-15 J. Tang <jtang@tresys.com>
+
+ * This is the official release of SETools 3.3.5.
+
+ * Fixed errors in libapol's AV and TE rule rendering functions
+ where errno was not being set properly.
+
+ * Fixed error in apol_syn_avrule_render() where braces were not
+ being added around the target set when there is exactly one target
+ type and the keyword 'self'. Thanks to Ryan Kagin for reporting
+ this error.
+
+2008-08-14 J. Tang <jtang@tresys.com>
+
+ * Synched libqpol to libsepol version 2.0.32, policy parser to
+ 2.0.16.
+
+ * Synched libqpol to libsepol version 2.0.26, policy parser to
+ 2.0.14.
+
+ * Added qpol_type_get_ispermissive(). SETools can now handle
+ version 23 policy; bumped libqpol to version 1.4.
+
+2008-03-07 J. Tang <jtang@tresys.com>
+
+ * This is the official release of SETools 3.3.4.
+
+ * debian/control: Added transitional packages for libapol1,
+ libseaudit1, libsefs1, and their associated development package.
+
+ * packages/rpm/fc9-compile.diff: Added a patch to allow building
+ of SETools on Fedora 9, due to possible buggy gcc/glibc-header
+ interaction in libseaudit/swig/python.
+
+2008-03-05 J. Tang <jtang@tresys.com>
+
+ * Fixes to libapol and libqpol to allow SETools to compile under
+ gcc/g++ 4.3.
+
+ * Added autodetection of Tk, needed for Tk 8.5.
+
+ * Synced libqpol to policy parser to 2.0.13.
+
+2008-02-29 J. Tang <jtang@tresys.com>
+
+ * Synced libqpol to libsepol version 2.0.23, policy parser to
+ 2.0.10.
+
+2008-02-21 J. Tang <jtang@tresys.com>
+
+ * This is the official release of SETools 3.3.3.
+
+ * Fixes to qpol_default_policy_find() to properly return policies
+ whose versions are greater than the currently running system.
+
+2008-02-19 J. Tang <jtang@tresys.com>
+
+ * Added QPOL_POLICY_OPTION_MATCH_SYSTEM as a policy load option to
+ qpol_policy_open_from_file(). The tools seinfo, sesearch, and
+ sechecker will now use this flag when loading the system's default
+ policy.
+
+ * Modified libqpol to understand version 22 policy. Added
+ QPOL_CAP_POLCAPS to qpol_capability enum.
+
+2008-02-15 J. Tang <jtang@tresys.com>
+
+ * Synced libqpol to libsepol version 2.0.20. The configure script
+ will check if dynamic avtabs exist (introduced in libsepol 2.0.20)
+ and will adjust policy loading as necessary.
+
+2008-01-09 J. Tang <jtang@tresys.com>
+
+ * Added initial attempt at Debian-izing SETools. The Debian
+ control files are only present in the SVN checkout of SETools, not
+ in the distributed tarball.
+
+2007-11-02 J. Tang <jtang@tresys.com>
+
+ * Ported bug fixes in branches/setools-devel to trunk. This is
+ the official release of SETools 3.3.2.
+
+ * Fixed error in libsefs/Java where the library has not been built
+ correctly since version 4 of libsefs.
+
+2007-10-31 J. Mowery <jmowery@tresys.com>
+
+ * Fix to libqpol when querying policies with no genfscon statements.
+
+ * Fix to SWIG wrappers to correctly report exceptions in multi-threaded
+ Java environments.
+
+2007-10-31 J. Tang <jtang@tresys.com>
+
+ * Modifed libqpol to explicitly ignore disabled aliases.
+
+ * Merged RedHat's spec file changes.
+
+2007-10-08 J. Tang <jtang@tresys.com>
+
+ * Fix to libqpol when qpol_type_get_alias_iter() could erroneously
+ be set to an initially wrong position for certain primary types.
+
+2007-10-02 J. Tang <jtang@tresys.com>
+
+ * Fix to libqpol to synthesize object_r. This was previously
+ (erroneously) handled in libapol.
+
+2007-09-26 J. Tang <jtang@tresys.com>
+
+ * Fix to apol where empty levels would throw an error during
+ validation.
+
+2007-09-04 J. Mowery <jmowery@tresys.com>
+
+ * Fixes to libsefs and apol if an invalid regular expression is
+ given.
+
+2007-08-30 J. Mowery <jmowery@tresys.com>
+
+ * Do not look for neverallow rules on default avrule query if not
+ available.
+
+2007-08-22 J. Tang <jtang@tresys.com>
+
+ * Ported bug fixes in branches/setools-devel to trunk. This is
+ the official release of SETools 3.3.1.
+
+2007-08-20 J. Tang <jtang@tresys.com>
+
+ * Fixed memory leak in libapol/infoflow analysis, where edges were
+ not being freed. Thanks to Ben Martin for spotting this error.
+
+2007-08-16 J. Tang <jtang@tresys.com>
+
+ * Fixed an error in libapol's infoflow. When performing a
+ transitive search, let there be an intermediate rule with source
+ A, an attribute. If a member of A, type T, is excluded by not
+ calling apol_infoflow_analysis_append_intermediate(), it was still
+ possible to return an infoflow result that uses T. This has been
+ fixed.
+
+2007-08-13 J. Tang <jtang@tresys.com>
+
+ * Fix error in libsefs when querying a filesystem or fcfile by
+ range, but the fclist is not MLS.
+
+2007-08-06 J. Tang <jtang@tresys.com>
+
+ * configure.ac: Tk is checked only if --enable-swig-tcl and
+ --enable-gui are given. Also, if tclconfig.sh is not found, then
+ abort configuration.
+
+ * apol/top.tcl: Fixed error where reading from an older .apol file
+ (from SETools < 3.2) would cause a startup problem.
+
+ * libapol/include/apol/domain-trans-analysis.h: Upon invalid
+ transition, empty vectors are returned rather than NULL pointers.
+
+2007-08-02 J. Tang <jtang@tresys.com>
+
+ * Official release of SETools 3.3.
+
+2007-08-02 J. Mowery <jmowery@tresys.com>
+
+ * Corrected sechecker module incomplete domain transitions
+ to once again return results for missing RBAC and user policy
+ for transitions that have sufficient type enforcement rules.
+
+2007-07-27 J. Tang <jtang@tresys.com>
+
+ * Library jar files are now installed in
+ $PREFIX/share/setools-<version>, with symlinks in
+ $PREFIX/share/java. They used to be placed in
+ $PREFIX/lib/setools.
+
+ * Added framework for CUnit tests for libseaudit.
+
+2007-07-26 J. Tang <jtang@tresys.com>
+
+ * Updated all linker map and SWIG interface files to match
+ exported library headers.
+
+ * libseaudit/src/filter.c (seaudit_filter_set_match): Added
+ ability to set strictness of libseaudit filters. Be default,
+ filters are not strict; this matches previous libseaudit behavior.
+ See seaudit_filter_set_match() for definition of "strictness".
+
+2007-07-26 M. Goldman <mgoldman@tresys.com>
+ * Added tooltip to glade interface noting that diffing never
+ allow rules could dramaticly increase run time
+
+ * Fixed names in sediffx to be more descriptive (the tree view
+ in the upper left hand corner). Names changed from
+ AVRules auditallow to Audit Allow Rules as an example.
+
+2007-07-24 J. Tang <jtang@tresys.com>
+
+ * Added framework for CUnit tests for libapol.
+
+2007-07-23 J. Tang <jtang@tresys.com>
+
+ * Added framework for CUnit tests for libpoldiff and libqpol.
+ configure now has the option --with-test-policies to specify where
+ the test policies reside.
+
+2007-07-20 J. Tang <jtang@tresys.com>
+
+ * Added seaudit_filter for PID.
+
+2007-07-19 J. Tang <jtang@tresys.com>
+
+ * Added many optimizations to qpol_policy_build_syn_rule_table()
+ and associated functions. With some code rewrites, a doubling of
+ the syntactic rule hash table size, and an improved hashing
+ function, it is at least three times faster to build the table.
+
+2007-07-18 J. Tang <jtang@tresys.com>
+
+ * Fixed error in libpoldiff where if an AV or a TE rule was within
+ a conditional that had multiple boolean variables, the diff
+ algorithm would generate an incorrect STC key for that rule.
+
+2007-07-17 J. Mowery <jmowery@tresys.com>
+
+ * Updated domain transition analysis to use the BST internally;
+ this should result in improved performance.
+
+2007-07-13 J. Tang <jtang@tresys.com>
+
+ * Java SWIG libraries now check if the libraries needs to be
+ loaded before doing such.
+
+ * Added more filtering options to libseaudit.
+
+2007-07-12 J. Tang <jtang@tresys.com>
+
+ * seaudit_filter_set_ipaddress() is renamed to
+ seaudit_filter_set_anyaddr(). seaudit_filter_set_port() is now
+ seaudit_filter_set_anyport(). Similar changes occurred to the
+ respective accessors.
+
+2007-07-11 M. Goldman <mgoldman@tresys.com>
+
+ * Split functions relating to item records out into their own
+ file. Added documentation for same.
+
+2007-07-09 J. Tang <jtang@tresys.com>
+
+ * Added additional sort constructors to libseaudit.
+
+2007-07-05 J. Tang <jtang@tresys.com>
+
+ * seaudit now has a button to clear all messages from a view.
+ This is useful when doing log monitoring, to show only new
+ messages.
+
+ * Added seaudit_model_hide_message(), to suppress display of
+ certain messages within a model.
+
+2007-06-28 J. Tang <jtang@tresys.com>
+
+ * Updated apol and sediffx to delay loading neverallow rules
+ unless the user requested analyzing them.
+
+2007-06-25 J. Tang <jtang@tresys.com>
+
+ * Updated apol(1) to use new libsefs
+
+ * Updated replcon(1) to use new libsefs.
+
+2007-06-19 J. Tang <jtang@tresys.com>
+
+ * Updated indexcon(1) to use new libsefs.
+
+ * Merged searchcon(1) functionality into findcon(1); man pages
+ updated. searchcon(1) has been removed from SETools.
+
+ * libsefs.so.4 is now wrapped by SWIG, for Python, Java, and
+ Tcl.
+
+ * Rewrite of libsefs to meet coding standards. libsefs.so.4 is an
+ object-oriented library that abstracts away filesystems,
+ file_contexts files, and databases into a unified object that can
+ be queried easily.
+
+2007-06-18 J. Tang <jtang@tresys.com>
+
+ * libpoldiff/include/poldiff/rbac_diff.h: Added
+ poldiff_role_allow_get_unmodified_roles(). The return values
+ poldiff_role_allow_get_added_roles() and
+ poldiff_role_allow_get_removed_roles() have been changed for forms
+ POLDIFF_FORM_ADDED and POLDIFF_FORM_REMOVED.
+
+2007-06-14 J. Tang <jtang@tresys.com>
+
+ * SETools can now compile against GTK+ 2.4. Nonetheless, GTK+ 2.8
+ is still the recommended toolkit.
+
+2007-06-13 J. Tang <jtang@tresys.com>
+
+ * libseaudit/src/log.c (seaudit_log_clear): Added ability to clear
+ a log of its messages.
+
+2007-06-12 J. Tang <jtang@tresys.com>
+
+ * SETools now requires libsqlite3, version 3.2.0 or greater. It
+ used to ship with its own copy of sqlite3; that is no longer the
+ case.
+
+2007-06-11 M. Goldman <mgoldman@tresys.com>
+ * Updated swig interface to properly expose av and te rule
+ vector accessor functions for libpoldiff.
+
+2007-06-08 J. Mowery <jmowery@tresys.com>
+
+ * Fixed an error in libpoldiff where
+ poldiff_terule_get_modified_default() was returning the
+ original default; it now correctly returns the modified
+ default type.
+
+2007-06-08 M. Goldman <mgoldman@tresys.com>
+
+ * updated sediffx diffs so that the subrules for avrules and
+ terules are broken out into allow, neverallow, auditallow,
+ dontaudit type_member, type_change, and type_transition rules
+
+2007-06-04 J. Tang <jtang@tresys.com>
+
+ * added convert functions to apol_context_t and
+ apol_mls_range_t; added constructors
+ apol_mls_range_create_from_string() and
+ apol_mls_range_create_from_literal().
+
+2007-05-31 J. Tang <jtang@tresys.com>
+
+ * libapol/src/util.c (apol_str_to_objclass): Added this function,
+ as the complement to apol_objclass_to_str().
+
+ * libapol/src/mls_level.c (apol_mls_level_is_literal),
+ libapol/src/mls_range.c (apol_mls_range_is_literal): Added
+ ability to query if level/range is literal. This is needed when
+ printing an apol_context_t containing a literal level.
+
+ * libapol: split mls-query.h into mls-query.h, mls_level.h, and
+ mls_range.h. This file was much too large.
+
+2007-05-30 J. Tang <jtang@tresys.com>
+
+ * libapol/src/mls-query.c (apol_mls_level_create_from_literal)
+ (apol_mls_level_convert): The apol_mls_level_t object can now be
+ created without relying upon a policy. Call
+ apol_mls_level_convert() to complete its category list.
+
+2007-05-30 J. Mowery <jmowery@tresys.com>
+
+ * The --regex flag in sesearch now also applies to --class.
+
+2007-05-29 J. Tang <jtang@tresys.com>
+
+ * ./configure no longer has --enable-sefs flag, for libselinux is
+ required to build SETools. (The kernel does need to have SELinux
+ running, though.) Therefore, libsefs is now always built.
+
+ * apol converted to use the SWIG interface for Tcl.
+
+ * libapol/src/types-relation-analysis.c
+ (apol_types_relation_domain): Domain transition table was not
+ correctly reset when intermixing calls between
+ apol_types_relation_analysis_do() and
+ apol_domain_trans_analysis_do().
+
+2007-05-22 J. Tang <jtang@tresys.com>
+
+ * Added infoflow defines to apol.i. Added apol_file_find_path()
+ to apol.i. They were inadvertently missing from the interface
+ file.
+
+2007-05-22 J. Mowery <jmowery@tresys.com>
+
+ * Renamed apol_permmap_*() to apol_policy_*_permmap() to
+ correctly reflect the fact that the policy owns the permmap.
+
+2007-05-21 J. Mowery <jmowery@tresys.com>
+
+ * Renamed apol_policy_domain_trans_table_build() and
+ apol_domain_trans_table_reset() to apol_policy_build_domain_trans_table()
+ and apol_polciy_reset_domain_trans_table() respectively. This change
+ reflects the correct namespace for these operations.
+
+2007-05-18 J. Mowery <jmowery@tresys.com>
+
+ * Deprecated qpol_policy_extend(); this function is called
+ automatically by qpol_policy_open* and qpol_policy_rebuild()
+ and therefore does not need to be called separately.
+
+2007-05-16 J. Tang <jtang@tresys.com>
+
+ * Right-clicking an apol results display now pops up a menu, that
+ allows the user to copy and to select all.
+
+ * libapol/swig/apol.i: Within the SWIG interfaces, there now
+ exists an object "apol_ip_t", that represents both an IP address
+ and a protocol. apol_str_to_internal_ip() returns an apol_ip_t,
+ which may then be used when setting an apol_nodecon_query_t. Note
+ that this only affects the SWIG generated libraries, not the
+ original libapol.
+
+ * SETools now requires a C99 compliant C compiler. The supplied
+ configure script will check for a working C99 compiler.
+
+2007-05-16 M. Goldman <mgoldman@tresys.com>
+ * modified libpoldiff to support partial diffing of avrules
+
+ * modified sediff to expose partial diffing of avrules functionality
+
+2007-05-15 J. Tang <jtang@tresys.com>
+
+ * libqpol/src/mls_query.c (qpol_level_get_alias_iter): Fixed
+ possible error when getting a level's alias iterator, where the
+ first element returned might be the original level instead of one
+ of its alias.
+
+ * libapol/include/apol/policy-path.h, libapol/include/apol/util.h:
+ Headers errantly included <config.h>.
+
+ * libapol/src/netcon-query.c (apol_nodecon_query_set_proto)
+ (apol_portcon_query_set_proto): These have been renamed to
+ apol_nodecon_query_set_protocol() and
+ apol_portcon_query_set_protocol() respectively.
+
+ * libapol/src/util.c (apol_str_to_protocol): Added this function,
+ to complement existing apol_protocol_to_str().
+
+ * libapol/src/mls-query.c (apol_mls_range_contain_subrange): Fixed
+ potential segfault if a range's high level is not yet set when
+ comparison occurs.
+
+2007-05-14 J. Mowery <jmowery@tresys.com>
+
+ * correctly marked libqpol defines for genfscon object classes
+ and fs_use behaviors as unsigned constants.
+
+2007-05-14 J. Tang <jtang@tresys.com>
+
+ * libapol/src/role-query.c (apol_role_get_by_query),
+ libapol/src/user-query.c (apol_user_get_by_query): Extra logic
+ added when "object_r" is given as the role. For user queries, all
+ users implicitly have object_r assigned to them. For role
+ queries, all types are assigned to object_r.
+
+ * libapol/swig/apol.i: apol_context_validate() and
+ apol_context_validate_partial() are now member functions of class
+ apol_context_t, rather than being a library function.
+
+ * libapol/include/mls-query.h (apol_mls_cat_name_compare): Removed
+ apol_mls_cat_name_compare() from the libapol public API. This
+ function should never have been made public.
+
+ * libapol/src/context-query.c: Fixed potential segfaults in
+ modifiers to apol_context_t object, if the same pointer returned
+ by its accessor is then passed back in the modifier modifier.
+
+ * libapol/src/context-query.c (apol_context_render): When
+ rendering a partial context, unset fields are now explicitly
+ represented by an asterisk, rather than by an empty string.
+
+2007-05-11 J. Mowery <jmowery@tresys.com>
+
+ * removed usage of typedef'ed bool_t and replaced with bool as
+ defined by stdbool.h; also replaced defined TRUE and FALSE
+ with true and false constants as defined by stdbool.h.
+
+2007-05-11 J. Tang <jtang@tresys.com>
+
+ * Fix potential segfaults in apol_mls_range functions if the high
+ level is not set.
+
+ * Added apol_mls_level_validate() to SWIG wrappers. Added
+ apol_mls_range_get_low() and apol_mls_range_get_high().
+ apol_mls_range_validate() is now a member function of class
+ apol_mls_range_t, rather than a library function.
+
+ * Within the SWIG wrappers, apol_mls_level_get_cats() returns a
+ vector. This should have been a string vector instead.
+
+ * Added function apol_mls_level_validate().
+
+2007-05-10 J. Tang <jtang@tresys.com>
+
+ * Added setools-libs-tcl target to setools.spec.
+
+ * Removed apol/apol.c; apol is now just a Tcl script that is
+ executed by tclsh, rather than a custom compiled Tcl interpreter.
+
+ * Removed the deprecated header apol/avl-util.h and its associated
+ source file.
+
+2007-05-08 M. Goldman <mgoldman@tresys.com>
+ * Added next and previous buttons to seaudit
+
+ * Added apol_bst_inorder_map()
+
+2007-05-07 J. Mowery <jmowery@tresys.com>
+
+ * Changed apol_ipv4_addr_render so that it now takes an array
+ of uint32_t's instead of a single uint32_t.
+
+ * Changed SWIG wrapping for apol_perm_query_t::run() to return
+ apol_string_vector_t instead of apol_vector_t.
+
+ * Added exported pointers to SWIG wrappers of each library
+ to allow changing of the message handler callback and its
+ void * argument.
+
+ * qpol_policy_rebuild() no longer requires a modular policy;
+ it can safely be called for any type of policy.
+
+2007-05-04 J. Tang <jtang@tresys.com>
+
+ * Fixed qpol_module function names in the libqpol SWIG wrappers.
+
+2007-05-04 J. Mowery <jmowery@tresys.com>
+
+ * Added SWIG wrappers for tcl to libpoldiff and libseaudit.
+
+2007-05-02 J. Mowery <jmowery@tresys.com>
+
+ * Versioned symbols in other libraries.
+
+2007-05-01 J. Mowery <jmowery@tresys.com>
+
+ * Versioned symbols in libqpol to allow conditional expansion
+ of neverallow rules from qpol_policy_open_from_*.
+
+2007-05-01 J. Tang <jtang@tresys.com>
+
+ * Added apol_avrule_query_set_all_perms(). This changes the
+ behavior when matching multiple permissions.
+
+2007-04-30 J. Tang <jtang@tresys.com>
+
+ * Added --enable-swig-tcl flag to configure script.
+
+2007-04-25 J. Tang <jtang@tresys.com>
+
+ * Official release of SETools-3.2.
+
+2007-04-20 J. Tang <jtang@tresys.com>
+
+ * Added seaudit_log_parse_buffer(), needed because
+ seaudit_log_parse() is not possible with the Java SWIG library.
+
+ * Build system updated to suppress warnings from automake
+ 1.10, and for parallel compilation (i.e., make -j).
+
+2007-04-18 J. Mowery <jmowery@tresys.com>
+
+ * The function apol_domain_trans_result_create_from_domain_trans_result()
+ is now publicly exported. Also added is apol_domain_trans_result_destroy()
+ to free memory used by duplicated results.
+
+2007-04-17 J. Tang <jtang@tresys.com>
+
+ * Fixed potential segfault in seaudit_filter_set_date() when
+ called using the same struct tm pointers that were returned by
+ seaudit_filter_get_date(). The old code would dereference
+ memory that was just free()d.
+
+2007-04-16 J. Tang <jtang@tresys.com>
+
+ * apol_mls_range_get_levels() fixed so that its returned levels
+ only include categories that are valid for the given policy.
+ Before it just copied the categories for the high level, even if a
+ lower level could not actually have one of those categories.
+
+ * apol_mls_level_get_cats() now always returns categories in
+ alphabetical order.
+
+2007-04-06 J. Tang <jtang@tresys.com>
+
+ * Added seaudit_avc_message_get_name() and
+ seaudit_sort_by_name(). seaudit now shows AVC messages' name
+ field; this column may be be hidden through the updated
+ preferences dialog.
+
+ * Updated libqpol to use libsepol >= 2.0.0 if available.
+ configure should be able to autodetect this.
+
+2007-04-05 J. Tang <jtang@tresys.com>
+
+ * sediffx now allows the user to select which components to diff,
+ rather than always diffing everything.
+
+ * fix to domain transition analysis, where setexec rules might not
+ be found correctly given certain policies.
+
+2007-04-04 J. Tang <jtang@tresys.com>
+
+ * New configure option --enable-swig-java to enable build of Java
+ SWIG wrappers.
+
+2007-04-03 J. Mowery <jmowery@tresys.com>
+
+ * Added apol_file_is_policy_path_list() to validate policy path
+ list files.
+
+ * Added ability to specify policy path list files from command
+ line for all tools taking a policy as an argument.
+
+2007-04-02 J. Mowery <jmowery@tresys.com>
+
+ * Removed the "all_files" symbol from libsefs; this symbol was
+ previously marked as deprecated.
+
+2007-03-29 J. Mowery <jmowery@tresys.com>
+
+ * Libpoldiff SWIG wrapper added; wrapper treats all structures as
+ classes in the target language.
+
+2007-03-28 J. Tang <jtang@tresys.com>
+
+ * Introduced 'policy list' in apol, seaudit, and sediffx. This is
+ a small text file that contains references to a base policy and
+ any number of modules.
+
+2007-03-27 J. Tang <jtang@tresys.com>
+
+ * added apol_policy_path_create_from_file() and
+ apol_policy_path_to_file().
+
+ * apol_str_trim() no longer has a return value; it also operates
+ directly on a string, rather than a reference to a string.
+
+ * apol_vector_t and apol_bst_t now require a destructor function
+ to be given during creation time, rather than being passed as a
+ second parameter respectively to apol_vector_destroy() and
+ apol_bst_destroy(). All of SETools has been updated to this
+ scheme.
+
+2007-03-26 J. Tang <jtang@tresys.com>
+
+ * apol_context_t is now an opaque structure. Accessors added to
+ the user, role, type, and range fields.
+
+2007-03-23 J. Tang <jtang@tresys.com>
+
+ * added apol_mls_level_get_sens() and apol_mls_level_get_cats().
+
+2007-03-23 J. Mowery <jmowery@tresys.com>
+
+ * Libseaudit SWIG wrapper added; wrapper treats all structures as
+ classes in the target language.
+
+2007-03-23 J. Tang <jtang@tresys.com>
+
+ * apol_mls_level_t and apol_mls_range_t are now opaque structures.
+
+ * fixed error in seaudit_model_create_from_model(), where the
+ duplicate's filters and sorts were being linked to the original
+ model, not the newly created one.
+
+ * fixed potential segfault in seaudit_filter_set_name() and
+ seaudit_filter_set_description() if the passed in pointer was the
+ same one obtained by the respecitve accessor function.
+
+2007-03-22 J. Mowery <jmowery@tresys.com>
+
+ * Libapol SWIG wrapper updated to treat all structures as classes
+ in the target language.
+
+2007-03-21 J. Tang <jtang@tresys.com>
+
+ * Added ability for user to specify type joins and splits in
+ sediffx. The libpoldiff library had always supported this
+ feature; it just was not available through the user interface.
+
+2007-03-20 J. Mowery <jmowery@tresys.com>
+
+ * Renamed apol_domain_trans_table_destroy to domain_trans_table_destroy
+ and moved to policy-query-internal.h; this function was not intended
+ for external use.
+
+2007-03-19 J. Tang <jtang@tresys.com>
+
+ * When viewing AV rule differences, click (either left or right) a
+ permission name. This will now popup a menu that gives line
+ numbers for rules that contributed just that permission, rather
+ than the entire AV rule. This is enabled when source policies are
+ used.
+
+2007-03-16 J. Tang <jtang@tresys.com>
+
+ * Added poldiff_avrule_get_orig_line_numbers_for_perm() and
+ poldiff_avrule_get_mod_line_numbers_for_perm().
+
+2007-03-15 J. Tang <jtang@tresys.com>
+
+ * Split libpoldiff/include/rule_diff.h into
+ libpoldiff/include/avrule_diff.h and
+ libpoldiff/include/terule_diff.h. None of the function names were
+ changed.
+
+ * Re-standardized all programs' command line options. -V always
+ shows the version, -c always deals with object classes, and so
+ forth. Man pages updated.
+
+ * Renamed libapol/include/rangetrans-query.h to
+ libapol/include/range_trans-query.h. Likewise renamed
+ libpoldiff's rangetrans_diff.h None of the function names were
+ changed.
+
+2007-03-13 J. Tang <jtang@tresys.com>
+
+ * fixed segfault in libseaudit when, while parsing an AVC
+ message's permissions list it does not encounter a closing brace.
+
+ * information flow graphs are now being constructed via a BST and
+ its O(log n), rather than via a vector and O(n). It should be
+ noticeably faster now.
+
+ * fixed error in information flow analysis, transitive mode. The
+ returned flows were mistakenly all set to be the same object
+ class. For example if a step was foo_t -> bar_t by way of class
+ baz_c, subsequent flows were accidentally being constrained to
+ also be of baz_c.
+
+ * fixed error in apol_bst_insert() and apol_bst_insert_and_get(),
+ where the return values of 0 and 1 were flipped. The comment was
+ correct, but the code was not.
+
+2007-03-12 J. Tang <jtang@tresys.com>
+
+ * fixed share libraries's soname, such that SETools's programs
+ depend upon the correct name. For example, seaudit's .dynamic
+ section should have as a dependency on libseaudit.so.4, not
+ libseaudit.so.4.1.
+
+ * sediffx now diffs and displays range transitions
+
+ * sediff -E now diffs range transitions
+
+ * range transition diff added libpoldiff
+
+2007-03-08 J. Tang <jtang@tresys.com>
+
+ * poldiff_user_get_added_roles() and
+ poldiff_user_get_removed_roles() now return all roles when the
+ form is POLDIFF_FORM_ADDED / POLDIFF_FORM_REMOVED.
+
+ * sediffx updated to show users' MLS differences
+
+2007-03-07 J. Tang <jtang@tresys.com>
+
+ * in libpoldiff, if either policy is MLS then when diffing users,
+ those users' default MLS level and assigned ranges are also
+ diffed.
+
+ * added poldiff_range_t object
+
+ * fixed segfault in libqpol when loading an invalid source policy
+
+ * error messages were not being shown correctly in apol's open
+ policy dialog; fixed.
+
+2007-03-06 J. Mowery <jmowery@tresys.com>
+
+ * added SWIG wrapper for libqpol. The wrapper treats all libqpol
+ structs as classes in the target language.
+
+2007-03-06 J. Tang <jtang@tresys.com>
+
+ * apol_mls_level_create() will now always return an allocated (and
+ empty) category vector as part of the level. Before it would
+ initialize the field as NULL.
+
+ * poldiff_avrule_get_unmodified_perms() no longer returns all
+ permissions whenever a rule is added or removed; it only returns
+ permissions when the form is POLDIFF_FORM_MODIFIED. The new
+ behavior matches the other policy components.
+
+2007-03-05 J. Tang <jtang@tresys.com>
+
+ * added apol_mls_level_free() and apol_mls_range_get_levels()
+
+2007-03-02 J. Tang <jtang@tresys.com>
+
+ * qpol_user_get_range() and qpol_user_get_dfltlevel() now return
+ NULL if the policy is not MLS. (It used to return a garbage
+ pointer.)
+
+2007-03-01 J. Tang <jtang@tresys.com>
+
+ * added additional accessors to the seaudit_avc_message_t object.
+
+2007-02-28 J. Tang <jtang@tresys.com>
+
+ * added apol_mls_level_create_from_mls_level() and
+ apol_mls_range_create_from_mls_range() copy constructors.
+
+ * Fixed segfault when libseaudit attempts to parse certain load
+ policy messages.
+
+2007-02-23 J. Tang <jtang@tresys.com>
+
+ * Implemented new sediffx results design. Results are implemented
+ as subclasses of an abstract result_item_t class. This will allow
+ easier future modifications of the sediffx interface.
+
+2007-02-21 J. Tang <jtang@tresys.com>
+
+ * fixed segfault when opening a policy with
+ APOL_POLICY_OPTION_NO_RULES when the policy has an unconditional
+ type_transition rule.
+
+2007-02-16 J. Mowery <jmowery@tresys.com>
+
+ * Moved libqpol/include/qpol/expand.h to libqpol/src/expand.h.
+ This header file was never supposed to be visible.
+
+ * added SWIG wrappers to libapol; build system updated. configure
+ must be passed --enable-swig-python to build the wrappers.
+
+2007-02-14 J. Tang <jtang@tresys.com>
+
+ * sesearch and apol now allow searching range transitions based
+ upon the target class. (Target classes were introduced in version
+ 21 policies.)
+
+ * added to apol_range_trans_query_append_class() to
+ libapol/include/apol/rangetrans-query.h
+
+2007-02-07 J. Mowery <jmowery@tresys.com>
+
+ * split --audit flag in sesearch to --auditallow and --dontaudit;
+ the --audit flag is now deprecated.
+
+2007-02-06 J. Tang <jtang@tresys.com>
+
+ * Official release of SETools-3.1.
+
+ * Compile fixes for 64-bit Linux.
+
+2007-02-02 J. Tang <jtang@tresys.com>
+
+ * added RPM spec files.
+
+2007-01-30 J. Tang <jtang@tresys.com>
+
+ * fixed error in apol when right-clicking an attribute when a file
+ contexts database is loaded.
+
+ * fixed error in SQLite configuration in which a pointer size was
+ defined as the size of an integer. this is a no longer valid
+ assumption on 64-bit architectures.
+
+ * if apol has a modular policy loaded, then its "policy version"
+ for purposes of permission maps is now set to the maximum policy
+ version as defined in libsepol. this mimics the linking behavior
+ of sepol.
+
+2007-01-25 J. Tang <jtang@tresys.com>
+
+ * fixed error where aliases within modules were not being detected
+ properly
+
+2007-01-19 J. Mowery <jmowery@tresys.com>
+
+ * all files now have correct copyright and license notices
+
+2007-01-19 J. Tang <jtang@tresys.com>
+
+ * updated all help documentation
+
+2007-01-17 J. Mowery <jmowery@tresys.com>
+
+ * updated all man pages
+
+2007-01-17 J. Tang <jtang@tresys.com>
+
+ * added poldiff_type_remap_entry_get_is_inferred()
+
+ * sediffx converted to use modular policies.
+
+2007-01-08 J. Tang <jtang@tresys.com>
+
+ * apol converted to use modular policies.
+
+2007-01-05 J. Mowery <jmowery@tresys.com>
+
+ * Added capability QPOL_CAP_SOURCE to detect ability to display
+ the policy source.
+
+ * Deprecated qpol_policy_is_mls_enabled() use
+ qpol_policy_has_capability() for QPOL_CAP_MLS instead.
+
+2007-01-04 J. Tang <jtang@tresys.com>
+
+ * seaudit converted to use modular policies.
+
+2007-01-03 J. Tang <jtang@tresys.com>
+
+ * Deprecated apol_policy_open() and apol_policy_open_no_rules();
+ use apol_policy_create_from_policy_path() instead.
+
+2006-12-14 J. Tang <jtang@tresys.com>
+
+ * Added apol_policy_path_t object. This is needed because policy
+ paths are not just a single string, but rather a base policy and
+ any number of modules.
+
+ * New functions apol_config_join_var() generalized to be
+ apol_str_join(). Same with apol_config_split_var() ->
+ apol_str_split().
+
+2006-12-12 J. Tang <jtang@tresys.com>
+
+ * Renamed qpol_find_default_policy_file() in qpol/policy.h to
+ qpol_default_policy_find() in qpol/util.h. Removed
+ qpol_find_default_policy_file_strerr(). These functions were
+ never supposed to be in these locations.
+
+ * Deprecated apol_policy_is_binary(); use qpol policy capabilities
+ instead.
+
+ * Deprecated qpol_open_policy_from_file(); it is now called
+ qpol_policy_open_from_file(). Similar change to
+ qpol_open_policy_from_file_no_rules() and
+ qpol_open_policy_from_memory().
+
+ * Merged qpol/policy_query.h into qpol/policy.h. Split module
+ related code into qpol/module.h.
+
+ * Rewrite of seaudit to utilize libseaudit.so.4; tweaked numerous
+ interface issues.
+
+ * Rewrite of libseaudit to meet coding standards. libseaudit.so.4
+ is now a shared object file with proper namespacing.
+
+2006-12-11 J. Mowery <jmowery@tresys.com>
+
+ * Added qpol_policy_has_capability() to qpol/policy_query.h
+ This function and the enumeration qpol_capability_e allows
+ a user of the library to check if a loaded policy is
+ capable of supporting various policy features.
+
+ * Support has been added to libqpol for loading policy modules.
+ See qpol/policy.h for details.
+
+2006-12-01 J. Tang <jtang@tresys.com>
+
+ * deprecated apol_config_get_varlist() and
+ apol_config_varlist_to_str(); added apol_file_find_path(),
+ apol_config_split_var(), and apol_config_join_var()
+
+2006-12-01 J. Tang <jtang@tresys.com>
+
+ * Official release of SETools 3.0.1.
+
+2006-11-29 J. Tang <jtang@tresys.com>
+
+ * synced source parser to libsepol 1.15.2.
+
+2006-11-27 J. Tang <jtang@tresys.com>
+
+ * added application icons to apol, seaudit, and sediffx
+
+2006-11-09 j. Mowery <jmowery@tresys.com>
+
+ * added __cplusplus guards to all external headers
+
+2006-11-08 J. Tang <jtang@tresys.com>
+
+ * apol_permmap_t is now a hidden declaration.
+ apol_permmap_destroy() has been removed. (It should never have
+ been publicly visible.)
+
+ * apol_policy_t is now an opaque structure. Added accessors such
+ as apol_policy_get_qpol() to retrieve some of those fields. This
+ also means that the apol callback function changed, so as to be
+ similar to qpol's callback.
+
+2006-11-07 J. Mowery <jmowery@tresys.com>
+
+ * renamed apol_get_*_by_query() to apol_*_get_by_query to match
+ naming conventions of other functions. Old versions were retained and
+ marked as deprecated.
+
+2006-11-02 J. Tang <jtang@tresys.com>
+
+ * added apol_str_strdup() (declared in apol/util.h)
+
+ * apol_vector_create_from_vector() takes two additional arguments,
+ so that it can act more like a copy constructor.
+
+
+2006-11-01 J. Mowery <jmowery@tresys.com>
+
+ * added ability for avrule and terule queries to search to only types or
+ only attributes for both source and target fields (separate option for
+ each field, default is to search both types and attributes).
+
+ * added apol_get_syn_avrule_by_query() and apol_get_syn_terule_by_query()
+
+2006-10-27 J. Tang <jtang@tresys.com>
+
+ * added apol_str_appendf() (declared in apol/util.h)
+
+2006-10-27 J. Mowery <jmowery@tresys.com>
+
+ * added make indent to standardize code indentation style
+
+ * indented all .c and .h files with make indent (except sqlite which it skips)
+
+2006-10-25 J. Tang <jtang@tresys.com>
+
+ * seaudit-report Logwatch script will look for
+ $(bindir)/seaudit-report, rather than the hard-coded
+ /usr/bin/seaudit-report
+
+2006-10-25 J. Mowery <jmowery@tresys.com>
+
+ * numbered steps for loading
+
+ * loading syntactic rule table is now off by default
+
+ * updated tools requiring line numbers to build syntactic table as needed
+
+2006-10-24 J. Tang <jtang@tresys.com>
+
+ * exclude the sqlite files from the Doxygen configuration
+
+2006-10-19 J. Tang <jtang@tresys.com>
+
+ * added lib<foo>_get_version() functions to libqpol, libpoldiff,
+ and libsefs
+
+2006-10-17 J. Tang <jtang@tresys.com>
+
+ * seaudit-report binary was mistakenly called "seaudit_report"
+
+ * Updated man pages to match programs' help files.
+
+2006-10-13 J. Tang <jtang@tresys.com>
+
+ * Official release of SETools 3.0.
diff --git a/KNOWN-BUGS b/KNOWN-BUGS
new file mode 100644
index 0000000..8e2acca
--- /dev/null
+++ b/KNOWN-BUGS
@@ -0,0 +1,73 @@
+SETools version 3.3
+Tresys Technology
+setools@tresys.com, http://oss.tresys.com/projects/setools
+
+
+August 15, 2008
+
+
+CURRENT BUGS AND ISSUES
+
+This file contains a description of the currently aware issues with
+SETools. There are undoubtedly more; please report any new problems
+to setools-bugs@tresys.com.
+
+SETOOLS
+
+1. There is a critical error in libsepol versions 1.16.2 and 2.0.2
+ that prevents SETools from opening source policies. The supplied
+ configure script will detect and abort if it finds this particular
+ version.
+
+APOL
+
+1. Double clicking on listbox items may not display popup windows in
+ certain window managers. Instead the user can right-click on the
+ item in order to display popup windows. This is due to Tk's event
+ buffer and is not fixable. (Ref: http://tinyurl.com/ltmuh)
+
+2. Apol will correctly load policies with aliases, but the alias names
+ will be lost (the root type name is used instead). Therefore,
+ aliases are not displayed and queries should not use them as
+ parameters.
+
+3. Certain older source policies will not load if complemented type
+ sets are used outside of neverallow rules. This is due to apol using
+ a similar parser to checkpolicy, which no longer accepts this use of
+ the complement operator (~).
+
+SEAUDIT
+
+1. Sorting by date will not work correctly if an audit log spans
+ through a new calendar year. For example, if the audit log
+ includes entries from December 2003 and January 2004, when sorted
+ by date January 2004 will come before the December 2003 entry. This
+ is because audit logs do not include a year; all messages are
+ assumed to have occurred on the same year.
+
+2. Audit messages that span multiple lines are not parsed correctly.
+ This will be addressed in a future revision of SETools.
+
+LIBPOLDIFF-SWIG-JAVA
+
+1. There is currently an issue with SWIG Java bindings that prevents
+ disowning an object, and there is an issue in libsepol that prevents the
+ duplication of a policy. Pending the availability of one of these features,
+ it will not be possible to safely use libpoldiff in Java. Future revisions
+ to the policy representation are expected to permit the duplication of
+ policies.
+
+TESTING INFORMATION
+
+Operating Systems:
+ Fedora Core 5 through 9, for both i386 and x86_64 architectures
+
+SELinux monolithic policy versions:
+ 12, 15 through 18
+ 19 through 23 (both with and without MLS support)
+
+SELinux modular policy versions:
+ 5 through 8
+
+Please report any new bugs or comments to setools-bugs@tresys.com.
+Thank you for using SETools!
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5dc1b5d
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,116 @@
+ACLOCAL_AMFLAGS = -I m4
+AUTOMAKE_OPTIONS = dist-bzip2
+
+if BUILD_APOL
+ MAYBE_APOL = apol
+endif
+
+if BUILD_GUI
+ MAYBE_GUI = seaudit
+endif
+# sediffx is also built conditionally, from sediffx/Makefile.am
+
+SUBDIRS = libqpol libapol libsefs libpoldiff libseaudit secmds sechecker sediff man packages debian $(MAYBE_APOL) $(MAYBE_GUI)
+
+#old indent opts
+#INDENT_OPTS = -npro -nbad -bap -sob -ss -l132 -di1 -nbc -br -nbbb -c40 -cd40 -ncdb -ce -cli0 -cp40 -ncs -d0 -nfc1 -nfca -i8 -ts8 -ci8 -lp -ip0 -npcs -npsl -sc
+C_INDENT_OPTS = -npro -nbad -bap -nbbb -nbbo -nbc -br -bli0 -bls -c40 -cbi0 -cd40 -cdw -ce -ci8 -cli0 -cp40 -ncs -d0 -nbfda -di1 -nfc1 -nfca -i8 -ip0 -l132 -lp -nlps -npcs -pi0 -nprs -npsl -saf -sai -sbi0 -sob -ss -ts8 -ut
+CXX_INDENT_OPTS = -npro -nbad -bap -nbbb -nbbo -nbc -bl -bli0 -bls -c40 -cbi0 -cd40 -cdw -nce -ci8 -cli0 -cp40 -ncs -d0 -nbfda -di1 -nfc1 -nfca -i8 -ip0 -l132 -lp -nlps -npcs -pi0 -nprs -npsl -saf -sai -sbi0 -sob -ss -ts8 -ut
+
+EXTRA_DIST = VERSION KNOWN-BUGS COPYING.LGPL COPYING.GPL
+
+libqpol:
+ $(MAKE) -C $(top_srcdir)/libqpol
+
+libapol:
+ $(MAKE) -C $(top_srcdir)/libapol
+
+libpoldiff:
+ $(MAKE) -C $(top_srcdir)/libpoldiff
+
+libpolsearch:
+ $(MAKE) -C $(top_srcdir)/libpolsearch
+
+libsefs:
+ $(MAKE) -C $(top_srcdir)/libsefs
+
+libseaudit:
+ $(MAKE) -C $(top_srcdir)/libseaudit
+
+apol: libqpol libapol libsefs packages
+ $(MAKE) -C $(top_srcdir)/apol
+
+secmds: libqpol libapol libsefs
+ $(MAKE) -C $(top_srcdir)/secmds
+
+seaudit: libqpol libapol libseaudit
+ $(MAKE) -C $(top_srcdir)/seaudit
+
+sediff: libqpol libapol libpoldiff
+ $(MAKE) -C $(top_srcdir)/sediff sediff
+
+sediffx: libqpol libapol libpoldiff
+ $(MAKE) -C $(top_srcdir)/sediff sediffx
+
+sechecker: libqpol libapol libsefs
+ $(MAKE) -C $(top_srcdir)/sechecker
+
+help:
+ @echo "Make targets for SETools:"
+ @echo " all: build everything, but do not install"
+ @echo " install: install everything"
+ @echo " uninstall: remove SETools from your system"
+ @echo ""
+ @echo " apol: build policy analysis tool"
+ @echo " secmds: build command line tools"
+ @echo " seaudit: build audit log analysis tools"
+ @echo " sediff: build semantic policy diff command line tool"
+ @echo " sediffx: build semantic policy diff graphical tool"
+ @echo " sechecker: build policy checking tool"
+ @echo ""
+ @echo " install-logwatch: install LogWatch config files for seaudit-report"
+ @echo " (requires LogWatch and root privileges)"
+ @echo ""
+ @echo " clean: clean up interim files"
+ @echo " distclean: clean everything not in original distribution"
+
+install-logwatch:
+ $(MAKE) -C $(top_srcdir)/seaudit install-logwatch
+
+.PHONY: libqpol libapol libpoldiff libsefs libseaudit \
+ apol secmds seaudit sediff sediffx sechecker \
+ install-logwatch help \
+ seinfo sesearch indexcon findcon replcon searchcon \
+ packages
+
+seinfo: libqpol libapol
+ $(MAKE) -C $(top_srcdir)/secmds seinfo
+
+sesearch: libqpol libapol
+ $(MAKE) -C $(top_srcdir)/secmds sesearch
+
+indexcon: libqpol libapol libsefs
+ $(MAKE) -C $(top_srcdir)/secmds indexcon
+
+findcon: libqpol libapol libsefs
+ $(MAKE) -C $(top_srcdir)/secmds findcon
+
+replcon: libqpol libapol libsefs
+ $(MAKE) -C $(top_srcdir)/secmds replcon
+
+searchcon: libqpol libapol libsefs
+ $(MAKE) -C $(top_srcdir)/secmds searchcon
+
+packages:
+ $(MAKE) -C $(top_srcdir)/packages
+
+distclean-local:
+ -rm -r config.tcl
+
+indent:
+ find $(top_srcdir) \( -wholename '*/packages/*' -prune \) , \
+ \( -name '*.[ch]' -type f -exec indent $(C_INDENT_OPTS) '{}' \; \)
+ find $(top_srcdir) -regex '.*\.\(cc\|hh\)' -type f -exec indent $(CXX_INDENT_OPTS) '{}' \;
+ find $(top_srcdir) -regex '.*\.\(cc\|hh\)' -type f -exec sed -i 's/\*>>/* > >/g' '{}' \;
+ find $(top_srcdir) -name '*.cc' -type f -exec sed -i 's/const \?const/const/g' '{}' \;
+ find $(top_srcdir) -name '*.hh' -type f -exec sed -i 's/virtual ~ /virtual ~/g' '{}' \;
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..4f68561
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,1018 @@
+SETools 3.3.6:
+
+This release builds upon SETools 3.3.5:
+
+ * Update attribute handling to use attributes in version 24 policy.
+
+ * Fix bug where dontaudit rules were loaded when the "no rules" option is
+ enabled.
+
+seaudit:
+
+ * Add MLS fields to source and target contexts.
+
+ * Double clicking a message line will display the orignal log message.
+
+seinfo:
+
+ * Qdd query for permissive types.
+
+ * Add query for policy capabilities.
+
+==================================================
+
+SETools 3.3.5:
+
+This release builds upon SETools 3.3.4:
+
+ * Update to policy loader to match checkpolicy 2.0.16 and libsepol
+ 2.0.32.
+
+ * Changes to libqpol to allow compiling against libsepol >= 2.0.29.
+
+ * Support for reading policy version 23.
+
+==================================================
+
+SETools 3.3.4:
+
+This release builds upon SETools 3.3.3:
+
+ * Update to policy loader to match checkpolicy 2.0.13 and libsepol
+ 2.0.23.
+
+ * Fixes to apol for proper handling of Tk 8.5.
+
+ * Fixes to libapol, libqpol, and sechecker to build using GCC 4.3.
+
+==================================================
+
+SETools 3.3.3:
+
+This release builds upon SETools 3.3.2:
+
+ * Changes to libqpol to allow compiling against libsepol >= 2.0.20.
+
+ * Support for reading policy version 22.
+
+ * Clarification to default policy loading for seinfo, sesearch, and
+ sechecker.
+
+ * Build scripts for Debian and Ubuntu.
+
+==================================================
+
+SETools 3.3.2:
+
+This release corrects a number of issues present in SETools 3.3.1:
+
+ * Fix to libqpol for policies lacking genfscon statements; for
+ policies containing disabled aliases; for
+ qpol_type_get_alias_iter() given certain policies; for the special
+ role object_r.
+
+ * Fix to libapol when running a default avrule query on policies
+ that have not had their neverallow rules loaded.
+
+ * Fix to libsefs and apol for invalid regular expressions.
+
+ * Fix to apol when validating empty levels.
+
+ * Fix to all SWIG generated wrappers for Java.
+
+==================================================
+
+SETools 3.3.1:
+
+This release corrects a number of issues present in SETools 3.3:
+
+ * Fix to configure when Tcl is not found on build system; fix when
+ compiling with --disable-gui option.
+
+ * Fix to uninstall targets of Java wrappers.
+
+ * Fix to libapol where transitive flows could return results that
+ were supposed to be excluded; fixed memory leaks in infoflow graph
+ generation.
+
+ * Fix to libsefs when running MLS query on non-MLS fclists.
+
+ * Fix to apol when reading older .apol files; fix copy and select
+ all on certain tabs; fix to filter by attribute on some advanced
+ dialogs.
+
+==================================================
+
+SETools 3.3:
+
+SETools:
+
+ * SETools now has an external dependency upon libsqlite3 >= 3.2. The
+ supplied configure script will enforce this dependency.
+
+ * pkg-config scripts are installed with the SETools libraries.
+
+libsefs:
+
+ * Rewrite of library to have proper namespaces and much more usable
+ object-oriented design.
+
+ * SWIG wrappers generated for this library if the appropriate
+ configure flags are set.
+
+findcon, searchcon:
+
+ * Merge searchcon's functionality into findcon. The searchcon tool
+ has been removed from SETools.
+
+indexcon, replcon:
+
+ * Updated to use new libsefs design.
+
+apol:
+
+ * Updated to use new libsefs design.
+
+ * Modified to use the SWIG Tcl interface rather than a custom C
+ library. apol is now a combination of a Tcl script (simply called
+ 'apol') and associated packages that are required at runtime.
+
+ * Neverallow rules are only loaded and expanded when the user
+ performs a search for them. This will dramatically speed up
+ initial policy load time.
+
+awish:
+
+ * awish is no longer needed and thus has been removed from SETools.
+
+sediff, sediffx:
+
+ * Instead of differentiating "AV rules" or "TE rules", user now
+ specifies which particular rule to compare (allow, dontaudit,
+ type_transition, etc.).
+
+ * Neverallow rules are only loaded and expanded when the user
+ performs a diff upon them. This will dramatically speed up
+ initial policy load time.
+
+==================================================
+
+SETools 3.2:
+
+libapol, libqpol, libesaudit, libpoldiff:
+
+ * If --enable-swig-python is given during configure time, the build
+ system will create Python SWIG wrappers for these libraries.
+
+ * If --enable-swig-java is given during configure time, the build
+ system will create Java SWIG wrappers for these libraries.
+
+libpoldiff:
+
+ * Provides ability to diff levels, categories, and range_transition
+ statements. Provides ability to diff modifications to a user's
+ default level and permitted MLS range, assuming the policies are MLS.
+
+apol, seaudit, sediffx:
+
+ * Introduces 'policy list', a small text file that contains
+ references to a base policy and any number of modules. After
+ selecting the base policy and modules in a tool's open policy
+ dialog, click on 'Export' to write a policy list to disk. That
+ policy list then may be imported into the same tool or any other
+ graphical SETools application. This file also may be specified
+ on the command line for all tools that load a policy.
+
+sesearch, apol:
+
+ * Provides full support for version 21 policy (i.e., object classes for
+ range_transition statements).
+
+sediff:
+
+ * Shows MLS diffs.
+
+sediffx:
+
+ * Shows MLS diffs.
+
+ * Provides option to show line numbers that contributed just to a
+ specific AV rule's permission by clicking that AV rule's permissions.
+ This is in addition to existing sediffx behavior that showed all lines
+ associated with a particular rule difference.
+
+ * Provides support for type joins and splits within the type remap dialog.
+
+==================================================
+
+SETools 3.1:
+
+SETools:
+
+ * All tools that open a policy now support loadable policy modules.
+ Command line tools expect the first module to be a base module
+ followed optionally by any other modules. Graphical tools have
+ a new open policy dialog to select a base module and any number of
+ additional modules.
+
+ * Release of RPM packages that are compatible with Fedora Core 5 and
+ 6. The spec and support files are in packages/rpm.
+
+libapol:
+
+ * New class apol_policy_path_t to represent a base policy and any
+ number of modules. Use this whenever referring to the file or
+ files constituting a policy.
+
+libqpol:
+
+ * Policy features such as attribute names or MLS can now be queried
+ individally via qpol_policy_has_capability() rather than inferred
+ by policy type and version.
+
+ * New class qpol_module_t to represent a particular policy module
+ prior to it being linked into a base policy (qpol_policy_t).
+
+libseaudit:
+
+ * Rewrite of library to have proper namespaces. libseaudit is now
+ fully documented and suitable for third-party users.
+
+seaudit:
+
+ * Rewrite to use new libseaudit.
+
+ * Numerous tweaks to the interface to be more user friendly.
+
+seaudit-report:
+
+ * Rewrite to use new libseaudit.
+
+sediffx:
+
+ * Numerous tweaks to the interface to be more user friendly.
+
+==================================================
+
+SETools 3.0.1
+
+SETools:
+
+ * All code has been indented uniformly via the 'make indent' target.
+
+SETools libraries:
+
+ * All libraries now have a get_version() function.
+
+libqpol:
+
+ * Syntactic rule table is now off by default; it requires an
+ explicit call to qpol_policy_build_syn_rule_table() to create it.
+
+libapol:
+
+ * apol_policy_t is now an opaque structure. apol_permmap_t is no
+ longer a public declaration.
+
+ * avrule and terule queries now have full "syntactic" searching
+ features.
+
+apol & sesearch:
+
+ * If loaded policy is source, the new syntactic search algorithm is used.
+
+==================================================
+
+SETools 3.0
+
+SETools:
+
+ * Moved entire project to autoconf build system. This will detect
+ dependencies correctly and make it easier to integrate with Linux
+ distributions and their packaging systems.
+
+SETools libraries:
+
+ * Rewrite of back-end of SETools to use libsepol data structures.
+ Most things should run notably faster.
+
+ * All exported library functions have a standardized naming
+ convention and are fully documented. This will prove helpful for
+ third-party developers integrating SETools into their own
+ projects.
+
+ * For policies version 15 or greater, domain transition analysis now
+ takes into consideration setexec permission and/or type_transition
+ rules.
+
+apol:
+
+ * Rework Tk interface to fit on 1024x768 displays.
+
+ * Indirect matching of attributes now works with rule searches.
+
+sediff:
+
+ * New diffing algorithm yields significant speed improvements.
+
+ * Can now diff neverallow and role_transition statements.
+
+ * Streamlined results display - should be easier to read.
+
+sechecker:
+
+ * Updated module format and template for ease of extension.
+
+
+=======================================================
+May 1, 2006, Version 2.4
+
+apol:
+ File contexts tab now allows for MLS range searching if
+ the loaded database is from a MLS filesystem.
+ Policy statistics dialog now shows MLS and ocontexts
+ summaries.
+
+libapol:
+ Added support for loading base policies containing optionals.
+ Added support for searching range transitions containing
+ attributes.
+
+libseaudit:
+ Bugfix to support parsing FC5-style audit logs.
+
+seaudit:
+ Added date filters.
+
+secmds:
+ Added support to indexcon and searchcon for MLS filesytems.
+ Added support to findcon and replcon for MLS filesystems.
+
+sechecker:
+ Added incomplete network access (inc_net_access) module.
+ Added unreachable domains (unreachable_doms) module.
+ Added impossible range transitions (imp_range_trans) module.
+
+sesearch:
+ Allow user to search range transitions by attributes and
+ indirect matching.
+ Added RBAC searching.
+
+
+=======================================================
+January 23, 2006, Version 2.3
+
+apol:
+ added new MLS components tab for sensitivities,
+ levels, and categories.
+ changed users tab to support ranges and default
+ levels.
+ added range transition tab for searching range
+ transition rules.
+ added new tab for network context components.
+ added new tab for file system context components.
+libapol:
+ added binpol support for MLS, network contexts,
+ and file system contexts.
+seinfo:
+ added command line options for MLS components.
+ added command line options for network contexts
+ and file system contexts.
+sesearch:
+ added command line option for searching for rules
+ by conditional boolean name.
+seaudit:
+ added new column in the log view for the 'comm'
+ field found in auditd log files.
+ added filters for the 'comm' field and 'message'
+ field.
+manpages:
+ added manpages for all tools.
+
+=======================================================
+October 31, 2005, Version 2.2
+
+libapol:
+ replaced the original dta algorithm with a new one
+ to properly support complements in rules. added
+ new structures to support the separation of diff
+ elements. added support for parsing additional
+ policy components in source policies.
+sediff:
+ enhanced the GUI for display and separation of diff
+ elements. added the ability to rename types.
+sechecker:
+ added a new tool - a commandline modular and
+ extensible policy checker program
+seuser:
+ removed - deprecated
+sepcut:
+ removed - deprecated
+
+=======================================================
+October 12, 2005 Version 2.1.3
+
+libapol:
+ fixed a mls bug in the source parser.
+
+=======================================================
+August 24, 2005 Version 2.1.2
+
+apol:
+ created new permission maps for policy versions
+ 19 and 20. also some minor changes to support
+ version 20 binary format.
+libseaudit:
+ updated the parser to properly parse avc
+ messages from auditd logfiles
+libapol:
+ updated the binary policy parser to handle the
+ new version 20 avtab format. The parser
+ preserves attributes in av rules by generating
+ fake attribute names.
+
+=======================================================
+May 17, 2005, Version 2.1.1
+
+libseaudit:
+ updated code to compile with gcc-4.0.0
+ minor bug fixes
+
+sediff:
+ updated code to compile with gcc-4.0.0
+
+seaudit:
+ updated code to compile with gcc-4.0.0
+
+libsefs:
+ updated code to compile with gcc-4.0.0
+
+libapol:
+ updated code to compile with gcc-4.0.0
+ minor bug fixes
+
+seuser:
+ updated code to compile with gcc-4.0.0
+
+======================================================
+April 18, 2005, Version 2.1.0
+
+apol:
+ improved direct relabel analysis algorithm
+
+libapol:
+ added policy version 19 support
+
+sediff:
+ added role transitions, improved role allow
+ added conditional expression differences
+
+=======================================================
+February 16, 2005, Version 2.0
+
+setools:
+
+libsefs:
+ Converted to use an on-disk SQLite database backend and
+ re-designed API to provide the functionality to other
+ applications, such as apol.
+libapol:
+ Added support for analyzing direct file relabels.
+ Added support for analyzing relationship between two types.
+ Integrated use of hashtable structures for easily analyzing
+ differences between policies.
+ Minor bug fixes.
+
+libseuser:
+ Minor bug fixes.
+
+apol:
+ New analysis module for performing direct file relabel
+ analysis.
+ New analysis module for analyzing the relationship be-
+ tween two types.
+ New interface added for viewing file contexts from an
+ SELinux filesystem.
+ Improvements to domain transition analysis interface.
+ Minor bug fixes and GUI tweaks.
+
+secmds:
+ Updated indexcon/searchcon to use an on-disk SQLite database
+ in order to decrease memory use. These changes are not
+ backwards-compatible.
+
+seaudit:
+ Integrated reporting functionality into GUI.
+ Minor GUI tweaks.
+
+sediff:
+ New gtk GUI and command-line tools for analyzing the semantic
+ differences between two policies. The semantic difference
+ of a policy is different from the syntactic difference in
+ that it shows the cumulative effect of rules rather than
+ doing a line-by-line comparison.
+=======================================================
+November 4, 2004, Version 1.5.1
+
+apol:
+ Fixed compatibility with tcl 8.3.
+
+libsefs:
+ Fixed compile problem on PPC.
+
+secmds:
+ Fixed fatal error in replcon.
+
+setools:
+ Reverted to static linking and fixed various small bugs.
+
+========================================================
+October 27, 2004, Version 1.5
+
+apol:
+ Advanced options added to forward domain
+ transition analysis module for performing
+ more granular searching of transitions to
+ domains using specified classes, permissions
+ and target types.
+ Minor bug fixes and improvements.
+
+libapol:
+ Fixed to handle new libapol user structs.
+ Enhanced forward domain transition analysis to
+ perform more granular searching using specified
+ classes, permissions and target types.
+ Minor bug fixes.
+
+libseuser:
+ General clean up of the policy components.
+ Fixed handling of users to be consistent with rest.
+
+seaudit:
+ New tool (seaudit-report) for generating customized
+ reports on SE Linux audit messages using saved
+ seaudit view files. This tool is highly configurable
+ and can effectively integrate with the LogWatch
+ application for automating SE Linux audit log reporting.
+ Added feature for exporting audit messages to a
+ file, as well as viewing all components of an audit
+ message within a text view.
+
+libseaudit:
+ Updated library to store audit header information, such as
+ the system call timestamp and serial number.
+ Fixed parse errors for new logs.
+
+secmds:
+ New tool (indexcon) for creating a snapshot of security
+ contexts for SE Linux filesystem entities.
+ New tool (searchcon) for searching the SE Linux filesystem
+ database that was created using indexcon.
+
+=======================================================
+July 7, 2004, Version 1.4.1
+
+setools:
+ Added the install target install-dev to install the
+ setools headers and libraries for third party
+ developers (libapol, libseuser, libseaudit).
+
+libapol:
+ Added support for parsing policy version 18 (source
+ and binary).
+ Added a permission map for version 18 policies.
+
+libseaudit:
+ Fix timezone related bug that resulted in incorrect
+ dates displayed in seaudit.
+
+=======================================================
+June 2, 2004, Version 1.4
+
+setools:
+ Made policy installation and file labeling a separate
+ makefile target to better support non-default policies
+ like the 'targeted' policy included in Fedora Core 2.
+apol:
+ Added support for the user to change the name of
+ result tabs.
+ Added new Tool Options dialog for opening limited
+ portions of the selinux policy.
+ GUI changes to correctly support binary policies.
+ Enhanced display of conditional rules in TE rule
+ search results.
+libapol:
+ Added support for loading binary policy files (in
+ addition to source policy files).
+ Added utility functions for finding default policies
+ - both source and binary.
+ Various cleanups and bug fixes to source policy parsing.
+secmds:
+ Added conditional policy support to seinfo.
+ Changed to use libapol default policy logic.
+seaudit:
+ Changed to use libapol default policy logic.
+ Gui changes to correctly support binary policies.
+libseuser:
+ Changed to use libapol default policy logic.
+
+=======================================================
+May 5, 2004 Version 1.3.1
+
+apol:
+ Fixed to properly exclude object classes and/or
+ permissions in information flow analysis.
+libapol:
+ Fixed to properly exclude object classes and/or
+ permissions in information flow analysis.
+seuser:
+ Changed default policy.conf location in seuser.conf
+ to /etc/security/selinux/src/policy/policy.conf
+ Minor bug fixes.
+sepcut:
+ Minor bug fixes.
+libseuser:
+ Minor fixes to parsing of the seuser.conf file.
+
+=======================================================
+April 15, 2004 Version 1.3
+
+apol:
+ Added conditional policy support.
+ Added permission weighting for information flows.
+libapol:
+ Added full support for conditional policies.
+ Included support for policy version 17
+ Various fixes and updates
+seaudit:
+ Added support for audit messages from changing
+ booleans in a conditional policy.
+ Added multiple filters/views.
+libseaudit:
+ Updated to support new audit framework in the 2.6.5
+ kernel.
+seuser:
+ Added home directory labeling command as command
+ line option.
+libseuser:
+ Updated to support home directory labeling.
+secmds:
+ Added new context swap tool (replcon).
+ Added new context search tool (findcon).
+
+
+========================================================
+February 6, 2004 Version 1.2.1
+
+Libapol:
+ Fixed parse error when using attributes in role
+ declarations.
+
+========================================================
+February 4, 2004 Version 1.2
+
+Apol:
+ Added saving and loading queries from the TE rules tab.
+ Added a tab for referencing initial SIDs in the policy.
+ Fixed some memory usage problems in information flow.
+ Combined Forward and Reverse domain transitions into one
+ analysis module.
+
+Seuser:
+ Some minor changes to command line parsing for better use on
+ non selinux machines.
+
+Seaudit:
+ Added real-time log monitoring capability.
+ Added support for hostname recognition in logs.
+ Added ability to select from values that appear in the
+ policy or the log, for filtering. An open policy is no
+ longer needed to filter a log.
+
+
+Libapol:
+ Added support for new policy language features ('-' in
+ lists of types and typealias).
+ Enabled conditional policy (v16) support by default.
+ Added support for parsing and storing initial SIDs.
+
+========================================================
+December 30, 2003 Version 1.1.1
+
+libapol:
+ fixed memory leakage on information flow analysis
+
+seaudit:
+ fixed to properly compile with ISO C90 standards.
+
+libseaudit:
+ fixed to properly compile with ISO C90 standards.
+
+seuser:
+ fixed build process to properly build with no GUI.
+ fixed help for 'seuser -X'
+ removed default_context and cron_context in seuser.conf
+
+secmds:
+ fixed seinfo to display version information
+
+
+========================================================
+December 18, 2003 Version 1.1
+
+Apol:
+ Significantly improved transitive information flow analysis
+ by allowing for greater control over the types,
+ object classes, and permissions to use in an analysis;
+ as well as the ability to search for multiple paths
+ bounded by number of founds paths and time.
+ Additional work to complete a fully functional
+ transitive flow analysis is planned.
+ Updated to work with restructured libraries
+ Added support for saving and loading analysis queries
+ Additional work to make the fonts and window sizes work better
+
+Seuser:
+ Updated to work with restructured libraries.
+ Created separate seuser (no X) and seuserx (X) commands
+
+Secmds: NEW
+ Added new command line tools:
+ seinfo: displays information--including expanded
+ information--about the components of a
+ policy (classes, types, attributes, users,
+ roles), as well as policy stats
+ sesearch: searches and displays type enforcement
+ rules based on criteria such as source
+ and target type, object class, permissions,
+ and rule type
+
+seaudit: NEW
+ Added a new GUI-based audit log analysis tool. The tool allows
+ one to view and search SE Linux messages from a log file
+ and to analyze the policy for rules that relate to
+ a given audit message. This is a first generation tool,
+ and real-time monitoring of the audit messages is planned.
+
+libseaudit: NEW
+ Includes library to parse and store SE Linux audit messages.
+
+Libapol:
+ Added latent support to parse future conditional policy syntax
+ Restructured library to separate core functions from
+ TCL/TK/X support functions (to allow non-X commands).
+ There are now libapol and libapol-tcl libraries.
+ Removed "dead" code and various bug fixes and clean up
+ Improved transitive information flow analysis.
+
+Libseuser:
+ Restructured library to separate core functions from TCL/TK/X
+ support functions (to allow non-X commands). There are
+ now libseuser and libseuser-tcl libraries.
+
+
+========================================================
+October 30, 2003 SE Linux Tools, version 1.0.1
+
+Apol:
+ Update to default font configuration
+
+Sepcut:
+ Update to default font configuration
+
+Seuser:
+ Updated seuser .te file
+ Update seuser Makefile to use -Z option when installing seuser
+ Update to default font configuration
+
+Libapol:
+ Minor fix to support Tcl 8.4 interface
+
+========================================================
+September 22, 2003 SE Linux Tools, version 1.0
+
+Added BWidgets source under packages.
+Added support for rpm packages.
+
+Apol:
+ Added reverse domain transition analysis.
+ Added direct information flow analysis.
+ Added an experimental transitive information flow
+ analysis.
+ Added permap loading/editing/saving support (required by
+ information flow analyses).
+ Fixed various bugs.
+
+Sepcut:
+ Added 'Relabel Files' button in the test policy tab.
+ Fixed various bugs.
+
+Seuser:
+ Fixed forward and backward compatibility in the use of system
+ user administration utilities (i.e., old versions of
+ SELinux use suseradd, new versions use useradd).
+ Changed shell scripts to fix compatibility.
+
+Libapol:
+ Fixed type alias support.
+ Added support for policy version 15.
+ Added direct information flow analysis capabilities.
+ Added partial transitive information flow analysis
+ capabilities.
+ Added reverse domain transition analysis capabilities.
+ Added permap support.
+ Fixed various bugs.
+
+
+
+========================================================
+June 9, 2003 SE Linux Tools, Release 20030609
+
+Apol:
+ Simplified the user interface by consolidating tabs.
+ Various bug fixes and clean up
+
+Sepcut:
+ Added feature to track recently opened policy dirs
+ Added feature to allow one to save policy module configurations
+ so that one policy directory may be use for multiple
+ configurations.
+ Added feature to allow individual user ability to control
+ tool global settings
+ Enhanced tools ability to stay in sync with on disk view
+ Various minor fixes and code clean up
+
+Seuser:
+ Various bug fixes and general clean up
+
+Libapol:
+ Fixed various bugs.
+
+
+========================================================
+April 10, 2003 SE Linux Tools, Version 0.8
+
+Apol:
+ Added Analysis tab for new domain transition analysis capability.
+ Changed compile process to install just a single, compressed .tcl file
+ Fixed problems with fonts.
+
+Sepcut:
+ Added support for older policy directories (customize
+ tab will disable if domains/program doesn't exist)
+ Minor fixes
+
+Seuser:
+ Significantly changed command line options. Added rename, show, and load
+ commands. Added -X, -f, and -R flags. Made loading policy the
+ default and replaced -L with -N flag. Removed -g and -r flags.
+ Added seuseradd, seuserdel, and seusermod scripts as shell wrappers for
+ the s* equivalent wrappers that also call seuser as necessary
+ to provide single command-line interface to manage users
+ Completely replaced the graphical user interface (GUI) that supports
+ a single interface to manage both system and selinux
+ user issues
+ Removed support for old-style default context management.
+
+Libapol:
+ Added extensions to support new domain transition analysis
+ Fix various problems with handling '*' in TE rules
+ Remove the OBJ_CLASSES_PERMS compile flag and the associated
+ old dead code.
+ Added command to get types for a given attribute as a list
+ Began restructuring the rule rendering code.
+ Fixed some memory leaks
+ Various bug fixes, clean up, and restructuring
+
+Libseuser:
+ Added commands to get system groups
+ Modified command that returns system users to also identify
+ user type.
+ Added several new command support wrap functions to support new command
+ line interface.
+ Various bug fixes.
+
+
+
+========================================================
+February 27, 2003 SE Linux Tools, Version 0.7
+
+Enhanced SepCut:
+ Added text search feature
+ Added ability to include a policy directory path on command line
+ Added ability to view all unsaved, modified files in various
+ dialogs
+ Fixed various bugs
+
+Apol updates:
+ Added a policy.conf tab with search ability
+ Added hyperlinks between TE rules and policy conf allowing one to
+ look up where in policy.conf where a given rule came from
+ Added basic ability to recognize roles declared via dominance statement
+ (semantics of statement still to be done)
+ Fixed various bugs
+
+Libapol:
+ Updated to support apol hyperlinking.
+ Minor bugs
+
+Seuser:
+ Updated seuser .te file to fix policy dependencies
+
+
+========================================================
+January 09, 2002 SE Linux Tools, Version 0.6.1
+
+Updated install process to allow setools to be installed during
+ initial selinux system install
+
+Fixed various problems with seuser's policy .te and .fc files
+ (Wayne Salamon, wsalamon@tislabs.com)
+
+Fixed way sepcut handle temporary files to accommodate policy
+ fixes
+
+
+
+=========================================================
+
+December 18, 2002 SE Linux Tools, Version 0.6
+
+
+Created SePCuT: SE Linux Policy Customization Tool
+ a first generation GUI policy customization/editing/testing tool
+
+
+Update libapol:
+ Added regular expression searches to types/attribs, TE rules, objects
+ Cleaned up the policy version hints support
+ Tested with MLS enabled and added a compile option
+
+
+Updated apol:
+ Changes to accommodate regex searches
+ Made displays read only
+ Various minor GUI improvements and bug fixes
+
+Updated libseuser:
+ Added checks and support for new login context style (support both
+ old and new style)
+ Added buffer overflow checks
+
+Updates seuser:
+ Supports old and new login context styles
+ Better error checking for command line interface
+
+
+
+===============================================================
+
+September 21, 2002 SE Linux Tools, Version 0.5
+
+
+Updated libapol:
+ Added object classes and permissions to lib
+ Added avl-tree based sorting (~40% improvement in load time)
+ various minor bugs
+
+Updated apol:
+ Added object classes and permissions tab
+ Added object classes and permissions as TE rule search options
+ Added multiple results tabs for type enforcement rule searches
+ Added a recent files menu to the File menu
+ Various minor fixes
+
+Updated libseuser:
+ Added non-TCL/TK wrappers for C programs (to support command line seuser)
+
+Updated seuser:
+ Added command line version and options
+
+Misc:
+ General clean up
+ Improved string buffer overflow validation
+
+=================================================================
+
+
+August 1, 2002 SELinux Tools, Version 0.4.2
+
+Updated libapol:
+ Updated policy parsing to work with July 2002 policy syntax changes
+ Added backward compatibility with older policies
+ Added policy version checking
+ Replaced notify with dontaudit
+ Added new generalized filesystem syntax
+ Added latent structures for object classes and permissions
+
+Updated apol:
+ Updated GUI to reflect libapol changes
+ Added dontaudit rule selector
+ Made several font fixes
+ Added policy version indicators
+ Various minor GUI fixes
+
+Updated seuser:
+ Added a policy for seuser tool itself
+ Various GUI updates and bug fixes
+ Compatibility updates
diff --git a/README b/README
new file mode 100644
index 0000000..04b159b
--- /dev/null
+++ b/README
@@ -0,0 +1,456 @@
+SETools - Policy analysis tools for SELinux (C) 2001-2010
+Tresys Technology
+setools@tresys.com, http://oss.tresys.com/projects/setools
+
+
+TABLE OF CONTENTS
+-----------------
+
+1. Overview
+2. Installation
+ 2.1. compiling from official distribution
+ 2.2. compiling from SVN checkout
+ 2.3. configure flags
+ 2.4. using development version of SELinux
+ 2.5. Logwatch support
+ 2.6. doxygen support
+3. Features
+ 3.1. graphical tools
+ 3.2. command-line tools
+ 3.3. analysis libraries
+4. Obtaining SETools
+5. Reporting bugs
+6. Copyright license
+
+
+1. Overview
+-----------
+
+This file describes SETools, developed by Tresys Technology. SETools
+is a collection of graphical tools, command-line tools, and libraries
+designed to facilitate SELinux policy analysis. Although SETools is
+primarily targeted for Red Hat-based systems, it should also work for
+Gentoo and Debian distributions. See the file KNOWN-BUGS for testing
+information.
+
+SETools includes the following graphical tools, command-line tools,
+and libraries:
+
+ apol policy analysis tool
+ libapol policy analysis library
+ libpoldiff semantic policy difference library
+ libqpol library that abstracts policy internals
+ libseaudit parse and filter SELinux audit messages in log files
+ libsefs open and search SELinux file contexts
+ seaudit audit log analysis tools: seaudit and seaudit-report
+ sechecker SELinux policy checking tool
+ secmds command line tools: seinfo, sesearch, findcon,
+ replcon, and indexcon
+ sediff semantic policy difference tools: sediff and sediffx
+
+Each of these components is in a subdirectory under the top-level
+source directory, along with supporting pieces in the following
+directories:
+
+ man manual pages for SETools commands
+ packages miscellaneous support for external packages
+
+In addition the top-level source directory contains various pieces of
+documentation. Please consult the file KNOWN-BUGS in this directory
+prior to filing any bug reports.
+
+
+2. Installation
+---------------
+
+SETools uses the GNU build system to configure, compile, and install.
+As such it contains a configure script that will verify its
+dependencies. SETools requires the following development packages for
+compilation:
+ flex
+ bison
+ pkg-config 0.23 or greater
+ libselinux 2.0.87 or greater
+ libsepol 2.0.38 or greater
+ libsepol-static 2.0.38 or greater
+ libxml2
+ sqlite 3.6.20 or greater
+
+These packages are needed to build SETools's graphical tools:
+ swig 1.3.28 or greater
+ bwidget 1.8 or later
+ tcl-devel 8.4.9 or greater
+ tk-devel 8.4.9 or greater
+ glib2-devel
+ gtk2-devel 2.8 or greater
+ libglade2-devel
+
+To build additional SETools SWIG wrappers, these packages are
+required:
+ Java JDK 1.2 or greater
+ python-devel 2.3 or greater
+
+Apol requires BWidget 1.7 or greater to run. The BWidget toolkit is
+part of the tcllib package and is often not present in Linux
+distributions; the toolkit may be freely downloaded at
+http://tcllib.sourceforge.net. The supplied configure script attempts
+to detect the version of BWidget installed. If it is not found then
+SETools will use the prepackaged one found within the 'packages'
+subdirectory. In some situations the toolkit will not be
+automatically found; if you are sure that BWidget is present then
+specify --disable-bwidget-check to the configure script.
+
+
+2.1. compiling from official distribution
+-----------------------------------------
+
+The official, stable source distribution is available from
+http://oss.tresys.com/projects/setools/. Untar and uncompress the
+distribution, and perform the following.
+
+ $ cd setools-3.3.7
+ $ ./configure
+ $ make
+ $ make install
+
+This will put the binaries in /usr/local/bin, data files in
+/usr/local/share/setool-3.3, and libraries in /usr/local/lib.
+Assuming that /usr/local/bin is in your $PATH and /usr/local/lib in
+$LD_LIBRARY_PATH everything should now work.
+
+
+2.2. compiling from SVN checkout
+--------------------------------
+
+If you prefer the bleeding edge of SETools development, you could
+instead obtain the development version of SETools from the Subversion
+repository (see Section 4).
+
+ $ cd setools
+ $ autoreconf -i -s
+ $ ./configure
+ $ make
+ $ make install
+
+You will need a recent version of autoconf to create the configure
+script. SETools was written using autoconf-2.60, although
+autoconf-2.59 also seems to work correctly albeit with a build
+warning.
+
+As SETools uses the GNU build system, other make targets are
+available. `make install-strip' will strip unneeded symbols from
+installed binaries. `make uninstall' removes files written by an
+earlier install.
+
+
+2.3. configure flags
+--------------------
+
+You can customize your SETools build using the flags given to
+`configure'. Notable options include:
+
+ --enable-debug
+ All code will be compiled using static libraries and the gcc
+ flags '-g3 -gdwarf-2 -O0'. This flag is useful for tracking
+ down issues.
+
+ --disable-gui
+ Build only the command-line tools: seinfo, sesearch, findcon,
+ indexcon, replcon, sechecker, and sediff.
+
+ --disable-bwidget-check
+ Assume that BWidget 1.8 is installed on the system. The
+ configure script normally tries to launch a Tcl script that
+ loads BWidget, which requires a running X session. You will
+ need this flag if compiling in a non-X environment.
+
+ --disable-selinux-check
+ Disable the build-time check for SELinux. In rare
+ circumstances the build computer will not have SELinux
+ running, resulting in 'configure' producing a warning and
+ disable parts of SETools. By specifying this flag,
+ 'configure' will not disable parts of SETools.
+
+ --enable-swig-java
+ Build SWIG interfaces for Java. This permits third-party
+ developers who prefer Java to use the SETools libraries for
+ their own projects.
+
+ --enable-swig-python
+ Build SWIG interfaces for Python. This permits third-party
+ developers who prefer Python to use the SETools libraries for
+ their own projects.
+
+ --enable-swig-tcl
+ Build SWIG interfaces for Tcl. This is needed for the apol
+ tool. By default this flag is enabled.
+
+ --enable-sepol-src=PATH
+ Look for libsepol source files in PATH. Use this flag when
+ compiling against a development version of SELinux (see
+ Section 2.4). Note that if --enable-sepol-src and
+ --with-sepol-devel are both specified then this flag takes
+ precedence.
+
+ --with-tcl=PATH
+ Look for Tcl development files in PATH. Debian users will
+ need to specify this flag, as Tcl 8.4 is typically located at
+ /usr/lib/tcl8.4.
+
+ --with-tk=PATH
+ Look for Tk development files in PATH. Debian users will need
+ to specify this flag, as Tk 8.4 is typically located at
+ /usr/lib/tk8.4.
+
+ --with-sepol-devel=PATH
+ Look for libsepol header files in PATH/include and library in
+ PATH/lib64 and PATH/lib. Note that if --enable-sepol-src and
+ --with-sepol-devel are both specified then --enable-sepol-src
+ takes precedence.
+
+ --with-selinux-devel=PATH
+ Look for libselinux header files in PATH/include and library
+ in PATH/lib64 and PATH/lib.
+
+ --with-default-policy=PATH
+ Explicitly use PATH as the default SELinux policy source file,
+ instead of inferring its location based upon the return value
+ of selinux_policy_root().
+
+ --with-test-policies=PATH
+ Use the policies in PATH as input to the SETools tests; these
+ tests are invoked upon `make check'.
+
+Of course, `configure' accepts other usual flags such as --prefix.
+
+
+2.4. Using a development version of SELinux
+-----------------------------------------
+
+As SELinux is a rapidly evolving project, you may wish to use a
+version of libsepol.so that is newer than the one installed to
+/usr/lib. To support different versions of libsepol, SETools can be
+configured to compile against a specific version of libsepol using the
+--enable-sepol-src flag. For example, suppose you have a SELinux SVN
+checkout and compilation like the following:
+
+ $ cd /home/gburdell
+ $ svn co https://svn.sourceforge.net/svnroot/selinux/trunk selinux
+ $ cd selinux/libsepol
+ $ make
+
+You can compile SETools against this particular copy of libsepol:
+
+ $ cd /home/gburdell/setools
+ $ ./configure --enable-sepol-src=/home/gburdell/selinux/libsepol
+
+Note that --enable-sepol-src will override the flag
+--with-sepol-devel.
+
+
+2.5. Logwatch support
+---------------------
+
+Integrating SETools with Logwatch can provide an effective IDS
+solution by automating customized audit reports and having them
+emailed to a specific recipient(s) for further analysis. You can
+integrate SETools into Logwatch using the seaudit-report plugin by
+specifying the `make install-logwatch' target. This target installs
+the configuration necessary for having seaudit-report run as a
+Logwatch service. The configuration files are part of the SETools
+source distribution, located in the seaudit subdirectory, and include:
+
+ seaudit-report-group.conf:
+ logfile group configuration file
+
+ seaudit-report-service.conf:
+ service filter config file
+
+ seaudit-report-service:
+ service filter script
+
+Make sure the Logwatch program is installed before proceeding with
+using this install target.
+
+
+2.6. doxygen support
+--------------------
+
+All externally exported library functions include doxygen-style tags
+in the documentation. To produce your own HTML outputs when writing
+third-party tools, use the doxygen configuration file located in
+packages/Doxyfile; it directs generated output to /tmp/setools. From
+the top-level source directory do:
+
+ $ doxygen packages/Doxyfile
+
+
+3. Features
+-----------
+
+SETools encompasses a number of tools, both graphical and command
+line, and libraries. Many of the programs have help files accessible
+during runtime.
+
+
+3.1. graphical tools
+--------------------
+
+The main emphasis of SETools is the graphical analysis tools.
+
+ apol:
+ A Tcl/Tk graphical analysis tool. Use it to open a SELinux
+ policy, examine the policy's components and rules, and perform
+ various types of analyses.
+
+ seaudit:
+ A GTK+ graphical audit log analysis tool for SELinux. This
+ tool allows users to sort and filter the system's audit log,
+ query the policy based on audit messages, and export audit log
+ messages to a file. The tool can also create reports in HTML
+ or plaintext format using an entire audit log or an seaudit
+ view. Note that this program is installed in $(PREFIX)/sbin
+ because its main function is to analyze /var/log/audit/audit.log.
+
+ sediffx:
+ A GTK+ graphical tool to semantically compare two policies.
+ Use sediffx to open two SELinux policies, find differences
+ between them, and then show those results.
+
+
+3.2. command-line tools
+-----------------------
+
+Some tools in the SETools suite may be run in a non-windowing
+environment. The first six tools listed below are located in the
+secmds subdirectory; the rest are in their own directories.
+
+ seinfo:
+ A tool to quickly get a list of components from a SELinux
+ policy.
+
+ sesearch:
+ A tool to search rules (allow, type_transition, etc.) and constraints
+ within a SELinux policy.
+
+ findcon:
+ A tool to search files with a matching SELinux file context.
+ The tool can search a filesystem directly, a file_contexts file,
+ or a database as created by indexcon.
+
+ replcon:
+ A tool to search the filesystem, replacing a matched file's
+ context with a different one.
+
+ indexcon:
+ A tool to create a database that indexes the security contexts
+ of a SELinux filesystem.
+
+ sechecker:
+ A tool for performing modular checks on an SELinux policy.
+ Sechecker supports configuration profiles to specify multiple
+ modules and generates a report of potential issues within a
+ policy.
+
+ seaudit-report:
+ A tool for generating reports on SELinux audit messages in
+ plaintext or HTML format. Reports generated by this tool can
+ be configured to include standard report sections such as
+ policy load messages, enforcement toggles messages, policy
+ boolean messages, etc. A key feature of the tool is that
+ reports can be further customized through the use of saved
+ seaudit view files. The tool can effectively be used as a
+ plugin to other audit log analysis tools, such as the Logwatch
+ daemon.
+
+ sediff:
+ A tool to load two SELinux policies, find differences between
+ them, and then show those results. The tool provides a
+ command-line interface to libpoldiff.
+
+
+3.3. analysis libraries
+-----------------------
+
+The SETools support libraries (libapol, libpoldiff, libqpol,
+libseaudit, and libsefs) are available for use in third-party
+applications. Although they are not officially supported (and thus
+subject to change between SETools releases), we will do our best to
+maintain compatibility beginning with SETools version 3.0.
+
+ libqpol:
+ Abstract the internals of an SELinux policy behind a
+ consistent interface, such that changes to the policy
+ representation (as governed by libsepol) do not affect
+ analysis tools.
+
+ libapol:
+ Work with libqpol to perform higher-order analyses of a
+ policy. A typical sequence for an analysis tool is:
+ open a policy via apol_policy_open()
+ execute some query via apol/policy-query.h
+ obtain detailed results via qpol/policy_query.h
+ close the policy via apol_policy_destroy()
+
+ libseaudit:
+ Parse and store SELinux audit messages. Its chief users are
+ seaudit and seaudit-report.
+
+ libpoldiff:
+ Accept two SELinux policies and finds differences between
+ them. Its main users are sediff and sediffx.
+
+ libsefs:
+ Create a represention of file contexts, by reading contexts
+ directly from a filesystem, from a file_contexts file, or from a
+ specially formatted database. Queries can then be created and
+ executed against those file contexts
+
+These libraries have SWIG wrappers that are built if
+--enable-swig-java, --enable-swig-python, and/or --enable-swig-tcl are
+given during configuration time. The generated Java wrappers will be
+in placed $PREFIX/lib; symlinks to jar files will be in
+$PREFIX/share/java. Python wrappers will be installed to Python's
+site-packages directory. Tcl wrappers are built as Tcl packages
+(e.g., `package require apol') and placed in $PREFIX/lib/setools.
+
+
+4. Obtaining SETools
+--------------------
+
+Official releases of SETools may be freely downloaded from Tresys's
+Open Source Software website, http://oss.tresys.com/projects/setools.
+
+Tresys builds RPM packages of SETools. They may also be obtained from
+the website listed above.
+
+SETools source code is maintained within a Subversion repository.
+From the command line do:
+
+ $ svn co http://oss.tresys.com/repos/setools/trunk/ setools
+
+You may also browse the SVN repository at
+http://oss.tresys.com/projects/setools/browser.
+
+Other binary releases SETools are available for your favorite Linux
+packaging system from third-party sources. Gentoo users have an
+ebuild script for SETools. Debian maintains the dpkg "setools" in
+section admin, priority optional.
+
+
+5. Reporting bugs
+-----------------
+
+If you found a bug, have a suggestion, or otherwise would like to
+comment upon SETools, please email setools-bugs@tresys.com. We will
+respond to you as soon as possible.
+
+
+6. Copyright license
+--------------------
+
+The intent is to allow free use of this source code. All programs'
+source files are copyright protected and freely distributed under the
+GNU General Public License (see COPYING.GPL). All library source
+files are copyright under the GNU Lesser General Public License (see
+COPYING.LGPL). Absolutely no warranty is provided or implied.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..c16f127
--- /dev/null
+++ b/TODO
@@ -0,0 +1,29 @@
+SETools TODO List
+=======================
+The following items are desirable features to be considered for future
+versions.
+
+Recent policies list - similar to that in seaudit - sediffx
+
+Improved type feedback - convert results to Tk table and permit
+right-click to pull up type info dialog - apol
+
+Module source loading - load .te and .if sources for modules - all
+tools
+
+Global semantic/syntactic option - allow semantic and syntactic search
+globally - apol
+
+Report generation - similar to saved queries export query results -
+apol
+
+Role dominance queries - query for role dominance relationships - apol
+& seinfo(display only)
+
+Constraint analysis - features to search and analyze constraints -
+apol
+
+Result filtering/sorting - allow rule diffs to be sorted or filtered
+by various fields - sediffx
+
+Duplicate TE warning - can we remove it - parser
diff --git a/VERSION.in b/VERSION.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/VERSION.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/apol/Makefile.am b/apol/Makefile.am
new file mode 100644
index 0000000..646d8b1
--- /dev/null
+++ b/apol/Makefile.am
@@ -0,0 +1,131 @@
+wrappedso_DATA = libapol_tcl.so.@libapol_version@
+wrappedso_SONAME = $(wrappedso_DATA)
+wrappedsodir = $(libdir)/setools/apol_tcl
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+bin_SCRIPTS = apol
+
+dist_noinst_DATA = apol_tcl.i apol_tcl.cc apol.png foo_module.tcl
+BUILT_SOURCES = apol_tcl_wrap.cc mkIndex.tcl init.tcl
+
+dist_setools_DATA = apol_help.txt domaintrans_help.txt file_relabel_help.txt \
+ infoflow_help.txt types_relation_help.txt \
+ perm_maps/apol_perm_mapping_ver12 \
+ perm_maps/apol_perm_mapping_ver15 \
+ perm_maps/apol_perm_mapping_ver16 \
+ perm_maps/apol_perm_mapping_ver17 \
+ perm_maps/apol_perm_mapping_ver18 \
+ perm_maps/apol_perm_mapping_ver19 \
+ perm_maps/apol_perm_mapping_ver20 \
+ perm_maps/apol_perm_mapping_ver21 \
+ perm_maps/apol_perm_mapping_ver22 \
+ perm_maps/apol_perm_mapping_ver23 \
+ perm_maps/apol_perm_mapping_ver24 \
+ apol.gif
+
+EXTRA_DIST = \
+ analysis_tab.tcl \
+ classes_perms_tab.tcl \
+ common_widgets.tcl \
+ cond_bools_tab.tcl \
+ cond_rules_tab.tcl \
+ context_dialog.tcl \
+ context_selector.tcl \
+ directflow_module.tcl \
+ domaintrans_module.tcl \
+ file_contexts_tab.tcl \
+ find.tcl \
+ fscontexts_tab.tcl \
+ goto.tcl \
+ head.tcl \
+ initial_sids_tab.tcl \
+ level_dialog.tcl \
+ mls_tab.tcl \
+ netcontexts_tab.tcl \
+ open_policy_dialog.tcl \
+ perms_map.tcl \
+ policyconf.tcl \
+ progress_dialog.tcl \
+ range_dialog.tcl \
+ range_selector.tcl \
+ range_trans.tcl \
+ rbac_tab.tcl \
+ relabel_module.tcl \
+ roles_tab.tcl \
+ terules_tab.tcl \
+ transflow_module.tcl \
+ types_relation_module.tcl \
+ types_tab.tcl \
+ users_tab.tcl \
+ util.tcl \
+ top.tcl
+# top.tcl must be last one written because it spawns the rest of the script
+
+TCL_STRIP_FILES = $(patsubst %,$(srcdir)/%,$(filter-out head.tcl,$(EXTRA_DIST)))
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so \
+ $(top_builddir)/libqpol/swig/tcl/libtqpol.so \
+ $(top_builddir)/libapol/swig/tcl/libtapol.so \
+ $(top_builddir)/libsefs/swig/tcl/libtsefs.so \
+ $(top_builddir)/config.tcl
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @SEFS_CFLAGS@ @APOL_CFLAGS@ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @SEFS_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+
+apol_tcl_wrap.cc: apol_tcl.i $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_TCL_OPT) -pkgversion @libapol_version@ -o $@ \
+ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libsefs/swig \
+ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libapol/swig \
+ -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): apol_tcl.cc apol_tcl_wrap.cc
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+
+$(package_SCRIPTS): $(wrappedso_DATA) mkIndex.tcl
+ cat mkIndex.tcl | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src:$(top_builddir)/libsefs/src $(TCLSH_PROG)
+ $(mkdir_p) apol_tcl
+ cp $< $@ apol_tcl
+
+mkIndex.tcl: $(wrappedso_DATA) Makefile
+ @echo "lappend auto_path $(top_builddir)/libapol/swig/tcl $(top_builddir)/libsefs/swig/tcl" > $@
+ @echo "package require apol" >> $@
+ @echo "package require sefs" >> $@
+ @echo "pkg_mkIndex . $<" >> $@
+
+TCL_AUTOPATH = @TCL_AUTOPATH@
+
+init.tcl: Makefile
+ @echo "proc tcl_config_init_libraries {} {" > $@
+ @echo " global auto_path" >> $@
+ @echo " lappend auto_path $(TCL_AUTOPATH)" >> $@
+ @echo " print_init \"Initializing libqpol... \"" >> $@
+ @echo " package require qpol @libqpol_version@" >> $@
+ @echo " print_init \"done.\nInitializing libapol... \"" >> $@
+ @echo " package require apol @libapol_version@" >> $@
+ @echo " print_init \"done.\nInitializing libsefs... \"" >> $@
+ @echo " package require sefs @libsefs_version@" >> $@
+ @echo " print_init \"done.\nInitializing libapol_tcl... \"" >> $@
+ @echo " package require apol_tcl @libapol_version@" >> $@
+ @echo " print_init \"done.\n\"" >> $@
+ @echo "}" >> $@
+ @echo "proc tcl_config_get_install_dir {} {" >> $@
+ @echo " return \"${setoolsdir}\"" >> $@
+ @echo "}" >> $@
+
+$(bin_SCRIPTS): $(package_SCRIPTS) head.tcl init.tcl $(top_builddir)/config.tcl $(TCL_STRIP_FILES)
+ cat $(srcdir)/head.tcl init.tcl $(top_builddir)/config.tcl > $@
+ cat $(TCL_STRIP_FILES) | perl -p -i -e 's/^\s*(\#[^!]*){0,1}$$//s' >> $@
+ chmod u+x $@
+
+%.tcl:
+# do nothing
+
+CLEANFILES = $(packages_SCRIPTS) $(bin_SCRIPTS) $(BUILT_SOURCES)
+
+MOSTLYCLEANFILES = $(wrappedso_DATA) $(package_SCRIPTS) apol_tcl/$(wrappedso_DATA) apol_tcl/$(package_SCRIPTS)
diff --git a/apol/analysis_tab.tcl b/apol/analysis_tab.tcl
new file mode 100644
index 0000000..99fb67c
--- /dev/null
+++ b/apol/analysis_tab.tcl
@@ -0,0 +1,326 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Analysis {
+ variable vals
+ variable widgets
+ variable tabs
+}
+
+proc Apol_Analysis::create {tab_name nb} {
+ variable vals
+ variable widgets
+
+ set frame [$nb insert end $tab_name -text "Analysis"]
+ set pw [PanedWindow $frame.pw -side left -weights extra]
+ set topf [$pw add -weight 0]
+ set bottomf [$pw add -weight 1]
+ pack $pw -expand 1 -fill both
+
+ set top_leftf [TitleFrame $topf.left -text "Analysis Type"]
+ set opts_f [TitleFrame $topf.opts -text "Analysis Options"]
+ set buttons_f [frame $topf.buttons]
+ pack $top_leftf -side left -expand 0 -fill y -padx 2
+ pack $opts_f -side left -expand 1 -fill both -padx 2
+ pack $buttons_f -side right -expand 0 -anchor ne -padx 2
+ set results_f [TitleFrame $bottomf.r -text "Analysis Results"]
+ pack $results_f -expand 1 -fill both -padx 2
+
+ set widgets(modules) [Apol_Widget::makeScrolledListbox [$top_leftf getframe].m \
+ -height 8 -width 24 -listvar Apol_Analysis::vals(module_names) -exportselection 0]
+ $widgets(modules).lb selection set 0
+ bind $widgets(modules).lb <<ListboxSelect>> Apol_Analysis::_selectModule
+ pack $widgets(modules) -expand 1 -fill both
+
+ set widgets(search_opts) [PagesManager [$opts_f getframe].s]
+ foreach m $vals(modules) {
+ ${m}::create [$widgets(search_opts) add $m]
+ }
+ $widgets(search_opts) compute_size
+ $widgets(search_opts) raise [lindex $vals(modules) 0]
+ pack $widgets(search_opts) -expand 1 -fill both
+
+ set widgets(new) [button $buttons_f.new -text "New Analysis" -width 12 \
+ -command [list Apol_Analysis::_analyze new]]
+ set widgets(update) [button $buttons_f.update -text "Update Analysis" -width 12 -state disabled \
+ -command [list Apol_Analysis::_analyze update]]
+ set widgets(reset) [button $buttons_f.reset -text "Reset Criteria" -width 12 \
+ -command Apol_Analysis::_reset]
+ set widgets(info) [button $buttons_f.info -text "Info" -width 12 \
+ -command Apol_Analysis::_info]
+ pack $widgets(new) $widgets(update) $widgets(reset) $widgets(info) \
+ -side top -pady 5 -padx 5 -anchor ne
+
+ set popupTab_Menu [menu .popup_analysis -tearoff 0]
+ set tab_menu_callbacks \
+ [list {"Close Tab" Apol_Analysis::_deleteResults} \
+ {"Rename Tab" Apol_Analysis::_displayRenameTabDialog}]
+
+ set widgets(results) [NoteBook [$results_f getframe].results]
+ $widgets(results) bindtabs <Button-1> Apol_Analysis::_switchTab
+ $widgets(results) bindtabs <Button-3> \
+ [list ApolTop::popup \
+ %W %x %y $popupTab_Menu $tab_menu_callbacks]
+ set close [button [$results_f getframe].close -text "Close Tab" \
+ -command Apol_Analysis::_deleteCurrentResults]
+ pack $widgets(results) -expand 1 -fill both -padx 4
+ pack $close -expand 0 -fill x -padx 4 -pady 2
+
+ _reinitializeTabs
+ return $frame
+}
+
+proc Apol_Analysis::open {ppath} {
+ variable vals
+ foreach m $vals(modules) {
+ ${m}::open
+ }
+}
+
+proc Apol_Analysis::close {} {
+ variable vals
+ variable widgets
+ foreach m $vals(modules) {
+ ${m}::close
+ }
+ _reinitializeTabs
+}
+
+proc Apol_Analysis::getTextWidget {} {
+ variable widgets
+ variable tabs
+ set curid [$widgets(results) raise]
+ if {$curid != {}} {
+ return [$tabs($curid:module)::getTextWidget [$widgets(results) getframe $curid]]
+ }
+ return {}
+}
+
+proc Apol_Analysis::save_query_options {file_channel query_file} {
+ variable widgets
+ set m [$widgets(search_opts) raise]
+ puts $file_channel $m
+ ${m}::saveQuery $file_channel
+}
+
+proc Apol_Analysis::load_query_options {file_channel} {
+ variable vals
+ variable widgets
+
+ # Search for the module name
+ set line {}
+ while {[gets $file_channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ break
+ }
+ if {$line == {} || [set i [lsearch -exact $vals(modules) $line]] == -1} {
+ tk_messageBox -icon error -type ok -title "Open Apol Query" -message "The specified query is not a valid analysis module."
+ return
+ }
+ ${line}::loadQuery $file_channel
+ $widgets(modules).lb selection clear 0 end
+ set module [lindex $vals(modules) $i]
+ $widgets(search_opts) raise $module
+ $widgets(modules).lb selection set [lsearch $vals(module_names) $vals($module:name)]
+}
+
+#################### functions invoked by modules ####################
+
+proc Apol_Analysis::registerAnalysis {mod_proc mod_name} {
+ variable vals
+ lappend vals(modules) $mod_proc
+ lappend vals(module_names) $mod_name
+ set vals($mod_proc:name) $mod_name
+}
+
+proc Apol_Analysis::createResultTab {short_name criteria} {
+ variable widgets
+ variable tabs
+
+ set i $tabs(next_result_id)
+ incr tabs(next_result_id)
+ set m [$widgets(search_opts) raise]
+ set id "results$i"
+ set frame [$widgets(results) insert end $id -text "($i) $short_name"]
+ $widgets(results) raise $id
+
+ set tabs($id:module) $m
+ set tabs($id:vals) $criteria
+ return $frame
+}
+
+proc Apol_Analysis::setResultTabCriteria {criteria} {
+ variable widgets
+ variable tabs
+ set id [$widgets(results) raise]
+ if {$id != {}} {
+ set tabs($id:vals) $criteria
+ }
+}
+
+#################### private functions ####################
+
+proc Apol_Analysis::_selectModule {} {
+ variable vals
+ variable widgets
+ variable tabs
+
+ focus $widgets(modules).lb
+ if {[set selection [$widgets(modules).lb curselection]] == {}} {
+ return
+ }
+ set module [lindex $vals(modules) [lindex $selection 0]]
+ $widgets(search_opts) raise $module
+ set result_tab [$widgets(results) raise]
+ if {$result_tab != {} && $tabs($result_tab:module) == $module} {
+ $widgets(update) configure -state normal
+ } else {
+ $widgets(update) configure -state disabled
+ }
+}
+
+proc Apol_Analysis::_analyze {which_button} {
+ variable vals
+ variable widgets
+ variable tabs
+ $widgets(new) configure -state disabled
+ $widgets(update) configure -state disabled
+
+ set m [$widgets(search_opts) raise]
+ set retval [Apol_Progress_Dialog::wait "$vals($m:name) Analysis" \
+ "Performing $vals($m:name) Analysis..." \
+ {
+ if {$which_button == "new"} {
+ ${m}::newAnalysis
+ } else {
+ set f [$widgets(results) getframe [$widgets(results) raise]]
+ if {[set retval [${m}::updateAnalysis $f]] != {}} {
+ _deleteCurrentResults
+ }
+ set retval
+ }
+ }]
+ if {$retval != {}} {
+ tk_messageBox -icon error -type ok -title "$vals($m:name) Analysis" -message "Error while performing analysis:\n\n$retval"
+ }
+ if {[$widgets(results) raise] == {}} {
+ $widgets(update) configure -state disabled
+ } else {
+ $widgets(update) configure -state normal
+ }
+
+ $widgets(new) configure -state normal
+}
+
+proc Apol_Analysis::_reset {} {
+ variable vals
+ variable widgets
+ set m [$widgets(search_opts) raise]
+ ${m}::reset
+}
+
+proc Apol_Analysis::_info {} {
+ variable vals
+ variable widgets
+ set m [$widgets(search_opts) raise]
+ Apol_Widget::showPopupParagraph $vals(${m}:name) [${m}::getInfo]
+}
+
+proc Apol_Analysis::_reinitializeTabs {} {
+ variable widgets
+ variable tabs
+ array set tabs {
+ next_result_id 1
+ }
+ foreach p [$widgets(results) pages 0 end] {
+ _deleteResults $p
+ }
+}
+
+proc Apol_Analysis::_switchTab {pageID} {
+ variable vals
+ variable widgets
+ variable tabs
+
+ $widgets(update) configure -state normal
+ # check if switching to already visible tab
+ if {[$widgets(results) raise] == $pageID} {
+ return
+ }
+ $widgets(results) raise $pageID
+ set cur_search_opts [$widgets(search_opts) raise]
+
+ # restore the tab's search criteria
+ set m $tabs($pageID:module)
+ ${m}::switchTab $tabs($pageID:vals)
+
+ # update the analysis type selection
+ $widgets(modules).lb selection clear 0 end
+ $widgets(modules).lb selection set [lsearch $vals(module_names) $vals(${m}:name)]
+ $widgets(search_opts) raise $m
+}
+
+proc Apol_Analysis::_deleteResults {pageID} {
+ variable widgets
+ variable tabs
+
+ # Remove tab and its widgets
+ set curpos [$widgets(results) index $pageID]
+ $widgets(results) delete $pageID
+ array unset tabs $pageID:*
+ array unset tabs $pageID
+
+ # try to raise the next tab
+ if {[set next_id [$widgets(results) pages $curpos]] != {}} {
+ _switchTab $next_id
+ } elseif {$curpos > 0} {
+ # raise the previous page instead
+ _switchTab [$widgets(results) pages [expr {$curpos - 1}]]
+ } else {
+ # no tabs remaining
+ $widgets(update) configure -state disabled
+ }
+}
+
+proc Apol_Analysis::_deleteCurrentResults {} {
+ variable widgets
+ if {[set curid [$widgets(results) raise]] != {}} {
+ _deleteResults $curid
+ }
+}
+
+proc Apol_Analysis::_displayRenameTabDialog {pageID} {
+ variable widgets
+ variable tabs
+ set d [Dialog .apol_analysis_tab_rename -homogeneous 1 -spacing 2 -cancel 1 \
+ -default 0 -modal local -parent . -place center -separator 1 \
+ -side bottom -title "Rename Results Tab"]
+ $d add -text "OK" -command [list $d enddialog "ok"]
+ $d add -text "Cancel" -command [list $d enddialog "cancel"]
+ set f [$d getframe]
+ set l [label $f.l -text "Tab name:"]
+ set tabs(tab:new_name) [$widgets(results) itemcget $pageID -text]
+ set e [entry $f.e -textvariable Apol_Analysis::tabs(tab:new_name) -width 16 -bg white]
+ pack $l $e -side left -padx 2
+ set retval [$d draw]
+ destroy $d
+ if {$retval == "ok"} {
+ $widgets(results) itemconfigure $pageID -text $tabs(tab:new_name)
+ }
+}
diff --git a/apol/apol.gif b/apol/apol.gif
new file mode 100644
index 0000000..48d74d5
--- /dev/null
+++ b/apol/apol.gif
Binary files differ
diff --git a/apol/apol.png b/apol/apol.png
new file mode 100644
index 0000000..7029175
--- /dev/null
+++ b/apol/apol.png
Binary files differ
diff --git a/apol/apol.xcf b/apol/apol.xcf
new file mode 100644
index 0000000..ea6d26e
--- /dev/null
+++ b/apol/apol.xcf
Binary files differ
diff --git a/apol/apol_help.txt b/apol/apol_help.txt
new file mode 100644
index 0000000..aad309b
--- /dev/null
+++ b/apol/apol_help.txt
@@ -0,0 +1,482 @@
+SELinux Policy Analysis Tool Help File
+
+
+Overview
+--------
+This file contains basic help information for using apol, a graphical
+policy analysis tool for Security Enhanced (SELinux) policies. The
+tool provides the ability to:
+
+ + Examine, search, and relate policy components (types, type
+ attributes, object classes, object permissions, roles, users,
+ initials SIDs, MLS components, network and file system contexts,
+ and booleans), and policy rules (allow, neverallow, auditallow,
+ dontaudit, type_transition, type_change, role allow,
+ role_transition, and range_transition).
+
+ + Create and query an on-disk database that contains SELinux
+ context information about the filesystem.
+
+ + Perform some automated analysis of policies, including forward and
+ reverse domain transition analyses, direct information flow
+ analysis, as well as transitive (indirect) information flow
+ analysis, direct relabel analysis, and type relationship analysis.
+
+The tool supports source, monolithic binary, and modular binary
+policies. Certain apol features may be disabled if the underlying
+policy does not support the action. For example, rule searches will
+not report line numbers when searching monolithic binary polices.
+
+Apol provides compatibility with the current and previous policy
+syntax. It supports analysis of monolithic policy versions 12 to the
+current version 21 and modular policy versions 5 and 6.
+
+See setools/ChangeLog for a list of new features in this release. See
+setools/KNOWN_BUGS for a list of current bugs.
+
+
+Menus
+-----
+Use 'Open' from the File menu to open a valid policy. The policy may
+be monolithic or be composed of a base linked with multiple modules.
+Only one policy can be open at a time; opening a second policy will
+result in the first being closed.
+
+The Query menu allows the user to save or load a query for a TE Rules
+search or for an analysis module listed on the Analysis tab. Saving a
+query writes the appropriate parameters and settings to a '.qf' file:
+for TE Rules queries, the required query parameters are saved; for
+analysis queries, the required query parameters as well as the
+specified advanced settings are saved. When loading a query, apol
+parses the specified query (.qf) file, raises the correct tab and
+configures the query options with the specified query parameters and
+advanced settings. The Load Query menu item is enabled across all
+tabs, but the save query menu item is only enabled when the Analysis
+tab or the TE Rules tab (see the Policy Rules tab description below)
+is raised. Choose 'Policy Summary' from the Query menu to display
+statistics about the currently loaded policy. A shorthand version of
+these statistics is always displayed on the status bar when a policy
+is opened.
+
+Permission mappings are managed through the Tools menu. The mappings
+are used by apol's direct and transitive information flow analyses.
+Mappings may be viewed with the View Perm Map menu item. Although the
+ensuing dialog is not required to perform an information flow
+analysis, the user may fine tune those mappings. See the separate
+help file on information flow for more information about permission
+mappings and their management.
+
+
+Policy Components tabs
+----------------------
+The policy components tabs provide the means to examine, search, and
+relate the core components of an SELinux policy.
+
+ Types tab
+ ---------
+ Use the Types tab to search through types and attributes. Double
+ click or right click on any type or attribute in the list boxes to
+ see full details for that type or attribute. If a file index has
+ been loaded (see File Contexts tab description below), details will
+ include files labeled with that particular type or attribute.
+
+ Use the search options and hit the OK button to perform searches for
+ types. Alternately, use the "Search using regular expression" box
+ to search for types and/or attributes using a POSIX-style regular
+ expression.
+
+ Classes/Perms tab
+ -----------------
+ Use the Classes/Perms tab to view and search object classes, common
+ permissions, and permissions defined in a policy. Double clicking
+ on any name from the three list boxes gives a brief summary of the
+ class, permission, or common permission. Use the search options to
+ view more detailed aspects of classes and permissions.
+
+ For example, to display the objects that use the permission getattr,
+ select "Permissions", and the button "Object Classes" directly below
+ it. Then select "Search using regular expression" and type
+ "^getattr$" in the box. Press OK and a list of object classes that
+ use that permission displays (a * will mean that the class uses that
+ permission via a common permission).
+
+ Regular expressions can be used to constrain the search. For
+ example, to find all the permissions that start with the string
+ "set", use the regular expression "^set".
+
+ Roles tab
+ ----------
+ Use the Roles tab to search roles and their allowed types.
+ Functionality for this tab is essentially the same as the Types tab
+ (e.g., double click on a role for details about that role).
+
+ The primary search option provides the means to find all roles that
+ include a given type.
+
+ Users tab
+ ---------
+ Select the Users tab to search users defined in the policy and to
+ view the roles allowed for that user, the default MLS level and
+ allowed MLS range for users (if a MLS policy is loaded).
+
+ Booleans tab
+ ------------
+ Select the Booleans tab to search the boolean variables defined in
+ the policy, as well as to view the current state and/or policy
+ default state of the variable. This tab also provides the interface
+ to change the state of the boolean variable to TRUE or FALSE. This
+ boolean state change will be applied in memory and but will not
+ change the state within the actual policy.
+
+ MLS tab
+ -------
+ Select the MLS tab to search sensitivities and categories in the
+ policy, as well as to display the level statements for sensitivities
+ and which sensitivites can be associated with a category.
+
+ Initial SIDS tab
+ ----------------
+ Select the Initial SIDS tab to search initial sids defined in the
+ policy, as well as to view the context for each initial sid.
+
+ Net Contexts tab
+ ----------------
+ Select the Net Contexts tab to search network-based contexts
+ (portcon, netifcon, and nodecon statements) defined in the policy.
+
+ FS Contexts tab
+ ---------------
+ Select the FS Contexts tab to search filesystem-based contexts
+ (fs_use_ and genfscon statements) defined in the policy.
+
+
+Policy Rules tabs
+-----------------
+The Policy Rules tabs allow more advanced analysis of an SELinux
+policy. They provide the means to search and select from the many
+rules in a policy based on selected search criteria.
+
+ TE Rules tab
+ ------------
+ Select the TE Rules tab to search through the Type Enforcement
+ rules. This is the most extensively used tab, as well as the most
+ complicated.
+
+ Three different types of search criteria exist for TE Rules:
+
+ 1. RULE SELECTION: provides options to limit the scope of search;
+ only those rules selected will be included in the search. At
+ least one must be selected. NOTE: If no additional search
+ criteria is specified, apol will search for all of the selected
+ rules.
+
+ 2. TYPE/ATTRIBUTES SUBTAB: provides options to refine a search
+ based on types and/or type attributes used by a rule. There
+ are three general type search options: source, target, and
+ default. Default is useful only if one or more of type
+ transition/member/change rules are selected; other rules do not
+ use the default field. The source field also can be used as an
+ "any" field. In this case, the other two options will not be
+ available, and the search will look for the selected
+ type/attribute in any field of the selected rules.
+
+ Use drop down boxes to select a type or attribute. If the
+ "Search using regular expression" box is checked, enter a
+ regular expression in any type/attrib box. If regular
+ expressions are disabled, apol currently supports only one
+ type/attribute in each box. This type/attrib must be a
+ complete, valid type or attrib string. The Default field can
+ only be a type (not an attribute).
+
+ If the "Search only enabled rules" checkbox is selected, query
+ results will include all rules that meet the search criteria,
+ EXCLUDING any rules that have been disbled by a conditional
+ expression. If the checkbox is not selected, query results
+ will include all rules that meet the search criteria, INCLUDING
+ those rules that have been disabled by a conditional
+ expression.
+
+ Typically a search for a particular type also returns rules
+ that employ any of that type's attributes. Likewise, a search
+ for an attribute returns rules that use any of that attribute's
+ types. This "indirect" searching is enabled by default. The
+ "Only direct matches" checkbox alters the meaning of the search
+ field such that it performs literal searches upon the
+ identifier.
+
+ 3. CLASSES/PERMISSIONS SUBTAB: provides options to refine a search
+ using object classes and/or permissions. Only rules that
+ contain the selected object classes and selected permissions
+ will be returned. Each of these boxes allow multiple
+ selections. In the case of multiple select, apol treats them
+ using an "or" semantic (e.g., if two object classes, such as
+ 'dir' and 'file', are selected, rules that apply to file OR
+ directory object classes are selected).
+
+ This tab also includes a section for AV Rule Permissions, as a
+ means to prune the list of permissions based on the object
+ classes selected. However, if none of the AV rules have been
+ selected the permissions section will be disabled. If "All for
+ selected classes" is selected, only permissions related to
+ selected objects are shown. "Common to selected classes"
+ instead only shows permissions that all selected classes have.
+ Below is a checkbox that changes permission matching behavior.
+ If more than one permission is selected, the default behavior
+ is to return rules that contain any of those selections. When
+ the checkbox is enabled, returned rules instead will contain
+ all of them.
+
+ In the Results Tab for a given search, all rules that meet the
+ search criteria are displayed. In addition, if the policy that is
+ opened is capable of showing line numbers, a hyperlink for each rule
+ is shown. Clicking on this link will raise the Policy Source tab
+ and highlight the exact line in the source file where the rule was
+ found. This traces the rule back to the ultimate source code. If
+ the policy cannot show line numbers then there will be no
+ hyperlinks.
+
+ The TE Rules Tab also supports multiple results windows. Each
+ active window remembers the search options used for it, and will set
+ all the options accordingly when selected. Use the "Update Search"
+ button to change the results displayed for the current window based
+ on the current search option. "New Search" creates a new results
+ window based on the current search options. Use the "Close Tab" bar
+ at the bottom to destroy a results window. Also, the TE Rules tab
+ provides the means to save/load search criteria to a file (see Menus
+ section above).
+
+ Conditional Expressions tab
+ ---------------------------
+ Select the Conditional Expressions tab to search conditional
+ expressions within the policy, as well as to view the rules within
+ these conditional expressions. Note that conditional expressions
+ are displayed in Reverse Polish Notation.
+
+ By default, all conditionals are displayed; however they can be
+ limited to expressions that use particular boolean variables. The
+ current state of each rule is provided by means of a tag within the
+ results:
+
+ [Enabled] - indicates the rule is enabled
+ [Disabled] - indicates the rule is disabled
+
+ RBAC Rules tab
+ --------------
+ Select the RBAC Rules tab to search role-based access control rules.
+ It is similar in nature to the TE Rules tab, but somewhat simpler.
+ It supports searches of both role allow and role_transition rules.
+
+ As with TE Rules, the Source role can also be used in an "any"
+ search.
+
+ Range Transition Rules tab
+ --------------------------
+ Select the Range Transition Rules tab to search to search
+ range_transition rules by source and target types and by the MLS
+ range. There are three options when searching for ranges; find
+ exact matches to the entered range, find rules that have ranges
+ that contain the entered range, or find rules that have ranges
+ within the entered range.
+
+
+File Contexts tab
+-----------------
+The File Contexts tab is only available if apol has been built with
+libselinux support (see the setools INSTALL file for details on
+building apol with/without libselinux support). The tab provides the
+following features:
+
+ Creating/Loading an Index File
+ ------------------------------
+ An index file is an on-disk database that contains SELinux context
+ information about the filesystem, including SELinux users and types
+ associated with file paths and object classes. This tab provides
+ the option of creating an index file or loading an existing one. If
+ an index file is not loaded, all search items will be grayed out and
+ a red label indicating that an index file is not loaded is displayed
+ at the top. Buttons are presented for creating and loading an index
+ file. Selecting the 'Load' button displays a file selection dialog
+ for choosing saved index file to load. Selecting the 'Create and
+ Load' button will display a dialog to specify the save file and the
+ directory from which to start the indexing. Here, add multiple
+ directories from which to index by using the 'Add' button or simply
+ input a colon-delimited list of directory path strings within the
+ entrybox. Upon selecting the 'Create' button, an index file will be
+ created and then loaded into apol.
+
+ Searching an Index File
+ -----------------------
+ Searches on the index file can be done by specifying the user, type,
+ object class, or path search criteria to search for using the
+ widgets provided. Drop down lists and entryboxes are presented for
+ specifying the search criteria, of which the drop down lists contain
+ items from the index file. Regular expressions can be specified for
+ all fields except the object class field. To perform a search,
+ click the 'OK' button. Once the search is finished, list of files
+ that matched the criteria displays, along with the files' context
+ and/or object type, if specified.
+
+
+Analysis tab
+------------
+The Analysis tab provides automated analysis capabilities. The "Info"
+button provides a description for the selected analysis type. Also,
+this tab supports saving/loading any query criteria to a file (see
+Menus section above).
+
+ Domain Transition Analysis
+ --------------------------
+ Use the Domain Transition analysis module to specify a transition
+ direction for the analysis. The 2 directions provided are:
+
+ FORWARD: The Forward Domain Transition (FDT) analysis takes a
+ starting SOURCE domain and presents a tree of all the
+ resulting TARGET domains that can be transitioned into
+ from that starting domain. The tree can be walked to
+ follow the FDT tree to any depth. The only restriction
+ is that a subtree will not expand if its parent is the
+ same as the node. Each node in the FDT tree represents
+ a TARGET domain to which the parent domain can directly
+ transition.
+
+ The Forward Domain Transition (FDT) analysis also
+ provides the means to limit the query to find
+ transitions only to domains that are granted specific
+ object class permissions and/or are granted access to a
+ particular object type(s). Use the 'Access Filters'
+ dialog to select object types object classes, and
+ permissions in order to limit the query to this
+ constrained analysis. By default, all object types,
+ object classes and permissions are included in the
+ query. Selecting an object class from the listbox
+ widget will display all permissions for that object
+ class.
+
+ A specific example where this advanced feature would be
+ useful is when one is seeking to find transitions from
+ 'user_t' to domains with write access to files in the
+ 'shadow_t' domain. In this case:
+
+ - Specify 'user_t' as the source domain.
+ - Using the Access Filters dialog, select the
+ 'shadow_t' object type, 'file' object class, and
+ 'write' permission.
+
+ REVERSE: As its name implies, the Reverse Domain Transition
+ (RDT) analysis is the reverse of the FDT analysis. The
+ RDT takes a starting TARGET domain and presents a tree
+ of all the resulting SOURCE domains that can directly
+ transition to that TARGET domain. The tree can be
+ walked to follow the RDT tree to any depth. The only
+ restriction is that a subtree will not expand if its
+ parent is the same as the node. Each node in the RDT
+ tree represents a SOURCE domain that can transition to
+ its parent node. This analysis does not provide the
+ meands to constrain the query using the 'Access
+ Filters' dialog, as is possible in Forward Domain
+ Transition analysis.
+
+ Selecting a child node will show all the rules that permit the
+ transition to occur. In the case of a Forward Domain Transition
+ analysis, access granted to this target domain will also be appended
+ to the results.
+
+ See the separate help file for an overview of the criteria that
+ constitute a valid domain transition.
+
+ Direct Information Flow Analysis
+ --------------------------------
+ The Direct Information Flow (DIF) analysis takes a starting type and
+ an information flow direction (IN, OUT, EITHER, or BOTH), and
+ presents a tree with the starting type as the root node. The child
+ nodes represent other types in the policy where information flow can
+ occur DIRECTLY between its parent node and itself. If the flow
+ direction is IN, information in the child node types can flow to the
+ parent node type. If the flow direction is OUT, information in the
+ parent node can DIRECTLY flow to the child node. If the direction
+ is BOTH, information can flow from child to parent and from parent
+ to child. If EITHER is selected, flow direction will be IN, OUT, or
+ BOTH.
+
+ Selecting a child node will show all the rules that permit the
+ information flow to occur. Results are sorted by object class.
+
+ Results can be filtered by selecting one or more object classes.
+ This will ensure that only those flows that are allowed for the
+ selected object class will be shown (e.g., selecting file will
+ prevent flows allowed for sockets from being presented). Use a
+ regular expression to limit the results by end type. Only those end
+ types that match the provided regular expression will be presented.
+
+ See the separate help file on information flow for more information
+ about direct information flow.
+
+ Transitive Information Flow Analysis
+ ------------------------------------
+ Whereas the DIF analysis identifies information flows that are
+ directly allowed by one or more explicit rule, the Transitive
+ Information Flow (TIF) analysis attempts a much more extensive
+ analysis. Specifically the TIF identifies indirect paths between
+ two types. Since such paths can be circuitous or over many hops,
+ this analysis is quite difficult to achieve.
+
+ TIF takes a starting type and an information flow direction (To or
+ From) and presents a tree with the starting type as the root node.
+ The child nodes represent other types in the policy where
+ information flow can occur (directly or transitively) between the
+ parent node and itself. If the flow direction is To, the
+ information flow is to the parent node. If the flow direction is
+ From, the information flow is to the child node.
+
+ Selecting a child node shows each step in the flow chain between the
+ starting node and the child node, along with the rules that allow
+ that step to occur. Additionally, embedded in the text of the
+ results is a hyperlink for finding more flows between the starting
+ node and the selected child node. This link displays a dialog to
+ specify a time limit for the search and/or limit the number of flows
+ to find in the search.
+
+ As with the DIF analysis, results can be filtered using end type
+ regular expression.
+
+ Additionally, the TIF analysis provides the Advanced Filters dialog
+ for filtering results by object class permissions and/or types.
+ Selecting an object class in the Advanced Filters dialog will
+ display a list of permissions for that object class, whereby certain
+ permissions can be included or excluded. By default, all
+ permissions for an object class are included in the query, unless a
+ permission's 'Exclude' radiobutton is selected. Configuring all
+ permissions for an object class to be excluded will exclude the
+ object class itself from the query. When an object class becomes
+ excluded, its label will change to indicate that the object class is
+ to be excluded from the analysis query.
+
+ Additionally, the Advanced Filters dialog displays the weight value
+ of a permission, as specified in the loaded permission map. See the
+ separate help file on information flow for more information about
+ managing permission mappings. Specify a weight threshold in order
+ to exclude permissions from the results that have weights below a
+ certain threshold. Query results can also be filtered by including
+ or excluding intermediate types.
+
+ See the separate help file on information flow for more information
+ about transitive information flow.
+
+ Direct Relabel Analysis
+ -----------------------
+ See the separate help file on direct file relabel analysis, which can
+ be accessed from the help menu in apol.
+
+ Types Relationship Summary Analysis
+ -----------------------------------
+ See the separate help file on types relationship summary analysis,
+ which can be accessed from the help menu in apol.
+
+
+Policy Source tab
+-----------------
+The Policy Source tab provides a convenient display of the raw policy
+source file. If a modular policy was loaded, this tab shows only the
+base policy's source. Various search results will hyperlink to lines
+within this tab. If the loaded policy is not source then this tab
+will be disabled.
diff --git a/apol/apol_tcl.cc b/apol/apol_tcl.cc
new file mode 100644
index 0000000..0894b5c
--- /dev/null
+++ b/apol/apol_tcl.cc
@@ -0,0 +1,142 @@
+/**
+ * @file
+ *
+ * Support routines for the apol program that are faster/easier when
+ * written in C than in Tcl.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tcl.h>
+
+#include <apol/policy.h>
+#include <sefs/db.hh>
+#include <sefs/filesystem.hh>
+
+/** severity of most recent message */
+int msg_level = INT_MAX;
+
+/** pointer to most recent message string */
+char *message = NULL;
+
+/**
+ * Take the formated string, allocate space for it, and then write it
+ * the policy's msg_callback_arg. If there is already a string
+ * stored, then append to the string if the message level is equal to
+ * the previous one, overwrite the string if message level is less
+ * than previous, else ignore the message.
+ */
+static void apol_tcl_common_route(void *arg, int level, const char *fmt, va_list ap)
+{
+ char *s, *t;
+ Tcl_Interp *interp = static_cast < Tcl_Interp * >(arg);
+ if (level == APOL_MSG_INFO && msg_level >= APOL_MSG_INFO)
+ {
+ /* generate an info event */
+ free(message);
+ message = NULL;
+ if (vasprintf(&s, fmt, ap) < 0)
+ {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return;
+ }
+ message = s;
+ msg_level = level;
+ Tcl_Eval(interp, "Apol_Progress_Dialog::_update_message");
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS | TCL_DONT_WAIT)) ;
+ }
+ else if (message == NULL || level < msg_level)
+ {
+ /* overwrite the existing stored message string with a
+ * new, higher priority message */
+ free(message);
+ message = NULL;
+ if (vasprintf(&s, fmt, ap) < 0)
+ {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return;
+ }
+ message = s;
+ msg_level = level;
+ }
+ else if (level == msg_level)
+ {
+ /* append to existing error message */
+ if (vasprintf(&s, fmt, ap) < 0)
+ {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return;
+ }
+ if (asprintf(&t, "%s\n%s", message, s) < 0)
+ {
+ free(s);
+ fprintf(stderr, "%s\n", strerror(errno));
+ return;
+ }
+ free(s);
+ free(message);
+ message = t;
+ }
+}
+
+void apol_tcl_clear_info_string(void)
+{
+ if (message != NULL)
+ {
+ free(message);
+ message = NULL;
+ }
+ msg_level = INT_MAX;
+}
+
+void apol_tcl_route_apol_to_string(void *arg, const apol_policy_t * p
+ __attribute__ ((unused)), int level, const char *fmt, va_list ap)
+{
+ apol_tcl_common_route(arg, level, fmt, ap);
+}
+
+void apol_tcl_route_sefs_to_string(void *arg, const sefs_fclist * s
+ __attribute__ ((unused)), int level, const char *fmt, va_list ap)
+{
+ apol_tcl_common_route(arg, level, fmt, ap);
+}
+
+int apol_tcl_get_info_level(void)
+{
+ return msg_level;
+}
+
+char *apol_tcl_get_info_string(void)
+{
+ return message;
+}
+
+void apol_tcl_set_info_string(apol_policy_t * p, const char *s)
+{
+ INFO(p, "%s", s);
+}
diff --git a/apol/apol_tcl.i b/apol/apol_tcl.i
new file mode 100644
index 0000000..973c859
--- /dev/null
+++ b/apol/apol_tcl.i
@@ -0,0 +1,451 @@
+/**
+ * @file
+ *
+ * Support routines for the apol program that are faster/easier when
+ * written in C than in Tcl.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%module apol_tcl
+%import sefs.i
+%import apol.i
+%import qpol.i
+
+%{
+#include <config.h>
+
+#include <apol/avrule-query.h>
+#include <apol/terule-query.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <apol/util.h>
+#include <sefs/db.hh>
+#include <sefs/filesystem.hh>
+#include <sefs/fcfile.hh>
+%}
+
+%{
+/* Note that these must be placed in a different file rather than
+ * being inlined directly into this SWIG interface file. The reason
+ * is because they use some GNU functions that are only available when
+ * config.h is included prior to stdio.h. Unfortunately, SWIG will
+ * always place its own headers, which includes stdio.h, prior to any
+ * inlined headers when generating the wrapped C file. As a result,
+ * those GNU functions would not be available to the inlined
+ * functions.
+ */
+extern void apol_tcl_clear_info_string(void);
+extern int apol_tcl_get_info_level(void);
+extern char *apol_tcl_get_info_string(void);
+extern void apol_tcl_set_info_string(apol_policy_t *p, const char *s);
+extern void apol_tcl_route_apol_to_string(void *arg, const apol_policy_t * p, int level, const char *fmt, va_list ap);
+extern void apol_tcl_route_sefs_to_string(void *arg, const sefs_fclist * s, int level, const char *fmt, va_list ap);
+extern int msg_level;
+extern char *message;
+
+static void tcl_clear_error(void)
+{
+ apol_tcl_clear_info_string();
+}
+static void tcl_throw_error(const char *s)
+{
+ free(message);
+ message = strdup(s);
+}
+static char *tcl_get_error(void)
+{
+ if (msg_level != APOL_MSG_ERR) {
+ return NULL;
+ }
+ return apol_tcl_get_info_string();
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+%}
+
+/* Major hackery here to pass in the Tcl interpreter object as
+ * apol_policy_create_from_policy_path()'s callback argument. This is
+ * needed so that the callback can properly update apol's progress
+ * dialog without deadlocking itself.
+ */
+%newobject apol_tcl_open_policy(const apol_policy_path_t *, Tcl_Interp *);
+%typemap (in) (const apol_policy_path_t *ppath, Tcl_Interp *interp) {
+ int res = SWIG_ConvertPtr($input, SWIG_as_voidptrptr(&$1), $1_descriptor, 0);
+ if (res) {
+ SWIG_exception_fail(SWIG_ArgError(res), "in method '" "apol_tcl_open_policy" "', argument " "1"" of type '" "apol_policy_path_t const *""'");
+ }
+ $2 = interp;
+};
+%inline %{
+ /**
+ * Open a policy file, either source or binary, on disk. Note
+ * that this will not load neverallows; apol must rebuild
+ * neverallows (and call qpol_policy_build_syn_rule_table())
+ * when it needs to. If the file was opened successfully then
+ * allocate and return an apol_policy_t object. Otherwise
+ * throw an error and return a string that describes the
+ * error.
+ *
+ * @param ppath apol_policy_path object representing policy to
+ * open.
+ */
+ apol_policy_t *apol_tcl_open_policy(const apol_policy_path_t *ppath, Tcl_Interp *interp) {
+ apol_policy_t *p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS,
+ apol_tcl_route_apol_to_string, interp);
+ if (p == NULL && message == NULL) { // Assume lower level has generated error message
+ if (errno != 0) { // otherwise take a guess at it
+ SWIG_exception(SWIG_RuntimeError, strerror(errno));
+ } else {
+ SWIG_exception(SWIG_RuntimeError, "The selected file does not appear to be a valid SELinux Policy.");
+ }
+ }
+ fail:
+ return p;
+ }
+
+ static int avrule_sort(const void *a, const void *b, void *arg) {
+ const qpol_avrule_t *r1 = static_cast<const qpol_avrule_t *>(a);
+ const qpol_avrule_t *r2 = static_cast<const qpol_avrule_t *>(b);
+ apol_policy_t *p = static_cast<apol_policy_t *>(arg);
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+
+ uint32_t rule_type1, rule_type2;
+ const char *cs1, *cs2;
+ int compval;
+ if (qpol_avrule_get_rule_type(q, r1, &rule_type1) < 0 ||
+ qpol_avrule_get_rule_type(q, r2, &rule_type2) < 0) {
+ return 0;
+ }
+ if ((cs1 = apol_rule_type_to_str(rule_type1)) == NULL ||
+ (cs2 = apol_rule_type_to_str(rule_type2)) == NULL) {
+ return 0;
+ }
+ if ((compval = strcmp(cs1, cs2)) != 0) {
+ return compval;
+ }
+
+ const qpol_type_t *t1, *t2;
+ const char *s1, *s2;
+ if (qpol_avrule_get_source_type(q, r1, &t1) < 0 ||
+ qpol_avrule_get_source_type(q, r2, &t2) < 0) {
+ return 0;
+ }
+ if (qpol_type_get_name(q, t1, &s1) < 0 ||
+ qpol_type_get_name(q, t2, &s2) < 0) {
+ return 0;
+ }
+ if ((compval = strcmp(s1, s2)) != 0) {
+ return compval;
+ }
+
+ if (qpol_avrule_get_target_type(q, r1, &t1) < 0 ||
+ qpol_avrule_get_target_type(q, r2, &t2) < 0) {
+ return 0;
+ }
+ if (qpol_type_get_name(q, t1, &s1) < 0 ||
+ qpol_type_get_name(q, t2, &s2) < 0) {
+ return 0;
+ }
+ if ((compval = strcmp(s1, s2)) != 0) {
+ return compval;
+ }
+
+ const qpol_class_t *c1, *c2;
+ if (qpol_avrule_get_object_class(q, r1, &c1) < 0 ||
+ qpol_avrule_get_object_class(q, r2, &c2) < 0) {
+ return 0;
+ }
+ if (qpol_class_get_name(q, c1, &s1) < 0 ||
+ qpol_class_get_name(q, c2, &s2) < 0) {
+ return 0;
+ }
+ return strcmp(s1, s2);
+ }
+
+ /**
+ * Sort a vector of qpol_avrule_t, sorting by rule type, then
+ * source type, then target type, and then by object class.
+ */
+ void apol_tcl_avrule_sort(apol_policy_t *policy, apol_vector_t *v) {
+ if (policy != NULL && v != NULL) {
+ apol_vector_sort(v, avrule_sort, policy);
+ }
+ }
+
+ static int terule_sort(const void *a, const void *b, void *arg) {
+ const qpol_terule_t *r1 = static_cast<const qpol_terule_t *>(a);
+ const qpol_terule_t *r2 = static_cast<const qpol_terule_t *>(b);
+ apol_policy_t *p = static_cast<apol_policy_t *>(arg);
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+
+ uint32_t rule_type1, rule_type2;
+ const char *cs1, *cs2;
+ int compval;
+ if (qpol_terule_get_rule_type(q, r1, &rule_type1) < 0 ||
+ qpol_terule_get_rule_type(q, r2, &rule_type2) < 0) {
+ return 0;
+ }
+ if ((cs1 = apol_rule_type_to_str(rule_type1)) == NULL ||
+ (cs2 = apol_rule_type_to_str(rule_type2)) == NULL) {
+ return 0;
+ }
+ if ((compval = strcmp(cs1, cs2)) != 0) {
+ return compval;
+ }
+
+ const qpol_type_t *t1, *t2;
+ const char *s1, *s2;
+ if (qpol_terule_get_source_type(q, r1, &t1) < 0 ||
+ qpol_terule_get_source_type(q, r2, &t2) < 0) {
+ return 0;
+ }
+ if (qpol_type_get_name(q, t1, &s1) < 0 ||
+ qpol_type_get_name(q, t2, &s2) < 0) {
+ return 0;
+ }
+ if ((compval = strcmp(s1, s2)) != 0) {
+ return compval;
+ }
+
+ if (qpol_terule_get_target_type(q, r1, &t1) < 0 ||
+ qpol_terule_get_target_type(q, r2, &t2) < 0) {
+ return 0;
+ }
+ if (qpol_type_get_name(q, t1, &s1) < 0 ||
+ qpol_type_get_name(q, t2, &s2) < 0) {
+ return 0;
+ }
+ if ((compval = strcmp(s1, s2)) != 0) {
+ return compval;
+ }
+
+ const qpol_class_t *c1, *c2;
+ if (qpol_terule_get_object_class(q, r1, &c1) < 0 ||
+ qpol_terule_get_object_class(q, r2, &c2) < 0) {
+ return 0;
+ }
+ if (qpol_class_get_name(q, c1, &s1) < 0 ||
+ qpol_class_get_name(q, c2, &s2) < 0) {
+ return 0;
+ }
+ return strcmp(s1, s2);
+ }
+
+ /**
+ * Sort a vector of qpol_terule_t, sorting by rule type, then
+ * source type, then target type, and then by object class.
+ */
+ void apol_tcl_terule_sort(apol_policy_t *policy, apol_vector_t *v) {
+ if (policy != NULL && v != NULL) {
+ apol_vector_sort(v, terule_sort, policy);
+ }
+ }
+
+ /**
+ * Returns the policy version number for the currently opened
+ * policy. If the policy is modular, return the maximum
+ * allowed policy as per libsepol.
+ */
+ unsigned int apol_tcl_get_policy_version(apol_policy_t *policy) {
+ if (policy == NULL) {
+ SWIG_exception(SWIG_RuntimeError, "No policy opened");
+ }
+ if (apol_policy_get_policy_type(policy) != QPOL_POLICY_MODULE_BINARY) {
+ unsigned int version;
+ if (qpol_policy_get_policy_version(apol_policy_get_qpol(policy), &version) < 0) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get policy version");
+ }
+ return version;
+ } else {
+ return (unsigned int) SEPOL_POLICY_VERSION_MAX;
+ }
+ fail:
+ return 0;
+ }
+
+ char *apol_tcl_get_error_string(void) {
+ return tcl_get_error();
+ }
+%}
+
+%rename(apol_tcl_rule_render) apol_tcl_avrule_render;
+%rename(apol_tcl_rule_render) apol_tcl_terule_render;
+%rename(apol_tcl_rule_render) apol_tcl_syn_avrule_render;
+%rename(apol_tcl_rule_render) apol_tcl_syn_terule_render;
+
+/* Because this SWIG file will be written as C++, it expects all
+ * %newobject objects to be allocated via new and destructed with
+ * delete. However, the libapol render functions use malloc()/free()
+ * as that they come from C. Therefore, use an intermediate function
+ * to create a new string from the malloc() copy.
+ */
+%{
+ static char *apol_tcl_malloc_to_new(char *s) {
+ if (s == NULL) {
+ return new char[0];
+ }
+ char *t = new char[strlen(s) + 1];
+ strcpy(t, s);
+ free(s);
+ return t;
+ }
+ char *apol_tcl_avrule_render(apol_policy_t *policy, qpol_avrule_t *rule) {
+ return apol_tcl_malloc_to_new(apol_avrule_render(policy, rule));
+ }
+ char *apol_tcl_terule_render(apol_policy_t *policy, qpol_terule_t *rule) {
+ return apol_tcl_malloc_to_new(apol_terule_render(policy, rule));
+ }
+ char *apol_tcl_syn_avrule_render(apol_policy_t *policy, qpol_syn_avrule_t *rule) {
+ return apol_tcl_malloc_to_new(apol_syn_avrule_render(policy, rule));
+ }
+
+ char *apol_tcl_syn_terule_render(apol_policy_t *policy, qpol_syn_terule_t *rule) {
+ return apol_tcl_malloc_to_new(apol_syn_terule_render(policy, rule));
+ }
+%}
+%newobject apol_tcl_avrule_render(apol_policy_t *policy, qpol_avrule_t *rule);
+char *apol_tcl_avrule_render(apol_policy_t *policy, qpol_avrule_t *rule);
+%newobject apol_tcl_terule_render(apol_policy_t *policy, qpol_terule_t *rule);
+char *apol_tcl_terule_render(apol_policy_t *policy, qpol_terule_t *rule);
+%newobject apol_tcl_syn_avrule_render(apol_policy_t *policy, qpol_syn_avrule_t *rule);
+char *apol_tcl_syn_avrule_render(apol_policy_t *policy, qpol_syn_avrule_t *rule);
+%newobject apol_tcl_syn_terule_render(apol_policy_t *policy, qpol_syn_terule_t *rule);
+char *apol_tcl_syn_terule_render(apol_policy_t *policy, qpol_syn_terule_t *rule);
+
+
+void apol_tcl_avrule_sort(apol_policy_t *policy, apol_vector_t *v);
+void apol_tcl_terule_sort(apol_policy_t *policy, apol_vector_t *v);
+unsigned int apol_tcl_get_policy_version(apol_policy_t *policy);
+char *apol_tcl_get_error_string(void);
+
+%{
+ /**
+ * Open a sefs database from file.
+ *
+ * @param filename Database's filename.
+ */
+ sefs_db *apol_tcl_open_database(const char * filename, Tcl_Interp * interp)
+ {
+ try {
+ return new sefs_db(filename, apol_tcl_route_sefs_to_string, interp);
+ }
+ catch (...) {
+ return NULL;
+ }
+ }
+
+ /**
+ * Construct an in-memory database from part of a filesystem.
+ *
+ * @param filename Starting root directory.
+ */
+ sefs_db *apol_tcl_open_database_from_dir(const char * filename, Tcl_Interp * interp)
+ {
+ sefs_filesystem *fs = NULL;
+ sefs_db *db = NULL;
+ try {
+ fs = new sefs_filesystem(filename, apol_tcl_route_sefs_to_string, interp);
+ db = new sefs_db(fs, apol_tcl_route_sefs_to_string, interp);
+ }
+ catch (...) {
+ delete fs;
+ delete db;
+ return NULL;
+ }
+ delete fs;
+ return db;
+ }
+
+ struct apol_tcl_query_data {
+ Tcl_Interp *interp;
+ size_t matches;
+ };
+
+ static int apol_tcl_query_callback(sefs_fclist *fclist, const sefs_entry *entry, void *arg) {
+ struct apol_tcl_query_data *a = static_cast<struct apol_tcl_query_data *>(arg);
+ Tcl_Interp *interp = a->interp;
+ Tcl_Obj *cmd[2];
+ cmd[0] = Tcl_NewStringObj("Apol_File_Contexts::_search_callback", -1);
+ cmd[1] = SWIG_NewInstanceObj(SWIG_as_voidptr(entry), SWIGTYPE_p_sefs_entry, 0);
+ Tcl_EvalObjv(interp, 2, cmd, 0);
+ int retval = static_cast<int>(++(a->matches));
+ if (retval % 1000 == 0) {
+ SEFS_INFO(fclist, "Found %d results", retval);
+ }
+ return retval;
+ }
+
+ int apol_tcl_query_database(sefs_fclist *fclist, sefs_query *query, Tcl_Interp * interp)
+ {
+ struct apol_tcl_query_data a = {interp, 0};
+ int retval = fclist->runQueryMap(query, apol_tcl_query_callback, &a);
+ if (retval >= 0) {
+ tcl_clear_error();
+ }
+ return retval;
+ }
+
+ /**
+ * Include this function to force the generated SWIG wrapper
+ * to also include the code to convert from a Tcl object to a
+ * sefs_entry pointer; that code is used by
+ * apol_tcl_query_callback().
+ */
+ void apol_tcl_entry_do_nothing(sefs_entry *e) {
+ }
+%}
+
+
+/* Major hackery here to pass in the Tcl interpreter object as
+ * sefs_db's callback argument. This is needed so that the callback
+ * can properly update apol's progress dialog without deadlocking
+ * itself.
+ */
+%typemap (in) (const char * filename, Tcl_Interp *interp) {
+ $1 = Tcl_GetString($input);
+ $2 = interp;
+};
+/* More hackery to have a callback function for running queries
+ * against a sefs_db.
+ */
+%typemap (in) (sefs_query * query, Tcl_Interp *interp) {
+ int res = SWIG_ConvertPtr($input, SWIG_as_voidptrptr(&$1), $1_descriptor, 0);
+ if (res) {
+ SWIG_exception_fail(SWIG_ArgError(res), "in method '" "apol_tcl_query_database" "', argument " "1"" of type '" "sefs_query *""'");
+ }
+ $2 = interp;
+};
+sefs_db *apol_tcl_open_database(const char * filename, Tcl_Interp * interp);
+sefs_db *apol_tcl_open_database_from_dir(const char * filename, Tcl_Interp * interp);
+%newobject apol_tcl_open_database(const char*, Tcl_Interp*);
+%newobject apol_tcl_open_database_from_dir(const char*, Tcl_Interp*);
+int apol_tcl_query_database(sefs_fclist *fclist, sefs_query *query, Tcl_Interp * interp) throw (std::invalid_argument, std::runtime_error);
+void apol_tcl_entry_do_nothing(sefs_entry *e);
+
+// disable the exception handler, otherwise it will delete the error
+// message when this function gets called
+%exception;
+extern void apol_tcl_clear_info_string(void);
+extern int apol_tcl_get_info_level(void);
+extern char *apol_tcl_get_info_string(void);
+extern void apol_tcl_set_info_string(apol_policy_t *p, const char *s);
+
+// vim:ft=c noexpandtab
diff --git a/apol/classes_perms_tab.tcl b/apol/classes_perms_tab.tcl
new file mode 100644
index 0000000..ab9c4d7
--- /dev/null
+++ b/apol/classes_perms_tab.tcl
@@ -0,0 +1,484 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Class_Perms {
+ variable class_list {}
+ variable common_list {}
+ variable perms_list {}
+ variable opts
+ variable widgets
+}
+
+proc Apol_Class_Perms::create {tab_name nb} {
+ variable opts
+ variable widgets
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "Classes/Perms"]
+
+ set pw1 [PanedWindow $frame.pw -side top]
+ set left_pane [$pw1 add -weight 0]
+ set center_pane [$pw1 add -weight 1]
+ set class_pane [frame $left_pane.class]
+ set common_pane [frame $left_pane.common]
+ set perms_pane [frame $left_pane.perms]
+
+ set classes_box [TitleFrame $class_pane.tbox -text "Object Classes"]
+ set common_box [TitleFrame $common_pane.tbox -text "Common Permissions"]
+ set perms_box [TitleFrame $perms_pane.tbox -text "Permissions"]
+ set options_box [TitleFrame $center_pane.obox -text "Search Options"]
+ set results_box [TitleFrame $center_pane.rbox -text "Search Results"]
+ pack $classes_box -fill both -expand yes
+ pack $common_box -fill both -expand yes
+ pack $perms_box -fill both -expand yes
+ pack $options_box -padx 2 -fill both -expand 0
+ pack $results_box -padx 2 -fill both -expand yes
+ pack $pw1 -fill both -expand yes
+ pack $class_pane $common_pane -expand 0 -fill both
+ pack $perms_pane -expand 1 -fill both
+
+ # Object Classes listbox
+ set class_listbox [Apol_Widget::makeScrolledListbox [$classes_box getframe].lb -height 8 -width 20 -listvar Apol_Class_Perms::class_list]
+ Apol_Widget::setListboxCallbacks $class_listbox \
+ {{"Display Object Class Info" {Apol_Class_Perms::_popupInfo class}}}
+ pack $class_listbox -fill both -expand yes
+
+ # Common Permissions listbox
+ set common_listbox [Apol_Widget::makeScrolledListbox [$common_box getframe].lb -height 5 -width 20 -listvar Apol_Class_Perms::common_perms_list]
+ Apol_Widget::setListboxCallbacks $common_listbox \
+ {{"Display Common Permission Class Info" {Apol_Class_Perms::_popupInfo common}}}
+ pack $common_listbox -fill both -expand yes
+
+ # Permissions listbox
+ set perms_listbox [Apol_Widget::makeScrolledListbox [$perms_box getframe].lb -height 10 -width 20 -listvar Apol_Class_Perms::perms_list]
+ Apol_Widget::setListboxCallbacks $perms_listbox \
+ {{"Display Permission Info" {Apol_Class_Perms::_popupInfo perm}}}
+ pack $perms_listbox -fill both -expand yes
+
+ # Search options section
+ set ofm [$options_box getframe]
+ set classesfm [frame $ofm.classes]
+ set commonsfm [frame $ofm.commons]
+ set permsfm [frame $ofm.perms]
+ pack $classesfm $commonsfm $permsfm -side left -padx 4 -pady 2 -anchor ne
+
+ # First set of checkbuttons
+ set classes [checkbutton $classesfm.classes -text "Object classes" \
+ -variable Apol_Class_Perms::opts(classes:show)]
+ set perms [checkbutton $classesfm.perms -text "Include perms" \
+ -variable Apol_Class_Perms::opts(classes:perms)]
+ set commons [checkbutton $classesfm.commons -text "Expand common perms" \
+ -variable Apol_Class_Perms::opts(classes:commons)]
+ trace add variable Apol_Class_Perms::opts(classes:show) write \
+ [list Apol_Class_Perms::_toggleCheckbuttons $perms $commons]
+ trace add variable Apol_Class_Perms::opts(classes:perms) write \
+ [list Apol_Class_Perms::_toggleCheckbuttons $commons {}]
+ pack $classes -anchor w
+ pack $perms $commons -anchor w -padx 8
+
+ # Second set of checkbuttons
+ set commons [checkbutton $commonsfm.commons -text "Common permissions" \
+ -variable Apol_Class_Perms::opts(commons:show)]
+ set perms [checkbutton $commonsfm.perms2 -text "Include perms" \
+ -variable Apol_Class_Perms::opts(commons:perms) \
+ -state disabled]
+ set classes [checkbutton $commonsfm.classes -text "Object classes" \
+ -variable Apol_Class_Perms::opts(commons:classes) \
+ -state disabled]
+ trace add variable Apol_Class_Perms::opts(commons:show) write \
+ [list Apol_Class_Perms::_toggleCheckbuttons $perms $classes]
+ pack $commons -anchor w
+ pack $perms $classes -anchor w -padx 8
+
+ # Third set of checkbuttons
+ set perms [checkbutton $permsfm.prems -text "Permissions" \
+ -variable Apol_Class_Perms::opts(perms:show)]
+ set classes [checkbutton $permsfm.classes -text "Object classes" \
+ -variable Apol_Class_Perms::opts(perms:classes) \
+ -state disabled]
+ set commons [checkbutton $permsfm.commons -text "Common perms" \
+ -variable Apol_Class_Perms::opts(perms:commons) \
+ -state disabled]
+ trace add variable Apol_Class_Perms::opts(perms:show) write \
+ [list Apol_Class_Perms::_toggleCheckbuttons $classes $commons]
+ pack $perms -anchor w
+ pack $classes $commons -anchor w -padx 8
+
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry $ofm.regexp]
+
+ pack $widgets(regexp) -side left -padx 2 -pady 2 -anchor ne
+
+ set ok [button $ofm.ok -text OK -width 6 \
+ -command Apol_Class_Perms::_search]
+ pack $ok -side right -pady 5 -padx 5 -anchor ne
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$results_box getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Class_Perms::open {ppath} {
+ set q [new_apol_class_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable class_list [lsort [class_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ set q [new_apol_common_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable common_perms_list [lsort [common_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ set q [new_apol_perm_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable perms_list [lsort [str_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_Class_Perms::close {} {
+ variable class_list {}
+ variable common_perms_list {}
+ variable perms_list {}
+ variable widgets
+
+ _initializeVars
+ Apol_Widget::clearSearchResults $widgets(results)
+}
+
+proc Apol_Class_Perms::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+proc Apol_Class_Perms::getClasses {} {
+ variable class_list
+ set class_list
+}
+
+# Return a sorted list of all permissions assigned to a class. If the
+# class has a common, include the common's permissions as well.
+proc Apol_Class_Perms::getPermsForClass {class_name} {
+ set qpol_class_datum [new_qpol_class_t $::ApolTop::qpolicy $class_name]
+ set i [$qpol_class_datum get_perm_iter $::ApolTop::qpolicy]
+ set perms [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+ if {[set qpol_common_datum [$qpol_class_datum get_common $::ApolTop::qpolicy]] != "NULL"} {
+ set i [$qpol_common_datum get_perm_iter $::ApolTop::qpolicy]
+ set perms [concat $perms [iter_to_str_list $i]]
+ $i -acquire
+ $i -delete
+ }
+ lsort -dictionary -unique $perms
+}
+
+# Given a permission name, return a 2-ple of lists. The first list
+# will contain all classes that directly declare the permission. The
+# second list is a list of classes that inherited from a common that
+# declared the permission. Both lists will be sorted and uniquified
+# when returned.
+proc Apol_Class_Perms::getClassesForPerm {perm_name} {
+ set classes_list {}
+ set i [$::ApolTop::qpolicy get_class_iter $perm_name]
+ while {![$i end]} {
+ set qpol_class_datum [qpol_class_from_void [$i get_item]]
+ lappend classes_list [$qpol_class_datum get_name $::ApolTop::qpolicy]
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ set indirect_classes_list {}
+ set i [$::ApolTop::qpolicy get_common_iter $perm_name]
+ while {![$i end]} {
+ set qpol_common_datum [qpol_common_from_void [$i get_item]]
+ set q [new_apol_class_query_t]
+ $q set_common $::ApolTop::policy [$qpol_common_datum get_name $::ApolTop::qpolicy]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set indirect_classes_list [concat $indirect_classes_list [class_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ list [lsort $classes_list] [lsort -unique $indirect_classes_list]
+}
+
+#### private functions below ####
+
+proc Apol_Class_Perms::_initializeVars {} {
+ variable opts
+ array set opts {
+ classes:show 1 classes:perms 1 classes:commons 1
+ commons:show 0 commons:perms 1 commons:classes 1
+ perms:show 0 perms:classes 1 perms:commons 1
+ }
+}
+
+proc Apol_Class_Perms::_popupInfo {which name} {
+ if {$which == "class"} {
+ set text [_renderClass $name 1 0]
+ } elseif {$which == "common"} {
+ set text [_renderCommon $name 1 0]
+ } else {
+ set text [_renderPerm $name 1 1]
+ }
+ Apol_Widget::showPopupText $name $text
+}
+
+proc Apol_Class_Perms::_toggleCheckbuttons {cb1 cb2 name1 name2 op} {
+ variable opts
+ variable widgets
+ if {$opts($name2)} {
+ $cb1 configure -state normal
+ if {$name2 == "classes:show"} {
+ if {$opts(classes:perms)} {
+ $cb2 configure -state normal
+ } else {
+ $cb2 configure -state disabled
+ }
+ } elseif {$cb2 != {}} {
+ $cb2 configure -state normal
+ }
+ } else {
+ $cb1 configure -state disabled
+ if {$cb2 != {}} {
+ $cb2 configure -state disabled
+ }
+ }
+ if {!$opts(classes:show) && !$opts(commons:show) && !$opts(perms:show)} {
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 0
+ } else {
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 1
+ }
+}
+
+proc Apol_Class_Perms::_search {} {
+ variable opts
+ variable widgets
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ if {!$opts(classes:show) && !$opts(commons:show) && !$opts(perms:show)} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No search options provided."
+ return
+ }
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$use_regexp} {
+ if {$regexp == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No regular expression provided."
+ return
+ }
+ } else {
+ set regexp {}
+ }
+
+ set results {}
+
+ if {$opts(classes:show)} {
+ if {[set classes_perms $opts(classes:perms)]} {
+ set classes_commons $opts(classes:commons)
+ } else {
+ set classes_commons 0
+ }
+ set q [new_apol_class_query_t]
+ $q set_class $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set classes_data [class_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ append results "OBJECT CLASSES:\n"
+ if {$classes_data == {}} {
+ append results "Search returned no results.\n"
+ } else {
+ foreach c [lsort -index 0 $classes_data] {
+ append results [_renderClass $c $opts(classes:perms) $classes_commons]
+ }
+ }
+ }
+
+ if {$opts(commons:show)} {
+ set q [new_apol_common_query_t]
+ $q set_common $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set commons_data [common_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ append results "\nCOMMON PERMISSIONS: \n"
+ if {$commons_data == {}} {
+ append results "Search returned no results.\n"
+ } else {
+ foreach c [lsort -index 0 $commons_data] {
+ append results [_renderCommon $c $opts(commons:perms) $opts(commons:classes)]
+ }
+ }
+ }
+
+ if {$opts(perms:show)} {
+ set q [new_apol_perm_query_t]
+ $q set_perm $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set perms_data [str_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ append results "\nPERMISSIONS"
+ if {$opts(perms:classes)} {
+ append results " (* means class uses permission via a common permission)"
+ }
+ append results ":\n"
+ if {$perms_data == {}} {
+ append results "Search returned no results.\n"
+ } else {
+ foreach p [lsort -index 0 $perms_data] {
+ append results [_renderPerm $p $opts(perms:classes) $opts(perms:commons)]
+ }
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) [string trim $results]
+}
+
+proc Apol_Class_Perms::_renderClass {class_name show_perms expand_common} {
+ set qpol_class_datum [new_qpol_class_t $::ApolTop::qpolicy $class_name]
+ if {[set qpol_common_datum [$qpol_class_datum get_common $::ApolTop::qpolicy]] == "NULL"} {
+ set common_name {}
+ } else {
+ set common_name [$qpol_common_datum get_name $::ApolTop::qpolicy]
+ }
+ set text "$class_name\n"
+ if {$show_perms} {
+ set i [$qpol_class_datum get_perm_iter $::ApolTop::qpolicy]
+ set perms_list [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+ foreach perm [lsort $perms_list] {
+ append text " $perm\n"
+ }
+ if {$common_name != {}} {
+ append text " $common_name (common perm)\n"
+ if {$expand_common} {
+ set i [$qpol_common_datum get_perm_iter $::ApolTop::qpolicy]
+ foreach perm [lsort [iter_to_str_list $i]] {
+ append text " $perm\n"
+ }
+ $i -acquire
+ $i -delete
+ }
+ }
+ append text \n
+ }
+ return $text
+}
+
+proc Apol_Class_Perms::_renderCommon {common_name show_perms show_classes} {
+ set qpol_common_datum [new_qpol_common_t $::ApolTop::qpolicy $common_name]
+ set text "$common_name\n"
+ if {$show_perms} {
+ set i [$qpol_common_datum get_perm_iter $::ApolTop::qpolicy]
+ foreach perm [lsort [iter_to_str_list $i]] {
+ append text " $perm\n"
+ }
+ $i -acquire
+ $i -delete
+ }
+ if {$show_classes} {
+ append text " Object classes that use this common permission:\n"
+ set i [$::ApolTop::qpolicy get_class_iter]
+ set classes_list {}
+ while {![$i end]} {
+ set qpol_class_t [qpol_class_from_void [$i get_item]]
+ set q [$qpol_class_t get_common $::ApolTop::qpolicy]
+ if {$q != "NULL" && [$q get_name $::ApolTop::qpolicy] == $common_name} {
+ lappend classes_list [$qpol_class_t get_name $::ApolTop::qpolicy]
+ }
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ foreach class [lsort $classes_list] {
+ append text " $class\n"
+ }
+ }
+ if {$show_perms || $show_classes} {
+ append text "\n"
+ }
+ return $text
+}
+
+proc Apol_Class_Perms::_renderPerm {perm_name show_classes show_commons} {
+ set text "$perm_name\n"
+ if {$show_classes} {
+ append text " object classes:\n"
+ foreach {classes_list indirect_classes_list} [getClassesForPerm $perm_name] {break}
+ foreach c $indirect_classes_list {
+ lappend classes_list ${c}*
+ }
+ if {$classes_list == {}} {
+ append text " <none>\n"
+ } else {
+ foreach class [lsort -uniq $classes_list] {
+ append text " $class\n"
+ }
+ }
+ }
+ if {$show_commons} {
+ append text " common permissions:\n"
+ set commons_list {}
+ set i [$::ApolTop::qpolicy get_common_iter $perm_name]
+ while {![$i end]} {
+ set qpol_common_datum [qpol_common_from_void [$i get_item]]
+ lappend commons_list [$qpol_common_datum get_name $::ApolTop::qpolicy]
+ $i next
+ }
+ $i -acquire
+ $i -delete
+
+ if {$commons_list == {}} {
+ append text " <none>\n"
+ } else {
+ foreach common [lsort $commons_list] {
+ append text " $common\n"
+ }
+ }
+ }
+ if {$show_classes || $show_commons} {
+ append text "\n"
+ }
+ return $text
+}
diff --git a/apol/common_widgets.tcl b/apol/common_widgets.tcl
new file mode 100644
index 0000000..617cd70
--- /dev/null
+++ b/apol/common_widgets.tcl
@@ -0,0 +1,688 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Widget {
+ variable menuPopup {}
+ variable infoPopup {}
+ variable infoPopup2 {}
+ variable vars
+}
+
+# Create a listbox contained within a scrolled window. Whenever the
+# listbox has focus, if the user hits an alphanum key then scroll to
+# the first entry beginning with that letter. That entry is then
+# selected, with all others being cleared. Repeatedly hitting the
+# same key causes the widget to select succesive entries, wrapping
+# back to the first when at the end of the list. (This behavior
+# assumes that the listbox has been alphabetized.)
+proc Apol_Widget::makeScrolledListbox {path args} {
+ set sw [ScrolledWindow $path -scrollbar both -auto both]
+ set lb [eval listbox $sw.lb $args -bg white -highlightthickness 0]
+ $sw setwidget $lb
+
+ update
+ grid propagate $sw 0
+ bind $lb <<ListboxSelect>> [list focus $lb]
+
+ # if the user hits a letter while the listbox has focus, jump to
+ # the first entry that begins with that letter
+ bind $lb <Key> [list Apol_Widget::_listbox_key $lb %K]
+ return $sw
+}
+
+# Add callback(s) to a listbox. The callback_list is a list of
+# 2-uple entries like so:
+#
+# menu_name {function ?args?}
+#
+# The first entry is executed upon double-clicks.
+proc Apol_Widget::setListboxCallbacks {path callback_list} {
+ set lb [getScrolledListbox $path]
+
+ # add double-click on an item to immediately do something
+ bind $lb <Double-Button-1> [eval list Apol_Widget::_listbox_double_click $lb [lindex $callback_list 0 1]]
+
+ # enable right-clicks on listbox to popup a menu; that menu lets
+ # the user see more info
+ set lb [getScrolledListbox $path]
+ bind $lb <Button-3> [list Apol_Widget::_listbox_popup %W %x %y $callback_list $lb]
+}
+
+proc Apol_Widget::getScrolledListbox {path} {
+ return $path.lb
+}
+
+proc Apol_Widget::setScrolledListboxState {path newState} {
+ if {$newState == 0 || $newState == "disabled"} {
+ $path.lb configure -state disabled
+ } else {
+ $path.lb configure -state normal
+ }
+}
+
+# Create combobox from which the user may choose a type. Then create
+# a combobox from which the user may select an attribute; this
+# attribute filters the allowable types.
+proc Apol_Widget::makeTypeCombobox {path args} {
+ variable vars
+ array unset vars $path:*
+ set vars($path:type) ""
+ set vars($path:attribenable) 0
+ set vars($path:attrib) ""
+
+ set f [frame $path]
+ set type_box [eval ComboBox $f.tb -helptext {{Type or select a type}} \
+ -textvariable Apol_Widget::vars($path:type) \
+ -entrybg white -width 20 -autopost 1 $args]
+ pack $type_box -side top -expand 1 -fill x
+
+ set attrib_width [expr {[$type_box cget -width] - 4}]
+ set attrib_enable [checkbutton $f.ae \
+ -anchor w -text "Filter by attribute"\
+ -variable Apol_Widget::vars($path:attribenable) \
+ -command [list Apol_Widget::_attrib_enabled $path]]
+ set attrib_box [ComboBox $f.ab -autopost 1 -entrybg white -width $attrib_width \
+ -textvariable Apol_Widget::vars($path:attrib)]
+ trace add variable Apol_Widget::vars($path:attrib) write [list Apol_Widget::_attrib_changed $path]
+ pack $attrib_enable -side top -expand 0 -fill x -anchor sw -padx 5 -pady 2
+ pack $attrib_box -side top -expand 1 -fill x -padx 9
+ _attrib_enabled $path
+ return $f
+}
+
+proc Apol_Widget::resetTypeComboboxToPolicy {path} {
+ $path.tb configure -values [Apol_Types::getTypes]
+ $path.ab configure -values [Apol_Types::getAttributes]
+}
+
+proc Apol_Widget::clearTypeCombobox {path} {
+ variable vars
+ set vars($path:attribenable) 0
+ set vars($path:attrib) ""
+ set vars($path:type) ""
+ $path.tb configure -values {}
+ $path.ab configure -values {}
+ _attrib_enabled $path
+}
+
+# Return the currently selected type. If an attribute is acting as a
+# filter, the return value will instead be a 2-ple list of the
+# selected type and the selected attribute.
+proc Apol_Widget::getTypeComboboxValueAndAttrib {path} {
+ variable vars
+ if {$vars($path:attribenable)} {
+ list [string trim $vars($path:type)] $vars($path:attrib)
+ } else {
+ string trim $vars($path:type)
+ }
+}
+
+# Set the type and possibly attribute for a type combobox. The first
+# element of $type is the type to set. If $type has more than one
+# element, then the second element is the attribute upon which to
+# filter.
+proc Apol_Widget::setTypeComboboxValue {path type} {
+ variable vars
+ if {[llength $type] <= 1} {
+ set vars($path:type) $type
+ set vars($path:attribenable) 0
+ set vars($path:attrib) ""
+ } else {
+ set vars($path:type) [lindex $type 0]
+ set vars($path:attribenable) 1
+ set vars($path:attrib) [lindex $type 1]
+ }
+ _attrib_enabled $path
+}
+
+proc Apol_Widget::setTypeComboboxState {path newState} {
+ variable vars
+ if {$newState == 0 || $newState == "disabled"} {
+ $path.tb configure -state disabled
+ $path.ae configure -state disabled
+ $path.ab configure -state disabled
+ } else {
+ $path.tb configure -state normal
+ $path.ae configure -state normal
+ if {$vars($path:attribenable)} {
+ $path.ab configure -state normal
+ }
+ }
+}
+
+
+# Create a mega-widget used to select a single MLS level (a
+# sensitivity + 0 or more categories).
+#
+# @param catSize Number of categories to show in the dropdown box.
+proc Apol_Widget::makeLevelSelector {path catSize args} {
+ variable vars
+ array unset vars $path:*
+ set vars($path:sens) {}
+ set vars($path:cats) {}
+
+ set f [frame $path]
+ set sens_box [eval ComboBox $f.sens $args \
+ -textvariable Apol_Widget::vars($path:sens) \
+ -entrybg white -width 16 -autopost 1]
+ trace add variable Apol_Widget::vars($path:sens) write [list Apol_Widget::_sens_changed $path]
+ pack $sens_box -side top -expand 0 -fill x
+
+ set cats_label [label $f.cl -text "Categories:"]
+ pack $cats_label -side top -anchor sw -pady 2 -expand 0
+
+ set cats [makeScrolledListbox $f.cats -width 16 -height $catSize \
+ -listvariable Apol_Widget::vars($path:cats) \
+ -selectmode extended -exportselection 0]
+ pack $cats -side top -expand 1 -fill both
+
+ set reset [button $f.reset -text "Clear Categories" \
+ -command [list [getScrolledListbox $cats] selection clear 0 end]]
+ pack $reset -side top -anchor center -pady 2
+ return $f
+}
+
+# Return an apol_mls_level_t object that represents the level
+# selected. The caller must delete it afterwards.
+proc Apol_Widget::getLevelSelectorLevel {path} {
+ variable vars
+ set apol_level [new_apol_mls_level_t]
+ # convert sensitivity aliases to its real name, if necessary
+ set l [Apol_MLS::isSensInPolicy $vars($path:sens)]
+ if {[ApolTop::is_policy_open]} {
+ set p $::ApolTop::policy
+ } else {
+ set p NULL
+ }
+ if {$l == {}} {
+ $apol_level set_sens $p $vars($path:sens)
+ } else {
+ $apol_level set_sens $p $l
+ }
+ set sl [getScrolledListbox $path.cats]
+ set cats {}
+ foreach idx [$sl curselection] {
+ $apol_level append_cats $p [$sl get $idx]
+ }
+ return $apol_level
+}
+
+# Given an apol_mls_level_t object, set the level selector's display
+# to match the level.
+proc Apol_Widget::setLevelSelectorLevel {path level} {
+ variable vars
+ if {$level == "NULL"} {
+ set sens {}
+ } else {
+ set sens [$level get_sens]
+ }
+ set sens_list [$path.sens cget -values]
+ if {$sens != {} && [lsearch -exact $sens_list $sens] != -1} {
+ set vars($path:sens) $sens
+ set cats_list $vars($path:cats)
+ set first_idx -1
+ set listbox [getScrolledListbox $path.cats]
+ set cats [str_vector_to_list [$level get_cats]]
+ foreach cat $cats {
+ if {[set idx [lsearch -exact $cats_list $cat]] != -1} {
+ $listbox selection set $idx
+ if {$first_idx == -1 || $idx < $first_idx} {
+ set first_idx $idx
+ }
+ }
+ }
+ # scroll the listbox so that the first one selected is visible
+ # near the top
+ incr first_idx -1
+ $listbox yview scroll $first_idx units
+ }
+}
+
+proc Apol_Widget::resetLevelSelectorToPolicy {path} {
+ variable vars
+ set vars($path:sens) {}
+
+ if {![ApolTop::is_policy_open]} {
+ $path.sens configure -values {}
+ } else {
+ set level_data {}
+ set i [$::ApolTop::qpolicy get_level_iter]
+ while {![$i end]} {
+ set qpol_level_datum [qpol_level_from_void [$i get_item]]
+ if {![$qpol_level_datum get_isalias $::ApolTop::qpolicy]} {
+ set level_name [$qpol_level_datum get_name $::ApolTop::qpolicy]
+ set level_value [$qpol_level_datum get_value $::ApolTop::qpolicy]
+ lappend level_data [list $level_name $level_value]
+ }
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ set level_names {}
+ foreach l [lsort -integer -index 1 $level_data] {
+ lappend level_names [lindex $l 0]
+ }
+ $path.sens configure -values $level_names
+ }
+}
+
+proc Apol_Widget::clearLevelSelector {path} {
+ variable vars
+ set vars($path:sens) {}
+ $path.sens configure -values {}
+ # the category box will be cleared because of the trace on $path:sens
+}
+
+proc Apol_Widget::setLevelSelectorState {path newState} {
+ if {$newState == 0 || $newState == "disabled"} {
+ set newState disabled
+ } else {
+ set newState normal
+ }
+ $path.sens configure -state $newState
+ $path.cl configure -state $newState
+ $path.reset configure -state $newState
+ setScrolledListboxState $path.cats $newState
+}
+
+# Create a common "search using regular expression" checkbutton + entry.
+proc Apol_Widget::makeRegexpEntry {path args} {
+ variable vars
+ array unset vars $path:*
+ set vars($path:enable_regexp) 0
+
+ set f [frame $path]
+ set cb [checkbutton $f.cb -text "Search using regular expression" \
+ -variable Apol_Widget::vars($path:enable_regexp)]
+ set regexp [eval entry $f.entry $args \
+ -textvariable Apol_Widget::vars($path:regexp) \
+ -width 32 -state disabled -bg $ApolTop::default_bg_color]
+ trace add variable Apol_Widget::vars($path:enable_regexp) write \
+ [list Apol_Widget::_toggle_regexp_check_button $regexp]
+ pack $cb -side top -anchor nw
+ pack $regexp -side top -padx 4 -anchor nw -expand 0 -fill x
+ return $f
+}
+
+proc Apol_Widget::setRegexpEntryState {path newState} {
+ variable vars
+ if {$newState == 0 || $newState == "disabled"} {
+ set vars($path:enable_regexp) 0
+ $path.cb configure -state disabled
+ } else {
+ $path.cb configure -state normal
+ }
+}
+
+proc Apol_Widget::setRegexpEntryValue {path newState newValue} {
+ variable vars
+ set old_state [$path.cb cget -state]
+ set vars($path:enable_regexp) $newState
+ set vars($path:regexp) $newValue
+ $path.cb configure -state $old_state
+}
+
+proc Apol_Widget::getRegexpEntryState {path} {
+ return $Apol_Widget::vars($path:enable_regexp)
+}
+
+proc Apol_Widget::getRegexpEntryValue {path} {
+ return $Apol_Widget::vars($path:regexp)
+}
+
+# Create a scrolled non-editable text widget, from which search
+# results may be displayed.
+proc Apol_Widget::makeSearchResults {path args} {
+ variable vars
+ array unset vars $path:*
+ set sw [ScrolledWindow $path -scrollbar both -auto both]
+ set tb [eval text $sw.tb $args -bg white -wrap none -state disabled -font $ApolTop::text_font]
+ set vars($path:cursor) [$tb cget -cursor]
+ bind $tb <Button-3> [list Apol_Widget::_searchresults_popup %W %x %y]
+ $tb tag configure linenum -foreground blue -underline 1
+ $tb tag configure selected -foreground red -underline 1
+ $tb tag configure enabled -foreground green -underline 1
+ $tb tag configure disabled -foreground red -underline 1
+ $tb tag bind linenum <Button-1> [list Apol_Widget::_hyperlink $path %x %y]
+ $tb tag bind linenum <Enter> [list $tb configure -cursor hand2]
+ $tb tag bind linenum <Leave> [list $tb configure -cursor $Apol_Widget::vars($path:cursor)]
+ $sw setwidget $tb
+ return $sw
+}
+
+proc Apol_Widget::clearSearchResults {path} {
+ $path.tb configure -state normal
+ $path.tb delete 0.0 end
+ $path.tb configure -state disabled
+}
+
+proc Apol_Widget::copySearchResults {path} {
+ if {[$path tag ranges sel] != {}} {
+ set data [$path get sel.first sel.last]
+ clipboard clear
+ clipboard append -- $data
+ }
+}
+
+proc Apol_Widget::selectAllSearchResults {path} {
+ $path tag add sel 1.0 end
+}
+
+proc Apol_Widget::appendSearchResultHeader {path header} {
+ $path.tb configure -state normal
+ $path.tb insert 1.0 "$header\n"
+ $path.tb configure -state disabled
+}
+
+proc Apol_Widget::appendSearchResultText {path text} {
+ $path.tb configure -state normal
+ $path.tb insert end $text
+ $path.tb configure -state disabled
+}
+
+# Append a vector of qpol_avrule_t or qpol_terule_t to a search
+# results box. Sort the rules by string representation. Returns the
+# number of rules that were appended, number of enabled rules, and
+# number of disabled rules.
+#
+# @param cast SWIG casting function, one of "new_qpol_avrule_t" or
+# "new_qpol_terule_t"
+proc Apol_Widget::appendSearchResultRules {path indent rule_list cast} {
+ set curstate [$path.tb cget -state]
+ $path.tb configure -state normal
+
+ set num_enabled 0
+ set num_disabled 0
+
+ for {set i 0} {$i < [$rule_list get_size]} {incr i} {
+ set rule [$cast [$rule_list get_element $i]]
+ $path.tb insert end [string repeat " " $indent]
+ $path.tb insert end [apol_tcl_rule_render $::ApolTop::policy $rule]
+ if {[$rule get_cond $::ApolTop::qpolicy] != "NULL"} {
+ if {[$rule get_is_enabled $::ApolTop::qpolicy]} {
+ $path.tb insert end " \[" {} "Enabled" enabled "\]"
+ incr num_enabled
+ } else {
+ $path.tb insert end " \[" {} "Disabled" disabled "\]"
+ incr num_disabled
+ }
+ }
+ $path.tb insert end "\n"
+ }
+ $path.tb configure -state $curstate
+ list [$rule_list get_size] $num_enabled $num_disabled
+}
+
+# Append a vector of qpol_syn_avrule_t or qpol_syn_terule_t to a
+# search results box. Returns the number of rules that were appended,
+# number of enabled rules, and number of disabled rules.
+#
+# @param cast SWIG casting function, one of "new_qpol_syn_avrule_t" or
+# "new_qpol_syn_terule_t"
+proc Apol_Widget::appendSearchResultSynRules {path indent rule_list cast} {
+ set curstate [$path.tb cget -state]
+ $path.tb configure -state normal
+
+ set num_enabled 0
+ set num_disabled 0
+ if {[ApolTop::is_capable "line numbers"]} {
+ set do_linenums 1
+ } else {
+ set do_linenums 0
+ }
+
+ for {set i 0} {$i < [$rule_list get_size]} {incr i} {
+ set syn_rule [$cast [$rule_list get_element $i]]
+ $path.tb insert end [string repeat " " $indent]
+ if {$do_linenums} {
+ $path.tb insert end \
+ "\[" {} \
+ [$syn_rule get_lineno $::ApolTop::qpolicy] linenum \
+ "\] " {}
+ }
+ $path.tb insert end [apol_tcl_rule_render $::ApolTop::policy $syn_rule]
+ if {[$syn_rule get_cond $::ApolTop::qpolicy] != "NULL"} {
+ if {[$syn_rule get_is_enabled $::ApolTop::qpolicy]} {
+ $path.tb insert end " \[" {} "Enabled" enabled "\]"
+ incr num_enabled
+ } else {
+ $path.tb insert end " \[" {} "Disabled" disabled "\]"
+ incr num_disabled
+ }
+ }
+ $path.tb insert end "\n"
+ }
+ $path.tb configure -state $curstate
+ list [$rule_list get_size] $num_enabled $num_disabled
+}
+
+proc Apol_Widget::showPopupText {title info} {
+ variable infoPopup
+ if {![winfo exists $infoPopup]} {
+ set infoPopup [toplevel .apol_widget_info_popup]
+ wm withdraw $infoPopup
+ set sw [ScrolledWindow $infoPopup.sw -scrollbar both -auto horizontal]
+ set text [text [$sw getframe].text -font {helvetica 10} -wrap none -width 35 -height 10]
+ $sw setwidget $text
+ pack $sw -expand 1 -fill both
+ set b [button $infoPopup.close -text "Close" -command [list destroy $infoPopup]]
+ pack $b -side bottom -expand 0 -pady 5
+ wm geometry $infoPopup 250x200+50+50
+ update
+ grid propagate $sw 0
+ }
+ wm title $infoPopup $title
+ set text [$infoPopup.sw getframe].text
+ $text configure -state normal
+ $text delete 1.0 end
+ $text insert 0.0 $info
+ $text configure -state disabled
+ wm deiconify $infoPopup
+ raise $infoPopup
+}
+
+# Used to show pre-rendered paragraphs of text.
+proc Apol_Widget::showPopupParagraph {title info} {
+ variable infoPopup2
+ if {![winfo exists $infoPopup2]} {
+ set infoPopup2 [Dialog .apol_widget_info_popup2 -modal none -parent . \
+ -transient false -cancel 0 -default 0 -separator 1]
+ $infoPopup2 add -text "Close" -command [list destroy $infoPopup2]
+ set sw [ScrolledWindow [$infoPopup2 getframe].sw -auto both -scrollbar both]
+ $sw configure -relief sunken
+ set text [text [$sw getframe].text -font $ApolTop::text_font \
+ -wrap none -width 75 -height 25 -bg white]
+ $sw setwidget $text
+ update
+ grid propagate $sw 0
+ pack $sw -expand 1 -fill both -padx 4 -pady 4
+ $infoPopup2 draw
+ } else {
+ raise $infoPopup2
+ wm deiconify $infoPopup2
+ }
+ $infoPopup2 configure -title $title
+ set text [[$infoPopup2 getframe].sw getframe].text
+ $text configure -state normal
+ $text delete 1.0 end
+ $text insert 0.0 $info
+ $text configure -state disabled
+}
+
+########## private functions below ##########
+
+proc Apol_Widget::_listbox_key {listbox key} {
+ if {[string length $key] == 1} {
+ # only scroll with non-function keys
+ set values [set ::[$listbox cget -listvar]]
+ set x [lsearch $values $key*]
+ if {$x >= 0} {
+ # if the current value already begins with that letter,
+ # cycle to the next one, wrapping back to the first value
+ # as necessary
+ set curvalue [$listbox get active]
+ set curindex [$listbox curselection]
+ if {$curindex != "" && [string index $curvalue 0] == $key} {
+ set new_x [expr {$curindex + 1}]
+ if {[string index [lindex $values $new_x] 0] != $key} {
+ # wrap around
+ set new_x $x
+ }
+ } else {
+ set new_x $x
+ }
+
+ $listbox selection clear 0 end
+ $listbox selection set $new_x
+ $listbox activate $new_x
+ $listbox see $new_x
+ }
+ event generate $listbox <<ListboxSelect>>
+ }
+}
+
+proc Apol_Widget::_listbox_double_click {listbox callback_func args} {
+ eval $callback_func $args [$listbox get active]
+}
+
+proc Apol_Widget::_listbox_popup {w x y callbacks lb} {
+ focus $lb
+ set selected_item [$lb get active]
+ if {$selected_item == {}} {
+ return
+ }
+
+ # create a global popup menu widget if one does not already exist
+ variable menuPopup
+ if {![winfo exists $menuPopup]} {
+ set menuPopup [menu .apol_widget_menu_popup -tearoff 0]
+ }
+
+ ApolTop::popup $w $x $y $menuPopup $callbacks $selected_item
+}
+
+proc Apol_Widget::_attrib_enabled {path} {
+ variable vars
+ if {$vars($path:attribenable)} {
+ $path.ab configure -state normal
+ _filter_type_combobox $path $vars($path:attrib)
+ } else {
+ $path.ab configure -state disabled
+ _filter_type_combobox $path ""
+ }
+}
+
+proc Apol_Widget::_attrib_changed {path name1 name2 op} {
+ variable vars
+ if {$vars($path:attribenable)} {
+ _filter_type_combobox $path $vars($name2)
+ }
+}
+
+proc Apol_Widget::_attrib_validate {path} {
+ # check that the attribute given was valid
+}
+
+proc Apol_Widget::_filter_type_combobox {path attribvalue} {
+ variable vars
+ if {$attribvalue != {}} {
+ set typesList {}
+ if {[Apol_Types::isAttributeInPolicy $attribvalue]} {
+ set qpol_type_datum [new_qpol_type_t $::ApolTop::qpolicy $attribvalue]
+ set i [$qpol_type_datum get_type_iter $::ApolTop::qpolicy]
+ foreach t [iter_to_list $i] {
+ set t [qpol_type_from_void $t]
+ lappend typesList [$t get_name $::ApolTop::qpolicy]
+ }
+ $i -acquire
+ $i -delete
+ }
+ if {$typesList == {}} {
+ # unknown attribute, so don't change type combobox
+ return
+ }
+ } else {
+ set typesList [Apol_Types::getTypes]
+ # during policy load this list should already have been sorted
+ }
+ if {[lsearch -exact $typesList $vars($path:type)] == -1} {
+ set vars($path:type) {}
+ }
+ $path.tb configure -values [lsort $typesList]
+}
+
+proc Apol_Widget::_sens_changed {path name1 name2 op} {
+ variable vars
+ # get a list of categories associated with this sensitivity
+ [getScrolledListbox $path.cats] selection clear 0 end
+ set vars($path:cats) {}
+ set sens [Apol_MLS::isSensInPolicy $vars($path:sens)]
+ if {$sens != {}} {
+ # the given level exists within the given policy
+ set qpol_level_datum [new_qpol_level_t $::ApolTop::qpolicy $sens]
+ set i [$qpol_level_datum get_cat_iter $::ApolTop::qpolicy]
+ while {![$i end]} {
+ set qpol_cat_datum [qpol_cat_from_void [$i get_item]]
+ lappend vars($path:cats) [$qpol_cat_datum get_name $::ApolTop::qpolicy]
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ }
+}
+
+proc Apol_Widget::_toggle_regexp_check_button {path name1 name2 op} {
+ if {$Apol_Widget::vars($name2)} {
+ $path configure -state normal -bg white
+ } else {
+ $path configure -state disabled -bg $ApolTop::default_bg_color
+ }
+}
+
+proc Apol_Widget::_searchresults_popup {path x y} {
+ if {[ApolTop::is_policy_open]} {
+ focus $path
+ # create a global popup menu widget if one does not already exist
+ variable menuPopup
+ if {![winfo exists $menuPopup]} {
+ set menuPopup [menu .apol_widget_menu_popup -tearoff 0]
+ }
+ set callbacks {
+ {"Copy" Apol_Widget::copySearchResults}
+ {"Select All" Apol_Widget::selectAllSearchResults}
+ }
+ ApolTop::popup $path $x $y $menuPopup $callbacks $path
+ }
+}
+
+proc Apol_Widget::_hyperlink {path x y} {
+ set tb $path.tb
+ set range [$tb tag prevrange linenum "@$x,$y + 1 char"]
+ $tb tag add selected [lindex $range 0] [lindex $range 1]
+ set line_num [$tb get [lindex $range 0] [lindex $range 1]]
+ ApolTop::showPolicySourceLineNumber $line_num
+}
+
+proc Apol_Widget::_render_typeset {typeset} {
+ if {[llength $typeset] > 1} {
+ if {[lindex $typeset 0] == "~"} {
+ set typeset "~\{[lrange $typeset 1 end]\}"
+ } else {
+ set typeset "\{$typeset\}"
+ }
+ } else {
+ set typeset
+ }
+}
diff --git a/apol/cond_bools_tab.tcl b/apol/cond_bools_tab.tcl
new file mode 100644
index 0000000..76356f1
--- /dev/null
+++ b/apol/cond_bools_tab.tcl
@@ -0,0 +1,266 @@
+# Copyright (C) 2004-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Cond_Bools {
+ variable cond_bools_list {}
+ variable cond_bools_defaults
+ variable cond_bools_values
+ variable opts
+ variable widgets
+}
+
+proc Apol_Cond_Bools::create {tab_name nb} {
+ variable opts
+ variable widgets
+
+ _initializeVars
+
+ # Layout frames
+ set frame [$nb insert end $tab_name -text "Booleans"]
+ set pw [PanedWindow $frame.pw -side top]
+ set left_pane [$pw add -weight 0]
+ set right_pane [$pw add -weight 1]
+ pack $pw -expand 1 -fill both
+
+ # Title frames
+ set cond_bools_box [TitleFrame $left_pane.cond_bools_box -text "Booleans"]
+ set s_optionsbox [TitleFrame $right_pane.obox -text "Search Options"]
+ set rslts_frame [TitleFrame $right_pane.rbox -text "Search Results"]
+ pack $cond_bools_box -expand 1 -fill both
+ pack $s_optionsbox -padx 2 -fill x -expand 0
+ pack $rslts_frame -padx 2 -fill both -expand yes
+
+ # Booleans listbox widget
+ set left_frame [$cond_bools_box getframe]
+ set sw_b [ScrolledWindow $left_frame.sw -auto both]
+ set widgets(listbox) [ScrollableFrame $sw_b.listbox -bg white -width 200]
+ $sw_b setwidget $widgets(listbox)
+ set button_defaults [button $left_frame.button_defaults \
+ -text "Reset to Policy Defaults" \
+ -command Apol_Cond_Bools::_resetAll]
+ pack $sw_b -side top -expand 1 -fill both
+ pack $button_defaults -side bottom -pady 2 -expand 0 -fill x
+
+ # Search options subframes
+ set ofm [$s_optionsbox getframe]
+ set bool_frame [frame $ofm.bool]
+ set show_frame [frame $ofm.show]
+ pack $bool_frame $show_frame -side left -padx 4 -pady 2 -anchor nw
+
+ set enable [checkbutton $bool_frame.enable \
+ -variable Apol_Cond_Bools::opts(enable_bool) \
+ -text "Boolean"]
+ set widgets(combo_box) [ComboBox $bool_frame.combo_box \
+ -textvariable Apol_Cond_Bools::opts(name) \
+ -helptext "Type or select a boolean variable" \
+ -state disabled -entrybg white -autopost 1]
+ set widgets(regexp) [checkbutton $bool_frame.regexp \
+ -text "Search using regular expression" \
+ -state disabled \
+ -variable Apol_Cond_Bools::opts(use_regexp)]
+ trace add variable Apol_Cond_Bools::opts(enable_bool) write \
+ [list Apol_Cond_Bools::_toggleSearchBools]
+ pack $enable -anchor w
+ pack $widgets(combo_box) $widgets(regexp) -padx 4 -anchor nw -expand 0 -fill x
+
+ set show_default [checkbutton $show_frame.show_default \
+ -variable Apol_Cond_Bools::opts(show_default) \
+ -text "Show default state"]
+ set show_current [checkbutton $show_frame.show_current \
+ -variable Apol_Cond_Bools::opts(show_current) \
+ -text "Show current state"]
+ pack $show_default $show_current -anchor w
+
+ # Action Buttons
+ set ok_button [button $ofm.ok -text "OK" -width 6 \
+ -command Apol_Cond_Bools::_search]
+ pack $ok_button -side right -anchor ne -padx 5 -pady 5
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$rslts_frame getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Cond_Bools::open {ppath} {
+ set q [new_apol_bool_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable cond_bools_list [lsort [bool_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ variable cond_bools_defaults
+ foreach bool $cond_bools_list {
+ set b [new_qpol_bool_t $::ApolTop::qpolicy $bool]
+ set cond_bools_defaults($bool) [$b get_state $::ApolTop::qpolicy]
+ _insert_listbox_item $bool $cond_bools_defaults($bool)
+ }
+
+ variable widgets
+ $widgets(listbox) xview moveto 0
+ $widgets(listbox) yview moveto 0
+ $widgets(listbox) configure -areaheight 0 -areawidth 0
+ $widgets(combo_box) configure -values $cond_bools_list
+}
+
+proc Apol_Cond_Bools::close {} {
+ variable widgets
+ variable cond_bools_list {}
+ variable cond_bools_defaults
+ variable cond_bools_values
+
+ _initializeVars
+ $widgets(combo_box) configure -values {}
+ # clean up bools listbox, then hide its scrollbars
+ foreach w [winfo children [$widgets(listbox) getframe]] {
+ destroy $w
+ }
+ [$widgets(listbox) getframe] configure -width 1 -height 1
+ Apol_Widget::clearSearchResults $widgets(results)
+ array unset cond_bools_defaults
+ array unset cond_bools_values
+}
+
+proc Apol_Cond_Bools::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+# Return a list of names of all conditional booleans within the
+# policy. If no policy is opened then return an empty list.
+proc Apol_Cond_Bools::getBooleans {} {
+ variable cond_bools_list
+ set cond_bools_list
+}
+
+#### private functions below ####
+
+proc Apol_Cond_Bools::_initializeVars {} {
+ variable opts
+ array set opts {
+ enable_bool 0
+ name ""
+ use_regexp 0
+
+ show_default 1
+ show_current 1
+ }
+}
+
+proc Apol_Cond_Bools::_insert_listbox_item {bool initial_state} {
+ variable widgets
+ variable cond_bools_values
+
+ set cond_bools_values($bool) $initial_state
+ set subf [$widgets(listbox) getframe]
+ set rb_true [radiobutton $subf.t:$bool -bg white \
+ -variable Apol_Cond_Bools::cond_bools_values($bool) \
+ -value 1 -highlightthickness 0 -text "True"]
+ set rb_false [radiobutton $subf.f:$bool -bg white \
+ -variable Apol_Cond_Bools::cond_bools_values($bool) \
+ -value 0 -highlightthickness 0 -text "False"]
+ trace add variable Apol_Cond_Bools::cond_bools_values($bool) write \
+ [list Apol_Cond_Bools::_set_bool_value]
+ set rb_label [label $subf.l:$bool -bg white -text "- $bool"]
+ grid $rb_true $rb_false $rb_label -padx 2 -pady 5 -sticky w
+}
+
+proc Apol_Cond_Bools::_toggleSearchBools {name1 name2 op} {
+ variable opts
+ variable widgets
+ if {$opts(enable_bool)} {
+ $widgets(combo_box) configure -state normal
+ $widgets(regexp) configure -state normal
+ } else {
+ $widgets(combo_box) configure -state disabled
+ $widgets(regexp) configure -state disabled
+ }
+}
+
+proc Apol_Cond_Bools::_set_bool_value {name1 name2 op} {
+ variable cond_bools_values
+ set qpol_bool_datum [new_qpol_bool_t $::ApolTop::qpolicy $name2]
+ $qpol_bool_datum set_state $::ApolTop::qpolicy $cond_bools_values($name2)
+}
+
+proc Apol_Cond_Bools::_resetAll {} {
+ variable cond_bools_defaults
+ variable cond_bools_values
+
+ # hopefully each of the traces associated with each boolean
+ # triggers, causing the policy to be updated
+ array set cond_bools_values [array get cond_bools_defaults]
+}
+
+proc Apol_Cond_Bools::_search {} {
+ variable opts
+ variable widgets
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ set name [string trim $opts(name)]
+ if {$opts(enable_bool) && $name == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No boolean variable provided."
+ return
+ }
+
+ set q [new_apol_bool_query_t]
+ $q set_bool $::ApolTop::policy $name
+ $q set_regex $::ApolTop::policy $opts(use_regexp)
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set bools_data [bool_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set results {}
+ set results "BOOLEANS:\n"
+ if {[llength $bools_data] == 0} {
+ append results "Search returned no results."
+ } else {
+ foreach b [lsort $bools_data] {
+ append results "\n[_renderBool $b $opts(show_default) $opts(show_current)]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
+
+proc Apol_Cond_Bools::_renderBool {bool_name show_default show_current} {
+ variable cond_bools_defaults
+ set qpol_bool_datum [new_qpol_bool_t $::ApolTop::qpolicy $bool_name]
+ set cur_state [$qpol_bool_datum get_state $::ApolTop::qpolicy]
+ set text [format "%-28s" $bool_name]
+ if {$show_default} {
+ if {$cond_bools_defaults($bool_name)} {
+ append text " Default State: True "
+ } else {
+ append text " Default State: False"
+ }
+ }
+ if {$show_current} {
+ if {$cur_state} {
+ append text " Current State: True "
+ } else {
+ append text " Current State: False"
+ }
+ }
+ return $text
+}
diff --git a/apol/cond_rules_tab.tcl b/apol/cond_rules_tab.tcl
new file mode 100644
index 0000000..f0ab23d
--- /dev/null
+++ b/apol/cond_rules_tab.tcl
@@ -0,0 +1,281 @@
+# Copyright (C) 2004-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Cond_Rules {
+ variable vals
+ variable widgets
+}
+
+proc Apol_Cond_Rules::create {tab_name nb} {
+ variable vals
+ variable widgets
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "Conditional Expressions"]
+ set topf [frame $frame.top]
+ set bottomf [frame $frame.bottom]
+ pack $topf -expand 0 -fill both -pady 2
+ pack $bottomf -expand 1 -fill both -pady 2
+
+ set rules_box [TitleFrame $topf.rules_box -text "Rule Selection"]
+ set obox [TitleFrame $topf.obox -text "Search Options"]
+ set dbox [TitleFrame $bottomf.dbox -text "Conditional Expressions Display"]
+ pack $rules_box -side left -expand 0 -fill both -padx 2
+ pack $obox -side left -expand 1 -fill both -padx 2
+ pack $dbox -expand 1 -fill both -padx 2
+
+ # Rule selection subframe
+ set fm_rules [$rules_box getframe]
+ set allow [checkbutton $fm_rules.allow -text "allow" \
+ -onvalue $::QPOL_RULE_ALLOW -offvalue 0 \
+ -variable Apol_Cond_Rules::vals(rs:avrule_allow)]
+ set auditallow [checkbutton $fm_rules.auditallow -text "auditallow" \
+ -onvalue $::QPOL_RULE_AUDITALLOW -offvalue 0 \
+ -variable Apol_Cond_Rules::vals(rs:avrule_auditallow)]
+ set dontaudit [checkbutton $fm_rules.dontaudit -text "dontaudit" \
+ -onvalue $::QPOL_RULE_DONTAUDIT -offvalue 0 \
+ -variable Apol_Cond_Rules::vals(rs:avrule_dontaudit)]
+ set type_transition [checkbutton $fm_rules.type_transition -text "type_trans" \
+ -onvalue $::QPOL_RULE_TYPE_TRANS -offvalue 0 \
+ -variable Apol_Cond_Rules::vals(rs:type_transition)]
+ set type_member [checkbutton $fm_rules.type_member -text "type_member" \
+ -onvalue $::QPOL_RULE_TYPE_MEMBER -offvalue 0 \
+ -variable Apol_Cond_Rules::vals(rs:type_member)]
+ set type_change [checkbutton $fm_rules.type_change -text "type_change" \
+ -onvalue $::QPOL_RULE_TYPE_CHANGE -offvalue 0 \
+ -variable Apol_Cond_Rules::vals(rs:type_change)]
+ grid $allow $type_transition -sticky w -padx 2
+ grid $auditallow $type_member -sticky w -padx 2
+ grid $dontaudit $type_change -sticky w -padx 2
+
+ # Search options subframes
+ set ofm [$obox getframe]
+ set bool_frame [frame $ofm.bool]
+ pack $bool_frame -side left -padx 4 -pady 2 -anchor nw
+ set enable [checkbutton $bool_frame.enable \
+ -variable Apol_Cond_Rules::vals(enable_bool) \
+ -text "Boolean"]
+ set widgets(combo_box) [ComboBox $bool_frame.combo_box \
+ -textvariable Apol_Cond_Rules::vals(name) \
+ -helptext "Type or select a boolean variable" \
+ -state disabled -entrybg white -autopost 1]
+ set widgets(regexp) [checkbutton $bool_frame.regexp \
+ -text "Search using regular expression" \
+ -state disabled \
+ -variable Apol_Cond_Rules::vals(use_regexp)]
+ trace add variable Apol_Cond_Rules::vals(enable_bool) write \
+ [list Apol_Cond_Rules::_toggleSearchBools]
+ pack $enable -anchor w
+ pack $widgets(combo_box) $widgets(regexp) -padx 4 -anchor nw -expand 0 -fill x
+
+ set ok_button [button $ofm.ok -text OK -width 6 \
+ -command Apol_Cond_Rules::_search]
+ pack $ok_button -side right -anchor ne -padx 5 -pady 5
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$dbox getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Cond_Rules::open {ppath} {
+ variable widgets
+ $widgets(combo_box) configure -values [Apol_Cond_Bools::getBooleans]
+}
+
+proc Apol_Cond_Rules::close {} {
+ variable widgets
+
+ _initializeVars
+ $widgets(combo_box) configure -values {}
+ Apol_Widget::clearSearchResults $widgets(results)
+}
+
+proc Apol_Cond_Rules::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+#### private functions below ####
+
+proc Apol_Cond_Rules::_initializeVars {} {
+ variable vals
+ array set vals [list \
+ rs:avrule_allow $::QPOL_RULE_ALLOW \
+ rs:avrule_auditallow $::QPOL_RULE_AUDITALLOW \
+ rs:avrule_dontaudit $::QPOL_RULE_DONTAUDIT \
+ rs:type_transition $::QPOL_RULE_TYPE_TRANS \
+ rs:type_member $::QPOL_RULE_TYPE_MEMBER \
+ rs:type_change $::QPOL_RULE_TYPE_CHANGE \
+ enable_bool 0 \
+ name {} \
+ use_regexp 0]
+}
+
+proc Apol_Cond_Rules::_toggleSearchBools {name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals(enable_bool)} {
+ $widgets(combo_box) configure -state normal
+ $widgets(regexp) configure -state normal
+ } else {
+ $widgets(combo_box) configure -state disabled
+ $widgets(regexp) configure -state disabled
+ }
+}
+
+proc Apol_Cond_Rules::_search {} {
+ variable vals
+ variable widgets
+ .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state disabled
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
+ return
+ }
+
+ set avrule_selection 0
+ foreach {key value} [array get vals rs:avrule_*] {
+ set avrule_selection [expr {$avrule_selection | $value}]
+ }
+ set terule_selection 0
+ foreach {key value} [array get vals rs:type_*] {
+ set terule_selection [expr {$terule_selection | $value}]
+ }
+ if {$avrule_selection == 0 && $terule_selection == 0} {
+ tk_messageBox -icon error -type ok -title "Error" -message "At least one rule must be selected."
+ .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
+ return
+ }
+
+ set bool_name {}
+ if {$vals(enable_bool)} {
+ if {[set bool_name $vals(name)] == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No booleean selected."
+ .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
+ return
+ }
+ }
+
+ set q [new_apol_cond_query_t]
+ $q set_bool $::ApolTop::policy $bool_name
+ if {$vals(use_regexp)} {
+ $q set_regex $::ApolTop::policy 1
+ }
+
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set results [cond_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ if {[llength $results] == 0} {
+ set text "Search returned no results."
+ } else {
+ set text "[llength $results] conditional"
+ if {[llength $results] != 1} {
+ append text s
+ }
+ append text " match the search criteria. Expressions are in Reverse Polish Notation.\n\n"
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $text
+ if {![info exists apol_progress]} {
+ Apol_Progress_Dialog::wait "Conditional Expressions" "Rendering conditionals" \
+ {
+ if {[ApolTop::is_capable "syntactic rules"]} {
+ $::ApolTop::qpolicy build_syn_rule_table
+ }
+ set counter 1
+ set num_results [llength $results]
+ foreach r [lsort -index 0 $results] {
+ apol_tcl_set_info_string $::ApolTop::policy "Rendering $counter of $num_results"
+ set text [_renderConditional $r $avrule_selection $terule_selection $counter]
+ Apol_Widget::appendSearchResultText $widgets(results) "$text\n\n"
+ incr counter
+ }
+ }
+ }
+ .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
+}
+
+proc Apol_Cond_Rules::_renderConditional {cond avrules terules cond_number} {
+ set cond_expr [apol_cond_expr_render $::ApolTop::policy $cond]
+ set i [$cond get_av_true_iter $::ApolTop::qpolicy $avrules]
+ set av_true_vector [new_apol_vector_t $i]
+ $i -acquire
+ $i -delete
+ set i [$cond get_av_false_iter $::ApolTop::qpolicy $avrules]
+ set av_false_vector [new_apol_vector_t $i]
+ $i -acquire
+ $i -delete
+ set i [$cond get_te_true_iter $::ApolTop::qpolicy $terules]
+ set te_true_vector [new_apol_vector_t $i]
+ $i -acquire
+ $i -delete
+ set i [$cond get_te_false_iter $::ApolTop::qpolicy $terules]
+ set te_false_vector [new_apol_vector_t $i]
+ $i -acquire
+ $i -delete
+
+ variable widgets
+ set text "conditional expression $cond_number: \[ [join $cond_expr] \]\n"
+
+ Apol_Widget::appendSearchResultText $widgets(results) "$text\nTRUE list:\n"
+ if {![ApolTop::is_capable "syntactic rules"]} {
+ apol_tcl_avrule_sort $::ApolTop::policy $av_true_vector
+ Apol_Widget::appendSearchResultRules $widgets(results) 4 $av_true_vector qpol_avrule_from_void
+ apol_tcl_terule_sort $::ApolTop::policy $te_true_vector
+ Apol_Widget::appendSearchResultRules $widgets(results) 4 $te_true_vector qpol_terule_from_void
+ } else {
+ set syn_avrules [apol_avrule_list_to_syn_avrules $::ApolTop::policy $av_true_vector NULL]
+ Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_avrules qpol_syn_avrule_from_void
+ set syn_terules [apol_terule_list_to_syn_terules $::ApolTop::policy $te_true_vector]
+ Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_terules qpol_syn_terule_from_void
+ $syn_avrules -acquire
+ $syn_avrules -delete
+ $syn_terules -acquire
+ $syn_terules -delete
+ }
+
+ Apol_Widget::appendSearchResultText $widgets(results) "\nFALSE list:\n"
+ if {![ApolTop::is_capable "syntactic rules"]} {
+ apol_tcl_avrule_sort $::ApolTop::policy $av_false_vector
+ Apol_Widget::appendSearchResultRules $widgets(results) 4 $av_false_vector qpol_avrule_from_void
+ apol_tcl_terule_sort $::ApolTop::policy $te_false_vector
+ Apol_Widget::appendSearchResultRules $widgets(results) 4 $te_false_vector qpol_terule_from_void
+ } else {
+ set syn_avrules [apol_avrule_list_to_syn_avrules $::ApolTop::policy $av_false_vector NULL]
+ Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_avrules qpol_syn_avrule_from_void
+ set syn_terules [apol_terule_list_to_syn_terules $::ApolTop::policy $te_false_vector]
+ Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_terules qpol_syn_terule_from_void
+ $syn_avrules -acquire
+ $syn_avrules -delete
+ $syn_terules -acquire
+ $syn_terules -delete
+ }
+
+ $av_true_vector -acquire
+ $av_true_vector -delete
+ $av_false_vector -acquire
+ $av_false_vector -delete
+ $te_true_vector -acquire
+ $te_true_vector -delete
+ $te_false_vector -acquire
+ $te_false_vector -delete
+}
diff --git a/apol/context_dialog.tcl b/apol/context_dialog.tcl
new file mode 100644
index 0000000..2bc63bc
--- /dev/null
+++ b/apol/context_dialog.tcl
@@ -0,0 +1,340 @@
+# Copyright (C) 2005-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Context_Dialog {
+ variable dialog ""
+ variable vars
+}
+
+# Create a dialog box to allow the user to select a single context
+# (user + role + type + level [if MLS]). This will return a 2-ple
+# list. The first is an apol_context_t; the caller must delete this
+# afterwards. Note that the context may be partially filled. The
+# second element is the attribute used to filter types; it may be an
+# empty string to indicate no filtering. If the dialog is cancelled
+# then return an empty list.
+proc Apol_Context_Dialog::getContext {{defaultContext {}} {defaultAttribute {}} {parent .}} {
+ variable dialog
+ variable vars
+
+ if {![winfo exists $dialog]} {
+ _create_dialog $parent
+ }
+
+ set user {}
+ set role {}
+ set type {}
+ set low_level {}
+ set high_level {}
+
+ # initialize widget states
+ array set vars [list $dialog:low_enable 0 $dialog:high_enable 0]
+ if {$defaultContext != {}} {
+ set user [$defaultContext get_user] #line causing segfault. most likely the entire $defaultContext doesn't exist
+ set role [$defaultContext get_role]
+ set type [$defaultContext get_type]
+ if {$defaultAttribute != {}} {
+ lappend type $defaultAttribute
+ }
+ set range [$defaultContext get_range]
+ if {$range != "NULL"} {
+ set low_level [$range get_low]
+ set high_level [$range get_high]
+ }
+ }
+
+ $vars($dialog:user_box) configure -values [Apol_Users::getUsers]
+ set vars($dialog:user) $user
+ if {$user == {}} {
+ set vars($dialog:user_enable) 0
+ } else {
+ set vars($dialog:user_enable) 1
+ }
+
+
+
+ $vars($dialog:role_box) configure -values [Apol_Roles::getRoles]
+ set vars($dialog:role) $role
+ if {$role == {}} {
+ set vars($dialog:role_enable) 0
+ } else {
+ set vars($dialog:role_enable) 1
+ }
+
+ Apol_Widget::resetTypeComboboxToPolicy $vars($dialog:type_box)
+ Apol_Widget::setTypeComboboxValue $vars($dialog:type_box) $type
+ if {$type == {}} {
+ set vars($dialog:type_enable) 0
+ } else {
+ set vars($dialog:type_enable) 1
+ }
+
+ Apol_Widget::resetLevelSelectorToPolicy $vars($dialog:low_level)
+ Apol_Widget::resetLevelSelectorToPolicy $vars($dialog:high_level)
+ if {[ApolTop::is_policy_open] && [ApolTop::is_capable "mls"]} {
+ if {$low_level != {}} {
+ set vars($dialog:low_enable) 1
+ Apol_Widget::setLevelSelectorLevel $vars($dialog:low_level) $low_level
+ }
+ if {$high_level != {} && $high_level != "NULL"} {
+ set vars($dialog:low_enable) 1
+ set vars($dialog:high_enable) 1
+ Apol_Widget::setLevelSelectorLevel $vars($dialog:high_level) $high_level
+ }
+ $vars($dialog:low_cb) configure -state normal
+ } else {
+ set vars($dialog:low_enable) 0
+ set vars($dialog:high_enable) 0
+ $vars($dialog:low_cb) configure -state disabled
+ }
+
+ # force a recomputation of button sizes (bug in ButtonBox)
+ $dialog.bbox _redraw
+ set retval [$dialog draw]
+ if {$retval == -1 || $retval == 1} {
+ return {}
+ }
+ set context [_get_context $dialog]
+ set attribute [lindex [Apol_Widget::getTypeComboboxValueAndAttrib $vars($dialog:type_box)] 1]
+ list $context $attribute
+}
+
+
+########## private functions below ##########
+
+proc Apol_Context_Dialog::_create_dialog {parent} {
+ variable dialog
+ variable vars
+
+ set dialog [Dialog .context_dialog -modal local -parent $parent \
+ -separator 1 -homogeneous 1 -title "Select Context"]
+ array unset vars $dialog:*
+
+
+ set f [$dialog getframe]
+ set left_f [frame $f.left]
+
+ set user_f [frame $left_f.user]
+ set vars($dialog:user_cb) [checkbutton $user_f.enable -text "User" \
+ -variable Apol_Context_Dialog::vars($dialog:user_enable)]
+ set vars($dialog:user_box) [ComboBox $user_f.user -entrybg white \
+ -width 12 \
+ -textvariable Apol_Context_Dialog::vars($dialog:user) \
+ -autopost 1]
+ trace add variable Apol_Context_Dialog::vars($dialog:user_enable) write \
+ [list Apol_Context_Dialog::_user_changed $dialog]
+ pack $vars($dialog:user_cb) -anchor nw
+ pack $vars($dialog:user_box) -anchor nw -padx 4 -expand 0 -fill x
+
+ set role_f [frame $left_f.role]
+ set vars($dialog:role_cb) [checkbutton $role_f.enable -text "Role" \
+ -variable Apol_Context_Dialog::vars($dialog:role_enable)]
+ set vars($dialog:role_box) [ComboBox $role_f.role -entrybg white -width 12 \
+ -textvariable Apol_Context_Dialog::vars($dialog:role) -autopost 1]
+ trace add variable Apol_Context_Dialog::vars($dialog:role_enable) write \
+ [list Apol_Context_Dialog::_role_changed $dialog]
+ pack $vars($dialog:role_cb) -anchor nw
+ pack $vars($dialog:role_box) -anchor nw -padx 4 -expand 0 -fill x
+
+ set type_f [frame $left_f.type]
+ set vars($dialog:type_cb) [checkbutton $type_f.enable -text "Type" \
+ -variable Apol_Context_Dialog::vars($dialog:type_enable)]
+ set vars($dialog:type_box) [Apol_Widget::makeTypeCombobox $type_f.type]
+ pack $vars($dialog:type_cb) -anchor nw
+ pack $vars($dialog:type_box) -anchor nw -padx 4 -expand 0 -fill x
+ trace add variable Apol_Context_Dialog::vars($dialog:type_enable) write \
+ [list Apol_Context_Dialog::_type_changed $dialog]
+ pack $user_f $role_f $type_f -side top -expand 1 -fill x
+
+ set mlsbox [TitleFrame $f.mlsbox -text "MLS Range"]
+ set mls_f [$mlsbox getframe]
+ set vars($dialog:low_cb) [checkbutton $mls_f.low_cb -text "Single Level" \
+ -variable Apol_Context_Dialog::vars($dialog:low_enable)]
+ set vars($dialog:low_level) [Apol_Widget::makeLevelSelector $mls_f.low 8]
+ trace add variable Apol_Context_Dialog::vars($dialog:low_enable) write \
+ [list Apol_Context_Dialog::_low_changed $dialog]
+ set vars($dialog:high_cb) [checkbutton $mls_f.high_cb \
+ -text "High Level" \
+ -variable Apol_Context_Dialog::vars($dialog:high_enable)]
+ set vars($dialog:high_level) [Apol_Widget::makeLevelSelector $mls_f.high 8]
+ trace add variable Apol_Context_Dialog::vars($dialog:high_enable) write \
+ [list Apol_Context_Dialog::_high_changed $dialog]
+ grid $vars($dialog:low_cb) $vars($dialog:high_cb) -sticky w
+ grid $vars($dialog:low_level) $vars($dialog:high_level) -sticky nsew
+ grid columnconfigure $mls_f 0 -weight 1 -uniform 1 -pad 2
+ grid columnconfigure $mls_f 1 -weight 1 -uniform 1 -pad 2
+ grid rowconfigure $mls_f 1 -weight 1
+
+ pack $left_f $mlsbox -side left -expand 1 -fill both
+
+ $dialog add -text "OK" -command [list Apol_Context_Dialog::_okay $dialog]
+ $dialog add -text "Cancel"
+}
+
+# For all options that have been enabled, ensure that the user also
+# selected a value. With those values, ensure that they are
+# authorized (user has the role, etc). For the MLS range, also check
+# that the level is legal by constructing a 'range' with it (as both
+# the low and high level).
+proc Apol_Context_Dialog::_okay {dialog} {
+ variable vars
+ set context [new_apol_context_t]
+ if {[ApolTop::is_policy_open]} {
+ set p $::ApolTop::policy
+ } else {
+ set p NULL
+ }
+
+ if {$vars($dialog:user_enable)} {
+ if {[set user $vars($dialog:user)] == {}} {
+ tk_messageBox -icon error -type ok -title "Could Not Validate Context" \
+ -message "No user was selected."
+ return
+ }
+ $context set_user $p $user
+ }
+ if {$vars($dialog:role_enable)} {
+ if {[set role $vars($dialog:role)] == {}} {
+ tk_messageBox -icon error -type ok -title "Could Not Validate Context" \
+ -message "No role was selected."
+ return
+ }
+ $context set_role $p $role
+ }
+ if {$vars($dialog:type_enable)} {
+ set type [lindex [Apol_Widget::getTypeComboboxValueAndAttrib $vars($dialog:type_box)] 0]
+ if {$type == {}} {
+ tk_messageBox -icon error -type ok -title "Could Not Validate Context" \
+ -message "No type was selected."
+ return
+ }
+ $context set_type $p $type
+ }
+ if {$vars($dialog:low_enable)} {
+ set range [_get_range $dialog]
+ if {$range == {}} {
+ tk_messageBox -icon error -type ok -title "Could Not Validate Context" \
+ -message "No level was selected."
+ return
+ }
+ $context set_range $p $range
+ }
+ if {![ApolTop::is_policy_open] || [$context validate_partial $p] <= 0} {
+ tk_messageBox -icon error -type ok -title "Could Not Validate Context" \
+ -message "The selected context is not valid for the current policy."
+ return
+ } else {
+ $dialog enddialog 0
+ }
+ $context -acquire
+ $context -delete
+}
+
+proc Apol_Context_Dialog::_get_context {dialog} {
+ variable vars
+ set context [new_apol_context_t]
+ if {[ApolTop::is_policy_open]} {
+ set p $::ApolTop::policy
+ } else {
+ set p NULL
+ }
+ if {$vars($dialog:user_enable)} {
+ $context set_user $p $vars($dialog:user)
+ }
+ if {$vars($dialog:role_enable)} {
+ $context set_role $p $vars($dialog:role)
+ }
+ if {$vars($dialog:type_enable)} {
+ set type [lindex [Apol_Widget::getTypeComboboxValueAndAttrib $vars($dialog:type_box)] 0]
+ $context set_type $p $type
+ }
+ set range [_get_range $dialog]
+ if {$range != {}} {
+ $context set_range $p $range
+ }
+ return $context
+}
+
+proc Apol_Context_Dialog::_get_range {dialog} {
+ variable vars
+ if {!$vars($dialog:low_enable)} {
+ return {}
+ }
+ if {[ApolTop::is_policy_open]} {
+ set p $::ApolTop::policy
+ } else {
+ set p NULL
+ }
+ set range [new_apol_mls_range_t]
+ $range set_low $p [Apol_Widget::getLevelSelectorLevel $vars($dialog:low_level)]
+
+ if {$vars($dialog:high_enable)} {
+ $range set_high $p [Apol_Widget::getLevelSelectorLevel $vars($dialog:high_level)]
+ }
+ return $range
+}
+
+proc Apol_Context_Dialog::_user_changed {dialog name1 name2 op} {
+ variable vars
+ if {$vars($dialog:user_enable)} {
+ $vars($dialog:user_box) configure -state normal
+ } else {
+ $vars($dialog:user_box) configure -state disabled
+ }
+}
+
+proc Apol_Context_Dialog::_role_changed {dialog name1 name2 op} {
+ variable vars
+ if {$vars($dialog:role_enable)} {
+ $vars($dialog:role_box) configure -state normal
+ } else {
+ $vars($dialog:role_box) configure -state disabled
+ }
+}
+
+proc Apol_Context_Dialog::_type_changed {dialog name1 name2 op} {
+ variable vars
+ if {$vars($dialog:type_enable)} {
+ Apol_Widget::setTypeComboboxState $vars($dialog:type_box) 1
+ } else {
+ Apol_Widget::setTypeComboboxState $vars($dialog:type_box) 0
+ }
+}
+
+proc Apol_Context_Dialog::_low_changed {dialog name1 name2 op} {
+ variable vars
+ if {$vars($dialog:low_enable)} {
+ $vars($dialog:high_cb) configure -state normal
+ Apol_Widget::setLevelSelectorState $vars($dialog:low_level) 1
+ if {$vars($dialog:high_enable)} {
+ Apol_Widget::setLevelSelectorState $vars($dialog:high_level) 1
+ }
+ } else {
+ $vars($dialog:high_cb) configure -state disabled
+ Apol_Widget::setLevelSelectorState $vars($dialog:low_level) 0
+ Apol_Widget::setLevelSelectorState $vars($dialog:high_level) 0
+ }
+}
+
+proc Apol_Context_Dialog::_high_changed {dialog name1 name2 op} {
+ variable vars
+ if {$vars($dialog:high_enable)} {
+ $vars($dialog:low_cb) configure -text "Low Level"
+ Apol_Widget::setLevelSelectorState $vars($dialog:high_level) 1
+ } else {
+ $vars($dialog:low_cb) configure -text "Single Level"
+ Apol_Widget::setLevelSelectorState $vars($dialog:high_level) 0
+ }
+}
diff --git a/apol/context_selector.tcl b/apol/context_selector.tcl
new file mode 100644
index 0000000..d2b7e2a
--- /dev/null
+++ b/apol/context_selector.tcl
@@ -0,0 +1,150 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Widget {
+ variable vars
+}
+
+# Creates a widget that lets the user select a context (user + role +
+# type + MLS range) and a range search type (exact, subset, superset).
+# If the second argument is not "" then add a checkbutton that
+# enables/disables the entire widget.
+proc Apol_Widget::makeContextSelector {path rangeMatchText {enableText "Context"} args} {
+ variable vars
+ array unset vars $path:*
+ set vars($path:context) {}
+ set vars($path:attribute) {}
+ set vars($path:context_rendered) {}
+ set vars($path:search_type) $::APOL_QUERY_EXACT
+
+ set f [frame $path]
+ set context_frame [frame $f.context]
+ set context2_frame [frame $f.context2]
+ pack $context_frame $context2_frame -side left -expand 0 -anchor nw
+
+ if {$enableText != {}} {
+ set vars($path:enable) 0
+ set context_cb [checkbutton $context_frame.enable -text $enableText \
+ -variable Apol_Widget::vars($path:enable)]
+ pack $context_cb -side top -expand 0 -anchor nw
+ trace add variable Apol_Widget::vars($path:enable) write [list Apol_Widget::_toggle_context_selector $path $context_cb]
+ }
+ set context_display [eval Entry $context_frame.display -textvariable Apol_Widget::vars($path:context_rendered) -width 26 -editable 0 $args]
+ set context_button [button $context_frame.button -text "Select Context..." -state disabled -command [list Apol_Widget::_show_context_dialog $path]]
+ trace add variable Apol_Widget::vars($path:context) write [list Apol_Widget::_update_context_display $path]
+ set vars($path:context) {} ;# this will invoke the display function
+ pack $context_display -side top -expand 1 -fill x -anchor nw
+ pack $context_button -side top -expand 0 -anchor ne
+ if {$enableText != {}} {
+ pack configure $context_display -padx 4
+ pack configure $context_button -padx 4
+ }
+
+ # range search type
+ set range_label [label $context2_frame.label -text "MLS range matching:" \
+ -state disabled]
+ set range_exact [radiobutton $context2_frame.exact -text "Exact matches" \
+ -state disabled -value $::APOL_QUERY_EXACT \
+ -variable Apol_Widget::vars($path:search_type)]
+ set range_subset [radiobutton $context2_frame.subset -text "$rangeMatchText containing range" \
+ -state disabled -value $::APOL_QUERY_SUB \
+ -variable Apol_Widget::vars($path:search_type)]
+ set range_superset [radiobutton $context2_frame.superset -text "$rangeMatchText within range" \
+ -state disabled -value $::APOL_QUERY_SUPER \
+ -variable Apol_Widget::vars($path:search_type)]
+ pack $range_label $range_exact $range_subset $range_superset \
+ -side top -expand 0 -anchor nw
+
+ return $f
+}
+
+proc Apol_Widget::setContextSelectorState {path newState} {
+ if {$newState == 0 || $newState == "disabled"} {
+ set new_state disabled
+ } else {
+ set new_state normal
+ }
+ foreach w {display button} {
+ $path.context.$w configure -state $new_state
+ }
+ if {![ApolTop::is_capable "mls"]} {
+ set new_state disabled
+ }
+ foreach w {label exact subset superset} {
+ $path.context2.$w configure -state $new_state
+ }
+}
+
+proc Apol_Widget::clearContextSelector {path} {
+ set Apol_Widget::vars($path:context) {}
+ set Apol_Widget::vars($path:attribute) {}
+ set Apol_Widget::vars($path:search_type) $::APOL_QUERY_EXACT
+ catch {set Apol_Widget::vars($path:enable) 0}
+}
+
+proc Apol_Widget::getContextSelectorState {path} {
+ return $Apol_Widget::vars($path:enable)
+}
+
+# Return the currently selected context and other stuff. This will be
+# a 3-ple list of:
+# <ol>
+# <li>The (possibly partial) context, an apol_context_t. The caller
+# must delete this afterwards.
+# <li>The MLS range search type, one of $::APOL_QUERY_EXACT or its like.
+# <li>If not an empty string, the attribute used to filter types.
+# </ol>
+proc Apol_Widget::getContextSelectorValue {path} {
+ variable vars
+ list $vars($path:context) $vars($path:search_type) $vars($path:attribute)
+}
+
+########## private functions below ##########
+
+proc Apol_Widget::_toggle_context_selector {path cb name1 name2 op} {
+ if {$Apol_Widget::vars($path:enable)} {
+ Apol_Widget::setContextSelectorState $path normal
+ } else {
+ Apol_Widget::setContextSelectorState $path disabled
+ }
+}
+
+proc Apol_Widget::_show_context_dialog {path} {
+ variable vars
+ $path.context.button configure -state disabled
+ set new_context [Apol_Context_Dialog::getContext $vars($path:context) $vars($path:attribute)]
+ if {$new_context != {}} {
+ set vars($path:context) [lindex $new_context 0]
+ set vars($path:attribute) [lindex $new_context 1]
+ }
+
+ $path.context.button configure -state normal
+ # the trace on this variable will trigger [_update_context_display] to execute
+}
+
+proc Apol_Widget::_update_context_display {path name1 name2 op} {
+ variable vars
+ set display $path.context.display
+ if {$vars($path:context) == {}} {
+ set context_str "*:*:*"
+ if {[ApolTop::is_policy_open] && [ApolTop::is_capable "mls"]} {
+ append context_str ":*"
+ }
+ } else {
+ set context_str [$vars($path:context) render $::ApolTop::policy]
+ }
+ set vars($path:context_rendered) $context_str
+ $display configure -helptext $vars($path:context_rendered)
+}
diff --git a/apol/directflow_module.tcl b/apol/directflow_module.tcl
new file mode 100644
index 0000000..61055ec
--- /dev/null
+++ b/apol/directflow_module.tcl
@@ -0,0 +1,582 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Analysis_directflow {
+ variable vals
+ variable widgets
+ Apol_Analysis::registerAnalysis "Apol_Analysis_directflow" "Direct Information Flow"
+}
+
+proc Apol_Analysis_directflow::create {options_frame} {
+ variable vals
+ variable widgets
+
+ _reinitializeVals
+
+ set dir_tf [TitleFrame $options_frame.mode -text "Direction"]
+ pack $dir_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set dir_in [radiobutton [$dir_tf getframe].in -text In \
+ -value $::APOL_INFOFLOW_IN \
+ -variable Apol_Analysis_directflow::vals(dir)]
+ set dir_out [radiobutton [$dir_tf getframe].out -text Out \
+ -value $::APOL_INFOFLOW_OUT \
+ -variable Apol_Analysis_directflow::vals(dir)]
+ set dir_either [radiobutton [$dir_tf getframe].either -text Either \
+ -value $::APOL_INFOFLOW_EITHER \
+ -variable Apol_Analysis_directflow::vals(dir)]
+ set dir_both [radiobutton [$dir_tf getframe].both -text Both \
+ -value $::APOL_INFOFLOW_BOTH \
+ -variable Apol_Analysis_directflow::vals(dir)]
+ pack $dir_in $dir_out $dir_either $dir_both -anchor w
+
+ set req_tf [TitleFrame $options_frame.req -text "Required Parameters"]
+ pack $req_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set l [label [$req_tf getframe].l -text "Starting type"]
+ pack $l -anchor w
+ set widgets(type) [Apol_Widget::makeTypeCombobox [$req_tf getframe].type]
+ pack $widgets(type)
+
+ set filter_tf [TitleFrame $options_frame.filter -text "Optional Result Filters"]
+ pack $filter_tf -side left -padx 2 -pady 2 -expand 1 -fill both
+ set class_f [frame [$filter_tf getframe].class]
+ pack $class_f -side left -anchor nw
+ set class_enable [checkbutton $class_f.enable -text "Filter by object class" \
+ -variable Apol_Analysis_directflow::vals(classes:enable)]
+ pack $class_enable -anchor w
+ set widgets(classes) [Apol_Widget::makeScrolledListbox $class_f.classes \
+ -height 6 -width 24 \
+ -listvar Apol_Analysis_directflow::vals(classes:all_classes) \
+ -selectmode multiple -exportselection 0]
+ set classes_lb [Apol_Widget::getScrolledListbox $widgets(classes)]
+ bind $classes_lb <<ListboxSelect>> \
+ [list Apol_Analysis_directflow::_selectClassesListbox $classes_lb]
+ pack $widgets(classes) -padx 4 -expand 0 -fill both
+ trace add variable Apol_Analysis_directflow::vals(classes:enable) write \
+ Apol_Analysis_directflow::_toggleClasses
+ Apol_Widget::setScrolledListboxState $widgets(classes) disabled
+ set classes_bb [ButtonBox $class_f.bb -homogeneous 1 -spacing 4]
+ $classes_bb add -text "Include All" \
+ -command [list Apol_Analysis_directflow::_includeAll $classes_lb]
+ $classes_bb add -text "Exclude All" \
+ -command [list Apol_Analysis_directflow::_excludeAll $classes_lb]
+ pack $classes_bb -pady 4
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry [$filter_tf getframe].end]
+ $widgets(regexp).cb configure -text "Filter result types using regular expression"
+ pack $widgets(regexp) -side left -anchor nw -padx 8
+}
+
+proc Apol_Analysis_directflow::open {} {
+ variable vals
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(type)
+ set vals(classes:all_classes) [Apol_Class_Perms::getClasses]
+ set vals(classes:selected) $vals(classes:all_classes)
+ Apol_Widget::setScrolledListboxState $widgets(classes) normal
+ set classes_lb [Apol_Widget::getScrolledListbox $widgets(classes)]
+ $classes_lb selection set 0 end
+ _toggleClasses {} {} {}
+}
+
+proc Apol_Analysis_directflow::close {} {
+ variable widgets
+ _reinitializeVals
+ _reinitializeWidgets
+ Apol_Widget::clearTypeCombobox $widgets(type)
+}
+
+proc Apol_Analysis_directflow::getInfo {} {
+ return "This analysis generates the results of a Direct Information Flow
+analysis beginning from the starting type selected. The results of
+the analysis are presented in tree form with the root of the tree
+being the start point for the analysis.
+
+\nEach child node in the tree represents a type in the current policy
+for which there is a direct information flow to or from its parent
+node. If 'in' was selected then the information flow is from the
+child to the parent. If 'out' was selected then information flows
+from the parent to the child.
+
+\nThe results of the analysis may be optionally filtered by object class
+selection or an end type regular expression.
+
+\nNOTE: For any given generation, if the parent and the child are the
+same, the child cannot be opened. This avoids cyclic analyses.
+
+\nFor additional help on this topic select \"Information Flow Analysis\"
+from the help menu."
+}
+
+proc Apol_Analysis_directflow::newAnalysis {} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ set f [_createResultsDisplay]
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_directflow::updateAnalysis {f} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ _clearResultsDisplay $f
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_directflow::reset {} {
+ _reinitializeVals
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_directflow::switchTab {query_options} {
+ variable vals
+ variable widgets
+ array set vals $query_options
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_directflow::saveQuery {channel} {
+ variable vals
+ variable widgets
+ foreach {key value} [array get vals] {
+ puts $channel "$key $value"
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ puts $channel "type [lindex $type 0]"
+ puts $channel "type:attrib [lindex $type 1]"
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ puts $channel "regexp:enable $use_regexp"
+ puts $channel "regexp $regexp"
+}
+
+proc Apol_Analysis_directflow::loadQuery {channel} {
+ variable vals
+
+ set classes {}
+ while {[gets $channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ set key {}
+ set value {}
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ switch -- $key {
+ classes:selected {
+ set classes $value
+ }
+ default {
+ set vals($key) $value
+ }
+ }
+ }
+
+ # fill in only classes found within the current policy
+ open
+
+ set vals(classes:selected) {}
+ foreach c $classes {
+ set i [lsearch [Apol_Class_Perms::getClasses] $c]
+ if {$i >= 0} {
+ lappend vals(classes:selected) $c
+ }
+ }
+ set vals(classes:selected) [lsort $vals(classes:selected)]
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_directflow::getTextWidget {tab} {
+ return [$tab.right getframe].res.tb
+}
+
+proc Apol_Analysis_directflow::appendResultsNodes {tree parent_node results} {
+ _createResultsNodes $tree $parent_node $results 0
+}
+
+#################### private functions below ####################
+
+proc Apol_Analysis_directflow::_reinitializeVals {} {
+ variable vals
+ set vals(dir) $::APOL_INFOFLOW_IN
+ array set vals {
+ type {} type:attrib {}
+
+ classes:enable 0
+ classes:selected {}
+
+ regexp:enable 0
+ regexp {}
+ }
+ set vals(classes:all_classes) [Apol_Class_Perms::getClasses]
+}
+
+proc Apol_Analysis_directflow::_reinitializeWidgets {} {
+ variable vals
+ variable widgets
+
+ if {$vals(type:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(type) [list $vals(type) $vals(type:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(type) $vals(type)
+ }
+ Apol_Widget::setRegexpEntryValue $widgets(regexp) $vals(regexp:enable) $vals(regexp)
+
+ Apol_Widget::setScrolledListboxState $widgets(classes) enabled
+ set classes_lb [Apol_Widget::getScrolledListbox $widgets(classes)]
+ $classes_lb selection clear 0 end
+ foreach c $vals(classes:selected) {
+ set i [lsearch $vals(classes:all_classes) $c]
+ $classes_lb selection set $i $i
+ }
+ _toggleClasses {} {} {}
+}
+
+proc Apol_Analysis_directflow::_toggleClasses {name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals(classes:enable)} {
+ Apol_Widget::setScrolledListboxState $widgets(classes) enabled
+ } else {
+ Apol_Widget::setScrolledListboxState $widgets(classes) disabled
+ }
+}
+
+proc Apol_Analysis_directflow::_selectClassesListbox {lb} {
+ variable vals
+ for {set i 0} {$i < [$lb index end]} {incr i} {
+ set t [$lb get $i]
+ if {[$lb selection includes $i]} {
+ lappend vals(classes:selected) $t
+ } else {
+ if {[set j [lsearch $vals(classes:selected) $t]] >= 0} {
+ set vals(classes:selected) [lreplace $vals(classes:selected) $j $j]
+ }
+ }
+ }
+ set vals(classes:selected) [lsort -uniq $vals(classes:selected)]
+ focus $lb
+}
+
+proc Apol_Analysis_directflow::_includeAll {lb} {
+ variable vals
+ $lb selection set 0 end
+ set vals(classes:selected) $vals(classes:all_classes)
+}
+
+proc Apol_Analysis_directflow::_excludeAll {lb} {
+ variable vals
+ $lb selection clear 0 end
+ set vals(classes:selected) {}
+}
+
+#################### functions that do analyses ####################
+
+proc Apol_Analysis_directflow::_checkParams {} {
+ variable vals
+ variable widgets
+ if {![ApolTop::is_policy_open]} {
+ return "No current policy file is opened."
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ if {[lindex $type 0] == {}} {
+ return "No type was selected."
+ }
+ if {![Apol_Types::isTypeInPolicy [lindex $type 0]]} {
+ return "[lindex $type 0] is not a type within the policy."
+ }
+ set vals(type) [lindex $type 0]
+ set vals(type:attrib) [lindex $type 1]
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$use_regexp && $regexp == {}} {
+ return "No regular expression provided."
+ }
+ set vals(regexp:enable) $use_regexp
+ set vals(regexp) $regexp
+ if {$vals(classes:enable) && $vals(classes:selected) == {}} {
+ return "At least one object class must be included."
+ }
+
+ # if a permap is not loaded then load the default permap
+ if {![Apol_Perms_Map::is_pmap_loaded]} {
+ if {![ApolTop::openDefaultPermMap]} {
+ return "This analysis requires that a permission map is loaded."
+ }
+ apol_tcl_clear_info_string
+ }
+
+ return {} ;# all parameters passed, now ready to do search
+}
+
+proc Apol_Analysis_directflow::_analyze {} {
+ variable vals
+ set classes {}
+ if {$vals(classes:enable)} {
+ foreach c $vals(classes:selected) {
+ foreach p [Apol_Class_Perms::getPermsForClass $c] {
+ lappend classes $c $p
+ }
+ }
+ }
+ if {$vals(regexp:enable)} {
+ set regexp $vals(regexp)
+ } else {
+ set regexp {}
+ }
+
+ set q [new_apol_infoflow_analysis_t]
+ $q set_mode $::ApolTop::policy $::APOL_INFOFLOW_MODE_DIRECT
+ $q set_dir $::ApolTop::policy $vals(dir)
+ $q set_type $::ApolTop::policy $vals(type)
+ foreach {c p} $classes {
+ $q append_class_perm $::ApolTop::policy $c $p
+ }
+ $q set_result_regex $::ApolTop::policy $regexp
+ set results [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ return $results
+}
+
+proc Apol_Analysis_directflow::_analyzeMore {tree node} {
+ # disallow more analysis if this node is the same as its parent
+ set new_start [$tree itemcget $node -text]
+ if {[$tree itemcget [$tree parent $node] -text] == $new_start} {
+ return {}
+ }
+ set g [lindex [$tree itemcget top -data] 0]
+ $g do_more $::ApolTop::policy $new_start
+}
+
+################# functions that control analysis output #################
+
+proc Apol_Analysis_directflow::_createResultsDisplay {} {
+ variable vals
+
+ set f [Apol_Analysis::createResultTab "Direct Flow" [array get vals]]
+
+ set tree_tf [TitleFrame $f.left -text "Direct Information Flow Tree"]
+ pack $tree_tf -side left -expand 0 -fill y -padx 2 -pady 2
+ set sw [ScrolledWindow [$tree_tf getframe].sw -auto both]
+ set tree [Tree [$sw getframe].tree -width 24 -redraw 1 -borderwidth 0 \
+ -highlightthickness 0 -showlines 1 -padx 0 -bg white]
+ $sw setwidget $tree
+ pack $sw -expand 1 -fill both
+
+ set res_tf [TitleFrame $f.right -text "Direct Information Flow Results"]
+ pack $res_tf -side left -expand 1 -fill both -padx 2 -pady 2
+ set res [Apol_Widget::makeSearchResults [$res_tf getframe].res]
+ $res.tb tag configure title -font {Helvetica 14 bold}
+ $res.tb tag configure title_type -foreground blue -font {Helvetica 14 bold}
+ $res.tb tag configure subtitle -font {Helvetica 10 bold}
+ $res.tb tag configure subtitle_dir -foreground blue -font {Helvetica 10 bold}
+ pack $res -expand 1 -fill both
+
+ $tree configure -selectcommand [list Apol_Analysis_directflow::_treeSelect $res]
+ $tree configure -opencmd [list Apol_Analysis_directflow::_treeOpen $tree]
+ return $f
+}
+
+proc Apol_Analysis_directflow::_treeSelect {res tree node} {
+ if {$node != {}} {
+ $res.tb configure -state normal
+ $res.tb delete 0.0 end
+ set data [$tree itemcget $node -data]
+ if {[string index $node 0] == "x"} {
+ _renderResultsDirectFlow $res $tree $node [lindex $data 1]
+ } else {
+ # an informational node, whose data has already been rendered
+ eval $res.tb insert end [lindex $data 1]
+ }
+ $res.tb configure -state disabled
+ }
+}
+
+# perform additional direct infoflows if this node has not been
+# analyzed yet
+proc Apol_Analysis_directflow::_treeOpen {tree node} {
+ foreach {is_expanded results} [$tree itemcget $node -data] {break}
+ if {[string index $node 0] == "x" && !$is_expanded} {
+ Apol_Progress_Dialog::wait "Direct Information Flow Analysis" \
+ "Performing Direct Information Flow Analysis..." \
+ {
+ set new_results [_analyzeMore $tree $node]
+ # mark this node as having been expanded
+ $tree itemconfigure $node -data [list 1 $results]
+ if {$new_results != {}} {
+ _createResultsNodes $tree $node $new_results 1
+ $new_results -acquire
+ $new_results -delete
+ }
+ }
+ }
+}
+
+proc Apol_Analysis_directflow::_clearResultsDisplay {f} {
+ variable vals
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+ $tree delete [$tree nodes root]
+ Apol_Widget::clearSearchResults $res
+ Apol_Analysis::setResultTabCriteria [array get vals]
+}
+
+proc Apol_Analysis_directflow::_renderResults {f results} {
+ variable vals
+
+ set graph_handler [$results extract_graph]
+ $graph_handler -acquire ;# let Tcl's GC destroy graph when this tab closes
+ set results_list [$results extract_result_vector]
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+
+ $tree insert end root top -text $vals(type) -open 1 -drawcross auto
+ set top_text [_renderTopText]
+ $tree itemconfigure top -data [list $graph_handler $top_text]
+
+ _createResultsNodes $tree top $results_list 1
+ $tree selection set top
+ $tree opentree top 0
+ $tree see top
+
+ $results_list -acquire
+ $results_list -delete
+}
+
+proc Apol_Analysis_directflow::_renderTopText {} {
+ variable vals
+
+ set top_text [list "Direct Information Flow Analysis: Starting type: " title]
+ lappend top_text $vals(type) title_type \
+ "\n\n" title \
+ "This tab provides the results of a Direct Information Flow analysis
+beginning from the starting type selected above. The results of the
+analysis are presented in tree form with the root of the tree (this
+node) being the start point for the analysis.
+
+\nEach child node in the tree represents a type in the current policy
+for which there is a direct information flow to or from (depending on
+your selection above) its parent node.
+
+\nNOTE: For any given generation, if the parent and the child are the
+same, you cannot open the child. This avoids cyclic analyses."
+}
+
+# If do_expand is zero, then generate result nodes for only the first
+# target type of $results. This is needed by two types relationship
+# analysis.
+proc Apol_Analysis_directflow::_createResultsNodes {tree parent_node results do_expand} {
+ set all_targets {}
+ set info_list [infoflow_result_vector_to_list $results]
+ set results_processed 0
+ foreach r $info_list {
+ apol_tcl_set_info_string $::ApolTop::policy "Processing result $results_processed of [llength $info_list]"
+
+ if {$do_expand} {
+ set target [[$r get_end_type] get_name $::ApolTop::qpolicy]
+ } else {
+ set target [[[lindex $info_list 0] get_end_type] get_name $::ApolTop::qpolicy]
+ }
+ set flow_dir [$r get_dir]
+ set step0 [apol_infoflow_step_from_void [[$r get_steps] get_element 0]]
+ set rules [$step0 get_rules]
+
+ lappend all_targets $target
+ foreach r [avrule_vector_to_list $rules] {
+ set class [[$r get_object_class $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ lappend classes($target) $class
+ lappend classes($target:$class) $r
+ }
+ set dir($target:$flow_dir) 1
+ incr results_processed
+ }
+
+ set all_targets [lsort -uniq $all_targets]
+ apol_tcl_set_info_string $::ApolTop::policy "Displaying [llength $all_targets] result(s)"
+ update idle
+
+ foreach t $all_targets {
+ if {[info exists dir(${t}:${::APOL_INFOFLOW_BOTH})] ||
+ ([info exists dir(${t}:${::APOL_INFOFLOW_IN})] &&
+ [info exists dir(${t}:${::APOL_INFOFLOW_OUT})])} {
+ set flow_dir "both"
+ } elseif {[info exists dir(${t}:${::APOL_INFOFLOW_IN})]} {
+ set flow_dir "in"
+ } else {
+ set flow_dir "out"
+ }
+ set rules {}
+ foreach c [lsort -uniq $classes($t)] {
+ lappend rules [list $c [lsort -uniq $classes($t:$c)]]
+ }
+ set data [list $flow_dir $rules]
+ $tree insert end $parent_node x\#auto -text $t -drawcross allways \
+ -data [list 0 $data]
+ }
+}
+
+proc Apol_Analysis_directflow::_renderResultsDirectFlow {res tree node data} {
+ set parent_name [$tree itemcget [$tree parent $node] -text]
+ set name [$tree itemcget $node -text]
+ foreach {flow_dir classes} $data {break}
+ switch -- $flow_dir {
+ both {
+ $res.tb insert end "Information flows both into and out of " title \
+ $parent_name title_type \
+ " from/to " title \
+ $name title_type
+ }
+ in {
+ $res.tb insert end "Information flows into " title \
+ $parent_name title_type \
+ " from " title \
+ $name title_type
+ }
+ out {
+ $res.tb insert end "Information flows out of " title \
+ $parent_name title_type \
+ " to " title \
+ $name title_type
+ }
+ }
+ $res.tb insert end "\n\n" title_type \
+ "Objects classes for " subtitle \
+ [string toupper $flow_dir] subtitle_dir \
+ " flows:\n" subtitle
+ foreach c $classes {
+ foreach {class_name rules} $c {break}
+ $res.tb insert end " " {} \
+ $class_name\n subtitle
+ set v [new_apol_vector_t]
+ foreach r $rules {
+ $v append $r
+ }
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+}
diff --git a/apol/domaintrans_help.txt b/apol/domaintrans_help.txt
new file mode 100644
index 0000000..5f0666a
--- /dev/null
+++ b/apol/domaintrans_help.txt
@@ -0,0 +1,141 @@
+An overview of domain transition analysis
+
+
+A key feature of Type Enforcement (TE) security is the ability to
+define domain types with which programs run, use that domain type to
+control access to objects (which are also typed), and strictly control
+the ability of a process to change its domain type. This last ability
+is known as domain transition.
+
+Apol supports analysis of an SELinux policy to understand the domain
+transitions it allows. As with all access in SELinux, the ability to
+transition from one domain to another is controlled by 'allow' rules
+in the policy. Below, we describe how apol performs a domain
+transition analysis.
+
+
+The three types of interest for domain transitions
+--------------------------------------------------
+When discussing domain transition access, there are three different
+types we must consider:
+
+ + SOURCE TYPE: This is the domain type associated with a process
+ that is trying to change (transition) its domain type to another
+ type.
+
+ + TARGET TYPE: This is the domain type to which the source type is
+ trying to transition.
+
+ + FILE TYPE (ENTRYPOINT TYPE): This is a type associated with an
+ executable file object that allows the target type to be entered
+ as part of an execve() system call.
+
+
+Forward vs. reverse domain transition analysis
+----------------------------------------------
+Apol supports both forward and reverse domain transition analysis. A
+forward analysis determines all the TARGET types to which the selected
+SOURCE types may transition. The results may be further filtered by
+selecting particular object classes, permissions, and object types to
+find transitions to domains that have those specific privileges or
+that have access to a particular object type(s). A reverse analysis
+is the opposite; select a TARGET type and determine all the SOURCE
+types that may transition to the target type.
+
+In each case, apol creates a tree structure to show the result. Drill
+down the tree to follow any given transition path.
+
+
+Criteria for identifying allow domain transitions
+-------------------------------------------------
+In SELinux, three types of access (and hence at least three rules)
+must be allowed by the policy for a domain transition to occur. These
+three access types form the criteria used by apol to determine allowed
+transitions.
+
+Given an understanding of the three types of interest in a domain
+transition, the criteria for an allowed domain transition are as
+follows. In the examples below, assume 'user_t' is the source type,
+'passwd_t' is the target type, and 'passwd_exec_t' is the file entry
+point type.
+
+ 1. A rule must exist that allows the SOURCE domain type 'transition'
+ access for 'process' object class for the TARGET domain type. For
+ example, the rule:
+
+ allow user_t passwd_t : process transition;
+
+ meets this criterion by allowing the source type (user_t) 'process
+ transition' permission to the target type (passwd_t).
+
+ 2. A rule must exist that allows the SOURCE domain type 'execute'
+ access to the FILE ENTRYPOINT type. For example, the rule:
+
+ allow user_t passwd_exec_t : file {read getattr execute};
+
+ meets the criterion by allowing the source type (user_t) 'execute'
+ access to the file entrypoint type (passwd_exec_t).
+
+ 3. A rule must exist that allows the TARGET domain type 'entrypoint'
+ access to the FILE ENTRYPOINT type for file objects. For
+ example, the rule:
+
+ allow passwd_t passwd_exec_t : file entrypoint;
+
+ meets this criterion by allowing the target type (passwd_t) 'file
+ entrypoint' access to the file entrypoint type (passwd_exec_t).
+
+ 4. There must be a way for the transition to be specified. Typically
+ this is accomplished in the policy with a TYPE TRANSITION statement.
+ For example, the statement:
+
+ type_transition user_t password_exec_t : process passwd_t;
+
+ meets this criterion by specifying that when user_t executes
+ a program with the passwd_exec_t type, the default type of the
+ new process is passwd_t. This is the most common specifier because
+ it does not require the programs to be SELinux-aware. Alternatively,
+ the program can be made SELinux-aware and the program itself may
+ specify the type of the new process. For example, the statement:
+
+ allow user_t self : process setexec;
+
+ allows the source type (user_t) to specify the type of new processes
+ when executing programs. In both the type transition and setexec
+ cases, the types that the source domain may transition to are
+ limited by the previous three criterion.
+
+In the analysis results for a reverse domain transition analysis, apol
+will list all the types that meet the above four criteria. On the
+other hand, results for a forward domain transition analysis will be
+limited to types that meet the above four criteria and that have the
+specified privileges or access to a particular object type(s). See
+'General Help' for the Forward DTA Advanced Search Options feature in
+apol.
+
+
+Filtering domain transition results in apol
+-------------------------------------------
+The domain transition analysis interface in apol provides the ability
+to further refine a domain transition query in order to find
+transitions to a specific domain and/or transitions to domains that
+are granted specific access to object types or classes. Filtering
+results types using regular expressions is enabled for both forward
+and reverse domain transition queries; however, the access filters are
+only enabled for a forward domain transition query.
+
+To enable and use the access filters, select the "Use access filters"
+checkbox and display the Access Filters dialog. This dialog presents
+listboxes for including object types, object classes, and permissions.
+An access filter may be particulary useful to a user searching for
+transitions to domains that have specific access to an object type
+and/or class. For example, one could determine whether the type
+user_t is allowed to transition to a domain that can write a file of
+type shadow_t. To run this query from apol, specify the starting type
+as user_t, go to the Access Filters dialog, select shadow_t in the
+included object types listbox, select 'file' from the object classes
+listbox and then select the 'write' permission. If multiple types,
+classes, or permissions are selected, the results will include all
+transitions to a domain with access to at least one of the selected
+types for at least one of the selected classes with at least one of
+the selected permissions.
diff --git a/apol/domaintrans_module.tcl b/apol/domaintrans_module.tcl
new file mode 100644
index 0000000..ea1d471
--- /dev/null
+++ b/apol/domaintrans_module.tcl
@@ -0,0 +1,999 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Analysis_domaintrans {
+ variable vals
+ variable widgets
+ Apol_Analysis::registerAnalysis "Apol_Analysis_domaintrans" "Domain Transition"
+}
+
+proc Apol_Analysis_domaintrans::create {options_frame} {
+ variable vals
+ variable widgets
+
+ _reinitializeVals
+
+ set dir_tf [TitleFrame $options_frame.dir -text "Direction"]
+ pack $dir_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set dir_forward [radiobutton [$dir_tf getframe].forward -text "Forward" \
+ -variable Apol_Analysis_domaintrans::vals(dir) \
+ -value $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD]
+ set dir_reverse [radiobutton [$dir_tf getframe].reverse -text "Reverse" \
+ -variable Apol_Analysis_domaintrans::vals(dir) \
+ -value $::APOL_DOMAIN_TRANS_DIRECTION_REVERSE]
+ pack $dir_forward $dir_reverse -anchor w
+ trace add variable Apol_Analysis_domaintrans::vals(dir) write \
+ Apol_Analysis_domaintrans::_toggleDirection
+
+ set req_tf [TitleFrame $options_frame.req -text "Required Parameters"]
+ pack $req_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set l [label [$req_tf getframe].l -textvariable Apol_Analysis_domaintrans::vals(type:label)]
+ pack $l -anchor w
+ set widgets(type) [Apol_Widget::makeTypeCombobox [$req_tf getframe].type]
+ pack $widgets(type)
+
+ set filter_tf [TitleFrame $options_frame.filter -text "Optional Result Filters"]
+ pack $filter_tf -side left -padx 2 -pady 2 -expand 1 -fill both
+ set access_f [frame [$filter_tf getframe].access]
+ pack $access_f -side left -anchor nw
+ set widgets(access_enable) [checkbutton $access_f.enable -text "Use access filters" \
+ -variable Apol_Analysis_domaintrans::vals(access:enable)]
+ pack $widgets(access_enable) -anchor w
+ set widgets(access) [button $access_f.b -text "Access Filters" \
+ -command Apol_Analysis_domaintrans::_createAccessDialog \
+ -state disabled]
+ pack $widgets(access) -anchor w -padx 4
+ trace add variable Apol_Analysis_domaintrans::vals(access:enable) write \
+ Apol_Analysis_domaintrans::_toggleAccessSelected
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry [$filter_tf getframe].end]
+ $widgets(regexp).cb configure -text "Filter result types using regular expression"
+ pack $widgets(regexp) -side left -anchor nw -padx 8
+}
+
+proc Apol_Analysis_domaintrans::open {} {
+ variable vals
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(type)
+ set vals(targets:inc) [Apol_Types::getTypes]
+ set vals(targets:inc_displayed) [Apol_Types::getTypes]
+ foreach c [Apol_Class_Perms::getClasses] {
+ set vals(classes:$c) [Apol_Class_Perms::getPermsForClass $c]
+ set vals(classes:$c:enable) 1
+ }
+}
+
+proc Apol_Analysis_domaintrans::close {} {
+ variable widgets
+ _reinitializeVals
+ _reinitializeWidgets
+ Apol_Widget::clearTypeCombobox $widgets(type)
+}
+
+proc Apol_Analysis_domaintrans::getInfo {} {
+ return "A forward domain transition analysis will determine all (target)
+domains to which a given (source) domain may transition. For a
+forward domain transition to be allowed, multiple forms of access must
+be granted:
+
+\n (1) source domain must have process transition permission for
+ target domain,
+ (2) source domain must have file execute permission for some
+ entrypoint type,
+ (3) target domain must have file entrypoint permission for the
+ same entrypoint type, and,
+ (4) for policies version 15 or later, either a type_transition
+ rule or a setexec permission for the source domain.
+
+\nA reverse domain transition analysis will determine all (source)
+domains that can transition to a given (target) domain. For a reverse
+domain transition to be allowed, three forms of access must be
+granted:
+
+\n (1) target domain must have process transition permission from the
+ source domain,
+ (2) target domain must have file entrypoint permission to some
+ entrypoint type, and
+ (3) source domain must have file execute permission to the same
+ entrypoint type.
+
+\nThe results are presented in tree form. Open target children domains
+to perform another domain transition analysis on that domain.
+
+\nFor additional help on this topic select \"Domain Transition Analysis\"
+from the Help menu."
+}
+
+proc Apol_Analysis_domaintrans::newAnalysis {} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ set f [_createResultsDisplay]
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_domaintrans::updateAnalysis {f} {
+ variable vals
+
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ $f.left configure -text "Forward Domain Transition"
+ } else {
+ $f.left configure -text "Reverse Domain Transition"
+ }
+
+ set results [_analyze]
+ _clearResultsDisplay $f
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_domaintrans::reset {} {
+ _reinitializeVals
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_domaintrans::switchTab {query_options} {
+ variable vals
+ variable widgets
+ array set vals $query_options
+ if {$vals(type:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(type) [list $vals(type) $vals(type:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(type) $vals(type)
+ }
+ Apol_Widget::setRegexpEntryValue $widgets(regexp) $vals(regexp:enable) $vals(regexp)
+}
+
+proc Apol_Analysis_domaintrans::saveQuery {channel} {
+ variable vals
+ variable widgets
+ foreach {key value} [array get vals] {
+ switch -- $key {
+ targets:inc_displayed -
+ classes:perms_displayed -
+ search:regexp -
+ search:object_types -
+ search:classperm_perms {
+ # don't save these variables
+ }
+ default {
+ puts $channel "$key $value"
+ }
+ }
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ puts $channel "type [lindex $type 0]"
+ puts $channel "type:attrib [lindex $type 1]"
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ puts $channel "regexp:enable $use_regexp"
+ puts $channel "regexp $regexp"
+}
+
+proc Apol_Analysis_domaintrans::loadQuery {channel} {
+ variable vals
+ set targets_inc {}
+ while {[gets $channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ set key {}
+ set value {}
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ if {$key == "targets:inc"} {
+ lappend targets_inc $value
+ } elseif {[regexp -- {^classes:(.+)} $key -> class]} {
+ set c($class) $value
+ } else {
+ set vals($key) $value
+ }
+ }
+
+ # fill in the inclusion lists using only types/classes found
+ # within the current policy
+ open
+
+ set vals(targets:inc) {}
+ foreach s $targets_inc {
+ set i [lsearch [Apol_Types::getTypes] $s]
+ if {$i >= 0} {
+ lappend vals(targets:inc) $s
+ }
+ }
+
+ foreach class_key [array names c] {
+ if {[regexp -- {^([^:]+):enable} $class_key -> class]} {
+ if {[lsearch [Apol_Class_Perms::getClasses] $class] >= 0} {
+ set vals(classes:$class:enable) $c($class_key)
+ }
+ } else {
+ set class $class_key
+ set old_p $vals(classes:$class)
+ set new_p {}
+ foreach p $c($class) {
+ if {[lsearch $old_p $p] >= 0} {
+ lappend new_p $p
+ }
+ }
+ set vals(classes:$class) [lsort -uniq $new_p]
+ }
+ }
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_domaintrans::getTextWidget {tab} {
+ return [$tab.right getframe].res.tb
+}
+
+proc Apol_Analysis_domaintrans::appendResultsNodes {tree parent_node results} {
+ _createResultsNodes $tree $parent_node $results $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD
+}
+
+#################### private functions below ####################
+
+proc Apol_Analysis_domaintrans::_reinitializeVals {} {
+ variable vals
+
+ set vals(dir) $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD
+ array set vals {
+ type:label "Source domain"
+ type {} type:attrib {}
+
+ regexp:enable 0
+ regexp {}
+
+ access:enable 0
+ targets:inc {} targets:inc_displayed {}
+ targets:attribenable 0 targets:attrb {}
+ }
+ array unset vals classes:*
+ array unset vals search:*
+ foreach c [Apol_Class_Perms::getClasses] {
+ set vals(classes:$c) [Apol_Class_Perms::getPermsForClass $c]
+ set vals(classes:$c:enable) 1
+ }
+}
+
+proc Apol_Analysis_domaintrans::_reinitializeWidgets {} {
+ variable vals
+ variable widgets
+
+ if {$vals(type:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(type) [list $vals(type) $vals(type:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(type) $vals(type)
+ }
+ Apol_Widget::setRegexpEntryValue $widgets(regexp) $vals(regexp:enable) $vals(regexp)
+}
+
+proc Apol_Analysis_domaintrans::_toggleDirection {name1 name2 op} {
+ variable vals
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ set vals(type:label) "Source domain"
+ } else {
+ set vals(type:label) "Target domain"
+ }
+ _maybeEnableAccess
+}
+
+proc Apol_Analysis_domaintrans::_toggleAccessSelected {name1 name2 op} {
+ _maybeEnableAccess
+}
+
+proc Apol_Analysis_domaintrans::_maybeEnableAccess {} {
+ variable vals
+ variable widgets
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ $widgets(access_enable) configure -state normal
+ if {$vals(access:enable)} {
+ $widgets(access) configure -state normal
+ } else {
+ $widgets(access) configure -state disabled
+ }
+ } else {
+ $widgets(access_enable) configure -state disabled
+ $widgets(access) configure -state disabled
+ }
+}
+
+################# functions that do access filters #################
+
+proc Apol_Analysis_domaintrans::_createAccessDialog {} {
+ variable widgets
+ $widgets(access) configure -state disabled
+ destroy .domaintrans_adv
+ set d [Dialog .domaintrans_adv -modal local -separator 1 -title "Domain Transition Access Filter" -parent .]
+ $d add -text "Close"
+ _createAccessTargets [$d getframe]
+ _createAccessClasses [$d getframe]
+ $d draw
+ $widgets(access) configure -state normal
+}
+
+proc Apol_Analysis_domaintrans::_createAccessTargets {f} {
+ variable vals
+
+ set type_f [frame $f.targets]
+ pack $type_f -side left -expand 0 -fill both -padx 4 -pady 4
+ set l1 [label $type_f.l1 -text "Included Object Types"]
+ pack $l1 -anchor w
+
+ set targets [Apol_Widget::makeScrolledListbox $type_f.targets -height 10 -width 24 \
+ -listvar Apol_Analysis_domaintrans::vals(targets:inc_displayed) \
+ -selectmode extended -exportselection 0]
+ set targets_lb [Apol_Widget::getScrolledListbox $targets]
+ bind $targets_lb <<ListboxSelect>> \
+ [list Apol_Analysis_domaintrans::_selectTargetListbox $targets_lb]
+ pack $targets -expand 0 -fill both
+
+ set bb [ButtonBox $type_f.bb -homogeneous 1 -spacing 4]
+ $bb add -text "Include All" \
+ -command [list Apol_Analysis_domaintrans::_includeAllItems $targets_lb targets]
+ $bb add -text "Ignore All" \
+ -command [list Apol_Analysis_domaintrans::_ignoreAllItems $targets_lb targets]
+ pack $bb -pady 4
+
+ set attrib [frame $type_f.a]
+ pack $attrib
+ set attrib_enable [checkbutton $attrib.ae -anchor w \
+ -text "Filter by attribute" \
+ -variable Apol_Analysis_domaintrans::vals(targets:attribenable)]
+ set attrib_box [ComboBox $attrib.ab -autopost 1 -entrybg white -width 16 \
+ -values $Apol_Types::attriblist \
+ -textvariable Apol_Analysis_domaintrans::vals(targets:attrib)]
+ $attrib_enable configure -command \
+ [list Apol_Analysis_domaintrans::_attribEnabled $attrib_box $targets_lb]
+ # remove any old traces on the attribute
+ trace remove variable Apol_Analysis_domaintrans::vals(targets:attrib) write \
+ [list Apol_Analysis_domaintrans::_attribChanged $targets_lb]
+ trace add variable Apol_Analysis_domaintrans::vals(targets:attrib) write \
+ [list Apol_Analysis_domaintrans::_attribChanged $targets_lb]
+ pack $attrib_enable -side top -expand 0 -fill x -anchor sw -padx 5 -pady 2
+ pack $attrib_box -side top -expand 1 -fill x -padx 10
+ _attribEnabled $attrib_box $targets_lb
+ if {[set anchor [lindex [lsort [$targets_lb curselection]] 0]] != {}} {
+ $targets_lb selection anchor $anchor
+ $targets_lb see $anchor
+ }
+}
+
+proc Apol_Analysis_domaintrans::_selectTargetListbox {lb} {
+ variable vals
+ for {set i 0} {$i < [$lb index end]} {incr i} {
+ set t [$lb get $i]
+ if {[$lb selection includes $i]} {
+ lappend vals(targets:inc) $t
+ } else {
+ if {[set j [lsearch $vals(targets:inc) $t]] >= 0} {
+ set vals(targets:inc) [lreplace $vals(targets:inc) $j $j]
+ }
+ }
+ }
+ set vals(targets:inc) [lsort -uniq $vals(targets:inc)]
+ focus $lb
+}
+
+proc Apol_Analysis_domaintrans::_includeAllItems {lb varname} {
+ variable vals
+ $lb selection set 0 end
+ set displayed [$lb get 0 end]
+ set vals($varname:inc) [lsort -uniq [concat $vals($varname:inc) $displayed]]
+}
+
+proc Apol_Analysis_domaintrans::_ignoreAllItems {lb varname} {
+ variable vals
+ $lb selection clear 0 end
+ set displayed [$lb get 0 end]
+ set inc {}
+ foreach t $vals($varname:inc) {
+ if {[lsearch $displayed $t] == -1} {
+ lappend inc $t
+ }
+ }
+ set vals($varname:inc) $inc
+}
+
+proc Apol_Analysis_domaintrans::_attribEnabled {cb lb} {
+ variable vals
+ if {$vals(targets:attribenable)} {
+ $cb configure -state normal
+ _filterTypeLists $vals(targets:attrib) $lb
+ } else {
+ $cb configure -state disabled
+ _filterTypeLists "" $lb
+ }
+}
+
+proc Apol_Analysis_domaintrans::_attribChanged {lb name1 name2 op} {
+ variable vals
+ if {$vals(targets:attribenable)} {
+ _filterTypeLists $vals(targets:attrib) $lb
+ }
+}
+
+proc Apol_Analysis_domaintrans::_filterTypeLists {attrib lb} {
+ variable vals
+ $lb selection clear 0 end
+ if {$attrib != ""} {
+ set vals(targets:inc_displayed) {}
+ set qpol_type_datum [new_qpol_type_t $::ApolTop::qpolicy $attrib]
+ set i [$qpol_type_datum get_type_iter $::ApolTop::qpolicy]
+ while {![$i end]} {
+ set t [qpol_type_from_void [$i get_item]]
+ lappend vals(targets:inc_displayed) [$t get_name $::ApolTop::qpolicy]
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ set vals(targets:inc_displayed) [lsort $vals(targets:inc_displayed)]
+ } else {
+ set vals(targets:inc_displayed) [Apol_Types::getTypes]
+ }
+ foreach t $vals(targets:inc) {
+ if {[set i [lsearch $vals(targets:inc_displayed) $t]] >= 0} {
+ $lb selection set $i $i
+ }
+ }
+}
+
+proc Apol_Analysis_domaintrans::_createAccessClasses {f} {
+ variable vals
+ variable widgets
+
+ set lf [frame $f.left]
+ pack $lf -side left -expand 0 -fill both -padx 4 -pady 4
+ set l1 [label $lf.l -text "Included Object Classes"]
+ pack $l1 -anchor w
+ set rf [frame $f.right]
+ pack $rf -side left -expand 0 -fill both -padx 4 -pady 4
+ set l2 [label $rf.l]
+ pack $l2 -anchor w
+
+ set vals(classes:all_classes) [Apol_Class_Perms::getClasses]
+ set classes [Apol_Widget::makeScrolledListbox $lf.classes -height 10 -width 24 \
+ -listvar Apol_Analysis_domaintrans::vals(classes:all_classes) \
+ -selectmode extended -exportselection 0]
+ set classes_lb [Apol_Widget::getScrolledListbox $classes]
+ pack $classes -expand 1 -fill both
+ set cbb [ButtonBox $lf.cbb -homogeneous 1 -spacing 4]
+ $cbb add -text "Include All" \
+ -command [list Apol_Analysis_domaintrans::_includeAllClasses $classes_lb]
+ $cbb add -text "Ignore All" \
+ -command [list Apol_Analysis_domaintrans::_ignoreAllClasses $classes_lb]
+ pack $cbb -pady 4 -expand 0
+
+ set perms [Apol_Widget::makeScrolledListbox $rf.perms -height 10 -width 24 \
+ -listvar Apol_Analysis_domaintrans::vals(classes:perms_displayed) \
+ -selectmode extended -exportselection 0]
+ set perms_lb [Apol_Widget::getScrolledListbox $perms]
+ pack $perms -expand 1 -fill both
+ set pbb [ButtonBox $rf.pbb -homogeneous 1 -spacing 4]
+ $pbb add -text "Include All" \
+ -command [list Apol_Analysis_domaintrans::_includeAllPerms $classes_lb $perms_lb]
+ $pbb add -text "Ignore All" \
+ -command [list Apol_Analysis_domaintrans::_ignoreAllPerms $classes_lb $perms_lb]
+ pack $pbb -pady 4 -expand 0
+
+ bind $classes_lb <<ListboxSelect>> \
+ [list Apol_Analysis_domaintrans::_selectClassListbox $l2 $classes_lb $perms_lb]
+ bind $perms_lb <<ListboxSelect>> \
+ [list Apol_Analysis_domaintrans::_selectPermListbox $classes_lb $perms_lb]
+
+ foreach class_key [array names vals classes:*:enable] {
+ if {$vals($class_key)} {
+ regexp -- {^classes:([^:]+):enable} $class_key -> class
+ set i [lsearch [Apol_Class_Perms::getClasses] $class]
+ $classes_lb selection set $i $i
+ }
+ }
+ if {[set anchor [lindex [lsort [$classes_lb curselection]] 0]] != {}} {
+ $classes_lb selection anchor $anchor
+ $classes_lb see $anchor
+ }
+ set vals(classes:perms_displayed) {}
+ _selectClassListbox $l2 $classes_lb $perms_lb
+}
+
+proc Apol_Analysis_domaintrans::_selectClassListbox {perm_label lb plb} {
+ variable vals
+ for {set i 0} {$i < [$lb index end]} {incr i} {
+ set c [$lb get $i]
+ set vals(classes:$c:enable) [$lb selection includes $i]
+ }
+ if {[set class [$lb get anchor]] == {}} {
+ $perm_label configure -text "Permissions"
+ return
+ }
+
+ $perm_label configure -text "Permissions for $class"
+ set vals(classes:perms_displayed) [Apol_Class_Perms::getPermsForClass $class]
+ $plb selection clear 0 end
+ foreach p $vals(classes:$class) {
+ set i [lsearch $vals(classes:perms_displayed) $p]
+ $plb selection set $i
+ }
+ if {[set anchor [lindex [lsort [$plb curselection]] 0]] != {}} {
+ $plb selection anchor $anchor
+ $plb see $anchor
+ }
+ focus $lb
+}
+
+proc Apol_Analysis_domaintrans::_includeAllClasses {lb} {
+ variable vals
+ $lb selection set 0 end
+ foreach c [Apol_Class_Perms::getClasses] {
+ set vals(classes:$c:enable) 1
+ }
+}
+
+proc Apol_Analysis_domaintrans::_ignoreAllClasses {lb} {
+ variable vals
+ $lb selection clear 0 end
+ foreach c [Apol_Class_Perms::getClasses] {
+ set vals(classes:$c:enable) 0
+ }
+}
+
+proc Apol_Analysis_domaintrans::_selectPermListbox {lb plb} {
+ variable vals
+ set class [$lb get anchor]
+ set p {}
+ foreach i [$plb curselection] {
+ lappend p [$plb get $i]
+ }
+ set vals(classes:$class) $p
+ focus $plb
+}
+
+proc Apol_Analysis_domaintrans::_includeAllPerms {lb plb} {
+ variable vals
+ set class [$lb get anchor]
+ $plb selection set 0 end
+ set vals(classes:$class) $vals(classes:perms_displayed)
+}
+
+proc Apol_Analysis_domaintrans::_ignoreAllPerms {lb plb} {
+ variable vals
+ set class [$lb get anchor]
+ $plb selection clear 0 end
+ set vals(classes:$class) {}
+}
+
+#################### functions that do analyses ####################
+
+proc Apol_Analysis_domaintrans::_checkParams {} {
+ variable vals
+ variable widgets
+ if {![ApolTop::is_policy_open]} {
+ return "No current policy file is opened."
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ if {[lindex $type 0] == {}} {
+ return "No type was selected."
+ }
+ if {![Apol_Types::isTypeInPolicy [lindex $type 0]]} {
+ return "[lindex $type 0] is not a type within the policy."
+ }
+ set vals(type) [lindex $type 0]
+ set vals(type:attrib) [lindex $type 1]
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$use_regexp && $regexp == {}} {
+ return "No regular expression provided."
+ }
+ set vals(regexp:enable) $use_regexp
+ set vals(regexp) $regexp
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD && $vals(access:enable)} {
+ set classperm_pairs {}
+ foreach class [Apol_Class_Perms::getClasses] {
+ if {$vals(classes:$class:enable) == 0} {
+ continue
+ }
+ if {$vals(classes:$class) == {}} {
+ return "No permissions were selected for class $class."
+ }
+ foreach perm $vals(classes:$class) {
+ lappend classperm_pairs [list $class $perm]
+ }
+ }
+ if {$vals(targets:inc) == {}} {
+ return "No object types were selected."
+ }
+ if {$classperm_pairs == {}} {
+ return "No object classes were selected."
+ }
+ set vals(search:object_types) $vals(targets:inc)
+ set vals(search:classperm_pairs) $classperm_pairs
+ } else {
+ set vals(search:object_types) {}
+ set vals(search:classperm_pairs) {}
+ }
+ if {$vals(regexp:enable)} {
+ set vals(search:regexp) $vals(regexp)
+ } else {
+ set vals(search:regexp) {}
+ }
+ return {} ;# all parameters passed, now ready to do search
+}
+
+proc Apol_Analysis_domaintrans::_analyze {} {
+ variable vals
+ $::ApolTop::policy reset_domain_trans_table
+ set q [new_apol_domain_trans_analysis_t]
+ $q set_direction $::ApolTop::policy $vals(dir)
+ $q set_start_type $::ApolTop::policy $vals(type)
+ $q set_result_regex $::ApolTop::policy $vals(search:regexp)
+ foreach o $vals(search:object_types) {
+ $q append_access_type $::ApolTop::policy $o
+ }
+ foreach {cp_pair} $vals(search:classperm_pairs) {
+ $q append_class $::ApolTop::policy [lindex $cp_pair 0]
+ $q append_perm $::ApolTop::policy [lindex $cp_pair 1]
+ }
+ apol_tcl_set_info_string $::ApolTop::policy "Building domain transition table..."
+ $::ApolTop::policy build_domain_trans_table
+ apol_tcl_set_info_string $::ApolTop::policy "Performing Domain Transition Analysis..."
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ return $v
+}
+
+proc Apol_Analysis_domaintrans::_analyzeMore {tree node analysis_args} {
+ # disallow more analysis if this node is the same as its parent
+ set new_start [$tree itemcget $node -text]
+ if {[$tree itemcget [$tree parent $node] -text] == $new_start} {
+ return {}
+ }
+ foreach {dir orig_type object_types classperm_pairs regexp} $analysis_args {break}
+ set q [new_apol_domain_trans_analysis_t]
+ $q set_direction $::ApolTop::policy $dir
+ $q set_start_type $::ApolTop::policy $new_start
+ $q set_result_regex $::ApolTop::policy $regexp
+ foreach o $object_types {
+ $q append_access_type $::ApolTop::policy $o
+ }
+ foreach {cp_pair} $classperm_pairs {
+ $q append_class $::ApolTop::policy [lindex $cp_pair 0]
+ $q append_perm $::ApolTop::policy [lindex $cp_pair 1]
+ }
+ $::ApolTop::policy reset_domain_trans_table
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ return $v
+}
+
+################# functions that control analysis output #################
+
+proc Apol_Analysis_domaintrans::_createResultsDisplay {} {
+ variable vals
+
+ set f [Apol_Analysis::createResultTab "Domain Trans" [array get vals]]
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ set tree_title "Forward Domain Transition"
+ } else {
+ set tree_title "Reverse Domain Transition"
+ }
+ set tree_tf [TitleFrame $f.left -text $tree_title]
+ pack $tree_tf -side left -expand 0 -fill y -padx 2 -pady 2
+ set sw [ScrolledWindow [$tree_tf getframe].sw -auto both]
+ set tree [Tree [$sw getframe].tree -width 24 -redraw 1 -borderwidth 0 \
+ -highlightthickness 0 -showlines 1 -padx 0 -bg white]
+ $sw setwidget $tree
+ pack $sw -expand 1 -fill both
+
+ set res_tf [TitleFrame $f.right -text "Domain Transition Results"]
+ pack $res_tf -side left -expand 1 -fill both -padx 2 -pady 2
+ set res [Apol_Widget::makeSearchResults [$res_tf getframe].res]
+ $res.tb tag configure title -font {Helvetica 14 bold}
+ $res.tb tag configure title_type -foreground blue -font {Helvetica 14 bold}
+ $res.tb tag configure subtitle -font {Helvetica 10 bold}
+ $res.tb tag configure num -foreground blue -font {Helvetica 10 bold}
+ pack $res -expand 1 -fill both
+
+ $tree configure -selectcommand [list Apol_Analysis_domaintrans::_treeSelect $res]
+ $tree configure -opencmd [list Apol_Analysis_domaintrans::_treeOpen $tree]
+ return $f
+}
+
+proc Apol_Analysis_domaintrans::_treeSelect {res tree node} {
+ if {$node != {}} {
+ $res.tb configure -state normal
+ $res.tb delete 0.0 end
+ set data [$tree itemcget $node -data]
+ if {[string index $node 0] == "f" || [string index $node 0] == "r"} {
+ _renderResultsDTA $res $tree $node [lindex $data 1]
+ } else {
+ # an informational node, whose data has already been rendered
+ eval $res.tb insert end $data
+ }
+ $res.tb configure -state disabled
+ }
+}
+
+# perform additional domain transitions if this node has not been
+# analyzed yet
+proc Apol_Analysis_domaintrans::_treeOpen {tree node} {
+ foreach {search_crit results} [$tree itemcget $node -data] {break}
+ if {([string index $node 0] == "f" || [string index $node 0] == "r") && $search_crit != {}} {
+ set new_results [Apol_Progress_Dialog::wait "Domain Transition Analysis" \
+ "Performing Domain Transition Analysis..." \
+ { _analyzeMore $tree $node $search_crit }]
+ # mark this node as having been expanded
+ $tree itemconfigure $node -data [list {} $results]
+ if {$new_results != {}} {
+ _createResultsNodes $tree $node $new_results $search_crit
+ $new_results -acquire
+ $new_results -delete
+ }
+ }
+}
+
+proc Apol_Analysis_domaintrans::_clearResultsDisplay {f} {
+ variable vals
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+ $tree delete [$tree nodes root]
+ Apol_Widget::clearSearchResults $res
+ Apol_Analysis::setResultTabCriteria [array get vals]
+}
+
+proc Apol_Analysis_domaintrans::_renderResults {f results} {
+ variable vals
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+
+ $tree insert end root top -text $vals(type) -open 1 -drawcross auto
+ set top_text [_renderTopText]
+ $tree itemconfigure top -data $top_text
+
+ set search_crit [list $vals(dir) $vals(type) $vals(search:object_types) $vals(search:classperm_pairs) $vals(search:regexp)]
+ _createResultsNodes $tree top $results $search_crit
+ $tree selection set top
+ $tree opentree top 0
+ $tree see top
+}
+
+proc Apol_Analysis_domaintrans::_renderTopText {} {
+ variable vals
+
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ set top_text [list "Forward Domain Transition Analysis: Starting Type: " title]
+ } else {
+ set top_text [list "Reverse Domain Transition Analysis: Starting Type: " title]
+ }
+ lappend top_text $vals(type) title_type \
+ "\n\n" title
+ if {$vals(dir) == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ lappend top_text \
+"This tab provides the results of a forward domain transition analysis
+starting from the source domain type above. The results of this
+analysis are presented in tree form with the root of the tree (this
+node) being the start point for the analysis.
+
+\nEach child node in the tree represents a TARGET DOMAIN TYPE. A target
+domain type is a domain to which the source domain may transition.
+You can follow the domain transition tree by opening each subsequent
+generation of children in the tree.\n" {}
+ } else {
+ lappend top_text \
+"This tab provides the results of a reverse domain transition analysis
+given the target domain type above. The results of this analysis are
+presented in tree form with the root of the tree (this node) being the
+target point of the analysis.
+
+\nEach child node in the tree represents a source DOMAIN TYPE. A source
+domain type is a domain that can transition to the target domain. You
+can follow the domain transition tree by opening each subsequent
+generation of children in the tree.\n" {}
+ }
+ lappend top_text \
+"\nNOTE: For any given generation, if the parent and the child are the
+same, you cannot open the child. This avoids cyclic analyses.
+
+\nThe criteria that defines an allowed domain transition are:
+
+\n1) There must be at least one rule that allows TRANSITION access for
+ PROCESS objects between the SOURCE and TARGET domain types.
+
+\n2) There must be at least one FILE TYPE that allows the TARGET type
+ ENTRYPOINT access for FILE objects.
+
+\n3) There must be at least one FILE TYPE that meets criterion 2) above
+ and allows the SOURCE type EXECUTE access for FILE objects.
+
+\n4) For modular policies and monolithic policies greater than version
+ 15, there must also be at least one of the following:
+ a) A type_transition rule for class PROCESS from SOURCE to TARGET
+ for FILE TYPE, or
+ b) A rule that allows SETEXEC for SOURCE to itself.
+
+\nThe information window shows all the rules and file types that meet
+these criteria for each target domain type." {}
+}
+
+proc Apol_Analysis_domaintrans::_createResultsNodes {tree parent_node results search_crit} {
+ set dir [lindex $search_crit 0]
+ set dt_list [domain_trans_result_vector_to_list $results]
+ set results_processed 0
+ foreach r $dt_list {
+ apol_tcl_set_info_string $::ApolTop::policy "Processing result $results_processed of [llength $dt_list]"
+ set source [[$r get_start_type] get_name $::ApolTop::qpolicy]
+ set target [[$r get_end_type] get_name $::ApolTop::qpolicy]
+ set intermed [[$r get_entrypoint_type] get_name $::ApolTop::qpolicy]
+ set proctrans [avrule_vector_to_list [$r get_proc_trans_rules]]
+ set entrypoint [avrule_vector_to_list [$r get_entrypoint_rules]]
+ set execute [avrule_vector_to_list [$r get_exec_rules]]
+ set setexec [avrule_vector_to_list [$r get_setexec_rules]]
+ set type_trans [terule_vector_to_list [$r get_type_trans_rules]]
+ set access_list [avrule_vector_to_list [$r get_access_rules]]
+ if {$dir == $::APOL_DOMAIN_TRANS_DIRECTION_FORWARD} {
+ set key $target
+ set node f:\#auto
+ } else {
+ set key $source
+ set node r:\#auto
+ }
+ foreach p $proctrans {
+ lappend types($key) $p
+ }
+ if {[info exists types($key:setexec)]} {
+ set types($key:setexec) [concat $types($key:setexec) $setexec]
+ } else {
+ set types($key:setexec) $setexec
+ }
+ lappend types($key:inter) $intermed
+ foreach e $entrypoint {
+ lappend types($key:inter:$intermed:entry) $e
+ }
+ foreach e $execute {
+ lappend types($key:inter:$intermed:exec) $e
+ }
+ if {[info exists types($key:inter:$intermed:type_trans)]} {
+ set types($key:inter:$intermed:type_trans) [concat $types($key:inter:$intermed:type_trans) $type_trans]
+ } else {
+ set types($key:inter:$intermed:type_trans) $type_trans
+ }
+ if {[info exists types($key:access)]} {
+ set types($key:access) [concat $types($key:access) $access_list]
+ } else {
+ set types($key:access) $access_list
+ }
+ incr results_processed
+ }
+ foreach key [lsort [array names types]] {
+ if {[string first : $key] != -1} {
+ continue
+ }
+ set ep {}
+ set proctrans [lsort -uniq $types($key)]
+ set setexec [lsort -uniq $types($key:setexec)]
+ foreach intermed [lsort -uniq $types($key:inter)] {
+ lappend ep [list $intermed \
+ [lsort -uniq $types($key:inter:$intermed:entry)] \
+ [lsort -uniq $types($key:inter:$intermed:exec)] \
+ [lsort -uniq $types($key:inter:$intermed:type_trans)]]
+ }
+ set access_list [lsort -uniq $types($key:access)]
+ set data [list $proctrans $setexec $ep $access_list]
+ $tree insert end $parent_node $node -text $key -drawcross allways \
+ -data [list $search_crit $data]
+ }
+}
+
+proc Apol_Analysis_domaintrans::_renderResultsDTA {res tree node data} {
+ set parent_name [$tree itemcget [$tree parent $node] -text]
+ set name [$tree itemcget $node -text]
+ foreach {proctrans setexec ep access_list} $data {break}
+ # direction of domain transition is encoded encoded in the node's
+ # identifier
+ if {[string index $node 0] == "f"} {
+ set header [list "Domain transition from " title \
+ $parent_name title_type \
+ " to " title \
+ $name title_type]
+ } else {
+ set header [list "Domain transition from " title \
+ $name title_type \
+ " to " title \
+ $parent_name title_type]
+ }
+ eval $res.tb insert end $header
+ $res.tb insert end "\n\n" title_type
+
+ $res.tb insert end "Process Transition Rules: " subtitle \
+ [llength $proctrans] num \
+ "\n" subtitle
+ set v [list_to_vector $proctrans]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ if {[llength $setexec] > 0} {
+ $res.tb insert end "\n" {} \
+ "Setexec Rules: " subtitle \
+ [llength $setexec] num \
+ "\n" subtitle
+ set v [list_to_vector $setexec]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+
+ $res.tb insert end "\nEntry Point File Types: " subtitle \
+ [llength $ep] num
+ foreach e [lsort -index 0 $ep] {
+ foreach {intermed entrypoint execute type_trans} $e {break}
+ $res.tb insert end "\n $intermed\n" {} \
+ " " {} \
+ "File Entrypoint Rules: " subtitle \
+ [llength $entrypoint] num \
+ "\n" subtitle
+ set v [list_to_vector $entrypoint]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ $res.tb insert end "\n" {} \
+ " " {} \
+ "File Execute Rules: " subtitle \
+ [llength $execute] num \
+ "\n" subtitle
+ set v [list_to_vector $execute]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ if {[llength $type_trans] > 0} {
+ $res.tb insert end "\n" {} \
+ " " {} \
+ "Type_transition Rules: " subtitle \
+ [llength $type_trans] num \
+ "\n" subtitle
+ set v [list_to_vector $type_trans]
+ apol_tcl_terule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_terule_from_void
+ $v -acquire
+ $v -delete
+ }
+ }
+
+ if {[llength $access_list] > 0} {
+ $res.tb insert end "\n" {} \
+ "The access filters you specified returned the following rules: " subtitle \
+ [llength $access_list] num \
+ "\n" subtitle
+ set v [list_to_vector $access_list]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+}
diff --git a/apol/file_contexts_tab.tcl b/apol/file_contexts_tab.tcl
new file mode 100644
index 0000000..55b9d73
--- /dev/null
+++ b/apol/file_contexts_tab.tcl
@@ -0,0 +1,504 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_File_Contexts {
+ variable opts
+ variable widgets
+
+ variable info_button_text \
+ "This tab allows the user to create and open a file context index.
+The file context index is an on-disk database which contains the
+labeling information for an entire filesystem. Once an index has been
+created it can then be queried by user, type, MLS range (if it
+contains MLS information), object class, and/or path.\n
+
+The result of the context query is a list of matching files, ordered
+by path. The first field is the full SELinux context, assuming that
+'Show SELinux file context' is enabled. If 'Show object class' is
+enabled, then the next field is the type of file that matched; this
+will be one of 'file', 'dir', and so forth. The remaining field is
+the full path to the file."
+}
+
+proc Apol_File_Contexts::create {tab_name nb} {
+ variable opts
+ variable widgets
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "File Contexts"]
+ set status [TitleFrame $frame.status -text "File Context Index"]
+ set options [TitleFrame $frame.opts -text "Search Options"]
+ set results [TitleFrame $frame.results -text "Matching Files"]
+ pack $status $options -expand 0 -fill x -pady 2
+ pack $results -expand 1 -fill both -pady 2
+
+ # File Context Index frame
+ set status_frame [$status getframe]
+ set status_buttons [ButtonBox $status_frame.bb -homogeneous 1 -padx 2]
+ $status_buttons add -text "Create and Open" -command {Apol_File_Contexts::_create_dialog}
+ $status_buttons add -text "Open" -command {Apol_File_Contexts::_open_database}
+ pack $status_buttons -side left -anchor nw -padx 2 -pady 4
+
+ set status_text [frame $status_frame.t]
+ pack $status_text -side left -anchor nw -padx 6 -pady 4
+ label $status_text.l -text "Opened Index:"
+ set status1 [label $status_text.t -textvariable Apol_File_Contexts::opts(statusText)]
+ set status2 [label $status_text.t2 -textvariable Apol_File_Contexts::opts(statusText2) -fg red]
+ trace add variable Apol_File_Contexts::opts(indexFilename) write \
+ [list Apol_File_Contexts::_changeStatusLabel $status1 $status2]
+ grid $status_text.l $status1 -sticky w
+ grid x $status2 -sticky w -pady 2
+ pack $status -side top -expand 0 -fill x -pady 2 -padx 2
+ # invoke trace to set initial label text
+ set opts(indexFilename) $opts(indexFilename)
+
+ # Search options subframes
+ set options_frame [$options getframe]
+ set show_frame [frame $options_frame.show]
+ set user_frame [frame $options_frame.user]
+ set role_frame [frame $options_frame.role]
+ set type_frame [frame $options_frame.type]
+ set range_frame [frame $options_frame.range]
+ set objclass_frame [frame $options_frame.objclass]
+ set path_frame [frame $options_frame.path]
+ grid $show_frame $user_frame $role_frame $type_frame $range_frame $objclass_frame $path_frame \
+ -padx 2 -sticky news
+ foreach idx {1 2 3 4 5} {
+ grid columnconfigure $options_frame $idx -uniform 1 -weight 0
+ }
+ grid columnconfigure $options_frame 0 -weight 0 -pad 8
+ grid columnconfigure $options_frame 6 -weight 0
+
+ set use_regexp [checkbutton $show_frame.regexp \
+ -variable Apol_File_Contexts::opts(useRegexp) \
+ -text "Search using regular expression"]
+ set show_context [checkbutton $show_frame.context \
+ -variable Apol_File_Contexts::opts(showContext) \
+ -text "Show SELinux file context"]
+ set show_objclass [checkbutton $show_frame.objclass \
+ -variable Apol_File_Contexts::opts(showObjclass) \
+ -text "Show object class"]
+ pack $use_regexp $show_context $show_objclass -side top -anchor nw
+
+ checkbutton $user_frame.enable -text "User" \
+ -variable Apol_File_Contexts::opts(useUser)
+ set widgets(user) [entry $user_frame.e -width 12 \
+ -textvariable Apol_File_Contexts::opts(user)]
+ trace add variable Apol_File_Contexts::opts(useUser) write \
+ [list Apol_File_Contexts::_toggleEnable $widgets(user)]
+ pack $user_frame.enable -side top -anchor nw
+ pack $widgets(user) -side top -anchor nw -padx 4 -expand 0 -fill x
+
+ checkbutton $role_frame.enable -text "Role" \
+ -variable Apol_File_Contexts::opts(useRole)
+ set widgets(role) [entry $role_frame.e -width 12 \
+ -textvariable Apol_File_Contexts::opts(role)]
+ trace add variable Apol_File_Contexts::opts(useRole) write \
+ [list Apol_File_Contexts::_toggleEnable $widgets(role)]
+ pack $role_frame.enable -side top -anchor nw
+ pack $widgets(role) -side top -anchor nw -padx 4 -expand 0 -fill x
+
+ checkbutton $type_frame.enable -text "Type" \
+ -variable Apol_File_Contexts::opts(useType)
+ set widgets(type) [entry $type_frame.e -width 12 \
+ -textvariable Apol_File_Contexts::opts(type)]
+ trace add variable Apol_File_Contexts::opts(useType) write \
+ [list Apol_File_Contexts::_toggleEnable $widgets(type)]
+ pack $type_frame.enable -side top -anchor nw
+ pack $widgets(type) -side top -anchor nw -padx 4 -expand 0 -fill x
+
+ checkbutton $objclass_frame.enable -text "Object class" \
+ -variable Apol_File_Contexts::opts(useObjclass)
+ set widgets(objclass) [entry $objclass_frame.e -width 12 \
+ -textvariable Apol_File_Contexts::opts(objclass)]
+ trace add variable Apol_File_Contexts::opts(useObjclass) write \
+ [list Apol_File_Contexts::_toggleEnable $widgets(objclass)]
+ pack $objclass_frame.enable -side top -anchor nw
+ pack $widgets(objclass) -side top -anchor nw -padx 4 -expand 0 -fill x
+
+ set range_cb [checkbutton $range_frame.enable \
+ -variable Apol_File_Contexts::opts(useRange) -text "MLS range"]
+ set range_entry [entry $range_frame.e -width 12 \
+ -textvariable Apol_File_Contexts::opts(range)]
+ trace add variable Apol_File_Contexts::opts(useRange) write \
+ [list Apol_File_Contexts::_toggleEnable $range_entry]
+ trace add variable Apol_File_Contexts::opts(fc_is_mls) write \
+ [list Apol_File_Contexts::_toggleRange $range_cb $range_entry]
+ pack $range_cb -side top -anchor nw
+ pack $range_entry -side top -anchor nw -padx 4 -expand 0 -fill x
+
+ checkbutton $path_frame.enable \
+ -variable Apol_File_Contexts::opts(usePath) -text "File path"
+ set path_entry [entry $path_frame.path -width 24 \
+ -textvariable Apol_File_Contexts::opts(path)]
+ trace add variable Apol_File_Contexts::opts(usePath) write \
+ [list Apol_File_Contexts::_toggleEnable $path_entry]
+ pack $path_frame.enable -side top -anchor nw
+ pack $path_entry -side top -anchor nw -padx 4 -expand 0 -fill x
+
+ set bb [ButtonBox $options_frame.bb -orient vertical -homogeneous 1 -pady 2]
+ $bb add -text OK -width 6 -command {Apol_File_Contexts::_search}
+ $bb add -text Info -width 6 -command {Apol_File_Contexts::_show_info}
+ grid $bb -row 0 -column 7 -padx 5 -pady 5 -sticky ne
+ grid columnconfigure $options_frame 7 -weight 1
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$results getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_File_Contexts::open {ppath} {
+ if {[is_db_loaded]} {
+ variable opts
+ $opts(db) associatePolicy $::ApolTop::policy
+ }
+}
+
+proc Apol_File_Contexts::close {} {
+ _close_database
+}
+
+proc Apol_File_Contexts::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+proc Apol_File_Contexts::is_db_loaded {} {
+ variable opts
+ if {$opts(db) != {}} {
+ return 1
+ }
+ return 0
+}
+
+proc Apol_File_Contexts::get_fc_files_for_ta {which ta} {
+ set q [new_sefs_query]
+ if {$which == "type"} {
+ $q type $ta 0
+ } else {
+ $q type $ta 1
+ }
+ variable opts
+ if {[catch {Apol_Progress_Dialog::wait "File Contexts" "Searching database for $ta" \
+ {
+ $opts(db) runQuery $q
+ }} results]} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message $results
+ delete_sefs_query $q
+ return {}
+ }
+ delete_sefs_query $q
+ return $results
+}
+
+#### private functions below ####
+
+proc Apol_File_Contexts::_initializeVars {} {
+ variable opts
+ variable widgets
+
+ array set opts {
+ useUser 0 user {}
+ useRole 0 role {}
+ useType 0 type {}
+ useObjclass 0 objclass {}
+ useRange 0 range {}
+ usePath 0 path {}
+
+ useRegexp 0 showContext 1 showObjclass 1
+ db {}
+ fc_is_mls 1
+ indexFilename {}
+ }
+}
+
+proc Apol_File_Contexts::_show_info {} {
+ .mainframe.frame.nb.fApol_File_Contexts.opts.f.bb.b1 configure -state disabled
+ Apol_Widget::showPopupParagraph "File Contexts Information" $Apol_File_Contexts::info_button_text
+ .mainframe.frame.nb.fApol_File_Contexts.opts.f.bb.b1 configure -state normal
+}
+
+proc Apol_File_Contexts::_changeStatusLabel {label1 label2 name1 name2 opt} {
+ variable opts
+ if {$opts(db) == {}} {
+ set opts(statusText) "No Index File Opened"
+ $label1 configure -fg red
+ set opts(statusText2) {}
+ } else {
+ set opts(statusText) $opts(indexFilename)
+ $label1 configure -fg black
+ if {$opts(fc_is_mls)} {
+ set opts(statusText2) "Database contexts include MLS ranges."
+ $label2 configure -fg black
+ } else {
+ set opts(statusText2) "Database contexts do not include MLS ranges."
+ $label2 configure -fg red
+ }
+ }
+}
+
+proc Apol_File_Contexts::_toggleEnable {entry name1 name2 op} {
+ variable opts
+ if {$opts($name2)} {
+ $entry configure -state normal -bg white
+ } else {
+ $entry configure -state disabled -bg $ApolTop::default_bg_color
+ }
+}
+
+proc Apol_File_Contexts::_toggleRange {cb entry name1 name2 op} {
+ variable opts
+ if {$opts(fc_is_mls)} {
+ $cb configure -state normal
+ if {$opts(useRange)} {
+ $entry configure -state normal -bg white
+ }
+ } else {
+ $cb configure -state disabled
+ $entry configure -state disabled -bg $ApolTop::default_bg_color
+ }
+}
+
+proc Apol_File_Contexts::_create_dialog {} {
+ variable opts
+
+ set opts(new_filename) $opts(indexFilename)
+ set opts(new_rootdir) "/"
+
+ set d [Dialog .filecontexts_create -title "Create Index File" \
+ -default 0 -cancel 1 -modal local -parent . -separator 1]
+ $d add -text "OK" -command [list Apol_File_Contexts::_create_database $d] \
+ -state disabled
+ $d add -text "Cancel"
+
+ set f [$d getframe]
+ set file_l [label $f.file_l -justify left -anchor w -text "Save index to:"]
+ set file_entry [entry $f.file_e -width 30 -bg white -takefocus 1\
+ -textvariable Apol_File_Contexts::opts(new_filename) \
+ -validate key \
+ -vcmd [list Apol_File_Contexts::_validateEntryKey %P $d new_rootdir]]
+ focus $file_entry
+ set file_browse [button $f.file_b -text "Browse" -width 8 -takefocus 1 \
+ -command [list Apol_File_Contexts::_browse_save]]
+
+ set root_l [label $f.root_l -justify left -anchor w -text "Directory to index:"]
+ set root_entry [entry $f.root_e -width 30 -bg white -takefocus 1 \
+ -textvariable Apol_File_Contexts::opts(new_rootdir) \
+ -validate key \
+ -vcmd [list Apol_File_Contexts::_validateEntryKey %P $d new_filename]]
+ set root_browse [button $f.root_b -text "Browse" -width 8 -takefocus 1 \
+ -command [list Apol_File_Contexts::_browse_root]]
+
+ grid $file_l $file_entry $file_browse -padx 4 -pady 2 -sticky ew
+ grid $root_l $root_entry $root_browse -padx 4 -pady 2 -sticky ew
+ grid columnconfigure $f 0 -weight 0
+ grid columnconfigure $f 1 -weight 1
+ grid columnconfigure $f 2 -weight 0
+
+ $d draw
+ destroy $d
+}
+
+proc Apol_File_Contexts::_browse_save {} {
+ variable opts
+ set f [tk_getSaveFile -initialfile $opts(new_filename) \
+ -parent .filecontexts_create -title "Save Index"]
+ if {$f != {}} {
+ set opts(new_filename) $f
+ }
+}
+
+proc Apol_File_Contexts::_browse_root {} {
+ variable opts
+ set f [tk_chooseDirectory -initialdir $opts(new_rootdir) \
+ -parent .filecontexts_create -title "Directory to Index"]
+ if {$f != {}} {
+ set opts(new_rootdir) $f
+ }
+}
+
+proc Apol_File_Contexts::_validateEntryKey {newvalue dialog othervar} {
+ variable opts
+ if {$newvalue == {} || $opts($othervar) == {}} {
+ $dialog itemconfigure 0 -state disabled
+ } else {
+ $dialog itemconfigure 0 -state normal
+ }
+ return 1
+}
+
+proc Apol_File_Contexts::_create_database {dialog} {
+ variable opts
+
+ if {[catch {Apol_Progress_Dialog::wait "Create Database" "Scanning $opts(new_rootdir)" \
+ {
+ set db [apol_tcl_open_database_from_dir $opts(new_rootdir)]
+ $db save $opts(new_filename)
+ set db
+ } \
+ } db] || $db == "NULL"} {
+ tk_messageBox -icon error -type ok -title "Create Database" \
+ -message [apol_tcl_get_info_string]
+ return
+ }
+ if {$opts(db) != {}} {
+ delete_sefs_fclist $opts(db)
+ }
+ _initializeVars
+ set opts(db) $db
+ set opts(fc_is_mls) [$db isMLS]
+ set opts(indexFilename) $opts(new_filename)
+ if {[ApolTop::is_policy_open]} {
+ $opts(db) associatePolicy $::ApolTop::policy
+ }
+ $dialog enddialog {}
+}
+
+proc Apol_File_Contexts::_open_database {} {
+ variable opts
+
+ set f [tk_getOpenFile -initialfile $opts(indexFilename) -parent . \
+ -title "Open Database"]
+ if {$f == {}} {
+ return
+ }
+ if {[catch {Apol_Progress_Dialog::wait "Open Database" "Opening $f" \
+ {apol_tcl_open_database $f} \
+ } db] || $db == "NULL"} {
+ tk_messageBox -icon error -type ok -title "Open Database" \
+ -message [apol_tcl_get_info_string]
+ return
+ }
+
+ if {$opts(db) != {}} {
+ delete_sefs_fclist $opts(db)
+ }
+ _initializeVars
+ set opts(db) $db
+ set opts(fc_is_mls) [$db isMLS]
+ set opts(indexFilename) $f
+ if {[ApolTop::is_policy_open]} {
+ $opts(db) associatePolicy $::ApolTop::policy
+ }
+}
+
+proc Apol_File_Contexts::_search {} {
+ variable opts
+ variable widgets
+
+ if {$opts(db) == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No database opened."
+ return
+ }
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {$opts(useUser)} {
+ if {[set user $opts(user)] == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No user selected."
+ return
+ }
+ } else {
+ set user {}
+ }
+ if {$opts(useRole)} {
+ if {[set role $opts(role)] == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No user selected."
+ return
+ }
+ } else {
+ set role {}
+ }
+ if {$opts(useType)} {
+ if {[set type $opts(type)] == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No type selected."
+ return
+ }
+ } else {
+ set type {}
+ }
+ if {$opts(fc_is_mls) && $opts(useRange)} {
+ if {[set range $opts(range)] == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No MLS range selected."
+ return
+ }
+ } else {
+ set range {}
+ }
+ if {$opts(useObjclass)} {
+ if {[set objclass $opts(objclass)] == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No object class selected."
+ return
+ }
+ } else {
+ set objclass {}
+ }
+ if {$opts(usePath)} {
+ if {[set path $opts(path)] == {}} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message "No path selected."
+ return
+ }
+ } else {
+ set path {}
+ }
+
+ set q [new_sefs_query]
+ $q user $user
+ $q role $role
+ $q type $type 0
+ $q range $range 0
+ $q objectClass $objclass
+ $q path $path
+ $q regex $opts(useRegexp)
+
+ if {[catch {Apol_Progress_Dialog::wait "File Contexts" "Searching database" \
+ {
+ set num_results [apol_tcl_query_database $opts(db) $q]
+ if {$num_results == 0} {
+ Apol_Widget::appendSearchResultText $widgets(results) "Search returned no results."
+ } else {
+ Apol_Widget::appendSearchResultHeader $widgets(results) "FILES FOUND ($num_results):\n\n"
+ }
+ }} err]} {
+ tk_messageBox -icon error -type ok -title "File Contexts" -message $err
+ }
+ delete_sefs_query $q
+}
+
+proc Apol_File_Contexts::_search_callback {entry} {
+ variable opts
+ variable widgets
+ set text {}
+ if {$opts(showContext)} {
+ set context [[$entry context] render NULL]
+ append text [format "%-40s" $context]
+ }
+ if {$opts(showObjclass)} {
+ set class [apol_objclass_to_str [$entry objectClass]]
+ append text [format " %-12s" $class]
+ }
+ append text " [$entry path]\n"
+ Apol_Widget::appendSearchResultText $widgets(results) $text
+}
+
+proc Apol_File_Contexts::_close_database {} {
+ variable opts
+ variable widgets
+ if {$opts(db) != {}} {
+ delete_sefs_fclist $opts(db)
+ }
+ _initializeVars
+ Apol_Widget::clearSearchResults $widgets(results)
+}
diff --git a/apol/file_relabel_help.txt b/apol/file_relabel_help.txt
new file mode 100644
index 0000000..39f26c6
--- /dev/null
+++ b/apol/file_relabel_help.txt
@@ -0,0 +1,68 @@
+An overview of direct file relabel analysis
+
+
+Understanding file relabel analysis
+-----------------------------------
+The permission to relabel objects in a mandatory access control system
+is an important privilege. In SELinux, this privilege is controlled
+by the relabelto and relabelfrom permissions. Understanding the net
+effect of these policy rules is complex because it requires the
+examination of multiple rules potentially spanning dozens of files.
+To be able to successfully relabel an object, a subject must be able
+to:
+
+ - relabelfrom the starting type
+ - relabelto at least one other type
+
+For example, consider the following rules:
+
+ allow sysadm_t filea_t : file relabelfrom;
+ allow sysadm_t { fileb_t filec_t } : file relabelto;
+
+If these rules are the only relabel rules present in the policy,
+sysadm_t would be allowed to relabel files of type filea_t to fileb_t
+or filec_t. Both a relabelfrom and a relabelto rule for a single
+subject must be present for a relabel to be possible.
+
+Determining the potential ending types to which a starting type can be
+relabeled requires examining all subjects for relabel rules from the
+starting type to one or more ending types. Determining the relabel
+privileges of a subject type requires examining all of the relabel
+rules containing the subject type. The direct file relabel analysis
+in apol automates both of these analyses.
+
+
+Using direct file relabel analysis in apol
+------------------------------------------
+Direct file relabel analysis is designed to facilitate querying a
+policy for both potential changes to object labels and relabel
+privileges granted to a subject. These two modes are respectively
+called Object Mode and Subject Mode.
+
+
+Object Mode
+-----------
+In Object Mode, the user specifies a starting or ending type and
+either to, from or both. When To is selected, all types to which the
+starting type can be relabeled will be displayed. When From is
+selected, all types from which the ending type can be relabeled will
+be displayed. If both options are selected, the analysis performs
+both.
+
+
+Subject Mode
+------------
+In Subject Mode, the user specifies only a subject type. Two lists of
+types will be displayed corresponding to all of the types to which the
+subject can relabel and from which the subject can relabel.
+
+
+Optional result filters
+-----------------------
+Results may be filtered in several ways. The end types resulting from
+a query may be filtered by regular expression. Advanced Filters
+provide the option of selecting which object classes to include in the
+analysis and which types to include as subjects of relabeling
+operations. The subject types filter is disabled in subject mode
+because all types are excluded as subjects except the type specified as
+the required parameter.
diff --git a/apol/find.tcl b/apol/find.tcl
new file mode 100644
index 0000000..f3cf5f5
--- /dev/null
+++ b/apol/find.tcl
@@ -0,0 +1,134 @@
+# Copyright (C) 2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Find {
+ variable dialog .apol_find_dialog
+ variable search_string {}
+ variable case_sensitive 0
+ variable enable_regexp 0
+ variable direction "down"
+}
+
+proc Apol_Find::find {} {
+ variable dialog
+ if {![winfo exists $dialog]} {
+ _create_dialog
+ } else {
+ raise $dialog
+ variable entry
+ focus $entry
+ $entry selection range 0 end
+ }
+}
+
+
+########## private functions below ##########
+
+proc Apol_Find::_create_dialog {} {
+ variable dialog
+ Dialog $dialog -title "Find" -separator 0 -parent . \
+ -side right -default 0 -cancel 1 -modal none -homogeneous 1
+ set top_frame [frame [$dialog getframe].top]
+ set bottom_frame [frame [$dialog getframe].bottom]
+ pack $top_frame -expand 1 -fill both -padx 10 -pady 5
+ pack $bottom_frame -expand 0 -fill both -padx 10 -pady 5
+
+ set entry_label [label $top_frame.l -text "Find:" -anchor e]
+ variable entry [entry $top_frame.e -bg white \
+ -textvariable Apol_Find::search_string -width 16]
+ pack $entry_label -side left -expand 0 -padx 10
+ pack $entry -side left -expand 1 -fill x
+
+ set options_frame [frame $bottom_frame.opts]
+ pack $options_frame -side left -padx 5
+ set options_case [checkbutton $options_frame.case -text "Match case" \
+ -variable Apol_Find::case_sensitive]
+ set options_regex [checkbutton $options_frame.regex -text "Regular expression" \
+ -variable Apol_Find::enable_regexp]
+ pack $options_case -anchor w
+ pack $options_regex -anchor w
+
+ set dir_frame [TitleFrame $bottom_frame.dir -text Direction]
+ pack $dir_frame -side left
+ set dir_up [radiobutton [$dir_frame getframe].up -text Up \
+ -variable Apol_Find::direction -value up]
+ set dir_down [radiobutton [$dir_frame getframe].down -text Down \
+ -variable Apol_Find::direction -value down]
+ pack $dir_up $dir_down -side left
+
+ $dialog add -text "Find Next" -command Apol_Find::_do_find
+ $dialog add -text "Cancel" -command [list destroy $dialog]
+
+ focus $entry
+ $dialog draw
+ wm resizable $dialog 0 0
+}
+
+proc Apol_Find::_do_find {} {
+ set w [ApolTop::getCurrentTextWidget]
+ if {$w == {}} {
+ return
+ }
+
+ variable search_string
+ variable case_sensitive
+ variable enable_regexp
+ variable direction
+
+ if {$search_string == {}} {
+ return
+ }
+
+ set opts {}
+ if {!$case_sensitive} {
+ lappend opts "-nocase"
+ }
+ if {$enable_regexp} {
+ lappend opts "-regexp"
+ }
+ if {$direction == "down"} {
+ lappend opts "-forward"
+ set start_pos [$w index insert]
+ } else {
+ lappend opts "-backward"
+ set start_pos [lindex [$w tag ranges sel] 0]
+ }
+ if {$start_pos == {}} {
+ set start_pos "1.0"
+ }
+
+ $w tag remove sel 0.0 end
+
+ variable dialog
+ if {[catch {eval $w search -count count $opts -- [list $search_string] $start_pos} pos]} {
+ tk_messageBox -parent $dialog -icon warning -type ok -title "Find" -message \
+ "Invalid regular expression."
+ return
+ }
+
+ if {$pos == {}} {
+ tk_messageBox -parent $dialog -icon warning -type ok -title "Find" -message \
+ "String not found."
+ } else {
+ if {$direction == "down"} {
+ $w mark set insert "$pos + $count char"
+ $w see "$pos + $count char"
+ } else {
+ $w mark set insert "$pos"
+ $w see $pos
+ }
+ $w tag add sel $pos "$pos + $count char"
+ }
+}
diff --git a/apol/foo_module.tcl b/apol/foo_module.tcl
new file mode 100644
index 0000000..2728267
--- /dev/null
+++ b/apol/foo_module.tcl
@@ -0,0 +1,143 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+## This module is not a real analysis, but an example that serves as a
+## guide to what one must do when creating a module via embedded
+## comments. This file also serves as a template for when new
+## analysis modules are created. To include this module in apol, add
+## the file name to apol_SOURCES variable in Makefile.am.
+##
+## All this module does is display an entry box and echo the contents
+## of that box.
+
+## The name space should following the convention of Apol_Analysis_XXX, where
+## XXX is a 3-4 letter name for the analysis.
+namespace eval Apol_Analysis_foo {
+ variable vals
+ variable widgets
+
+## Within the namespace command for the module, you must call
+## Apol_Analysis::registerAnalysis. The first argument is the
+## namespace name of the module, second is the descriptive display name
+## you want to be displayed in the GUI selection box.
+ Apol_Analysis::registerAnalysis "Apol_Analysis_foo" "Analysis Template Example"
+}
+
+# Called when the tool first starts up. It is given a blank frame to
+# which create its search widgets.
+proc Apol_Analysis_foo::create {options_frame} {
+ variable vals
+ set vals(entry_string) {}
+ set l [label $options_frame.l -text "Enter Text:"]
+ set e [entry $options_frame.e -textvariable Apol_Analysis_foo::vals(entry_string) -width 25 -background white]
+ pack $l $e -side left -anchor w
+}
+
+# Called when a policy is opened.
+proc Apol_Analysis_foo::open {} {
+}
+
+# Called when a policy is closed. Typically you should reset any
+# context or option variables you have.
+proc Apol_Analysis_foo::close {} {
+ variable vals
+ set vals(entry_string) {}
+}
+
+# Return a string that describes what the module does. Do not forget
+# that during compilation, blank lines are stripped; thus \n may be
+# needed within the text.
+proc Apol_Analysis_foo::getInfo {} {
+ return "This is an analysis template dialog that simply displays the content
+of the entry box. The purpose of this analysis is to provide a
+template for new analyses."
+}
+
+# Perform a new analysis. This function is responsible for obtaining
+# a new results tab if the analysis succeeds. If the analysis was
+# successful then return an empty string; otherwise return a string
+# describing the error, removing its tab if it had made one.
+proc Apol_Analysis_foo::newAnalysis {} {
+ variable vals
+ if {$vals(entry_string) == "" } {
+ return "You must enter text in the entry box."
+ }
+ set f [Apol_Analysis::createResultTab "Foo" [array get vals]]
+ set results_box [text $f.results -bg white]
+ pack $results_box -expand yes -fill both
+ $results_box insert 0.0 "new analysis: $vals(entry_string)"
+ return
+}
+
+# Update an existing analysis. The passed in frame will contain the
+# existing results; it is this function's responsibility to clear away
+# old data and to store the current search criteria onto the tab. If
+# the analysis was successful then return an empty string; otherwise
+# return a string describing the error. On error Apol_Analysis will
+# remove its tab.
+proc Apol_Analysis_foo::updateAnalysis {f} {
+ variable vals
+ if {$vals(entry_string) == "" } {
+ return "You must enter text in the entry box."
+ }
+ Apol_Analysis::setResultTabCriteria [array get vals]
+ $f.results delete 0.0 end
+ $f.results insert 0.0 "updated analysis: $vals(entry_string)"
+ return
+}
+
+# Called whenever the user hits the reset criteria button.
+proc Apol_Analysis_foo::reset {} {
+ variable vals
+ set vals(entry_string) {}
+}
+
+# Called when the user switches to this tab. The module should
+# restore its search criteria to the values that were stored within
+# the tab.
+proc Apol_Analysis_foo::switchTab {query_options} {
+ variable vals
+ array set vals $query_options
+}
+
+# Called to save the current criteria to a file channel.
+proc Apol_Analysis_foo::saveQuery {channel} {
+ variable vals
+ foreach {key value} [array get vals] {
+ puts $channel "$key $value"
+ }
+}
+
+# Called to load a query from a file channel. The module then updates
+# its display to match the criteria.
+proc Apol_Analysis_foo::loadQuery {channel} {
+ variable vals
+ while {[gets $channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ set vals($key) $value
+ }
+}
+
+# Get a text widget that contains this analysis's results. This is
+# then passed to the find dialog, goto line dialog, and so forth.
+proc Apol_Analysis_foo::getTextWidget {tab} {
+ return $tab.results
+}
diff --git a/apol/fscontexts_tab.tcl b/apol/fscontexts_tab.tcl
new file mode 100644
index 0000000..47522c2
--- /dev/null
+++ b/apol/fscontexts_tab.tcl
@@ -0,0 +1,472 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_FSContexts {
+ variable widgets
+ variable vals
+}
+
+proc Apol_FSContexts::create {tab_name nb} {
+ variable widgets
+ variable vals
+
+ _initializeVars
+
+ # Layout frames
+ set frame [$nb insert end $tab_name -text "FS Contexts"]
+ set pw [PanedWindow $frame.pw -side top -weights extra]
+ set leftf [$pw add -weight 0]
+ set rightf [$pw add -weight 1]
+ pack $pw -fill both -expand yes
+
+ # build the left column, where one selects a particular type of
+ # context; below it will be a scrolled listbox of keys for that
+ # context
+ set context_box [TitleFrame $leftf.context_f -text "Context Type"]
+ set context_f [$context_box getframe]
+ radiobutton $context_f.genfscon -text "genfscon" -value genfscon \
+ -variable Apol_FSContexts::vals(context_type)
+ radiobutton $context_f.fsuse -text "fs_use" -value fsuse \
+ -variable Apol_FSContexts::vals(context_type)
+ trace add variable Apol_FSContexts::vals(context_type) write \
+ {Apol_FSContexts::_contextTypeChanged}
+ pack $context_f.genfscon $context_f.fsuse \
+ -anchor w -expand 0 -padx 4 -pady 5
+ pack $context_box -expand 0 -fill x
+
+ set widgets(items_tf) [TitleFrame $leftf.items_f -text "GenFS Contexts"]
+ set widgets(items) [Apol_Widget::makeScrolledListbox [$widgets(items_tf) getframe].items \
+ -height 20 -width 20 -listvar Apol_FSContexts::vals(items)]
+ Apol_Widget::setListboxCallbacks $widgets(items) \
+ {{"Show Context Info" {Apol_FSContexts::_popupContextInfo}}}
+ pack $widgets(items) -expand 1 -fill both
+ pack $widgets(items_tf) -expand 1 -fill both
+
+ # build the search options
+ set optsbox [TitleFrame $rightf.optsbox -text "Search Options"]
+ pack $optsbox -side top -expand 0 -fill both -padx 2
+ set widgets(options_pm) [PagesManager [$optsbox getframe].pm]
+
+ _genfscon_create [$widgets(options_pm) add genfscon]
+ _fsuse_create [$widgets(options_pm) add fsuse]
+
+ $widgets(options_pm) compute_size
+ pack $widgets(options_pm) -expand 1 -fill both -side left
+ $widgets(options_pm) raise genfscon
+
+ set ok [button [$optsbox getframe].ok -text "OK" -width 6 \
+ -command Apol_FSContexts::_runSearch]
+ pack $ok -side right -pady 5 -padx 5 -anchor ne
+
+ # build the results box
+ set resultsbox [TitleFrame $rightf.resultsbox -text "Search Results"]
+ pack $resultsbox -expand yes -fill both -padx 2
+ set widgets(results) [Apol_Widget::makeSearchResults [$resultsbox getframe].results]
+ pack $widgets(results) -side top -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_FSContexts::open {ppath} {
+ variable vals
+
+ _genfscon_open
+ _fsuse_open
+
+ # force a flip to the genfscon page, via a trace on this variable
+ set vals(context_type) genfscon
+}
+
+proc Apol_FSContexts::close {} {
+ variable widgets
+
+ _initializeVars
+ Apol_Widget::clearSearchResults $widgets(results)
+ Apol_Widget::clearContextSelector $widgets(genfscon:context)
+ Apol_Widget::clearContextSelector $widgets(fsuse:context)
+ $widgets(genfscon:fs) configure -values {}
+ $widgets(fsuse:type) configure -values {}
+ $widgets(fsuse:fs) configure -values {}
+}
+
+proc Apol_FSContexts::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+#### private functions below ####
+
+proc Apol_FSContexts::_initializeVars {} {
+ variable vals
+ array set vals {
+ genfscon:items {}
+ genfscon:fs_enable 0 genfscon:fs {}
+ genfscon:path_enable 0 genfscon:path {}
+
+ fsuse:items {}
+ fsuse:type_enable 0 fsuse:type {}
+ fsuse:fs_enable 0 fsuse:fs {}
+
+ items {}
+ context_type genfscon
+ }
+}
+
+proc Apol_FSContexts::_contextTypeChanged {name1 name2 op} {
+ variable vals
+ variable widgets
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {$vals(context_type) == "genfscon"} {
+ _genfscon_show
+ } else {
+ _fsuse_show
+ }
+}
+
+proc Apol_FSContexts::_popupContextInfo {value} {
+ variable vals
+ if {$vals(context_type) == "genfscon"} {
+ _genfscon_popup $value
+ } else {
+ _fsuse_popup $value
+ }
+}
+
+proc Apol_FSContexts::_toggleCheckbutton {path name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals($name2)} {
+ $path configure -state normal
+ } else {
+ $path configure -state disabled
+ }
+}
+
+proc Apol_FSContexts::_runSearch {} {
+ variable vals
+ variable widgets
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ if {$vals(context_type) == "genfscon"} {
+ _genfscon_runSearch
+ } else {
+ _fsuse_runSearch
+ }
+}
+
+#### genfscon private functions below ####
+
+proc Apol_FSContexts::_genfscon_create {p_f} {
+ variable widgets
+ variable vals
+
+ set fs [frame $p_f.fs]
+ set fs_cb [checkbutton $fs.fs_enable -text "Filesystem" \
+ -variable Apol_FSContexts::vals(genfscon:fs_enable)]
+ set widgets(genfscon:fs) [ComboBox $fs.fs -entrybg white -width 12 -state disabled \
+ -textvariable Apol_FSContexts::vals(genfscon:fs) -autopost 1]
+ trace add variable Apol_FSContexts::vals(genfscon:fs_enable) write \
+ [list Apol_FSContexts::_toggleCheckbutton $widgets(genfscon:fs)]
+ pack $fs_cb -side top -anchor w
+ pack $widgets(genfscon:fs) -side top -expand 0 -fill x -padx 4
+
+ set p [frame $p_f.p]
+ set p_cb [checkbutton $p.p_enable -text "Path" \
+ -variable Apol_FSContexts::vals(genfscon:path_enable)]
+ set widgets(genfscon:path) [entry $p.path -bg white -width 24 \
+ -state disabled \
+ -textvariable Apol_FSContexts::vals(genfscon:path)]
+ trace add variable Apol_FSContexts::vals(genfscon:path_enable) write \
+ [list Apol_FSContexts::_toggleCheckbutton $widgets(genfscon:path)]
+ pack $p_cb -side top -anchor w
+ pack $widgets(genfscon:path) -side top -expand 0 -fill x -padx 4
+
+ frame $p_f.c
+ set widgets(genfscon:context) [Apol_Widget::makeContextSelector $p_f.c.context "Contexts"]
+ pack $widgets(genfscon:context)
+
+ pack $fs $p $p_f.c -side left -anchor n -padx 4 -pady 2
+}
+
+proc Apol_FSContexts::_genfscon_open {} {
+ variable vals
+
+ set q [new_apol_genfscon_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set genfscons [genfscon_vector_to_list $v]
+ set vals(genfscon:items) {}
+ foreach g $genfscons {
+ lappend vals(genfscon:items) [$g get_name $::ApolTop::qpolicy]
+ }
+ set vals(genfscon:items) [lsort -unique $vals(genfscon:items)]
+
+ # because qpol_policy_get_genfscon_iter() returns allocated items,
+ # destroying the vector before using its items will segfault
+ $v -acquire
+ $v -delete
+
+ variable widgets
+ $widgets(genfscon:fs) configure -values $vals(genfscon:items)
+}
+
+proc Apol_FSContexts::_genfscon_show {} {
+ variable vals
+ variable widgets
+ $widgets(items_tf) configure -text "GenFS Contexts"
+ $widgets(options_pm) raise genfscon
+ set vals(items) $vals(genfscon:items)
+}
+
+proc Apol_FSContexts::_genfscon_popup {fstype} {
+ set q [new_apol_genfscon_query_t]
+ $q set_filesystem $::ApolTop::policy $fstype
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set genfscons [genfscon_vector_to_list $v]
+ set text "genfs filesystem $fstype ([llength $genfscons] context"
+ if {[llength $genfscons] != 1} {
+ append text s
+ }
+ append text ")"
+ foreach g [lsort -command _genfscon_sort $genfscons] {
+ append text "\n [_genfscon_render $g]"
+ }
+ Apol_Widget::showPopupText "filesystem $fstype" $text
+
+ # because qpol_policy_get_genfscon_iter() returns allocated items,
+ # destroying the vector before using its items will segfault
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_FSContexts::_genfscon_runSearch {} {
+ variable vals
+ variable widgets
+
+ if {$vals(genfscon:fs_enable)} {
+ if {$vals(genfscon:fs) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No filesystem selected."
+ return
+ }
+ set fstype $vals(genfscon:fs_enable)
+ } else {
+ set fstype {}
+ }
+ if {$vals(genfscon:path_enable)} {
+ if {$vals(genfscon:path) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No path given."
+ return
+ }
+ set path $vals(genfscon:path)
+ } else {
+ set path {}
+ }
+
+ set q [new_apol_genfscon_query_t]
+ if {[Apol_Widget::getContextSelectorState $widgets(genfscon:context)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(genfscon:context)] {break}
+ $q set_context $::ApolTop::policy $context $range_match
+ }
+ $q set_filesystem $::ApolTop::policy $fstype
+ $q set_path $::ApolTop::policy $path
+
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set genfscons [genfscon_vector_to_list $v]
+
+ set results "GENFSCONS:"
+ if {[llength $genfscons] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach g [lsort -command _genfscon_sort $genfscons] {
+ append results "\n[_genfscon_render $g]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+
+ # because qpol_policy_get_genfscon_iter() returns allocated items,
+ # destroying the vector before using its items will segfault
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_FSContexts::_genfscon_render {qpol_genfscon_datum} {
+ apol_genfscon_render $::ApolTop::policy $qpol_genfscon_datum
+}
+
+proc Apol_FSContexts::_genfscon_sort {a b} {
+ set name_a [$a get_name $::ApolTop::qpolicy]
+ set name_b [$b get_name $::ApolTop::qpolicy]
+ if {[set z [string compare $name_a $name_b]] != 0} {
+ return $z
+ }
+ set path_a [$a get_path $::ApolTop::qpolicy]
+ set path_b [$b get_path $::ApolTop::qpolicy]
+ if {[set z [string compare $path_a $path_b]] != 0} {
+ return $z
+ }
+ return 0
+}
+
+#### fs_use private functions below ####
+
+proc Apol_FSContexts::_fsuse_create {p_f} {
+ variable widgets
+ variable vals
+
+ set t [frame $p_f.t]
+ set type_cb [checkbutton $t.type_enable -text "Statement type" \
+ -variable Apol_FSContexts::vals(fsuse:type_enable)]
+ set widgets(fsuse:type) [ComboBox $t.type -entrybg white -width 12 -state disabled \
+ -textvariable Apol_FSContexts::vals(fsuse:type) -autopost 1]
+ trace add variable Apol_FSContexts::vals(fsuse:type_enable) write \
+ [list Apol_FSContexts::_toggleCheckbutton $widgets(fsuse:type)]
+ pack $type_cb -side top -anchor w
+ pack $widgets(fsuse:type) -side top -expand 0 -fill x -padx 4
+
+ set fs [frame $p_f.fs]
+ set fs_cb [checkbutton $fs.fs_enable -text "Filesystem" \
+ -variable Apol_FSContexts::vals(fsuse:fs_enable)]
+ set widgets(fsuse:fs) [ComboBox $fs.fs -entrybg white -width 12 -state disabled \
+ -textvariable Apol_FSContexts::vals(fsuse:fs) -autopost 1]
+ trace add variable Apol_FSContexts::vals(fsuse:fs_enable) write \
+ [list Apol_FSContexts::_toggleCheckbutton $widgets(fsuse:fs)]
+ pack $fs_cb -side top -anchor w
+ pack $widgets(fsuse:fs) -side top -expand 0 -fill x -padx 4
+
+ frame $p_f.c
+ set widgets(fsuse:context) [Apol_Widget::makeContextSelector $p_f.c.context "Contexts"]
+ pack $widgets(fsuse:context)
+
+ pack $t $fs $p_f.c -side left -anchor n -padx 4 -pady 2
+}
+
+proc Apol_FSContexts::_fsuse_open {} {
+ variable vals
+
+ set q [new_apol_fs_use_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set fs_uses [lsort -unique [fs_use_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ # get a list of all behaviors present in this policy
+ set vals(fsuse:items) {}
+ set behavs {}
+ foreach f $fs_uses {
+ lappend vals(fsuse:items) [$f get_name $::ApolTop::qpolicy]
+ lappend behavs [apol_fs_use_behavior_to_str [$f get_behavior $::ApolTop::qpolicy]]
+ }
+
+ variable widgets
+ set vals(fsuse:items) [lsort -unique $vals(fsuse:items)]
+ $widgets(fsuse:type) configure -values [lsort -unique $behavs]
+ $widgets(fsuse:fs) configure -values $vals(fsuse:items)
+}
+
+proc Apol_FSContexts::_fsuse_show {} {
+ variable vals
+ variable widgets
+ $widgets(items_tf) configure -text "fs_use Contexts"
+ $widgets(options_pm) raise fsuse
+ set vals(items) $vals(fsuse:items)
+}
+
+proc Apol_FSContexts::_fsuse_popup {fs} {
+ set qpol_fs_use_datum [new_qpol_fs_use_t $::ApolTop::qpolicy $fs]
+ set text "fs_use $fs\n [_fsuse_render $qpol_fs_use_datum]"
+ Apol_Widget::showPopupText $fs $text
+}
+
+proc Apol_FSContexts::_fsuse_runSearch {} {
+ variable vals
+ variable widgets
+
+ if {$vals(fsuse:type_enable)} {
+ if {$vals(fsuse:type) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No fs_use statement type selected."
+ return
+ }
+ set behavior [apol_str_to_fs_use_behavior $vals(fsuse:type)]
+ if {$behavior < 0} {
+ tk_messageBox -icon error -type ok -title "Error" -message "$vals(fsuse:type) is not a valid fs_use statement type."
+ return
+ }
+ } else {
+ set behavior -1
+ }
+ if {$vals(fsuse:fs_enable)} {
+ if {$vals(fsuse:fs) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No filesystem selected."
+ return
+ }
+ set fstype $vals(fsuse:fs)
+ } else {
+ set fstype {}
+ }
+
+ set q [new_apol_fs_use_query_t]
+ if {[Apol_Widget::getContextSelectorState $widgets(fsuse:context)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(fsuse:context)] {break}
+ $q set_context $::ApolTop::policy $context $range_match
+ }
+ $q set_filesystem $::ApolTop::policy $fstype
+ $q set_behavior $::ApolTop::policy $behavior
+
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set fsuses [fs_use_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set results "FS_USES:"
+ if {[llength $fsuses] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach u [lsort -command _fsuse_sort $fsuses] {
+ append results "\n[_fsuse_render $u]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
+
+proc Apol_FSContexts::_fsuse_render {qpol_fs_use_datum} {
+ apol_fs_use_render $::ApolTop::policy $qpol_fs_use_datum
+}
+
+proc Apol_FSContexts::_fsuse_sort {a b} {
+ set behav_a [apol_fs_use_behavior_to_str [$a get_behavior $::ApolTop::qpolicy]]
+ set behav_b [apol_fs_use_behavior_to_str [$b get_behavior $::ApolTop::qpolicy]]
+ if {[set z [string compare $behav_a $behav_b]] != 0} {
+ return $z
+ }
+ set name_a [$a get_name $::ApolTop::qpolicy]
+ set name_b [$b get_name $::ApolTop::qpolicy]
+ if {[set z [string compare $name_a $name_b]] != 0} {
+ return $z
+ }
+ return 0
+}
diff --git a/apol/goto.tcl b/apol/goto.tcl
new file mode 100644
index 0000000..7988ebb
--- /dev/null
+++ b/apol/goto.tcl
@@ -0,0 +1,78 @@
+# Copyright (C) 2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Goto {
+ variable dialog .apol_goto_dialog
+ variable line_num
+}
+
+proc Apol_Goto::goto {} {
+ variable dialog
+ if {![winfo exists $dialog]} {
+ _create_dialog
+ } else {
+ raise $dialog
+ variable entry
+ focus $entry
+ $entry selection range 0 end
+ }
+}
+
+
+########## private functions below ##########
+
+proc Apol_Goto::_create_dialog {} {
+ variable dialog
+ Dialog $dialog -title "Goto Line" -separator 0 -parent . \
+ -default 0 -cancel 1 -modal none -homogeneous 1
+ set top_frame [$dialog getframe]
+ set entry_label [label $top_frame.l -text "Goto Line:" -anchor e]
+ variable entry [entry $top_frame.e -bg white \
+ -textvariable Apol_Goto::line_num -width 10]
+ pack $entry_label -side left -padx 5 -pady 5
+ pack $entry -side left -padx 5 -pady 5 -expand 1 -fill x
+
+ $dialog add -text "OK" -command [list Apol_Goto::_do_goto]
+ $dialog add -text "Cancel" -command [list destroy $dialog]
+
+ $entry selection range 0 end
+ focus $entry
+ $dialog draw
+ wm resizable $dialog 0 0
+}
+
+proc Apol_Goto::_do_goto {} {
+ set w [ApolTop::getCurrentTextWidget]
+ if {$w == {}} {
+ return
+ }
+
+ variable line_num
+ if {[string is integer -strict $line_num] != 1} {
+ tk_messageBox -icon error \
+ -type ok \
+ -title "Goto Line" \
+ -message "$line_num is not a valid line number."
+ } else {
+ $w tag remove sel 0.0 end
+ $w mark set insert ${line_num}.0
+ $w see ${line_num}.0
+ $w tag add sel $line_num.0 $line_num.end
+ focus $w
+ }
+
+ variable dialog
+ destroy $dialog
+}
diff --git a/apol/head.tcl b/apol/head.tcl
new file mode 100644
index 0000000..28dc44c
--- /dev/null
+++ b/apol/head.tcl
@@ -0,0 +1,29 @@
+#!/bin/sh
+# \
+exec tclsh "$0" ${1+"$@"}
+
+##############################################################
+#
+# apol: SELinux Policy Analysis Tools
+#
+# Copyright (C) 2002-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Question/comments to: setools@tresys.com
+#
+# This tool is designed to analyze SELinux policies. See the
+# assoicated help file for more information.
+#
+##############################################################
diff --git a/apol/infoflow_help.txt b/apol/infoflow_help.txt
new file mode 100644
index 0000000..fde6d42
--- /dev/null
+++ b/apol/infoflow_help.txt
@@ -0,0 +1,321 @@
+An overview of information flow analysis
+
+
+Apol supports the ability to automate the search for overt information
+flows between two types. The purpose of this analysis is to identify
+undesirable or unexpected flows of information allowed by a Type
+Enforcement (TE) policy. For example, imagine that the type shadow_t
+is assigned to the shadow password file /etc/shadow. To determine all
+the types to which information can flow from the shadow_t type (e.g,
+indicating possible paths for encrypted passwords to be
+unintentionally leaked), do a "flow from" analysis on the shadow_t
+type. Another example might be a firewall application where the
+intent is to understand all flows allowed between two network
+interfaces.
+
+Information flow analysis in SELinux is challenging for several
+reasons, including:
+
+ + The TE policy mechanism is extremely flexible, allowing for good
+ and bad flows to be easily specified, not necessarily by the
+ policy writer's intent.
+
+ + TE policies tend to be complex, with possibly tens of thousands of
+ rules and hundreds of types, making it difficult for a policy
+ writer to know all that is allowed.
+
+ + SELinux currently supports over 50 object classes and hundreds of
+ object permissions, each of which must be examined with their
+ ability to allow information flow from/to its associated object
+ class.
+
+The remainder of this file provides an overview on how apol performs
+information flow analysis.
+
+
+What Is Overt Information Flow In SELinux?
+------------------------------------------
+Information flow is defined in terms of access allowed (not
+necessarily whether that access is actually used). In SELinux, all
+objects and subjects have an associated type. Generally speaking,
+subjects can read or write objects, and thereby cause information to
+flow into and out of objects, and into and out of themselves. For
+example, given two types (say subject_t and object_t) and a subject
+(with subject_t type) able to read, but not write, an object (with
+object_t type), a rule that would allow this access might look like
+the following:
+
+ allow subject_t object_t : {file link_file} read;
+
+This case would have the following direct information flows for the
+types subject_t and object_t:
+
+ subject_t: FROM object_t
+
+ object_t: TO subject_t
+
+If this were the only rule relating to these two types, there would be
+no other direct information flows from or to either.
+
+An information flow can only occur when a subject is involved; a flow
+directly between two objects cannot exist since a subject is required
+to cause action. In SELinux, processes are generally the subject.
+There are currently 58 object classes (including processes, which are
+both subjects and objects).
+
+In apol, the subject is easy to recognize; any type that is used in
+the 'source' field of an allow rule is presumed to be associated with
+a subject, usually as the domain type of some process. The object
+type is the type used in the 'target' field of an allow rule.
+
+In the case of objects, the allow rule also explicitly identifies the
+object classes for which the rule applies. This fact results in a
+complication for analyzing information flows; specifically that flows
+between types are restricted by object classes. A flow between types
+is typically not allowed for all object classes, but for only those
+classes identified. So to be more precise, the direct information
+flows allowed by the object rules for object_t in the example above
+are:
+
+ object_t [file, link_file]: TO subject_t
+
+A perspective difference exists between source (subject) types and
+target (object) types. A read permission between a source type and a
+target type is a flow out of the target (which is being read) and flow
+into the source (which, being a process, is receiving the data being
+read into its memory).
+
+
+Object permission mappings
+--------------------------
+The above examples used 'read' permission, but described flows as 'in'
+or 'out' or 'from' and 'to'. In general, for information flow
+analysis, the only access between subjects and objects that are of
+interest, are read and write. Remembering the perspective difference
+mentioned above, read and write access results in the following flow
+for subjects (sources) and objects (targets):
+
+ SUBJECT: READ: IN flow
+ WRITE: OUT flow
+
+ OBJECT: READ: OUT flow
+ WRITE: IN flow
+
+ NOTE: A process can be either a subject or an object, so when the
+ process object class is specified in the allow rule, the target
+ type is associated with process object class and the object flow
+ rules apply.
+
+Although read and write access are the only access rights of interest
+for an information flow analysis, 'read' and 'write' permissions are
+not the only SELinux permissions of interest. The name of a
+permission does not necessarily imply whether it allows read or write
+access. Indeed, to perform an information flow analysis requires
+mapping all defined permissions for all object classes to read and
+write access.
+
+This mapping can be a difficult chore, and certainly requires
+extensive understanding of the access allowed by each of the hundreds
+of permissions currently defined. For example, the file object class
+has the 'getattr' permission defined that allows the ability to
+determine information about a file (such as date created and size).
+One could consider this a read access since the subject is reading
+information about the file. Then again this begins to feel like
+COVERT information flow analysis, where one is concerned about illicit
+signaling of information through non-traditional means (e.g.,
+signaling the critical data by varying the size of file is a covert
+flow, writing the data directly in the file so it can be read is an
+overt flow). This type of decision must be made for each defined
+object permission for each defined object class.
+
+The permission mapping mechanism in apol allows each permission to be
+mapped to read, write, both or none. In addition, the tool attempts
+to 'fix' a permission map to fit the needs of the currently opened
+policy. So, for example, if a permission map file does not map a set
+of permissions, or skips an entire object class, apol will label the
+missing permissions to "unmapped" and treat them as if they were
+mapped to 'none.' Likewise, if a map has permissions that are
+undefined in the current policy, it will ignore those mappings. In
+this way, apol continues its tradition of supporting old and new
+versions of policies (see below for more on managing permission maps).
+
+Apol provides mechanisms to manage and customize permission mappings
+that best suit the analyst's needs. Use the Tools menu (see below) to
+modify permission mappings.
+
+
+Permission weighting
+--------------------
+In addition to mapping each permission to read, write, both, or none,
+it is possible to assign the permission a weight between 1 and 10 (the
+default is 10). Apol uses this weight to rate the importance of the
+information flow this permission represents and allows the user to
+make fine-grained distinctions between high-bandwidth, overt
+information flows and low-bandwidth, or difficult to exploit, covert
+information flows. For example, the permissions "read" and "write" on
+the file object could be given a weight of 10 because they are very
+high-bandwidth information flows. Additionally, the "use" permission
+on the fd object (file descriptor) would probably be given a weight of
+1 as it is a very low-bandwidth covert flow at best. Note that a
+permission might be important for access control, like fd use, but be
+given a low weight for information flow because it cannot be used to
+pass large amounts of information.
+
+The default permission maps that are installed with apol have weights
+assigned for all of the permissions. The weights are in four general
+categories as follows:
+
+ 1 - 2 difficult to exploit covert flows (example: fd:use)
+ 3 - 5 less difficult to exploit covert flows
+ (example: process:signal)
+ 6 - 7 difficult to use, noisy, or low-bandwidth overt flows
+ (example: file:setattr)
+ 8 - 10 high-bandwidth overt flows (example: file:write)
+
+These categories are loosely defined and the placement of permissions
+into these categories is subjective. Additional work needs to be done
+to verify the accuracy of both the mappings of the permissions and the
+assigned weights.
+
+These weights are used in transitive information flow analysis to rank
+the results and to make certain that important paths between types are
+presented first. For example, consider a policy with the following
+information flows:
+
+ allow one_t two_t : file write;
+ allow three_t two_t : file read;
+ allow one_t three_t : fd use;
+
+If the permissions were mapped as described above and an analysis of
+the transitive flows from one_t to three_t were done, the analysis
+would return the path one_t->two_t->three_t first because the read and
+write permissions have a much higher weight. The direct flow between
+one_t and three_t would still be returned by the find more flows, but
+it would appear later in the list of flows.
+
+
+Types of information flow analysis
+----------------------------------
+The examples so far have only looked at 'direct' information flows.
+As its name implies, direct information flow analysis examines a
+policy for information flows that are directly permitted by one or
+more allow rules. In essence, every allow rule defines a direct
+information flow between the source and target types (for those
+allowed permissions that map to read, write, or both). The direct
+information flow analysis automates the search for these direct flows.
+
+Transitive information flow analysis attempts to link together a
+series of direct information flows to find an indirect path in which
+information can flow between two types. The results for a transitive
+closure will show one or more steps in the chain required for
+information to flow between the start and end types. Currently, the
+results will only show one such path for each end type; specifically
+the shortest path.
+
+For example, given the following rules:
+
+ allow one_t two_t : file write;
+ allow three_t two_t: file read;
+
+A direct flow analysis between one_t and three_t would not show any
+flows since no rule explicitly allows access between them. However, a
+two-step flow exists that would allow flow between these two types,
+namely one_t writing information into a file type (two_t) that three_t
+can read. These are the types of flows that the transitive analysis
+attempts to find.
+
+For both analyses, the results are presented in a less-than-desirable
+tree form (a more natural form might be a graph presentation;
+presently we are not prepared for that type of investment into the
+GUI). Each node in the tree represents a flow (in the direction
+selected) between the type of the parent node and the type of the
+node. The results window shows each step of the flow including the
+contributing access rule(s).
+
+
+Managing permission mappings
+----------------------------
+The ability to directly manage permission maps is important for the
+following reasons:
+
+ + Permission maps are central to analyzing information flows, and
+ the correctness of the map has a direct influence on the value of
+ the results.
+
+ + The mapping for individual permissions and object classes are
+ subjective, and changing permissions to alter the analysis might
+ be necessary (e.g., by unmapping certain object classes to remove
+ them from the analysis).
+
+ + The analyst may be working with several different policies each
+ with different definitions of object classes and permissions.
+
+
+Because of these reasons, apol was designed to provide great latitude
+in managing permission mappings using Tools menu. A user need not
+manage permission maps directly; apol is installed with default
+permission maps (typically in /usr/local/share/setools-<version>/)
+that will be loaded automatically when an information flow analysis is
+performed.
+
+Use the Tools menu to manually load a permission map from an arbitrary
+file. This capability allows the user to keep several versions of
+permission map files, loading the correct one for a given analysis.
+
+Although the user could view and modify mappings by editing a map file
+directly, an easier (and less error-prone) approach is apol's perm map
+viewer. Select View Perm Map from the Tools menu to display all
+object classes and permissions currently mapped (or unmapped) in the
+currently loaded policy. In addition, each permission's weight value
+is shown. These values tell apol the importance of each permission to
+the analysis. The user can configure these weight values according to
+the analysis goals. For example, the user may consider any read or
+write permissions of highest importance to the analysis, whereas
+permission to use a file descriptor may be of least importance. A
+permission will default to a weight of 10 if a weight value is not
+provided for the permission in the permission map.
+
+A user has access to the "default" permission file. If there exists a
+file named .apol_perm_mapping in his home directory (i.e.,
+$HOME/.apol_perm_mapping), then it is used when opening the default
+file. Otherwise the default file will be read from SETools's
+installed location, typically /usr/local/share/setools-<version>. The
+file .apol_perm_mapping is always used as the destination when saving
+to the default permission file.
+
+ NOTE: Only one permission map may be opened at a time, and only
+ when a policy is already opened. If apol has performed an
+ information flow analysis, the default permission map will be
+ loaded automatically unless a permission map was previously loaded.
+ Closing the policy will also close any existing permission mapping.
+ Unsaved changes will be lost.
+
+
+Finding more flows
+------------------
+For a transitive information flow, there might be many different
+information flows between two types. For example, given the
+following policy:
+
+ allow one_t two_t : file write;
+ allow three_t two_t: file read;
+ allow four_t two_t: file read;
+ allow four_t three_t: file write;
+
+In this policy, two ways exist that information can flow between one_t
+and three_t: through three_t and through three_t and four_t. In
+complicated policies, many information flows between two types can
+exist, but the initial transitive information flow analysis might not
+find all of them. For example, apol might only find the flow through
+three_t and four_t initially in the policy above. Apol provides the
+means to find more information flows between two types after the
+inital analysis is completed. In the results display for an end type,
+there is a link labeled "Find More Flows." Clicking on the link will
+bring up a dialog box that allows the user to set a maximum time
+duration and a maximum number of flows. Finding all of the paths
+between two types could take a significant amount of time for a
+complicated policy, so this dialog provides the means to set limits on
+the search. The search will stop when either of the limits are met.
+After the search completes, the additional paths will be displayed in
+the same results tab. Note that if a large number of flows are found
+it may take the display several seconds to render the text.
diff --git a/apol/initial_sids_tab.tcl b/apol/initial_sids_tab.tcl
new file mode 100644
index 0000000..ff81a32
--- /dev/null
+++ b/apol/initial_sids_tab.tcl
@@ -0,0 +1,142 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Initial_SIDS {
+ variable widgets
+ variable vals
+}
+
+proc Apol_Initial_SIDS::create {tab_name nb} {
+ variable widgets
+ variable vals
+
+ array set vals {
+ items {}
+ }
+
+ set frame [$nb insert end $tab_name -text "Initial SIDs"]
+ set pw [PanedWindow $frame.pw -side top -weights extra]
+ set leftf [$pw add -weight 0]
+ set rightf [$pw add -weight 1]
+ pack $pw -fill both -expand yes
+
+ set sids_box [TitleFrame $leftf.sids_box -text "Initial SIDs"]
+ set s_optionsbox [TitleFrame $rightf.obox -text "Search Options"]
+ set rslts_frame [TitleFrame $rightf.rbox -text "Search Results"]
+ pack $sids_box -expand 1 -fill both
+ pack $s_optionsbox -side top -expand 0 -fill both -padx 2
+ pack $rslts_frame -side top -expand yes -fill both -padx 2
+
+ set widgets(items) [Apol_Widget::makeScrolledListbox [$sids_box getframe].lb -width 20 -listvar Apol_Initial_SIDS::vals(items)]
+ Apol_Widget::setListboxCallbacks $widgets(items) \
+ {{"Display Initial SID Context" {Apol_Initial_SIDS::_popupSIDInfo}}}
+ pack $widgets(items) -expand 1 -fill both
+
+ set f [frame [$s_optionsbox getframe].c]
+ set widgets(context) [Apol_Widget::makeContextSelector $f.context "Context"]
+ pack $widgets(context)
+ pack $f -side left -anchor n -padx 4 -pady 2
+
+ set ok [button [$s_optionsbox getframe].ok -text "OK" -width 6 \
+ -command Apol_Initial_SIDS::_search]
+ pack $ok -side right -pady 5 -padx 5 -anchor ne
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$rslts_frame getframe].results]
+ pack $widgets(results) -side top -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Initial_SIDS::open {ppath} {
+ variable vals
+ set q [new_apol_isid_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set vals(items) [lsort [isid_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_Initial_SIDS::close {} {
+ variable vals
+ variable widgets
+ set vals(items) {}
+ Apol_Widget::clearSearchResults $widgets(results)
+ Apol_Widget::clearContextSelector $widgets(context)
+}
+
+proc Apol_Initial_SIDS::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+#### private functions below ####
+
+proc Apol_Initial_SIDS::_popupSIDInfo {sid} {
+ set text "$sid:\n [_render_isid $sid 1]"
+ Apol_Widget::showPopupText "$sid Context" $text
+}
+
+proc Apol_Initial_SIDS::_search {} {
+ variable vals
+ variable widgets
+
+ set name {}
+ set context {}
+ set range_match 0
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+
+ set q [new_apol_isid_query_t]
+
+ if {[Apol_Widget::getContextSelectorState $widgets(context)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(context)] {break}
+ $q set_context $::ApolTop::policy $context $range_match
+ }
+
+ set v [$q run $::ApolTop::policy] #line causing segfaulting
+
+ $q -acquire
+ $q -delete
+ set isids [isid_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set results "INITIAL SIDS:"
+ if {[llength $isids] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach i [lsort -dictionary $isids] {
+ append results "\n[_render_isid $i]"
+ }
+ }
+
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
+
+proc Apol_Initial_SIDS::_render_isid {isid_name {compact 0}} {
+ set qpol_isid_datum [new_qpol_isid_t $::ApolTop::qpolicy $isid_name]
+ set qpol_context [$qpol_isid_datum get_context $::ApolTop::qpolicy]
+ set context_str [apol_qpol_context_render $::ApolTop::policy $qpol_context]
+ if {$compact} {
+ format "sid %s %s" $isid_name $context_str
+ } else {
+ format "sid %-16s %s" $isid_name $context_str
+ }
+}
diff --git a/apol/level_dialog.tcl b/apol/level_dialog.tcl
new file mode 100644
index 0000000..181cc3c
--- /dev/null
+++ b/apol/level_dialog.tcl
@@ -0,0 +1,82 @@
+# Copyright (C) 2005-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Level_Dialog {
+ variable dialog ""
+ variable vars
+}
+
+# Create a dialog box to allow the user to select a single MLS level.
+# Return an instance of an apol_mls_level. If no level is selected
+# then return an empty list; otherwise return the level. The caller
+# must delete it afterwards.
+proc Apol_Level_Dialog::getLevel {{defaultLevel {}} {parent .}} {
+ variable dialog
+ if {![winfo exists $dialog]} {
+ _create_dialog $parent
+ }
+ set f [$dialog getframe]
+ Apol_Widget::resetLevelSelectorToPolicy $f.level
+ if {$defaultLevel != {}} {
+ Apol_Widget::setLevelSelectorLevel $f.level $defaultLevel
+ }
+ # force a recomputation of button sizes (bug in ButtonBox)
+ $dialog.bbox _redraw
+ set retval [$dialog draw]
+ if {$retval == -1 || $retval == 1} {
+ return {}
+ }
+ _get_level $dialog
+}
+
+
+########## private functions below ##########
+
+proc Apol_Level_Dialog::_create_dialog {parent} {
+ variable dialog
+ variable vars
+
+ set dialog [Dialog .level_dialog -modal local -parent $parent \
+ -separator 1 -homogeneous 1 -title "Select Level"]
+ array unset vars $dialog:*
+
+ set f [$dialog getframe]
+ set label [label $f.ll -text "Level:"]
+ set level [Apol_Widget::makeLevelSelector $f.level 12]
+
+ pack $label -anchor w
+ pack $level -expand 1 -fill both
+
+ $dialog add -text "OK" -command [list Apol_Level_Dialog::_okay $dialog]
+ $dialog add -text "Cancel"
+}
+
+proc Apol_Level_Dialog::_get_level {dialog} {
+ return [Apol_Widget::getLevelSelectorLevel [$dialog getframe].level]
+}
+
+# Check that the level is legal by validating it against the current
+# policy.
+proc Apol_Level_Dialog::_okay {dialog} {
+ set level [_get_level $dialog]
+ if {![ApolTop::is_policy_open] || [$level validate $::ApolTop::policy] != 1} {
+ tk_messageBox -icon error -type ok -title "Invalid Level" \
+ -message "The selected level is not valid for the current policy."
+ } else {
+ $dialog enddialog 0
+ }
+ $level -acquire
+ $level -delete
+}
diff --git a/apol/mls_tab.tcl b/apol/mls_tab.tcl
new file mode 100644
index 0000000..e8e5a8e
--- /dev/null
+++ b/apol/mls_tab.tcl
@@ -0,0 +1,332 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_MLS {
+ variable widgets
+ variable vals
+}
+
+proc Apol_MLS::create {tab_name nb} {
+ variable widgets
+ variable vals
+
+ _initializeVars
+
+ # Layout frames
+ set frame [$nb insert end $tab_name -text "MLS"]
+ set pw [PanedWindow $frame.pw -side top -weights extra]
+ set leftf [$pw add -weight 0]
+ set rightf [$pw add -weight 1]
+ pack $pw -fill both -expand yes
+
+ # build the left column, where one may browse sensitivities and categories
+ set sensbox [TitleFrame $leftf.sensbox -text "Sensitivities"]
+ set catsbox [TitleFrame $leftf.catsbox -text "Categories"]
+ pack $sensbox -fill both -expand 0
+ pack $catsbox -fill both -expand yes
+
+ set sensbox [Apol_Widget::makeScrolledListbox [$sensbox getframe].sens \
+ -height 10 -width 20 -listvar Apol_MLS::vals(senslist)]
+ Apol_Widget::setListboxCallbacks $sensbox \
+ {{"Show Sensitivity Info" {Apol_MLS::_popupSensInfo}}}
+ pack $sensbox -expand 1 -fill both
+ set catsbox [Apol_Widget::makeScrolledListbox [$catsbox getframe].cats \
+ -height 16 -width 20 -listvar Apol_MLS::vals(catslist)]
+ Apol_Widget::setListboxCallbacks $catsbox \
+ {{"Show Category Info" {Apol_MLS::_popupCatsInfo}}}
+ pack $catsbox -expand 1 -fill both
+
+ # build the search options
+ set optsbox [TitleFrame $rightf.optsbox -text "Search Options"]
+ pack $optsbox -side top -expand 0 -fill both -padx 2
+ set sensf [frame [$optsbox getframe].sensf]
+ set catsf [frame [$optsbox getframe].catsf]
+ pack $sensf $catsf -side left -padx 4 -pady 2 -anchor nw
+
+ set enable_sens [checkbutton $sensf.enable -text "Sensitivities" \
+ -variable Apol_MLS::vals(enable_sens)]
+ set show_cats [checkbutton $sensf.cats -text "Show levels (categories)" \
+ -variable Apol_MLS::vals(show_cats_too)]
+ trace add variable Apol_MLS::vals(enable_sens) write \
+ [list Apol_MLS::_toggleCheckbutton $show_cats]
+ pack $enable_sens -side top -anchor nw
+ pack $show_cats -side top -anchor nw -padx 8
+
+ set enable_cats [checkbutton $catsf.enable -text "Categories" \
+ -variable Apol_MLS::vals(enable_cats)]
+ set show_sens [checkbutton $catsf.cats -text "Show sensitivities" \
+ -variable Apol_MLS::vals(show_sens_too) -state disabled]
+ trace add variable Apol_MLS::vals(enable_cats) write \
+ [list Apol_MLS::_toggleCheckbutton $show_sens]
+ pack $enable_cats -side top -anchor nw
+ pack $show_sens -side top -anchor nw -padx 8
+
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry [$optsbox getframe].regexpf]
+ pack $widgets(regexp) -side left -padx 4 -pady 2 -anchor nw
+
+ set ok [button [$optsbox getframe].ok -text "OK" -width 6 \
+ -command Apol_MLS::_search]
+ pack $ok -side right -pady 5 -padx 5 -anchor ne
+
+ # build the results box
+ set resultsbox [TitleFrame $rightf.resultsbox -text "Search Results"]
+ pack $resultsbox -expand yes -fill both -padx 2
+ set widgets(results) [Apol_Widget::makeSearchResults [$resultsbox getframe].results]
+ pack $widgets(results) -side top -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_MLS::open {ppath} {
+ variable vals
+
+ set q [new_apol_level_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set vals(senslist) [lsort [level_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ set q [new_apol_cat_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set vals(catslist) [lsort [cat_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_MLS::close {} {
+ variable widgets
+
+ _initializeVars
+ Apol_Widget::clearSearchResults $widgets(results)
+}
+
+proc Apol_MLS::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+# Given a sensitivity name, return a non-empty string if that
+# sensitivity is within the loaded policy. This string is the same as
+# the given parameter if the name is a sensitivity; it will be the
+# real sensitivity's name if the parameter is an alias. If no policy
+# has been loaded then return an empty string.
+proc Apol_MLS::isSensInPolicy {sens} {
+ variable vals
+ if {![ApolTop::is_policy_open]} {
+ return {}
+ }
+ if {[lsearch $vals(senslist) $sens] >= 0} {
+ return $sens
+ }
+ # try looking up aliases
+ foreach s $vals(senslist) {
+ set qpol_level_t [new_qpol_level_t $::ApolTop::qpolicy $s]
+ set i [$qpol_level_t get_alias_iter $::ApolTop::qpolicy]
+ set l [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+ if {[lsearch $l $sens] >= 0} {
+ return $s
+ }
+ }
+ return {}
+}
+
+#### private functions below ####
+
+proc Apol_MLS::_initializeVars {} {
+ variable vals
+ array set vals {
+ senslist {} catslist {}
+ enable_sens 1 show_cats_too 1
+ enable_cats 0 show_sens_too 1
+ }
+}
+
+proc Apol_MLS::_toggleCheckbutton {path name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals($name2)} {
+ $path configure -state normal
+ } else {
+ $path configure -state disabled
+ }
+ if {$vals(enable_sens) == 0 && $vals(enable_cats) == 0} {
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 0
+ } else {
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 1
+ }
+}
+
+proc Apol_MLS::_popupSensInfo {sens} {
+ Apol_Widget::showPopupText $sens [_renderLevel $sens 1]
+}
+
+proc Apol_MLS::_popupCatsInfo {cats} {
+ Apol_Widget::showPopupText $cats [_renderCats $cats 1]
+}
+
+
+proc Apol_MLS::_renderLevel {level_name show_level} {
+ set qpol_level_datum [new_qpol_level_t $::ApolTop::qpolicy $level_name]
+ set i [$qpol_level_datum get_alias_iter $::ApolTop::qpolicy]
+ set aliases [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+
+ set text $level_name
+ if {[llength $aliases] > 0} {
+ append text " alias \{$aliases\}"
+ }
+ if {$show_level} {
+ set i [$qpol_level_datum get_cat_iter $::ApolTop::qpolicy]
+ set num_cats [$i get_size]
+ $i -acquire
+ $i -delete
+ append text " ($num_cats categor"
+ if {$num_cats == 1} {
+ append text "y)"
+ } else {
+ append text "ies)"
+ }
+ set level [new_apol_mls_level_t $::ApolTop::policy $qpol_level_datum]
+ append text "\n level [$level render $::ApolTop::policy]\n"
+ $level -acquire
+ $level -delete
+ }
+ return $text
+}
+
+proc Apol_MLS::_renderCats {cat_name show_sens} {
+ set qpol_cat_datum [new_qpol_cat_t $::ApolTop::qpolicy $cat_name]
+ set i [$qpol_cat_datum get_alias_iter $::ApolTop::qpolicy]
+ set aliases [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+
+ set text $cat_name
+ if {[llength $aliases] > 0} {
+ append text " alias \{$aliases\}"
+ }
+ if {$show_sens} {
+ append text "\n"
+ set q [new_apol_level_query_t]
+ $q set_cat $::ApolTop::policy $cat_name
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set sens_list {}
+ for {set i 0} {$i < [$v get_size]} {incr i} {
+ set qpol_level_datum [qpol_level_from_void [$v get_element $i]]
+ set level_name [$qpol_level_datum get_name $::ApolTop::qpolicy]
+ set level_value [$qpol_level_datum get_value $::ApolTop::qpolicy]
+ lappend sens_list [list $level_name $level_value]
+ }
+ $v -acquire
+ $v -delete
+ foreach s [lsort -integer -index 1 $sens_list] {
+ append text " [lindex $s 0]\n"
+ }
+ }
+ return $text
+}
+
+proc Apol_MLS::_search {} {
+ variable vals
+ variable widgets
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ if {$vals(enable_sens) == 0 && $vals(enable_cats) == 0} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No search options provided."
+ return
+ }
+ set results ""
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ if {$use_regexp} {
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$regexp == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No regular expression provided."
+ return
+ }
+ } else {
+ set regexp {}
+ }
+ if {$vals(enable_sens)} {
+ set q [new_apol_level_query_t]
+ $q set_sens $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+
+ set level_data {}
+ for {set i 0} {$i < [$v get_size]} {incr i} {
+ set qpol_level_datum [qpol_level_from_void [$v get_element $i]]
+ set level_name [$qpol_level_datum get_name $::ApolTop::qpolicy]
+ set level_value [$qpol_level_datum get_value $::ApolTop::qpolicy]
+ lappend level_data [list $level_name $level_value]
+ }
+ $v -acquire
+ $v -delete
+
+ append results "SENSITIVITIES (ordered by dominance from low to high):"
+ if {[llength $level_data] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach l [lsort -integer -index 1 $level_data] {
+ append results "\n[_renderLevel [lindex $l 0] $vals(show_cats_too)]"
+ }
+ }
+ }
+ if {$vals(enable_cats)} {
+ if {$vals(enable_sens)} {
+ append results "\n\n"
+ }
+
+ set q [new_apol_cat_query_t]
+ $q set_cat $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+
+ set cats_data {}
+ for {set i 0} {$i < [$v get_size]} {incr i} {
+ set qpol_cat_datum [qpol_cat_from_void [$v get_element $i]]
+ set cat_name [$qpol_cat_datum get_name $::ApolTop::qpolicy]
+ set cat_value [$qpol_cat_datum get_value $::ApolTop::qpolicy]
+ lappend cats_data [list $cat_name $cat_value]
+ }
+ $v -acquire
+ $v -delete
+
+ append results "CATEGORIES (ordered by appearance within policy):"
+ if {[llength $cats_data] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach c [lsort -integer -index 1 $cats_data] {
+ append results "\n[_renderCats [lindex $c 0] $vals(show_sens_too)]"
+ }
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
diff --git a/apol/netcontexts_tab.tcl b/apol/netcontexts_tab.tcl
new file mode 100644
index 0000000..d59d19c
--- /dev/null
+++ b/apol/netcontexts_tab.tcl
@@ -0,0 +1,878 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_NetContexts {
+ variable widgets
+ variable vals
+}
+
+proc Apol_NetContexts::create {tab_name nb} {
+ variable widgets
+ variable vals
+
+ _initializeVars
+
+ # Layout frames
+ set frame [$nb insert end $tab_name -text "Net Contexts"]
+ set pw [PanedWindow $frame.pw -side top -weights extra]
+ set leftf [$pw add -weight 0]
+ set rightf [$pw add -weight 1]
+ pack $pw -fill both -expand yes
+
+ # build the left column, where one selects a particular type of
+ # context; below it will be a scrolled listbox of keys for that
+ # context
+ set context_box [TitleFrame $leftf.context_f -text "Context Type"]
+ set context_f [$context_box getframe]
+ radiobutton $context_f.portcon -text "portcon" -value portcon \
+ -variable Apol_NetContexts::vals(context_type)
+ radiobutton $context_f.netifcon -text "netifcon" -value netifcon \
+ -variable Apol_NetContexts::vals(context_type)
+ radiobutton $context_f.nodecon -text "nodecon" -value nodecon \
+ -variable Apol_NetContexts::vals(context_type)
+ trace add variable Apol_NetContexts::vals(context_type) write \
+ {Apol_NetContexts::_contextTypeChanged}
+ pack $context_f.portcon $context_f.netifcon $context_f.nodecon \
+ -anchor w -expand 0 -padx 4 -pady 5
+ pack $context_box -anchor nw -expand 0 -fill x
+
+ set widgets(items_tf) [TitleFrame $leftf.items_f -text "Port Contexts"]
+ set widgets(items) [Apol_Widget::makeScrolledListbox [$widgets(items_tf) getframe].items \
+ -height 20 -width 20 -listvar Apol_NetContexts::vals(items)]
+ Apol_Widget::setListboxCallbacks $widgets(items) \
+ {{"Show Context Info" {Apol_NetContexts::_popupContextInfo}}}
+ pack $widgets(items) -expand 1 -fill both
+ pack $widgets(items_tf) -expand 1 -fill both
+
+ # build the search options
+ set optsbox [TitleFrame $rightf.optsbox -text "Search Options"]
+ pack $optsbox -side top -expand 0 -fill both -padx 2
+ set widgets(options_pm) [PagesManager [$optsbox getframe].pm]
+
+ _portcon_create [$widgets(options_pm) add portcon]
+ _netifcon_create [$widgets(options_pm) add netifcon]
+ _nodecon_create [$widgets(options_pm) add nodecon]
+
+ $widgets(options_pm) compute_size
+ pack $widgets(options_pm) -expand 1 -fill both -side left
+ $widgets(options_pm) raise portcon
+
+ set ok [button [$optsbox getframe].ok -text "OK" -width 6 \
+ -command Apol_NetContexts::_runSearch]
+ pack $ok -side right -pady 5 -padx 5 -anchor ne
+
+ set resultsbox [TitleFrame $rightf.resultsbox -text "Search Results"]
+ pack $resultsbox -expand yes -fill both -padx 2
+ set widgets(results) [Apol_Widget::makeSearchResults [$resultsbox getframe].results]
+ pack $widgets(results) -side top -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_NetContexts::open {ppath} {
+ variable vals
+
+ _portcon_open
+ _netifcon_open
+ _nodecon_open
+
+ # force a flip to the portcon page
+ set vals(context_type) portcon
+}
+
+proc Apol_NetContexts::close {} {
+ variable widgets
+
+ _initializeVars
+ Apol_Widget::clearSearchResults $widgets(results)
+ Apol_Widget::clearContextSelector $widgets(portcon:context)
+ Apol_Widget::clearContextSelector $widgets(netifcon:ifcon)
+ Apol_Widget::clearContextSelector $widgets(netifcon:msgcon)
+ Apol_Widget::clearContextSelector $widgets(nodecon:context)
+ $widgets(portcon:proto) configure -values {}
+ $widgets(netifcon:dev) configure -values {}
+}
+
+proc Apol_NetContexts::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+#### private functions below ####
+
+proc Apol_NetContexts::_initializeVars {} {
+ variable vals
+ array set vals {
+ portcon:items {}
+ portcon:proto_enable 0 portcon:proto {}
+ portcon:port_enable 0 portcon:port 0
+ portcon:hiport_enable 0 portcon:hiport 0
+
+ netifcon:items {}
+ netifcon:dev_enable 0 netifcon:dev {}
+
+ nodecon:items {}
+ nodecon:ip_type ipv4
+ nodecon:ipv4_addr_enable 0
+ nodecon:ipv4_addr0 0 nodecon:ipv4_addr1 0
+ nodecon:ipv4_addr2 0 nodecon:ipv4_addr3 0
+ nodecon:ipv4_mask_enable 0
+ nodecon:ipv4_mask0 255 nodecon:ipv4_mask1 255
+ nodecon:ipv4_mask2 255 nodecon:ipv4_mask3 255
+ nodecon:ipv6_addr_enable 0 nodecon:ipv6_addr ::
+ nodecon:ipv6_mask_enable 0 nodecon:ipv6_mask ::
+
+ items {}
+ context_type portcon
+ }
+}
+
+proc Apol_NetContexts::_contextTypeChanged {name1 name2 op} {
+ variable vals
+ variable widgets
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {$vals(context_type) == "portcon"} {
+ _portcon_show
+ } elseif {$vals(context_type) == "netifcon"} {
+ _netifcon_show
+ } else {
+ _nodecon_show
+ }
+}
+
+proc Apol_NetContexts::_popupContextInfo {value} {
+ variable vals
+ if {$vals(context_type) == "portcon"} {
+ _portcon_popup $value
+ } elseif {$vals(context_type) == "netifcon"} {
+ _netifcon_popup $value
+ } else {
+ _nodecon_popup $value
+ }
+}
+
+proc Apol_NetContexts::_toggleCheckbutton {path name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals($name2)} {
+ $path configure -state normal
+ } else {
+ $path configure -state disabled
+ }
+}
+
+proc Apol_NetContexts::_runSearch {} {
+ variable vals
+ variable widgets
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ if {$vals(context_type) == "portcon"} {
+ _portcon_runSearch
+ } elseif {$vals(context_type) == "netifcon"} {
+ _netifcon_runSearch
+ } else {
+ _nodecon_runSearch
+ }
+}
+
+#### portcon private functions below ####
+
+# create the portcon-specific options widgets
+proc Apol_NetContexts::_portcon_create {p_f} {
+ variable widgets
+ variable vals
+
+ frame $p_f.proto
+ set proto_cb [checkbutton $p_f.proto.proto_enable -text "Protocol" \
+ -variable Apol_NetContexts::vals(portcon:proto_enable)]
+ set widgets(portcon:proto) [ComboBox $p_f.proto.proto -entrybg white -width 8 -state disabled \
+ -textvariable Apol_NetContexts::vals(portcon:proto) -autopost 1]
+ trace add variable Apol_NetContexts::vals(portcon:proto_enable) write \
+ [list Apol_NetContexts::_toggleCheckbutton $widgets(portcon:proto)]
+ pack $proto_cb -side top -anchor w
+ pack $widgets(portcon:proto) -side top -expand 0 -fill x -padx 4
+
+ frame $p_f.port
+ set low [frame $p_f.port.l]
+ set port_cb [checkbutton $low.port_enable -text "Single Port" \
+ -variable Apol_NetContexts::vals(portcon:port_enable)]
+ set widgets(portcon:port) [spinbox $low.port -bg white -width 8 \
+ -justify right -state disabled \
+ -from 0 -to 65535 \
+ -validate all -vcmd [list Apol_NetContexts::_portcon_limitPort %W %V %P port] \
+ -textvariable Apol_NetContexts::vals(portcon:port)]
+ set high [frame $p_f.port.h]
+ set hiport_cb [checkbutton $high.hiport_enable -text "High Port" \
+ -state disabled \
+ -variable Apol_NetContexts::vals(portcon:hiport_enable)]
+ set widgets(portcon:hiport) [spinbox $high.hiport -bg white -width 8 \
+ -justify right -state disabled \
+ -from 0 -to 65535 \
+ -validate all -vcmd [list Apol_NetContexts::_portcon_limitPort %W %V %P hiport] \
+ -textvariable Apol_NetContexts::vals(portcon:hiport)]
+ trace add variable Apol_NetContexts::vals(portcon:port_enable) write \
+ [list Apol_NetContexts::_portcon_toggleCheckbutton_lowport \
+ $widgets(portcon:port) $hiport_cb $widgets(portcon:hiport)]
+ trace add variable Apol_NetContexts::vals(portcon:hiport_enable) write \
+ [list Apol_NetContexts::_portcon_toggleCheckbutton_hiport $port_cb $widgets(portcon:hiport)]
+ pack $port_cb -side top -anchor w -expand 0
+ pack $widgets(portcon:port) -side top -expand 0 -fill x -padx 4
+ pack $hiport_cb -side top -anchor w -expand 0
+ pack $widgets(portcon:hiport) -side top -expand 0 -fill x -padx 4
+ pack $low $high -side left -expand 0 -fill both
+
+ frame $p_f.c
+ set widgets(portcon:context) [Apol_Widget::makeContextSelector $p_f.c.context "Contexts"]
+ pack $widgets(portcon:context)
+ pack $p_f.proto $p_f.port $p_f.c -side left -padx 4 -pady 2 -anchor nw
+}
+
+proc Apol_NetContexts::_portcon_open {} {
+ variable vals
+
+ set q [new_apol_portcon_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set portcons [portcon_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ set vals(portcon:items) {}
+ set protos {}
+ foreach p $portcons {
+ set low [$p get_low_port $::ApolTop::qpolicy]
+ set high [$p get_high_port $::ApolTop::qpolicy]
+ set proto [$p get_protocol $::ApolTop::qpolicy]
+ if {$low == $high} {
+ lappend vals(portcon:items) $low
+ } else {
+ lappend vals(portcon:items) "$low-$high"
+ }
+ lappend protos [apol_protocol_to_str $proto]
+ }
+
+ variable widgets
+ set vals(portcon:items) [lsort -unique -dictionary $vals(portcon:items)]
+ $widgets(portcon:proto) configure -values [lsort -unique -dictionary $protos]
+}
+
+proc Apol_NetContexts::_portcon_show {} {
+ variable vals
+ variable widgets
+ $widgets(items_tf) configure -text "Port Contexts"
+ $widgets(options_pm) raise portcon
+ set vals(items) $vals(portcon:items)
+}
+
+proc Apol_NetContexts::_portcon_popup {port} {
+ foreach {low high} [split $port "-"] {break}
+ if {$high == {}} {
+ set high $low
+ }
+
+ set q [new_apol_portcon_query_t]
+ $q set_low $::ApolTop::policy $low
+ $q set_high $::ApolTop::policy $high
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set portcons [portcon_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set text "port $port ([llength $portcons] context"
+ if {[llength $portcons] != 1} {
+ append text s
+ }
+ append text ")"
+ foreach p [lsort -command _portcon_sort $portcons] {
+ append text "\n [_portcon_render $p]"
+ }
+ Apol_Widget::showPopupText "port $port" $text
+}
+
+proc Apol_NetContexts::_portcon_limitPort {widget command new_port varname} {
+ variable vals
+ if {$command == "key"} {
+ if {$new_port != "" &&
+ (![string is integer $new_port] || $new_port < 0 || $new_port > 65535)} {
+ return 0
+ }
+ } elseif {$command == "focusout"} {
+ if {$new_port == ""} {
+ set vals(portcon:$varname) 0
+ } elseif {[string length $new_port] > 1} {
+ set vals(portcon:$varname) [string trimleft $new_port " 0"]
+ }
+
+ # re-enable the validation command (it could have been
+ # disabled because the variable changed)
+ $widget config -validate all
+ }
+ return 1
+}
+
+proc Apol_NetContexts::_portcon_toggleCheckbutton_lowport {low high_cb high name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals($name2)} {
+ $low configure -state normal
+ $high_cb configure -state normal
+ if {$vals(portcon:hiport_enable)} {
+ $high configure -state normal
+ }
+ } else {
+ $low configure -state disabled
+ $high_cb configure -state disabled
+ $high configure -state disabled
+ }
+}
+
+proc Apol_NetContexts::_portcon_toggleCheckbutton_hiport {low high name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals($name2)} {
+ $low configure -text "Low Port"
+ $high configure -state normal
+ } else {
+ $low configure -text "Single Port"
+ $high configure -state disabled
+ }
+}
+
+proc Apol_NetContexts::_portcon_runSearch {} {
+ variable vals
+ variable widgets
+
+ # explicitly validate the spinboxes (they could still have focus)
+ _portcon_limitPort $widgets(portcon:port) focusout $vals(portcon:port) port
+ _portcon_limitPort $widgets(portcon:hiport) focusout $vals(portcon:hiport) hiport
+
+ if {$vals(portcon:port_enable)} {
+ set low $vals(portcon:port)
+ set high $low
+ if {$vals(portcon:hiport_enable)} {
+ set high $vals(portcon:hiport)
+ if {$vals(portcon:port_enable) && $high < $low} {
+ tk_messageBox -icon error -type ok -title "Error" -message "The second port is not greater than the first."
+ return
+ }
+ }
+ } else {
+ set low -1
+ set high -1
+ }
+
+ set q [new_apol_portcon_query_t]
+ $q set_low $::ApolTop::policy $low
+ $q set_high $::ApolTop::policy $high
+ if {$vals(portcon:proto_enable)} {
+ if {[set proto $vals(portcon:proto)] == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No protocol selected."
+ return
+ }
+ $q set_protocol $::ApolTop::policy [apol_str_to_protocol $proto]
+ }
+ if {[Apol_Widget::getContextSelectorState $widgets(portcon:context)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(portcon:context)] {break}
+ $q set_context $::ApolTop::policy $context $range_match
+ }
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set portcons [portcon_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set results "PORTCONS:"
+ if {[llength $portcons] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach p [lsort -command _portcon_sort $portcons] {
+ append results "\n[_portcon_render $p]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
+
+proc Apol_NetContexts::_portcon_render {qpol_portcon_datum} {
+ set loport [$qpol_portcon_datum get_low_port $::ApolTop::qpolicy]
+ set hiport [$qpol_portcon_datum get_high_port $::ApolTop::qpolicy]
+ set proto [apol_protocol_to_str [$qpol_portcon_datum get_protocol $::ApolTop::qpolicy]]
+ set qpol_context [$qpol_portcon_datum get_context $::ApolTop::qpolicy]
+ if {$loport == $hiport} {
+ set line "portcon $proto $loport "
+ } else {
+ set line "portcon $proto ${loport}-${hiport} "
+ }
+ concat $line [apol_qpol_context_render $::ApolTop::policy $qpol_context]
+}
+
+proc Apol_NetContexts::_portcon_sort {a b} {
+ set loport1 [$a get_low_port $::ApolTop::qpolicy]
+ set hiport1 [$a get_high_port $::ApolTop::qpolicy]
+ set loport2 [$b get_low_port $::ApolTop::qpolicy]
+ set hiport2 [$b get_high_port $::ApolTop::qpolicy]
+ if {$loport1 == $hiport1} {
+ set singleport1 1
+ } else {
+ set singleport1 0
+ }
+ if {$loport2 == $hiport2} {
+ set singleport2 1
+ } else {
+ set singleport2 0
+ }
+ if {$singleport1 && !$singleport2} {
+ return -1
+ } elseif {!$singleport1 && $singleport2} {
+ return 1
+ }
+ if {$loport1 < $loport2} {
+ return -1
+ } elseif {$loport1 > $loport2} {
+ return 1
+ }
+ if {$hiport1 < $hiport2} {
+ return -1
+ } elseif {$hiport1 > $hiport2} {
+ return 1
+ }
+ set proto1 [apol_protocol_to_str [$a get_protocol $::ApolTop::qpolicy]]
+ set proto2 [apol_protocol_to_str [$b get_protocol $::ApolTop::qpolicy]]
+ string compare $proto1 $proto2
+}
+
+#### netifcon private functions below ####
+
+proc Apol_NetContexts::_netifcon_create {p_f} {
+ variable vals
+ variable widgets
+
+ frame $p_f.dev
+ set dev_cb [checkbutton $p_f.dev.dev_enable -text "Device" \
+ -variable Apol_NetContexts::vals(netifcon:dev_enable)]
+ set widgets(netifcon:dev) [ComboBox $p_f.dev.dev -entrybg white -width 8 -state disabled \
+ -textvariable Apol_NetContexts::vals(netifcon:dev) -autopost 1]
+ trace add variable Apol_NetContexts::vals(netifcon:dev_enable) write \
+ [list Apol_NetContexts::_toggleCheckbutton $widgets(netifcon:dev)]
+ pack $dev_cb -side top -anchor w
+ pack $widgets(netifcon:dev) -side top -expand 0 -fill x -padx 4
+
+ frame $p_f.ifcon
+ set widgets(netifcon:ifcon) [Apol_Widget::makeContextSelector $p_f.ifcon.context "Contexts" "Interface context" -width 18]
+ pack $widgets(netifcon:ifcon)
+
+ frame $p_f.msgcon
+ set widgets(netifcon:msgcon) [Apol_Widget::makeContextSelector $p_f.msgcon.context "Contexts" "Message context" -width 18]
+ pack $widgets(netifcon:msgcon)
+
+ pack $p_f.dev $p_f.ifcon $p_f.msgcon -side left -padx 4 -pady 2 -anchor nw
+}
+
+proc Apol_NetContexts::_netifcon_open {} {
+ variable vals
+
+ set q [new_apol_netifcon_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set vals(netifcon:items) [lsort [netifcon_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ variable widgets
+ $widgets(netifcon:dev) configure -values $vals(netifcon:items)
+}
+
+proc Apol_NetContexts::_netifcon_show {} {
+ variable vals
+ variable widgets
+ $widgets(items_tf) configure -text "NetIF Contexts"
+ $widgets(options_pm) raise netifcon
+ set vals(items) $vals(netifcon:items)
+}
+
+proc Apol_NetContexts::_netifcon_popup {netif} {
+ set text "network interface $netif"
+ append text "\n [_netifcon_render $netif]"
+ Apol_Widget::showPopupText "interface $netif" $text
+}
+
+proc Apol_NetContexts::_netifcon_runSearch {} {
+ variable vals
+ variable widgets
+
+ if {$vals(netifcon:dev_enable)} {
+ if {$vals(netifcon:dev) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No device selected."
+ return
+ }
+ set dev $vals(netifcon:dev)
+ } else {
+ set dev {}
+ }
+
+ set q [new_apol_netifcon_query_t]
+ $q set_device $::ApolTop::policy $dev
+ if {[Apol_Widget::getContextSelectorState $widgets(netifcon:ifcon)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(netifcon:ifcon)] {break}
+ $q set_if_context $::ApolTop::policy $context $range_match
+ }
+ if {[Apol_Widget::getContextSelectorState $widgets(netifcon:msgcon)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(netifcon:msgcon)] {break}
+ $q set_msg_context $::ApolTop::policy $context $range_match
+ }
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set netifcons [netifcon_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set results "NETIFCONS:"
+ if {[llength $netifcons] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach n [lsort $netifcons] {
+ append results "\n[_netifcon_render $n]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
+
+proc Apol_NetContexts::_netifcon_render {netifcon} {
+ set qpol_netifcon_datum [new_qpol_netifcon_t $::ApolTop::qpolicy $netifcon]
+ apol_netifcon_render $::ApolTop::policy $qpol_netifcon_datum
+}
+
+#### nodecon private functions below ####
+
+proc Apol_NetContexts::_nodecon_create {p_f} {
+ variable vals
+ variable widgets
+
+ frame $p_f.ip_type
+ set ipv4_rb [radiobutton $p_f.ip_type.v4 -text "IPv4" -value ipv4 \
+ -variable Apol_NetContexts::vals(nodecon:ip_type)]
+ set ipv6_rb [radiobutton $p_f.ip_type.v6 -text "IPv6" -value ipv6 \
+ -variable Apol_NetContexts::vals(nodecon:ip_type)]
+ trace add variable Apol_NetContexts::vals(nodecon:ip_type) write \
+ [list Apol_NetContexts::_nodecon_pageChanged]
+ pack $ipv4_rb $ipv6_rb -side top -anchor nw -pady 5
+
+ frame $p_f.opts
+ set widgets(nodecon:ip_pm) [PagesManager $p_f.opts.pm]
+ _nodecon_ipv4Create [$widgets(nodecon:ip_pm) add ipv4]
+ _nodecon_ipv6Create [$widgets(nodecon:ip_pm) add ipv6]
+ $widgets(nodecon:ip_pm) compute_size
+ pack $widgets(nodecon:ip_pm)
+ $widgets(nodecon:ip_pm) raise ipv4
+
+ frame $p_f.con
+ set widgets(nodecon:context) [Apol_Widget::makeContextSelector $p_f.con.context "Contexts"]
+ pack $widgets(nodecon:context)
+
+ pack $p_f.ip_type $p_f.opts $p_f.con -side left -padx 4 -pady 2 -anchor nw
+}
+
+proc Apol_NetContexts::_nodecon_open {} {
+ set q [new_apol_nodecon_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set nodecons [nodecon_vector_to_list $v]
+
+ variable vals
+ variable widgets
+ set vals(nodecon:items) {}
+ foreach n [lsort -command _nodecon_sort $nodecons] {
+ set proto [$n get_protocol $::ApolTop::qpolicy]
+ set addr [$n get_addr $::ApolTop::qpolicy]
+ if {$proto == $::QPOL_IPV4} {
+ set addr [apol_ipv4_addr_render $::ApolTop::policy $addr]
+ } elseif {$proto == $::QPOL_IPV6} {
+ set addr [apol_ipv6_addr_render $::ApolTop::policy $addr]
+ } else {
+ puts stderr "Unknown protocol $proto"
+ exit -1
+ }
+ lappend vals(nodecon:items) $addr
+ }
+ set vals(nodecon:items) [lsort -unique -dictionary $vals(nodecon:items)]
+
+ # because qpol_policy_get_nodecon_iter() returns allocated items,
+ # destroying the vector before using its items will segfault
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_NetContexts::_nodecon_show {} {
+ variable vals
+ variable widgets
+ $widgets(items_tf) configure -text "Node Contexts"
+ $widgets(options_pm) raise nodecon
+ set vals(items) $vals(nodecon:items)
+}
+
+proc Apol_NetContexts::_nodecon_ipv4Create {fv4} {
+ variable widgets
+ set v4addrf [frame $fv4.addr]
+ set ipv4_addr_cb [checkbutton $v4addrf.enable -text "IP address" \
+ -variable Apol_NetContexts::vals(nodecon:ipv4_addr_enable)]
+ set widgets(nodecon:v4addrf2) [frame $v4addrf.a]
+ for {set i 0} {$i < 4} {incr i} {
+ set e [entry $widgets(nodecon:v4addrf2).e$i -bg white -justify center -width 4 \
+ -state disabled \
+ -validate all -vcmd [list Apol_NetContexts::_nodecon_limitAddr %W %V %P ipv4_addr$i] \
+ -textvariable Apol_NetContexts::vals(nodecon:ipv4_addr$i)]
+ pack $e -side left -padx 1 -anchor center
+ if {$i < 3} {
+ pack [label $widgets(nodecon:v4addrf2).l$i -text "."] -side left -expand 0 -anchor s
+ }
+ }
+ trace add variable Apol_NetContexts::vals(nodecon:ipv4_addr_enable) write \
+ [list Apol_NetContexts::_nodecon_toggleV4button $widgets(nodecon:v4addrf2).e]
+ pack $ipv4_addr_cb -anchor w
+ pack $widgets(nodecon:v4addrf2) -padx 3 -expand 0 -fill x
+
+ set v4maskf [frame $fv4.mask]
+ set ipv4_mask_cb [checkbutton $v4maskf.enable -text "Mask" \
+ -variable Apol_NetContexts::vals(nodecon:ipv4_mask_enable)]
+ set widgets(nodecon:v4maskf2) [frame $v4maskf.m]
+ for {set i 0} {$i < 4} {incr i} {
+ set e [entry $widgets(nodecon:v4maskf2).e$i -bg white -justify center -width 4 \
+ -state disabled \
+ -validate all -vcmd [list Apol_NetContexts::_nodecon_limitAddr %W %V %P ipv4_mask$i] \
+ -textvariable Apol_NetContexts::vals(nodecon:ipv4_mask$i)]
+ pack $e -side left -padx 1 -anchor center
+ if {$i < 3} {
+ pack [label $widgets(nodecon:v4maskf2).l$i -text "."] -side left -expand 0 -anchor s
+ }
+ }
+ trace add variable Apol_NetContexts::vals(nodecon:ipv4_mask_enable) write \
+ [list Apol_NetContexts::_nodecon_toggleV4button $widgets(nodecon:v4maskf2).e]
+ pack $ipv4_mask_cb -anchor w
+ pack $widgets(nodecon:v4maskf2) -padx 3 -expand 0 -fill x
+
+ pack $v4addrf $v4maskf -padx 4 -pady 2 -anchor nw
+}
+
+proc Apol_NetContexts::_nodecon_ipv6Create {fv6} {
+ set v6addrf [frame $fv6.addr]
+ set ipv4_addr_cb [checkbutton $v6addrf.enable -text "IP address" \
+ -variable Apol_NetContexts::vals(nodecon:ipv6_addr_enable)]
+ set e [entry $v6addrf.addr -bg white -width 28 -state disabled \
+ -textvariable Apol_NetContexts::vals(nodecon:ipv6_addr)]
+ trace add variable Apol_NetContexts::vals(nodecon:ipv6_addr_enable) write \
+ [list Apol_NetContexts::_toggleCheckbutton $e]
+ pack $ipv4_addr_cb -anchor w
+ pack $e -padx 4 -expand 0 -fill x
+
+ set v6maskf [frame $fv6.mask]
+ set ipv6_mask_cb [checkbutton $v6maskf.enable -text "Mask" \
+ -variable Apol_NetContexts::vals(nodecon:ipv6_mask_enable)]
+ set e [entry $v6maskf.addr -bg white -width 28 -state disabled \
+ -textvariable Apol_NetContexts::vals(nodecon:ipv6_mask)]
+ trace add variable Apol_NetContexts::vals(nodecon:ipv6_mask_enable) write \
+ [list Apol_NetContexts::_toggleCheckbutton $e]
+ pack $ipv6_mask_cb -anchor w
+ pack $e -padx 4 -expand 0 -fill x
+
+ pack $v6addrf $v6maskf -padx 4 -pady 2 -anchor w
+}
+
+proc Apol_NetContexts::_nodecon_pageChanged {name1 name2 op} {
+ variable vals
+ variable widgets
+ $widgets(nodecon:ip_pm) raise $vals(nodecon:ip_type)
+}
+
+proc Apol_NetContexts::_nodecon_limitAddr {widget command new_addr varname} {
+ variable vals
+ if {$command == "key"} {
+ if {$new_addr != "" &&
+ (![string is integer $new_addr] || $new_addr < 0 || $new_addr > 255)} {
+ return 0
+ }
+ } elseif {$command == "focusout"} {
+ if {$new_addr == ""} {
+ set vals(nodecon:$varname) 0
+ } elseif {[string length $new_addr] > 1} {
+ set vals(nodecon:$varname) [string trimleft $new_addr " 0"]
+ }
+
+ # re-enable the validation command (it could have been
+ # disabled because the variable changed)
+ after idle [list $widget config -validate all]
+ }
+ return 1
+}
+
+proc Apol_NetContexts::_nodecon_toggleV4button {path name1 name2 op} {
+ variable vals
+ if {$vals($name2)} {
+ for {set i 0} {$i < 4} {incr i} {
+ ${path}${i} configure -state normal
+ }
+ } else {
+ for {set i 0} {$i < 4} {incr i} {
+ ${path}${i} configure -state disabled
+ }
+ }
+}
+
+proc Apol_NetContexts::_nodecon_popup {nodecon_addr} {
+ set q [new_apol_nodecon_query_t]
+ set ip [apol_str_to_internal_ip $nodecon_addr]
+ $q set_addr $::ApolTop::policy $ip
+ $ip -acquire
+ $ip -delete
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set nodecons [nodecon_vector_to_list $v]
+
+ set text "nodecon $nodecon_addr ([llength $nodecons] context"
+ if {[llength $nodecons] != 1} {
+ append text s
+ }
+ append text ")"
+ foreach n [lsort -command _nodecon_sort $nodecons] {
+ append text "\n [_nodecon_render $n]"
+ }
+ Apol_Widget::showPopupText "address $nodecon_addr" $text
+
+ # because qpol_policy_get_nodecon_iter() returns allocated items,
+ # destroying the vector before using its items will segfault
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_NetContexts::_nodecon_runSearch {} {
+ variable vals
+ variable widgets
+
+ set addr {}
+ set mask {}
+ if {$vals(nodecon:ip_type) == "ipv4"} {
+ # explicitly validate the entries (they could still have focus)
+ foreach i {0 1 2 3} {
+ _nodecon_limitAddr $widgets(nodecon:v4addrf2).e$i focusout $vals(nodecon:ipv4_addr$i) ipv4_addr$i
+ _nodecon_limitAddr $widgets(nodecon:v4maskf2).e$i focusout $vals(nodecon:ipv4_mask$i) ipv4_mask$i
+ }
+ if {$vals(nodecon:ipv4_addr_enable)} {
+ set addr [format "%d.%d.%d.%d" \
+ $vals(nodecon:ipv4_addr0) $vals(nodecon:ipv4_addr1) \
+ $vals(nodecon:ipv4_addr2) $vals(nodecon:ipv4_addr3)]
+ }
+ if {$vals(nodecon:ipv4_mask_enable)} {
+ set mask [format "%d.%d.%d.%d" \
+ $vals(nodecon:ipv4_mask0) $vals(nodecon:ipv4_mask1) \
+ $vals(nodecon:ipv4_mask2) $vals(nodecon:ipv4_mask3)]
+ }
+ set proto $::QPOL_IPV4
+ } else {
+ if {$vals(nodecon:ipv6_addr_enable)} {
+ if {[set addr $vals(nodecon:ipv6_addr)] == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No IPV6 address provided."
+ return
+ }
+ }
+ if {$vals(nodecon:ipv6_mask_enable)} {
+ if {[set mask $vals(nodecon:ipv6_mask)] == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No IPV6 address provided."
+ return
+ }
+ }
+ set proto $::QPOL_IPV6
+ }
+
+ set q [new_apol_nodecon_query_t]
+ $q set_protocol $::ApolTop::policy $proto
+ if {$addr != {}} {
+ if {[catch {apol_str_to_internal_ip $addr} u]} {
+ tk_messageBox -icon error -type ok -title "Error" -message $u
+ return
+ }
+ $q set_addr $::ApolTop::policy $u
+ }
+ if {$mask != {}} {
+ if {[catch {apol_str_to_internal_ip $mask} u]} {
+ tk_messageBox -icon error -type ok -title "Error" -message $u
+ return
+ }
+ $q set_mask $::ApolTop::policy $u
+ }
+ if {[Apol_Widget::getContextSelectorState $widgets(nodecon:context)]} {
+ foreach {context range_match attribute} [Apol_Widget::getContextSelectorValue $widgets(nodecon:context)] {break}
+ $q set_context $::ApolTop::policy $context $range_match
+ }
+
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set nodecons [nodecon_vector_to_list $v]
+
+ set results "NODECONS:"
+ if {[llength $nodecons] == 0} {
+ append results "\nSearch returned no results."
+ } else {
+ foreach n [lsort -command _nodecon_sort $nodecons] {
+ append results "\n[_nodecon_render $n]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+
+ # because qpol_policy_get_nodecon_iter() returns allocated items,
+ # destroying the vector before using its items will segfault
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_NetContexts::_nodecon_render {qpol_nodecon_datum} {
+ apol_nodecon_render $::ApolTop::policy $qpol_nodecon_datum
+}
+
+# Sort nodecons, grouping ipv4 before ipv6. Then sort by address and
+# then mask.
+proc Apol_NetContexts::_nodecon_sort {a b} {
+ set proto1 [$a get_protocol $::ApolTop::qpolicy]
+ set proto2 [$b get_protocol $::ApolTop::qpolicy]
+ if {$proto1 == $::QPOL_IPV4 && $proto2 == $::QPOL_IPV6} {
+ return -1
+ } elseif {$proto1 == $::QPOL_IPV6 && $proto1 == $::QPOL_IPV4} {
+ return 0
+ }
+
+ if {$proto1 == $::QPOL_IPV4} {
+ set render apol_ipv4_addr_render
+ } else {
+ set render apol_ipv6_addr_render
+ }
+ set addr1 [$render $::ApolTop::policy [$a get_addr $::ApolTop::qpolicy]]
+ set addr2 [$render $::ApolTop::policy [$b get_addr $::ApolTop::qpolicy]]
+ if {[set x [string compare $addr1 $addr2]] != 0} {
+ return $x
+ }
+
+ set mask1 [$render $::ApolTop::policy [$a get_mask $::ApolTop::qpolicy]]
+ set mask2 [$render $::ApolTop::policy [$b get_mask $::ApolTop::qpolicy]]
+ string compare $mask1 $mask2
+}
diff --git a/apol/open_policy_dialog.tcl b/apol/open_policy_dialog.tcl
new file mode 100644
index 0000000..217ca85
--- /dev/null
+++ b/apol/open_policy_dialog.tcl
@@ -0,0 +1,388 @@
+# Copyright (C) 2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Open_Policy_Dialog {
+ variable dialog {}
+ variable widgets
+ variable vars
+}
+
+# Create a dialog box to allow the user to select a policy path.
+proc Apol_Open_Policy_Dialog::getPolicyPath {defaultPath} {
+ variable dialog
+ variable vars
+
+ array unset vars
+ _create_dialog .
+
+ set vars(path_type) "monolithic"
+ set vars(primary_file) {}
+ set vars(last_module) {}
+ set vars(mod_names) {}
+ set vars(mod_vers) {}
+ set vars(mod_paths) {}
+
+ if {$defaultPath != {}} {
+ foreach {path_type primary modules} [policy_path_to_list $defaultPath] {break}
+ set vars(path_type) $path_type
+ if {[set vars(primary_file) $primary] != {}} {
+ $dialog itemconfigure 0 -state normal
+ }
+ set vars(last_module) $vars(primary_file)
+ foreach m $modules {
+ if {[catch {getModuleInfo $m} info]} {
+ tk_messageBox -icon error -type ok -title "Open Module" -message $info -detail "Module file $m" -parent [$dialog getframe]
+ } else {
+ foreach {name vers type} $info {break}
+ lappend vars(mod_names) $name
+ lappend vars(mod_vers) $vers
+ lappend vars(mod_paths) $m
+ set vars(last_module) $m
+ }
+ }
+ }
+ # force a recomputation of button sizes (bug in ButtonBox)
+ $dialog.bbox _redraw
+ $dialog draw
+ destroy $dialog
+}
+
+########## private functions below ##########
+
+proc Apol_Open_Policy_Dialog::_create_dialog {parent} {
+ variable dialog
+ variable widgets
+ variable vars
+
+ destroy $dialog
+ set dialog [Dialog .open_policy_dialog -modal local -parent $parent \
+ -cancel 1 \
+ -separator 1 -homogeneous 1 -title "Open Policy"]
+
+ set f [$dialog getframe]
+
+ set policy_type_f [frame $f.policy_type]
+ pack $policy_type_f -padx 4 -pady 4 -expand 0 -anchor w
+ set l [label $policy_type_f.l -text "Policy Type:"]
+ set mono_cb [radiobutton $policy_type_f.mono -text "Monolithic policy" \
+ -value monolithic \
+ -variable Apol_Open_Policy_Dialog::vars(path_type)]
+ set mod_cb [radiobutton $policy_type_f.mod -text "Modular policy" \
+ -value modular \
+ -variable Apol_Open_Policy_Dialog::vars(path_type)]
+ pack $l -anchor w
+ pack $mono_cb $mod_cb -anchor w -padx 8
+
+ set primary_f [frame $f.primary]
+ pack $primary_f -padx 4 -pady 8 -expand 0 -fill x
+ set widgets(main_label) [label $primary_f.l -text "Policy Filename:"]
+ pack $widgets(main_label) -anchor w
+ frame $primary_f.f
+ pack $primary_f.f -expand 1 -fill x
+ set e [entry $primary_f.f.e -width 32 -bg white \
+ -textvariable Apol_Open_Policy_Dialog::vars(primary_file) \
+ -validate key \
+ -vcmd [list Apol_Open_Policy_Dialog::_validateEntryKey %P]]
+ bind $e <Key-Return> Apol_Open_Policy_Dialog::tryOpenPolicy
+ set b [button $primary_f.f.b -text "Browse" \
+ -command Apol_Open_Policy_Dialog::browsePrimary]
+ pack $e -side left -expand 1 -fill x -padx 4
+ pack $b -side right -expand 0 -padx 4
+
+ set modules_f [frame $f.modules]
+ pack $modules_f -pady 4 -padx 4 -expand 1 -fill both
+ set mod_list_f [frame $modules_f.mods -relief sunken]
+ pack $mod_list_f -side left -expand 1 -fill both -padx 4
+ set mlabel [label $mod_list_f.ml -text "Module:"]
+ set vlabel [label $mod_list_f.vl -text "Version:"]
+ set plabel [label $mod_list_f.pl -text "Path:"]
+ grid $mlabel $vlabel $plabel x -sticky w
+ set dis_bg [$mlabel cget -bg]
+ set ml [listbox $mod_list_f.mods -height 6 -width 10 \
+ -listvariable Apol_Open_Policy_Dialog::vars(mod_names)]
+ set vl [listbox $mod_list_f.vers -height 6 -width 4 \
+ -listvariable Apol_Open_Policy_Dialog::vars(mod_vers)]
+ set pl [listbox $mod_list_f.paths -height 6 -width 24 \
+ -listvariable Apol_Open_Policy_Dialog::vars(mod_paths)]
+ set sb [scrollbar $mod_list_f.sb -orient vertical \
+ -command [list Apol_Open_Policy_Dialog::multiscroll yview]]
+ grid $ml $vl $pl $sb -sticky nsew
+ set widgets(bb) [ButtonBox $modules_f.bb -homogeneous 1 -orient vertical -pady 2]
+ $widgets(bb) add -text "Add" -command Apol_Open_Policy_Dialog::browseModule
+ $widgets(bb) add -text "Remove" -command Apol_Open_Policy_Dialog::removeModule -state disabled
+ $widgets(bb) add -text "Import" -command Apol_Open_Policy_Dialog::importList
+ $widgets(bb) add -text "Export" -command Apol_Open_Policy_Dialog::exportList -state disabled
+ pack $widgets(bb) -side right -expand 0 -anchor n -padx 4 -pady 10
+
+ set widgets(listboxes) [list $ml $vl $pl]
+ set widgets(scrollbar) $sb
+ foreach lb $widgets(listboxes) {
+ $lb configure -yscrollcommand Apol_Open_Policy_Dialog::multiyview \
+ -relief groove -bg white -exportselection 0
+ bind $lb <<ListboxSelect>> \
+ [list Apol_Open_Policy_Dialog::multiselect $lb]
+ }
+
+ trace add variable Apol_Open_Policy_Dialog::vars(path_type) write \
+ [list Apol_Open_Policy_Dialog::togglePathType \
+ [list $mlabel $vlabel $plabel] $dis_bg]
+ $dialog add -text "OK" -command Apol_Open_Policy_Dialog::tryOpenPolicy \
+ -state disabled
+ $dialog add -text "Cancel"
+}
+
+proc Apol_Open_Policy_Dialog::_validateEntryKey {newvalue} {
+ variable vars
+ variable dialog
+ variable widgets
+ if {$newvalue == {}} {
+ $dialog itemconfigure 0 -state disabled
+ $widgets(bb) itemconfigure 3 -state disabled
+ } else {
+ $dialog itemconfigure 0 -state normal
+ if {$vars(path_type) == "modular"} {
+ $widgets(bb) itemconfigure 3 -state normal
+ } else {
+ $widgets(bb) itemconfigure 3 -state disabled
+ }
+ }
+ return 1
+}
+
+proc Apol_Open_Policy_Dialog::togglePathType {labels disabled_bg name1 name2 op} {
+ variable vars
+ variable widgets
+ if {$vars(path_type) == "modular"} {
+ set state normal
+ set bg white
+ $widgets(main_label) configure -text "Base Filename:"
+ } else {
+ set state disabled
+ set bg $disabled_bg
+ $widgets(main_label) configure -text "Policy Filename:"
+ }
+ foreach w $labels {
+ $w configure -state $state
+ }
+ foreach w $widgets(listboxes) {
+ $w configure -state $state -bg $bg
+ }
+ $widgets(bb) configure -state $state
+ if {$state == "normal" && [[lindex $widgets(listboxes) 0] curselection] > 0} {
+ $widgets(bb) itemconfigure 1 -state normal
+ } else {
+ $widgets(bb) itemconfigure 1 -state disabled
+ }
+ if {$state == "normal" && $vars(primary_file) != {}} {
+ $widgets(bb) itemconfigure 3 -state normal
+ } else {
+ $widgets(bb) itemconfigure 3 -state disabled
+ }
+}
+
+proc Apol_Open_Policy_Dialog::browsePrimary {} {
+ variable vars
+ variable dialog
+ .open_policy_dialog.frame.primary.f.b configure -state disabled
+ if {$vars(path_type) == "monolithic"} {
+ set title "Open Monolithic Policy"
+ set initDirName {}
+ } else {
+ set title "Open Modular Policy"
+ if {$vars(primary_file) != {} } {
+ set initDirName [file dirname $vars(primary_file)]
+ } else {
+ set initDirName [file dirname $vars(last_module)]
+ }
+ }
+ set f [tk_getOpenFile -initialdir $initDirName \
+ -initialfile $vars(primary_file) -parent $dialog -title $title]
+ if {$f != {}} {
+ set vars(primary_file) $f
+ $dialog itemconfigure 0 -state normal
+ }
+ .open_policy_dialog.frame.primary.f.b configure -state normal
+}
+
+proc Apol_Open_Policy_Dialog::browseModule {} {
+ variable vars
+ variable dialog
+
+ if {$vars(last_module) != {} } {
+ set initDirName [file dirname $vars(last_module)]
+ } else {
+ set initDirName [file dirname $vars(primary_file)]
+ }
+ set paths [tk_getOpenFile -initialdir $initDirName \
+ -initialfile $vars(last_module) -parent $dialog \
+ -title "Open Module" -multiple 1]
+ if {$paths == {}} {
+ return
+ }
+ foreach f $paths {
+ # tk_getOpenFile returns "initialfile" as a selected file, so skip it.
+ if { $f != $vars(last_module) } {
+ addModule $f
+ }
+ }
+}
+
+proc Apol_Open_Policy_Dialog::addModule {f} {
+ variable vars
+ variable widgets
+ if {[lsearch $vars(mod_paths) $f] >= 0} {
+ tk_messageBox -icon error -type ok -title "Open Module" -message "Module $f was already added." -parent .open_policy_dialog
+ return
+ }
+ if {[catch {getModuleInfo $f} info]} {
+ tk_messageBox -icon error -type ok -title "Open Module" -message $info -detail "Module file $f" -parent .open_policy_dialog
+ } else {
+ foreach {name vers type} $info {break}
+ if {$type == 1} {
+ if {$vars(primary_file) != {}} {
+ if {$vars(primary_file) != $f} {
+ tk_messageBox -icon error -type ok -title "Open Module" -message "Base already set" -detail "Current $vars(primary_file)\n\nNew file $f\n\nIgnoring new file." -parent .open_policy_dialog
+ }
+ return
+ }
+ set vars(primary_file) $f
+ return
+ }
+ set vars(mod_names) [lsort [concat $vars(mod_names) $name]]
+ set i [lsearch $vars(mod_names) $name]
+ set vars(mod_vers) [linsert $vars(mod_vers) $i $vers]
+ set vars(mod_paths) [linsert $vars(mod_paths) $i $f]
+ foreach lb $widgets(listboxes) {
+ $lb selection clear 0 end
+ $lb selection set $i
+ }
+ [lindex $widgets(listboxes) 0] see $i
+ set vars(last_module) $f
+ $widgets(bb) itemconfigure 1 -state normal
+ }
+}
+
+proc Apol_Open_Policy_Dialog::removeModule {} {
+ variable widgets
+ set i [[lindex $widgets(listboxes) 0] curselection]
+ if {[llength $i] > 0} {
+ foreach lb $widgets(listboxes) {
+ $lb delete [lindex $i 0]
+ }
+ }
+ $widgets(bb) itemconfigure 1 -state disabled
+}
+
+proc Apol_Open_Policy_Dialog::importList {} {
+ variable vars
+ variable dialog
+ variable widgets
+ set f [tk_getOpenFile -initialdir [file dirname $vars(primary_file)] \
+ -parent $dialog -title "Import Policy List"]
+ if {$f == {}} {
+ return
+ }
+ if {[catch {new_apol_policy_path_t $f} ppath]} {
+ tk_messageBox -icon error -type ok -title "Import Policy List" \
+ -message "Error importing policy list $f: $ppath"
+ return
+ }
+ foreach lb $widgets(listboxes) {
+ $lb delete 0 end
+ }
+ foreach {path_type primary modules} [policy_path_to_list $ppath] {break}
+ set vars(path_type) $path_type
+ if {[set vars(primary_file) $primary] != {}} {
+ $dialog itemconfigure 0 -state normal
+ }
+ set vars(last_module) $f
+ foreach m $modules {
+ addModule $m
+ }
+ _validateEntryKey $vars(primary_file)
+ $ppath -acquire
+ $ppath -delete
+}
+
+proc Apol_Open_Policy_Dialog::exportList {} {
+ variable vars
+ variable dialog
+ set f [tk_getSaveFile -parent $dialog -title "Export Policy List"]
+ if {$f == {}} {
+ return
+ }
+ set ppath [list_to_policy_path $vars(path_type) $vars(primary_file) $vars(mod_paths)]
+ if {[catch {$ppath to_file $f} err]} {
+ tk_messageBox -icon error -type ok -title "Export Policy List" \
+ -message "Error exporting policy list $f: $err"
+ }
+}
+
+proc Apol_Open_Policy_Dialog::multiscroll {args} {
+ variable widgets
+ foreach lb $widgets(listboxes) {
+ eval $lb $args
+ }
+}
+
+proc Apol_Open_Policy_Dialog::multiselect {lb} {
+ variable widgets
+ set sellist [$lb curselection]
+ set enable_remove 0
+ foreach lb $widgets(listboxes) {
+ $lb selection clear 0 end
+ foreach item $sellist {
+ $lb selection set $item
+ set enable_remove 1
+ }
+ }
+ if {$enable_remove} {
+ $widgets(bb) itemconfigure 1 -state normal
+ }
+}
+
+proc Apol_Open_Policy_Dialog::multiyview {args} {
+ variable widgets
+ eval $widgets(scrollbar) set $args
+ multiscroll yview moveto [lindex $args 0]
+}
+
+
+# Generate a policy path and try to open the given policy. Upon
+# success end the dialog and return that path. Otherwise do not close
+# the dialog.
+proc Apol_Open_Policy_Dialog::tryOpenPolicy {} {
+ variable dialog
+ variable vars
+ .open_policy_dialog.bbox.b0 configure -state disabled
+ if {[string trim $vars(primary_file)] != {}} {
+ set ppath [list_to_policy_path $vars(path_type) $vars(primary_file) $vars(mod_paths)]
+ if {[ApolTop::openPolicyPath $ppath] == 0} {
+ $dialog enddialog {}
+ }
+ }
+ .open_policy_dialog.bbox.b0 configure -state normal
+}
+
+# Retrieve information about a policy module file, either source or
+# binary, from disk. This will be a 3-ple of module name, version and type.
+# The policy module will be closed afterwards.
+proc Apol_Open_Policy_Dialog::getModuleInfo {f} {
+ set mod [new_qpol_module_t $f]
+ set retval [list [$mod get_name] [$mod get_version] [$mod get_type]]
+ $mod -acquire
+ $mod -delete
+ return $retval
+}
diff --git a/apol/perm_maps/apol_perm_mapping_ver12 b/apol/perm_maps/apol_perm_mapping_ver12
new file mode 100644
index 0000000..7e3df06
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver12
@@ -0,0 +1,575 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+29
+
+
+class blk_file 17
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ ioctl n 1
+ execute r 0
+ append w 1
+ read r 10
+ setattr w 7
+ swapon b 0
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+
+
+class file 19
+ setattr w 7
+ swapon b 0
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+ entrypoint r 0
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ execute_no_trans r 0
+ ioctl n 1
+ execute r 0
+ append w 1
+ read r 10
+
+
+class udp_socket 22
+ listen r 1
+ setattr w 7
+ shutdown w 1
+ relabelto w 10
+ recv_msg r 10
+ accept r 1
+ name_bind n 1
+ append w 1
+ relabelfrom r 10
+ create w 1
+ read r 10
+ sendto w 10
+ connect w 1
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getattr r 7
+ write w 10
+ setopt w 1
+ getopt r 1
+
+
+class socket 22
+ append w 1
+ relabelfrom r 10
+ create w 1
+ read r 10
+ sendto w 10
+ connect w 1
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getattr r 7
+ write w 10
+ setopt w 1
+ getopt r 1
+ listen r 0
+ setattr w 7
+ shutdown w 1
+ relabelto w 10
+ recv_msg r 10
+ accept r 1
+ name_bind n 1
+
+
+class fifo_file 17
+ relabelto w 10
+ getattr r 7
+ lock n 1
+ execute r 0
+ unlink w 1
+ ioctl n 1
+ setattr w 7
+ append w 1
+ write w 10
+ swapon b 0
+ create w 1
+ link w 1
+ rename w 5
+ relabelfrom r 10
+ mounton b 1
+ quotaon b 1
+ read r 10
+
+
+class chr_file 17
+ append w 1
+ swapon b 0
+ mounton b 1
+ quotaon b 1
+ create w 1
+ rename w 5
+ ioctl n 1
+ getattr r 7
+ link w 1
+ write w 10
+ execute r 0
+ relabelto w 10
+ setattr w 7
+ relabelfrom r 10
+ read r 10
+ unlink w 1
+ lock n 1
+
+
+class netlink_socket 22
+ listen r 1
+ accept r 1
+ read r 10
+ setattr w 7
+ append w 1
+ bind w 1
+ lock n 1
+ shutdown w 1
+ recv_msg r 10
+ create w 1
+ sendto w 10
+ relabelto w 10
+ ioctl n 1
+ name_bind n 1
+ connect w 1
+ write w 10
+ recvfrom r 10
+ send_msg w 10
+ relabelfrom r 10
+ setopt w 1
+ getattr r 7
+ getopt r 1
+
+
+class unix_dgram_socket 22
+ connect w 1
+ getopt r 1
+ listen r 1
+ relabelto w 10
+ name_bind n 1
+ accept r 1
+ shutdown w 1
+ getattr r 7
+ recv_msg r 10
+ append w 1
+ read r 10
+ create w 1
+ sendto w 10
+ ioctl n 1
+ setattr w 7
+ bind w 1
+ lock n 1
+ recvfrom r 10
+ send_msg w 10
+ write w 10
+ relabelfrom r 10
+ setopt w 1
+
+
+class node 7
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ enforce_dest n 1
+ udp_recv r 10
+ udp_send w 10
+
+
+class netif 6
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+
+
+class unix_stream_socket 25
+ relabelto w 10
+ append w 1
+ name_bind n 1
+ setattr w 7
+ connectto w 1
+ newconn w 1
+ recvfrom r 10
+ create w 1
+ sendto w 10
+ send_msg w 10
+ read r 10
+ bind w 1
+ lock n 1
+ connect w 1
+ setopt w 1
+ acceptfrom r 1
+ getopt r 1
+ ioctl n 1
+ getattr r 7
+ shutdown w 1
+ recv_msg r 10
+ listen r 1
+ accept r 1
+ relabelfrom r 10
+ write w 10
+
+
+class tcp_socket 25
+ connectto w 1
+ newconn w 1
+ recvfrom r 10
+ create w 1
+ sendto w 10
+ send_msg w 10
+ read r 10
+ bind w 1
+ lock n 1
+ connect w 1
+ setopt w 1
+ acceptfrom r 1
+ getopt r 1
+ ioctl n 1
+ getattr r 7
+ shutdown w 1
+ recv_msg r 10
+ listen r 1
+ accept r 1
+ relabelfrom r 10
+ write w 10
+ relabelto w 10
+ append w 1
+ name_bind n 1
+ setattr w 7
+
+
+class dir 22
+ mounton b 1
+ search r 1
+ link w 1
+ quotaon b 1
+ append w 1
+ swapon b 0
+ rmdir b 1
+ create w 1
+ ioctl n 1
+ getattr r 7
+ remove_name w 1
+ rename w 5
+ read r 10
+ write w 10
+ relabelfrom r 10
+ execute r 0
+ relabelto w 10
+ lock n 1
+ setattr w 7
+ reparent w 1
+ add_name w 5
+ unlink w 1
+
+
+class shm 10
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ lock w 1
+ associate n 0
+ setattr w 1
+ create w 1
+
+
+class security 9
+ change_sid n 1
+ transition_sid n 1
+ sid_to_context n 1
+ member_sid n 1
+ get_user_sids n 1
+ compute_av n 1
+ load_policy n 1
+ get_sids n 1
+ context_to_sid n 1
+
+
+class packet_socket 22
+ setattr w 7
+ read r 10
+ relabelto w 10
+ shutdown w 1
+ name_bind n 1
+ recv_msg r 10
+ setopt w 1
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getopt r 1
+ connect w 1
+ relabelfrom r 10
+ listen r 1
+ write w 10
+ accept r 1
+ append w 1
+ recvfrom r 10
+ send_msg w 10
+ getattr r 7
+ create w 1
+ sendto w 10
+
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ associate n 0
+ setattr w 1
+
+
+class key_socket 22
+ connect w 1
+ setopt w 1
+ relabelto w 10
+ read r 10
+ name_bind n 1
+ getopt r 1
+ getattr r 7
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ listen r 1
+ lock n 1
+ accept r 1
+ append w 1
+ setattr w 7
+ ioctl n 1
+ create w 1
+ sendto w 10
+ relabelfrom r 10
+ write w 10
+ shutdown w 1
+ recv_msg r 10
+
+
+class capability 29
+ net_bind_service n 1
+ sys_module n 0
+ sys_admin n 3
+ fowner n 1
+ net_raw n 1
+ setuid n 1
+ sys_chroot n 1
+ lease n 1
+ net_admin n 1
+ ipc_owner n 1
+ fsetid n 1
+ sys_resource n 1
+ sys_rawio n 1
+ sys_ptrace n 1
+ sys_nice n 1
+ setpcap n 3
+ kill n 1
+ sys_pacct n 1
+ sys_boot n 1
+ dac_override n 1
+ setgid n 3
+ net_broadcast n 1
+ chown n 3
+ sys_tty_config n 1
+ linux_immutable n 1
+ sys_time n 1
+ ipc_lock n 1
+ mknod n 1
+ dac_read_search n 1
+
+
+class fd 1
+ use b 1
+
+
+class rawip_socket 22
+ lock n 1
+ write w 10
+ getattr r 1
+ recvfrom r 10
+ send_msg w 10
+ setopt w 1
+ setattr w 1
+ getopt r 1
+ relabelto w 10
+ listen r 1
+ name_bind n 1
+ accept r 1
+ append w 1
+ shutdown w 1
+ recv_msg r 10
+ relabelfrom r 10
+ read r 10
+ ioctl n 1
+ connect w 1
+ create w 1
+ sendto w 10
+ bind w 1
+
+
+class ipc 9
+ write w 10
+ destroy w 1
+ unix_write w 3
+ getattr r 1
+ create w 1
+ read r 10
+ setattr w 1
+ unix_read r 3
+ associate n 1
+
+
+class lnk_file 17
+ relabelfrom r 10
+ append w 1
+ ioctl n 1
+ swapon b 0
+ create w 1
+ read r 10
+ write w 10
+ rename w 1
+ mounton b 1
+ quotaon b 1
+ lock n 1
+ relabelto w 10
+ getattr r 7
+ unlink w 1
+ execute r 0
+ link w 1
+ setattr w 7
+
+
+class system 8
+ ipc_info n 1
+ syslog_mod n 1
+ syslog_read n 1
+ syslog_console n 1
+ nfsd_control n 1
+ avc_toggle n 1
+ bdflush n 1
+ ichsid n 1
+
+
+class sem 9
+ unix_read r 3
+ associate n 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ read r 10
+ setattr w 1
+ write w 10
+ unix_write w 3
+
+
+class filesystem 10
+ remount w 1
+ relabelfrom r 10
+ getattr r 1
+ relabelto w 10
+ mount w 1
+ transition w 1
+ quotaget r 1
+ quotamod w 1
+ unmount w 1
+ associate n 1
+
+
+class sock_file 17
+ setattr w 7
+ rename w 1
+ ioctl n 1
+ link w 1
+ write w 10
+ mounton b 1
+ relabelto w 10
+ quotaon b 1
+ read r 10
+ unlink w 1
+ append w 1
+ lock n 1
+ getattr r 7
+ swapon b 0
+ relabelfrom r 10
+ execute r 0
+ create w 1
+
+
+class process 16
+ getsched r 1
+ signull n 1
+ sigstop w 1
+ share b 1
+ getpgid r 1
+ signal w 5
+ setcap w 1
+ sigchld w 1
+ getcap r 3
+ getsession r 1
+ setsched w 1
+ fork n 1
+ ptrace b 10
+ sigkill w 1
+ setpgid w 5
+ transition w 1
+
+
+class msg 2
+ receive r 10
+ send w 10
+
+
diff --git a/apol/perm_maps/apol_perm_mapping_ver15 b/apol/perm_maps/apol_perm_mapping_ver15
new file mode 100644
index 0000000..689e91f
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver15
@@ -0,0 +1,580 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+30
+
+
+class blk_file 17
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ ioctl n 1
+ execute r 1
+ append w 1
+ read r 10
+ setattr w 7
+ swapon b 1
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+
+
+class file 19
+ setattr w 7
+ swapon b 1
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+ entrypoint r 1
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ execute_no_trans r 1
+ ioctl n 1
+ execute r 1
+ append w 1
+ read r 10
+
+
+class udp_socket 22
+ listen r 1
+ setattr w 7
+ shutdown w 1
+ relabelto w 10
+ recv_msg r 10
+ accept r 1
+ name_bind n 1
+ append w 1
+ relabelfrom r 10
+ create w 1
+ read r 10
+ sendto w 10
+ connect w 1
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getattr r 7
+ write w 10
+ setopt w 1
+ getopt r 1
+
+
+class socket 22
+ append w 1
+ relabelfrom r 10
+ create w 1
+ read r 10
+ sendto w 10
+ connect w 1
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getattr r 7
+ write w 10
+ setopt w 1
+ getopt r 1
+ listen r 1
+ setattr w 7
+ shutdown w 1
+ relabelto w 10
+ recv_msg r 10
+ accept r 1
+ name_bind n 1
+
+
+class passwd 3
+ passwd n 1
+ chfn w 5
+ chsh w 5
+
+
+class fifo_file 17
+ relabelto w 10
+ getattr r 7
+ lock n 1
+ execute r 1
+ unlink w 1
+ ioctl n 1
+ setattr w 7
+ append w 1
+ write w 10
+ swapon b 1
+ create w 1
+ link w 1
+ rename w 5
+ relabelfrom r 10
+ mounton b 1
+ quotaon b 1
+ read r 10
+
+
+class chr_file 17
+ append w 1
+ swapon b 1
+ mounton b 1
+ quotaon b 1
+ create w 1
+ rename w 5
+ ioctl n 1
+ getattr r 7
+ link w 1
+ write w 10
+ execute r 1
+ relabelto w 10
+ setattr w 7
+ relabelfrom r 10
+ read r 10
+ unlink w 1
+ lock n 1
+
+
+class netlink_socket 22
+ listen r 1
+ accept r 1
+ read r 10
+ setattr w 7
+ append w 1
+ bind w 1
+ lock n 1
+ shutdown w 1
+ recv_msg r 10
+ create w 1
+ sendto w 10
+ relabelto w 10
+ ioctl n 1
+ name_bind n 1
+ connect w 1
+ write w 10
+ recvfrom r 10
+ send_msg w 10
+ relabelfrom r 10
+ setopt w 1
+ getattr r 7
+ getopt r 1
+
+
+class unix_dgram_socket 22
+ connect w 1
+ getopt r 1
+ listen r 1
+ relabelto w 10
+ name_bind n 1
+ accept r 1
+ shutdown w 1
+ getattr r 7
+ recv_msg r 10
+ append w 1
+ read r 10
+ create w 1
+ sendto w 10
+ ioctl n 1
+ setattr w 7
+ bind w 1
+ lock n 1
+ recvfrom r 10
+ send_msg w 10
+ write w 10
+ relabelfrom r 10
+ setopt w 1
+
+
+class node 7
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ enforce_dest n 1
+ udp_recv r 10
+ udp_send w 10
+
+
+class netif 6
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+
+
+class unix_stream_socket 25
+ relabelto w 10
+ append w 1
+ name_bind n 1
+ setattr w 7
+ connectto w 1
+ newconn w 1
+ recvfrom r 10
+ create w 1
+ sendto w 10
+ send_msg w 10
+ read r 10
+ bind w 1
+ lock n 1
+ connect w 1
+ setopt w 1
+ acceptfrom r 1
+ getopt r 1
+ ioctl n 1
+ getattr r 7
+ shutdown w 1
+ recv_msg r 10
+ listen r 1
+ accept r 1
+ relabelfrom r 10
+ write w 10
+
+
+class tcp_socket 25
+ connectto w 1
+ newconn w 1
+ recvfrom r 10
+ create w 1
+ sendto w 10
+ send_msg w 10
+ read r 10
+ bind w 1
+ lock n 1
+ connect w 1
+ setopt w 1
+ acceptfrom r 1
+ getopt r 1
+ ioctl n 1
+ getattr r 7
+ shutdown w 1
+ recv_msg r 10
+ listen r 1
+ accept r 1
+ relabelfrom r 10
+ write w 10
+ relabelto w 10
+ append w 1
+ name_bind n 1
+ setattr w 7
+
+
+class dir 22
+ mounton b 1
+ search r 1
+ link w 1
+ quotaon b 1
+ append w 1
+ swapon b 1
+ rmdir b 1
+ create w 1
+ ioctl n 1
+ getattr r 7
+ remove_name w 1
+ rename w 5
+ read r 10
+ write w 10
+ relabelfrom r 10
+ execute r 1
+ relabelto w 10
+ lock n 1
+ setattr w 7
+ reparent w 1
+ add_name w 5
+ unlink w 1
+
+
+class shm 10
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ lock w 1
+ associate n 1
+ setattr w 1
+ create w 1
+
+
+class security 8
+ compute_user n 1
+ compute_relabel n 1
+ compute_create n 1
+ compute_av n 1
+ compute_member n 1
+ setenforce n 1
+ check_context n 1
+ load_policy n 1
+
+
+class packet_socket 22
+ setattr w 7
+ read r 10
+ relabelto w 10
+ shutdown w 1
+ name_bind n 1
+ recv_msg r 10
+ setopt w 1
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getopt r 1
+ connect w 1
+ relabelfrom r 10
+ listen r 1
+ write w 10
+ accept r 1
+ append w 1
+ recvfrom r 10
+ send_msg w 10
+ getattr r 7
+ create w 1
+ sendto w 10
+
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ associate n 1
+ setattr w 1
+
+
+class key_socket 22
+ connect w 1
+ setopt w 1
+ relabelto w 10
+ read r 10
+ name_bind n 1
+ getopt r 1
+ getattr r 7
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ listen r 1
+ lock n 1
+ accept r 1
+ append w 1
+ setattr w 7
+ ioctl n 1
+ create w 1
+ sendto w 10
+ relabelfrom r 10
+ write w 10
+ shutdown w 1
+ recv_msg r 10
+
+
+class capability 29
+ net_bind_service n 1
+ sys_module n 1
+ sys_admin n 3
+ fowner n 1
+ net_raw n 1
+ setuid n 1
+ sys_chroot n 1
+ lease n 1
+ net_admin n 1
+ ipc_owner n 1
+ fsetid n 1
+ sys_resource n 1
+ sys_rawio n 1
+ sys_ptrace n 1
+ sys_nice n 1
+ setpcap n 3
+ kill n 1
+ sys_pacct n 1
+ sys_boot n 1
+ dac_override n 1
+ setgid n 3
+ net_broadcast n 1
+ chown n 3
+ sys_tty_config n 1
+ linux_immutable n 1
+ sys_time n 1
+ ipc_lock n 1
+ mknod n 1
+ dac_read_search n 1
+
+
+class fd 1
+ use b 1
+
+
+class rawip_socket 22
+ lock n 1
+ write w 10
+ getattr r 1
+ recvfrom r 10
+ send_msg w 10
+ setopt w 1
+ setattr w 1
+ getopt r 1
+ relabelto w 10
+ listen r 1
+ name_bind n 1
+ accept r 1
+ append w 1
+ shutdown w 1
+ recv_msg r 10
+ relabelfrom r 10
+ read r 10
+ ioctl n 1
+ connect w 1
+ create w 1
+ sendto w 10
+ bind w 1
+
+
+class ipc 9
+ write w 10
+ destroy w 1
+ unix_write w 3
+ getattr r 1
+ create w 1
+ read r 10
+ setattr w 1
+ unix_read r 3
+ associate n 1
+
+
+class lnk_file 17
+ relabelfrom r 10
+ append w 1
+ ioctl n 1
+ swapon b 1
+ create w 1
+ read r 10
+ write w 10
+ rename w 1
+ mounton b 1
+ quotaon b 1
+ lock n 1
+ relabelto w 10
+ getattr r 7
+ unlink w 1
+ execute r 1
+ link w 1
+ setattr w 7
+
+
+class system 4
+ ipc_info n 1
+ syslog_mod n 1
+ syslog_read n 1
+ syslog_console n 1
+
+
+class sem 9
+ unix_read r 3
+ associate n 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ read r 10
+ setattr w 1
+ write w 10
+ unix_write w 3
+
+
+class filesystem 10
+ remount w 1
+ relabelfrom r 10
+ getattr r 1
+ relabelto w 10
+ mount w 1
+ transition w 1
+ quotaget r 1
+ quotamod w 1
+ unmount w 1
+ associate n 1
+
+
+class sock_file 17
+ setattr w 7
+ rename w 1
+ ioctl n 1
+ link w 1
+ write w 10
+ mounton b 1
+ relabelto w 10
+ quotaon b 1
+ read r 10
+ unlink w 1
+ append w 1
+ lock n 1
+ getattr r 7
+ swapon b 1
+ relabelfrom r 10
+ execute r 1
+ create w 1
+
+
+class process 20
+ noatsecure n 1
+ getsched r 1
+ signull n 1
+ sigstop w 1
+ getattr r 1
+ share b 1
+ getpgid r 1
+ signal w 5
+ setcap w 1
+ sigchld w 1
+ setexec w 1
+ getcap r 3
+ getsession r 1
+ setsched w 1
+ fork n 1
+ ptrace b 10
+ sigkill w 1
+ setpgid w 5
+ transition w 1
+ setfscreate w 1
+
+
+class msg 2
+ receive r 10
+ send w 10
+
+
diff --git a/apol/perm_maps/apol_perm_mapping_ver16 b/apol/perm_maps/apol_perm_mapping_ver16
new file mode 100644
index 0000000..a62fb77
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver16
@@ -0,0 +1,560 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+30
+
+class security 9
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+
+class process 23
+ fork n 1
+ transition w 1
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 29
+ net_bind_service n 1
+ sys_module n 1
+ sys_admin n 3
+ fowner n 1
+ net_raw n 1
+ setuid n 1
+ sys_chroot n 1
+ lease n 1
+ net_admin n 1
+ ipc_owner n 1
+ fsetid n 1
+ sys_resource n 1
+ sys_rawio n 1
+ sys_ptrace n 1
+ sys_nice n 1
+ setpcap n 3
+ kill n 1
+ sys_pacct n 1
+ sys_boot n 1
+ dac_override n 1
+ setgid n 3
+ net_broadcast n 1
+ chown n 3
+ sys_tty_config n 1
+ linux_immutable n 1
+ sys_time n 1
+ ipc_lock n 1
+ mknod n 1
+ dac_read_search n 1
+
+class filesystem 10
+ remount w 1
+ relabelfrom r 10
+ getattr r 1
+ relabelto w 10
+ mount w 1
+ transition w 1
+ quotaget r 1
+ quotamod w 1
+ unmount w 1
+ associate n 1
+
+class file 19
+ setattr w 7
+ swapon b 1
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+ entrypoint r 1
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ execute_no_trans r 1
+ ioctl n 1
+ execute r 1
+ append w 1
+ read r 10
+
+class dir 22
+ mounton b 1
+ search r 1
+ link w 1
+ quotaon b 1
+ append w 1
+ swapon b 1
+ rmdir b 1
+ create w 1
+ ioctl n 1
+ getattr r 7
+ remove_name w 1
+ rename w 5
+ read r 10
+ write w 10
+ relabelfrom r 10
+ execute r 1
+ relabelto w 10
+ lock n 1
+ setattr w 7
+ reparent w 1
+ add_name w 5
+ unlink w 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ relabelfrom r 10
+ append w 1
+ ioctl n 1
+ swapon b 1
+ create w 1
+ read r 10
+ write w 10
+ rename w 1
+ mounton b 1
+ quotaon b 1
+ lock n 1
+ relabelto w 10
+ getattr r 7
+ unlink w 1
+ execute r 1
+ link w 1
+ setattr w 7
+
+class chr_file 17
+ append w 1
+ swapon b 1
+ mounton b 1
+ quotaon b 1
+ create w 1
+ rename w 5
+ ioctl n 1
+ getattr r 7
+ link w 1
+ write w 10
+ execute r 1
+ relabelto w 10
+ setattr w 7
+ relabelfrom r 10
+ read r 10
+ unlink w 1
+ lock n 1
+
+class blk_file 17
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ ioctl n 1
+ execute r 1
+ append w 1
+ read r 10
+ setattr w 7
+ swapon b 1
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+
+class sock_file 17
+ setattr w 7
+ rename w 1
+ ioctl n 1
+ link w 1
+ write w 10
+ mounton b 1
+ relabelto w 10
+ quotaon b 1
+ read r 10
+ unlink w 1
+ append w 1
+ lock n 1
+ getattr r 7
+ swapon b 1
+ relabelfrom r 10
+ execute r 1
+ create w 1
+
+class fifo_file 17
+ relabelto w 10
+ getattr r 7
+ lock n 1
+ execute r 1
+ unlink w 1
+ ioctl n 1
+ setattr w 7
+ append w 1
+ write w 10
+ swapon b 1
+ create w 1
+ link w 1
+ rename w 5
+ relabelfrom r 10
+ mounton b 1
+ quotaon b 1
+ read r 10
+
+class socket 22
+ append w 1
+ relabelfrom r 10
+ create w 1
+ read r 10
+ sendto w 10
+ connect w 1
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getattr r 7
+ write w 10
+ setopt w 1
+ getopt r 1
+ listen r 1
+ setattr w 7
+ shutdown w 1
+ relabelto w 10
+ recv_msg r 10
+ accept r 1
+ name_bind n 1
+
+class tcp_socket 26
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+
+class node 7
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ enforce_dest n 1
+ udp_recv r 10
+ udp_send w 10
+
+class netif 6
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+
+class netlink_socket 22
+ listen r 1
+ accept r 1
+ read r 10
+ setattr w 7
+ append w 1
+ bind w 1
+ lock n 1
+ shutdown w 1
+ recv_msg r 10
+ create w 1
+ sendto w 10
+ relabelto w 10
+ ioctl n 1
+ name_bind n 1
+ connect w 1
+ write w 10
+ recvfrom r 10
+ send_msg w 10
+ relabelfrom r 10
+ setopt w 1
+ getattr r 7
+ getopt r 1
+
+class packet_socket 22
+ setattr w 7
+ read r 10
+ relabelto w 10
+ shutdown w 1
+ name_bind n 1
+ recv_msg r 10
+ setopt w 1
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getopt r 1
+ connect w 1
+ relabelfrom r 10
+ listen r 1
+ write w 10
+ accept r 1
+ append w 1
+ recvfrom r 10
+ send_msg w 10
+ getattr r 7
+ create w 1
+ sendto w 10
+
+class key_socket 22
+ connect w 1
+ setopt w 1
+ relabelto w 10
+ read r 10
+ name_bind n 1
+ getopt r 1
+ getattr r 7
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ listen r 1
+ lock n 1
+ accept r 1
+ append w 1
+ setattr w 7
+ ioctl n 1
+ create w 1
+ sendto w 10
+ relabelfrom r 10
+ write w 10
+ shutdown w 1
+ recv_msg r 10
+
+class unix_stream_socket 25
+ relabelto w 10
+ append w 1
+ name_bind n 1
+ setattr w 7
+ connectto w 1
+ newconn w 1
+ recvfrom r 10
+ create w 1
+ sendto w 10
+ send_msg w 10
+ read r 10
+ bind w 1
+ lock n 1
+ connect w 1
+ setopt w 1
+ acceptfrom r 1
+ getopt r 1
+ ioctl n 1
+ getattr r 7
+ shutdown w 1
+ recv_msg r 10
+ listen r 1
+ accept r 1
+ relabelfrom r 10
+ write w 10
+
+class unix_dgram_socket 22
+ connect w 1
+ getopt r 1
+ listen r 1
+ relabelto w 10
+ name_bind n 1
+ accept r 1
+ shutdown w 1
+ getattr r 7
+ recv_msg r 10
+ append w 1
+ read r 10
+ create w 1
+ sendto w 10
+ ioctl n 1
+ setattr w 7
+ bind w 1
+ lock n 1
+ recvfrom r 10
+ send_msg w 10
+ write w 10
+ relabelfrom r 10
+ setopt w 1
+
+class sem 9
+ unix_read r 3
+ associate n 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ read r 10
+ setattr w 1
+ write w 10
+ unix_write w 3
+
+class msg 2
+ receive r 10
+ send w 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ associate n 1
+ setattr w 1
+
+class shm 10
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ lock w 1
+ associate n 1
+ setattr w 1
+ create w 1
+
+class ipc 9
+ write w 10
+ destroy w 1
+ unix_write w 3
+ getattr r 1
+ create w 1
+ read r 10
+ setattr w 1
+ unix_read r 3
+ associate n 1
+
+class passwd 4
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
diff --git a/apol/perm_maps/apol_perm_mapping_ver17 b/apol/perm_maps/apol_perm_mapping_ver17
new file mode 100644
index 0000000..648b538
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver17
@@ -0,0 +1,561 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+41
+
+class security 9
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+
+class process 23
+ fork n 1
+ transition w 1
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 29
+ net_bind_service n 1
+ sys_module n 1
+ sys_admin n 3
+ fowner n 1
+ net_raw n 1
+ setuid n 1
+ sys_chroot n 1
+ lease n 1
+ net_admin n 1
+ ipc_owner n 1
+ fsetid n 1
+ sys_resource n 1
+ sys_rawio n 1
+ sys_ptrace n 1
+ sys_nice n 1
+ setpcap n 3
+ kill n 1
+ sys_pacct n 1
+ sys_boot n 1
+ dac_override n 1
+ setgid n 3
+ net_broadcast n 1
+ chown n 3
+ sys_tty_config n 1
+ linux_immutable n 1
+ sys_time n 1
+ ipc_lock n 1
+ mknod n 1
+ dac_read_search n 1
+
+class filesystem 10
+ remount w 1
+ relabelfrom r 10
+ getattr r 1
+ relabelto w 10
+ mount w 1
+ transition w 1
+ quotaget r 1
+ quotamod w 1
+ unmount w 1
+ associate n 1
+
+class file 19
+ setattr w 7
+ swapon b 1
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+ entrypoint r 1
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ execute_no_trans r 1
+ ioctl n 1
+ execute r 1
+ append w 1
+ read r 10
+
+class dir 22
+ mounton b 1
+ search r 1
+ link w 1
+ quotaon b 1
+ append w 1
+ swapon b 1
+ rmdir b 1
+ create w 1
+ ioctl n 1
+ getattr r 7
+ remove_name w 1
+ rename w 5
+ read r 10
+ write w 10
+ relabelfrom r 10
+ execute r 1
+ relabelto w 10
+ lock n 1
+ setattr w 7
+ reparent w 1
+ add_name w 5
+ unlink w 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ relabelfrom r 10
+ append w 1
+ ioctl n 1
+ swapon b 1
+ create w 1
+ read r 10
+ write w 10
+ rename w 1
+ mounton b 1
+ quotaon b 1
+ lock n 1
+ relabelto w 10
+ getattr r 7
+ unlink w 1
+ execute r 1
+ link w 1
+ setattr w 7
+
+class chr_file 17
+ append w 1
+ swapon b 1
+ mounton b 1
+ quotaon b 1
+ create w 1
+ rename w 5
+ ioctl n 1
+ getattr r 7
+ link w 1
+ write w 10
+ execute r 1
+ relabelto w 10
+ setattr w 7
+ relabelfrom r 10
+ read r 10
+ unlink w 1
+ lock n 1
+
+class blk_file 17
+ getattr r 7
+ relabelto w 10
+ unlink w 1
+ ioctl n 1
+ execute r 1
+ append w 1
+ read r 10
+ setattr w 7
+ swapon b 1
+ write w 10
+ lock n 1
+ create w 1
+ rename w 5
+ mounton b 1
+ quotaon b 1
+ relabelfrom r 10
+ link w 1
+
+class sock_file 17
+ setattr w 7
+ rename w 1
+ ioctl n 1
+ link w 1
+ write w 10
+ mounton b 1
+ relabelto w 10
+ quotaon b 1
+ read r 10
+ unlink w 1
+ append w 1
+ lock n 1
+ getattr r 7
+ swapon b 1
+ relabelfrom r 10
+ execute r 1
+ create w 1
+
+class fifo_file 17
+ relabelto w 10
+ getattr r 7
+ lock n 1
+ execute r 1
+ unlink w 1
+ ioctl n 1
+ setattr w 7
+ append w 1
+ write w 10
+ swapon b 1
+ create w 1
+ link w 1
+ rename w 5
+ relabelfrom r 10
+ mounton b 1
+ quotaon b 1
+ read r 10
+
+class socket 22
+ append w 1
+ relabelfrom r 10
+ create w 1
+ read r 10
+ sendto w 10
+ connect w 1
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getattr r 7
+ write w 10
+ setopt w 1
+ getopt r 1
+ listen r 1
+ setattr w 7
+ shutdown w 1
+ relabelto w 10
+ recv_msg r 10
+ accept r 1
+ name_bind n 1
+
+class tcp_socket 26
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+
+class node 7
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ enforce_dest n 1
+ udp_recv r 10
+ udp_send w 10
+
+class netif 6
+ rawip_recv r 10
+ rawip_send w 10
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+
+class netlink_socket 22
+ listen r 1
+ accept r 1
+ read r 10
+ setattr w 7
+ append w 1
+ bind w 1
+ lock n 1
+ shutdown w 1
+ recv_msg r 10
+ create w 1
+ sendto w 10
+ relabelto w 10
+ ioctl n 1
+ name_bind n 1
+ connect w 1
+ write w 10
+ recvfrom r 10
+ send_msg w 10
+ relabelfrom r 10
+ setopt w 1
+ getattr r 7
+ getopt r 1
+
+class packet_socket 22
+ setattr w 7
+ read r 10
+ relabelto w 10
+ shutdown w 1
+ name_bind n 1
+ recv_msg r 10
+ setopt w 1
+ bind w 1
+ lock n 1
+ ioctl n 1
+ getopt r 1
+ connect w 1
+ relabelfrom r 10
+ listen r 1
+ write w 10
+ accept r 1
+ append w 1
+ recvfrom r 10
+ send_msg w 10
+ getattr r 7
+ create w 1
+ sendto w 10
+
+class key_socket 22
+ connect w 1
+ setopt w 1
+ relabelto w 10
+ read r 10
+ name_bind n 1
+ getopt r 1
+ getattr r 7
+ recvfrom r 10
+ send_msg w 10
+ bind w 1
+ listen r 1
+ lock n 1
+ accept r 1
+ append w 1
+ setattr w 7
+ ioctl n 1
+ create w 1
+ sendto w 10
+ relabelfrom r 10
+ write w 10
+ shutdown w 1
+ recv_msg r 10
+
+class unix_stream_socket 25
+ relabelto w 10
+ append w 1
+ name_bind n 1
+ setattr w 7
+ connectto w 1
+ newconn w 1
+ recvfrom r 10
+ create w 1
+ sendto w 10
+ send_msg w 10
+ read r 10
+ bind w 1
+ lock n 1
+ connect w 1
+ setopt w 1
+ acceptfrom r 1
+ getopt r 1
+ ioctl n 1
+ getattr r 7
+ shutdown w 1
+ recv_msg r 10
+ listen r 1
+ accept r 1
+ relabelfrom r 10
+ write w 10
+
+class unix_dgram_socket 22
+ connect w 1
+ getopt r 1
+ listen r 1
+ relabelto w 10
+ name_bind n 1
+ accept r 1
+ shutdown w 1
+ getattr r 7
+ recv_msg r 10
+ append w 1
+ read r 10
+ create w 1
+ sendto w 10
+ ioctl n 1
+ setattr w 7
+ bind w 1
+ lock n 1
+ recvfrom r 10
+ send_msg w 10
+ write w 10
+ relabelfrom r 10
+ setopt w 1
+
+class sem 9
+ unix_read r 3
+ associate n 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ read r 10
+ setattr w 1
+ write w 10
+ unix_write w 3
+
+class msg 2
+ receive r 10
+ send w 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ associate n 1
+ setattr w 1
+
+class shm 10
+ destroy w 1
+ write w 10
+ read r 10
+ getattr r 1
+ unix_write w 3
+ unix_read r 3
+ lock w 1
+ associate n 1
+ setattr w 1
+ create w 1
+
+class ipc 9
+ write w 10
+ destroy w 1
+ unix_write w 3
+ getattr r 1
+ create w 1
+ read r 10
+ setattr w 1
+ unix_read r 3
+ associate n 1
+
+class passwd 4
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+
diff --git a/apol/perm_maps/apol_perm_mapping_ver18 b/apol/perm_maps/apol_perm_mapping_ver18
new file mode 100644
index 0000000..b2f4403
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver18
@@ -0,0 +1,922 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+54
+
+class security 10
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+ setsecparam n 1
+
+class process 26
+ fork n 1
+ transition w 5
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+ dyntransition w 10
+ setcurrent w 1
+ execmem n 1
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 31
+ chown n 3
+ dac_override n 1
+ dac_read_search n 1
+ fowner n 1
+ fsetid n 1
+ kill n 1
+ setgid n 3
+ setuid n 1
+ setpcap n 3
+ linux_immutable n 1
+ net_bind_service n 1
+ net_broadcast n 1
+ net_admin n 1
+ net_raw n 1
+ ipc_lock n 1
+ ipc_owner n 1
+ sys_module n 1
+ sys_rawio n 1
+ sys_chroot n 1
+ sys_ptrace n 1
+ sys_pacct n 1
+ sys_admin n 3
+ sys_boot n 1
+ sys_nice n 1
+ sys_resource n 1
+ sys_time n 1
+ sys_tty_config n 1
+ mknod n 1
+ lease n 1
+ audit_write n 3
+ audit_control n 1
+
+class filesystem 10
+ mount w 1
+ remount w 1
+ unmount w 1
+ getattr r 1
+ relabelfrom r 10
+ relabelto w 10
+ transition w 1
+ associate n 1
+ quotamod w 1
+ quotaget r 1
+
+class file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class dir 22
+ add_name w 5
+ remove_name w 1
+ reparent w 1
+ search r 1
+ rmdir b 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class chr_file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class blk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class sock_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fifo_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class tcp_socket 26
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class node 7
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+ enforce_dest n 1
+
+class netif 6
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+
+class netlink_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class packet_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_stream_socket 25
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_dgram_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class sem 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class msg 2
+ send w 10
+ receive r 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class shm 10
+ lock w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class ipc 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class passwd 5
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+ crontab w 5
+
+class drawable 5
+ create w 1
+ destroy w 1
+ draw w 10
+ copy r 10
+ getattr r 7
+
+class window 26
+ addchild w 1
+ create w 1
+ destroy w 1
+ map w 1
+ unmap w 1
+ chstack w 10
+ chproplist w 7
+ chprop w 10
+ listprop r 5
+ getattr r 5
+ setattr w 5
+ setfocus w 1
+ move w 10
+ chselection w 10
+ chparent w 5
+ ctrllife w 5
+ enumerate w 1
+ transparent w 1
+ mousemotion w 10
+ clientcomevent w 5
+ inputevent w 5
+ drawevent w 5
+ windowchangeevent w 5
+windowchangerequest w 5
+ serverchangeevent w 5
+ extensionevent w 5
+
+class gc 4
+ create w 1
+ free w 1
+ getattr r 5
+ setattr w 5
+
+class font 4
+ load r 1
+ free w 1
+ getattr r 5
+ use r 1
+
+class colormap 9
+ create w 1
+ free w 1
+ install w 10
+ uninstall w 1
+ list r 5
+ read r 10
+ store w 10
+ getattr r 5
+ setattr w 5
+
+class property 4
+ create w 1
+ free w 1
+ read r 10
+ write w 10
+
+class cursor 5
+ create w 1
+ createglyph w 10
+ free w 1
+ assign w 10
+ setattr w 5
+
+class xclient 1
+ kill w 1
+
+class xinput 11
+ lookup r 10
+ getattr r 5
+ setattr w 5
+ setfocus w 10
+ warppointer w 10
+ activegrab w 1
+ passivegrab w 1
+ ungrab w 1
+ bell w 3
+ mousemotion w 10
+ relabelinput b 3
+
+class xserver 8
+ screensaver w 10
+ gethostlist r 7
+ sethostlist w 7
+ getfontpath r 7
+ setfontpath w 7
+ getattr r 7
+ grab w 10
+ ungrab w 1
+
+class xextension 2
+ query r 10
+ use b 1
+
+class pax 6
+ pageexec n 1
+ emutramp n 1
+ mprotect n 1
+ randmmap n 1
+ randexec n 1
+ segmexec n 1
+
+class netlink_route_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_firewall_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_tcpdiag_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_nflog_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_xfrm_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_selinux_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_audit_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_ip6fw_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_dnrt_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class nscd 8
+ getpwd r 7
+ getgrp r 7
+ gethost r 7
+ getstat r 7
+ admin w 5
+ shmempwd r 7
+ shmemgrp r 7
+ shmemhost r 7
+
+class association 2
+ sendto w 10
+ recvfrom r 10
diff --git a/apol/perm_maps/apol_perm_mapping_ver19 b/apol/perm_maps/apol_perm_mapping_ver19
new file mode 100644
index 0000000..77e9a28
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver19
@@ -0,0 +1,952 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+55
+
+class security 11
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+ setsecparam n 1
+ setcheckreqprot n 1
+
+class process 28
+ fork n 1
+ transition w 5
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+ dyntransition w 10
+ setcurrent w 1
+ execmem n 1
+ execstack n 1
+ execheap n 1
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 31
+ chown n 3
+ dac_override n 1
+ dac_read_search n 1
+ fowner n 1
+ fsetid n 1
+ kill n 1
+ setgid n 3
+ setuid n 1
+ setpcap n 3
+ linux_immutable n 1
+ net_bind_service n 1
+ net_broadcast n 1
+ net_admin n 1
+ net_raw n 1
+ ipc_lock n 1
+ ipc_owner n 1
+ sys_module n 1
+ sys_rawio n 1
+ sys_chroot n 1
+ sys_ptrace n 1
+ sys_pacct n 1
+ sys_admin n 3
+ sys_boot n 1
+ sys_nice n 1
+ sys_resource n 1
+ sys_time n 1
+ sys_tty_config n 1
+ mknod n 1
+ lease n 1
+ audit_write n 3
+ audit_control n 1
+
+class filesystem 10
+ mount w 1
+ remount w 1
+ unmount w 1
+ getattr r 1
+ relabelfrom r 10
+ relabelto w 10
+ transition w 1
+ associate n 1
+ quotamod w 1
+ quotaget r 1
+
+class file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class dir 22
+ add_name w 5
+ remove_name w 1
+ reparent w 1
+ search r 1
+ rmdir b 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class chr_file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class blk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class sock_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fifo_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class tcp_socket 27
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ name_connect w 1
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class node 7
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+ enforce_dest n 1
+
+class netif 6
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+
+class netlink_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class packet_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_stream_socket 25
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_dgram_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class sem 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class msg 2
+ send w 10
+ receive r 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class shm 10
+ lock w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class ipc 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class passwd 5
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+ crontab w 5
+
+class drawable 5
+ create w 1
+ destroy w 1
+ draw w 10
+ copy r 10
+ getattr r 7
+
+class window 26
+ addchild w 1
+ create w 1
+ destroy w 1
+ map w 1
+ unmap w 1
+ chstack w 10
+ chproplist w 7
+ chprop w 10
+ listprop r 5
+ getattr r 5
+ setattr w 5
+ setfocus w 1
+ move w 10
+ chselection w 10
+ chparent w 5
+ ctrllife w 5
+ enumerate w 1
+ transparent w 1
+ mousemotion w 10
+ clientcomevent w 5
+ inputevent w 5
+ drawevent w 5
+ windowchangeevent w 5
+windowchangerequest w 5
+ serverchangeevent w 5
+ extensionevent w 5
+
+class gc 4
+ create w 1
+ free w 1
+ getattr r 5
+ setattr w 5
+
+class font 4
+ load r 1
+ free w 1
+ getattr r 5
+ use r 1
+
+class colormap 9
+ create w 1
+ free w 1
+ install w 10
+ uninstall w 1
+ list r 5
+ read r 10
+ store w 10
+ getattr r 5
+ setattr w 5
+
+class property 4
+ create w 1
+ free w 1
+ read r 10
+ write w 10
+
+class cursor 5
+ create w 1
+ createglyph w 10
+ free w 1
+ assign w 10
+ setattr w 5
+
+class xclient 1
+ kill w 1
+
+class xinput 11
+ lookup r 10
+ getattr r 5
+ setattr w 5
+ setfocus w 10
+ warppointer w 10
+ activegrab w 1
+ passivegrab w 1
+ ungrab w 1
+ bell w 3
+ mousemotion w 10
+ relabelinput b 3
+
+class xserver 8
+ screensaver w 10
+ gethostlist r 7
+ sethostlist w 7
+ getfontpath r 7
+ setfontpath w 7
+ getattr r 7
+ grab w 10
+ ungrab w 1
+
+class xextension 2
+ query r 10
+ use b 1
+
+class pax 6
+ pageexec n 1
+ emutramp n 1
+ mprotect n 1
+ randmmap n 1
+ randexec n 1
+ segmexec n 1
+
+class netlink_route_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_firewall_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_tcpdiag_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_nflog_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_xfrm_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_selinux_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_audit_socket 26
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ nlmsg_relay w 10
+ nlmsg_readpriv r 10
+
+class netlink_ip6fw_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_dnrt_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_kobject_uevent_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class nscd 8
+ getpwd r 7
+ getgrp r 7
+ gethost r 7
+ getstat r 7
+ admin w 5
+ shmempwd r 7
+ shmemgrp r 7
+ shmemhost r 7
+
+class association 2
+ sendto w 10
+ recvfrom r 10
diff --git a/apol/perm_maps/apol_perm_mapping_ver20 b/apol/perm_maps/apol_perm_mapping_ver20
new file mode 100644
index 0000000..a7123ad
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver20
@@ -0,0 +1,993 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+58
+
+class security 11
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+ setsecparam n 1
+ setcheckreqprot n 1
+
+class process 29
+ fork n 1
+ transition w 5
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+ dyntransition w 10
+ setcurrent w 1
+ execmem n 1
+ execstack n 1
+ execheap n 1
+ setkeycreate w 1
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 31
+ chown n 3
+ dac_override n 1
+ dac_read_search n 1
+ fowner n 1
+ fsetid n 1
+ kill n 1
+ setgid n 3
+ setuid n 1
+ setpcap n 3
+ linux_immutable n 1
+ net_bind_service n 1
+ net_broadcast n 1
+ net_admin n 1
+ net_raw n 1
+ ipc_lock n 1
+ ipc_owner n 1
+ sys_module n 1
+ sys_rawio n 1
+ sys_chroot n 1
+ sys_ptrace n 1
+ sys_pacct n 1
+ sys_admin n 3
+ sys_boot n 1
+ sys_nice n 1
+ sys_resource n 1
+ sys_time n 1
+ sys_tty_config n 1
+ mknod n 1
+ lease n 1
+ audit_write n 3
+ audit_control n 1
+
+class filesystem 10
+ mount w 1
+ remount w 1
+ unmount w 1
+ getattr r 1
+ relabelfrom r 10
+ relabelto w 10
+ transition w 1
+ associate n 1
+ quotamod w 1
+ quotaget r 1
+
+class file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class dir 22
+ add_name w 5
+ remove_name w 1
+ reparent w 1
+ search r 1
+ rmdir b 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class chr_file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class blk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class sock_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fifo_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class tcp_socket 27
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ name_connect w 1
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class node 7
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+ enforce_dest n 1
+
+class netif 6
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+
+class netlink_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class packet_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_stream_socket 25
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_dgram_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class sem 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class msg 2
+ send w 10
+ receive r 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class shm 10
+ lock w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class ipc 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class passwd 5
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+ crontab w 5
+
+class drawable 5
+ create w 1
+ destroy w 1
+ draw w 10
+ copy r 10
+ getattr r 7
+
+class window 26
+ addchild w 1
+ create w 1
+ destroy w 1
+ map w 1
+ unmap w 1
+ chstack w 10
+ chproplist w 7
+ chprop w 10
+ listprop r 5
+ getattr r 5
+ setattr w 5
+ setfocus w 1
+ move w 10
+ chselection w 10
+ chparent w 5
+ ctrllife w 5
+ enumerate w 1
+ transparent w 1
+ mousemotion w 10
+ clientcomevent w 5
+ inputevent w 5
+ drawevent w 5
+ windowchangeevent w 5
+windowchangerequest w 5
+ serverchangeevent w 5
+ extensionevent w 5
+
+class gc 4
+ create w 1
+ free w 1
+ getattr r 5
+ setattr w 5
+
+class font 4
+ load r 1
+ free w 1
+ getattr r 5
+ use r 1
+
+class colormap 9
+ create w 1
+ free w 1
+ install w 10
+ uninstall w 1
+ list r 5
+ read r 10
+ store w 10
+ getattr r 5
+ setattr w 5
+
+class property 4
+ create w 1
+ free w 1
+ read r 10
+ write w 10
+
+class cursor 5
+ create w 1
+ createglyph w 10
+ free w 1
+ assign w 10
+ setattr w 5
+
+class xclient 1
+ kill w 1
+
+class xinput 11
+ lookup r 10
+ getattr r 5
+ setattr w 5
+ setfocus w 10
+ warppointer w 10
+ activegrab w 1
+ passivegrab w 1
+ ungrab w 1
+ bell w 3
+ mousemotion w 10
+ relabelinput b 3
+
+class xserver 8
+ screensaver w 10
+ gethostlist r 7
+ sethostlist w 7
+ getfontpath r 7
+ setfontpath w 7
+ getattr r 7
+ grab w 10
+ ungrab w 1
+
+class xextension 2
+ query r 10
+ use b 1
+
+class pax 6
+ pageexec n 1
+ emutramp n 1
+ mprotect n 1
+ randmmap n 1
+ randexec n 1
+ segmexec n 1
+
+class netlink_route_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_firewall_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_tcpdiag_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_nflog_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_xfrm_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_selinux_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_audit_socket 26
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ nlmsg_relay w 10
+ nlmsg_readpriv r 10
+
+class netlink_ip6fw_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_dnrt_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_kobject_uevent_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class nscd 8
+ getpwd r 7
+ getgrp r 7
+ gethost r 7
+ getstat r 7
+ admin w 5
+ shmempwd r 7
+ shmemgrp r 7
+ shmemhost r 7
+
+class association 4
+ sendto w 10
+ recvfrom r 10
+ setcontext w 3
+ polmatch r 1
+
+class appletalk_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key 7
+ view r 7
+ read r 10
+ write w 10
+ search r 5
+ link w 7
+ setattr w 7
+ create w 10
+
+class packet 3
+ send w 10
+ recv r 10
+ relabelto w 3
diff --git a/apol/perm_maps/apol_perm_mapping_ver21 b/apol/perm_maps/apol_perm_mapping_ver21
new file mode 100644
index 0000000..e5b115f
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver21
@@ -0,0 +1,998 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+59
+
+class security 11
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+ setsecparam n 1
+ setcheckreqprot n 1
+
+class process 30
+ fork n 1
+ transition w 5
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+ dyntransition w 10
+ setcurrent w 1
+ execmem n 1
+ execstack n 1
+ execheap n 1
+ setkeycreate w 1
+ setsockcreate w 1
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 31
+ chown n 3
+ dac_override n 1
+ dac_read_search n 1
+ fowner n 1
+ fsetid n 1
+ kill n 1
+ setgid n 3
+ setuid n 1
+ setpcap n 3
+ linux_immutable n 1
+ net_bind_service n 1
+ net_broadcast n 1
+ net_admin n 1
+ net_raw n 1
+ ipc_lock n 1
+ ipc_owner n 1
+ sys_module n 1
+ sys_rawio n 1
+ sys_chroot n 1
+ sys_ptrace n 1
+ sys_pacct n 1
+ sys_admin n 3
+ sys_boot n 1
+ sys_nice n 1
+ sys_resource n 1
+ sys_time n 1
+ sys_tty_config n 1
+ mknod n 1
+ lease n 1
+ audit_write n 3
+ audit_control n 1
+
+class filesystem 10
+ mount w 1
+ remount w 1
+ unmount w 1
+ getattr r 1
+ relabelfrom r 10
+ relabelto w 10
+ transition w 1
+ associate n 1
+ quotamod w 1
+ quotaget r 1
+
+class file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class dir 22
+ add_name w 5
+ remove_name w 1
+ reparent w 1
+ search r 1
+ rmdir b 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class chr_file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class blk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class sock_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fifo_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class tcp_socket 27
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ name_connect w 1
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class node 7
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+ enforce_dest n 1
+
+class netif 6
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+
+class netlink_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class packet_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_stream_socket 25
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_dgram_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class sem 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class msg 2
+ send w 10
+ receive r 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class shm 10
+ lock w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class ipc 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class passwd 5
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+ crontab w 5
+
+class drawable 5
+ create w 1
+ destroy w 1
+ draw w 10
+ copy r 10
+ getattr r 7
+
+class window 26
+ addchild w 1
+ create w 1
+ destroy w 1
+ map w 1
+ unmap w 1
+ chstack w 10
+ chproplist w 7
+ chprop w 10
+ listprop r 5
+ getattr r 5
+ setattr w 5
+ setfocus w 1
+ move w 10
+ chselection w 10
+ chparent w 5
+ ctrllife w 5
+ enumerate w 1
+ transparent w 1
+ mousemotion w 10
+ clientcomevent w 5
+ inputevent w 5
+ drawevent w 5
+ windowchangeevent w 5
+windowchangerequest w 5
+ serverchangeevent w 5
+ extensionevent w 5
+
+class gc 4
+ create w 1
+ free w 1
+ getattr r 5
+ setattr w 5
+
+class font 4
+ load r 1
+ free w 1
+ getattr r 5
+ use r 1
+
+class colormap 9
+ create w 1
+ free w 1
+ install w 10
+ uninstall w 1
+ list r 5
+ read r 10
+ store w 10
+ getattr r 5
+ setattr w 5
+
+class property 4
+ create w 1
+ free w 1
+ read r 10
+ write w 10
+
+class cursor 5
+ create w 1
+ createglyph w 10
+ free w 1
+ assign w 10
+ setattr w 5
+
+class xclient 1
+ kill w 1
+
+class xinput 11
+ lookup r 10
+ getattr r 5
+ setattr w 5
+ setfocus w 10
+ warppointer w 10
+ activegrab w 1
+ passivegrab w 1
+ ungrab w 1
+ bell w 3
+ mousemotion w 10
+ relabelinput b 3
+
+class xserver 8
+ screensaver w 10
+ gethostlist r 7
+ sethostlist w 7
+ getfontpath r 7
+ setfontpath w 7
+ getattr r 7
+ grab w 10
+ ungrab w 1
+
+class xextension 2
+ query r 10
+ use b 1
+
+class pax 6
+ pageexec n 1
+ emutramp n 1
+ mprotect n 1
+ randmmap n 1
+ randexec n 1
+ segmexec n 1
+
+class netlink_route_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_firewall_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_tcpdiag_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_nflog_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_xfrm_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_selinux_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_audit_socket 26
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ nlmsg_relay w 10
+ nlmsg_readpriv r 10
+
+class netlink_ip6fw_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_dnrt_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_kobject_uevent_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class nscd 8
+ getpwd r 7
+ getgrp r 7
+ gethost r 7
+ getstat r 7
+ admin w 5
+ shmempwd r 7
+ shmemgrp r 7
+ shmemhost r 7
+
+class association 4
+ sendto w 10
+ recvfrom r 10
+ setcontext w 3
+ polmatch r 1
+
+class appletalk_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key 7
+ view r 7
+ read r 10
+ write w 10
+ search r 5
+ link w 7
+ setattr w 7
+ create w 10
+
+class packet 3
+ send w 10
+ recv r 10
+ relabelto w 3
+
+class context 2
+ contains n 1
+ translate n 1
diff --git a/apol/perm_maps/apol_perm_mapping_ver22 b/apol/perm_maps/apol_perm_mapping_ver22
new file mode 100644
index 0000000..e5b115f
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver22
@@ -0,0 +1,998 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+59
+
+class security 11
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+ setsecparam n 1
+ setcheckreqprot n 1
+
+class process 30
+ fork n 1
+ transition w 5
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+ dyntransition w 10
+ setcurrent w 1
+ execmem n 1
+ execstack n 1
+ execheap n 1
+ setkeycreate w 1
+ setsockcreate w 1
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 31
+ chown n 3
+ dac_override n 1
+ dac_read_search n 1
+ fowner n 1
+ fsetid n 1
+ kill n 1
+ setgid n 3
+ setuid n 1
+ setpcap n 3
+ linux_immutable n 1
+ net_bind_service n 1
+ net_broadcast n 1
+ net_admin n 1
+ net_raw n 1
+ ipc_lock n 1
+ ipc_owner n 1
+ sys_module n 1
+ sys_rawio n 1
+ sys_chroot n 1
+ sys_ptrace n 1
+ sys_pacct n 1
+ sys_admin n 3
+ sys_boot n 1
+ sys_nice n 1
+ sys_resource n 1
+ sys_time n 1
+ sys_tty_config n 1
+ mknod n 1
+ lease n 1
+ audit_write n 3
+ audit_control n 1
+
+class filesystem 10
+ mount w 1
+ remount w 1
+ unmount w 1
+ getattr r 1
+ relabelfrom r 10
+ relabelto w 10
+ transition w 1
+ associate n 1
+ quotamod w 1
+ quotaget r 1
+
+class file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class dir 22
+ add_name w 5
+ remove_name w 1
+ reparent w 1
+ search r 1
+ rmdir b 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class chr_file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class blk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class sock_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fifo_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class tcp_socket 27
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ name_connect w 1
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class node 7
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+ enforce_dest n 1
+
+class netif 6
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+
+class netlink_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class packet_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_stream_socket 25
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_dgram_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class sem 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class msg 2
+ send w 10
+ receive r 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class shm 10
+ lock w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class ipc 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class passwd 5
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+ crontab w 5
+
+class drawable 5
+ create w 1
+ destroy w 1
+ draw w 10
+ copy r 10
+ getattr r 7
+
+class window 26
+ addchild w 1
+ create w 1
+ destroy w 1
+ map w 1
+ unmap w 1
+ chstack w 10
+ chproplist w 7
+ chprop w 10
+ listprop r 5
+ getattr r 5
+ setattr w 5
+ setfocus w 1
+ move w 10
+ chselection w 10
+ chparent w 5
+ ctrllife w 5
+ enumerate w 1
+ transparent w 1
+ mousemotion w 10
+ clientcomevent w 5
+ inputevent w 5
+ drawevent w 5
+ windowchangeevent w 5
+windowchangerequest w 5
+ serverchangeevent w 5
+ extensionevent w 5
+
+class gc 4
+ create w 1
+ free w 1
+ getattr r 5
+ setattr w 5
+
+class font 4
+ load r 1
+ free w 1
+ getattr r 5
+ use r 1
+
+class colormap 9
+ create w 1
+ free w 1
+ install w 10
+ uninstall w 1
+ list r 5
+ read r 10
+ store w 10
+ getattr r 5
+ setattr w 5
+
+class property 4
+ create w 1
+ free w 1
+ read r 10
+ write w 10
+
+class cursor 5
+ create w 1
+ createglyph w 10
+ free w 1
+ assign w 10
+ setattr w 5
+
+class xclient 1
+ kill w 1
+
+class xinput 11
+ lookup r 10
+ getattr r 5
+ setattr w 5
+ setfocus w 10
+ warppointer w 10
+ activegrab w 1
+ passivegrab w 1
+ ungrab w 1
+ bell w 3
+ mousemotion w 10
+ relabelinput b 3
+
+class xserver 8
+ screensaver w 10
+ gethostlist r 7
+ sethostlist w 7
+ getfontpath r 7
+ setfontpath w 7
+ getattr r 7
+ grab w 10
+ ungrab w 1
+
+class xextension 2
+ query r 10
+ use b 1
+
+class pax 6
+ pageexec n 1
+ emutramp n 1
+ mprotect n 1
+ randmmap n 1
+ randexec n 1
+ segmexec n 1
+
+class netlink_route_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_firewall_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_tcpdiag_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_nflog_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_xfrm_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_selinux_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_audit_socket 26
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ nlmsg_relay w 10
+ nlmsg_readpriv r 10
+
+class netlink_ip6fw_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_dnrt_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_kobject_uevent_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class nscd 8
+ getpwd r 7
+ getgrp r 7
+ gethost r 7
+ getstat r 7
+ admin w 5
+ shmempwd r 7
+ shmemgrp r 7
+ shmemhost r 7
+
+class association 4
+ sendto w 10
+ recvfrom r 10
+ setcontext w 3
+ polmatch r 1
+
+class appletalk_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key 7
+ view r 7
+ read r 10
+ write w 10
+ search r 5
+ link w 7
+ setattr w 7
+ create w 10
+
+class packet 3
+ send w 10
+ recv r 10
+ relabelto w 3
+
+class context 2
+ contains n 1
+ translate n 1
diff --git a/apol/perm_maps/apol_perm_mapping_ver23 b/apol/perm_maps/apol_perm_mapping_ver23
new file mode 100644
index 0000000..e5b115f
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver23
@@ -0,0 +1,998 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+59
+
+class security 11
+ compute_av n 1
+ compute_create n 1
+ compute_member n 1
+ check_context n 1
+ load_policy n 1
+ compute_relabel n 1
+ compute_user n 1
+ setenforce n 1
+ setbool n 1
+ setsecparam n 1
+ setcheckreqprot n 1
+
+class process 30
+ fork n 1
+ transition w 5
+ sigchld w 1
+ sigkill w 1
+ sigstop w 1
+ signull n 1
+ signal w 5
+ ptrace b 10
+ getsched r 1
+ setsched w 1
+ getsession r 1
+ getpgid r 1
+ setpgid w 5
+ getcap r 3
+ setcap w 1
+ share b 1
+ getattr r 1
+ setexec w 1
+ setfscreate w 1
+ noatsecure n 1
+ siginh n 1
+ setrlimit n 1
+ rlimitinh n 1
+ dyntransition w 10
+ setcurrent w 1
+ execmem n 1
+ execstack n 1
+ execheap n 1
+ setkeycreate w 1
+ setsockcreate w 1
+
+class system 4
+ ipc_info n 1
+ syslog_read n 1
+ syslog_mod n 1
+ syslog_console n 1
+
+class capability 31
+ chown n 3
+ dac_override n 1
+ dac_read_search n 1
+ fowner n 1
+ fsetid n 1
+ kill n 1
+ setgid n 3
+ setuid n 1
+ setpcap n 3
+ linux_immutable n 1
+ net_bind_service n 1
+ net_broadcast n 1
+ net_admin n 1
+ net_raw n 1
+ ipc_lock n 1
+ ipc_owner n 1
+ sys_module n 1
+ sys_rawio n 1
+ sys_chroot n 1
+ sys_ptrace n 1
+ sys_pacct n 1
+ sys_admin n 3
+ sys_boot n 1
+ sys_nice n 1
+ sys_resource n 1
+ sys_time n 1
+ sys_tty_config n 1
+ mknod n 1
+ lease n 1
+ audit_write n 3
+ audit_control n 1
+
+class filesystem 10
+ mount w 1
+ remount w 1
+ unmount w 1
+ getattr r 1
+ relabelfrom r 10
+ relabelto w 10
+ transition w 1
+ associate n 1
+ quotamod w 1
+ quotaget r 1
+
+class file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class dir 22
+ add_name w 5
+ remove_name w 1
+ reparent w 1
+ search r 1
+ rmdir b 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fd 1
+ use b 1
+
+class lnk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class chr_file 20
+ execute_no_trans r 1
+ entrypoint r 1
+ execmod n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class blk_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class sock_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 1
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class fifo_file 17
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ unlink w 1
+ link w 1
+ rename w 5
+ execute r 1
+ swapon b 1
+ quotaon b 1
+ mounton b 1
+
+class socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class tcp_socket 27
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ name_connect w 1
+
+class udp_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class rawip_socket 23
+ node_bind n 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class node 7
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+ enforce_dest n 1
+
+class netif 6
+ tcp_recv r 10
+ tcp_send w 10
+ udp_recv r 10
+ udp_send w 10
+ rawip_recv r 10
+ rawip_send w 10
+
+class netlink_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class packet_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_stream_socket 25
+ connectto w 1
+ newconn w 1
+ acceptfrom r 1
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class unix_dgram_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class sem 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class msg 2
+ send w 10
+ receive r 10
+
+class msgq 10
+ enqueue w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class shm 10
+ lock w 1
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class ipc 9
+ create w 1
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ write w 10
+ associate n 1
+ unix_read r 3
+ unix_write w 3
+
+class passwd 5
+ passwd w 1
+ chfn w 5
+ chsh w 5
+ rootok n 1
+ crontab w 5
+
+class drawable 5
+ create w 1
+ destroy w 1
+ draw w 10
+ copy r 10
+ getattr r 7
+
+class window 26
+ addchild w 1
+ create w 1
+ destroy w 1
+ map w 1
+ unmap w 1
+ chstack w 10
+ chproplist w 7
+ chprop w 10
+ listprop r 5
+ getattr r 5
+ setattr w 5
+ setfocus w 1
+ move w 10
+ chselection w 10
+ chparent w 5
+ ctrllife w 5
+ enumerate w 1
+ transparent w 1
+ mousemotion w 10
+ clientcomevent w 5
+ inputevent w 5
+ drawevent w 5
+ windowchangeevent w 5
+windowchangerequest w 5
+ serverchangeevent w 5
+ extensionevent w 5
+
+class gc 4
+ create w 1
+ free w 1
+ getattr r 5
+ setattr w 5
+
+class font 4
+ load r 1
+ free w 1
+ getattr r 5
+ use r 1
+
+class colormap 9
+ create w 1
+ free w 1
+ install w 10
+ uninstall w 1
+ list r 5
+ read r 10
+ store w 10
+ getattr r 5
+ setattr w 5
+
+class property 4
+ create w 1
+ free w 1
+ read r 10
+ write w 10
+
+class cursor 5
+ create w 1
+ createglyph w 10
+ free w 1
+ assign w 10
+ setattr w 5
+
+class xclient 1
+ kill w 1
+
+class xinput 11
+ lookup r 10
+ getattr r 5
+ setattr w 5
+ setfocus w 10
+ warppointer w 10
+ activegrab w 1
+ passivegrab w 1
+ ungrab w 1
+ bell w 3
+ mousemotion w 10
+ relabelinput b 3
+
+class xserver 8
+ screensaver w 10
+ gethostlist r 7
+ sethostlist w 7
+ getfontpath r 7
+ setfontpath w 7
+ getattr r 7
+ grab w 10
+ ungrab w 1
+
+class xextension 2
+ query r 10
+ use b 1
+
+class pax 6
+ pageexec n 1
+ emutramp n 1
+ mprotect n 1
+ randmmap n 1
+ randexec n 1
+ segmexec n 1
+
+class netlink_route_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_firewall_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_tcpdiag_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_nflog_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_xfrm_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_selinux_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_audit_socket 26
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+ nlmsg_relay w 10
+ nlmsg_readpriv r 10
+
+class netlink_ip6fw_socket 24
+ nlmsg_read r 10
+ nlmsg_write w 10
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_dnrt_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class netlink_kobject_uevent_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 7
+ setattr w 7
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class nscd 8
+ getpwd r 7
+ getgrp r 7
+ gethost r 7
+ getstat r 7
+ admin w 5
+ shmempwd r 7
+ shmemgrp r 7
+ shmemhost r 7
+
+class association 4
+ sendto w 10
+ recvfrom r 10
+ setcontext w 3
+ polmatch r 1
+
+class appletalk_socket 22
+ ioctl n 1
+ read r 10
+ write w 10
+ create w 1
+ getattr r 1
+ setattr w 1
+ lock n 1
+ relabelfrom r 10
+ relabelto w 10
+ append w 1
+ bind w 1
+ connect w 1
+ listen r 1
+ accept r 1
+ getopt r 1
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ name_bind n 1
+
+class key 7
+ view r 7
+ read r 10
+ write w 10
+ search r 5
+ link w 7
+ setattr w 7
+ create w 10
+
+class packet 3
+ send w 10
+ recv r 10
+ relabelto w 3
+
+class context 2
+ contains n 1
+ translate n 1
diff --git a/apol/perm_maps/apol_perm_mapping_ver24 b/apol/perm_maps/apol_perm_mapping_ver24
new file mode 100644
index 0000000..102ce04
--- /dev/null
+++ b/apol/perm_maps/apol_perm_mapping_ver24
@@ -0,0 +1,1227 @@
+# This is a permission map file for use in policy analysis. This
+# file maps object permissions (read, getattr, setattr, ..., etc.)
+# for an object class, to exactly one of the following: read, write,
+# both, or none. This file may be edited as long as the specific
+# syntax rules are obeyed.
+#
+# For each object class, there is a set of object permissions that are
+# individually mapped to read, write, both, or none. If a new object
+# class is added, make sure that the current number of object classes
+# is increased.
+#
+# The syntax for an object class definition is:
+# class <class_name> <num_permissions>
+#
+# This is followed by each permission and its individual mapping to one
+# of the following:
+#
+# r = Read
+# w = Write
+# n = None
+# b = Both
+#
+# Additionally, you can choose to follow the mapping with an optional
+# permission weight value from 1 (less importance) to 10 (higher importance).
+# 10 is the default weight value if one is not provided.
+#
+# Look to the examples below for further clarification.
+#
+# Number of object classes.
+77
+
+class netlink_audit_socket 27
+ nlmsg_relay w 10
+ nlmsg_tty_audit w 10
+ nlmsg_readpriv r 10
+ nlmsg_write w 10
+ nlmsg_read r 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class tcp_socket 27
+ acceptfrom r 1
+ connectto w 1
+ node_bind n 1
+ newconn w 1
+ name_connect w 1
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class msgq 10
+ enqueue w 1
+ associate n 1
+ create w 1
+ write w 10
+ unix_read r 3
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ unix_write w 3
+
+class x_property 7
+ append w 10
+ create w 1
+ write w 10
+ destroy w 1
+ getattr r 7
+ setattr w 7
+ read r 10
+
+class db_procedure 9
+ execute r 1
+ install w 10
+ entrypoint r 1
+ drop w 1
+ create w 1
+ relabelfrom r 1
+ getattr r 7
+ setattr w 7
+ relabelto w 1
+
+class dir 23
+ rmdir b 1
+ remove_name w 1
+ add_name w 5
+ reparent w 1
+ search r 1
+ open n 1
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 5
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class peer 1
+ recv r 10
+
+class blk_file 18
+ open n 1
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 5
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class chr_file 21
+ entrypoint r 1
+ execmod n 1
+ execute_no_trans r 1
+ open n 1
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 5
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class db_table 12
+ select n 1
+ delete w 1
+ update w 10
+ insert w 10
+ use r 10
+ lock n 1
+ drop w 1
+ create w 1
+ relabelfrom r 1
+ getattr r 7
+ setattr w 7
+ relabelto w 1
+
+class db_tuple 7
+ select n 1
+ delete w 1
+ update w 10
+ relabelfrom r 1
+ insert w 10
+ use r 10
+ relabelto w 1
+
+class dbus 2
+ acquire_svc b 1
+ send_msg w 10
+
+class ipc 9
+ associate n 1
+ create w 1
+ write w 10
+ unix_read r 3
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ unix_write w 3
+
+class lnk_file 17
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 1
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class process 30
+ getcap r 3
+ setcap w 1
+ sigstop w 1
+ sigchld w 1
+ share b 1
+ execheap n 1
+ setcurrent w 1
+ setfscreate w 1
+ setkeycreate w 1
+ siginh n 1
+ dyntransition w 10
+ transition w 5
+ fork n 1
+ getsession r 1
+ noatsecure n 1
+ sigkill w 1
+ signull n 1
+ setrlimit n 1
+ getattr r 1
+ getsched r 1
+ setexec w 1
+ setsched w 1
+ getpgid r 1
+ setpgid w 5
+ ptrace b 10
+ execstack n 1
+ rlimitinh n 1
+ setsockcreate w 1
+ signal w 5
+ execmem n 1
+
+class capability2 2
+ mac_override n 1
+ mac_admin n 1
+
+class fd 1
+ use b 1
+
+class packet 7
+ forward_out w 10
+ flow_out w 10
+ send w 10
+ recv r 10
+ forward_in r 10
+ relabelto w 3
+ flow_in r 10
+
+class socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class fifo_file 18
+ open n 1
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 5
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class file 21
+ entrypoint r 1
+ execmod n 1
+ execute_no_trans r 1
+ open n 1
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 5
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class node 11
+ rawip_recv r 10
+ tcp_recv r 10
+ udp_recv r 10
+ rawip_send w 10
+ tcp_send w 10
+ udp_send w 10
+ dccp_recv r 10
+ dccp_send w 10
+ enforce_dest n 1
+ sendto w 10
+ recvfrom r 10
+
+class x_cursor 7
+ create w 1
+ write w 10
+ destroy w 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ use r 1
+
+class x_server 6
+ record r 10
+ getattr r 7
+ grab w 1
+ setattr w 7
+ manage w 10
+ debug b 10
+
+class netlink_nflog_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class key 7
+ create w 10
+ write w 10
+ view r 7
+ link w 7
+ setattr w 7
+ read r 10
+ search r 5
+
+class netlink_tcpdiag_socket 24
+ nlmsg_write w 10
+ nlmsg_read r 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class unix_stream_socket 25
+ acceptfrom r 1
+ connectto w 1
+ newconn w 1
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_synthetic_event 2
+ send w 10
+ receive r 10
+
+class db_database 11
+ access b 10
+ set_param w 7
+ load_module r 10
+ get_param r 7
+ install_module r 10
+ drop w 1
+ create w 1
+ relabelfrom r 1
+ getattr r 7
+ setattr w 7
+ relabelto w 1
+
+class kernel_service 2
+ create_files_as n 1
+ use_as_override n 1
+
+class netlink_route_socket 24
+ nlmsg_write w 10
+ nlmsg_read r 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_extension 2
+ use r 1
+ query r 5
+
+class shm 10
+ lock w 1
+ associate n 1
+ create w 1
+ write w 10
+ unix_read r 3
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ unix_write w 3
+
+class x_resource 2
+ write w 10
+ read r 10
+
+class netlink_selinux_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class capability 32
+ setfcap n 1
+ setpcap n 3
+ fowner n 1
+ sys_boot n 1
+ sys_tty_config n 1
+ net_raw n 1
+ sys_admin n 3
+ sys_chroot n 1
+ sys_module n 1
+ sys_rawio n 1
+ dac_override n 1
+ ipc_owner n 1
+ kill n 1
+ dac_read_search n 1
+ sys_pacct n 1
+ net_broadcast n 1
+ net_bind_service n 1
+ sys_nice n 1
+ sys_time n 1
+ fsetid n 1
+ mknod n 1
+ setgid n 3
+ setuid n 1
+ lease n 1
+ net_admin n 1
+ audit_write n 3
+ linux_immutable n 1
+ sys_ptrace n 1
+ audit_control n 1
+ ipc_lock n 1
+ sys_resource n 1
+ chown n 3
+
+class netlink_ip6fw_socket 24
+ nlmsg_write w 10
+ nlmsg_read r 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class dccp_socket 24
+ node_bind n 1
+ name_connect w 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class netlink_firewall_socket 24
+ nlmsg_write w 10
+ nlmsg_read r 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class sock_file 18
+ open n 1
+ append w 1
+ create w 1
+ execute r 1
+ write w 10
+ relabelfrom r 10
+ link w 1
+ unlink w 1
+ ioctl n 1
+ getattr r 7
+ setattr w 7
+ read r 10
+ rename w 1
+ lock n 1
+ relabelto w 10
+ mounton b 1
+ quotaon b 1
+ swapon b 1
+
+class unix_dgram_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class netlink_kobject_uevent_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class db_blob 10
+ write w 10
+ export r 10
+ import w 10
+ read r 10
+ drop w 1
+ create w 1
+ relabelfrom r 1
+ getattr r 7
+ setattr w 7
+ relabelto w 1
+
+class filesystem 10
+ associate n 1
+ quotaget r 1
+ relabelfrom r 10
+ transition w 1
+ getattr r 1
+ quotamod w 1
+ mount w 1
+ remount w 1
+ unmount w 1
+ relabelto w 10
+
+class netlink_xfrm_socket 24
+ nlmsg_write w 10
+ nlmsg_read r 10
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_device 19
+ get_property r 7
+ list_property r 7
+ set_property w 7
+ add w 1
+ setfocus w 1
+ create w 1
+ freeze w 1
+ getfocus r 1
+ remove w 1
+ write w 10
+ force_cursor w 1
+ destroy w 1
+ bell w 1
+ getattr r 7
+ grab w 1
+ setattr w 7
+ read r 10
+ manage w 10
+ use r 1
+
+class netlink_dnrt_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto r 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_client 4
+ destroy w 1
+ getattr r 7
+ setattr w 7
+ manage w 10
+
+class x_gc 5
+ create w 1
+ destroy w 1
+ getattr r 7
+ setattr w 7
+ use r 1
+
+class context 2
+ contains n 1
+ translate n 1
+
+class nscd 10
+ shmemserv r 7
+ gethost r 7
+ getstat r 7
+ getgrp r 7
+ shmemhost r 7
+ shmempwd r 7
+ getpwd r 7
+ getserv r 7
+ shmemgrp r 7
+ admin w 5
+
+class passwd 5
+ chfn w 5
+ crontab w 5
+ passwd w 1
+ chsh w 5
+ rootok n 1
+
+class x_event 2
+ send w 10
+ receive r 10
+
+class x_font 6
+ create w 1
+ destroy w 1
+ add_glyph w 1
+ remove_glyph w 1
+ getattr r 7
+ use r 1
+
+class key_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class netif 10
+ rawip_recv r 10
+ tcp_recv r 10
+ udp_recv r 10
+ rawip_send w 10
+ egress w 10
+ ingress r 10
+ tcp_send w 10
+ udp_send w 10
+ dccp_recv r 10
+ dccp_send w 10
+
+class packet_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class memprotect 1
+ mmap_zero n 1
+
+class msg 2
+ send w 10
+ receive r 10
+
+class tun_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class udp_socket 23
+ node_bind n 1
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class appletalk_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 1
+ setattr w 1
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_colormap 10
+ add_color w 10
+ create w 1
+ write w 10
+ destroy w 1
+ install w 1
+ getattr r 7
+ read r 10
+ use r 1
+ remove_color w 10
+ uninstall w 1
+
+class x_screen 8
+ show_cursor w 1
+ hide_cursor w 1
+ saver_show w 1
+ getattr r 7
+ setattr w 7
+ saver_hide w 1
+ saver_getattr r 7
+ saver_setattr w 7
+
+class rawip_socket 23
+ node_bind n 1
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 1
+ setattr w 1
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_application_data 3
+ paste w 10
+paste_after_confirm w 10
+ copy r 10
+
+class association 4
+ setcontext w 3
+ sendto w 10
+ recvfrom r 10
+ polmatch r 1
+
+class x_selection 4
+ write w 10
+ getattr r 7
+ setattr w 7
+ read r 10
+
+class db_column 10
+ select r 10
+ update w 10
+ insert w 1
+ use r 10
+ drop w 1
+ create w 1
+ relabelfrom r 1
+ getattr r 7
+ setattr w 7
+ relabelto w 1
+
+class netlink_socket 22
+ append w 1
+ bind w 1
+ connect w 1
+ create w 1
+ write w 10
+ relabelfrom r 10
+ ioctl n 1
+ name_bind n 1
+ sendto w 10
+ recv_msg r 10
+ send_msg w 10
+ getattr r 7
+ setattr w 7
+ accept r 1
+ getopt r 1
+ read r 10
+ setopt w 1
+ shutdown w 1
+ recvfrom r 10
+ lock n 1
+ relabelto w 10
+ listen r 1
+
+class x_drawable 19
+ get_property r 7
+ list_property r 7
+ set_property w 7
+ add_child w 1
+ override n 1
+ blend w 1
+ send w 10
+ create w 1
+ hide w 1
+ receive r 10
+ write w 10
+ show w 1
+ destroy w 1
+ list_child r 7
+ getattr r 7
+ setattr w 7
+ read r 10
+ manage w 10
+ remove_child w 1
+
+class sem 9
+ associate n 1
+ create w 1
+ write w 10
+ unix_read r 3
+ destroy w 1
+ getattr r 1
+ setattr w 1
+ read r 10
+ unix_write w 3
+
+class system 5
+ module_request n 1
+ ipc_info n 1
+ syslog_read n 1
+ syslog_console n 1
+ syslog_mod n 1
+
+class x_keyboard 19
+ get_property r 7
+ list_property r 7
+ set_property w 7
+ add w 1
+ setfocus w 1
+ create w 1
+ freeze w 1
+ getfocus w 1
+ remove w 1
+ write w 10
+ force_cursor w 1
+ destroy w 1
+ bell w 1
+ getattr r 7
+ grab w 1
+ setattr w 7
+ read r 10
+ manage w 10
+ use r 1
+
+class security 11
+ compute_member n 1
+ compute_user n 1
+ compute_create n 1
+ setenforce n 1
+ check_context n 1
+ setcheckreqprot n 1
+ compute_relabel n 1
+ setbool n 1
+ load_policy n 1
+ setsecparam n 1
+ compute_av n 1
+
+class x_pointer 19
+ get_property r 7
+ list_property r 7
+ set_property w 7
+ add w 1
+ setfocus w 1
+ create w 1
+ freeze w 1
+ getfocus w 1
+ remove w 1
+ write w 10
+ force_cursor w 1
+ destroy w 1
+ bell w 1
+ getattr r 7
+ grab w 1
+ setattr w 7
+ read r 10
+ manage w 10
+ use r 1
diff --git a/apol/perms_map.tcl b/apol/perms_map.tcl
new file mode 100644
index 0000000..c1a1a75
--- /dev/null
+++ b/apol/perms_map.tcl
@@ -0,0 +1,410 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Perms_Map {
+ variable dialog .apol_perms
+ variable user_default_pmap_name [file join $::env(HOME) .apol_perm_mapping]
+
+ variable opts ;# options for edit perm map dialog
+ variable widgets
+}
+
+proc Apol_Perms_Map::close {} {
+ variable opts
+
+ _close_dialog
+ set opts(filename) {}
+ set opts(is_saveable) 0
+ set opts(modified) 0
+}
+
+proc Apol_Perms_Map::showPermMappings {} {
+ variable dialog
+ if {[winfo exists $dialog]} {
+ raise $dialog
+ } else {
+ _createEditDialog
+ _refreshEditDialog
+ }
+}
+
+# Let the user select a permission map file to load. Returns 1 on
+# success, 0 on error.
+proc Apol_Perms_Map::openPermMapFromFile {} {
+ set pmap_name [tk_getOpenFile -title "Select Perm Map to Load" -parent .]
+ if {$pmap_name != {}} {
+ return [_loadPermMap $pmap_name [file tail $pmap_name] 1]
+ }
+ return 0
+}
+
+# Attempt to load the "default" permission map. If there exists
+# within the user's home directory a file called ".apol_perm_mapping"
+# then use that. Otherwise look for the file
+# "apol_perm_mapping_ver$ver", where $ver is the currently loaded
+# policy version number. If that fails then simply try
+# "apol_perm_mapping". If all of that fails, then display an error
+# message and abort the loading. Returns 1 on success, 0 on error.
+proc Apol_Perms_Map::openDefaultPermMap {} {
+ variable user_default_pmap_name
+ if {[file exists $user_default_pmap_name]} {
+ set pmap_name $user_default_pmap_name
+ set pmap_short "User Default Permission Map"
+ set pmap_editable 1
+ } else {
+ set pmap_editable 0
+ # try policy-specific file
+ set policy_version [apol_tcl_get_policy_version $::ApolTop::policy]
+ set pmap_name [apol_file_find_path "apol_perm_mapping_ver${policy_version}"]
+ if {$pmap_name == {}} {
+ # finally try fallback one
+ set pmap_name [apol_file_find_path apol_perm_mapping]
+ if {$pmap_name == {}} {
+ set message "Could not locate system default permission map. You must explicitly load a permission map from file."
+ if {[Apol_Progress_Dialog::is_waiting]} {
+ error $message
+ } else {
+ tk_messageBox -icon error -type ok -title "Permission Maps" \
+ -message $message
+ }
+ return 0
+ }
+ }
+ set pmap_short "System Default Permission Map (Read-Only)"
+ }
+ return [_loadPermMap $pmap_name $pmap_short $pmap_editable]
+}
+
+proc Apol_Perms_Map::savePermMap {} {
+ variable opts
+ if {!$opts(is_saveable)} {
+ savePermMapAs
+ } else {
+ _savePermMap $opts(filename) $opts(shortname)
+ }
+}
+
+proc Apol_Perms_Map::savePermMapAs {} {
+ set pmap_name [tk_getSaveFile -title "Save Perm Map" -parent .]
+ if {$pmap_name != {}} {
+ _savePermMap $pmap_name [file tail $pmap_name]
+ }
+}
+
+proc Apol_Perms_Map::saveDefaultPermMap {} {
+ variable user_default_pmap_name
+ variable opts
+ _savePermMap $user_default_pmap_name "User Default Permission Map"
+}
+
+proc Apol_Perms_Map::is_pmap_loaded {} {
+ variable opts
+ if {$opts(filename) == {}} {
+ return 0
+ }
+ return 1
+}
+
+#################### private functions below ####################
+
+
+proc Apol_Perms_Map::_loadPermMap {filename shortname saveable} {
+ if {[catch {$::ApolTop::policy open_permmap $filename} err]} {
+ if {[Apol_Progress_Dialog::is_waiting]} {
+ error $err
+ } else {
+ tk_messageBox -icon error -type ok -title "Permission Maps" -message $err
+ return 0
+ }
+ }
+ variable opts
+ set opts(filename) $filename
+ set opts(shortname) $shortname
+ set opts(is_saveable) $saveable
+ set opts(modified) 0
+ if {$err != {}} {
+ set len [llength [split $err "\n"]]
+ if {$len > 5} {
+ incr len -4
+ set err [lrange [split $err "\n"] 0 3]
+ lappend err "(plus $len more lines)"
+ set err [join $err "\n"]
+ }
+ if {![Apol_Progress_Dialog::is_waiting]} {
+ set message "There were warnings while opening the permission map:"
+ tk_messageBox -icon warning -type ok -title "Permission Maps" \
+ -message "$message\n\n$err"
+ }
+ } else {
+ if {![Apol_Progress_Dialog::is_waiting]} {
+ tk_messageBox -icon info -type ok -title "Permission Maps" \
+ -message "Permission map successfully loaded."
+ }
+ }
+ variable dialog
+ if {[winfo exists $dialog]} {
+ _refreshEditDialog
+ }
+ return 1
+}
+
+proc Apol_Perms_Map::_createEditDialog {} {
+ variable dialog
+ variable opts
+ variable widgets
+
+ set title "Permissions Mappings: $opts(shortname)"
+ Dialog $dialog -parent . -separator 1 -title $title -modal none \
+ -default 0 -cancel 2
+ set topf [frame [$dialog getframe].top]
+ pack $topf -side top -expand 1 -fill both
+
+ set classes_box [TitleFrame $topf.classes -text "Object Classes"]
+ pack $classes_box -side left -padx 2 -pady 2 -expand 0 -fill y
+ set widgets(classes) [Apol_Widget::makeScrolledListbox [$classes_box getframe].c \
+ -height 16 -width 24 -listvar Apol_Perms_Map::opts(classes)]
+ bind $widgets(classes).lb <<ListboxSelect>> Apol_Perms_Map::_refreshPermEdit
+ pack $widgets(classes) -expand 1 -fill both
+
+ set results_box [TitleFrame $topf.perms -text "Permission Mappings"]
+ pack $results_box -side right -padx 2 -pady 2 -expand 1 -fill both
+ set sw [ScrolledWindow [$results_box getframe].sw -auto both]
+ set widgets(perms) [ScrollableFrame $sw.perms -width 300]
+ $sw setwidget $widgets(perms)
+ pack $sw -expand 1 -fill both
+
+ set label_box [frame [$dialog getframe].l]
+ pack $label_box -side bottom -anchor center
+ set widgets(l1) [label $label_box.l1 -fg red -text ""]
+ set widgets(l2) [label $label_box.l2 -text ""]
+ pack $widgets(l1) $widgets(l2) -side left
+ # delay setting the labels' text until [_refresh_edit_dialog], to
+ # see if anything is unmapped
+
+ $dialog add -text "OK" -command Apol_Perms_Map::_okay
+ $dialog add -text "Apply" -command Apol_Perms_Map::_apply
+ $dialog add -text "Cancel" -command Apol_Perms_Map::_cancel
+
+ trace add variable Apol_Perms_Map::opts(modified) write \
+ Apol_Perms_Map::_toggleButtons
+
+ # forcibly invoke the button callback
+ set opts(modified) $opts(modified)
+ $dialog draw
+}
+
+proc Apol_Perms_Map::_refreshEditDialog {} {
+ variable opts
+ variable widgets
+
+ array set opts {
+ classes {}
+ }
+
+ set all_mapped 1
+ set class_index 0
+ foreach class [Apol_Class_Perms::getClasses] {
+ set suffix {}
+ set perm_list {}
+ foreach perm [Apol_Class_Perms::getPermsForClass $class] {
+ set direction [$::ApolTop::policy get_permmap_direction $class $perm]
+ set weight [$::ApolTop::policy get_permmap_weight $class $perm]
+ set opts(p:${class}:${perm}:map) $direction
+ set opts(p:${class}:${perm}:weight) $weight
+ if {$direction == $::APOL_PERMMAP_UNMAPPED} {
+ set suffix *
+ set all_mapped 0
+ }
+ lappend perm_list [list $perm $direction $weight]
+ }
+
+ # store original perm map values, needed if user cancels dialog
+ set opts(c:$class) $perm_list
+ lappend opts(classes) "$class$suffix"
+ if {$suffix != {}} {
+ $widgets(classes).lb itemconfigure $class_index -foreground red
+ }
+ incr class_index
+ }
+
+ # add the warning to the bottom if there exists any unmapped permissions
+ if {!$all_mapped} {
+ $widgets(l1) configure -text "*"
+ $widgets(l2) configure -text " - Undefined permission mapping(s)"
+ } else {
+ $widgets(l1) configure -text ""
+ $widgets(l2) configure -text ""
+ }
+}
+
+proc Apol_Perms_Map::_refreshPermEdit {} {
+ variable opts
+ variable widgets
+
+ focus $widgets(classes).lb
+
+ set perms [$widgets(perms) getframe]
+ foreach w [winfo children $perms] {
+ destroy $w
+ }
+
+ if {[set selection [$widgets(classes).lb curselection]] == {}} {
+ return
+ }
+ set class [lindex $opts(classes) [lindex $selection 0]]
+ set class [string trimright $class "*"]
+
+ foreach perm $opts(c:$class) {
+ foreach {perm map weight} $perm {break}
+ if {$map != $::APOL_PERMMAP_UNMAPPED} {
+ set l [label $perms.$perm:l -text $perm -anchor w]
+ } else {
+ set l [label $perms.$perm:l -text "${perm}*" -fg red -anchor w]
+ }
+ # tk_optionMenu does not have a -command flag, so implement an
+ # option menu via a menubutton
+ set menubutton [menubutton $perms.$perm:mb -bd 2 -relief raised \
+ -indicatoron 1 -width 8 \
+ -textvariable Apol_Perms_Map::opts(p:${class}:${perm}:map_label)]
+ set menu [menu $menubutton.m -type normal -tearoff 0]
+ $menubutton configure -menu $menu
+ $menu add radiobutton -label "Read" -value $::APOL_PERMMAP_READ \
+ -command [list Apol_Perms_Map::_togglePermMap $class $perm 1] \
+ -variable Apol_Perms_Map::opts(p:${class}:${perm}:map)
+ $menu add radiobutton -label "Write" -value $::APOL_PERMMAP_WRITE \
+ -command [list Apol_Perms_Map::_togglePermMap $class $perm 1] \
+ -variable Apol_Perms_Map::opts(p:${class}:${perm}:map)
+ $menu add radiobutton -label "Both" -value $::APOL_PERMMAP_BOTH \
+ -command [list Apol_Perms_Map::_togglePermMap $class $perm 1] \
+ -variable Apol_Perms_Map::opts(p:${class}:${perm}:map)
+ $menu add radiobutton -label "None" -value $::APOL_PERMMAP_NONE \
+ -command [list Apol_Perms_Map::_togglePermMap $class $perm 1] \
+ -variable Apol_Perms_Map::opts(p:${class}:${perm}:map)
+ set l2 [label $perms.$perm:l2 -text "Weight:" -anchor e]
+ set weight [spinbox $perms.$perm:weight -from 1 -to 10 -increment 1 \
+ -width 2 -bg white \
+ -command [list Apol_Perms_Map::_togglePermMap $class $perm 1] \
+ -textvariable Apol_Perms_Map::opts(p:${class}:${perm}:weight)]
+ grid $l $menubutton $l2 $weight -padx 2 -sticky w -pady 4
+ grid configure $l2 -ipadx 10
+ _togglePermMap $class $perm 0
+ }
+ grid columnconfigure $perms 0 -minsize 100 -weight 1
+ $widgets(perms) xview moveto 0
+ $widgets(perms) yview moveto 0
+}
+
+proc Apol_Perms_Map::_togglePermMap {class perm modification} {
+ variable opts
+ set map $opts(p:${class}:${perm}:map)
+ if {$map == $::APOL_PERMMAP_READ} {
+ set opts(p:${class}:${perm}:map_label) "Read"
+ } elseif {$map == $::APOL_PERMMAP_WRITE} {
+ set opts(p:${class}:${perm}:map_label) "Write"
+ } elseif {$map == $::APOL_PERMMAP_BOTH} {
+ set opts(p:${class}:${perm}:map_label) "Both"
+ } elseif {$map == $::APOL_PERMMAP_NONE} {
+ set opts(p:${class}:${perm}:map_label) "None"
+ } else {
+ set opts(p:${class}:${perm}:map_label) "Unmapped"
+ }
+ set opts(modified) $modification
+}
+
+proc Apol_Perms_Map::_toggleButtons {name1 name2 op} {
+ variable opts
+ variable dialog
+ if {$opts(modified)} {
+ $dialog itemconfigure 1 -state normal
+ } else {
+ $dialog itemconfigure 1 -state disabled
+ }
+}
+
+proc Apol_Perms_Map::_okay {} {
+ _apply
+ _close_dialog
+}
+
+proc Apol_Perms_Map::_apply {} {
+ variable dialog
+ variable opts
+
+ if {[winfo exists $dialog] && $opts(modified)} {
+ foreach class $opts(classes) {
+ set class [string trimright $class "*"]
+ set perm_list {}
+ foreach perm [Apol_Class_Perms::getPermsForClass $class] {
+ set map $opts(p:${class}:${perm}:map)
+ set weight $opts(p:${class}:${perm}:weight)
+ if {$map != $::APOL_PERMMAP_UNMAPPED} {
+ $::ApolTop::policy set_permmap $class $perm $map $weight
+ }
+ lappend perm_list [list $perm $map $weight]
+ }
+ # overwrite perm map values with applied values
+ set opts(c:$class) $perm_list
+ }
+ }
+ set opts(modified) 0
+}
+
+proc Apol_Perms_Map::_cancel {} {
+ variable opts
+
+ # revert the permission map to original values
+ if {$opts(modified)} {
+ foreach class $opts(classes) {
+ set class [string trimright $class "*"]
+ foreach perm $opts(c:$class) {
+ foreach {perm map weight} $perm {break}
+ $::ApolTop::policy set_permmap $class $perm $map $weight
+ }
+ }
+ }
+ _close_dialog
+}
+
+proc Apol_Perms_Map::_close_dialog {} {
+ variable opts
+ array unset opts c:*
+ array unset opts p:*
+ trace remove variable Apol_Perms_Map::opts(modified) write \
+ Apol_Perms_Map::_toggleButtons
+
+ variable dialog
+ destroy $dialog
+}
+
+proc Apol_Perms_Map::_savePermMap {filename shortname} {
+ variable opts
+ variable dialog
+
+ _apply
+ if {[catch {$::ApolTop::policy save_permmap $filename} err]} {
+ tk_messageBox -icon error -type ok -title "Permission Maps" -message "Error saving permission map: $err"
+ } else {
+ set opts(filename) $filename
+ set opts(shortname) $shortname
+ set opts(is_saveable) 1
+ set opts(modified) 0
+ set title "Permissions Mappings: $opts(shortname)"
+ if {[winfo exists $dialog]} {
+ $dialog configure -title $title
+ _refreshEditDialog
+ _refreshPermEdit
+ }
+ }
+}
diff --git a/apol/policyconf.tcl b/apol/policyconf.tcl
new file mode 100644
index 0000000..8f7e9c1
--- /dev/null
+++ b/apol/policyconf.tcl
@@ -0,0 +1,102 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_PolicyConf {
+ variable textbox
+}
+
+proc Apol_PolicyConf::create {tab_name nb} {
+ variable textbox
+
+ set frame [$nb insert end $tab_name -text "Policy Source"]
+ set sw [ScrolledWindow $frame.sw -auto none]
+ set textbox [text [$sw getframe].text -bg white -wrap none]
+ $sw setwidget $textbox
+ bind $textbox <Button-3> [list Apol_Widget::_searchresults_popup %W %x %y]
+ pack $sw -expand yes -fill both
+
+ bind $textbox <<Insertion>> Apol_PolicyConf::insertionMarkChanged
+
+ rename $textbox ::Apol_PolicyConf::_real_text
+
+ # Override the textbox's command to do two things:
+ #
+ # Deny normal insert and delete commands - but still show the
+ # insertion cursor. (Setting the state to disabled hides
+ # insertion cursor.) Use the 'fakeinsert' and 'fakedelete'
+ # commands to make changes.
+ #
+ # Unfortunately the Tk 8.4 text widget does not generate a virtual
+ # event whenever the insertion mark moves. Thus to simulate the
+ # behavior, override the mark command to generate the event
+ # <<Insertion>> after the mark changes.
+ proc ::$textbox {cmd args} {
+ switch -- $cmd {
+ insert -
+ delete { return }
+ fakeinsert { set cmd insert }
+ fakedelete { set cmd delete }
+ }
+ set retval [uplevel 1 ::Apol_PolicyConf::_real_text $cmd $args]
+ if {$cmd == "mark" && [string equal -length 10 $args "set insert"]} {
+ event generate $Apol_PolicyConf::textbox <<Insertion>>
+ }
+ return $retval
+ }
+}
+
+proc Apol_PolicyConf::open {ppath} {
+ variable textbox
+
+ $textbox fakedelete 0.0 end
+ if {![ApolTop::is_capable "source"]} {
+ $textbox fakeinsert end "The currently loaded policy is not a source policy."
+ } else {
+ set primary_file [$ppath get_primary]
+ if {[catch {::open $primary_file r} f]} {
+ $textbox fakeinsert end "$primary_file does not exist or could not be read by the user."
+ } else {
+ $textbox fakeinsert end [read $f]
+ ::close $f
+ }
+ }
+ $textbox see 0.0
+ $textbox mark set insert 1.0
+}
+
+proc Apol_PolicyConf::close {} {
+ variable textbox
+ $textbox fakedelete 0.0 end
+}
+
+proc Apol_PolicyConf::getTextWidget {} {
+ variable textbox
+ return $textbox
+}
+
+proc Apol_PolicyConf::insertionMarkChanged {} {
+ set lpos [$Apol_PolicyConf::textbox index insert]
+ foreach {line col} [split $lpos .] {break}
+ ApolTop::setPolicySourceLinenumber $line
+}
+
+proc Apol_PolicyConf::gotoLine {line} {
+ variable textbox
+ $textbox tag remove sel 0.0 end
+ $textbox mark set insert $line.0
+ $textbox see $line.0
+ $textbox tag add sel $line.0 $line.end
+ focus $textbox
+}
diff --git a/apol/progress_dialog.tcl b/apol/progress_dialog.tcl
new file mode 100644
index 0000000..2b44420
--- /dev/null
+++ b/apol/progress_dialog.tcl
@@ -0,0 +1,74 @@
+# Copyright (C) 2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Progress_Dialog {
+ variable text
+ variable prev_text
+ variable val
+ variable waiting 0
+}
+
+# Create a dialog to display messages while some library process is
+# running.
+proc Apol_Progress_Dialog::wait {title initialtext lambda} {
+ variable text "$title:\n $initialtext"
+ variable prev_text $initialtext
+ variable val -1
+
+ set title_width [string length $title]
+ set text_width [expr {[string length $initialtext] + 4}]
+ if {$text_width < $title_width} {
+ set text_width $title_width
+ }
+ if {$text_width < 32} {
+ set text_width 32
+ }
+
+ if {[info exists .apol_progress] == 0} {
+ ProgressDlg .apol_progress -title $title \
+ -type normal -stop {} -separator 1 -parent . -maximum 2 \
+ -width $text_width -textvariable Apol_Progress_Dialog::text \
+ -variable Apol_Progress_Dialog::val
+ }
+
+ set orig_cursor [. cget -cursor]
+ . configure -cursor watch
+ update idletasks
+
+ apol_tcl_clear_info_string
+ variable waiting 1
+ set catchval [catch {uplevel 1 $lambda} retval]
+ set waiting 0
+
+ . configure -cursor $orig_cursor
+ destroy .apol_progress
+ update idletasks
+ return -code $catchval $retval
+}
+
+proc Apol_Progress_Dialog::is_waiting {} {
+ variable waiting
+ set waiting
+}
+
+proc Apol_Progress_Dialog::_update_message {} {
+ variable text
+ variable prev_text
+ if {[set infoString [apol_tcl_get_info_string]] != $prev_text} {
+ set text "[lindex [split $text "\n"] 0]\n $infoString"
+ update idletasks
+ set prev_text $infoString
+ }
+}
diff --git a/apol/range_dialog.tcl b/apol/range_dialog.tcl
new file mode 100644
index 0000000..96457fb
--- /dev/null
+++ b/apol/range_dialog.tcl
@@ -0,0 +1,132 @@
+# Copyright (C) 2005-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Range_Dialog {
+ variable dialog ""
+ variable vars
+}
+
+# Create a dialog box to allow the user to select an MLS range (either
+# a single level or two levels. Ensure that the selected range is
+# valid (i.e., high dominates low). If the range is invalid or if no
+# range is selected then return an empty list. Returns an instance of
+# an apol_mls_range_t; the caller must delete it afterwards.
+proc Apol_Range_Dialog::getRange {{defaultRange {}} {parent .}} {
+ variable dialog
+ variable vars
+ if {![winfo exists $dialog]} {
+ _create_dialog $parent
+ }
+ set f [$dialog getframe]
+ Apol_Widget::resetLevelSelectorToPolicy $f.low
+ Apol_Widget::resetLevelSelectorToPolicy $f.high
+ set vars($dialog:highenable) 0
+ if {$defaultRange != {}} {
+ set low_level [$defaultRange get_low]
+ set high_level [$defaultRange get_high]
+
+ Apol_Widget::setLevelSelectorLevel $f.low $low_level
+ if {[apol_mls_level_compare $::ApolTop::policy $low_level $high_level] != $::APOL_MLS_EQ} {
+ set vars($dialog:highenable) 1
+ Apol_Widget::setLevelSelectorLevel $f.high $high_level
+ }
+ }
+
+ _high_enabled $dialog
+ # force a recomputation of button sizes (bug in ButtonBox)
+ $dialog.bbox _redraw
+ set retval [$dialog draw]
+ if {$retval == -1 || $retval == 1} {
+ return {}
+ }
+ _get_range $dialog
+}
+
+
+########## private functions below ##########
+
+proc Apol_Range_Dialog::_create_dialog {parent} {
+ variable dialog
+ variable vars
+
+ set dialog [Dialog .range_dialog -modal local -parent $parent \
+ -separator 1 -homogeneous 1 -title "Select Range"]
+ array unset vars $dialog:*
+
+ set f [$dialog getframe]
+ set low_label [label $f.ll -text "Single level"]
+ set low_level [Apol_Widget::makeLevelSelector $f.low 12]
+
+ set high_cb [checkbutton $f.high_enable \
+ -text "High level" \
+ -variable Apol_Range_Dialog::vars($dialog:highenable) \
+ -command [list Apol_Range_Dialog::_high_enabled $dialog]]
+ set high_level [Apol_Widget::makeLevelSelector $f.high 12]
+ Apol_Widget::setLevelSelectorState $high_level 0
+
+ grid $low_label $high_cb -sticky w
+ grid $low_level $high_level -sticky ns
+ grid columnconfigure $f 0 -weight 1 -uniform 1 -pad 4
+ grid columnconfigure $f 1 -weight 1 -uniform 1 -pad 4
+
+ $dialog add -text "OK" -command [list Apol_Range_Dialog::_okay $dialog]
+ $dialog add -text "Cancel"
+}
+
+proc Apol_Range_Dialog::_get_range {dialog} {
+ variable vars
+ set f [$dialog getframe]
+ set range [new_apol_mls_range_t]
+
+ if {[ApolTop::is_policy_open]} {
+ set p $::ApolTop::policy
+ } else {
+ set p NULL
+ }
+
+ set low_level [Apol_Widget::getLevelSelectorLevel $f.low]
+ $range set_low $p $low_level
+
+ if {$vars($dialog:highenable)} {
+ set high_level [Apol_Widget::getLevelSelectorLevel $f.high]
+ $range set_high $p $high_level
+ }
+
+ return $range
+}
+
+proc Apol_Range_Dialog::_okay {dialog} {
+ set range [_get_range $dialog]
+ if {![ApolTop::is_policy_open] || [$range validate $::ApolTop::policy] != 1} {
+ tk_messageBox -icon error -type ok -title "Invalid Range" \
+ -message "The selected range is not valid. The high level does not dominate the low level."
+ } else {
+ $dialog enddialog 0
+ }
+ $range -acquire
+ $range -delete
+}
+
+proc Apol_Range_Dialog::_high_enabled {dialog} {
+ variable vars
+ set f [$dialog getframe]
+ if {$vars($dialog:highenable)} {
+ $f.ll configure -text "Low level"
+ Apol_Widget::setLevelSelectorState $f.high 1
+ } else {
+ $f.ll configure -text "Single level"
+ Apol_Widget::setLevelSelectorState $f.high 0
+ }
+}
diff --git a/apol/range_selector.tcl b/apol/range_selector.tcl
new file mode 100644
index 0000000..8234394
--- /dev/null
+++ b/apol/range_selector.tcl
@@ -0,0 +1,156 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Widget {
+ variable vars
+}
+
+# Creates a widget that lets the user select an MLS range and a range
+# search type (exact, subset, superset). If the second argument is
+# not "" then add a checkbutton that enables/disables the entire
+# widget.
+proc Apol_Widget::makeRangeSelector {path rangeMatchText {enableText "MLS range"} args} {
+ variable vars
+ array unset vars $path:*
+ set vars($path:range) {}
+ set vars($path:range_rendered) {}
+ set vars($path:search_type) $::APOL_QUERY_EXACT
+
+ set f [frame $path]
+ set range_frame [frame $f.range]
+ set range2_frame [frame $f.range2]
+ pack $range_frame $range2_frame -side left -expand 0 -anchor nw
+
+ if {$enableText != {}} {
+ set vars($path:enable) 0
+ set range_cb [checkbutton $range_frame.enable -text $enableText \
+ -variable Apol_Widget::vars($path:enable)]
+ pack $range_cb -side top -expand 0 -anchor nw
+ trace add variable Apol_Widget::vars($path:enable) write [list Apol_Widget::_toggle_range_selector $path $range_cb]
+ }
+ set range_display [eval Entry $range_frame.display -textvariable Apol_Widget::vars($path:range_rendered) -width 20 -editable 0 $args]
+ set range_button [button $range_frame.button -text "Select Range..." -state disabled -command [list Apol_Widget::_show_mls_range_dialog $path]]
+ trace add variable Apol_Widget::vars($path:range) write [list Apol_Widget::_update_range_display $path]
+ pack $range_display -side top -expand 1 -fill x -anchor nw
+ pack $range_button -side top -expand 0 -anchor ne
+ if {$enableText != {}} {
+ pack configure $range_display -padx 4
+ pack configure $range_button -padx 4
+ }
+
+ # range search type
+ set range_label [label $range2_frame.label -text "Range matching:" \
+ -state disabled]
+ set range_exact [radiobutton $range2_frame.exact -text "Exact matches" \
+ -state disabled \
+ -value $::APOL_QUERY_EXACT \
+ -variable Apol_Widget::vars($path:search_type)]
+ set range_subset [radiobutton $range2_frame.subset -text "$rangeMatchText containing range" \
+ -state disabled \
+ -value $::APOL_QUERY_SUB \
+ -variable Apol_Widget::vars($path:search_type)]
+ set range_superset [radiobutton $range2_frame.superset -text "$rangeMatchText within range" \
+ -state disabled \
+ -value $::APOL_QUERY_SUPER \
+ -variable Apol_Widget::vars($path:search_type)]
+ pack $range_label $range_exact $range_subset $range_superset \
+ -side top -expand 0 -anchor nw
+
+ return $f
+}
+
+proc Apol_Widget::setRangeSelectorState {path newState} {
+ if {$newState == 0 || $newState == "disabled"} {
+ set new_state disabled
+ } else {
+ set new_state normal
+ }
+ foreach w {display button} {
+ $path.range.$w configure -state $new_state
+ }
+ foreach w {label exact subset superset} {
+ $path.range2.$w configure -state $new_state
+ }
+}
+
+proc Apol_Widget::setRangeSelectorCompleteState {path newState} {
+ if {$newState == 0 || $newState == "disabled"} {
+ set new_state disabled
+ } else {
+ set new_state normal
+ }
+ catch {$path.range.enable configure -state $new_state}
+}
+
+proc Apol_Widget::clearRangeSelector {path} {
+ set Apol_Widget::vars($path:range) {}
+ set Apol_Widget::vars($path:search_type) $::APOL_QUERY_EXACT
+ catch {set Apol_Widget::vars($path:enable) 0}
+}
+
+proc Apol_Widget::getRangeSelectorState {path} {
+ return $Apol_Widget::vars($path:enable)
+}
+
+# Return a 2-uple containing the range value (an instance of an
+# apol_mls_range_t) and the search type (one of APOL_QUERY_EXACT,
+# APOL_QUERY_SUB, or APOL_QUERY_SUPER). Caller must delete the range
+# afterwards. Note that the range could be an empty string if none is
+# set.
+proc Apol_Widget::getRangeSelectorValue {path} {
+ variable vars
+ if {$vars($path:range) != {}} {
+ set range [new_apol_mls_range_t $vars($path:range)]
+ } else {
+ set range {}
+ }
+ list $range $vars($path:search_type)
+}
+
+########## private functions below ##########
+
+proc Apol_Widget::_toggle_range_selector {path cb name1 name2 op} {
+ if {$Apol_Widget::vars($path:enable)} {
+ Apol_Widget::setRangeSelectorState $path normal
+ } else {
+ Apol_Widget::setRangeSelectorState $path disabled
+ }
+}
+
+proc Apol_Widget::_show_mls_range_dialog {path} {
+ $path.range.button configure -state disabled
+ set range [Apol_Range_Dialog::getRange $Apol_Widget::vars($path:range)]
+ if {$range != {}} {
+ set Apol_Widget::vars($path:range) $range
+ $range -acquire
+ }
+
+ $path.range.button configure -state normal
+ # the trace on this variable will trigger [_update_range_display]
+ # to execute
+}
+
+proc Apol_Widget::_update_range_display {path name1 name2 op} {
+ variable vars
+ set display $path.range.display
+ if {$vars($path:range) == {}} {
+ set vars($path:range_rendered) {}
+ $display configure -helptext {}
+ } else {
+ set s [$vars($path:range) render $::ApolTop::policy]
+ set vars($path:range_rendered) $s
+ $display configure -helptext $vars($path:range_rendered)
+ }
+}
diff --git a/apol/range_trans.tcl b/apol/range_trans.tcl
new file mode 100644
index 0000000..b35aca7
--- /dev/null
+++ b/apol/range_trans.tcl
@@ -0,0 +1,204 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Range {
+ variable widgets
+ variable vals
+}
+
+proc Apol_Range::create {tab_name nb} {
+ variable widgets
+ variable vals
+
+ set frame [$nb insert end $tab_name -text "Range Transition Rules"]
+ set obox [TitleFrame $frame.obox -text "Search Options"]
+ set dbox [TitleFrame $frame.dbox -text "Range Transition Rules Display"]
+ pack $obox -fill x -expand 0 -padx 2 -pady 2
+ pack $dbox -fill both -expand yes -padx 2 -pady 2
+
+ set source_frame [frame [$obox getframe].source]
+ set target_frame [frame [$obox getframe].target]
+ set classes_frame [frame [$obox getframe].classes]
+ pack $source_frame $target_frame $classes_frame -side left -padx 4 -pady 2 -expand 0 -anchor nw
+
+ # source type
+ set vals(enable_source) 0
+ set source_cb [checkbutton $source_frame.cb -text "Source type" \
+ -variable Apol_Range::vals(enable_source)]
+ set widgets(source_type) [Apol_Widget::makeTypeCombobox $source_frame.tcb]
+ Apol_Widget::setTypeComboboxState $widgets(source_type) 0
+ trace add variable Apol_Range::vals(enable_source) write \
+ [list Apol_Range::_toggleTypeCombobox $widgets(source_type)]
+ pack $source_cb -side top -expand 0 -anchor nw
+ pack $widgets(source_type) -side top -expand 0 -anchor nw -padx 4
+
+ # target type
+ set vals(enable_target) 0
+ set target_cb [checkbutton $target_frame.cb -text "Target type" \
+ -variable Apol_Range::vals(enable_target)]
+ set widgets(target_type) [Apol_Widget::makeTypeCombobox $target_frame.tcb]
+ Apol_Widget::setTypeComboboxState $widgets(target_type) 0
+ trace add variable Apol_Range::vals(enable_target) write \
+ [list Apol_Range::_toggleTypeCombobox $widgets(target_type)]
+ pack $target_cb -side top -expand 0 -anchor nw
+ pack $widgets(target_type) -side top -expand 0 -anchor nw -padx 4
+
+ # object classes
+ set l [label $classes_frame.l -text "Target Classes"]
+ set sw [ScrolledWindow $classes_frame.sw -auto both]
+ set widgets(classes) [listbox [$sw getframe].lb -height 5 -width 24 \
+ -highlightthickness 0 -selectmode multiple \
+ -exportselection 0 -state disabled \
+ -bg $ApolTop::default_bg_color \
+ -listvar Apol_Range::vals(classes)]
+ $sw setwidget $widgets(classes)
+ update
+ grid propagate $sw 0
+ pack $l $sw -side top -expand 0 -anchor nw
+
+ set widgets(range) [Apol_Widget::makeRangeSelector [$obox getframe].range Rules]
+ pack $widgets(range) -side left -padx 4 -pady 2 -expand 0 -anchor nw
+
+ set ok [button [$obox getframe].ok -text "OK" -width 6 -command Apol_Range::_searchRanges]
+ pack $ok -side right -pady 5 -padx 5 -anchor ne
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$dbox getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Range::open {ppath} {
+ variable vals
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(source_type)
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(target_type)
+ set vals(classes) [Apol_Class_Perms::getClasses]
+ $widgets(classes) configure -bg white -state normal
+}
+
+proc Apol_Range::close {} {
+ variable vals
+ variable widgets
+ Apol_Widget::clearTypeCombobox $widgets(source_type)
+ Apol_Widget::clearTypeCombobox $widgets(target_type)
+ set vals(classes) {}
+ $widgets(classes) configure -bg $ApolTop::default_bg_color -state disabled
+ Apol_Widget::clearRangeSelector $widgets(range)
+ Apol_Widget::clearSearchResults $widgets(results)
+ set vals(enable_source) 0
+ set vals(enable_target) 0
+}
+
+proc Apol_Range::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+#### private functions below ####
+
+proc Apol_Range::_toggleTypeCombobox {path name1 name2 op} {
+ Apol_Widget::setTypeComboboxState $path $Apol_Range::vals($name2)
+}
+
+proc Apol_Range::_searchRanges {} {
+ variable vals
+ variable widgets
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+
+ if {$vals(enable_source)} {
+ set source [lindex [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(source_type)] 0]
+ if {$source == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No source type provided."
+ return
+ }
+ } else {
+ set source {}
+ }
+ if {$vals(enable_target)} {
+ set target [lindex [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(target_type)] 0]
+ if {$target == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No target type provided."
+ return
+ }
+ } else {
+ set target {}
+ }
+ if {[Apol_Widget::getRangeSelectorState $widgets(range)]} {
+ foreach {range range_match} [Apol_Widget::getRangeSelectorValue $widgets(range)] break
+ if {$range == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No range selected."
+ return
+ }
+ } else {
+ set range NULL
+ set range_match 0
+ }
+
+ set q [new_apol_range_trans_query_t]
+ $q set_source $::ApolTop::policy $source 0
+ $q set_target $::ApolTop::policy $target 0
+ foreach c [$widgets(classes) curselection] {
+ $q append_class $::ApolTop::policy [$widgets(classes) get $c]
+ }
+ $q set_range $::ApolTop::policy $range $range_match
+
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set results [range_trans_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ if {[llength $results] == 0} {
+ set text "Search returned no results."
+ } else {
+ set text "[llength $results] rule"
+ if {[llength $results] != 1} {
+ append text s
+ }
+ append text " match the search criteria.\n\n"
+ }
+ foreach r [lsort -command _range_trans_sort $results] {
+ append text "[_renderRangeTrans $r]\n"
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $text
+}
+
+proc Apol_Range::_renderRangeTrans {rule} {
+ apol_range_trans_render $::ApolTop::policy $rule
+}
+
+proc Apol_Range::_range_trans_sort {a b} {
+ set t1 [[$a get_source_type $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set t2 [[$b get_source_type $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ if {[set z [string compare $t1 $t2]] != 0} {
+ return $z
+ }
+
+ set t1 [[$a get_target_type $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set t2 [[$b get_target_type $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ if {[set z [string compare $t1 $t2]] != 0} {
+ return $z
+ }
+
+ set c1 [[$a get_target_class $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set c2 [[$b get_target_class $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ string compare $c1 $c2
+}
diff --git a/apol/rbac_tab.tcl b/apol/rbac_tab.tcl
new file mode 100644
index 0000000..2bc11c2
--- /dev/null
+++ b/apol/rbac_tab.tcl
@@ -0,0 +1,490 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_RBAC {
+ variable vals
+ variable widgets
+}
+
+proc Apol_RBAC::create {tab_name nb} {
+ variable vals
+ variable widgets
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "RBAC Rules"]
+ set topf [frame $frame.top]
+ set bottomf [frame $frame.bottom]
+ pack $topf -expand 0 -fill both -pady 2
+ pack $bottomf -expand 1 -fill both -pady 2
+
+ set rsbox [TitleFrame $topf.rs -text "Rule Selection"]
+ set obox [TitleFrame $topf.opts -text "Search Options"]
+ set dbox [TitleFrame $bottomf.results -text "RBAC Rules Display"]
+ pack $rsbox -side left -expand 0 -fill both -padx 2
+ pack $obox -side left -expand 1 -fill both -padx 2
+ pack $dbox -expand 1 -fill both -padx 2
+
+ # Rule selection subframe
+ set rs [$rsbox getframe]
+ radiobutton $rs.allow -text allow -value allow \
+ -variable Apol_RBAC::vals(rule_selection)
+ radiobutton $rs.trans -text role_transition -value trans \
+ -variable Apol_RBAC::vals(rule_selection)
+ radiobutton $rs.both -text "allow and role_transition" -value both \
+ -variable Apol_RBAC::vals(rule_selection)
+ trace add variable Apol_RBAC::vals(rule_selection) write \
+ [list Apol_RBAC::_ruleChanged]
+ pack $rs.allow $rs.trans $rs.both -side top -anchor w
+
+ set widgets(options_pm) [PagesManager [$obox getframe].opts]
+
+ _allowCreate [$widgets(options_pm) add allow]
+ _transCreate [$widgets(options_pm) add trans]
+ _bothCreate [$widgets(options_pm) add both]
+ trace add variable Apol_RBAC::vals(source:which) write Apol_RBAC::_toggleRoleBox
+
+ $widgets(options_pm) compute_size
+ pack $widgets(options_pm) -expand 1 -fill both -side left
+ $widgets(options_pm) raise allow
+
+ set ok [button [$obox getframe].ok -text OK -width 6 -command Apol_RBAC::_searchRBACs]
+ pack $ok -side right -padx 5 -pady 5 -anchor ne
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$dbox getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_RBAC::open {ppath} {
+ variable vals
+ variable widgets
+ $widgets(allow:source) configure -values $Apol_Roles::role_list
+ $widgets(allow:target) configure -values $Apol_Roles::role_list
+ $widgets(trans:source) configure -values $Apol_Roles::role_list
+ $widgets(trans:default) configure -values $Apol_Roles::role_list
+ $widgets(both:source) configure -values $Apol_Roles::role_list
+
+ # force a refresh of the types box for transitions
+ set vals(target_type:types) $vals(target_type:types)
+
+ # force a flip to the allow page
+ set vals(rule_selection) allow
+}
+
+proc Apol_RBAC::close {} {
+ variable widgets
+
+ _initializeVars
+ $widgets(allow:source) configure -values {}
+ $widgets(allow:target) configure -values {}
+ $widgets(trans:source) configure -values {}
+ $widgets(trans:target) configure -values {}
+ $widgets(trans:default) configure -values {}
+ $widgets(both:source) configure -values {}
+}
+
+proc Apol_RBAC::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+#### private functions below ####
+
+proc Apol_RBAC::_initializeVars {} {
+ variable vals
+ array set vals {
+ rule_selection allow
+
+ source:use 0
+ source:sym {}
+ source:which source
+
+ target_role:use 0
+ target_role:sym {}
+ target_type:use 0
+ target_type:sym {}
+ target_type:types 1
+ target_type:attribs 0
+
+ default:use 0
+ default:sym {}
+ }
+}
+
+proc Apol_RBAC::_allowCreate {a_f} {
+ variable vals
+ variable widgets
+
+ set source [frame $a_f.source]
+ set source_cb [checkbutton $source.enable -text "Source role" \
+ -variable Apol_RBAC::vals(source:use)]
+ set widgets(allow:source) [ComboBox $source.cb -width 20 -state disabled \
+ -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_RBAC::vals(source:sym) \
+ -helptext "Type or select a role" -autopost 1]
+ set which_fm [frame $source.which]
+ set which_source [radiobutton $which_fm.source \
+ -text "As source" -state disabled \
+ -variable Apol_RBAC::vals(source:which) \
+ -value source]
+ set which_any [radiobutton $which_fm.any \
+ -text "As source or target" -state disabled \
+ -variable Apol_RBAC::vals(source:which) \
+ -value either]
+ trace add variable Apol_RBAC::vals(source:use) write \
+ [list Apol_RBAC::_toggleCheckbutton $widgets(allow:source) [list $which_source $which_any]]
+ pack $which_source $which_any -side top -anchor w
+ pack $source_cb -side top -anchor w
+ pack $widgets(allow:source) -side top -expand 0 -fill x -padx 4
+ pack $which_fm -anchor w -padx 8
+ pack $source -side left -padx 4 -pady 2 -expand 0 -anchor nw
+
+ set target [frame $a_f.target]
+ set widgets(allow:target_cb) [checkbutton $target.enable -text "Target role" \
+ -variable Apol_RBAC::vals(target_role:use)]
+ set widgets(allow:target) [ComboBox $target.cb -width 20 -state disabled \
+ -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_RBAC::vals(target_role:sym) \
+ -helptext "Type or select a role" -autopost 1]
+ trace add variable Apol_RBAC::vals(target_role:use) write \
+ [list Apol_RBAC::_toggleCheckbutton $widgets(allow:target) {}]
+ pack $widgets(allow:target_cb) -side top -anchor w
+ pack $widgets(allow:target) -side top -expand 0 -fill x -padx 4
+ pack $target -side left -padx 4 -pady 2 -expand 0 -fill y
+}
+
+proc Apol_RBAC::_transCreate {t_f} {
+ variable vals
+ variable widgets
+
+ set source [frame $t_f.source]
+ set source_cb [checkbutton $source.enable -text "Source role" \
+ -variable Apol_RBAC::vals(source:use)]
+ set widgets(trans:source) [ComboBox $source.cb -width 20 -state disabled \
+ -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_RBAC::vals(source:sym) \
+ -helptext "Type or select a role" -autopost 1]
+ set which_fm [frame $source.which]
+ set which_source [radiobutton $which_fm.source \
+ -text "As source" -state disabled \
+ -variable Apol_RBAC::vals(source:which) \
+ -value source]
+ set which_any [radiobutton $which_fm.any \
+ -text "As source or default" -state disabled \
+ -variable Apol_RBAC::vals(source:which) \
+ -value either]
+ trace add variable Apol_RBAC::vals(source:use) write \
+ [list Apol_RBAC::_toggleCheckbutton $widgets(trans:source) [list $which_source $which_any]]
+ pack $which_source $which_any -side top -anchor w
+ pack $source_cb -side top -anchor w
+ pack $widgets(trans:source) -side top -expand 0 -fill x -padx 4
+ pack $which_fm -anchor w -padx 8
+ pack $source -side left -padx 4 -pady 2 -expand 0 -anchor nw
+
+ set target [frame $t_f.target]
+ set target_cb [checkbutton $target.enable -text "Target type" \
+ -variable Apol_RBAC::vals(target_type:use)]
+ set widgets(trans:target) [ComboBox $target.cb -width 20 -state disabled \
+ -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_RBAC::vals(target_type:sym) \
+ -helptext "Type or select a type/attribute" -autopost 1]
+ set ta_frame [frame $target.ta]
+ set types [checkbutton $ta_frame.types -text "Types" -state disabled \
+ -variable Apol_RBAC::vals(target_type:types)]
+ set attribs [checkbutton $ta_frame.attribs -text "Attribs" -state disabled \
+ -variable Apol_RBAC::vals(target_type:attribs)]
+ $types configure -command [list Apol_RBAC::_toggleTAPushed $types]
+ $attribs configure -command [list Apol_RBAC::_toggleTAPushed $attribs]
+ trace add variable Apol_RBAC::vals(target_type:types) write \
+ [list Apol_RBAC::_toggleTASym]
+ trace add variable Apol_RBAC::vals(target_type:attribs) write \
+ [list Apol_RBAC::_toggleTASym]
+ pack $types $attribs -side left -padx 2
+ trace add variable Apol_RBAC::vals(target_type:use) write \
+ [list Apol_RBAC::_toggleCheckbutton $widgets(trans:target) [list $types $attribs]]
+ pack $target_cb -side top -anchor w
+ pack $widgets(trans:target) -side top -expand 0 -fill x -padx 4
+ pack $ta_frame -anchor center -pady 2
+ pack $target -side left -padx 4 -pady 2 -expand 0 -fill y
+
+ set default [frame $t_f.default]
+ set widgets(trans:default_cb) [checkbutton $default.enable -text "Default role" \
+ -variable Apol_RBAC::vals(default:use)]
+ set widgets(trans:default) [ComboBox $default.cb -width 20 -state disabled \
+ -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_RBAC::vals(default:sym) \
+ -helptext "Type or select a role" -autopost 1]
+ trace add variable Apol_RBAC::vals(default:use) write \
+ [list Apol_RBAC::_toggleCheckbutton $widgets(trans:default) {}]
+ pack $widgets(trans:default_cb) -side top -anchor w
+ pack $widgets(trans:default) -side top -expand 0 -fill x -padx 4
+ pack $default -side left -padx 4 -pady 2 -expand 0 -fill y
+}
+
+proc Apol_RBAC::_bothCreate {b_f} {
+ variable vals
+ variable widgets
+
+ set source [frame $b_f.source]
+ set source_cb [checkbutton $source.enable -text "Source role" \
+ -variable Apol_RBAC::vals(source:use)]
+ set widgets(both:source) [ComboBox $source.cb -width 20 -state disabled \
+ -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_RBAC::vals(source:sym) \
+ -helptext "Type or select a role" -autopost 1]
+ set which_fm [frame $source.which]
+ set which_source [radiobutton $which_fm.source \
+ -text "As source" -state disabled \
+ -variable Apol_RBAC::vals(source:which) \
+ -value source]
+ set which_any [radiobutton $which_fm.any \
+ -text "Any field" -state disabled \
+ -variable Apol_RBAC::vals(source:which) \
+ -value either]
+ trace add variable Apol_RBAC::vals(source:use) write \
+ [list Apol_RBAC::_toggleCheckbutton $widgets(both:source) [list $which_source $which_any]]
+ pack $which_source $which_any -side top -anchor w
+ pack $source_cb -side top -anchor w
+ pack $widgets(both:source) -side top -expand 0 -fill x -padx 4
+ pack $which_fm -anchor w -padx 8
+ pack $source -side left -padx 4 -pady 2 -expand 0 -anchor nw
+}
+
+proc Apol_RBAC::_toggleCheckbutton {cb w name1 name2 ops} {
+ variable vals
+
+ if {$vals($name2)} {
+ $cb configure -state normal -entrybg white
+ foreach x $w {
+ $x configure -state normal
+ }
+ } else {
+ $cb configure -state disabled -entrybg $ApolTop::default_bg_color
+ foreach x $w {
+ $x configure -state disabled
+ }
+ }
+
+ _maybeEnableTargetRole
+ _maybeEnableDefaultRole
+}
+
+# called whenever user selects 'as source'/'any' radio button
+proc Apol_RBAC::_toggleRoleBox {name1 name2 ops} {
+ _maybeEnableTargetRole
+ _maybeEnableDefaultRole
+}
+
+proc Apol_RBAC::_maybeEnableTargetRole {} {
+ variable vals
+ variable widgets
+ if {$vals(source:use) && $vals(source:which) == "either"} {
+ $widgets(allow:target_cb) configure -state disabled
+ $widgets(allow:target) configure -state disabled -entrybg $ApolTop::default_bg_color
+ } else {
+ $widgets(allow:target_cb) configure -state normal
+ # reset subwidgets' state
+ set vals(target_role:use) $vals(target_role:use)
+ }
+}
+
+proc Apol_RBAC::_maybeEnableDefaultRole {} {
+ variable vals
+ variable widgets
+ if {$vals(source:use) && $vals(source:which) == "either"} {
+ $widgets(trans:default_cb) configure -state disabled
+ $widgets(trans:default) configure -state disabled -entrybg $ApolTop::default_bg_color
+ } else {
+ $widgets(trans:default_cb) configure -state normal
+ # reset subwidgets' state
+ set vals(default:use) $vals(default:use)
+ }
+}
+
+proc Apol_RBAC::_toggleTASym {name1 name2 ops} {
+ variable vals
+ variable widgets
+
+ if {!$vals(target_type:types) && !$vals(target_type:attribs)} {
+ # don't change combobox if both types and attribs are deselected
+ return
+ }
+ if {$vals(target_type:types) && $vals(target_type:attribs)} {
+ set items [lsort [concat [Apol_Types::getTypes] [Apol_Types::getAttributes]]]
+ } elseif {$vals(target_type:types)} {
+ set items [Apol_Types::getTypes]
+ } else {
+ set items [Apol_Types::getAttributes]
+ }
+ $widgets(trans:target) configure -values $items
+}
+
+# disallow both types and attribs to be deselected
+proc Apol_RBAC::_toggleTAPushed {cb} {
+ variable vals
+ if {!$vals(target_type:types) && !$vals(target_type:attribs)} {
+ $cb select
+ }
+}
+
+# callback invoked when the user changes which RBAC rule to search
+proc Apol_RBAC::_ruleChanged {name1 name2 ops} {
+ variable vals
+ variable widgets
+ Apol_Widget::clearSearchResults $widgets(results)
+ $widgets(options_pm) raise $vals(rule_selection)
+}
+
+proc Apol_RBAC::_searchRBACs {} {
+ variable vals
+ variable widgets
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+
+ set raq {}
+ set rtq {}
+
+ if {$vals(rule_selection) == "allow" || $vals(rule_selection) == "both"} {
+ set raq [new_apol_role_allow_query_t]
+ }
+ if {$vals(rule_selection) == "trans" || $vals(rule_selection) == "both"} {
+ set rtq [new_apol_role_trans_query_t]
+ }
+
+ set source_sym {}
+ set is_any 0
+ if {$vals(source:use)} {
+ if {$vals(source:sym) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No source role selected."
+ return
+ }
+ if {$vals(source:which) == "either"} {
+ set is_any 1
+ }
+ set source_sym $vals(source:sym)
+ }
+
+ set target_role {}
+ set target_type {}
+ if {$vals(rule_selection) == "allow" && $vals(target_role:use) && \
+ (!$vals(source:use) || $vals(source:which) != "either")} {
+ if {$vals(target_role:sym) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No target role selected."
+ return
+ }
+ set target_role $vals(target_role:sym)
+ }
+ if {$vals(rule_selection) == "trans" && $vals(target_type:use)} {
+ if {$vals(target_type:sym) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No target type selected."
+ return
+ }
+ set target_type $vals(target_type:sym)
+ }
+
+ set default_role {}
+ if {$vals(rule_selection) == "trans" && $vals(default:use) && \
+ (!$vals(source:use) || $vals(source:which) != "either")} {
+ if {$vals(default:sym) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No default role selected."
+ return
+ }
+ set default_role $vals(default:sym)
+ }
+
+ set role_allows {}
+ if {$raq != {}} {
+ $raq set_source $::ApolTop::policy $source_sym
+ $raq set_source_any $::ApolTop::policy $is_any
+ $raq set_target $::ApolTop::policy $target_role
+ set v [$raq run $::ApolTop::policy]
+ $raq -acquire
+ $raq -delete
+ set role_allows [role_allow_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ }
+
+ set role_trans {}
+ if {$rtq != {}} {
+ $rtq set_source $::ApolTop::policy $source_sym
+ $rtq set_source_any $::ApolTop::policy $is_any
+ $rtq set_target $::ApolTop::policy $target_type 0
+ $rtq set_default $::ApolTop::policy $default_role
+ set v [$rtq run $::ApolTop::policy]
+ $rtq -acquire
+ $rtq -delete
+ set role_trans [role_trans_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ }
+
+ set num_results [expr {[llength $role_allows] + [llength $role_trans]}]
+ if {$num_results == 0} {
+ set text "Search returned no results."
+ } else {
+ set text "$num_results rule"
+ if {$num_results != 1} {
+ append text s
+ }
+ append text " match the search criteria.\n\n"
+ }
+
+ foreach r [lsort -command _role_allow_sort $role_allows] {
+ append text "[_render_role_allow $r]\n"
+ }
+ foreach r [lsort -command _role_trans_sort $role_trans] {
+ append text "[_render_role_trans $r]\n"
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $text
+}
+
+proc Apol_RBAC::_render_role_allow {qpol_role_allow_datum} {
+ apol_role_allow_render $::ApolTop::policy $qpol_role_allow_datum
+}
+
+proc Apol_RBAC::_render_role_trans {qpol_role_trans_datum} {
+ apol_role_trans_render $::ApolTop::policy $qpol_role_trans_datum
+}
+
+proc Apol_RBAC::_role_allow_sort {a b} {
+ set r1 [[$a get_source_role $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set r2 [[$b get_source_role $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ if {[set z [string compare $r1 $r2]] != 0} {
+ return $z
+ }
+
+ set r1 [[$a get_target_role $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set r2 [[$b get_target_role $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ string compare $r1 $r2
+}
+
+proc Apol_RBAC::_role_trans_sort {a b} {
+ set r1 [[$a get_source_role $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set r2 [[$b get_source_role $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ if {[set z [string compare $r1 $r2]] != 0} {
+ return $z
+ }
+
+ set r1 [[$a get_target_type $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ set r2 [[$b get_target_type $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ string compare $r1 $r2
+}
diff --git a/apol/relabel_module.tcl b/apol/relabel_module.tcl
new file mode 100644
index 0000000..ddace64
--- /dev/null
+++ b/apol/relabel_module.tcl
@@ -0,0 +1,898 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Analysis_relabel {
+ variable vals
+ variable widgets
+ Apol_Analysis::registerAnalysis "Apol_Analysis_relabel" "Direct Relabel"
+}
+
+proc Apol_Analysis_relabel::create {options_frame} {
+ variable vals
+ variable widgets
+
+ _reinitializeVals
+
+ set mode_tf [TitleFrame $options_frame.mode -text "Mode"]
+ pack $mode_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set object_mode [radiobutton [$mode_tf getframe].object \
+ -text "Object" -value "object" \
+ -variable Apol_Analysis_relabel::vals(mode)]
+ pack $object_mode -anchor w
+ set widgets(mode:to) [checkbutton [$mode_tf getframe].to \
+ -text "To" \
+ -variable Apol_Analysis_relabel::vals(mode:to)]
+ $widgets(mode:to) configure -command \
+ [list Apol_Analysis_relabel::_toggleToFromPushed $widgets(mode:to)]
+ set widgets(mode:from) [checkbutton [$mode_tf getframe].from \
+ -text "From" \
+ -variable Apol_Analysis_relabel::vals(mode:from)]
+ $widgets(mode:from) configure -command \
+ [list Apol_Analysis_relabel::_toggleToFromPushed $widgets(mode:from)]
+ pack $widgets(mode:to) $widgets(mode:from) -anchor w -padx 8
+ set subject_mode [radiobutton [$mode_tf getframe].subject \
+ -text "Subject" -value "subject" \
+ -variable Apol_Analysis_relabel::vals(mode)]
+ pack $subject_mode -anchor w -pady 4
+ trace add variable Apol_Analysis_relabel::vals(mode) write \
+ Apol_Analysis_relabel::_toggleModeSelected
+
+ set req_tf [TitleFrame $options_frame.req -text "Required Parameters"]
+ pack $req_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set l [label [$req_tf getframe].l -textvariable Apol_Analysis_relabel::vals(type:label)]
+ pack $l -anchor w
+ set widgets(type) [Apol_Widget::makeTypeCombobox [$req_tf getframe].type]
+ pack $widgets(type)
+
+ set filter_tf [TitleFrame $options_frame.filter -text "Optional Result Filters"]
+ pack $filter_tf -side left -padx 2 -pady 2 -expand 1 -fill both
+ set advanced_f [frame [$filter_tf getframe].advanced]
+ pack $advanced_f -side left -anchor nw
+ set access_enable [checkbutton $advanced_f.enable -text "Use advanced filters" \
+ -variable Apol_Analysis_relabel::vals(advanced_enable)]
+ pack $access_enable -anchor w
+ set widgets(advanced) [button $advanced_f.adv -text "Advanced Filters" \
+ -command Apol_Analysis_relabel::_createAdvancedDialog \
+ -state disabled]
+ pack $widgets(advanced) -anchor w -padx 4
+ trace add variable Apol_Analysis_relabel::vals(advanced_enable) write \
+ Apol_Analysis_relabel::_toggleAdvancedSelected
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry [$filter_tf getframe].end]
+ $widgets(regexp).cb configure -text "Filter result types using regular expression"
+ pack $widgets(regexp) -side left -anchor nw -padx 8
+}
+
+proc Apol_Analysis_relabel::open {} {
+ variable vals
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(type)
+ set vals(classes:inc) {}
+ foreach class [Apol_Class_Perms::getClasses] {
+ set perms [Apol_Class_Perms::getPermsForClass $class]
+ if {[lsearch $perms "relabelto"] >= 0 && [lsearch $perms "relabelfrom"] >= 0} {
+ lappend vals(classes:inc) $class
+ }
+ }
+ set vals(subjects:inc) [Apol_Types::getTypes]
+ set vals(subjects:inc_all) $vals(subjects:inc)
+}
+
+proc Apol_Analysis_relabel::close {} {
+ variable widgets
+ _reinitializeVals
+ _reinitializeWidgets
+ Apol_Widget::clearTypeCombobox $widgets(type)
+}
+
+proc Apol_Analysis_relabel::getInfo {} {
+ return "Direct relabel analysis is designed to facilitate querying a policy
+for both potential changes to object labels and relabel privileges
+granted to a subject. These two modes are respectively called Object
+Mode and Subject Mode.
+
+\nOBJECT MODE
+In object mode the user specifies a starting or ending type and either
+To, From, or Both. When To is selected all types to which the starting
+type can be relabeled will be displayed. When From is selected all
+types from which the ending type can be relabeled will be
+displayed. Both will, obviously, do both analyses.
+
+\nSUBJECT MODE
+In subject mode the user specifies only a subject type. Two lists of
+types will be displayed corresponding to all of the types To which the
+subject can relabel and From which the subject can relabel.
+
+\nOPTIONAL RESULT FILTERS
+Results may be filtered in several ways. The end types resulting from
+a query may be filtered by regular expression. The Advanced Filters
+provide the option of selecting which object classes to include in the
+analysis and which types to include as subjects of relabeling
+operations. Note, excluded subjects are ignored in subject mode
+because only the selected subject type is used as a subject."
+}
+
+proc Apol_Analysis_relabel::newAnalysis {} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ set f [_createResultsDisplay]
+ _renderResults $f $results
+ #results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_relabel::updateAnalysis {f} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ _clearResultsDisplay $f
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_relabel::reset {} {
+ _reinitializeVals
+ _reinitializeWidgets
+ open
+}
+
+proc Apol_Analysis_relabel::switchTab {query_options} {
+ variable vals
+ variable widgets
+ array set vals $query_options
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_relabel::saveQuery {channel} {
+ variable vals
+ variable widgets
+ foreach {key value} [array get vals] {
+ if {$key != "classes:inc" && \
+ $key != "subjects:inc_all" && $key != "subjects:inc" && \
+ $key != "subjects:exc"} {
+ puts $channel "$key $value"
+ }
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ puts $channel "type [lindex $type 0]"
+ puts $channel "type:attrib [lindex $type 1]"
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ puts $channel "regexp:enable $use_regexp"
+ puts $channel "regexp $regexp"
+}
+
+proc Apol_Analysis_relabel::loadQuery {channel} {
+ variable vals
+
+ set classes_exc {}
+ set subjects_exc {}
+ while {[gets $channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ set key {}
+ set value {}
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ switch -- $key {
+ classes:exc {
+ set classes_exc $value
+ }
+ subjects:exc_all {
+ set subjects_exc $value
+ }
+ default {
+ set vals($key) $value
+ }
+ }
+ }
+
+ # fill in the exclusion lists using only classes/types found
+ # within the current policy
+ open
+
+ set vals(classes:exc) {}
+ foreach c $classes_exc {
+ set i [lsearch $vals(classes:inc) $c]
+ if {$i >= 0} {
+ lappend vals(classes:exc) $c
+ set vals(classes:inc) [lreplace $vals(classes:inc) $i $i]
+ }
+ }
+ set vals(classes:exc) [lsort $vals(classes:exc)]
+
+ set vals(subjects:exc_all) {}
+ set vals(subjects:exc) {}
+ foreach s $subjects_exc {
+ set i [lsearch $vals(subjects:inc_all) $s]
+ if {$i >= 0} {
+ lappend vals(subjects:exc_all) $s
+ lappend vals(subjects:exc) $s
+ set vals(subjects:inc_all) [lreplace $vals(subjects:inc_all) $i $i]
+ set i [lsearch $vals(subjects:inc) $s]
+ set vals(subjects:inc) [lreplace $vals(subjects:inc) $i $i]
+ }
+ }
+ set vals(subjects:exc_all) [lsort $vals(subjects:exc_all)]
+ set vals(subjects:exc) [lsort $vals(subjects:exc)]
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_relabel::getTextWidget {tab} {
+ return [$tab.right getframe].res.tb
+}
+
+#################### private functions below ####################
+
+proc Apol_Analysis_relabel::_reinitializeVals {} {
+ variable vals
+
+ array set vals {
+ mode object
+ mode:to 1
+ mode:from 0
+
+ type:label "Starting type"
+ type {} type:attrib {}
+
+ regexp:enable 0
+ regexp {}
+
+ advanced_enable 0
+ classes:inc {} classes:exc {}
+ subjects:inc {} subjects:inc_all {}
+ subjects:exc {} subjects:exc_all {}
+ subjects:attribenable 0 subjects:attrib {}
+ }
+}
+
+proc Apol_Analysis_relabel::_reinitializeWidgets {} {
+ variable vals
+ variable widgets
+
+ if {$vals(type:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(type) [list $vals(type) $vals(type:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(type) $vals(type)
+ }
+ Apol_Widget::setRegexpEntryValue $widgets(regexp) $vals(regexp:enable) $vals(regexp)
+ _updateTypeLabel
+}
+
+proc Apol_Analysis_relabel::_toggleModeSelected {name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals(mode) == "object"} {
+ $widgets(mode:to) configure -state normal
+ $widgets(mode:from) configure -state normal
+ } else {
+ $widgets(mode:to) configure -state disabled
+ $widgets(mode:from) configure -state disabled
+ }
+ _updateTypeLabel
+}
+
+# disallow both to and from to be deselected
+proc Apol_Analysis_relabel::_toggleToFromPushed {cb} {
+ variable vals
+ if {!$vals(mode:to) && !$vals(mode:from)} {
+ $cb select
+ }
+ _updateTypeLabel
+}
+
+proc Apol_Analysis_relabel::_updateTypeLabel {} {
+ variable vals
+ if {$vals(mode) == "subject"} {
+ set vals(type:label) "Subject"
+ } elseif {$vals(mode:to) && $vals(mode:from)} {
+ set vals(type:label) "Starting/ending type"
+ } elseif {$vals(mode:from)} {
+ set vals(type:label) "Ending type"
+ } else {
+ set vals(type:label) "Starting type"
+ }
+}
+
+proc Apol_Analysis_relabel::_toggleAdvancedSelected {name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals(advanced_enable)} {
+ $widgets(advanced) configure -state normal
+ } else {
+ $widgets(advanced) configure -state disabled
+ }
+}
+
+################# functions that do advanced filters #################
+
+proc Apol_Analysis_relabel::_createAdvancedDialog {} {
+ variable widgets
+ $widgets(advanced) configure -state disabled
+ destroy .relabel_analysis_adv
+ variable vals
+
+ set d [Dialog .relabel_analysis_adv -modal local -separator 1 -title "Direct Relabel Advanced Filters" -parent .]
+ $d add -text "Close"
+
+ set tf [TitleFrame [$d getframe].objs -text "Filter By Object Classes"]
+ pack $tf -side top -expand 1 -fill both -padx 2 -pady 4
+ _createAdvancedFilter [$tf getframe] "Object Classes" classes 0
+ set l [label [$tf getframe].l -text "Only showing object classes that have both 'relabelto' and 'relabelfrom' permissions."]
+ grid $l - - -padx 4 -pady 2
+
+ set tf [TitleFrame [$d getframe].types -text "Filter By Subject Types"]
+ pack $tf -side top -expand 1 -fill both -padx 2 -pady 4
+ if {$vals(mode) == "object"} {
+ _createAdvancedFilter [$tf getframe] "Subject Types" subjects 0
+ } else {
+ _createAdvancedFilter [$tf getframe] "Subject Types" subjects 1
+ }
+ set inc [$tf getframe].inc
+ set exc [$tf getframe].exc
+
+ set attrib [frame [$tf getframe].a]
+ grid $attrib - -
+ set attrib_enable [checkbutton $attrib.ae -anchor w \
+ -text "Filter by attribute" \
+ -variable Apol_Analysis_relabel::vals(subjects:attribenable)]
+ set attrib_box [ComboBox $attrib.ab -autopost 1 -entrybg white -width 16 \
+ -values $Apol_Types::attriblist \
+ -textvariable Apol_Analysis_relabel::vals(subjects:attrib)]
+ $attrib_enable configure -command \
+ [list Apol_Analysis_relabel::_attribEnabled $attrib_box]
+ # remove any old traces on the attribute
+ trace remove variable Apol_Analysis_relabel::vals(subjects:attrib) write \
+ [list Apol_Analysis_relabel::_attribChanged]
+ trace add variable Apol_Analysis_relabel::vals(subjects:attrib) write \
+ [list Apol_Analysis_relabel::_attribChanged]
+ pack $attrib_enable -side top -expand 0 -fill x -anchor sw -padx 5 -pady 2
+ pack $attrib_box -side top -expand 1 -fill x -padx 10
+ _attribEnabled $attrib_box
+ if {$vals(mode) == "subject"} {
+ $attrib_enable configure -state disabled
+ $attrib_box configure -state disabled
+ }
+
+ $d draw
+ $widgets(advanced) configure -state normal
+}
+
+proc Apol_Analysis_relabel::_createAdvancedFilter {f title varname disabled} {
+ set l1 [label $f.l1 -text "Included $title"]
+ set l2 [label $f.l2 -text "Excluded $title"]
+ grid $l1 x $l2 -sticky w
+
+ set inc [Apol_Widget::makeScrolledListbox $f.inc -height 10 -width 24 \
+ -listvar Apol_Analysis_relabel::vals($varname:inc) \
+ -selectmode extended -exportselection 0]
+ set exc [Apol_Widget::makeScrolledListbox $f.exc -height 10 -width 24 \
+ -listvar Apol_Analysis_relabel::vals($varname:exc) \
+ -selectmode extended -exportselection 0]
+ set inc_lb [Apol_Widget::getScrolledListbox $inc]
+ set exc_lb [Apol_Widget::getScrolledListbox $exc]
+ set bb [ButtonBox $f.bb -homogeneous 1 -orient vertical -spacing 4]
+ $bb add -text "-->" -width 10 -command [list Apol_Analysis_relabel::_moveToExclude $varname $inc_lb $exc_lb]
+ $bb add -text "<--" -width 10 -command [list Apol_Analysis_relabel::_moveToInclude $varname $inc_lb $exc_lb]
+ grid $inc $bb $exc -sticky nsew
+
+ set inc_bb [ButtonBox $f.inc_bb -homogeneous 1 -spacing 4]
+ $inc_bb add -text "Select All" -command [list $inc_lb selection set 0 end]
+ $inc_bb add -text "Unselect" -command [list $inc_lb selection clear 0 end]
+ set exc_bb [ButtonBox $f.exc_bb -homogeneous 1 -spacing 4]
+ $exc_bb add -text "Select All" -command [list $exc_lb selection set 0 end]
+ $exc_bb add -text "Unselect" -command [list $exc_lb selection clear 0 end]
+ grid $inc_bb x $exc_bb -pady 4
+
+ grid columnconfigure $f 0 -weight 1 -uniform 0 -pad 2
+ grid columnconfigure $f 1 -weight 0 -pad 8
+ grid columnconfigure $f 2 -weight 1 -uniform 0 -pad 2
+
+ if {$disabled} {
+ foreach w [list $l1 $l2 $bb $inc_bb $exc_bb] {
+ $w configure -state disabled
+ }
+ Apol_Widget::setScrolledListboxState $inc disabled
+ Apol_Widget::setScrolledListboxState $exc disabled
+ }
+}
+
+proc Apol_Analysis_relabel::_moveToExclude {varname inc exc} {
+ variable vals
+ if {[set selection [$inc curselection]] == {}} {
+ return
+ }
+ foreach i $selection {
+ lappend perms [$inc get $i]
+ }
+ set vals($varname:exc) [lsort [concat $vals($varname:exc) $perms]]
+ if {$varname == "subjects"} {
+ set vals(subjects:exc_all) [lsort [concat $vals(subjects:exc_all) $perms]]
+ }
+ foreach p $perms {
+ set i [lsearch $vals($varname:inc) $p]
+ set vals($varname:inc) [lreplace $vals($varname:inc) $i $i]
+ if {$varname == "subjects"} {
+ set i [lsearch $vals(subjects:inc_all) $p]
+ set vals(subjects:inc_all) [lreplace $vals(subjects:inc_all) $i $i]
+ }
+ }
+ $inc selection clear 0 end
+ $exc selection clear 0 end
+}
+
+proc Apol_Analysis_relabel::_moveToInclude {varname inc exc} {
+ variable vals
+ if {[set selection [$exc curselection]] == {}} {
+ return
+ }
+ foreach i $selection {
+ lappend perms [$exc get $i]
+ }
+ set vals($varname:inc) [lsort [concat $vals($varname:inc) $perms]]
+ if {$varname == "subjects"} {
+ set vals(subjects:inc_all) [lsort [concat $vals(subjects:inc_all) $perms]]
+ }
+ foreach p $perms {
+ set i [lsearch $vals($varname:exc) $p]
+ set vals($varname:exc) [lreplace $vals($varname:exc) $i $i]
+ if {$varname == "subjects"} {
+ set i [lsearch $vals(subjects:exc_all) $p]
+ set vals(subjects:exc_all) [lreplace $vals(subjects:exc_all) $i $i]
+ }
+ }
+ $inc selection clear 0 end
+ $exc selection clear 0 end
+}
+
+proc Apol_Analysis_relabel::_attribEnabled {cb} {
+ variable vals
+ if {$vals(subjects:attribenable)} {
+ $cb configure -state normal
+ _filterTypeLists $vals(subjects:attrib)
+ } else {
+ $cb configure -state disabled
+ _filterTypeLists ""
+ }
+}
+
+proc Apol_Analysis_relabel::_attribChanged {name1 name2 op} {
+ variable vals
+ if {$vals(subjects:attribenable)} {
+ _filterTypeLists $vals(subjects:attrib)
+ }
+}
+
+proc Apol_Analysis_relabel::_filterTypeLists {attrib} {
+ variable vals
+ if {$attrib != {}} {
+ set typesList {}
+ if {[Apol_Types::isAttributeInPolicy $attrib]} {
+ set qpol_type_datum [new_qpol_type_t $::ApolTop::qpolicy $attrib]
+ set i [$qpol_type_datum get_type_iter $::ApolTop::qpolicy]
+ foreach t [iter_to_list $i] {
+ set t [qpol_type_from_void $t]
+ lappend typesList [$t get_name $::ApolTop::qpolicy]
+ }
+ $i -acquire
+ $i -delete
+ }
+ if {$typesList == {}} {
+ # unknown attribute, so don't change listboxes
+ return
+ }
+ set vals(subjects:inc) {}
+ set vals(subjects:exc) {}
+ foreach t $typesList {
+ if {[lsearch $vals(subjects:inc_all) $t] >= 0} {
+ lappend vals(subjects:inc) $t
+ }
+ if {[lsearch $vals(subjects:exc_all) $t] >= 0} {
+ lappend vals(subjects:exc) $t
+ }
+ }
+ set vals(subjects:inc) [lsort $vals(subjects:inc)]
+ set vals(subjects:exc) [lsort $vals(subjects:exc)]
+ } else {
+ set vals(subjects:inc) $vals(subjects:inc_all)
+ set vals(subjects:exc) $vals(subjects:exc_all)
+ }
+}
+
+#################### functions that do analyses ####################
+
+proc Apol_Analysis_relabel::_checkParams {} {
+ variable vals
+ variable widgets
+ if {![ApolTop::is_policy_open]} {
+ return "No current policy file is opened."
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ if {[lindex $type 0] == {}} {
+ return "No type was selected."
+ }
+ if {![Apol_Types::isTypeInPolicy [lindex $type 0]]} {
+ return "[lindex $type 0] is not a type within the policy."
+ }
+ set vals(type) [lindex $type 0]
+ set vals(type:attrib) [lindex $type 1]
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$use_regexp && $regexp == {}} {
+ return "No regular expression provided."
+ }
+ set vals(regexp:enable) $use_regexp
+ set vals(regexp) $regexp
+ if {$vals(advanced_enable)} {
+ if {$vals(classes:inc) == {}} {
+ return "At least one object class must be included."
+ }
+ if {$vals(mode) == "object" && $vals(subjects:inc_all) == {}} {
+ return "At least one subject type must be included."
+ }
+ }
+ return {} ;# all parameters passed, now ready to do search
+}
+
+proc Apol_Analysis_relabel::_analyze {} {
+ variable vals
+ if {$vals(mode) == "object"} {
+ if {$vals(mode:to) && $vals(mode:from)} {
+ set mode $::APOL_RELABEL_DIR_BOTH
+ } elseif {$vals(mode:to)} {
+ set mode $::APOL_RELABEL_DIR_TO
+ } else {
+ set mode $::APOL_RELABEL_DIR_FROM
+ }
+ } else {
+ set mode $::APOL_RELABEL_DIR_SUBJECT
+ }
+ if {$vals(advanced_enable) && $vals(classes:exc) != {}} {
+ set classes $vals(classes:inc)
+ } else {
+ set classes {}
+ }
+ if {$vals(advanced_enable) && $vals(subjects:exc) != {}} {
+ set subjects $vals(subjects:inc)
+ } else {
+ set subjects {}
+ }
+ if {$vals(regexp:enable)} {
+ set regexp $vals(regexp)
+ } else {
+ set regexp {}
+ }
+
+ set q [new_apol_relabel_analysis_t]
+ $q set_dir $::ApolTop::policy $mode
+ $q set_type $::ApolTop::policy $vals(type)
+ foreach c $classes {
+ $q append_class $::ApolTop::policy $c
+ }
+ foreach s $subjects {
+ $q append_subject $::ApolTop::policy $s
+ }
+ $q set_result_regex $::ApolTop::policy $regexp
+ set results [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ return $results
+}
+
+################# functions that control analysis output #################
+
+proc Apol_Analysis_relabel::_createResultsDisplay {} {
+ variable vals
+
+ set f [Apol_Analysis::createResultTab "Relabel" [array get vals]]
+
+ if {$vals(mode) == "object"} {
+ if {$vals(mode:to) && $vals(mode:from)} {
+ set tree_title "Type $vals(type) relabels to/from"
+ } elseif {$vals(mode:to)} {
+ set tree_title "Type $vals(type) relabels to"
+ } else {
+ set tree_title "Type $vals(type) relabels from"
+ }
+ } else {
+ set tree_title "Subject $vals(type) relabels"
+ }
+ set tree_tf [TitleFrame $f.left -text $tree_title]
+ pack $tree_tf -side left -expand 0 -fill y -padx 2 -pady 2
+ set sw [ScrolledWindow [$tree_tf getframe].sw -auto both]
+ set tree [Tree [$sw getframe].tree -width 24 -redraw 1 -borderwidth 0 \
+ -highlightthickness 0 -showlines 1 -padx 0 -bg white]
+ $sw setwidget $tree
+ pack $sw -expand 1 -fill both
+
+ set res_tf [TitleFrame $f.right -text "Relabeling Results"]
+ pack $res_tf -side left -expand 1 -fill both -padx 2 -pady 2
+ set res [Apol_Widget::makeSearchResults [$res_tf getframe].res]
+ $res.tb tag configure title -font {Helvetica 14 bold}
+ $res.tb tag configure title_type -foreground blue -font {Helvetica 14 bold}
+ $res.tb tag configure num -font {Helvetica 12 bold}
+ $res.tb tag configure type_tag -foreground blue -font {Helvetica 12 bold}
+ pack $res -expand 1 -fill both
+
+ $tree configure -selectcommand [list Apol_Analysis_relabel::_treeSelect $res]
+ return $f
+}
+
+proc Apol_Analysis_relabel::_treeSelect {res tree node} {
+ if {$node != {}} {
+ $res.tb configure -state normal
+ $res.tb delete 0.0 end
+ set data [$tree itemcget $node -data]
+ if {[string index $node 0] == "o"} {
+ _renderResultsRuleObject $res $tree $node $data
+ } elseif {[string index $node 0] == "s"} {
+ _renderResultsRuleSubject $res $tree $node $data
+ } else {
+ # an informational node, whose data has already been rendered
+ eval $res.tb insert end $data
+ }
+ $res.tb configure -state disabled
+ }
+}
+
+proc Apol_Analysis_relabel::_clearResultsDisplay {f} {
+ variable vals
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+ $tree delete [$tree nodes root]
+ Apol_Widget::clearSearchResults $res
+ Apol_Analysis::setResultTabCriteria [array get vals]
+}
+
+proc Apol_Analysis_relabel::_renderResults {f results} {
+ variable vals
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+
+ $tree insert end root top -text $vals(type) -open 1 -drawcross auto
+ if {$vals(mode) == "object"} {
+ set top_text [_renderResultsObject $results $tree]
+ } else { ;# subject mode
+ set top_text [_renderResultsSubject $results $tree]
+ }
+ $tree itemconfigure top -data $top_text
+ $tree selection set top
+ $tree opentree top
+ $tree see top
+}
+
+proc Apol_Analysis_relabel::_result_type_sort {a b} {
+ set t1 [[$a get_result_type] get_name $::ApolTop::qpolicy]
+ set t2 [[$b get_result_type] get_name $::ApolTop::qpolicy]
+ string compare $t1 $t2
+}
+
+proc Apol_Analysis_relabel::_renderResultsObject {results tree} {
+ variable vals
+ if {$vals(mode:from) && $vals(mode:to)} {
+ set dir both
+ } elseif {$vals(mode:to)} {
+ set dir to
+ } else {
+ set dir from
+ }
+
+ foreach r [lsort -command _result_type_sort [relabel_result_vector_to_list $results]] {
+ set type [[$r get_result_type] get_name $::ApolTop::qpolicy]
+ set to [relabel_result_pair_vector_to_list [$r get_to]]
+ set from [relabel_result_pair_vector_to_list [$r get_from]]
+ set both [relabel_result_pair_vector_to_list [$r get_both]]
+ set pairs {}
+ foreach pair [concat $to $from $both] {
+ set intermed [[$pair get_intermediate_type] get_name $::ApolTop::qpolicy]
+ lappend pairs [list [$pair get_ruleA] [$pair get_ruleB] $intermed]
+ }
+ set pairs [lsort -unique $pairs]
+ $tree insert end top o:$dir:\#auto -text $type -data $pairs
+ }
+
+ set top_text [list "Direct Relabel Analysis: " title]
+ switch -- $dir {
+ both { lappend top_text "Starting/Ending Type: " title }
+ to { lappend top_text "Ending Type: " title }
+ from { lappend top_text "Starting Type: " title }
+ }
+ lappend top_text $vals(type) title_type \
+ "\n\n" title \
+ $vals(type) type_tag
+ if {[$results get_size]} {
+ switch -- $dir {
+ both { lappend top_text " can be relabeled to and from " {} }
+ to { lappend top_text " can be relabeled to " {} }
+ from { lappend top_text " can be relabeled from " {} }
+ }
+ lappend top_text [$results get_size] num \
+ " type(s).\n\n" {} \
+ "This tab provides the results of a Direct Relabel Analysis beginning\n" {}
+ switch -- $dir {
+ both { lappend top_text "with the starting/ending" {} }
+ to { lappend top_text "with the starting" {} }
+ from { lappend top_text "with the ending" {} }
+ }
+ lappend top_text " type above. The results of the analysis are\n" {} \
+ "presented in tree form with the root of the tree (this node) being the\n" {} \
+ "starting point for the analysis.\n\n" {} \
+ "Each child node in the tree represents a type in the current policy\n" {} \
+ "to/from which relabeling is allowed (depending on you selection\n" {} \
+ "above)." {}
+ } else {
+ switch -- $dir {
+ both { lappend top_text " cannot be relabeled to/from any type." {} }
+ to { lappend top_text " cannot be relabeled to any type." {} }
+ from { lappend top_text " cannot be relabeled from any type." {} }
+ }
+ }
+}
+
+proc Apol_Analysis_relabel::_renderResultsRuleObject {res tree node data} {
+ set header [list [$tree itemcget top -text] title_type]
+ lappend header " can be relabeled:\n" {}
+ eval $res.tb insert end $header
+
+ set dir [lindex [split $node :] 1]
+ set target_type [$tree itemcget $node -text]
+ foreach rule_pairs $data {
+ set class [[[lindex $rule_pairs 0] get_object_class $::ApolTop::qpolicy] get_name $::ApolTop::qpolicy]
+ lappend classes($class) $rule_pairs
+ }
+
+ foreach key [lsort [array names classes]] {
+ $res.tb configure -state normal
+ $res.tb insert end "\n$key:\n" title
+ foreach rule_pairs [lsort -index 2 $classes($key)] {
+ foreach {a_rule b_rule intermed} $rule_pairs {break}
+
+ # determine direction of relabelling
+ if {$dir == "to" || $dir == "from"} {
+ set dir_string $dir
+ } else {
+ set i [$a_rule get_perm_iter $::ApolTop::qpolicy]
+ set a_perms [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+ set i [$b_rule get_perm_iter $::ApolTop::qpolicy]
+ set b_perms [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+
+ if {[lsearch $a_perms "relabelto"] >= 0 && \
+ [lsearch $a_perms "relabelfrom"] >= 0 && \
+ [lsearch $b_perms "relabelto"] >= 0 && \
+ [lsearch $b_perms "relabelfrom"] >= 0} {
+ set dir_string "to and from"
+ } elseif {[lsearch $a_perms "relabelto"] >= 0 &&
+ [lsearch $b_perms "relabelfrom"] >= 0} {
+ set dir_string "to"
+ } else {
+ set dir_string "from"
+ }
+ }
+
+ $res.tb configure -state normal
+ $res.tb insert end "\n $dir_string " num \
+ $target_type type_tag \
+ " by " {} \
+ $intermed type_tag \
+ "\n" {}
+
+ set v [new_apol_vector_t]
+ $v append $a_rule
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ if {$a_rule != $b_rule} {
+ set v [new_apol_vector_t]
+ $v append $b_rule
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+ }
+ }
+}
+
+proc Apol_Analysis_relabel::_renderResultsSubject {results tree} {
+ variable vals
+ set to_count 0
+ set from_count 0
+
+ foreach r [relabel_result_vector_to_list $results] {
+ set type [[$r get_result_type] get_name $::ApolTop::qpolicy]
+ set to [relabel_result_pair_vector_to_list [$r get_to]]
+ set from [relabel_result_pair_vector_to_list [$r get_from]]
+ set both [relabel_result_pair_vector_to_list [$r get_both]]
+
+ foreach pair [concat $to $both] {
+ lappend to_types($type) [$pair get_ruleA]
+ }
+ foreach pair [concat $from $both] {
+ lappend from_types($type) [$pair get_ruleA]
+ }
+
+ }
+
+ set to_count [llength [array names to_types]]
+ if {$to_count} {
+ set to_text [list $vals(type) title_type " can relabel to " {} ]
+ lappend to_text $to_count num \
+ " type(s). Open the subtree of this item to view the list of types." {}
+ $tree insert end top to -text "To" -data $to_text -drawcross auto
+ foreach type [lsort [array names to_types]] {
+ set rules [lsort -unique $to_types($type)]
+ $tree insert end to s\#auto -text $type -data [list to $rules]
+ }
+ }
+ set from_count [llength [array names from_types]]
+ if {$from_count} {
+ set from_text [list $vals(type) title_type " can relabel from " {} ]
+ lappend from_text $from_count num \
+ " type(s). Open the subtree of this item to view the list of types." {}
+ $tree insert end top from -text "From" -data $from_text -drawcross auto
+ foreach type [lsort [array names from_types]] {
+ set rules [lsort -unique $from_types($type)]
+ $tree insert end from s\#auto -text $type -data [list from $rules]
+ }
+ }
+
+ set top_text [list "Direct Relabel Analysis: Subject: " title]
+ lappend top_text $vals(type) title_type \
+ "\n\n" title \
+ $vals(type) type_tag
+ if {$to_count + $from_count} {
+ lappend top_text " can relabel to " {} \
+ $to_count num \
+ " type(s) and relabel from " {} \
+ $from_count num \
+ " type(s).\n\n" {} \
+ "This tab provides the results of a Direct Relabel Analysis for the\n" {} \
+ "subject above. The results of the analysis are presented in tree form\n" {} \
+ "with the root of the tree (this node) being the starting point for the\n" {} \
+ "analysis.\n\n" {} \
+ "Each child node in the To and From subtrees represents a type in the\n" {} \
+ "current policy which the chosen subject can relabel." {}
+ } else {
+ lappend top_text " does not relabel to or from any type as a subject." {}
+ }
+}
+
+proc Apol_Analysis_relabel::_renderResultsRuleSubject {res tree node data} {
+ foreach {dir rules} $data {break}
+ set header [list [$tree itemcget top -text] title_type]
+ lappend header " can relabel $dir " {} \
+ [$tree itemcget $node -text] title_type \
+ "\n\n" {}
+ eval $res.tb insert end $header
+ set v [new_apol_vector_t]
+ foreach r $rules {
+ $v append $r
+ }
+ Apol_Widget::appendSearchResultRules $res 0 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+}
diff --git a/apol/roles_tab.tcl b/apol/roles_tab.tcl
new file mode 100644
index 0000000..4d8339a
--- /dev/null
+++ b/apol/roles_tab.tcl
@@ -0,0 +1,196 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Roles {
+ variable widgets
+ variable opts
+ variable role_list {}
+}
+
+proc Apol_Roles::create {tab_name nb} {
+ variable widgets
+ variable opts
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "Roles"]
+ set pw [PanedWindow $frame.pw -side top]
+ set leftf [$pw add -weight 0]
+ set rightf [$pw add -weight 1]
+ pack $pw -fill both -expand yes
+
+ set rolebox [TitleFrame $leftf.rolebox -text "Roles"]
+ set s_optionsbox [TitleFrame $rightf.obox -text "Search Options"]
+ set resultsbox [TitleFrame $rightf.rbox -text "Search Results"]
+ pack $rolebox -fill both -expand yes
+ pack $s_optionsbox -padx 2 -fill both -expand 0
+ pack $resultsbox -padx 2 -fill both -expand yes
+
+ set rlistbox [Apol_Widget::makeScrolledListbox [$rolebox getframe].lb \
+ -width 20 -listvar Apol_Roles::role_list]
+ Apol_Widget::setListboxCallbacks $rlistbox \
+ {{"Display Role Info" {Apol_Roles::_popupRoleInfo role}}}
+ pack $rlistbox -fill both -expand yes
+
+ # Search options subframes
+ set ofm [$s_optionsbox getframe]
+ set lfm [frame $ofm.to]
+ set cfm [frame $ofm.co]
+ pack $lfm $cfm -side left -anchor nw -padx 4 -pady 2
+
+ radiobutton $lfm.all_info -text "All information" \
+ -variable Apol_Roles::opts(showSelection) -value all
+ radiobutton $lfm.names_only -text "Names only" \
+ -variable Apol_Roles::opts(showSelection) -value names
+ pack $lfm.all_info $lfm.names_only -anchor w -padx 5 -pady 4
+
+ set cb_type [checkbutton $cfm.cb -variable Apol_Roles::opts(useType) -text "Type"]
+ set widgets(combo_types) [Apol_Widget::makeTypeCombobox $cfm.combo_types]
+ Apol_Widget::setTypeComboboxState $widgets(combo_types) disabled
+ trace add variable Apol_Roles::opts(useType) write \
+ [list Apol_Roles::_toggleTypeCombobox $widgets(combo_types)]
+ pack $cb_type -anchor w
+ pack $widgets(combo_types) -anchor w -padx 4
+
+ button $ofm.ok -text OK -width 6 -command Apol_Roles::_searchRoles
+ pack $ofm.ok -side top -anchor e -pady 5 -padx 5
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$resultsbox getframe].sw]
+ pack $widgets(results) -expand 1 -fill both
+
+ return $frame
+}
+
+proc Apol_Roles::open {ppath} {
+ set q [new_apol_role_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable role_list [lsort [role_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(combo_types)
+}
+
+proc Apol_Roles::close {} {
+ variable widgets
+ variable opts
+ variable role_list {}
+
+ _initializeVars
+ Apol_Widget::clearTypeCombobox $widgets(combo_types)
+ Apol_Widget::clearSearchResults $widgets(results)
+}
+
+proc Apol_Roles::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+# Return a list of all role names in the current policy. If no policy
+# is loaded then return an empty list.
+proc Apol_Roles::getRoles {} {
+ variable role_list
+ set role_list
+}
+
+#### private functions below ####
+
+proc Apol_Roles::_initializeVars {} {
+ variable opts
+ array set opts {
+ useType 0
+ showSelection all
+ }
+}
+
+proc Apol_Roles::_toggleTypeCombobox {path name1 name2 op} {
+ Apol_Widget::setTypeComboboxState $path $Apol_Roles::opts(useType)
+}
+
+proc Apol_Roles::_popupRoleInfo {which role} {
+ Apol_Widget::showPopupText $role [_renderRole $role 1]
+}
+
+proc Apol_Roles::_searchRoles {} {
+ variable widgets
+ variable opts
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ if {$opts(useType)} {
+ set type [lindex [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(combo_types)] 0]
+ if {$type == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No type selected."
+ return
+ }
+ } else {
+ set type {}
+ }
+ if {$opts(showSelection) == "names"} {
+ set show_all 0
+ } else {
+ set show_all 1
+ }
+
+ set q [new_apol_role_query_t]
+ $q set_type $::ApolTop::policy $type
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set roles_data [role_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ set text "ROLES:\n"
+ if {[llength $roles_data] == 0} {
+ append text "Search returned no results."
+ } else {
+ foreach r [lsort $roles_data] {
+ append text "\n[_renderRole $r $show_all]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $text
+}
+
+proc Apol_Roles::_renderRole {role_name show_all} {
+ set qpol_role_datum [new_qpol_role_t $::ApolTop::qpolicy $role_name]
+ if {!$show_all} {
+ return $role_name
+ }
+ set i [$qpol_role_datum get_type_iter $::ApolTop::qpolicy]
+ set types {}
+ while {![$i end]} {
+ set qpol_type_datum [qpol_type_from_void [$i get_item]]
+ lappend types [$qpol_type_datum get_name $::ApolTop::qpolicy]
+ $i next
+ }
+ $i -acquire
+ $i -delete
+ set text "$role_name ([llength $types] type"
+ if {[llength $types] != 1} {
+ append text "s"
+ }
+ append text ")\n"
+ foreach t [lsort -dictionary $types] {
+ append text " $t\n"
+ }
+# append text " dominance: $dominates\n"
+ return $text
+}
diff --git a/apol/terules_tab.tcl b/apol/terules_tab.tcl
new file mode 100644
index 0000000..c5a490f
--- /dev/null
+++ b/apol/terules_tab.tcl
@@ -0,0 +1,1034 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_TE {
+ variable vals
+ variable widgets
+ variable tabs
+ variable enabled
+}
+
+proc Apol_TE::create {tab_name nb} {
+ variable vals
+ variable widgets
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "TE Rules"]
+ set pw [PanedWindow $frame.pw -side left -weights extra]
+ set topf [$pw add -weight 0]
+ set bottomf [$pw add -weight 1]
+ pack $pw -expand 1 -fill both
+
+ # Major SubFrames:
+ # rsbox - rule selection
+ # oobox - other search options
+ # obox - holds search options widgets
+ # rbox - holds display window widgets
+ # abox - action buttons
+ set top_leftf [frame $topf.tl]
+ set widgets(search_opts) [NoteBook $topf.nb]
+ set abox [frame $topf.abox]
+ pack $top_leftf -side left -expand 0 -fill y
+ pack $widgets(search_opts) -side left -expand 1 -fill both -padx 10
+ pack $abox -side right -fill y -padx 5
+ set rsbox [TitleFrame $top_leftf.rsbox -text "Rule Selection"]
+ set oobox [TitleFrame $top_leftf.oobox -text "Search Options"]
+ set rbox [TitleFrame $bottomf.rbox -text "Type Enforcement Rules Display"]
+ pack $rsbox -side top -expand 0 -fill both
+ pack $oobox -side top -expand 1 -fill both -pady 2
+ pack $rbox -expand yes -fill both -padx 2
+
+ # Rule selection subframe
+ set fm_rules [$rsbox getframe]
+ set allow [checkbutton $fm_rules.allow -text "allow" \
+ -onvalue $::QPOL_RULE_ALLOW -offvalue 0 \
+ -variable Apol_TE::vals(rs:avrule_allow)]
+ set neverallow [checkbutton $fm_rules.neverallow -text "neverallow" \
+ -onvalue $::QPOL_RULE_NEVERALLOW -offvalue 0 \
+ -variable Apol_TE::vals(rs:avrule_neverallow)]
+ set auditallow [checkbutton $fm_rules.auditallow -text "auditallow" \
+ -onvalue $::QPOL_RULE_AUDITALLOW -offvalue 0 \
+ -variable Apol_TE::vals(rs:avrule_auditallow)]
+ set dontaudit [checkbutton $fm_rules.dontaudit -text "dontaudit" \
+ -onvalue $::QPOL_RULE_DONTAUDIT -offvalue 0 \
+ -variable Apol_TE::vals(rs:avrule_dontaudit)]
+ set type_transition [checkbutton $fm_rules.type_transition -text "type_trans" \
+ -onvalue $::QPOL_RULE_TYPE_TRANS -offvalue 0 \
+ -variable Apol_TE::vals(rs:type_transition)]
+ set type_member [checkbutton $fm_rules.type_member -text "type_member" \
+ -onvalue $::QPOL_RULE_TYPE_MEMBER -offvalue 0 \
+ -variable Apol_TE::vals(rs:type_member)]
+ set type_change [checkbutton $fm_rules.type_change -text "type_change" \
+ -onvalue $::QPOL_RULE_TYPE_CHANGE -offvalue 0 \
+ -variable Apol_TE::vals(rs:type_change)]
+ grid $allow $type_transition -sticky w -padx 2
+ grid $auditallow $type_member -sticky w -padx 2
+ grid $dontaudit $type_change -sticky w -padx 2
+ grid $neverallow x -sticky w -padx 2
+ foreach x {allow neverallow auditallow dontaudit type_transition type_member type_change} {
+ trace add variable Apol_TE::vals(rs:$x) write \
+ [list Apol_TE::_toggle_rule_selection]
+ }
+
+ # Other search options subframe
+ set fm_options [$oobox getframe]
+ set enabled [checkbutton $fm_options.enabled -text "Search only enabled rules" \
+ -variable Apol_TE::vals(oo:enabled)]
+ set regexp [checkbutton $fm_options.regex -text "Search using regular expression" \
+ -variable Apol_TE::vals(oo:regexp)]
+ pack $enabled $regexp -expand 0 -fill none -anchor w
+
+ _createTypesAttribsTab
+ _createClassesPermsTab
+
+ # Action buttons
+ set widgets(new) [button $abox.new -text "New Search" -width 12 \
+ -command [list Apol_TE::_search_terules new]]
+ set widgets(update) [button $abox.update -text "Update Search" -width 12 -state disabled \
+ -command [list Apol_TE::_search_terules update]]
+ set widgets(reset) [button $abox.reset -text "Reset Criteria" -width 12 \
+ -command Apol_TE::_reset]
+ pack $widgets(new) $widgets(update) $widgets(reset) \
+ -side top -pady 5 -padx 5 -anchor ne
+
+ $widgets(search_opts) compute_size
+
+ # Popup menu widget
+ set popupTab_Menu [menu .popup_terules -tearoff 0]
+ set tab_menu_callbacks \
+ [list {"Close Tab" Apol_TE::_delete_results} \
+ {"Rename Tab" Apol_TE::_display_rename_tab_dialog}]
+
+ # Notebook creation for results
+ set widgets(results) [NoteBook [$rbox getframe].results]
+ $widgets(results) bindtabs <Button-1> Apol_TE::_switch_to_tab
+ $widgets(results) bindtabs <Button-3> \
+ [list ApolTop::popup \
+ %W %x %y $popupTab_Menu $tab_menu_callbacks]
+ set close [button [$rbox getframe].close -text "Close Tab" \
+ -command Apol_TE::_delete_current_results]
+ pack $widgets(results) -expand 1 -fill both -padx 4
+ pack $close -expand 0 -fill x -padx 4 -pady 2
+
+ _initializeVars
+
+ return $frame
+}
+
+proc Apol_TE::open {ppath} {
+ _initializeVars
+ _initializeWidgets
+ _initializeTabs
+
+ variable vals
+ variable enabled
+ set vals(cp:classes) [Apol_Class_Perms::getClasses]
+ set enabled(cp:classes) 1
+ set enabled(cp:perms) 1
+}
+
+proc Apol_TE::close {} {
+ _initializeTabs
+ _initializeWidgets
+ _initializeVars
+ set enabled(cp:perms) 1
+}
+
+proc Apol_TE::getTextWidget {} {
+ variable widgets
+ variable tabs
+ if {[$widgets(results) pages] != {}} {
+ set raisedPage [$widgets(results) raise]
+ if {$raisedPage != {}} {
+ return $tabs($raisedPage).tb
+ }
+ }
+ return {}
+}
+
+proc Apol_TE::save_query_options {file_channel query_file} {
+ variable vals
+ foreach {key value} [array get vals] {
+ if {$key != "cp:classes" && $key != "cp:perms"} {
+ puts $file_channel "$key $value"
+ }
+ }
+}
+
+proc Apol_TE::load_query_options {file_channel} {
+ variable vals
+ variable widgets
+ variable enabled
+ _initializeVars
+
+ # load as many values as possible
+ set classes_selected {}
+ set perms_selected {}
+ while {[gets $file_channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ if {$key == "cp:classes_selected"} {
+ set classes_selected $value
+ } elseif {$key == "cp:perms_selected"} {
+ set perms_selected $value
+ } else {
+ set vals($key) $value
+ }
+ }
+
+ # update the display
+ _initializeWidgets
+ set vals(cp:classes) [Apol_Class_Perms::getClasses]
+ set enabled(cp:classes) 1
+ set enabled(cp:perms) 1
+ _toggle_perms_toshow -> -> reset
+
+ # then verify that selected object classes and permissions exist
+ # for this policy
+
+ set unknowns {}
+ set vals(cp:classes_selected) {}
+ foreach class $classes_selected {
+ if {[set i [lsearch $vals(cp:classes) $class]] >= 0} {
+ $widgets(cp:classes) selection set $i
+ lappend vals(cp:classes_selected) $class
+ } else {
+ lappend unknowns $class
+ }
+ }
+ if {[llength $unknowns] > 0} {
+ tk_messageBox -icon warning -type ok -title "Open Apol Query" \
+ -message "The following object classes do not exist in the currently loaded policy and were ignored:\n\n[join $unknowns ", "]" \
+ -parent .
+ }
+
+ _toggle_perms_toshow {} {} {}
+ set unknowns {}
+ set vals(cp:perms_selected) {}
+ foreach perm $perms_selected {
+ if {[set i [lsearch $vals(cp:perms) $perm]] >= 0} {
+ $widgets(cp:perms) selection set $i
+ lappend vals(cp:perms_selected) $perm
+ } else {
+ lappend unknowns $perm
+ }
+ }
+ if {[llength $unknowns] > 0} {
+ tk_messageBox -icon warning -type ok -title "Open Apol Query" \
+ -message "The following permissions do not exist in the currently loaded policy and were ignored:\n\n[join $unknowns ", "]" \
+ -parent $parentDlg
+ }
+}
+
+#### private functions below ####
+
+proc Apol_TE::_initializeVars {} {
+ variable vals
+ array set vals [list \
+ rs:avrule_allow $::QPOL_RULE_ALLOW \
+ rs:avrule_neverallow 0 \
+ rs:avrule_auditallow $::QPOL_RULE_AUDITALLOW \
+ rs:avrule_dontaudit $::QPOL_RULE_DONTAUDIT \
+ rs:type_transition $::QPOL_RULE_TYPE_TRANS \
+ rs:type_member $::QPOL_RULE_TYPE_MEMBER \
+ rs:type_change $::QPOL_RULE_TYPE_CHANGE \
+ ta:source_sym,types $::APOL_QUERY_SYMBOL_IS_TYPE \
+ ta:target_sym,types $::APOL_QUERY_SYMBOL_IS_TYPE \
+ ta:default_sym,types $::APOL_QUERY_SYMBOL_IS_TYPE \
+ ]
+
+ array set vals {
+ oo:enabled 0
+ oo:regexp 0
+
+ ta:use_source 0
+ ta:source_indirect 1
+ ta:source_which source
+ ta:source_sym,attribs 0
+ ta:source_sym {}
+
+ ta:use_target 0
+ ta:target_indirect 1
+ ta:target_sym,attribs 0
+ ta:target_sym {}
+
+ ta:use_default 0
+ ta:default_sym,attribs 0
+ ta:default_sym {}
+
+ cp:classes {}
+ cp:classes_selected {}
+ cp:perms {}
+ cp:perms_selected {}
+ cp:perms_toshow all
+ cp:perms_matchall 0
+ }
+
+ variable enabled
+ array set enabled {
+ ta:use_source 1
+ ta:use_target 1
+ ta:use_default 1
+
+ cp:classes 0
+ cp:perms 0
+ }
+}
+
+proc Apol_TE::_initializeTabs {} {
+ variable widgets
+ variable tabs
+ array set tabs {
+ next_result_id 1
+ }
+ foreach p [$widgets(results) pages 0 end] {
+ _delete_results $p
+ }
+}
+
+proc Apol_TE::_initializeWidgets {} {
+ variable widgets
+ $widgets(search_opts) raise typeattrib
+
+ $widgets(cp:classes) selection clear 0 end
+ $widgets(cp:perms) selection clear 0 end
+}
+
+proc Apol_TE::_createTypesAttribsTab {} {
+ variable vals
+ variable widgets
+ variable enabled
+
+ set ta_tab [$widgets(search_opts) insert end typeattrib -text "Types/Attributes"]
+ set fm_source [frame $ta_tab.source]
+ set fm_target [frame $ta_tab.target]
+ set fm_default [frame $ta_tab.default]
+ grid $fm_source $fm_target $fm_default -padx 4 -sticky ewns
+ foreach i {0 1 2} {
+ grid columnconfigure $ta_tab $i -weight 1 -uniform 1
+ }
+ grid rowconfigure $ta_tab 0 -weight 1
+
+ _create_ta_box source $fm_source "Source type/attribute" 1 1 1
+ _create_ta_box target $fm_target "Target type/attribute" 1 0 1
+ _create_ta_box default $fm_default "Default type" 0 0 0
+
+ $widgets(search_opts) raise typeattrib
+}
+
+proc Apol_TE::_create_ta_box {prefix f title has_indirect has_which has_attribs} {
+ variable vals
+ variable widgets
+
+ set widgets(ta:use_${prefix}) [checkbutton $f.use -text $title \
+ -variable Apol_TE::vals(ta:use_${prefix})]
+ pack $widgets(ta:use_${prefix}) -side top -anchor w
+ trace add variable Apol_TE::vals(ta:use_${prefix}) write \
+ [list Apol_TE::_toggle_ta_box $prefix]
+
+ set w {}
+
+ if {$has_attribs} {
+ set helptext "Type or select a type or attribute"
+ } else {
+ set helptext "Type or select a type"
+ }
+ set widgets(ta:${prefix}_sym) [ComboBox $f.sym \
+ -state disabled -entrybg $ApolTop::default_bg_color \
+ -textvariable Apol_TE::vals(ta:${prefix}_sym) \
+ -helptext $helptext -autopost 1]
+ pack $widgets(ta:${prefix}_sym) -expand 0 -fill x -padx 8
+ lappend w $widgets(ta:${prefix}_sym)
+
+ if {$has_attribs} {
+ set ta_frame [frame $f.ta]
+ pack $ta_frame -expand 0 -anchor center -pady 2
+ set types [checkbutton $ta_frame.types -text "Types" -state disabled \
+ -onvalue $::APOL_QUERY_SYMBOL_IS_TYPE -offvalue 0 \
+ -variable Apol_TE::vals(ta:${prefix}_sym,types)]
+ set attribs [checkbutton $ta_frame.attribs -text "Attribs" -state disabled \
+ -onvalue $::APOL_QUERY_SYMBOL_IS_ATTRIBUTE -offvalue 0 \
+ -variable Apol_TE::vals(ta:${prefix}_sym,attribs)]
+ $types configure -command [list Apol_TE::_toggle_ta_pushed $prefix $types]
+ $attribs configure -command [list Apol_TE::_toggle_ta_pushed $prefix $attribs]
+ trace add variable Apol_TE::vals(ta:${prefix}_sym,attribs) write \
+ [list Apol_TE::_toggle_ta_sym $prefix]
+ pack $types $attribs -side left -padx 2
+ lappend w $types $attribs
+ }
+
+ if {$has_indirect} {
+ set indirect [checkbutton $f.indirect -text "Only direct matches" \
+ -state disabled -variable Apol_TE::vals(ta:${prefix}_indirect) \
+ -onvalue 0 -offvalue 1]
+ pack $indirect -anchor w -padx 8
+ lappend w $indirect
+ }
+
+ if {$has_which} {
+ set which_fm [frame $f.which]
+ set which_source [radiobutton $which_fm.source \
+ -text "As source" -state disabled \
+ -variable Apol_TE::vals(ta:${prefix}_which) \
+ -value source]
+ set which_any [radiobutton $which_fm.any \
+ -text "Any" -state disabled \
+ -variable Apol_TE::vals(ta:${prefix}_which) \
+ -value either]
+ trace add variable Apol_TE::vals(ta:${prefix}_which) write \
+ [list Apol_TE::_toggle_which]
+ pack $which_source $which_any -side left -padx 2
+ pack $which_fm -anchor w -padx 6
+ lappend w $which_source $which_any
+ }
+
+ # update contents of the combobox whenever the 'Types' checkbutton
+ # is enabled; for default type, this is invoked by a call to
+ # reinitialize_default_search_options
+ trace add variable Apol_TE::vals(ta:${prefix}_sym,types) write \
+ [list Apol_TE::_toggle_ta_sym $prefix]
+
+ set widgets(ta:${prefix}_widgets) $w
+ trace add variable Apol_TE::enabled(ta:use_${prefix}) write \
+ [list Apol_TE::_toggle_enable_ta ${prefix}]
+}
+
+proc Apol_TE::_toggle_rule_selection {name1 name2 op} {
+ _maybe_enable_default_type
+ _maybe_enable_perms
+}
+
+
+## lots of obscure callbacks here to determine when to enable a ta box ##
+
+
+# called when there is a change in state to the top checkbutton within
+# a ta box
+proc Apol_TE::_toggle_ta_box {col name1 name2 op} {
+ variable vals
+ variable enabled
+ if {$col == "source"} {
+ _maybe_enable_target_type
+ _maybe_enable_default_type
+ }
+
+ # force a refresh of this box's state; this invokes
+ # _toggle_enable_ta callback
+ set enabled(ta:use_${col}) $enabled(ta:use_${col})
+}
+
+# disable target type/attrib and default type if source box is marked as "any"
+proc Apol_TE::_toggle_which {name1 name2 op} {
+ _maybe_enable_target_type
+ _maybe_enable_default_type
+}
+
+proc Apol_TE::_maybe_enable_target_type {} {
+ variable vals
+ variable enabled
+
+ set any_set 0
+ if {$enabled(ta:use_source) && $vals(ta:use_source) && $vals(ta:source_which) == "either"} {
+ set any_set 1
+ }
+ if {!$any_set} {
+ set enabled(ta:use_target) 1
+ } else {
+ set enabled(ta:use_target) 0
+ }
+}
+
+proc Apol_TE::_maybe_enable_default_type {} {
+ variable vals
+ variable enabled
+
+ set typerule_set 0
+ set any_set 0
+ foreach x {type_transition type_member type_change} {
+ if {$vals(rs:$x)} {
+ set typerule_set 1
+ break
+ }
+ }
+ if {$enabled(ta:use_source) && $vals(ta:use_source) && $vals(ta:source_which) == "either"} {
+ set any_set 1
+ }
+ if {$typerule_set && !$any_set} {
+ set enabled(ta:use_default) 1
+ } else {
+ set enabled(ta:use_default) 0
+ }
+}
+
+# called whenever a ta box is enabled or disabled
+proc Apol_TE::_toggle_enable_ta {col name1 name2 op} {
+ variable vals
+ variable widgets
+ variable enabled
+ if {$enabled(ta:use_${col})} {
+ $widgets(ta:use_${col}) configure -state normal
+ } else {
+ $widgets(ta:use_${col}) configure -state disabled
+ }
+ if {$enabled(ta:use_${col}) && $vals(ta:use_${col})} {
+ foreach w $widgets(ta:${col}_widgets) {
+ $w configure -state normal
+ }
+ $widgets(ta:${col}_sym) configure -entrybg white
+ } else {
+ foreach w $widgets(ta:${col}_widgets) {
+ $w configure -state disabled
+ }
+ $widgets(ta:${col}_sym) configure -entrybg $ApolTop::default_bg_color
+ }
+
+ # update this tab's name if one of the columns is enabled and used
+ if {($enabled(ta:use_source) && $vals(ta:use_source)) || \
+ ($enabled(ta:use_target) && $vals(ta:use_target)) || \
+ ($enabled(ta:use_default) && $vals(ta:use_default))} {
+ $widgets(search_opts) itemconfigure typeattrib -text "Types/Attributes *"
+ } else {
+ $widgets(search_opts) itemconfigure typeattrib -text "Types/Attributes"
+ }
+}
+
+proc Apol_TE::_toggle_ta_sym {col name1 name2 op} {
+ variable vals
+ variable widgets
+
+ if {!$vals(ta:${col}_sym,types) && !$vals(ta:${col}_sym,attribs)} {
+ # don't change combobox if both types and attribs are deselected
+ return
+ }
+ if {$vals(ta:${col}_sym,types) && $vals(ta:${col}_sym,attribs)} {
+ set items [lsort [concat [Apol_Types::getTypes] [Apol_Types::getAttributes]]]
+ } elseif {$vals(ta:${col}_sym,types)} {
+ set items [Apol_Types::getTypes]
+ } else {
+ set items [Apol_Types::getAttributes]
+ }
+ $widgets(ta:${col}_sym) configure -values $items
+}
+
+# disallow both types and attribs to be deselected within a ta box
+proc Apol_TE::_toggle_ta_pushed {col cb} {
+ variable vals
+ if {!$vals(ta:${col}_sym,types) && !$vals(ta:${col}_sym,attribs)} {
+ $cb select
+ }
+}
+
+# code to create and handle the classe/permissions subtab
+
+proc Apol_TE::_createClassesPermsTab {} {
+ variable vals
+ variable widgets
+ variable enabled
+
+ set objects_tab [$widgets(search_opts) insert end classperms -text "Classes/Permissions"]
+ set fm_objs [TitleFrame $objects_tab.objs -text "Object Classes"]
+ set fm_perms [TitleFrame $objects_tab.perms -text "AV Rule Permissions"]
+ pack $fm_objs -side left -expand 0 -fill both -padx 2 -pady 2
+ pack $fm_perms -side left -expand 1 -fill both -padx 2 -pady 2
+
+ # object classes subframe
+ set sw [ScrolledWindow [$fm_objs getframe].sw -auto both]
+ set widgets(cp:classes) [listbox [$sw getframe].lb -height 5 -width 24 \
+ -highlightthickness 0 -selectmode multiple \
+ -exportselection 0 -state disabled \
+ -bg $ApolTop::default_bg_color \
+ -listvar Apol_TE::vals(cp:classes)]
+ $sw setwidget $widgets(cp:classes)
+ update
+ grid propagate $sw 0
+ bind $widgets(cp:classes) <<ListboxSelect>> \
+ [list Apol_TE::_toggle_cp_select classes]
+ pack $sw -expand 1 -fill both
+ set clear [button [$fm_objs getframe].b -text "Clear" -width 6 -state disabled \
+ -command [list Apol_TE::_clear_cp_listbox $widgets(cp:classes) classes]]
+ pack $clear -expand 0 -pady 2
+ set widgets(cp:classes_widgets) [list $widgets(cp:classes) $clear]
+
+ # permissions subframe
+ set f [$fm_perms getframe]
+ set sw [ScrolledWindow $f.sw -auto both]
+ set widgets(cp:perms) [listbox [$sw getframe].lb -height 5 -width 24 \
+ -highlightthickness 0 -selectmode multiple \
+ -exportselection 0 -bg white \
+ -listvar Apol_TE::vals(cp:perms)]
+ $sw setwidget $widgets(cp:perms)
+ update
+ grid propagate $sw 0
+ bind $widgets(cp:perms) <<ListboxSelect>> \
+ [list Apol_TE::_toggle_cp_select perms]
+ set clear [button $f.clear -text "Clear" \
+ -command [list Apol_TE::_clear_cp_listbox $widgets(cp:perms) perms]]
+ set reverse [button $f.reverse -text "Reverse" \
+ -command [list Apol_TE::_reverse_cp_listbox $widgets(cp:perms)]]
+ set perm_opts_f [frame $f.perms]
+ set perm_rb_f [frame $perm_opts_f.rb]
+ set l [label $perm_rb_f.l -text "Permissions to show:" -state disabled]
+ set all [radiobutton $perm_rb_f.all -text "All" \
+ -variable Apol_TE::vals(cp:perms_toshow) -value all]
+ set union [radiobutton $perm_rb_f.union -text "All for selected classes" \
+ -variable Apol_TE::vals(cp:perms_toshow) -value union]
+ set intersect [radiobutton $perm_rb_f.inter -text "Common to selected classes" \
+ -variable Apol_TE::vals(cp:perms_toshow) -value intersect]
+ trace add variable Apol_TE::vals(cp:perms_toshow) write \
+ Apol_TE::_toggle_perms_toshow
+ pack $l $all $union $intersect -anchor w
+ set all_perms [checkbutton $perm_opts_f.all -text "AV rule must have all selected permissions" \
+ -variable Apol_TE::vals(cp:perms_matchall)]
+ pack $perm_rb_f $all_perms -anchor w -pady 4 -padx 4
+ grid $sw - $perm_opts_f -sticky nsw
+ grid $clear $reverse ^ -pady 2 -sticky ew
+ grid columnconfigure $f 0 -weight 0 -uniform 1 -pad 2
+ grid columnconfigure $f 1 -weight 0 -uniform 1 -pad 2
+ grid columnconfigure $f 2 -weight 1
+ grid rowconfigure $f 0 -weight 1
+ set widgets(cp:perms_widgets) \
+ [list $widgets(cp:perms) $clear $reverse $l $all $union $intersect $all_perms]
+
+ trace add variable Apol_TE::vals(cp:classes_selected) write \
+ [list Apol_TE::_update_cp_tabname]
+ trace add variable Apol_TE::vals(cp:perms_selected) write \
+ [list Apol_TE::_update_cp_tabname]
+ trace add variable Apol_TE::enabled(cp:classes) write \
+ [list Apol_TE::_toggle_enable_cp classes]
+ trace add variable Apol_TE::enabled(cp:perms) write \
+ [list Apol_TE::_toggle_enable_cp perms]
+}
+
+proc Apol_TE::_toggle_enable_cp {prefix name1 name2 op} {
+ variable vals
+ variable widgets
+ variable enabled
+ if {$enabled(cp:${prefix})} {
+ foreach w $widgets(cp:${prefix}_widgets) {
+ $w configure -state normal
+ }
+ $widgets(cp:${prefix}) configure -bg white
+ } else {
+ foreach w $widgets(cp:${prefix}_widgets) {
+ $w configure -state disabled
+ }
+ $widgets(cp:${prefix}) configure -bg $ApolTop::default_bg_color
+ }
+ # force a refresh of this tab's name
+ set vals(cp:${prefix}_selected) $vals(cp:${prefix}_selected)
+}
+
+proc Apol_TE::_maybe_enable_perms {} {
+ variable vals
+ variable enabled
+
+ set avrule_set 0
+ foreach x {avrule_allow avrule_neverallow avrule_auditallow avrule_dontaudit} {
+ if {$vals(rs:$x)} {
+ set avrule_set 1
+ break
+ }
+ }
+ if {$avrule_set} {
+ set enabled(cp:perms) 1
+ } else {
+ set enabled(cp:perms) 0
+ }
+}
+
+proc Apol_TE::_toggle_perms_toshow {name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals(cp:perms_toshow) == "all"} {
+ # don't change the list of permissions if there was a new
+ # object class selection and the current radiobutton is all
+ if {$op != "update"} {
+ set vals(cp:perms) $Apol_Class_Perms::perms_list
+ set vals(cp:perms_selected) {}
+ }
+ } elseif {$vals(cp:perms_toshow) == "union"} {
+ set vals(cp:perms) {}
+ set vals(cp:perms_selected) {}
+ foreach class $vals(cp:classes_selected) {
+ set vals(cp:perms) [lsort -unique -dictionary [concat $vals(cp:perms) [Apol_Class_Perms::getPermsForClass $class]]]
+ }
+ } else { ;# intersection
+ set vals(cp:perms) {}
+ set vals(cp:perms_selected) {}
+ set classes {}
+ foreach i [$widgets(cp:classes) curselection] {
+ lappend classes [$widgets(cp:classes) get $i]
+ }
+ if {$classes == {}} {
+ return
+ }
+ set vals(cp:perms) [Apol_Class_Perms::getPermsForClass [lindex $classes 0]]
+ foreach class [lrange $classes 1 end] {
+ set this_perms [Apol_Class_Perms::getPermsForClass $class]
+ set new_perms {}
+ foreach p $vals(cp:perms) {
+ if {[lsearch -exact $this_perms $p] >= 0} {
+ lappend new_perms $p
+ }
+ }
+ set vals(cp:perms) $new_perms
+ }
+ }
+}
+
+# called whenever an item with a class/perm listbox is
+# selected/deselected
+proc Apol_TE::_toggle_cp_select {col} {
+ variable vals
+ variable widgets
+ set items {}
+ foreach i [$widgets(cp:${col}) curselection] {
+ lappend items [$widgets(cp:${col}) get $i]
+ }
+ set vals(cp:${col}_selected) $items
+ if {$col == "classes"} {
+ _toggle_perms_toshow {} {} update
+ }
+}
+
+proc Apol_TE::_clear_cp_listbox {lb prefix} {
+ variable vals
+ $lb selection clear 0 end
+ set vals(cp:${prefix}_selected) {}
+ if {$prefix == "classes"} {
+ _toggle_perms_toshow {} {} update
+ }
+}
+
+proc Apol_TE::_reverse_cp_listbox {lb} {
+ variable vals
+ set old_selection [$lb curselection]
+ set items {}
+ for {set i 0} {$i < [$lb index end]} {incr i} {
+ if {[lsearch $old_selection $i] >= 0} {
+ $lb selection clear $i
+ } else {
+ $lb selection set $i
+ lappend items [$lb get $i]
+ }
+ }
+ set vals(cp:perms_selected) $items
+}
+
+proc Apol_TE::_update_cp_tabname {name1 name2 op} {
+ variable vals
+ variable widgets
+ variable enabled
+ if {($enabled(cp:classes) && $vals(cp:classes_selected) > 0) || \
+ ($enabled(cp:perms) && $vals(cp:perms_selected) > 0)} {
+ $widgets(search_opts) itemconfigure classperms -text "Classes/Permissions *"
+ } else {
+ $widgets(search_opts) itemconfigure classperms -text "Classes/Permissions"
+ }
+}
+
+proc Apol_TE::_delete_results {pageID} {
+ variable widgets
+ variable tabs
+
+ # Remove tab and its widgets
+ set curpos [$widgets(results) index $pageID]
+ $widgets(results) delete $pageID
+ array unset tabs $pageID:*
+ array unset tabs $pageID
+
+ # try to raise the next tab
+ if {[set next_id [$widgets(results) pages $curpos]] != {}} {
+ _switch_to_tab $next_id
+ } elseif {$curpos > 0} {
+ # raise the previous page instead
+ _switch_to_tab [$widgets(results) pages [expr {$curpos - 1}]]
+ } else {
+ # no tabs remaining
+ $widgets(update) configure -state disabled
+ }
+}
+
+proc Apol_TE::_display_rename_tab_dialog {pageID} {
+ variable widgets
+ variable tabs
+ set d [Dialog .apol_te_tab_rename -homogeneous 1 -spacing 2 -cancel 1 \
+ -default 0 -modal local -parent . -place center -separator 1 \
+ -side bottom -title "Rename Results Tab"]
+ $d add -text "OK" -command [list $d enddialog "ok"]
+ $d add -text "Cancel" -command [list $d enddialog "cancel"]
+ set f [$d getframe]
+ set l [label $f.l -text "Tab name:"]
+ set tabs(tab:new_name) [$widgets(results) itemcget $pageID -text]
+ set e [entry $f.e -textvariable Apol_TE::tabs(tab:new_name) -width 16 -bg white]
+ pack $l $e -side left -padx 2
+ set retval [$d draw]
+ destroy $d
+ if {$retval == "ok"} {
+ $widgets(results) itemconfigure $pageID -text $tabs(tab:new_name)
+ }
+}
+
+proc Apol_TE::_delete_current_results {} {
+ variable widgets
+ if {[set curid [$widgets(results) raise]] != {}} {
+ _delete_results $curid
+ }
+}
+
+proc Apol_TE::_create_new_results_tab {} {
+ variable vals
+ variable widgets
+ variable tabs
+
+ set i $tabs(next_result_id)
+ incr tabs(next_result_id)
+ set id "results$i"
+ set frame [$widgets(results) insert end "$id" -text "Results $i"]
+ $widgets(results) raise $id
+ set tabs($id) [Apol_Widget::makeSearchResults $frame.results]
+ pack $tabs($id) -expand 1 -fill both
+
+ set tabs($id:vals) [array get vals]
+ return $tabs($id)
+}
+
+proc Apol_TE::_switch_to_tab {pageID} {
+ variable vals
+ variable widgets
+ variable tabs
+
+ # check if switching to already visible tab
+ if {[$Apol_TE::widgets(results) raise] == $pageID} {
+ return
+ }
+ $widgets(results) raise $pageID
+ set cur_search_opts [$widgets(search_opts) raise]
+
+ # restore the tab's search criteria
+ array set tmp_vals $tabs($pageID:vals)
+ set classes_selected $tmp_vals(cp:classes_selected)
+ set perms_selected $tmp_vals(cp:perms_selected)
+ array set vals $tabs($pageID:vals)
+ _initializeWidgets
+ set vals(cp:classes_selected) $classes_selected
+ set vals(cp:perms_selected) $perms_selected
+ foreach c $classes_selected {
+ $widgets(cp:classes) selection set [lsearch $vals(cp:classes) $c]
+ }
+ foreach p $perms_selected {
+ $widgets(cp:perms) selection set [lsearch $vals(cp:perms) $p]
+ }
+ $widgets(search_opts) raise $cur_search_opts
+}
+
+########################################################################
+
+proc Apol_TE::_reset {} {
+ variable enabled
+ set old_classes_enabled $enabled(cp:classes)
+ _initializeVars
+ _initializeWidgets
+ if {[set enabled(cp:classes) $old_classes_enabled]} {
+ variable vals
+ set vals(cp:classes) [Apol_Class_Perms::getClasses]
+ set enabled(cp:classes) 1
+ set enabled(cp:perms) 1
+ }
+}
+
+proc Apol_TE::_search_terules {whichButton} {
+ variable vals
+ variable widgets
+ variable enabled
+ variable tabs
+
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "TE Rule Search" -message "No current policy file is opened."
+ return
+ }
+
+ # check search options
+ if {$enabled(ta:use_source) && $vals(ta:use_source) && $vals(ta:source_sym) == {}} {
+ tk_messageBox -icon error -type ok -title "TE Rule Search" -message "No source type/attribute was selected."
+ return
+ }
+ if {$enabled(ta:use_target) && $vals(ta:use_target) && $vals(ta:target_sym) == {}} {
+ tk_messageBox -icon error -type ok -title "TE Rule Search" -message "No target type/attribute was selected."
+ return
+ }
+ if {$enabled(ta:use_default) && $vals(ta:use_default) && $vals(ta:default_sym) == {}} {
+
+ tk_messageBox -icon error -type ok -title "TE Rule Search" -message "No default type selected."
+ return
+ }
+
+ set avrule_selection 0
+ foreach {key value} [array get vals rs:avrule_*] {
+ set avrule_selection [expr {$avrule_selection | $value}]
+ }
+ set terule_selection 0
+ foreach {key value} [array get vals rs:type_*] {
+ set terule_selection [expr {$terule_selection | $value}]
+ }
+ if {$avrule_selection == 0 && $terule_selection == 0} {
+ tk_messageBox -icon error -type ok -title "TE Rule Search" -message "At least one rule must be selected."
+ return
+ }
+
+ # start building queries
+ set avq [new_apol_avrule_query_t]
+ set teq [new_apol_terule_query_t]
+
+ if {$enabled(ta:use_source) && $vals(ta:use_source)} {
+ if {$vals(ta:source_which) == "either"} {
+ $avq set_source_any $::ApolTop::policy 1
+ }
+ $avq set_source $::ApolTop::policy $vals(ta:source_sym) $vals(ta:source_indirect)
+ $avq set_source_component $::ApolTop::policy [expr {$vals(ta:source_sym,types) | $vals(ta:source_sym,attribs)}]
+ $teq set_source $::ApolTop::policy $vals(ta:source_sym) $vals(ta:source_indirect)
+ $teq set_source_component $::ApolTop::policy [expr {$vals(ta:source_sym,types) | $vals(ta:source_sym,attribs)}]
+ }
+ if {$enabled(ta:use_target) && $vals(ta:use_target)} {
+ $avq set_target $::ApolTop::policy $vals(ta:target_sym) $vals(ta:target_indirect)
+ $avq set_target_component $::ApolTop::policy [expr {$vals(ta:target_sym,types) | $vals(ta:target_sym,attribs)}]
+ $teq set_target $::ApolTop::policy $vals(ta:target_sym) $vals(ta:target_indirect)
+ $teq set_target_component $::ApolTop::policy [expr {$vals(ta:target_sym,types) | $vals(ta:target_sym,attribs)}]
+ }
+ if {$enabled(ta:use_default) && $vals(ta:use_default)} {
+ $teq set_default $::ApolTop::policy $vals(ta:default_sym)
+ }
+
+ if {$enabled(cp:classes)} {
+ foreach c $vals(cp:classes_selected) {
+ $avq append_class $::ApolTop::policy $c
+ $teq append_class $::ApolTop::policy $c
+ }
+ }
+ if {$enabled(cp:perms)} {
+ foreach p $vals(cp:perms_selected) {
+ $avq append_perm $::ApolTop::policy $p
+ }
+ $avq set_all_perms $::ApolTop::policy $vals(cp:perms_matchall)
+ }
+
+ $avq set_rules $::ApolTop::policy $avrule_selection
+ $teq set_rules $::ApolTop::policy $terule_selection
+ $avq set_enabled $::ApolTop::policy $vals(oo:enabled)
+ $teq set_enabled $::ApolTop::policy $vals(oo:enabled)
+ $avq set_regex $::ApolTop::policy $vals(oo:regexp)
+ $teq set_regex $::ApolTop::policy $vals(oo:regexp)
+
+ foreach x {new update reset} {
+ $widgets($x) configure -state disabled
+ }
+
+ if {$vals(rs:avrule_neverallow)} {
+ ApolTop::loadNeverAllows
+ }
+ if {![ApolTop::is_capable "neverallow"]} {
+ set avrule_selection [expr {$avrule_selection & (~$::QPOL_RULE_NEVERALLOW)}]
+ $avq set_rules $::ApolTop::policy $avrule_selection
+ }
+
+ Apol_Progress_Dialog::wait "TE Rules" "Searching rules" \
+ {
+ set numTEs {0 0 0}
+ set numAVs {0 0 0}
+ set avresults NULL
+ set teresults NULL
+ set num_avresults 0
+ set num_teresults 0
+ if {![ApolTop::is_capable "syntactic rules"]} {
+ if {$avrule_selection != 0} {
+ set avresults [$avq run $::ApolTop::policy]
+ }
+ if {$terule_selection != 0} {
+ set teresults [$teq run $::ApolTop::policy]
+ }
+ } else {
+ $::ApolTop::qpolicy build_syn_rule_table
+ if {$avrule_selection != 0} {
+ set avresults [$avq run_syn $::ApolTop::policy]
+ }
+ if {$terule_selection != 0} {
+ set teresults [$teq run_syn $::ApolTop::policy]
+ }
+ }
+
+ $avq -acquire
+ $avq -delete
+ $teq -acquire
+ $teq -delete
+ if {$avresults != "NULL"} {
+ set num_avresults [$avresults get_size]
+ }
+ if {$teresults != "NULL"} {
+ set num_teresults [$teresults get_size]
+ }
+
+ if {$whichButton == "new"} {
+ set sr [_create_new_results_tab]
+ } else {
+ set id [$widgets(results) raise]
+ set tabs($id:vals) [array get vals]
+ set sr $tabs($id)
+ Apol_Widget::clearSearchResults $sr
+ }
+
+ if {![ApolTop::is_capable "syntactic rules"]} {
+ apol_tcl_set_info_string $::ApolTop::policy "Rendering $num_avresults AV rule results"
+ apol_tcl_terule_sort $::ApolTop::policy $teresults
+ if {$num_avresults > 0} {
+ set numAVs [Apol_Widget::appendSearchResultRules $sr 0 $avresults qpol_avrule_from_void]
+ }
+ apol_tcl_set_info_string $::ApolTop::policy "Rendering $num_teresults TE rule results"
+ apol_tcl_avrule_sort $::ApolTop::policy $avresults
+ if {$num_teresults > 0} {
+ set numTEs [Apol_Widget::appendSearchResultRules $sr 0 $teresults qpol_terule_from_void]
+ }
+ } else {
+ apol_tcl_set_info_string $::ApolTop::policy "Rendering $num_avresults AV rule results"
+ if {$num_avresults > 0} {
+ set numAVs [Apol_Widget::appendSearchResultSynRules $sr 0 $avresults qpol_syn_avrule_from_void]
+ }
+ apol_tcl_set_info_string $::ApolTop::policy "Rendering $num_teresults TE rule results"
+ if {$num_teresults > 0} {
+ set numTEs [Apol_Widget::appendSearchResultSynRules $sr 0 $teresults qpol_syn_terule_from_void]
+ }
+ }
+ set num_rules [expr {[lindex $numAVs 0] + [lindex $numTEs 0]}]
+ set num_enabled [expr {[lindex $numAVs 1] + [lindex $numTEs 1]}]
+ set num_disabled [expr {[lindex $numAVs 2] + [lindex $numTEs 2]}]
+ set header "$num_rules rule"
+ if {$num_rules != 1} {
+ append header s
+ }
+ append header " match the search criteria.\n"
+ append header "Number of enabled conditional rules: $num_enabled\n"
+ append header "Number of disabled conditional rules: $num_disabled\n"
+ Apol_Widget::appendSearchResultHeader $sr $header
+ }
+ $widgets(new) configure -state normal
+ $widgets(reset) configure -state normal
+ if {[$widgets(results) pages] != {} || $retval == 0} {
+ $widgets(update) configure -state normal
+ }
+}
diff --git a/apol/top.tcl b/apol/top.tcl
new file mode 100644
index 0000000..e0f87a3
--- /dev/null
+++ b/apol/top.tcl
@@ -0,0 +1,1228 @@
+# Copyright (C) 2001-2008 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+set COPYRIGHT_INFO "Copyright (C) 2001-2008 Tresys Technology, LLC"
+
+namespace eval ApolTop {
+ variable policy {} ;# handle to an apol_policy, or {} if none opened
+ variable qpolicy {} ;# handle to policy's qpol_policy_t, or {} if none opened
+ # these three are shown on the status line of the toplevel window
+ variable policy_version_string {}
+ variable policy_source_linenum {}
+ variable policy_stats_summary {}
+ variable policy_stats ;# array of statistics for the current policy
+
+ # user's preferences
+ variable dot_apol_file [file join $::env(HOME) .apol]
+ variable recent_files {}
+ variable last_policy_path {}
+ variable max_recent_files 5
+ variable show_fake_attrib_warning 1 ;# warn if using fake attribute names
+
+ # store the default background color for use when diabling widgets
+ variable default_bg_color
+ variable text_font {}
+ variable title_font {}
+ variable dialog_font {}
+ variable general_font {}
+ variable query_file_ext ".qf"
+ # Main window dimension defaults
+ variable mainframe_width 1000
+ variable mainframe_height 700
+
+ # Other global widgets
+ variable mainframe
+ variable notebook
+ variable current_tab
+
+ # The following list describes the layout of apol. All tab names
+ # must be unique and shall not contain colons. For each tab, the
+ # first element gives the identifier; this corresponds with the
+ # namespace. The second element describes the path to get to the
+ # tab, starting from the topmost notebook. For tabs that are to
+ # be topmost, this is just an empty list. The third element is a
+ # list of tags for the tab. Valid tags are:
+ # tag_conditionals - show this only if the policy supports conditionals
+ # tag_mls - show this only if policy supports MLS
+ # tag_query_saveable - if this tab is shown, enable query saving
+ # tag_source - show this only if a source policy is loaded
+ variable tabs {
+ {Apol_Types components {}}
+ {Apol_Class_Perms components {}}
+ {Apol_Roles components {}}
+ {Apol_Users components {}}
+ {Apol_Cond_Bools components {tag_conditionals}}
+ {Apol_MLS components {tag_mls}}
+ {Apol_Initial_SIDS components {}}
+ {Apol_NetContexts components {}}
+ {Apol_FSContexts components {}}
+ {Apol_TE rules {tag_query_saveable}}
+ {Apol_Cond_Rules rules {tag_conditionals}}
+ {Apol_RBAC rules {}}
+ {Apol_Range rules {tag_mls}}
+ {Apol_File_Contexts {} {}}
+ {Apol_Analysis {} {tag_query_saveable}}
+ {Apol_PolicyConf {} {tag_source}}
+ }
+}
+
+#################### public functions ####################
+
+proc ApolTop::is_policy_open {} {
+ if {$::ApolTop::policy == {}} {
+ return 0
+ }
+ return 1
+}
+
+# If a policy is open and it has the given capability then return
+# non-zero. Valid capabilities are:
+# "attribute names"
+# "conditionals"
+# "line numbers"
+# "mls"
+# "neverallow"
+# "source"
+# "syntactic rules"
+proc ApolTop::is_capable {capability} {
+ if {![is_policy_open]} {
+ return 0;
+ }
+ switch -- $capability {
+ "attribute names" { set cap $::QPOL_CAP_ATTRIB_NAMES }
+ "conditionals" { set cap $::QPOL_CAP_CONDITIONALS }
+ "line numbers" { set cap $::QPOL_CAP_LINE_NUMBERS }
+ "mls" { set cap $::QPOL_CAP_MLS }
+ "neverallow" { set cap $::QPOL_CAP_NEVERALLOW }
+ "source" { set cap $::QPOL_CAP_SOURCE }
+ "syntactic rules" { set cap $::QPOL_CAP_SYN_RULES }
+ default { return 0 }
+ }
+ variable qpolicy
+ $qpolicy has_capability $cap
+}
+
+# Open the given policy path. Re-initialize all tabs and add the path
+# to the list of recently opened policies.
+#
+# @param ppath Policy path to open.
+proc ApolTop::openPolicyPath {ppath} {
+ _close_policy
+
+ set primary_file [$ppath get_primary]
+ if {[catch {Apol_Progress_Dialog::wait $primary_file "Opening policy." \
+ {
+ apol_tcl_open_policy $ppath
+ } \
+ } p] || $p == "NULL"} {
+ tk_messageBox -icon error -type ok -title "Open Policy" -parent . \
+ -message "[apol_tcl_get_error_string]"
+ return -1 ;# indicates failed to open policy
+ }
+
+ variable policy $p
+ variable qpolicy [$p get_qpol]
+
+ _toplevel_policy_open $ppath
+
+ _add_recent $ppath
+ variable last_policy_path $ppath
+
+ variable show_fake_attrib_warning
+ if {![is_capable "attribute names"] && \
+ [llength $::Apol_Types::attriblist] > 0 && \
+ $show_fake_attrib_warning} {
+ set d [Dialog .fake_attribute_dialog -modal local -parent . \
+ -title "Open Policy" -separator 1]
+ $d add -text "OK"
+ set f [$d getframe]
+ label $f.l -text "Warning: Apol has generated attribute names because\nthe original names were not preserved in the policy." -justify left
+ checkbutton $f.cb -text "Show this message again next time." \
+ -variable ApolTop::show_fake_attrib_warning
+ pack $f.l $f.cb -padx 10 -pady 10
+ $d draw
+ destroy $d
+ }
+
+ return 0 ;# indicates policy opened successfully
+}
+
+proc ApolTop::loadNeverAllows {} {
+ if {![is_capable "neverallow"]} {
+ Apol_Progress_Dialog::wait "Loading neverallow rules" "Rebuilding policy" \
+ {
+ $::ApolTop::qpolicy rebuild 0
+ _toplevel_update_stats
+ }
+ }
+}
+
+proc ApolTop::popup {parent x y menu callbacks callback_arg} {
+ # determine where to place the popup menu
+ set gx [winfo rootx $parent]
+ set gy [winfo rooty $parent]
+ set cmx [expr {$gx + $x}]
+ set cmy [expr {$gy + $y}]
+
+ $menu delete 0 end
+ foreach callback $callbacks {
+ $menu add command -label [lindex $callback 0] -command [concat [lindex $callback 1] $callback_arg]
+ }
+ tk_popup $menu $cmx $cmy
+}
+
+# Return the name of the currently shown tab. If the current tab is
+# nested, show the inner-most tab.
+proc ApolTop::getCurrentTab {} {
+ variable current_tab
+ set current_tab
+}
+
+proc ApolTop::getCurrentTextWidget {} {
+ [getCurrentTab]::getTextWidget
+}
+
+proc ApolTop::setCurrentTab {tab_name} {
+ variable tabs
+ # search through all tabs until one is found
+ foreach tab $tabs {
+ if {[lindex $tab 0] == $tab_name} {
+ variable notebook
+ set parent_nb $notebook
+ # raise all parent tabs as well
+ foreach nb [lindex $tab 1] {
+ $parent_nb raise $nb
+ set parent_nb [$parent_nb getframe $nb].nb
+ }
+ $parent_nb raise $tab_name
+ variable current_tab $tab_name
+ _toplevel_tab_switched
+ return
+ }
+ }
+ puts stderr "\[setCurrentTab\] tried to set the tab to $tab_name"
+ exit -1
+}
+
+proc ApolTop::setPolicySourceLinenumber {line} {
+ variable policy_source_linenum "Line $line"
+}
+
+proc ApolTop::showPolicySourceLineNumber {line} {
+ setCurrentTab Apol_PolicyConf
+ Apol_PolicyConf::gotoLine $line
+}
+
+############### functions for creating and maintaining toplevel ###############
+
+proc ApolTop::_create_toplevel {} {
+ set menus {
+ "&File" {} file 0 {
+ {command "&Open..." {} "Open a new policy" {Ctrl o} -command ApolTop::_open_policy}
+ {command "&Close" {tag_policy_open} "Close current polocy" {Ctrl w} -command ApolTop::_user_close_policy}
+ {separator}
+ {cascade "&Recent Files" {} recent 0 {}}
+ {separator}
+ {command "&Quit" {} "Quit policy analysis tool" {Ctrl q} -command ApolTop::_exit}
+ }
+ "&Edit" {} edit 0 {
+ {command "&Copy" {tag_policy_open} {} {Ctrl c} -command ApolTop::_copy}
+ {command "Select &All" {tag_policy_open} {} {Ctrl a} -command ApolTop::_select_all}
+ {separator}
+ {command "&Find..." {tag_policy_open} "Find text in current buffer" {Ctrl f} -command Apol_Find::find}
+ {command "&Goto Line..." {tag_policy_open} "Goto a line in current buffer" {Ctrl g} -command Apol_Goto::goto}
+ {separator}
+ }
+ "&Query" {} query 0 {
+ {command "&Open Query..." {tag_policy_open} "Open query criteria file" {} -command ApolTop::_open_query_file}
+ {command "&Save Query..." {tag_policy_open tag_query_saveable} "Save current query criteria to file" {} -command ApolTop::_save_query_file}
+ {separator}
+ {command "&Policy Summary" {tag_policy_open} "Display summary statistics" {} -command ApolTop::_show_policy_summary}
+ }
+ "&Tools" {} tools 0 {
+ {command "&Open Perm Map..." {tag_policy_open} "Open a permission map from file" {} -command ApolTop::_open_perm_map_from_file}
+ {command "Open &Default Perm Map" {tag_policy_open} "Open the default permission map" {} -command ApolTop::openDefaultPermMap}
+ {command "&Save Perm Map..." {tag_policy_open tag_perm_map_open} "Save the permission map to a file" {} -command ApolTop::_save_perm_map}
+ {command "Save Perm Map &As..." {tag_policy_open tag_perm_map_open} "Save the permission map to a file" {} -command ApolTop::_save_perm_map_as}
+ {command "Save Perm Map as D&efault" {tag_policy_open tag_perm_map_open} "Save the permission map to default file" {} -command ApolTop::_save_perm_map_default}
+ {command "&View Perm Map..." {tag_policy_open tag_perm_map_open} "Edit currently loaded permission map" {} -command Apol_Perms_Map::showPermMappings}
+ }
+ "&Help" {} helpmenu 0 {
+ {command "&General Help" {} "Show help on using apol" {} -command {ApolTop::_show_file Help apol_help.txt}}
+ {command "&Domain Transition Analysis" {} "Show help on domain transitions" {} -command {ApolTop::_show_file "Domain Transition Analysis Help" domaintrans_help.txt}}
+ {command "&Information Flow Analysis" {} "Show help on information flows" {} -command {ApolTop::_show_file "Information Flow Analysis Help" infoflow_help.txt}}
+ {command "Direct &Relabel Analysis" {} "Show help on file relabeling" {} -command {ApolTop::_show_file "Relabel Analysis Help" file_relabel_help.txt}}
+ {command "&Types Relationship Summary Analysis" {} "Show help on types relationships" {} -command {ApolTop::_show_file "Types Relationship Summary Analysis Help" types_relation_help.txt}}
+ {separator}
+ {command "&About apol" {} "Show copyright information" {} -command ApolTop::_about}
+ }
+ }
+ # Note that the name of the last menu is "helpmenu", not "help".
+ # This is because Tk handles menus named "help" differently in X
+ # Windows -- specifically, it is right justified on the menu bar.
+ # See the man page for [menu] for details. It was decided that
+ # the behavior is undesirable; the Help menu is intended to be
+ # left justified along with the other menus. Therefore the menu
+ # name is "helpmenu".
+
+ variable mainframe [MainFrame .mainframe -menu $menus -textvariable ApolTop::statu_line]
+ pack $mainframe -fill both -expand yes
+
+ $mainframe addindicator -textvariable ApolTop::policy_source_linenum -width 14
+ $mainframe addindicator -textvariable ApolTop::policy_stats_summary -width 88
+ $mainframe addindicator -textvariable ApolTop::policy_version_string -width 28
+
+ $mainframe setmenustate tag_policy_open disabled
+
+ variable notebook [NoteBook [$mainframe getframe].nb]
+ pack $notebook -fill both -expand yes -padx 4 -pady 4
+ set page [$notebook insert end components -text "Policy Components"]
+ set components [NoteBook $page.nb]
+ pack $components -fill both -expand yes -padx 4 -pady 4
+ set page [$notebook insert end rules -text "Policy Rules"]
+ set rules [NoteBook $page.nb]
+ pack $rules -fill both -expand yes -padx 4 -pady 4
+ $notebook bindtabs <Button-1> [list ApolTop::_switch_tab $components $rules]
+ $components bindtabs <Button-1> [list ApolTop::_switch_tab $components $rules]
+ $rules bindtabs <Button-1> [list ApolTop::_switch_tab $components $rules]
+
+ variable tabs
+ foreach tab $tabs {
+ set parent_nb $notebook
+ foreach nb [lindex $tab 1] {
+ # (intermediate notebooks were created just above here)
+ set parent_nb [set $nb]
+ }
+ [lindex $tab 0]::create [lindex $tab 0] $parent_nb
+ }
+
+ $components raise [$components page 0]
+ $rules raise [$rules page 0]
+ $notebook raise [$notebook page 0]
+
+ $notebook compute_size
+ setCurrentTab [$components page 0]
+}
+
+# Callback invoked whenever the user clicks on a (possibly different)
+# tab in the toplevel notebook(s).
+proc ApolTop::_switch_tab {components_nb rules_nb new_tab} {
+ if {$new_tab == "components"} {
+ set new_tab [$components_nb raise]
+ } elseif {$new_tab == "rules"} {
+ set new_tab [$rules_nb raise]
+ }
+ variable current_tab $new_tab
+ _toplevel_tab_switched
+}
+
+proc ApolTop::_toplevel_tab_switched {} {
+ variable tabs
+ variable current_tab
+ variable mainframe
+ foreach tab $tabs {
+ if {[lindex $tab 0] != $current_tab} {
+ continue
+ }
+ focus [getCurrentTextWidget]
+ if {[lsearch [lindex $tab 2] "tag_query_saveable"] >= 0} {
+ $mainframe setmenustate tag_query_saveable normal
+ } else {
+ $mainframe setmenustate tag_query_saveable disabled
+ }
+ if {[lsearch [lindex $tab 2] "tag_source"] >= 0} {
+ [lindex $tab 0]::insertionMarkChanged
+ } else {
+ variable policy_source_linenum {}
+ }
+ break
+ }
+}
+
+# Enable and disable various widgets in the toplevel window, based
+# upon the type of policy that was opened.
+proc ApolTop::_toplevel_policy_open {ppath} {
+ variable tabs
+ foreach tab $tabs {
+ [lindex $tab 0]::open $ppath
+ }
+
+ if {![is_capable "conditionals"]} {
+ _toplevel_enable_tabs tag_conditionals disabled
+ }
+ if {![is_capable "mls"]} {
+ _toplevel_enable_tabs tag_mls disabled
+ }
+ if {![is_capable "source"]} {
+ _toplevel_enable_tabs tag_source disabled
+ }
+ _toplevel_tab_switched
+
+ variable mainframe
+ $mainframe setmenustate tag_policy_open normal
+ $mainframe setmenustate tag_perm_map_open disabled
+
+ _toplevel_update_stats
+ variable policy_version_string [$::ApolTop::policy get_version_type_mls_str]
+
+ set primary_file [$ppath get_primary]
+ wm title . "SELinux Policy Analysis - $primary_file"
+}
+
+# Enable/disable tabs that contain the given tag. If the currently
+# raised page is one of those tabs then raise the first tab (which
+# hopefully does not have that tag).
+proc ApolTop::_toplevel_enable_tabs {tag new_state} {
+ variable tabs
+ variable notebook
+ foreach tab $tabs {
+ if {[lsearch [lindex $tab 2] $tag] >= 0} {
+ set parent_nb $notebook
+ foreach nb [lindex $tab 1] {
+ set parent_nb [$parent_nb getframe $nb].nb
+ }
+ $parent_nb itemconfigure [lindex $tab 0] -state $new_state
+ if {[$parent_nb raise] == {}} {
+ $parent_nb raise [$parent_nb pages 0]
+ setCurrentTab [lindex $tabs 0 0]
+ }
+ }
+ }
+}
+
+proc ApolTop::_build_recent_files_menu {} {
+ variable mainframe
+ variable recent_files
+ variable max_recent_files
+ set recent_menu [$mainframe getmenu recent]
+ $recent_menu delete 0 $max_recent_files
+ foreach r $recent_files {
+ foreach {path_type primary_file modules} [policy_path_to_list $r] {break}
+ if {$path_type == "monolithic"} {
+ set label $primary_file
+ } else {
+ set label "$primary_file + [llength $modules] module"
+ if {[llength $modules] != 1} {
+ append label "s"
+ }
+ }
+ $recent_menu add command -label $label \
+ -command [list ApolTop::openPolicyPath $r]
+ }
+}
+
+# Add a policy path to the recently opened list, trim the menu to
+# max_recent_files, and then regenerate the recent menu.
+proc ApolTop::_add_recent {ppath} {
+ variable recent_files
+ variable max_recent_files
+
+ # if ppath is already in recent files list, remove it from there
+ set new_recent $ppath
+ foreach r $recent_files {
+ if {[apol_policy_path_compare $r $ppath] != 0} {
+ lappend new_recent $r
+ }
+ }
+ set recent_files [lrange $new_recent 0 [expr {$max_recent_files - 1}]]
+ _build_recent_files_menu
+}
+
+proc ApolTop::_toplevel_update_stats {} {
+ variable policy_stats
+ variable policy_stats_summary
+
+ set iter_funcs {
+ "classes" get_class_iter
+ "commons" get_common_iter
+
+ "roles" get_role_iter
+ "role_allow" get_role_allow_iter
+ "role_trans" get_role_trans_iter
+
+ "users" get_user_iter
+ "bools" get_bool_iter
+ "sens" get_level_iter
+ "cats" get_cat_iter
+ "range_trans" get_range_trans_iter
+
+ "sids" get_isid_iter
+ "portcons" get_portcon_iter
+ "netifcons" get_netifcon_iter
+ "nodecons" get_nodecon_iter
+ "genfscons" get_genfscon_iter
+ "fs_uses" get_fs_use_iter
+ }
+ foreach {key func} $iter_funcs {
+ set i [$::ApolTop::qpolicy $func]
+ set policy_stats($key) [$i get_size]
+ $i -acquire
+ $i -delete
+ }
+
+ set query_funcs {
+ "perms" new_apol_perm_query_t
+ "types" new_apol_type_query_t
+ "attribs" new_apol_attr_query_t
+ }
+
+ foreach {key func} $query_funcs {
+ set q [$func]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set policy_stats($key) [$v get_size]
+ $v -acquire
+ $v -delete
+ }
+
+ set avrule_bits [list \
+ avrule_allow $::QPOL_RULE_ALLOW \
+ avrule_auditallow $::QPOL_RULE_AUDITALLOW \
+ avrule_dontaudit $::QPOL_RULE_DONTAUDIT \
+ avrule_neverallow $::QPOL_RULE_NEVERALLOW \
+ ]
+ foreach {key bit} $avrule_bits {
+ if {$bit == $::QPOL_RULE_NEVERALLOW && ![is_capable "neverallow"]} {
+ # neverallow rules have not yet been loaded
+ set policy_stats($key) 0
+ } else {
+ set i [$::ApolTop::qpolicy get_avrule_iter $bit]
+ set policy_stats($key) [$i get_size]
+ $i -acquire
+ $i -delete
+ }
+ }
+
+ set terule_bits [list \
+ type_trans $::QPOL_RULE_TYPE_TRANS \
+ type_member $::QPOL_RULE_TYPE_CHANGE \
+ type_change $::QPOL_RULE_TYPE_MEMBER \
+ ]
+ foreach {key bit} $terule_bits {
+ set i [$::ApolTop::qpolicy get_avrule_iter $bit]
+ set policy_stats($key) [$i get_size]
+ $i -acquire
+ $i -delete
+ }
+
+ set policy_stats_summary ""
+ append policy_stats_summary "Classes: $policy_stats(classes) "
+ append policy_stats_summary "Perms: $policy_stats(perms) "
+ append policy_stats_summary "Types: $policy_stats(types) "
+ append policy_stats_summary "Attribs: $policy_stats(attribs) "
+ set num_te_rules [expr {$policy_stats(avrule_allow) + $policy_stats(avrule_auditallow) +
+ $policy_stats(avrule_dontaudit) + $policy_stats(avrule_neverallow) +
+ $policy_stats(type_trans) + $policy_stats(type_member) +
+ $policy_stats(type_change)}]
+ if {![is_capable "neverallow"]} {
+ append num_te_rules "+"
+ }
+ append policy_stats_summary "AV + TE rules: $num_te_rules "
+ append policy_stats_summary "Roles: $policy_stats(roles) "
+ append policy_stats_summary "Users: $policy_stats(users)"
+}
+
+############### callbacks for top-level menu items ###############
+
+proc ApolTop::_open_policy {} {
+ variable last_policy_path
+ Apol_Open_Policy_Dialog::getPolicyPath $last_policy_path
+}
+
+proc ApolTop::_user_close_policy {} {
+ variable last_policy_path
+
+ _close_policy
+ set last_policy_path {}
+}
+
+proc ApolTop::_close_policy {} {
+ variable policy_version_string {}
+ variable policy_stats_summary {}
+
+ wm title . "SELinux Policy Analysis"
+ set i 0
+ Apol_Progress_Dialog::wait "apol" "Closing policy." \
+ {
+ variable tabs
+ foreach tab $tabs {
+ if {[catch [lindex $tab 0]::close]} {
+ set i [expr $i+2]
+ }
+ }
+ Apol_Perms_Map::close
+ variable policy
+ if {$policy != {}} {
+ $policy -acquire
+ $policy -delete
+ set policy {}
+ variable qpolicy {}
+ }
+ }
+
+ variable mainframe
+ $mainframe setmenustate tag_policy_open disabled
+ $mainframe setmenustate tag_perm_map_open disabled
+
+ _toplevel_enable_tabs tag_conditionals normal
+ _toplevel_enable_tabs tag_mls normal
+ _toplevel_enable_tabs tag_source normal
+}
+
+proc ApolTop::_exit {} {
+ variable policy
+ if {$policy != {}} {
+ _close_policy
+ }
+
+ Apol_File_Contexts::close
+ _write_configuration_file
+ exit
+}
+
+proc ApolTop::_copy {} {
+ set w [getCurrentTextWidget]
+ if {$w != {} && [$w tag ranges sel] != {}} {
+ set data [$w get sel.first sel.last]
+ clipboard clear
+ clipboard append -- $data
+ }
+}
+
+proc ApolTop::_select_all {} {
+ set w [getCurrentTextWidget]
+ if {$w != {}} {
+ $w tag add sel 1.0 end
+ }
+}
+
+proc ApolTop::_find {} {
+ Apol_Find::find
+}
+
+proc ApolTop::_goto {} {
+ Apol_Goto::goto
+}
+
+proc ApolTop::_open_query_file {} {
+ set types {
+ {"Query files" {$ApolTop::query_file_ext}}
+ }
+ set query_file [tk_getOpenFile -filetypes $types -title "Open Apol Query" \
+ -defaultextension $ApolTop::query_file_ext -parent .]
+ if {$query_file != {}} {
+ if {[catch {::open $query_file r} f]} {
+ tk_messageBox -icon error -type ok -title "Open Apol Query" \
+ -message "Could not open $query_file: $f"
+ }
+ # Search for the analysis type line
+ while {[gets $f line] >= 0} {
+ set query_id [string trim $line]
+ # Skip empty lines and comments
+ if {$query_id == {} || [string index $query_id 0] == "#"} {
+ continue
+ }
+ break
+ }
+
+ variable tabs
+ foreach tab $tabs {
+ if {$query_id == [lindex $tab 0] && [lsearch [lindex $tab 2] "tag_query_saveable"] >= 0} {
+ if {[catch {${query_id}::load_query_options $f} err]} {
+ tk_messageBox -icon error -type ok -title "Open Apol Query" \
+ -message $err
+ } else {
+ setCurrentTab $query_id
+ }
+ return
+ }
+ }
+ tk_messageBox -icon error -type ok -title "Open Apol Query" \
+ -message "The query criteria file could not be read and may be corrupted."
+ close $f
+ }
+}
+
+proc ApolTop::_save_query_file {} {
+ set types {
+ {"Query files" {$ApolTop::query_file_ext}}
+ }
+ set query_file [tk_getSaveFile -title "Save Apol Query" \
+ -defaultextension $ApolTop::query_file_ext \
+ -filetypes $types -parent .]
+ if {$query_file != {}} {
+ if {[catch {::open $query_file w} f]} {
+ tk_messageBox -icon error -type ok -title "Save Apol Query" \
+ -message "Could not save $query_file: $f"
+ }
+ if {[catch {puts $f [getCurrentTab]} err]} {
+ tk_messageBox -icon error -type ok -title "Save Apol Query" \
+ -message $err
+ }
+ if {[catch {[getCurrentTab]::save_query_options $f $query_file} err]} {
+ tk_messageBox -icon error -type ok -title "Save Apol Query" \
+ -message $err
+ }
+ close $f
+ }
+}
+
+proc ApolTop::_show_policy_summary {} {
+ variable policy_version_string
+ variable policy_stats
+
+ if {![regexp -- {^([^\(]+) \(([^,]+), ([^\)]+)} $ApolTop::policy_version_string -> policy_version policy_type policy_mls_type]} {
+ set policy_version $ApolTop::policy_version_string
+ set policy_type "unknown"
+ set policy_mls_type "unknown"
+ }
+ set policy_version [string trim $policy_version]
+
+ destroy .policy_statsbox
+ set dialog [Dialog .policy_statsbox -separator 1 -title "Policy Summary" \
+ -modal none -parent .]
+ $dialog add -text Close -command [list destroy $dialog]
+
+ set w [$dialog getframe]
+
+ label $w.title -text "Policy Summary Statistics"
+ set f [frame $w.summary]
+ label $f.l -justify left -text " Policy Version:\n Policy Type:\n MLS Status:"
+ label $f.r -justify left -text "$policy_version\n$policy_type\n$policy_mls_type"
+ grid $f.l $f.r -sticky w
+ grid configure $f.r -padx 30
+ grid $w.title - -sticky w -padx 8
+ grid $f - -sticky w -padx 8
+ grid [Separator $w.sep] - -sticky ew -pady 5
+
+ set f [frame $w.left]
+ set i 0
+ foreach {title block} {
+ "Number of Classes and Permissions" {
+ "Object Classes" classes
+ "Common Permissions" commons
+ "Permissions" perms
+ }
+ "Number of Types and Attributes" {
+ "Types" types
+ "Attributes" attribs
+ }
+ "Number of Type Enforcement Rules" {
+ "allows" avrule_allow
+ "auditallows" avrule_auditallow
+ "dontaudits" avrule_dontaudit
+ "neverallows" avrule_neverallow
+ "type_transitions" type_trans
+ "type_members" type_member
+ "type_changes" type_change
+ }
+ "Number of Roles" {
+ "Roles" roles
+ }
+ "Number of RBAC Rules" {
+ "allows" role_allow
+ "role_transitions" role_trans
+ }
+ } {
+ set ltext "$title:"
+ set rtext {}
+ foreach {l r} $block {
+ append ltext "\n $l:"
+ if {$r != "avrule_neverallow" || [is_capable "neverallow"]} {
+ append rtext "\n$policy_stats($r)"
+ } else {
+ append rtext "\nN/A"
+ }
+ }
+ label $f.l$i -justify left -text $ltext
+ label $f.r$i -justify left -text $rtext
+ grid $f.l$i $f.r$i -sticky w -padx 4 -pady 2
+ incr i
+ }
+
+ set i 0
+ set g [frame $w.right]
+ foreach {title block} {
+ "Number of Users" {
+ "Users" users
+ }
+ "Number of Booleans" {
+ "Booleans" bools
+ }
+ "Number of MLS Components" {
+ "Sensitivities" sens
+ "Categories" cats
+ }
+ "Number of MLS Rules" {
+ "range_transitions" range_trans
+ }
+ "Number of Initial SIDs" {
+ "SIDs" sids
+ }
+ "Number of OContexts" {
+ "PortCons" portcons
+ "NetIfCons" netifcons
+ "NodeCons" nodecons
+ "GenFSCons" genfscons
+ "fs_use statements" fs_uses
+ }
+ } {
+ set ltext "$title:"
+ set rtext {}
+ foreach {l r} $block {
+ append ltext "\n $l:"
+ append rtext "\n$policy_stats($r)"
+ }
+ label $g.l$i -justify left -text $ltext
+ label $g.r$i -justify left -text $rtext
+ grid $g.l$i $g.r$i -sticky w -padx 4 -pady 2
+ incr i
+ }
+ grid $f $g -sticky nw -padx 4
+ $dialog draw
+}
+
+proc ApolTop::_open_perm_map_from_file {} {
+ if {[Apol_Perms_Map::openPermMapFromFile]} {
+ variable mainframe
+ $mainframe setmenustate tag_perm_map_open normal
+ }
+}
+
+# Return non-zero if a permission map was found and opened, zero if
+# not.
+proc ApolTop::openDefaultPermMap {} {
+ if {[Apol_Perms_Map::openDefaultPermMap]} {
+ variable mainframe
+ $mainframe setmenustate tag_perm_map_open normal
+ return 1
+ }
+ return 0
+}
+
+proc ApolTop::_save_perm_map {} {
+ Apol_Perms_Map::savePermMap
+}
+
+proc ApolTop::_save_perm_map_as {} {
+ Apol_Perms_Map::savePermMapAs
+}
+
+proc ApolTop::_save_perm_map_default {} {
+ Apol_Perms_Map::saveDefaultPermMap
+}
+
+proc ApolTop::_show_file {title file_name} {
+ set helpfile [file join [tcl_config_get_install_dir] $file_name]
+ if {[catch {::open $helpfile} f]} {
+ set info $f
+ } else {
+ set info [read $f]
+ close $f
+ }
+ Apol_Widget::showPopupParagraph $title $info
+}
+
+proc ApolTop::_about {} {
+ if {[winfo exists .apol_about]} {
+ raise .apol_about
+ } else {
+ variable apol_icon
+
+ Dialog .apol_about -cancel 0 -default 0 -image $apol_icon \
+ -modal none -parent . -separator 1 -title "About apol"
+ set f [.apol_about getframe]
+ set l1 [label $f.l1 -text "apol [tcl_config_get_version]" -height 2]
+ set label_font [$l1 cget -font]
+ # Tk 8.4 differs from 8.5 in how fonts are handled
+ if {[llength $label_font] > 1} {
+ foreach {name size} [$l1 cget -font] {break}
+ incr size 6
+ $l1 configure -font [list $name $size bold]
+ }
+ set l2 [label $f.l2 -text "Security Policy Analysis Tool for Security Enhanced Linux\n${::COPYRIGHT_INFO}\nhttp://oss.tresys.com/projects/setools"]
+ pack $l1 $l2
+ .apol_about add -text "Close" -command [list destroy .apol_about]
+ .apol_about draw
+ }
+}
+
+##### functions that load and write user's configuration file #####
+
+proc ApolTop::_load_fonts {} {
+ variable title_font
+ variable dialog_font
+ variable general_font
+ variable text_font
+
+ tk scaling -displayof . 1.0
+ # First set all fonts in general; then change specific fonts
+ if {$general_font == ""} {
+ set general_font "Helvetica 10"
+ }
+ option add *Font $general_font
+ if {$title_font == {}} {
+ set title_font "Helvetica 10 bold italic"
+ }
+ option add *TitleFrame.l.font $title_font
+ if {$dialog_font == {}} {
+ set dialog_font "Helvetica 10"
+ }
+ option add *Dialog*font $dialog_font
+ option add *Dialog*TitleFrame.l.font $title_font
+ if {$text_font == ""} {
+ set text_font "fixed"
+ }
+ option add *text*font $text_font
+}
+
+# Reads in user data from their $HOME/.apol file
+proc ApolTop::_read_configuration_file {} {
+ variable dot_apol_file
+ variable recent_files
+
+ # if it doesn't exist, it will be created later
+ if {![file exists $dot_apol_file]} {
+ return
+ }
+
+ if {[catch {::open $dot_apol_file r} f]} {
+ tk_messageBox -icon error -type ok -title "apol" \
+ -message "Could not open $dot_apol_file: $f"
+ return
+ }
+
+ while {![eof $f]} {
+ set option [string trim [gets $f]]
+ if {$option == {} || [string compare -length 1 $option "\#"] == 0} {
+ continue
+ }
+ set value [string trim [gets $f]]
+ if {[eof $f]} {
+ puts stderr "EOF reached while reading $option"
+ break
+ }
+ if {$value == {}} {
+ puts stderr "Empty value for option $option"
+ continue
+ }
+ switch -- $option {
+ "\[window_height\]" {
+ if {[string is integer -strict $value] != 1} {
+ puts stderr "window_height was not given as an integer and is ignored"
+ break
+ }
+ variable mainframe_height $value
+ }
+ "\[window_width\]" {
+ if {[string is integer -strict $value] != 1} {
+ puts stderr "window_width was not given as an integer and is ignored"
+ break
+ }
+ variable mainframe_width $value
+ }
+ "\[title_font\]" {
+ variable title_font $value
+ }
+ "\[dialog_font\]" {
+ variable dialog_font $value
+ }
+ "\[text_font\]" {
+ variable text_font $value
+ }
+ "\[general_font\]" {
+ variable general_font $value
+ }
+ "\[show_fake_attrib_warning\]" {
+ variable show_fake_attrib_warning $value
+ }
+
+ # The form of [max_recent_file] is a single line that
+ # follows containing an integer with the max number of
+ # recent files to keep. The default is 5 if this is not
+ # specified. The minimum is 2.
+ "\[max_recent_files\]" {
+ if {[string is integer -strict $value] != 1} {
+ puts stderr "max_recent_files was not given as an integer and is ignored"
+ } else {
+ if {$value < 2} {
+ variable max_recent_files 2
+ } else {
+ variable max_recent_files $value
+ }
+ }
+ }
+ # The form of this key in the .apol file is as such
+ #
+ # recent_files
+ # 5 (# indicating how many file names follow)
+ # policy_path_0
+ # policy_path_1
+ # ...
+ "recent_files" {
+ if {[string is integer -strict $value] != 1} {
+ puts stderr "Number of recent files was not given as an integer and was ignored."
+ continue
+ } elseif {$value < 0} {
+ puts stderr "Number of recent was less than 0 and was ignored."
+ continue
+ }
+ while {$value > 0} {
+ incr value -1
+ set line [gets $f]
+ if {[eof $f]} {
+ puts stderr "EOF reached trying to read recent files."
+ break
+ }
+ if {[llength $line] == 1} {
+ # reading older recent files, before advent of
+ # policy_path
+ set ppath [new_apol_policy_path_t $::APOL_POLICY_PATH_TYPE_MONOLITHIC $line NULL]
+ $ppath -acquire
+ } else {
+ foreach {path_type primary modules} $line {break}
+ if {[catch {list_to_policy_path $path_type $primary $modules} ppath]} {
+ puts stderr "Invalid policy path line: $line"
+ continue
+ }
+ }
+ lappend recent_files $ppath
+ }
+ }
+ }
+ }
+ close $f
+}
+
+# Saves user data in their $HOME/.apol file
+proc ApolTop::_write_configuration_file {} {
+ variable dot_apol_file
+ variable recent_files
+ variable text_font
+ variable title_font
+ variable dialog_font
+ variable general_font
+
+ if {[catch {::open $dot_apol_file w} f]} {
+ tk_messageBox -icon error -type ok -title "apol" \
+ -message "Could not open $dot_apol_file for writing: $f"
+ return
+ }
+ puts $f "recent_files"
+ puts $f [llength $recent_files]
+ foreach r $recent_files {
+ puts $f [policy_path_to_list $r]
+ }
+
+ puts $f "\n"
+ puts $f "# Font format: family ?size? ?style? ?style ...?"
+ puts $f "# Possible values for the style arguments are as follows:"
+ puts $f "# normal bold roman italic underline overstrike\n#\n#"
+ puts $f "# NOTE: When configuring fonts, remember to remove the following "
+ puts $f "# \[window height\] and \[window width\] entries before starting apol. "
+ puts $f "# Not doing this may cause widgets to be obscured when running apol."
+ puts $f "\[general_font\]"
+ if {$general_font == {}} {
+ puts $f "Helvetica 10"
+ } else {
+ puts $f "$general_font"
+ }
+ puts $f "\[title_font\]"
+ if {$title_font == {}} {
+ puts $f "Helvetica 10 bold italic"
+ } else {
+ puts $f "$title_font"
+ }
+ puts $f "\[dialog_font\]"
+ if {$dialog_font == {}} {
+ puts $f "Helvetica 10"
+ } else {
+ puts $f "$dialog_font"
+ }
+ puts $f "\[text_font\]"
+ if {$text_font == {}} {
+ puts $f "fixed"
+ } else {
+ puts $f "$text_font"
+ }
+ puts $f "\[window_height\]"
+ puts $f [winfo height .]
+ puts $f "\[window_width\]"
+ puts $f [winfo width .]
+ puts $f "\[show_fake_attrib_warning\]"
+ variable show_fake_attrib_warning
+ puts $f $show_fake_attrib_warning
+ puts $f "\[max_recent_files\]"
+ variable max_recent_files
+ puts $f $max_recent_files
+ close $f
+}
+
+#######################################################
+# Start script here
+
+proc ApolTop::main {} {
+ variable notebook
+
+ tcl_config_init
+
+ # Prevent the application from responding to incoming send
+ # requests and sending outgoing requests. This way any other
+ # applications that can connect to our X server cannot send
+ # harmful scripts to our application.
+ rename send {}
+
+ if {[catch {package require BWidget}]} {
+ tk_messageBox -icon error -type ok -title "Apol Startup" -message \
+ "The BWidget package could not be found. Ensure that BWidget is installed in a location that Tcl/Tk can read."
+ exit -1
+ }
+
+ wm withdraw .
+ wm title . "SELinux Policy Analysis"
+ wm protocol . WM_DELETE_WINDOW ApolTop::_exit
+ variable default_bg_color [. cget -background]
+
+ # Read apol's default settings file, gather all font information,
+ # create the gui and then load recent files into the menu.
+ catch {tcl_config_patch_bwidget}
+ _load_fonts
+ _read_configuration_file
+ _create_toplevel
+ bind . <Button-1> {focus %W}
+ bind . <Button-2> {focus %W}
+ bind . <Button-3> {focus %W}
+ _build_recent_files_menu
+
+ set icon_file [file join [tcl_config_get_install_dir] apol.gif]
+ if {![catch {image create photo -file $icon_file} icon]} {
+ catch {wm iconphoto . -default $icon}
+ }
+ variable apol_icon $icon
+
+ variable mainframe_width [$notebook cget -width]
+ variable mainframe_height [$notebook cget -height]
+ wm geom . ${mainframe_width}x${mainframe_height}
+
+ wm deiconify .
+ raise .
+ focus .
+}
+
+proc handle_args {argv0 argv} {
+ set argvp 0
+ while {$argvp < [llength $argv]} {
+ set arg [lindex $argv $argvp]
+ switch -- $arg {
+ "-h" - "--help" { print_help $argv0 verbose; exit }
+ "-V" - "--version" { print_version_info; exit }
+ "--" { incr argvp; break }
+ default {
+ if {[string index $arg 0] != "-"} {
+ break
+ } else {
+ puts stderr "$argv0: unrecognized option `$arg'"
+ print_help $argv0 brief
+ exit 1
+ }
+ }
+ }
+ incr argvp
+ }
+
+ set arglen [expr [llength $argv]-$argvp]
+ set ppath {}
+ if {$arglen <= 0} {
+ return {}
+ } elseif {$arglen == 1} {
+ set path_type $::APOL_POLICY_PATH_TYPE_MONOLITHIC
+ set policy_file [lindex $argv $argvp]
+ set mod_paths [list_to_str_vector {}]
+ if {[apol_file_is_policy_path_list $policy_file]} {
+ set ppath [new_apol_policy_path_t $policy_file]
+ }
+ } elseif {$arglen > 1} {
+ set path_type $::APOL_POLICY_PATH_TYPE_MODULAR
+ set policy_file {}
+ foreach f [lrange $argv $argvp end] {
+ if {[catch {Apol_Open_Policy_Dialog::getModuleInfo $f} modinfo]} {
+ tk_messageBox -icon error -type ok -title "Module access error" -message $modinfo
+ } else {
+ foreach {name vers type} $modinfo {break}
+ if {$type == 1} { ;# This file is a base 'module'
+ if {$policy_file != {} && $policy_file != $f} {
+ set rsp [tk_messageBox -icon error -type okcancel -title "Open Module" -message "Multiple base entries found." -detail "Current file: $policy_file\n\nNew file: $f\n\nClick OK to ignore new file, Cancel to exit"]
+ if {$rsp == "cancel"} { exit 1}
+ } else {
+ set policy_file $f
+ }
+ } else { ;# Append regular modules to the list.
+ lappend module_list $f
+ }
+ }
+ }
+ set mod_paths [list_to_str_vector $module_list]
+ }
+
+ if {$ppath == {}} {
+ set ppath [new_apol_policy_path_t $path_type $policy_file $mod_paths]
+ }
+ if {$ppath == {}} {
+ puts stderr "Error loading $policy_file."
+ } else {
+ $ppath -acquire
+ }
+ return $ppath
+}
+
+proc print_help {program_name verbose} {
+ puts "Usage: $program_name \[OPTIONS\] \[POLICY ...\]\n"
+ if {$verbose != "verbose"} {
+ puts "\tTry $program_name --help for more help.\n"
+ } else {
+ puts "Policy Analysis tool for Security Enhanced Linux.\n"
+ puts " -h, --help print this help text and exit"
+ puts " -V, --version print version information and exit\n"
+ }
+}
+
+proc print_version_info {} {
+ puts "apol [tcl_config_get_version]\n$::COPYRIGHT_INFO"
+}
+
+proc print_init {s} {
+ puts -nonewline $s
+ flush stdout
+}
+
+if {[catch {tcl_config_init_libraries}]} {
+ puts stderr "FAILED. The SETools libraries could not be found in any of these subdirectories:\n\t[join $auto_path "\n\t"]"
+ exit -1
+}
+
+print_init "Initializing Tk... "
+if {[catch {package require Tk}]} {
+ puts stderr "FAILED. This library could not be found in any of these subdirectories:\n\t[join $auto_path "\n\t"]"
+ puts stderr "This may indicate a problem with the tcl package's auto_path variable.\n"
+ exit -1
+}
+puts "done."
+
+set path [handle_args $argv0 $argv]
+ApolTop::main
+if {$path != {}} {
+ after idle [list ApolTop::openPolicyPath $path]
+}
diff --git a/apol/transflow_module.tcl b/apol/transflow_module.tcl
new file mode 100644
index 0000000..ff46f9b
--- /dev/null
+++ b/apol/transflow_module.tcl
@@ -0,0 +1,1156 @@
+# Copyright (C) 2003-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Analysis_transflow {
+ variable vals
+ variable widgets
+ Apol_Analysis::registerAnalysis "Apol_Analysis_transflow" "Transitive Information Flow"
+}
+
+proc Apol_Analysis_transflow::create {options_frame} {
+ variable vals
+ variable widgets
+
+ _reinitializeVals
+
+ set dir_tf [TitleFrame $options_frame.dir -text "Direction"]
+ pack $dir_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set dir_to [radiobutton [$dir_tf getframe].to -text "To" \
+ -value $::APOL_INFOFLOW_IN \
+ -variable Apol_Analysis_transflow::vals(dir)]
+ set dir_from [radiobutton [$dir_tf getframe].from -text "From" \
+ -value $::APOL_INFOFLOW_OUT \
+ -variable Apol_Analysis_transflow::vals(dir)]
+ pack $dir_to $dir_from -anchor w
+
+ set req_tf [TitleFrame $options_frame.req -text "Required Parameters"]
+ pack $req_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set l [label [$req_tf getframe].l -text "Starting type"]
+ pack $l -anchor w
+ set widgets(type) [Apol_Widget::makeTypeCombobox [$req_tf getframe].type]
+ pack $widgets(type)
+
+ set filter_tf [TitleFrame $options_frame.filter -text "Optional Result Filters"]
+ pack $filter_tf -side left -padx 2 -pady 2 -expand 1 -fill both
+ set advanced_f [frame [$filter_tf getframe].advanced]
+ pack $advanced_f -side left -anchor nw
+ set widgets(advanced_enable) [checkbutton $advanced_f.enable -text "Use advanced filters" \
+ -variable Apol_Analysis_transflow::vals(advanced:enable)]
+ pack $widgets(advanced_enable) -anchor w
+ set widgets(advanced) [button $advanced_f.b -text "Advanced Filters" \
+ -command Apol_Analysis_transflow::_createAdvancedDialog \
+ -state disabled]
+ pack $widgets(advanced) -anchor w -padx 4
+ trace add variable Apol_Analysis_transflow::vals(advanced:enable) write \
+ Apol_Analysis_transflow::_toggleAdvancedSelected
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry [$filter_tf getframe].end]
+ $widgets(regexp).cb configure -text "Filter result types using regular expression"
+ pack $widgets(regexp) -side left -anchor nw -padx 8
+}
+
+proc Apol_Analysis_transflow::open {} {
+ variable vals
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(type)
+ set vals(intermed:inc) [Apol_Types::getTypes]
+ set vals(intermed:inc_all) $vals(intermed:inc)
+ set vals(classes:displayed) {}
+ foreach class [Apol_Class_Perms::getClasses] {
+ foreach perm [Apol_Class_Perms::getPermsForClass $class] {
+ set vals(perms:$class:$perm) 1
+ }
+ lappend vals(classes:displayed) $class
+ }
+}
+
+proc Apol_Analysis_transflow::close {} {
+ variable widgets
+ _reinitializeVals
+ _reinitializeWidgets
+ Apol_Widget::clearTypeCombobox $widgets(type)
+}
+
+proc Apol_Analysis_transflow::getInfo {} {
+ return "This analysis generates the results of a Transitive Information Flow
+analysis beginning from the starting type selected. The results of
+the analysis are presented in tree form with the root of the tree
+being the start point for the analysis.
+
+\nEach child node in the tree represents a type in the current policy
+for which there is a transitive information flow to or from its parent
+node. If flow 'To' is selected the information flows from the child
+to the parent. If flow 'From' is selected then information flows from
+the parent to the child.
+
+\nThe results of the analysis may be optionally filtered by object
+classes and/or permissions, intermediate types, or an end type regular
+expression.
+
+\nNOTE: For any given generation, if the parent and the child are the
+same, the child cannot be opened. This avoids cyclic analyses.
+
+\nFor additional help on this topic select \"Information Flow Analysis\"
+from the help menu."
+}
+
+proc Apol_Analysis_transflow::newAnalysis {} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ set f [_createResultsDisplay]
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_transflow::updateAnalysis {f} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ _clearResultsDisplay $f
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_transflow::reset {} {
+ _reinitializeVals
+ _reinitializeWidgets
+ open
+}
+
+proc Apol_Analysis_transflow::switchTab {query_options} {
+ variable vals
+ variable widgets
+ array set vals $query_options
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_transflow::saveQuery {channel} {
+ variable vals
+ variable widgets
+ foreach {key value} [array get vals] {
+ switch -glob -- $key {
+ find_more:* -
+ intermed:inc* -
+ intermed:exc -
+ classes:title {}
+ classes:displayed {}
+ perms:* {
+ # only write permissions that have been excluded
+ if {$value == 0} {
+ puts $channel "$key $value"
+ }
+ }
+ default {
+ puts $channel "$key $value"
+ }
+ }
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ puts $channel "type [lindex $type 0]"
+ puts $channel "type:attrib [lindex $type 1]"
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ puts $channel "regexp:enable $use_regexp"
+ puts $channel "regexp $regexp"
+}
+
+proc Apol_Analysis_transflow::loadQuery {channel} {
+ variable vals
+ set intermed_exc {}
+ set perms_disabled {}
+ while {[gets $channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ set key {}
+ set value {}
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ switch -glob -- $key {
+ intermed:exc_all {
+ set intermed_exc $value
+ }
+ perms:* {
+ set perms_disabled [concat $perms_disabled $key $value]
+ }
+ default {
+ set vals($key) $value
+ }
+ }
+ }
+
+ # fill in only types and classes found within the current policy
+ open
+
+ set vals(intermed:exc_all) {}
+ set vals(intermed:exc) {}
+ foreach t $intermed_exc {
+ set i [lsearch $vals(intermed:inc_all) $t]
+ if {$i >= 0} {
+ lappend vals(intermed:exc_all) $t
+ lappend vals(intermed:exc) $t
+ set vals(intermed:inc_all) [lreplace $vals(intermed:inc_all) $i $i]
+ set i [lsearch $vals(intermed:inc) $t]
+ set vals(intermed:inc) [lreplace $vals(intermed:inc) $i $i]
+ }
+ }
+ set vals(intermed:exc_all) [lsort $vals(intermed:exc_all)]
+ set vals(intermed:exc) [lsort $vals(intermed:exc)]
+
+ foreach {key value} $perms_disabled {
+ if {[info exists vals($key)]} {
+ set vals($key) $value
+ }
+ }
+ set vals(classes:displayed) {}
+ foreach class [Apol_Class_Perms::getClasses] {
+ set all_disabled 1
+ foreach perm_key [array names vals perms:$class:*] {
+ if {$vals($perm_key)} {
+ set all_disabled 0
+ break
+ }
+ }
+ if {$all_disabled} {
+ lappend vals(classes:displayed) "$class (excluded)"
+ } else {
+ lappend vals(classes:displayed) $class
+ }
+ }
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_transflow::getTextWidget {tab} {
+ return [$tab.right getframe].res.tb
+}
+
+proc Apol_Analysis_transflow::appendResultsNodes {tree parent_node results} {
+ _createResultsNodes $tree $parent_node $results 0
+}
+
+proc Apol_Analysis_transflow::renderPath {res path_num path} {
+ _renderPath $res $path_num $path
+}
+
+#################### private functions below ####################
+
+proc Apol_Analysis_transflow::_reinitializeVals {} {
+ variable vals
+
+ set vals(dir) $::APOL_INFOFLOW_IN
+ array set vals {
+ type {} type:attrib {}
+
+ regexp:enable 0
+ regexp {}
+
+ advanced:enable 0
+
+ classes:title {}
+ classes:displayed {}
+ classes:threshold_enable 0
+ classes:threshold 1
+
+ intermed:inc {} intermed:inc_all {}
+ intermed:exc {} intermed:exc_all {}
+ intermed:attribenable 0 intermed:attrib {}
+
+ find_more:hours 0 find_more:minutes 0 find_more:seconds 30
+ find_more:limit 20
+ }
+ array unset vals perms:*
+ foreach class [Apol_Class_Perms::getClasses] {
+ foreach perm [Apol_Class_Perms::getPermsForClass $class] {
+ set vals(perms:$class:$perm) 1
+ }
+ }
+}
+
+proc Apol_Analysis_transflow::_reinitializeWidgets {} {
+ variable vals
+ variable widgets
+
+ if {$vals(type:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(type) [list $vals(type) $vals(type:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(type) $vals(type)
+ }
+ Apol_Widget::setRegexpEntryValue $widgets(regexp) $vals(regexp:enable) $vals(regexp)
+}
+
+proc Apol_Analysis_transflow::_toggleAdvancedSelected {name1 name2 op} {
+ variable vals
+ variable widgets
+ if {$vals(advanced:enable)} {
+ $widgets(advanced) configure -state normal
+ } else {
+ $widgets(advanced) configure -state disabled
+ }
+}
+
+################# functions that do advanced filters #################
+
+proc Apol_Analysis_transflow::_createAdvancedDialog {} {
+ variable widgets
+ $widgets(advanced) configure -state disabled
+ destroy .transflow_adv
+ variable vals
+
+ # if a permap is not loaded then load the default permap
+ if {[ApolTop::is_policy_open] && ![Apol_Perms_Map::is_pmap_loaded]} {
+ if {![ApolTop::openDefaultPermMap]} {
+ return "This analysis requires that a permission map is loaded."
+ }
+ }
+
+ set d [Dialog .transflow_adv -modal none -separator 1 -title "Transitive Information Flow Advanced Filters" -parent .]
+ $d add -text "Close" -command [list Apol_Analysis_transflow::_closeAdvancedDialog $d]
+
+ set tf [TitleFrame [$d getframe].classes -text "Filter By Object Class Permissions"]
+ pack $tf -side top -expand 1 -fill both -padx 2 -pady 4
+ _createClassFilter [$tf getframe]
+
+ set tf [TitleFrame [$d getframe].types -text "Filter By Intermediate Types"]
+ pack $tf -side top -expand 1 -fill both -padx 2 -pady 4
+ _createIntermedFilter [$tf getframe]
+ set inc [$tf getframe].inc
+ set exc [$tf getframe].exc
+
+ set attrib [frame [$tf getframe].a]
+ grid $attrib - -
+ set attrib_enable [checkbutton $attrib.ae -anchor w \
+ -text "Filter by attribute" \
+ -variable Apol_Analysis_transflow::vals(intermed:attribenable)]
+ set attrib_box [ComboBox $attrib.ab -autopost 1 -entrybg white -width 16 \
+ -values $Apol_Types::attriblist \
+ -textvariable Apol_Analysis_transflow::vals(intermed:attrib)]
+ $attrib_enable configure -command \
+ [list Apol_Analysis_transflow::_attribEnabled $attrib_box]
+ # remove any old traces on the attribute before adding new ones
+ trace remove variable Apol_Analysis_transflow::vals(intermed:attrib) write \
+ [list Apol_Analysis_transflow::_attribChanged]
+ trace add variable Apol_Analysis_transflow::vals(intermed:attrib) write \
+ [list Apol_Analysis_transflow::_attribChanged]
+ pack $attrib_enable -side top -expand 0 -fill x -anchor sw -padx 5 -pady 2
+ pack $attrib_box -side top -expand 1 -fill x -padx 10
+ _attribEnabled $attrib_box
+
+ $d draw
+ $widgets(advanced) configure -state normal
+}
+
+proc Apol_Analysis_transflow::_closeAdvancedDialog {d} {
+ $d withdraw
+}
+
+proc Apol_Analysis_transflow::_createClassFilter {f} {
+ variable vals
+
+ set l1 [label $f.l1 -text "Object Classes"]
+ set l [label $f.l]
+ set vals(classes:title) "Permissions"
+ set l2 [label $f.l2 -textvariable Apol_Analysis_transflow::vals(classes:title)]
+ grid $l1 $l $l2 -sticky w
+
+ set classes [Apol_Widget::makeScrolledListbox $f.c -selectmode extended \
+ -height 12 -width 24 -listvar Apol_Analysis_transflow::vals(classes:displayed)]
+ set sw [ScrolledWindow $f.sw -auto both -bd 2 -relief groove]
+ set perms [ScrollableFrame $sw.perms -height 150 -width 250]
+ $sw setwidget $perms
+ bind $classes.lb <<ListboxSelect>> \
+ [list Apol_Analysis_transflow::_refreshPerm $classes $perms]
+ grid $classes x $sw -sticky nsew
+ update
+ grid propagate $sw 0
+
+ set bb [ButtonBox $f.bb -homogeneous 1 -spacing 4]
+ $bb add -text "Include All Perms" -width 16 -command [list Apol_Analysis_transflow::_setAllPerms $classes $perms 1]
+ $bb add -text "Exclude All Perms" -width 16 -command [list Apol_Analysis_transflow::_setAllPerms $classes $perms 0]
+ grid ^ x $bb -pady 4
+
+ set f [frame $f.f]
+ grid ^ x $f
+ grid configure $f -sticky ew
+ set cb [checkbutton $f.cb -text "Exclude permissions with weights below:" \
+ -variable Apol_Analysis_transflow::vals(classes:threshold_enable)]
+
+ set weight [spinbox $f.threshold -from 1 -to 10 -increment 1 \
+ -width 2 -bg white -justify right \
+ -textvariable Apol_Analysis_transflow::vals(classes:threshold)]
+ # remove any old traces on the threshold checkbutton befored adding new one
+ trace remove variable Apol_Analysis_transflow::vals(classes:threshold_enable) write \
+ [list Apol_Analysis_transflow::_thresholdChanged $weight]
+ trace add variable Apol_Analysis_transflow::vals(classes:threshold_enable) write \
+ [list Apol_Analysis_transflow::_thresholdChanged $weight]
+ pack $cb $weight -side left
+ _thresholdChanged $weight {} {} {}
+
+ grid columnconfigure $f 0 -weight 0
+ grid columnconfigure $f 1 -weight 0 -pad 4
+ grid columnconfigure $f 2 -weight 1
+}
+
+proc Apol_Analysis_transflow::_refreshPerm {classes perms} {
+ variable vals
+ focus $classes.lb
+ if {[$classes.lb curselection] == {}} {
+ return
+ }
+ set pf [$perms getframe]
+ foreach w [winfo children $pf] {
+ destroy $w
+ }
+
+ foreach {class foo} [$classes.lb get anchor] {break}
+ set i [$classes.lb index anchor]
+ set vals(classes:title) "Permissions for $class"
+
+ foreach perm_key [lsort [array names vals perms:$class:*]] {
+ foreach {foo bar perm} [split $perm_key :] {break}
+ set weight [$::ApolTop::policy get_permmap_weight $class $perm]
+ set l [label $pf.$perm:l -text $perm -anchor w]
+ set inc [checkbutton $pf.$perm:i -text "Include" \
+ -command [list Apol_Analysis_transflow::_togglePerm $class $i] \
+ -variable Apol_Analysis_transflow::vals(perms:$class:$perm)]
+ set w [label $pf.$perm:w -text "Weight: $weight"]
+ grid $l $inc $w -padx 2 -sticky w -pady 4
+ grid configure $w -ipadx 10
+ }
+ grid columnconfigure $pf 0 -minsize 100 -weight 1
+ $perms xview moveto 0
+ $perms yview moveto 0
+}
+
+proc Apol_Analysis_transflow::_togglePerm {class i} {
+ variable vals
+ set all_disabled 1
+ foreach perm_key [array names vals perms:$class:*] {
+ if {$vals($perm_key)} {
+ set all_disabled 0
+ break
+ }
+ }
+ if {$all_disabled} {
+ set vals(classes:displayed) [lreplace $vals(classes:displayed) $i $i "$class (excluded)"]
+ } else {
+ set vals(classes:displayed) [lreplace $vals(classes:displayed) $i $i $class]
+ }
+}
+
+proc Apol_Analysis_transflow::_setAllPerms {classes perms newValue} {
+ variable vals
+ foreach i [$classes.lb curselection] {
+ foreach {class foo} [split [$classes.lb get $i]] {break}
+ foreach perm_key [array names vals perms:$class:*] {
+ set vals($perm_key) $newValue
+ }
+ if {$newValue == 1} {
+ set vals(classes:displayed) [lreplace $vals(classes:displayed) $i $i $class]
+ } else {
+ set vals(classes:displayed) [lreplace $vals(classes:displayed) $i $i "$class (excluded)"]
+ }
+ }
+}
+
+proc Apol_Analysis_transflow::_thresholdChanged {w name1 name2 op} {
+ variable vals
+ if {$vals(classes:threshold_enable)} {
+ $w configure -state normal
+ } else {
+ $w configure -state disabled
+ }
+}
+
+proc Apol_Analysis_transflow::_createIntermedFilter {f} {
+ set l1 [label $f.l1 -text "Included Intermediate Types"]
+ set l2 [label $f.l2 -text "Excluded Intermediate Types"]
+ grid $l1 x $l2 -sticky w
+
+ set inc [Apol_Widget::makeScrolledListbox $f.inc -height 10 -width 24 \
+ -listvar Apol_Analysis_transflow::vals(intermed:inc) \
+ -selectmode extended -exportselection 0]
+ set exc [Apol_Widget::makeScrolledListbox $f.exc -height 10 -width 24 \
+ -listvar Apol_Analysis_transflow::vals(intermed:exc) \
+ -selectmode extended -exportselection 0]
+ set inc_lb [Apol_Widget::getScrolledListbox $inc]
+ set exc_lb [Apol_Widget::getScrolledListbox $exc]
+ set bb [ButtonBox $f.bb -homogeneous 1 -orient vertical -spacing 4]
+ $bb add -text "-->" -width 10 -command [list Apol_Analysis_transflow::_moveToExclude $inc_lb $exc_lb]
+ $bb add -text "<--" -width 10 -command [list Apol_Analysis_transflow::_moveToInclude $inc_lb $exc_lb]
+ grid $inc $bb $exc -sticky nsew
+
+ set inc_bb [ButtonBox $f.inc_bb -homogeneous 1 -spacing 4]
+ $inc_bb add -text "Select All" -command [list $inc_lb selection set 0 end]
+ $inc_bb add -text "Unselect" -command [list $inc_lb selection clear 0 end]
+ set exc_bb [ButtonBox $f.exc_bb -homogeneous 1 -spacing 4]
+ $exc_bb add -text "Select All" -command [list $exc_lb selection set 0 end]
+ $exc_bb add -text "Unselect" -command [list $exc_lb selection clear 0 end]
+ grid $inc_bb x $exc_bb -pady 4
+
+ grid columnconfigure $f 0 -weight 1 -uniform 0 -pad 2
+ grid columnconfigure $f 1 -weight 0 -pad 8
+ grid columnconfigure $f 2 -weight 1 -uniform 0 -pad 2
+}
+
+proc Apol_Analysis_transflow::_moveToExclude {inc exc} {
+ variable vals
+ if {[set selection [$inc curselection]] == {}} {
+ return
+ }
+ foreach i $selection {
+ lappend types [$inc get $i]
+ }
+ set vals(intermed:exc) [lsort [concat $vals(intermed:exc) $types]]
+ set vals(intermed:exc_all) [lsort [concat $vals(intermed:exc_all) $types]]
+ foreach t $types {
+ set i [lsearch $vals(intermed:inc) $t]
+ set vals(intermed:inc) [lreplace $vals(intermed:inc) $i $i]
+ set i [lsearch $vals(intermed:inc_all) $t]
+ set vals(intermed:inc_all) [lreplace $vals(intermed:inc_all) $i $i]
+ }
+ $inc selection clear 0 end
+ $exc selection clear 0 end
+}
+
+proc Apol_Analysis_transflow::_moveToInclude {inc exc} {
+ variable vals
+ if {[set selection [$exc curselection]] == {}} {
+ return
+ }
+ foreach i $selection {
+ lappend types [$exc get $i]
+ }
+ set vals(intermed:inc) [lsort [concat $vals(intermed:inc) $types]]
+ set vals(intermed:inc_all) [lsort [concat $vals(intermed:inc_all) $types]]
+ foreach t $types {
+ set i [lsearch $vals(intermed:exc) $t]
+ set vals(intermed:exc) [lreplace $vals(intermed:exc) $i $i]
+ set i [lsearch $vals(intermed:exc_all) $t]
+ set vals(intermed:exc_all) [lreplace $vals(intermed:exc_all) $i $i]
+ }
+ $inc selection clear 0 end
+ $exc selection clear 0 end
+}
+
+proc Apol_Analysis_transflow::_attribEnabled {cb} {
+ variable vals
+ if {$vals(intermed:attribenable)} {
+ $cb configure -state normal
+ _filterTypeLists $vals(intermed:attrib)
+ } else {
+ $cb configure -state disabled
+ _filterTypeLists ""
+ }
+}
+
+proc Apol_Analysis_transflow::_attribChanged {name1 name2 op} {
+ variable vals
+ if {$vals(intermed:attribenable)} {
+ _filterTypeLists $vals(intermed:attrib)
+ }
+}
+
+proc Apol_Analysis_transflow::_filterTypeLists {attrib} {
+ variable vals
+ if {$attrib != {}} {
+ set typesList {}
+ if {[Apol_Types::isAttributeInPolicy $attrib]} {
+ set qpol_type_datum [new_qpol_type_t $::ApolTop::qpolicy $attrib]
+ set i [$qpol_type_datum get_type_iter $::ApolTop::qpolicy]
+ foreach t [iter_to_list $i] {
+ set t [qpol_type_from_void $t]
+ lappend typesList [$t get_name $::ApolTop::qpolicy]
+ }
+ $i -acquire
+ $i -delete
+ }
+ if {$typesList == {}} {
+ # unknown attribute, so don't change listboxes
+ return
+ }
+ set vals(intermed:inc) {}
+ set vals(intermed:exc) {}
+ foreach t $typesList {
+ if {[lsearch $vals(intermed:inc_all) $t] >= 0} {
+ lappend vals(intermed:inc) $t
+ }
+ if {[lsearch $vals(intermed:exc_all) $t] >= 0} {
+ lappend vals(intermed:exc) $t
+ }
+ }
+ set vals(intermed:inc) [lsort $vals(intermed:inc)]
+ set vals(intermed:exc) [lsort $vals(intermed:exc)]
+ } else {
+ set vals(intermed:inc) $vals(intermed:inc_all)
+ set vals(intermed:exc) $vals(intermed:exc_all)
+ }
+}
+
+#################### functions that do analyses ####################
+
+proc Apol_Analysis_transflow::_checkParams {} {
+ variable vals
+ variable widgets
+ if {![ApolTop::is_policy_open]} {
+ return "No current policy file is opened."
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(type)]
+ if {[lindex $type 0] == {}} {
+ return "No type was selected."
+ }
+ if {![Apol_Types::isTypeInPolicy [lindex $type 0]]} {
+ return "[lindex $type 0] is not a type within the policy."
+ }
+ set vals(type) [lindex $type 0]
+ set vals(type:attrib) [lindex $type 1]
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$use_regexp && $regexp == {}} {
+ return "No regular expression provided."
+ }
+ set vals(regexp:enable) $use_regexp
+ set vals(regexp) $regexp
+
+ # if a permap is not loaded then load the default permap
+ if {![Apol_Perms_Map::is_pmap_loaded]} {
+ if {![ApolTop::openDefaultPermMap]} {
+ return "This analysis requires that a permission map is loaded."
+ }
+ apol_tcl_clear_info_string
+ }
+
+ if {$vals(advanced:enable)} {
+ if {$vals(intermed:inc_all) == {}} {
+ return "At least one intermediate type must be selected."
+ }
+ if {[lsearch $vals(intermed:exc_all) $vals(type)] >= 0} {
+ return "The starting type is on the excluded intermediate types list"
+ }
+ set num_perms 0
+ foreach perm_key [array names vals perms:*] {
+ if {$vals($perm_key)} {
+ set num_perms 1
+ break
+ }
+ }
+ if {$num_perms == 0} {
+ return "At least one permissions must be enabled."
+ }
+ }
+ return {} ;# all parameters passed, now ready to do search
+}
+
+proc Apol_Analysis_transflow::_analyze {} {
+ variable vals
+ if {$vals(regexp:enable)} {
+ set regexp $vals(regexp)
+ } else {
+ set regexp {}
+ }
+ set threshold {}
+ if {$vals(advanced:enable)} {
+ set intermed $vals(intermed:inc_all)
+ set classperms {}
+ foreach perm_key [array names vals perms:*] {
+ if {$vals($perm_key)} {
+ foreach {foo class perm} [split $perm_key :] {break}
+ lappend classperms $class $perm
+ }
+ }
+ if {$vals(classes:threshold_enable)} {
+ set threshold $vals(classes:threshold)
+ }
+ } else {
+ set intermed {}
+ set classperms {}
+ }
+
+ set q [new_apol_infoflow_analysis_t]
+ $q set_mode $::ApolTop::policy $::APOL_INFOFLOW_MODE_TRANS
+ $q set_dir $::ApolTop::policy $vals(dir)
+ $q set_type $::ApolTop::policy $vals(type)
+ foreach i $intermed {
+ $q append_intermediate $::ApolTop::policy $i
+ }
+ foreach {c p} $classperms {
+ $q append_class_perm $::ApolTop::policy $c $p
+ }
+ if {$threshold != {}} {
+ $q set_min_weight $::ApolTop::policy $threshold
+ }
+ $q set_result_regex $::ApolTop::policy $regexp
+ set results [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ return $results
+}
+
+proc Apol_Analysis_transflow::_analyzeMore {tree node} {
+ # disallow more analysis if this node is the same as its parent
+ set new_start [$tree itemcget $node -text]
+ if {[$tree itemcget [$tree parent $node] -text] == $new_start} {
+ return {}
+ }
+ set g [lindex [$tree itemcget top -data] 0]
+ $g do_more $::ApolTop::policy $new_start
+}
+
+################# functions that control analysis output #################
+
+proc Apol_Analysis_transflow::_createResultsDisplay {} {
+ variable vals
+
+ set f [Apol_Analysis::createResultTab "Trans Flow" [array get vals]]
+
+ set tree_tf [TitleFrame $f.left -text "Transitive Information Flow Tree"]
+ pack $tree_tf -side left -expand 0 -fill y -padx 2 -pady 2
+ set sw [ScrolledWindow [$tree_tf getframe].sw -auto both]
+ set tree [Tree [$sw getframe].tree -width 24 -redraw 1 -borderwidth 0 \
+ -highlightthickness 0 -showlines 1 -padx 0 -bg white]
+ $sw setwidget $tree
+ pack $sw -expand 1 -fill both
+
+ set res_tf [TitleFrame $f.right -text "Transitive Information Flow Results"]
+ pack $res_tf -side left -expand 1 -fill both -padx 2 -pady 2
+ set res [Apol_Widget::makeSearchResults [$res_tf getframe].res]
+ $res.tb tag configure title -font {Helvetica 14 bold}
+ $res.tb tag configure title_type -foreground blue -font {Helvetica 14 bold}
+ $res.tb tag configure find_more -underline 1
+ $res.tb tag configure subtitle -font {Helvetica 10 bold}
+ $res.tb tag configure num -foreground blue -font {Helvetica 10 bold}
+ $res.tb tag bind find_more <Button-1> [list Apol_Analysis_transflow::_findMore $res $tree]
+ $res.tb tag bind find_more <Enter> [list $res.tb configure -cursor hand2]
+ $res.tb tag bind find_more <Leave> [list $res.tb configure -cursor {}]
+ pack $res -expand 1 -fill both
+
+ $tree configure -selectcommand [list Apol_Analysis_transflow::_treeSelect $res]
+ $tree configure -opencmd [list Apol_Analysis_transflow::_treeOpen $tree]
+ return $f
+}
+
+proc Apol_Analysis_transflow::_treeSelect {res tree node} {
+ if {$node != {}} {
+ $res.tb configure -state normal
+ $res.tb delete 0.0 end
+ set data [$tree itemcget $node -data]
+ if {[string index $node 0] == "y"} {
+ _renderResultsTransFlow $res $tree $node [lindex $data 1]
+ } else {
+ # an informational node, whose data has already been rendered
+ eval $res.tb insert end [lindex $data 1]
+ }
+ $res.tb configure -state disabled
+ }
+}
+
+proc Apol_Analysis_transflow::_treeOpen {tree node} {
+ foreach {is_expanded results} [$tree itemcget $node -data] {break}
+ if {[string index $node 0] == "y" && !$is_expanded} {
+ Apol_Progress_Dialog::wait "Transitive Information Flow Analysis" \
+ "Performing Transitive Information Flow Analysis..." \
+ {
+ set new_results [_analyzeMore $tree $node]
+ # mark this node as having been expanded
+ $tree itemconfigure $node -data [list 1 $results]
+ if {$new_results != {}} {
+ _createResultsNodes $tree $node $new_results 1
+ $new_results -acquire
+ $new_results -delete
+ }
+ }
+ }
+}
+
+proc Apol_Analysis_transflow::_clearResultsDisplay {f} {
+ variable vals
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+ $tree delete [$tree nodes root]
+ Apol_Widget::clearSearchResults $res
+ Apol_Analysis::setResultTabCriteria [array get vals]
+}
+
+
+proc Apol_Analysis_transflow::_renderResults {f results} {
+ variable vals
+
+ set graph_handler [$results extract_graph]
+ $graph_handler -acquire ;# let Tcl's GC destroy graph when this tab closes
+ set results_list [$results extract_result_vector]
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+
+ $tree insert end root top -text $vals(type) -open 1 -drawcross auto
+ set top_text [_renderTopText]
+ $tree itemconfigure top -data [list $graph_handler $top_text]
+
+ _createResultsNodes $tree top $results_list 1
+ $tree selection set top
+ $tree opentree top 0
+ $tree see top
+
+ $results_list -acquire
+ $results_list -delete
+}
+
+proc Apol_Analysis_transflow::_renderTopText {} {
+ variable vals
+
+ set top_text [list "Transitive Information Flow Analysis: Starting type: " title]
+ lappend top_text $vals(type) title_type \
+ "\n\n" title \
+ "This tab provides the results of a Transitive Information Flow
+analysis beginning from the starting type selected above. The results
+of the analysis are presented in tree form with the root of the tree
+(this node) being the start point for the analysis.
+
+\nEach child node in the tree represents a type in the current policy
+for which there is a transitive information flow to or from (depending
+on your selection above) its parent node.
+
+\nNOTE: For any given generation, if the parent and the child are the
+same, you cannot open the child. This avoids cyclic analyses." {}
+}
+
+# If do_expand is zero, then generate result nodes for only the first
+# target type of $results. This is needed by two types relationship
+# analysis.
+proc Apol_Analysis_transflow::_createResultsNodes {tree parent_node results do_expand} {
+ set all_targets {}
+ set info_list [infoflow_result_vector_to_list $results]
+ set results_processed 0
+ foreach r $info_list {
+ apol_tcl_set_info_string $::ApolTop::policy "Processing result $results_processed of [llength $info_list]"
+
+ if {$do_expand} {
+ set target [[$r get_end_type] get_name $::ApolTop::qpolicy]
+ } else {
+ set first_target [[lindex $info_list 0] get_end_type]
+ set target [$first_target get_name $::ApolTop::qpolicy]
+ }
+ set flow_dir [$r get_dir]
+ set length [$r get_length]
+ set steps_v [$r get_steps]
+
+ lappend all_targets $target
+ lappend paths($target) [list $length $steps_v]
+ incr results_processed
+ }
+
+ set all_targets [lsort -uniq $all_targets]
+ apol_tcl_set_info_string $::ApolTop::policy "Displaying [llength $all_targets] result(s)"
+ update idle
+
+ foreach t $all_targets {
+ set sorted_paths {}
+ foreach path [lsort -uniq [lsort -index 0 -integer $paths($t)]] {
+ set step_v [lindex $path 1]
+ set p {}
+ if {$flow_dir == $::APOL_INFOFLOW_IN} {
+ # flip the steps around
+ for {set i [expr {[$step_v get_size] - 1}]} {$i >= 0} {incr i -1} {
+ set r [apol_infoflow_step_from_void [$step_v get_element $i]]
+ lappend p [_infoflow_step_to_list $r]
+ }
+ } else {
+ for {set i 0} {$i < [$step_v get_size]} {incr i} {
+ set r [apol_infoflow_step_from_void [$step_v get_element $i]]
+ lappend p [_infoflow_step_to_list $r]
+ }
+ }
+ lappend sorted_paths $p
+ }
+ set data [list $flow_dir $sorted_paths]
+ $tree insert end $parent_node y\#auto -text $t -drawcross allways \
+ -data [list 0 $data]
+ }
+}
+
+proc Apol_Analysis_transflow::_infoflow_step_to_list {step} {
+ set start [[$step get_start_type] get_name $::ApolTop::qpolicy]
+ set end [[$step get_end_type] get_name $::ApolTop::qpolicy]
+ set weight [$step get_weight]
+ set rules [avrule_vector_to_list [$step get_rules]]
+ list $start $end $weight $rules
+}
+
+proc Apol_Analysis_transflow::_renderResultsTransFlow {res tree node data} {
+ set parent_name [$tree itemcget [$tree parent $node] -text]
+ set name [$tree itemcget $node -text]
+ foreach {flow_dir paths} $data {break}
+ switch -- $flow_dir [list \
+ $::APOL_INFOFLOW_IN {
+ $res.tb insert end "Information flows to " title \
+ $parent_name title_type \
+ " from " title \
+ $name title_type
+ } \
+ $::APOL_INFOFLOW_OUT {
+ $res.tb insert end "Information flows from " title \
+ $parent_name title_type \
+ " to " title \
+ $name title_type
+ } \
+ ]
+ $res.tb insert end " (" title \
+ "Find more flows" {title_type find_more} \
+ ")\n\n" title \
+ "Apol found the following number of information flows: " subtitle \
+ [llength $paths] num \
+ "\n" subtitle
+ set path_num 1
+ foreach path $paths {
+ $res.tb insert end "\n" {}
+ _renderPath $res $path_num $path
+ incr path_num
+ }
+}
+
+proc Apol_Analysis_transflow::_renderPath {res path_num path} {
+ $res.tb insert end "Flow " subtitle \
+ $path_num num \
+ " requires " subtitle \
+ [llength $path] num \
+ " steps(s).\n" subtitle \
+ " " {}
+ $res.tb insert end [lindex $path 0 0] subtitle \
+ " -> " {} \
+ [lindex $path 0 1] subtitle
+ foreach step [lrange $path 1 end] {
+ $res.tb insert end " -> " {} \
+ [lindex $step 1] subtitle
+ }
+ $res.tb insert end \n {}
+ foreach steps $path {
+ set rules [lindex $steps 3]
+ set v [new_apol_vector_t]
+ $v append [lindex $rules 0]
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+
+ set v [new_apol_vector_t]
+ foreach r [lrange $rules 1 end] {
+ $v append $r
+ }
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 10 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+}
+
+#################### procedures to find further flows ####################
+
+proc Apol_Analysis_transflow::_findMore {res tree} {
+ set node [$tree selection get]
+ set start [$tree itemcget [$tree parent $node] -text]
+ set end [$tree itemcget $node -text]
+
+ set d [Dialog .trans_more -cancel 1 -default 0 -modal local -parent . \
+ -separator 1 -title "Find More Flows"]
+ $d add -text Find -command [list Apol_Analysis_transflow::_verifyFindMore $d]
+ $d add -text Cancel
+
+ set f [$d getframe]
+ set l1 [label $f.l1 -text "Source: $start"]
+ set l2 [label $f.l2 -text "Target: $end"]
+ set time_f [frame $f.time]
+ set path_f [frame $f.path]
+ pack $l1 $l2 $time_f $path_f -anchor w -padx 8 -pady 4
+
+ set t1 [label $time_f.t1 -text "Time limit: "]
+ set e1 [entry $time_f.e1 -textvariable Apol_Analysis_transflow::vals(find_more:hours) -width 5 -justify right -bg white]
+ set t2 [label $time_f.t2 -text "Hour(s) "]
+ set e2 [entry $time_f.e2 -textvariable Apol_Analysis_transflow::vals(find_more:minutes) -width 5 -justify right -bg white]
+ set t3 [label $time_f.t3 -text "Minute(s) "]
+ set e3 [entry $time_f.e3 -textvariable Apol_Analysis_transflow::vals(find_more:seconds) -width 5 -justify right -bg white]
+ set t4 [label $time_f.t4 -text "Second(s) "]
+ pack $t1 $e1 $t2 $e2 $t3 $e3 $t4 -side left
+
+ set t1 [label $path_f.t1 -text "Limit by these number of flows: "]
+ set e1 [entry $path_f.e1 -textvariable Apol_Analysis_transflow::vals(find_more:limit) -width 5 -justify right -bg white]
+ pack $t1 $e1 -side left
+
+ set retval [$d draw]
+ destroy .trans_more
+ if {$retval == 0} {
+ set graph_handler [lindex [$tree itemcget top -data] 0]
+ $graph_handler trans_further_prepare $::ApolTop::policy $start $end
+ _doFindMore $res $tree $node
+ }
+}
+
+proc Apol_Analysis_transflow::_verifyFindMore {d} {
+ variable vals
+ set message {}
+ if {[set hours [string trim $vals(find_more:hours)]] == {}} {
+ set hours 0
+ }
+ if {[set minutes [string trim $vals(find_more:minutes)]] == {}} {
+ set minutes 0
+ }
+ if {[set seconds [string trim $vals(find_more:seconds)]] == {}} {
+ set seconds 0
+ }
+ set path_limit [string trim $vals(find_more:limit)]
+ if {![string is integer $hours] || $hours > 24 || $hours < 0} {
+ set message "Invalid hours limit input. Must be between 0-24 inclusive."
+ } elseif {![string is integer $minutes] || $minutes > 59 || $minutes < 0} {
+ set message "Invalid minutes limit input. Must be between 0-59 inclusive."
+ } elseif {![string is integer $seconds] || $seconds > 59 || $seconds < 0} {
+ set message "Invalid seconds limit input. Must be between 0-59 inclusive."
+ } elseif {$path_limit == {} && $hours == 0 && $minutes == 0 && $seconds == 0} {
+ set message "You must specify a time limit."
+ } elseif {$path_limit != {} && (![string is integer $path_limit] || $path_limit < 0)} {
+ set message "Number of flows cannot be less than 1."
+ }
+ if {$message != {}} {
+ tk_messageBox -icon error -type ok -title "Find More Flows" -message $message
+ } else {
+ $d enddialog 0
+ }
+}
+
+proc Apol_Analysis_transflow::_doFindMore {res tree node} {
+ variable vals
+ if {[set hours [string trim $vals(find_more:hours)]] == {}} {
+ set hours 0
+ }
+ if {[set minutes [string trim $vals(find_more:minutes)]] == {}} {
+ set minutes 0
+ }
+ if {[set seconds [string trim $vals(find_more:seconds)]] == {}} {
+ set seconds 0
+ }
+ set path_limit [string trim $vals(find_more:limit)]
+ if {$hours != 0 || $minutes != 0 || $seconds != 0} {
+ set time_limit [expr {$hours * 3600 + $minutes * 60 + $seconds}]
+ set time_limit_str [format " elapsed out of %02d:%02d:%02d" $hours $minutes $seconds]
+ } else {
+ set time_limit {}
+ set time_limit_str {}
+ }
+ if {$path_limit != {}} {
+ set path_limit_str " out of $path_limit"
+ } else {
+ set path_limit 0
+ set path_limit_str {}
+ }
+ set vals(find_more:abort) 0
+ set vals(find_more:searches_text) {}
+ set vals(find_more:searches_done) -1
+
+ set d [ProgressDlg .trans_domore -parent . -title "Find Results" \
+ -width 40 -height 5 \
+ -textvariable Apol_Analysis_transflow::vals(find_more:searches_text) \
+ -variable Apol_Analysis_transflow::vals(find_more:searches_done) \
+ -stop Stop \
+ -command [list set Apol_Analysis_transflow::vals(find_more:abort) 1]]
+ set graph_handler [lindex [$tree itemcget top -data] 0]
+ set start_time [clock seconds]
+ set elapsed_time 0
+ set path_found 0
+ set v NULL
+ while {1} {
+ set elapsed_time [expr {[clock seconds] - $start_time}]
+ set vals(find_more:searches_text) "Finding more flows:\n\n"
+ append vals(find_more:searches_text) " Time: [clock format $elapsed_time -format "%H:%M:%S" -gmt 1]$time_limit_str\n\n"
+ append vals(find_more:searches_text) " Flows: found $path_found$path_limit_str"
+ update
+ set v [$graph_handler trans_further_next $::ApolTop::policy $v]
+ set path_found [$v get_size]
+ if {($time_limit != {} && $elapsed_time >= $time_limit) || \
+ ($path_limit != 0 && $path_found > $path_limit) || \
+ $vals(find_more:abort)} {
+ break
+ }
+ }
+ set vals(find_more:searches_text) "Rendering $path_found flow(s)."
+ update idletasks
+
+ $res.tb configure -state normal
+ $res.tb delete 0.0 end
+ set parent_name [$tree itemcget [$tree parent $node] -text]
+ set name [$tree itemcget $node -text]
+ set flow_dir [lindex [$tree itemcget $node -data] 1 0]
+ switch -- $flow_dir [list \
+ $::APOL_INFOFLOW_IN {
+ $res.tb insert end "More information flows to " title \
+ $parent_name title_type \
+ " from " title \
+ $name title_type
+ } \
+ $::APOL_INFOFLOW_OUT {
+ $res.tb insert end "More information flows from " title \
+ $parent_name title_type \
+ " to " title \
+ $name title_type
+ } \
+ ]
+ $res.tb insert end " (" title \
+ "Find more flows" {title_type find_more} \
+ ")\n\n" title \
+ "Time: " subtitle \
+ [clock format $elapsed_time -format "%H:%M:%S" -gmt 1] subtitle \
+ [format " out of %02d:%02d:%02d" $hours $minutes $seconds] subtitle \
+ "\n\nApol found the following number of information flows: " subtitle \
+ $path_found num \
+ " out of " subtitle \
+ $path_limit num \
+ "\n" subtitle
+
+ set results {}
+ foreach r [infoflow_result_vector_to_list $v] {
+ set length [$r get_length]
+ set steps_v [$r get_steps]
+ lappend results [list $length $steps_v]
+ }
+
+ set path_num 1
+ foreach r [lsort -index 0 -integer $results] {
+ set steps_v [lindex $r 1]
+ set sorted_path {}
+ if {$flow_dir == $::APOL_INFOFLOW_IN} {
+ # flip the steps around
+ for {set i [expr {[$steps_v get_size] - 1}]} {$i >= 0} {incr i -1} {
+ set s [apol_infoflow_step_from_void [$steps_v get_element $i]]
+ lappend sorted_path [_infoflow_step_to_list $s]
+ }
+ } else {
+ for {set i 0} {$i < [$steps_v get_size]} {incr i} {
+ set s [apol_infoflow_step_from_void [$steps_v get_element $i]]
+ lappend sorted_path [_infoflow_step_to_list $s]
+ }
+ }
+ $res.tb insert end "\n" {}
+ _renderPath $res $path_num $sorted_path
+ incr path_num
+ }
+
+ $res.tb configure -state disabled
+ destroy $d
+ $v -acquire
+ $v -delete
+}
diff --git a/apol/types_relation_help.txt b/apol/types_relation_help.txt
new file mode 100644
index 0000000..3217702
--- /dev/null
+++ b/apol/types_relation_help.txt
@@ -0,0 +1,53 @@
+An overview of types relationship summary analysis
+
+
+Understanding types relationship summary analysis
+-------------------------------------------------
+The types relationship summary analysis in apol is a convenience
+mechanism to allow a user to quickly do several queries and analyses
+already in present in apol to understand the relationship between two
+types. It is meant to quickly display the relationship between two
+types and therefore does not include all of the options present in the
+standard queries and analyses. The analyses are grouped into two
+categories: Basic and Analysis.
+
+
+Basic
+-----
+The basic group includes several rule searches that can be performed
+using the Policy Rules tab.
+
+ Common Attributes: the attributes common to both types.
+
+ Common Roles: the roles to which both types are assigned.
+
+ Common Users: the users allowed associate with roles to which both
+ types are assigned.
+
+ Similar Access to Resources: object types to which both types have
+ some access.
+
+ Dissimilar Access to Resources: object types to which one type has
+ some access but the other type has none.
+
+ TE Allow Rules: rules that provide direct access between both types.
+
+ Type Transition / Change Rules: type_* rules that allow transitions
+ between both types.
+
+
+Analysis
+--------
+The analysis group includes several other analyses that can be
+performed using the Analysis tab.
+
+ Direct Flows Between A and B: direct information flow analysis between
+ the two types.
+
+ Transitive Flows A->B: transitive information flows from type A to B.
+
+ Transitive Flows B->A: transitive information flows from type B to A.
+
+ Domain Transitions A->B: domain transitions allowed from type A to B.
+
+ Domain Transitions B->A: domain transitions allowed from type B to A.
diff --git a/apol/types_relation_module.tcl b/apol/types_relation_module.tcl
new file mode 100644
index 0000000..32924c5
--- /dev/null
+++ b/apol/types_relation_module.tcl
@@ -0,0 +1,770 @@
+# Copyright (C) 2004-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# This module implements the two types relationship analysis interface.
+
+namespace eval Apol_Analysis_tra {
+ variable vals
+ variable widgets
+ Apol_Analysis::registerAnalysis "Apol_Analysis_tra" "Types Relationship Summary"
+}
+
+proc Apol_Analysis_tra::create {options_frame} {
+ variable vals
+ variable widgets
+
+ _reinitializeVals
+
+ set req_tf [TitleFrame $options_frame.req -text "Required Parameters"]
+ pack $req_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ set fA [frame [$req_tf getframe].fA]
+ pack $fA -side left -anchor nw -padx 2
+ set lA [label $fA.l -text "Type A"]
+ pack $lA -anchor w
+ set widgets(typeA) [Apol_Widget::makeTypeCombobox $fA.t -width 19]
+ pack $widgets(typeA)
+ set fB [frame [$req_tf getframe].fB]
+ pack $fB -side left -anchor nw -padx 2
+ set lB [label $fB.l -text "Type B"]
+ pack $lB -anchor w
+ set widgets(typeB) [Apol_Widget::makeTypeCombobox $fB.t -width 19]
+ pack $widgets(typeB)
+
+ set basic_tf [TitleFrame $options_frame.basic -text "Basic Relationships"]
+ pack $basic_tf -side left -padx 2 -pady 2 -expand 0 -fill y
+ foreach {t a v} [list \
+ "Common attributes" attribs $::APOL_TYPES_RELATION_COMMON_ATTRIBS \
+ "Common roles" roles $::APOL_TYPES_RELATION_COMMON_ROLES \
+ "Common users" users $::APOL_TYPES_RELATION_COMMON_USERS \
+ "Similar access to resources" similars $::APOL_TYPES_RELATION_SIMILAR_ACCESS \
+ "Dissimilar access to resources" dissimilars $::APOL_TYPES_RELATION_DISSIMILAR_ACCESS \
+ "TE allow rules" allows $::APOL_TYPES_RELATION_ALLOW_RULES \
+ "Type transition/change rules" typerules $::APOL_TYPES_RELATION_TYPE_RULES] {
+ set cb [checkbutton [$basic_tf getframe].$v -text $t \
+ -variable Apol_Analysis_tra::vals(run:$a) \
+ -onvalue $v -offvalue 0]
+ pack $cb -anchor w
+ }
+
+ set an_tf [TitleFrame $options_frame.an -text "Analysis Relationships"]
+ pack $an_tf -side left -padx 2 -pady 2 -expand 1 -fill both
+ foreach {t a v} [list \
+ "Direct flows between A and B" direct $::APOL_TYPES_RELATION_DIRECT_FLOW \
+ "Transitive flows A -> B" transAB $::APOL_TYPES_RELATION_TRANS_FLOW_AB \
+ "Transitive flows B -> A" transBA $::APOL_TYPES_RELATION_TRANS_FLOW_BA \
+ "Domain transitions A -> B" domainAB $::APOL_TYPES_RELATION_DOMAIN_TRANS_AB \
+ "Domain transitions B -> A" domainBA $::APOL_TYPES_RELATION_DOMAIN_TRANS_BA] {
+ set cb [checkbutton [$an_tf getframe].$v -text $t \
+ -variable Apol_Analysis_tra::vals(run:$a) \
+ -onvalue $v -offvalue 0]
+ pack $cb -anchor w
+ }
+}
+
+proc Apol_Analysis_tra::open {} {
+ variable widgets
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(typeA)
+ Apol_Widget::resetTypeComboboxToPolicy $widgets(typeB)
+}
+
+proc Apol_Analysis_tra::close {} {
+ variable widgets
+ _reinitializeVals
+ _reinitializeWidgets
+ Apol_Widget::clearTypeCombobox $widgets(typeA)
+ Apol_Widget::clearTypeCombobox $widgets(typeB)
+}
+
+proc Apol_Analysis_tra::getInfo {} {
+ return "The types relationship summary analysis in Apol is a convenience
+mechanism to allow a user to quickly do several queries and analyses
+already in present in Apol to understand the relationship between two
+types. This is meant to quickly display the relationship between two
+types and therefore does not include all of the options present in the
+standard queries and analyses.
+
+\nFor additional help on this topic select \"Types Relationship Summary
+Analysis\" from the help menu."
+}
+
+proc Apol_Analysis_tra::newAnalysis {} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ set f [_createResultsDisplay]
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+
+proc Apol_Analysis_tra::updateAnalysis {f} {
+ if {[set rt [_checkParams]] != {}} {
+ return $rt
+ }
+ set results [_analyze]
+ _clearResultsDisplay $f
+ _renderResults $f $results
+ $results -acquire
+ $results -delete
+ return {}
+}
+
+proc Apol_Analysis_tra::reset {} {
+ _reinitializeVals
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_tra::switchTab {query_options} {
+ variable vals
+ variable widgets
+ array set vals $query_options
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_tra::saveQuery {channel} {
+ variable vals
+ variable widgets
+ foreach {key value} [array get vals] {
+ puts $channel "$key $value"
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(typeA)]
+ puts $channel "typeA [lindex $type 0]"
+ puts $channel "typeA:attrib [lindex $type 1]"
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(typeB)]
+ puts $channel "typeB [lindex $type 0]"
+ puts $channel "typeB:attrib [lindex $type 1]"
+}
+
+proc Apol_Analysis_tra::loadQuery {channel} {
+ variable vals
+
+ set classes_exc {}
+ set subjects_exc {}
+ while {[gets $channel line] >= 0} {
+ set line [string trim $line]
+ # Skip empty lines and comments
+ if {$line == {} || [string index $line 0] == "#"} {
+ continue
+ }
+ set key {}
+ set value {}
+ regexp -line -- {^(\S+)( (.+))?} $line -> key --> value
+ set vals($key) $value
+ }
+ _reinitializeWidgets
+}
+
+proc Apol_Analysis_tra::getTextWidget {tab} {
+ return [$tab.right getframe].res.tb
+}
+
+
+#################### private functions below ####################
+
+proc Apol_Analysis_tra::_reinitializeVals {} {
+ variable vals
+
+ set vals(run:attribs) $::APOL_TYPES_RELATION_COMMON_ATTRIBS
+ set vals(run:roles) $::APOL_TYPES_RELATION_COMMON_ROLES
+ set vals(run:users) $::APOL_TYPES_RELATION_COMMON_USERS
+ array set vals {
+ typeA {} typeA:attrib {}
+ typeB {} typeB:attrib {}
+
+ run:similars 0
+ run:dissimilars 0
+ run:allows 0
+ run:typerules 0
+
+ run:direct 0
+ run:transAB 0
+ run:transBA 0
+ run:domainAB 0
+ run:domainBA 0
+ }
+}
+
+proc Apol_Analysis_tra::_reinitializeWidgets {} {
+ variable vals
+ variable widgets
+
+ if {$vals(typeA:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(typeA) [list $vals(typeA) $vals(typeA:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(typeA) $vals(typeA)
+ }
+ if {$vals(typeB:attrib) != {}} {
+ Apol_Widget::setTypeComboboxValue $widgets(typeB) [list $vals(typeB) $vals(typeB:attrib)]
+ } else {
+ Apol_Widget::setTypeComboboxValue $widgets(typeB) $vals(typeB)
+ }
+}
+
+#################### functions that do analyses ####################
+
+proc Apol_Analysis_tra::_checkParams {} {
+ variable vals
+ variable widgets
+ if {![ApolTop::is_policy_open]} {
+ return "No current policy file is opened."
+ }
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(typeA)]
+ if {[lindex $type 0] == {}} {
+ return "No type was selected for type A."
+ }
+ if {![Apol_Types::isTypeInPolicy [lindex $type 0]]} {
+ return "[lindex $type 0] is not a type within the policy."
+ }
+ set vals(typeA) [lindex $type 0]
+ set vals(typeA:attrib) [lindex $type 1]
+ set type [Apol_Widget::getTypeComboboxValueAndAttrib $widgets(typeB)]
+ if {[lindex $type 0] == {}} {
+ return "No type was selected for type B."
+ }
+ if {![Apol_Types::isTypeInPolicy [lindex $type 0]]} {
+ return "[lindex $type 0] is not a type within the policy."
+ }
+ set vals(typeB) [lindex $type 0]
+ set vals(typeB:attrib) [lindex $type 1]
+ set analysis_selected 0
+ foreach key [array names vals run:*] {
+ if {$vals($key)} {
+ # if a permap is not loaded then load the default permap
+ if {($key == "run:direct" || [string match run:trans* $key]) && \
+ ![Apol_Perms_Map::is_pmap_loaded]} {
+ if {![ApolTop::openDefaultPermMap]} {
+ return "This analysis requires that a permission map is loaded."
+ }
+ apol_tcl_clear_info_string
+ }
+ set analysis_selected 1
+ }
+ }
+ if {!$analysis_selected} {
+ return "At least one analysis must be selected."
+ }
+ return {} ;# all parameters passed, now ready to do search
+}
+
+proc Apol_Analysis_tra::_analyze {} {
+ variable vals
+ set q [new_apol_types_relation_analysis_t]
+ $q set_first_type $::ApolTop::policy $vals(typeA)
+ $q set_other_type $::ApolTop::policy $vals(typeB)
+ set analyses 0
+ foreach key [array names vals run:*] {
+ set analyses [expr {$analyses | $vals($key)}]
+ }
+ $q set_analyses $::ApolTop::policy $analyses
+
+ set results [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ return $results
+}
+
+################# functions that control analysis output #################
+
+proc Apol_Analysis_tra::_createResultsDisplay {} {
+ variable vals
+
+ set f [Apol_Analysis::createResultTab "Types Relationship" [array get vals]]
+
+ set tree_tf [TitleFrame $f.left -text "Types Relationship Results"]
+ pack $tree_tf -side left -expand 0 -fill y -padx 2 -pady 2
+ set sw [ScrolledWindow [$tree_tf getframe].sw -auto both]
+ set tree [Tree [$sw getframe].tree -width 24 -redraw 1 -borderwidth 0 \
+ -highlightthickness 0 -showlines 1 -padx 0 -bg white]
+ $sw setwidget $tree
+ pack $sw -expand 1 -fill both
+
+ set res_tf [TitleFrame $f.right -text "Types Relationship Information"]
+ pack $res_tf -side left -expand 1 -fill both -padx 2 -pady 2
+ set res [Apol_Widget::makeSearchResults [$res_tf getframe].res]
+ $res.tb tag configure title -font {Helvetica 14 bold}
+ $res.tb tag configure title_type -foreground blue -font {Helvetica 14 bold}
+ $res.tb tag configure subtitle -font {Helvetica 10 bold}
+ $res.tb tag configure subtitle_dir -foreground blue -font {Helvetica 10 bold}
+ $res.tb tag configure num -foreground blue -font {Helvetica 10 bold}
+ pack $res -expand 1 -fill both
+
+ update
+ grid propagate $sw 0
+ $tree configure -selectcommand [list Apol_Analysis_tra::_treeSelect $res]
+ return $f
+}
+
+proc Apol_Analysis_tra::_treeSelect {res tree node} {
+ if {$node != {}} {
+ $res.tb configure -state normal
+ $res.tb delete 0.0 end
+ set data [$tree itemcget $node -data]
+ set name [$tree itemcget $node -text]
+ if {[set parent [$tree parent $node]] != "root"} {
+ set parent_name [$tree itemcget $parent -text]
+ set parent_data [$tree itemcget $parent -data]
+ }
+ switch -glob -- $node {
+ pre:* {
+ # an informational node, whose data has already been rendered
+ eval $res.tb insert end $data
+ }
+ simtitle {
+ _showSimilarTitle $res $data
+ }
+ sim:* {
+ _showSimilar $res $name $parent_data $data
+ }
+ distitle {
+ _showDissimilarTitle $res $data
+ }
+ dissubtitle* {
+ _showDissimilarSubtitle $res $data
+ }
+ dis:* {
+ _showDissimilar $res $name $parent_name $data
+ }
+ allow {
+ _showAllows $res $data
+ }
+ typerules {
+ _showTypeRules $res $data
+ }
+ x* {
+ _showDirectFlow $res $data
+ }
+ y* {
+ _showTransFlow $res $data
+ }
+ f:* {
+ _showDTA $res $data
+ }
+ }
+ $res.tb configure -state disabled
+ }
+}
+
+proc Apol_Analysis_tra::_clearResultsDisplay {f} {
+ variable vals
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+ $tree delete [$tree nodes root]
+ Apol_Widget::clearSearchResults $res
+ Apol_Analysis::setResultTabCriteria [array get vals]
+}
+
+proc Apol_Analysis_tra::_renderResults {f results} {
+ variable vals
+
+ set tree [[$f.left getframe].sw getframe].tree
+ set res [$f.right getframe].res
+ if {$vals(run:attribs)} {
+ _renderCommon Attributes $tree $results get_attributes attr_vector_to_list
+ }
+ if {$vals(run:roles)} {
+ _renderCommon Roles $tree $results get_roles role_vector_to_list
+ }
+ if {$vals(run:users)} {
+ _renderCommon Users $tree $results get_users user_vector_to_list
+ }
+ if {$vals(run:similars)} {
+ _renderSimilars $tree $results
+ }
+ if {$vals(run:dissimilars)} {
+ _renderDissimilars $tree $results
+ }
+ if {$vals(run:allows)} {
+ _renderAllows $tree $results
+ }
+ if {$vals(run:typerules)} {
+ _renderTypeRules $tree $results
+ }
+ if {$vals(run:direct)} {
+ _renderDirectFlow $tree $results
+ }
+ if {$vals(run:transAB)} {
+ _renderTransFlow 0 $tree $results
+ }
+ if {$vals(run:transBA)} {
+ _renderTransFlow 1 $tree $results
+ }
+ if {$vals(run:domainAB)} {
+ _renderDTA 0 $tree $results
+ }
+ if {$vals(run:domainBA)} {
+ _renderDTA 1 $tree $results
+ }
+ set first_node [$tree nodes root 0]
+ $tree selection set $first_node
+ $tree see $first_node
+}
+
+proc Apol_Analysis_tra::_renderCommon {title tree results func convert_func} {
+ set names [$convert_func [$results $func]]
+ set text [list "Common $title ([llength $names]):\n\n" title]
+ foreach n [lsort $names] {
+ lappend text "$n\n" {}
+ }
+ $tree insert end root pre:$title -text "Common $title" -data $text
+}
+
+# Convert a vector of apol_types_relation_access_t pointers into a
+# list of access tuples. Each tuple is:
+#
+# type name
+# list of avrules
+proc Apol_Analysis_tra::_types_relation_access_vector_to_list {v} {
+ set l {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set a [apol_types_relation_access_from_void [$v get_element $i]]
+ set type [[$a get_type] get_name $::ApolTop::qpolicy]
+ set rules [avrule_vector_to_list [$a get_rules]]
+ lappend l [list $type $rules]
+ }
+ set l
+}
+
+proc Apol_Analysis_tra::_renderSimilars {tree results} {
+ variable vals
+ set simA [_types_relation_access_vector_to_list [$results get_similar_first]]
+ set simB [_types_relation_access_vector_to_list [$results get_similar_other]]
+ set data [list $vals(typeA) $vals(typeB) [llength $simA]]
+ $tree insert end root simtitle -text "Similar access to resources" -data $data -drawcross allways
+ foreach accessA [lsort -index 0 $simA] accessB [lsort -index 0 $simB] {
+ set type [lindex $accessA 0]
+ set rulesA [lindex $accessA 1]
+ set rulesB [lindex $accessB 1]
+ $tree insert end simtitle sim:$type -text $type -data [list $rulesA $rulesB]
+ }
+}
+
+proc Apol_Analysis_tra::_showSimilarTitle {res data} {
+ foreach {typeA typeB numTypes} $data {break}
+ $res.tb insert end $typeA title_type \
+ " and " title \
+ $typeB title_type \
+ " access $numTypes common type(s).\n\n" title \
+ "Open the subtree for this item to see the list of common types that
+can be accessed. You may then select a type from the subtree to see
+the allow rules which provide the access." {}
+}
+
+proc Apol_Analysis_tra::_showSimilar {res name parent_data data} {
+ foreach {typeA typeB} $parent_data {rulesA rulesB} $data {break}
+ $res.tb insert end $typeA title_type \
+ " accesses " title \
+ $name title_type \
+ ":\n\n" title
+ set v [new_apol_vector_t]
+ foreach r $rulesA {
+ $v append $r
+ }
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 2 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+
+ $res.tb insert end "\n" title \
+ $typeB title_type \
+ " accesses " title \
+ $name title_type \
+ ":\n\n" title
+ set v [new_apol_vector_t]
+ foreach r $rulesB {
+ $v append $r
+ }
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 2 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_Analysis_tra::_renderDissimilars {tree results} {
+ variable vals
+ set disA [_types_relation_access_vector_to_list [$results get_dissimilar_first]]
+ set disB [_types_relation_access_vector_to_list [$results get_dissimilar_other]]
+ set data [list $vals(typeA) $vals(typeB)]
+ $tree insert end root distitle -text "Dissimilar access to resources" -data $data
+
+ set data [list $vals(typeA) $vals(typeB) [llength $disA]]
+ $tree insert end distitle dissubtitleA -text $vals(typeA) -data $data -drawcross allways
+ foreach access [lsort -index 0 $disA] {
+ set type [lindex $access 0]
+ set rules [lindex $access 1]
+ $tree insert end dissubtitleA dis:$type -text $type -data $rules
+ }
+ set data [list $vals(typeB) $vals(typeA) [llength $disB]]
+ $tree insert end distitle dissubtitleB -text $vals(typeB) -data $data -drawcross allways
+ foreach access [lsort -index 0 $disB] {
+ set type [lindex $access 0]
+ set rules [lindex $access 1]
+ $tree insert end dissubtitleB dis:$type -text $type -data $rules
+ }
+}
+
+proc Apol_Analysis_tra::_showDissimilarTitle {res data} {
+ foreach {typeA typeB} $data {break}
+ $res.tb insert end "Dissimilar access between " title \
+ $typeA title_type \
+ " and " title \
+ $typeB title_type \
+ ".\n\n" title \
+ "Open the subtree for this item to access individual subtrees of types
+which can be accessed by one type but not the other. You may then
+select a type from a subtree to see the allow rules which provide the
+access." {}
+}
+
+proc Apol_Analysis_tra::_showDissimilarSubtitle {res data} {
+ foreach {one_type other_type numTypes} $data {break}
+ $res.tb insert end $one_type title_type \
+ " accesss $numTypes type(s) to which " title \
+ $other_type title_type \
+ " does not have access.\n\n" title \
+ "Open the subtree for this item to see the list of types. You may then
+select a type from the subtree to see the allow rules which provide
+the access." {}
+}
+
+proc Apol_Analysis_tra::_showDissimilar {res name parent_name data} {
+ $res.tb insert end $parent_name title_type \
+ " accesses " title \
+ $name title_type \
+ ":\n\n" title
+ set v [new_apol_vector_t]
+ foreach r $data {
+ $v append $r
+ }
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 2 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_Analysis_tra::_renderAllows {tree results} {
+ set rules [$results get_allowrules]
+ set rules_dup [new_apol_vector_t $rules]
+ $rules_dup -acquire
+ apol_tcl_avrule_sort $::ApolTop::policy $rules_dup
+ $tree insert end root allow -text "TE Allow Rules" -data $rules_dup
+}
+
+proc Apol_Analysis_tra::_showAllows {res data} {
+ $res.tb insert end "TE Allow Rules ([$data get_size]):\n\n" title
+ Apol_Widget::appendSearchResultRules $res 2 $data qpol_avrule_from_void
+}
+
+proc Apol_Analysis_tra::_renderTypeRules {tree results} {
+ set rules [$results get_typerules]
+ set rules_dup [new_apol_vector_t $rules]
+ apol_tcl_terule_sort $::ApolTop::policy $rules_dup
+ $rules_dup -acquire
+ $tree insert end root typerules -text "Type Transition/Change Rules" -data $rules_dup
+}
+
+proc Apol_Analysis_tra::_showTypeRules {res data} {
+ $res.tb insert end "Type transition/change rules ([$data get_size]):\n\n" title
+ Apol_Widget::appendSearchResultRules $res 2 $data qpol_terule_from_void
+}
+
+proc Apol_Analysis_tra::_renderDirectFlow {tree results} {
+ set v [$results get_directflows]
+ if {$v == "NULL" || [$v get_size] == 0} {
+ $tree insert end root pre:direct
+ set node [$tree nodes root end]
+ set data [list "No direct information flows found." title]
+ } else {
+ variable vals
+ Apol_Analysis_directflow::appendResultsNodes $tree root $v
+ set node [$tree nodes root end]
+ set data [list $vals(typeA) $vals(typeB) [$tree itemcget $node -data]]
+ }
+ $tree itemconfigure $node -text "Direct Flows Between A and B" -data $data -drawcross auto
+}
+
+proc Apol_Analysis_tra::_showDirectFlow {res data} {
+ foreach {parent_name name data} $data {break}
+ foreach {flow_dir classes} [lindex $data 1] {break}
+ $res.tb insert end "Information flows both into and out of " title \
+ $parent_name title_type \
+ " from/to " title \
+ $name title_type
+ $res.tb insert end "\n\n" title_type \
+ "Objects classes for " subtitle \
+ [string toupper $flow_dir] subtitle_dir \
+ " flows:\n" subtitle
+ foreach c $classes {
+ foreach {class_name rules} $c {break}
+ $res.tb insert end " " {} \
+ $class_name\n subtitle
+ set v [new_apol_vector_t]
+ foreach r $rules {
+ $v append $r
+ }
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+}
+
+proc Apol_Analysis_tra::_renderTransFlow {dir tree results} {
+ variable vals
+ if {$dir == 0} {
+ set title2 "A->B"
+ set v [$results get_transflowsAB]
+ set data [list $vals(typeB) $vals(typeA)]
+ } else {
+ set title2 "B->A"
+ set v [$results get_transflowsBA]
+ set data [list $vals(typeA) $vals(typeB)]
+ }
+ if {$v == "NULL" || [$v get_size] == 0} {
+ $tree insert end root pre:trans$dir
+ set node [$tree nodes root end]
+ set data [list "No transitive information flows found." title]
+ } else {
+ Apol_Analysis_transflow::appendResultsNodes $tree root $v
+ set node [$tree nodes root end]
+ lappend data [$tree itemcget $node -data]
+ }
+ $tree itemconfigure $node -text "Transitive Flows $title2" -data $data -drawcross auto
+}
+
+proc Apol_Analysis_tra::_showTransFlow {res data} {
+ foreach {parent_name name data} $data {break}
+ foreach {flow_dir paths} [lindex $data 1] {break}
+ $res.tb insert end "Information flows from " title \
+ $name title_type \
+ " to " title \
+ $parent_name title_type
+ $res.tb insert end "\n\n" title \
+ "Apol found the following number of information flows: " subtitle \
+ [llength $paths] num \
+ "\n" subtitle
+ set path_num 1
+ foreach path $paths {
+ $res.tb insert end "\n" {}
+ Apol_Analysis_transflow::renderPath $res $path_num $path
+ incr path_num
+ }
+}
+
+proc Apol_Analysis_tra::_renderDTA {dir tree results} {
+ variable vals
+ if {$dir == 0} {
+ set title2 "A->B"
+ set data [list $vals(typeA) $vals(typeB)]
+ set dta [$results get_domainsAB]
+ } else {
+ set title2 "B->A"
+ set data [list $vals(typeB) $vals(typeA)]
+ set dta [$results get_domainsBA]
+ }
+ if {$dta == "NULL" || [$dta get_size] == 0} {
+ $tree insert end root pre:dta$dir
+ set node [$tree nodes root end]
+ set data [list "No domain transitions found." title]
+ } else {
+ Apol_Analysis_domaintrans::appendResultsNodes $tree root $dta
+ set node [$tree nodes root end]
+ lappend data [$tree itemcget $node -data]
+ }
+ $tree itemconfigure $node -text "Domain Transitions $title2" -data $data -drawcross auto
+}
+
+proc Apol_Analysis_tra::_showDTA {res data} {
+ foreach {parent_name name data} $data {break}
+ foreach {proctrans setexec ep access_list} [lindex $data 1] {break}
+ set header [list "Domain transition from " title \
+ $parent_name title_type \
+ " to " title \
+ $name title_type]
+ eval $res.tb insert end $header
+ $res.tb insert end "\n\n" title_type
+
+ $res.tb insert end "Process Transition Rules: " subtitle \
+ [llength $proctrans] num \
+ "\n" subtitle
+ set v [list_to_vector $proctrans]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 6 $v _qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ if {[llength $setexec] > 0} {
+ $res.tb insert end "\n" {} \
+ "Setexec Rules: " subtitle \
+ [llength $setexec] num \
+ "\n" subtitle
+ set v [list_to_vector $setexec]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+
+ $res.tb insert end "\nEntry Point File Types: " subtitle \
+ [llength $ep] num
+ foreach e [lsort -index 0 $ep] {
+ foreach {intermed entrypoint execute type_trans} $e {break}
+ $res.tb insert end "\n $intermed\n" {} \
+ " " {} \
+ "File Entrypoint Rules: " subtitle \
+ [llength $entrypoint] num \
+ "\n" subtitle
+ set v [list_to_vector $entrypoint]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ $res.tb insert end "\n" {} \
+ " " {} \
+ "File Execute Rules: " subtitle \
+ [llength $execute] num \
+ "\n" subtitle
+ set v [list_to_vector $execute]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ if {[llength $type_trans] > 0} {
+ $res.tb insert end "\n" {} \
+ " " {} \
+ "Type_transition Rules: " subtitle \
+ [llength $type_trans] num \
+ "\n" subtitle
+ set v [list_to_vector $type_trans]
+ apol_tcl_terule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 12 $v qpol_terule_from_void
+ $v -acquire
+ $v -delete
+ }
+ }
+
+ if {[llength $access_list] > 0} {
+ $res.tb insert end "\n" {} \
+ "The access filters you specified returned the following rules: " subtitle \
+ [llength $access_list] num \
+ "\n" subtitle
+ set v [list_to_vector $access_list]
+ apol_tcl_avrule_sort $::ApolTop::policy $v
+ Apol_Widget::appendSearchResultRules $res 6 $v qpol_avrule_from_void
+ $v -acquire
+ $v -delete
+ }
+}
diff --git a/apol/types_tab.tcl b/apol/types_tab.tcl
new file mode 100644
index 0000000..26def34
--- /dev/null
+++ b/apol/types_tab.tcl
@@ -0,0 +1,411 @@
+# Copyright (C) 2001-2008 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Types {
+ variable typelist {}
+ variable attriblist {}
+ variable opts
+ variable widgets
+}
+
+proc Apol_Types::create {tab_name nb} {
+ variable opts
+ variable widgets
+
+ _initializeVars
+
+ set frame [$nb insert end $tab_name -text "Types"]
+ set pw1 [PanedWindow $frame.pw -side top]
+ set left_pane [$pw1 add -weight 0]
+ set center_pane [$pw1 add -weight 1]
+ set tpane [frame $left_pane.t]
+ set apane [frame $left_pane.a]
+
+ set tbox [TitleFrame $tpane.tbox -text "Types"]
+ set abox [TitleFrame $apane.abox -text "Attributes"]
+ set obox [TitleFrame $center_pane.obox -text "Search Options"]
+ set rbox [TitleFrame $center_pane.rbox -text "Search Results"]
+
+ pack $obox -side top -expand 0 -fill both -padx 2
+ pack $rbox -expand yes -fill both -padx 2
+ pack $tbox -fill both -expand yes
+ pack $abox -fill both -expand yes
+ pack $pw1 -fill both -expand yes
+ pack $tpane -fill both -expand 1
+ pack $apane -fill both -expand 1
+
+ set tlistbox [Apol_Widget::makeScrolledListbox [$tbox getframe].types \
+ -height 10 -width 20 -listvar Apol_Types::typelist]
+ Apol_Widget::setListboxCallbacks $tlistbox \
+ {{"Show Type Info" {Apol_Types::_popupTypeInfo type}}}
+ pack $tlistbox -expand 1 -fill both
+
+ set alistbox [Apol_Widget::makeScrolledListbox [$abox getframe].attribs \
+ -height 5 -width 20 -listvar Apol_Types::attriblist]
+ Apol_Widget::setListboxCallbacks $alistbox {{"Show Attribute Info" {Apol_Types::_popupTypeInfo attrib}}}
+ pack $alistbox -expand 1 -fill both
+
+ set ofm [$obox getframe]
+ set fm_types_select [frame $ofm.to]
+ set fm_attribs_select [frame $ofm.ao]
+ pack $fm_types_select $fm_attribs_select -side left -padx 4 -pady 2 -anchor nw
+
+ set types_select [checkbutton $fm_types_select.type -text "Show types" -variable Apol_Types::opts(types)]
+ set typeattribs [checkbutton $fm_types_select.typeattribs -text "Include attributes" \
+ -variable Apol_Types::opts(types:show_attribs)]
+ pack $types_select -anchor w
+ pack $typeattribs -anchor w -padx 8
+ trace add variable Apol_Types::opts(types) write \
+ [list Apol_Types::_toggleCheckbuttons $typeattribs]
+
+ set attribs_select [checkbutton $fm_attribs_select.type -text "Show attributes" \
+ -variable Apol_Types::opts(attribs)]
+ set a_types [checkbutton $fm_attribs_select.types -text "Include types" \
+ -variable Apol_Types::opts(attribs:show_types) -state disabled]
+ set a_typeattribs [checkbutton $fm_attribs_select.typeattribs -text "Include types' attributes" \
+ -variable Apol_Types::opts(attribs:show_attribs) -state disabled]
+ pack $attribs_select -anchor w
+ pack $a_types $a_typeattribs -anchor w -padx 8
+ trace add variable Apol_Types::opts(attribs) write \
+ [list Apol_Types::_toggleCheckbuttons [list $a_typeattribs $a_types]]
+
+ set widgets(regexp) [Apol_Widget::makeRegexpEntry $ofm.regexpf]
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 1
+
+ pack $widgets(regexp) -side left -padx 4 -pady 2 -anchor nw
+
+ set ok [button $ofm.ok -text OK -width 6 -command Apol_Types::_searchTypes]
+ pack $ok -side right -padx 5 -pady 5 -anchor ne
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$rbox getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Types::open {ppath} {
+ set q [new_apol_type_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable typelist [lsort [type_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ set q [new_apol_attr_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable attriblist [lsort [attr_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+}
+
+proc Apol_Types::close {} {
+ variable widgets
+
+ _initializeVars
+ set Apol_Types::typelist {}
+ set Apol_Types::attriblist {}
+ Apol_Widget::clearSearchResults $widgets(results)
+}
+
+proc Apol_Types::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+# Given a type or alias name, return non-zero if that type/alias is
+# within the policy. If no policy has been loaded then return zero.
+proc Apol_Types::isTypeInPolicy {type} {
+ if {![ApolTop::is_policy_open]} {
+ return 0
+ }
+ set q [new_apol_type_query_t]
+ $q set_type $::ApolTop::policy $type
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ if {$v == "NULL" || [$v get_size] == 0} {
+ set retval 0
+ } else {
+ set retval 1
+ }
+ $v -acquire
+ $v -delete
+ set retval
+}
+
+# Given an attribute name, return non-zero if that attribute is within
+# the loaded policy. If no policy has been loaded then return zero.
+proc Apol_Types::isAttributeInPolicy {attrib} {
+ variable attriblist
+ if {[ApolTop::is_policy_open] && [lsearch $attriblist $attrib] >= 0} {
+ return 1
+ }
+ return 0
+}
+
+# Return a sorted list of all type names (not attributes nor aliases)
+# within the current policy. If no policy is open then return an
+# empty list.
+proc Apol_Types::getTypes {} {
+ variable typelist
+ set typelist
+}
+
+# Return a list of all attribute names within the current policy. If
+# no policy is open then return an empty list.
+proc Apol_Types::getAttributes {} {
+ variable attriblist
+ set attriblist
+}
+
+#### private functions below ####
+
+proc Apol_Types::_initializeVars {} {
+ variable opts
+ array set opts {
+ types 1 types:show_attribs 1 types:show_aliases 1
+ attribs 0 attribs:show_types 1 attribs:show_attribs 1
+ }
+}
+
+proc Apol_Types::_toggleCheckbuttons {w name1 name2 op} {
+ variable opts
+ variable widgets
+ if {$opts($name2)} {
+ foreach x $w {
+ $x configure -state normal
+ }
+ } else {
+ foreach x $w {
+ $x configure -state disabled
+ }
+ }
+ if {!$opts(types) && !$opts(attribs)} {
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 0
+ } else {
+ Apol_Widget::setRegexpEntryState $widgets(regexp) 1
+ }
+}
+
+proc Apol_Types::_popupTypeInfo {which ta} {
+ if {[Apol_File_Contexts::is_db_loaded]} {
+ set entry_vector [Apol_File_Contexts::get_fc_files_for_ta $which $ta]
+ set index_file_loaded 1
+ } else {
+ set entry_vector {}
+ set index_file_loaded 0
+ }
+
+ if {$which == "type"} {
+ set info_ta [_renderType $ta 1 1]
+ } else {
+ set info_ta [_renderAttrib $ta 1 0]
+ }
+
+ set w .ta_infobox
+ destroy $w
+
+ set w [Dialog .ta_infobox -cancel 0 -default 0 -modal none -parent . -separator 1 -title $ta]
+ $w add -text "Close" -command [list destroy $w]
+
+ set notebook [NoteBook [$w getframe].nb]
+ pack $notebook -expand 1 -fill both
+
+ set ta_info_tab [$notebook insert end ta_info_tab]
+ set fc_info_tab [$notebook insert end fc_info_tab -text "Files"]
+
+ if {$which == "type"} {
+ $notebook itemconfigure ta_info_tab -text "Attributes"
+ } else {
+ $notebook itemconfigure ta_info_tab -text "Types"
+ }
+ set sw [ScrolledWindow [$notebook getframe ta_info_tab].sw -scrollbar both -auto both]
+ set text [text [$sw getframe].text -wrap none -font {helvetica 10} -bg white]
+ $sw setwidget $text
+ pack $sw -expand 1 -fill both
+ $text insert 0.0 $info_ta
+ $text configure -state disabled
+
+ if {$which != "type"} {
+ set l [label [$notebook getframe fc_info_tab].l \
+ -text "Files labeled with types that are members of this attribute:" \
+ -justify left]
+ pack $l -anchor nw
+ }
+ set sw [ScrolledWindow [$notebook getframe fc_info_tab].sw -scrollbar both -auto both]
+ set fc_text [text [$sw getframe].text -wrap none -font {helvetica 10} -bg white]
+ $sw setwidget $fc_text
+ pack $sw -expand 1 -fill both
+
+ $notebook raise [$notebook page 0]
+
+ if {$index_file_loaded} {
+ if {$entry_vector != {}} {
+ set num [$entry_vector get_size]
+ $fc_text insert 1.0 "Number of files: $num\n\n"
+ for {set i 0} {$i < $num} {incr i} {
+ set entry [sefs_entry_from_void [$entry_vector get_element $i]]
+ $fc_text insert end "[$entry toString]\n"
+ }
+ $entry_vector -delete
+ } else {
+ $fc_text insert end "No files found."
+ }
+ } else {
+ $fc_text insert 0.0 "No index file is loaded. Load an index file through the File Contexts tab."
+ }
+ $fc_text configure -state disabled
+
+ $w draw {} 0 400x400
+}
+
+proc Apol_Types::_searchTypes {} {
+ variable widgets
+ variable opts
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ return
+ }
+ if {$opts(types) == 0 && $opts(attribs) == 0} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No search options provided."
+ return
+ }
+ set use_regexp [Apol_Widget::getRegexpEntryState $widgets(regexp)]
+ set regexp [Apol_Widget::getRegexpEntryValue $widgets(regexp)]
+ if {$use_regexp} {
+ if {$regexp == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No regular expression provided."
+ return
+ }
+ } else {
+ set regexp {}
+ }
+
+ set results {}
+ if {$opts(types)} {
+ set q [new_apol_type_query_t]
+ $q set_type $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set types_data [type_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ append results "TYPES ([llength $types_data]):\n\n"
+ foreach t [lsort $types_data] {
+ append results "[_renderType $t $opts(types:show_attribs) $opts(types:show_aliases)]\n"
+ }
+ }
+ if {$opts(attribs)} {
+ set q [new_apol_attr_query_t]
+ $q set_attr $::ApolTop::policy $regexp
+ $q set_regex $::ApolTop::policy $use_regexp
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set attribs_data [attr_vector_to_list $v]
+ $v -acquire
+ $v -delete
+ if {$opts(types)} {
+ append results "\n\n"
+ }
+ append results "ATTRIBUTES ([llength $attribs_data]):\n\n"
+ foreach a [lsort $attribs_data] {
+ append results "[_renderAttrib $a $opts(attribs:show_types) $opts(attribs:show_attribs)]\n"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $results
+}
+
+proc Apol_Types::_renderType {type_name show_attribs show_aliases} {
+ set qpol_type_datum [new_qpol_type_t $::ApolTop::qpolicy $type_name]
+ set aliases {}
+ set attribs {}
+ set i [$qpol_type_datum get_alias_iter $::ApolTop::qpolicy]
+ set aliases [iter_to_str_list $i]
+ $i -acquire
+ $i -delete
+ set i [$qpol_type_datum get_attr_iter $::ApolTop::qpolicy]
+ foreach a [iter_to_list $i] {
+ set a [qpol_type_from_void $a]
+ lappend attribs [$a get_name $::ApolTop::qpolicy]
+ }
+ $i -acquire
+ $i -delete
+
+ set text "$type_name"
+ if {$show_aliases && [llength $aliases] > 0} {
+ append text " alias [list $aliases]"
+ }
+ if {$show_attribs} {
+ append text " ([llength $attribs] attribute"
+ if {[llength $attribs] != 1} {
+ append text s
+ }
+ append text ")\n"
+ foreach a [lsort $attribs] {
+ append text " $a\n"
+ }
+ }
+ return $text
+}
+
+proc Apol_Types::_renderAttrib {attrib_name show_types show_attribs} {
+ set qpol_type_datum [new_qpol_type_t $::ApolTop::qpolicy $attrib_name]
+
+ set text "$attrib_name"
+ if {$show_types} {
+ set types {}
+ set i [$qpol_type_datum get_type_iter $::ApolTop::qpolicy]
+ foreach t [iter_to_list $i] {
+ set t [qpol_type_from_void $t]
+ lappend types [$t get_name $::ApolTop::qpolicy]
+ }
+ $i -acquire
+ $i -delete
+ append text " ([llength $types] type"
+ if {[llength $types] != 1} {
+ append text s
+ }
+ append text ")\n"
+ foreach type_name [lsort $types] {
+ append text " $type_name"
+ if {$show_attribs} {
+ set t [new_qpol_type_t $::ApolTop::qpolicy $type_name]
+ set this_attribs {}
+ set i [$t get_attr_iter $::ApolTop::qpolicy]
+ foreach a [iter_to_list $i] {
+ set a [qpol_type_from_void $a]
+ lappend this_attribs [$a get_name $::ApolTop::qpolicy]
+ }
+ $i -acquire
+ $i -delete
+
+ set this_attribs [lsort $this_attribs]
+ # remove the entry that we know should be there
+ set idx [lsearch -sorted -exact $attrib_name $this_attribs]
+ append text " { [lreplace $this_attribs $idx $idx] }"
+ }
+ append text "\n"
+ }
+ }
+ return $text
+}
diff --git a/apol/users_tab.tcl b/apol/users_tab.tcl
new file mode 100644
index 0000000..ad581d3
--- /dev/null
+++ b/apol/users_tab.tcl
@@ -0,0 +1,313 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+namespace eval Apol_Users {
+ variable opts
+ variable widgets
+ variable users_list {}
+}
+
+proc Apol_Users::create {tab_name nb} {
+ variable opts
+ variable widgets
+
+ _initializeVars
+
+ # Layout frames
+ set frame [$nb insert end $tab_name -text "Users"]
+ set pw1 [PanedWindow $frame.pw -side top]
+ set rpane [$pw1 add -weight 0]
+ set spane [$pw1 add -weight 1]
+
+ # Title frames
+ set userbox [TitleFrame $rpane.userbox -text "Users"]
+ set s_optionsbox [TitleFrame $spane.obox -text "Search Options"]
+ set resultsbox [TitleFrame $spane.rbox -text "Search Results"]
+ pack $pw1 -fill both -expand yes
+ pack $s_optionsbox -side top -expand 0 -fill both -padx 2
+ pack $userbox -fill both -expand yes
+ pack $resultsbox -expand yes -fill both -padx 2
+
+ # Users listbox widget
+ set users_listbox [Apol_Widget::makeScrolledListbox [$userbox getframe].lb -width 20 -listvar Apol_Users::users_list]
+ Apol_Widget::setListboxCallbacks $users_listbox \
+ {{"Display User Info" {Apol_Users::_popupUserInfo users}}}
+ pack $users_listbox -fill both -expand yes
+
+ # Search options subframes
+ set ofm [$s_optionsbox getframe]
+ set verboseFrame [frame $ofm.verbose]
+ set rolesFrame [frame $ofm.roles]
+ set defaultFrame [frame $ofm.default]
+ set rangeFrame [frame $ofm.range]
+ pack $verboseFrame $rolesFrame $defaultFrame $rangeFrame \
+ -side left -padx 4 -pady 2 -anchor nw -expand 0 -fill y
+
+ radiobutton $verboseFrame.all_info -text "All information" \
+ -variable Apol_Users::opts(showSelection) -value all
+ radiobutton $verboseFrame.names_only -text "Names only" \
+ -variable Apol_Users::opts(showSelection) -value names
+ pack $verboseFrame.all_info $verboseFrame.names_only -anchor w -padx 5 -pady 4
+
+ checkbutton $rolesFrame.cb -variable Apol_Users::opts(useRole) -text "Role"
+ set widgets(role) [ComboBox $rolesFrame.combo -width 12 -textvariable Apol_Users::opts(role) \
+ -helptext "Type or select a role" -state disabled \
+ -autopost 1]
+ trace add variable Apol_Users::opts(useRole) write \
+ [list Apol_Users::_toggleRolesCheckbutton $widgets(role)]
+ pack $rolesFrame.cb -anchor nw
+ pack $widgets(role) -padx 4
+
+ set widgets(defaultCB) [checkbutton $defaultFrame.cb -variable Apol_Users::opts(enable_default) -text "Default MLS level"]
+ set defaultDisplay [Entry $defaultFrame.display -textvariable Apol_Users::opts(default_level_display) -width 16 -editable 0]
+ set defaultButton [button $defaultFrame.button -text "Select Level..." -state disabled -command [list Apol_Users::_show_level_dialog]]
+ trace add variable Apol_Users::opts(enable_default) write \
+ [list Apol_Users::_toggleDefaultCheckbutton $widgets(defaultCB) $defaultDisplay $defaultButton]
+ trace add variable Apol_Users::opts(default_level) write \
+ [list Apol_Users::_updateDefaultDisplay $defaultDisplay]
+ pack $widgets(defaultCB) -side top -anchor nw -expand 0
+ pack $defaultDisplay -side top -expand 0 -fill x -padx 4
+ pack $defaultButton -side top -expand 1 -fill none -padx 4 -anchor ne
+
+ set widgets(range) [Apol_Widget::makeRangeSelector $rangeFrame.range Users]
+ pack $widgets(range) -expand 1 -fill x
+
+ # Action Buttons
+ button $ofm.ok -text OK -width 6 -command Apol_Users::_searchUsers
+ pack $ofm.ok -side right -pady 5 -padx 5 -anchor ne
+
+ set widgets(results) [Apol_Widget::makeSearchResults [$resultsbox getframe].results]
+ pack $widgets(results) -expand yes -fill both
+
+ return $frame
+}
+
+proc Apol_Users::open {ppath} {
+ set q [new_apol_user_query_t]
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ variable users_list [lsort [user_vector_to_list $v]]
+ $v -acquire
+ $v -delete
+
+ variable opts
+ variable widgets
+ $Apol_Users::widgets(role) configure -values [Apol_Roles::getRoles]
+ if {[ApolTop::is_capable "mls"]} {
+ Apol_Widget::setRangeSelectorCompleteState $widgets(range) normal
+ $widgets(defaultCB) configure -state normal
+ } else {
+ Apol_Widget::clearRangeSelector $widgets(range)
+ Apol_Widget::setRangeSelectorCompleteState $widgets(range) disabled
+ set opts(enable_default) 0
+ $widgets(defaultCB) configure -state disabled
+ }
+}
+
+proc Apol_Users::close {} {
+ variable widgets
+
+ _initializeVars
+ variable users_list {}
+ $widgets(role) configure -values ""
+ Apol_Widget::clearSearchResults $widgets(results)
+ Apol_Widget::clearRangeSelector $widgets(range)
+ Apol_Widget::setRangeSelectorCompleteState $widgets(range) normal
+ $widgets(defaultCB) configure -state normal
+}
+
+proc Apol_Users::getTextWidget {} {
+ variable widgets
+ return $widgets(results).tb
+}
+
+# Return a list of all user names within the current policy. If no
+# policy is loaded then return an empty list.
+proc Apol_Users::getUsers {} {
+ variable users_list
+ set users_list
+}
+
+#### private functions below ####
+
+proc Apol_Users::_initializeVars {} {
+ variable opts
+ array set opts {
+ showSelection all
+ useRole 0 role {}
+ enable_default 0 default_level {}
+ }
+}
+
+proc Apol_Users::_toggleRolesCheckbutton {path name1 name2 op} {
+ variable opts
+ if {$opts($name2)} {
+ $path configure -state normal -entrybg white
+ } else {
+ $path configure -state disabled -entrybg $ApolTop::default_bg_color
+ }
+}
+
+proc Apol_Users::_toggleDefaultCheckbutton {cb display button name1 name2 op} {
+ variable opts
+ if {$opts($name2)} {
+ $button configure -state normal
+ $display configure -state normal
+ } else {
+ $button configure -state disabled
+ $display configure -state disabled
+ }
+}
+
+proc Apol_Users::_show_level_dialog {} {
+ variable opts
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.default.button configure -state disabled
+ set new_level [Apol_Level_Dialog::getLevel $opts(default_level)]
+ if {$new_level != {}} {
+ set opts(default_level) $new_level
+ $opts(default_level) -acquire
+ }
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.default.button configure -state normal
+}
+
+proc Apol_Users::_updateDefaultDisplay {display name1 name2 op} {
+ variable opts
+ if {$opts(default_level) == {}} {
+ set opts(default_level_display) {}
+ $display configure -helptext {}
+ } else {
+ set level [$opts(default_level) render $::ApolTop::policy]
+ if {$level == {}} {
+ set opts(default_level_display) "<invalid MLS level>"
+ } else {
+ set opts(default_level_display) $level
+ }
+ $display configure -helptext $opts(default_level_display)
+ }
+}
+
+proc Apol_Users::_popupUserInfo {which user} {
+ Apol_Widget::showPopupText $user [_renderUser $user 1]
+}
+
+proc Apol_Users::_searchUsers {} {
+ variable opts
+ variable widgets
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.ok configure -state disabled
+
+ Apol_Widget::clearSearchResults $widgets(results)
+ if {![ApolTop::is_policy_open]} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.ok configure -state normal
+ return
+ }
+ if {$opts(useRole)} {
+ if {$opts(role) == ""} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No role selected."
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.ok configure -state normal
+ return
+ }
+ set role $opts(role)
+ } else {
+ set role {}
+ }
+ if {$opts(enable_default)} {
+ if {$opts(default_level) == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No default level selected."
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.ok configure -state normal
+ return
+ }
+ set default $opts(default_level)
+ # the user query will handle destroying the apol_mls_level object
+ } else {
+ set default NULL
+ }
+ set range_enabled [Apol_Widget::getRangeSelectorState $widgets(range)]
+ foreach {range range_type} [Apol_Widget::getRangeSelectorValue $widgets(range)] {break}
+ if {$range_enabled} {
+ if {$range == {}} {
+ tk_messageBox -icon error -type ok -title "Error" -message "No range selected."
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.ok configure -state normal
+ return
+ }
+ # the user query will handle destroying the apol_mls_range object
+ } else {
+ set range NULL
+ }
+ if {$opts(showSelection) == "all"} {
+ set show_all 1
+ } else {
+ set show_all 0
+ }
+
+ set q [new_apol_user_query_t]
+ $q set_role $::ApolTop::policy $role
+ $q set_default_level $::ApolTop::policy $default
+ $q set_range $::ApolTop::policy $range $range_type
+ set v [$q run $::ApolTop::policy]
+ $q -acquire
+ $q -delete
+ set users_data [user_vector_to_list $v]
+ $v -acquire
+ $v -delete
+
+ set text "USERS:\n"
+ if {[llength $users_data] == 0} {
+ append text "Search returned no results."
+ } else {
+ foreach u [lsort -index 0 $users_data] {
+ append text "\n[_renderUser $u $show_all]"
+ }
+ }
+ Apol_Widget::appendSearchResultText $widgets(results) $text
+ .mainframe.frame.nb.fcomponents.nb.fApol_Users.pw.f1.frame.obox.f.ok configure -state normal
+}
+
+proc Apol_Users::_renderUser {user_name show_all} {
+ set text "$user_name"
+ if {!$show_all} {
+ return $text
+ }
+ set qpol_user_datum [new_qpol_user_t $::ApolTop::qpolicy $user_name]
+ if {[ApolTop::is_capable "mls"]} {
+ set default [$qpol_user_datum get_dfltlevel $::ApolTop::qpolicy]
+ set apol_default [new_apol_mls_level_t $::ApolTop::policy $default]
+ append text " level [$apol_default render $::ApolTop::policy]"
+ $apol_default -acquire
+ $apol_default -delete
+ set range [$qpol_user_datum get_range $::ApolTop::qpolicy]
+ set apol_range [new_apol_mls_range_t $::ApolTop::policy $range]
+ append text " range [$apol_range render $::ApolTop::policy]"
+ $apol_range -acquire
+ $apol_range -delete
+ }
+ set i [$qpol_user_datum get_role_iter $::ApolTop::qpolicy]
+ set roles {}
+ while {![$i end]} {
+ set qpol_role_datum [qpol_role_from_void [$i get_item]]
+ lappend roles [$qpol_role_datum get_name $::ApolTop::qpolicy]
+ $i next
+ }
+ append text " ([llength $roles] role"
+ if {[llength $roles] != 1} {
+ append text "s"
+ }
+ append text ")\n"
+ foreach r $roles {
+ append text " $r\n"
+ }
+ return $text
+}
diff --git a/apol/util.tcl b/apol/util.tcl
new file mode 100644
index 0000000..dbcdda6
--- /dev/null
+++ b/apol/util.tcl
@@ -0,0 +1,312 @@
+# Copyright (C) 2001-2007 Tresys Technology, LLC
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# This file contains miscellaneous convenience routines to convert
+# between Tcl and libapol/libqpol.
+
+proc iter_to_list {iter} {
+ set list {}
+ while {![$iter end]} {
+ lappend list [$iter get_item]
+ $iter next
+ }
+ return $list
+}
+
+proc iter_to_str_list {iter} {
+ set list {}
+ while {![$iter end]} {
+ lappend list [to_str [$iter get_item]]
+ $iter next
+ }
+ return $list
+}
+
+proc list_to_vector {list} {
+ set v [new_apol_vector_t]
+ $v -acquire
+ foreach x $list {
+ $v append $x
+ }
+ return $v
+}
+
+proc list_to_str_vector {list} {
+ set v [new_apol_string_vector_t]
+ $v -acquire
+ foreach x $list {
+ $v append $x
+ }
+ return $v
+}
+
+proc str_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [$v get_element $i]
+ }
+ return $list
+}
+
+proc attr_vector_to_list {v} {
+ type_vector_to_list $v
+}
+
+proc avrule_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_avrule_from_void [$v get_element $i]]
+ lappend list $q
+ }
+ return $list
+}
+
+proc bool_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_bool_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc cat_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_cat_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc class_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_class_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc common_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_common_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+# Convert a vector a qpol_cond_t objects to a list of qpol_cond_t
+# objects.
+proc cond_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_cond_from_void [$v get_element $i]]
+ lappend list $q
+ }
+ return $list
+}
+
+proc domain_trans_result_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set a [apol_domain_trans_result_from_void [$v get_element $i]]
+ lappend list $a
+ }
+ return $list
+}
+
+# Convert a vector a qpol_fs_use_t objects to a list of qpol_fs_use_t
+# objects.
+proc fs_use_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_fs_use_from_void [$v get_element $i]]
+ lappend list $q
+ }
+ return $list
+}
+
+# Convert a vector of qpol_genfscon_t objects to a list of
+# qpol_genfscon_t objects.
+proc genfscon_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_genfscon_from_void [$v get_element $i]]
+ lappend list $q
+ }
+ return $list
+}
+
+proc infoflow_result_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set a [apol_infoflow_result_from_void [$v get_element $i]]
+ lappend list $a
+ }
+ return $list
+}
+
+proc isid_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_isid_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc level_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_level_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc netifcon_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_netifcon_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+# Convert a vector of qpol_nodecon_t objects to a list of
+# qpol_nodecon_t objects.
+proc nodecon_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_nodecon_from_void [$v get_element $i]]
+ lappend list $q
+ }
+ return $list
+}
+
+# Convert a vector of qpol_portcon_t objects to a list of qpol_portcon_t.
+proc portcon_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [qpol_portcon_from_void [$v get_element $i]]
+ }
+ return $list
+}
+
+# Convert a vector of qpol_range_trans_t objects to a list of
+# qpol_role_trans_t.
+proc range_trans_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [qpol_range_trans_from_void [$v get_element $i]]
+ }
+ return $list
+}
+
+proc relabel_result_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [apol_relabel_result_from_void [$v get_element $i]]
+ }
+ return $list
+}
+
+proc relabel_result_pair_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [apol_relabel_result_pair_from_void [$v get_element $i]]
+ }
+ return $list
+}
+
+# Convert a vector of qpol_role_allow_t objects to a list of
+# qpol_role_allow_t.
+proc role_allow_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [qpol_role_allow_from_void [$v get_element $i]]
+ }
+ return $list
+}
+
+# Convert a vector of qpol_role_trans_t objects to a list of
+# qpol_role_trans_t.
+proc role_trans_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ lappend list [qpol_role_trans_from_void [$v get_element $i]]
+ }
+ return $list
+}
+
+proc role_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_role_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc terule_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_terule_from_void [$v get_element $i]]
+ lappend list $q
+ }
+ return $list
+}
+
+proc type_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_type_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc user_vector_to_list {v} {
+ set list {}
+ for {set i 0} {$v != "NULL" && $i < [$v get_size]} {incr i} {
+ set q [qpol_user_from_void [$v get_element $i]]
+ lappend list [$q get_name $::ApolTop::qpolicy]
+ }
+ return $list
+}
+
+proc list_to_policy_path {path_type primary modules} {
+ if {$path_type == "monolithic"} {
+ set path_type $::APOL_POLICY_PATH_TYPE_MONOLITHIC
+ } else {
+ set path_type $::APOL_POLICY_PATH_TYPE_MODULAR
+ }
+ set ppath [new_apol_policy_path_t $path_type $primary [list_to_str_vector $modules]]
+ $ppath -acquire
+ return $ppath
+}
+
+proc policy_path_to_list {ppath} {
+ if {[$ppath get_type] == $::APOL_POLICY_PATH_TYPE_MONOLITHIC} {
+ set path_type "monolithic"
+ } else {
+ set path_type "modular"
+ }
+ set primary [$ppath get_primary]
+ set modules [str_vector_to_list [$ppath get_modules]]
+ list $path_type $primary $modules
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..b885981
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,896 @@
+AC_INIT(setools, 3.3.7, [Tresys Technology <setools@tresys.com>], [setools])
+AC_PREREQ([2.59])
+AC_CONFIG_SRCDIR(libqpol/src/policy.c)
+AC_CONFIG_HEADER(config.h)
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([-Wno-portability])
+
+dnl *** update these variables as versions change; also update AC_INIT ***
+
+libqpol_soversion=1
+libqpol_version=1.6
+
+libapol_soversion=4
+libapol_version=4.3
+
+libpoldiff_soversion=1
+libpoldiff_version=1.3.2
+
+libsefs_soversion=4
+libsefs_version=4.0.3
+
+libseaudit_soversion=4
+libseaudit_version=4.4
+
+setoolsdir='${prefix}/share/setools-3.3'
+javadir='${prefix}/share/java'
+
+version_min_sepol_major=1
+version_min_sepol_minor=12
+version_min_sepol_patch=27
+
+dnl *** end of tunable values ***
+
+libqpol_soname=libqpol.so.${libqpol_soversion}
+libqpol_pyswig_soname=_qpol.so.${libqpol_soversion}
+libqpol_jswig_soname=libjqpol.so.${libqpol_soversion}
+libqpol_tswig_soname=libtqpol.so.${libqpol_soversion}
+
+libapol_soname=libapol.so.${libapol_soversion}
+libapol_pyswig_soname=_apol.so.${libapol_soversion}
+libapol_jswig_soname=libjapol.so.${libapol_soversion}
+libapol_tswig_soname=libtapol.so.${libapol_soversion}
+
+libpoldiff_soname=libpoldiff.so.${libpoldiff_soversion}
+libpoldiff_pyswig_soname=_poldiff.so.${libpoldiff_soversion}
+libpoldiff_jswig_soname=libjpoldiff.so.${libpoldiff_soversion}
+libpoldiff_tswig_soname=libtpoldiff.so.${libpoldiff_soversion}
+
+libsefs_soname=libsefs.so.${libsefs_soversion}
+libsefs_pyswig_soname=_sefs.so.${libsefs_soversion}
+libsefs_jswig_soname=libjsefs.so.${libsefs_soversion}
+libsefs_tswig_soname=libtsefs.so.${libsefs_soversion}
+
+libseaudit_soname=libseaudit.so.${libseaudit_soversion}
+libseaudit_pyswig_soname=_seaudit.so.${libseaudit_soversion}
+libseaudit_jswig_soname=libjseaudit.so.${libseaudit_soversion}
+libseaudit_tswig_soname=libtseaudit.so.${libseaudit_soversion}
+
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+if test ${ac_cv_prog_cc_c99} = "no"; then
+ AC_MSG_ERROR([SETools requires a C99-compliant C compiler to build.])
+fi
+AC_PROG_CXX
+AC_LANG([C])
+AC_PROG_LIBTOOL
+AC_PROG_LN_S
+AC_PROG_LEX
+AC_PROG_YACC
+AC_PROG_INSTALL
+AC_HEADER_STDBOOL
+AC_C_BIGENDIAN
+AC_CHECK_FUNCS(rand_r)
+AC_SYS_LARGEFILE
+
+AC_CACHE_SAVE
+
+if test "x${enable_shared}" = xyes; then
+ use_shared=yes
+else
+ use_shared=no
+fi
+
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug],
+ [compile with full debugging support]),
+ enable_debug="$enableval")
+AC_MSG_CHECKING([for debugging support])
+if test "x${enable_debug}" = xyes; then
+ DEBUGCFLAGS="-g3 -gdwarf-2 -O0 -Wall -DSETOOLS_DEBUG=1 -fno-strict-aliasing"
+ DEBUGCXXFLAGS="-g3 -gdwarf-2 -O0 -Wall -DSETOOLS_DEBUG=1 -fno-strict-aliasing"
+ DEBUGJFLAGS="-g"
+ DEBUGLDFLAGS="-g -lm"
+ QPOL_LIB_FLAG='$(top_builddir)/libqpol/src/libqpol.so -Wl,-rpath=$(top_builddir)/libqpol/src'
+ APOL_LIB_FLAG='$(top_builddir)/libapol/src/libapol.a -Wl,-rpath=$(top_builddir)/libapol/src'
+ SEFS_LIB_FLAG='$(top_builddir)/libsefs/src/libsefs.a -Wl,-rpath=$(top_builddir)/libsefs/src'
+ POLDIFF_LIB_FLAG='$(top_builddir)/libpoldiff/src/libpoldiff.a -Wl,-rpath=$(top_builddir)/libpoldiff/src'
+ SEAUDIT_LIB_FLAG='$(top_builddir)/libseaudit/src/libseaudit.a -Wl,-rpath=$(top_builddir)/libseaudit/src'
+ use_shared=no
+ AC_MSG_RESULT(yes)
+else
+ AC_DEFINE(NDEBUG, 1, [disable calls to assert()])
+ DEBUGCFLAGS="-fno-strict-aliasing"
+ DEBUGCXXFLAGS="-fno-strict-aliasing"
+ DEBUGJFLAGS=""
+ DEBUGLDFLAGS=""
+ if test ${use_shared} = yes; then
+ QPOL_LIB_FLAG='$(top_builddir)/libqpol/src/libqpol.so'
+ APOL_LIB_FLAG='$(top_builddir)/libapol/src/libapol.so'
+ SEFS_LIB_FLAG='$(top_builddir)/libsefs/src/libsefs.so'
+ POLDIFF_LIB_FLAG='$(top_builddir)/libpoldiff/src/libpoldiff.so'
+ SEAUDIT_LIB_FLAG='$(top_builddir)/libseaudit/src/libseaudit.so'
+ else
+ QPOL_LIB_FLAG='$(top_builddir)/libqpol/src/libqpol.so'
+ APOL_LIB_FLAG='$(top_builddir)/libapol/src/libapol.a'
+ SEFS_LIB_FLAG='$(top_builddir)/libsefs/src/libsefs.a'
+ POLDIFF_LIB_FLAG='$(top_builddir)/libpoldiff/src/libpoldiff.a'
+ SEAUDIT_LIB_FLAG='$(top_builddir)/libseaudit/src/libseaudit.a'
+ fi
+ AC_MSG_RESULT(disabled)
+fi
+AC_SUBST(DEBUGCFLAGS)
+AC_SUBST(DEBUGCXXFLAGS)
+AC_SUBST(DEBUGJFLAGS)
+AC_SUBST(DEBUGLDFLAGS)
+
+QPOL_CFLAGS='-I$(top_srcdir)/libqpol/include'
+APOL_CFLAGS='-I$(top_srcdir)/libapol/include'
+SEFS_CFLAGS='-I$(top_srcdir)/libsefs/include'
+POLDIFF_CFLAGS='-I$(top_srcdir)/libpoldiff/include'
+SEAUDIT_CFLAGS='-I$(top_srcdir)/libseaudit/include'
+AC_SUBST(QPOL_CFLAGS)
+AC_SUBST(APOL_CFLAGS)
+AC_SUBST(SEFS_CFLAGS)
+AC_SUBST(POLDIFF_CFLAGS)
+AC_SUBST(SEAUDIT_CFLAGS)
+
+AC_SUBST(QPOL_LIB_FLAG)
+AC_SUBST(APOL_LIB_FLAG)
+AC_SUBST(SEFS_LIB_FLAG)
+AC_SUBST(POLDIFF_LIB_FLAG)
+AC_SUBST(SEAUDIT_LIB_FLAG)
+
+AC_MSG_CHECKING([for how to link against libraries])
+if test ${use_shared} = yes; then
+ AC_MSG_RESULT(shared)
+else
+ AC_MSG_RESULT(static)
+fi
+
+AC_ARG_ENABLE(warnall,
+ AC_HELP_STRING([--enable-warnall],
+ [compile with most warnings enabled]),
+ enable_warnall="$enableval")
+if test "x${enable_warnall}" = xyes; then
+ WARNCFLAGS=" -O1 -Wall -Wshadow -Wundef -pedantic -Wuninitialized -fno-strict-aliasing -std=c99"
+ WARNCXXFLAGS=" -O1 -Wall -Wshadow -Wundef -pedantic -Wuninitialized -Wabi -fno-strict-aliasing -Wno-variadic-macros"
+ WARNJFLAGS=" -verbose"
+ WARNLDFLAGS=""
+else
+ WARNCFLAGS=""
+ WARNCXXFLAGS=""
+ WARNJFLAGS=""
+ WARNLDFLAGS=""
+fi
+AC_SUBST(WARNCFLAGS)
+AC_SUBST(WARNCXXFLAGS)
+AC_SUBST(WARNJFLAGS)
+AC_SUBST(WARNLDFLAGS)
+
+AC_ARG_ENABLE(profiling,
+ AC_HELP_STRING([--enable-profiling],
+ [compile with gcov and gprof profiling support]),
+ enable_profiling="$enableval")
+AC_MSG_CHECKING([for profiling support])
+if test "x${enable_profiling}" = xyes; then
+ PROFILECFLAGS="-ftest-coverage -fprofile-arcs -pg"
+ PROFILELDFLAGS="-lgcov"
+ AC_MSG_RESULT(yes)
+else
+ PROFILECFLAGS=""
+ PROFILELDFLAGS=""
+ AC_MSG_RESULT(disabled)
+fi
+AC_SUBST(PROFILECFLAGS)
+AC_SUBST(PROFILELDFLAGS)
+
+do_swigify=no
+do_swigify_java=no
+do_swigify_python=no
+AC_ARG_ENABLE(swig-java,
+ AC_HELP_STRING([--enable-swig-java],
+ [build SWIG interfaces for Java]),
+ enable_jswig="$enableval")
+if test "x${enable_jswig}" = xyes; then
+ if test ${do_swigify} = no; then
+ AC_PROG_SWIG(1.3.28)
+ fi
+ AC_JAVA_OPTIONS
+ if test "x$JAVAPREFIX" = x; then
+ JAVAPREFIX=/usr/lib/jvm/java-gcj
+ fi
+ AC_CHECK_CLASSPATH
+ AC_PROG_JAVAC
+ AC_PROG_JAVA
+ AC_PROG_JAR
+ SWIG_JAVA_OPT=-java
+ java_save_CFLAGS="${CFLAGS}"
+ SWIG_JAVA_CFLAGS="-I${JAVAPREFIX}/include -I${JAVAPREFIX}/include/linux -DSWIGJAVA=1"
+ CFLAGS="${CFLAGS} ${SWIG_JAVA_CFLAGS}"
+ AC_CHECK_HEADER([jni.h], , AC_MSG_ERROR([Not found. Specify a Java SDK with --with-java-prefix flag.]))
+ AC_CHECK_HEADER([jni_md.h], , AC_MSG_ERROR([Not found. Specify a Java SDK with --with-java-prefix flag.]))
+ CFLAGS="${java_save_CFLAGS}"
+ AC_SUBST(SWIG_JAVA_OPT)
+ AC_SUBST(SWIG_JAVA_CFLAGS)
+ do_swigify_java=yes
+ do_swigify=yes
+fi
+AC_ARG_ENABLE(swig-python,
+ AC_HELP_STRING([--enable-swig-python],
+ [build SWIG interfaces for Python]),
+ enable_pyswig="$enableval")
+if test "x${enable_pyswig}" = xyes; then
+ if test ${do_swigify} = no; then
+ AC_PROG_SWIG(1.3.28)
+ fi
+ AM_PATH_PYTHON(2.3)
+ SWIG_PYTHON
+ do_swigify_python=yes
+ do_swigify=yes
+fi
+if test ${do_swigify} = "yes"; then
+ AC_PROG_SWIG(1.3.28)
+fi
+build_apol=yes
+AC_ARG_ENABLE(swig-tcl,
+ AC_HELP_STRING([--enable-swig-tcl],
+ [build SWIG interfaces for Tcl (default)]),
+ enable_tclswig="$enableval", enable_tclswig="yes")
+if test "x${enable_tclswig}" = xyes; then
+ if test ${do_swigify} = no; then
+ AC_PROG_SWIG(1.3.28)
+ fi
+ TEA_INIT(3.5)
+ TEA_PATH_TCLCONFIG
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ AC_MSG_ERROR([No Tcl install found.])
+ fi
+ TEA_LOAD_TCLCONFIG
+ AC_MSG_CHECKING([for Tcl 8.4 or greater])
+ if test \( "${TCL_MAJOR_VERSION}" -gt 8 \) -o \( "${TCL_MAJOR_VERSION}" -eq 8 -a "${TCL_MINOR_VERSION}" -ge 4 \); then
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_ERROR([setools requires Tcl 8.4 or greater])
+ fi
+ TEA_PUBLIC_TCL_HEADERS
+ TEA_PROG_TCLSH
+ SWIG_TCL_OPT=-tcl
+ SWIG_TCL_CFLAGS="${TCL_INCLUDES} -DSWIGTCL=1"
+ AC_SUBST(SWIG_TCL_OPT)
+ AC_SUBST(SWIG_TCL_CFLAGS)
+ do_swigify_tcl=yes
+ do_swigify=yes
+else
+ build_apol=no
+fi
+AC_CACHE_SAVE
+
+AC_ARG_ENABLE(gui,
+ AC_HELP_STRING([--disable-gui],
+ [do not compile graphical tools (default is ENABLE)]),
+ build_gui="$enableval",
+ build_gui="yes")
+AC_MSG_CHECKING([which setools programs to build])
+if test "x${build_gui}" = "xyes"; then
+ AC_MSG_RESULT(all)
+else
+ AC_MSG_RESULT(only command line tools)
+ build_apol=no
+fi
+
+# check for Tk if both --enable-swig-tcl and --enable-gui are given
+if test ${build_apol} = "yes"; then
+ TEA_PATH_TKCONFIG
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ AC_MSG_ERROR([No Tk install found.])
+ fi
+ TEA_LOAD_TKCONFIG
+ AC_MSG_CHECKING([for Tk 8.4 or greater])
+ if test \( "${TK_MAJOR_VERSION}" -gt 8 \) -o \( "${TK_MAJOR_VERSION}" -eq 8 -a "${TK_MINOR_VERSION}" -ge 4 \); then
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_ERROR([setools requires Tk 8.4 or greater])
+ fi
+ TEA_PUBLIC_TK_HEADERS
+ TEA_PATH_X
+ TEA_PROG_WISH
+fi
+
+AC_ARG_ENABLE(bwidget-check,
+ AC_HELP_STRING([--disable-bwidget-check],
+ [do not check for BWidget 1.8, assume it exists]),
+ enable_bwidget="$enableval",
+ enable_bwidget="yes")
+if test ${build_apol} = "no"; then
+ enable_bwidget=no
+else
+ AC_MSG_CHECKING([for BWidget 1.7 or greater])
+fi
+
+TCL_AUTOPATH=''
+if test "x${enable_bwidget}" = "xyes"; then
+ # certain distributions do not properly set the auto_path to point
+ # to Tk, so try to detect it here
+ changequote(<<<,>>>)dnl
+ (echo "if {[catch {package require Tk}]} {exit 1} ; exit 0" | ${TCLSH_PROG}) || \
+ TCL_AUTOPATH=$(find ${TK_EXEC_PREFIX} -maxdepth 3 -name pkgIndex.tcl -path '*tk*' -printf %h)
+ echo "lappend auto_path ${TCL_AUTOPATH}" > ./conftest.tcl
+ cat >> ./conftest.tcl <<EOF
+if {[catch {package require Tk}]} {
+ puts stderr "Could not load Tk."
+ exit 1
+}
+if {![catch {package require BWidget} bwidget_version]} {
+ if {[package vcompare \$bwidget_version 1.8] >= 0} {
+ exit 0
+ } elseif {[package vcompare \$bwidget_version 1.7] >= 0} {
+ exit 17
+ }
+}
+exit 16
+EOF
+ changequote([,])dnl
+ ${TCLSH_PROG} ./conftest.tcl
+ case $? in
+ 0)
+ AC_MSG_RESULT([1.8+])
+ copy_bwidget=no
+ bwidget_ver=1.8
+ ;;
+ 1)
+ AC_MSG_ERROR([failed. Try using --disable-bwidget-check flag if compiling in a non-graphical environment.])
+ ;;
+ 17)
+ AC_MSG_RESULT([1.7; will patch apol for compatibility])
+ copy_bwidget=no
+ bwidget_ver=1.7
+ ;;
+ *)
+ AC_MSG_WARN([not found, using setools's BWidget 1.8 instead])
+ copy_bwidget=yes
+ bwidget_ver=1.8
+ ;;
+ esac
+ if test ${copy_bwidget} = yes; then
+ changequote(<<<,>>>)dnl
+ echo 'puts [file join $tcl_library BWidget1.8]' > ./conftest.tcl
+ changequote([,])dnl
+ BWIDGET_DESTDIR=`${TCLSH_PROG} ./conftest.tcl`
+ AC_SUBST(BWIDGET_DESTDIR)
+ fi
+else
+ AC_MSG_RESULT([skipped, assuming 1.8+ is installed])
+ copy_bwidget=no
+ bwidget_ver=1.8
+fi
+
+if test "x${enable_debug}" = xyes; then
+ TCL_AUTOPATH+=' $(top_builddir)/libqpol/swig/tcl $(top_builddir)/libapol/swig/tcl $(top_builddir)/libsefs/swig/tcl $(top_builddir)/libpolsearch/swig/tcl $(top_builddir)/apol'
+else
+ TCL_AUTOPATH+=' $(libdir)/setools'
+fi
+AC_SUBST(TCL_AUTOPATH)
+
+
+dnl ** look for selinuxfs mount point
+
+AC_ARG_ENABLE(selinux-check,
+ AC_HELP_STRING([--disable-selinux-check],
+ [do not check for running SELinux, assume it is there]),
+ enable_selinux_check="$enableval",
+ enable_selinux_check="yes")
+if test "x${enable_selinux_check}" = "xyes"; then
+ AC_MSG_CHECKING([for running SELinux system])
+ if test ! -r "/selinux/policyvers" ; then
+ AC_MSG_WARN([could not read /selinux/policyvers - disabling runtime SELinux support])
+ use_selinux=no
+ else
+ AC_MSG_RESULT(yes)
+ use_selinux=yes
+ fi
+else
+ use_selinux=yes
+fi
+
+AC_CACHE_SAVE
+
+dnl ** find libselinux and libsepol
+
+AC_ARG_WITH(sepol-devel,
+ AC_HELP_STRING([--with-sepol-devel],
+ [prefix where sepol development files are installed]),
+ sepol_devel="$withval",
+ sepol_devel="/usr")
+AC_ARG_WITH(selinux-devel,
+ AC_HELP_STRING([--with-selinux-devel],
+ [prefix where SELinux development files are installed]),
+ selinux_devel="$withval",
+ selinux_devel="/usr")
+sepol_devel_incdir="${sepol_devel}/include"
+selinux_devel_incdir="${selinux_devel}/include"
+dnl if /lib64 exists then use that directory, otherwise revert to just /lib
+for dir in lib64 lib ; do
+ sepol_devel_libdir="${sepol_devel}/${dir}"
+ if test -f ${sepol_devel_libdir}/libsepol.so ; then
+ break
+ fi
+done
+for dir in lib64 lib ; do
+ selinux_devel_libdir="${selinux_devel}/${dir}"
+ if test -f ${selinux_devel_libdir}/libselinux.so ; then
+ break
+ fi
+done
+AC_MSG_CHECKING([for sepol/sepol.h])
+selinux_save_CFLAGS="${CFLAGS}"
+selinux_save_CPPFLAGS="${CPPFLAGS}"
+CFLAGS="${CFLAGS} -I${sepol_devel_incdir} -I${selinux_devel_incdir}"
+CPPFLAGS="${CPPFLAGS} -I${sepol_devel_incdir} -I${selinux_devel_incdir}"
+AC_CHECK_HEADER([sepol/sepol.h], , AC_MSG_ERROR([could not find sepol headers at $sepol_devel_incdir - make sure libsepol-devel is installed]))
+AC_CHECK_LIB([sepol], [sepol_policydb_read], ,
+ AC_MSG_ERROR([could not find libsepol at $sepol_devel_libdir]))
+AC_CHECK_HEADER([selinux/selinux.h], , AC_MSG_ERROR([could not find selinux headers at $selinux_devel_incdir - make sure libselinux-devel is installed]))
+AC_CHECK_HEADER([selinux/context.h], , AC_MSG_ERROR([could not find selinux headers at $selinux_devel_incdir - make sure libselinux-devel is installed]))
+AC_CHECK_LIB([selinux], [selinux_policy_root], ,
+AC_MSG_ERROR([could not find libselinux at $selinux_devel_libdir]),
+ -lsepol)
+SELINUX_LIB_FLAG="-L${sepol_devel_libdir} -L${selinux_devel_libdir}"
+CFLAGS="${selinux_save_CFLAGS}"
+CPPFLAGS="${selinux_save_CPPFLAGS}"
+
+AC_ARG_ENABLE(sepol-src,
+ AC_HELP_STRING([--enable-sepol-src=PATH],
+ [use another sepol source tree at PATH (developer only flag)]),
+ sepol_srcdir="$enableval",
+ sepol_srcdir="")
+if test "x${sepol_srcdir}" = "x"; then
+ sepol_srcdir=${sepol_devel_libdir}
+ AC_CHECK_FILE([${sepol_srcdir}/libsepol.a],,
+ AC_MSG_ERROR([make sure libsepol-static is installed]))
+else
+ AC_MSG_CHECKING([for compatible sepol source tree])
+ sepol_version=${sepol_srcdir}/VERSION
+ if test ! -r $sepol_version; then
+ AC_MSG_ERROR([could not read $sepol_version])
+ fi
+ version_sepol_major=$(cut -d. -f1 < ${sepol_version})
+ version_sepol_minor=$(cut -d. -f2 < ${sepol_version})
+ version_sepol_patch=$(cut -d. -f3 < ${sepol_version})
+ version_ok=""
+ if test $version_sepol_major -gt $version_min_sepol_major; then
+ version_ok=1
+ elif test $version_sepol_major -eq $version_min_sepol_major -a \
+ $version_sepol_minor -gt $version_min_sepol_minor; then
+ version_ok=1
+ elif test $version_sepol_major -eq $version_min_sepol_major -a \
+ $version_sepol_minor -eq $version_min_sepol_minor -a \
+ $version_sepol_patch -ge $version_min_sepol_patch; then
+ version_ok=1
+ fi
+ if test -z $version_ok; then
+ version_min_sepol="${version_min_sepol_major}.${version_min_sepol_minor}.${version_min_sepol_patch}"
+ version_found_sepol="${version_sepol_major}.${version_sepol_minor}.${version_sepol_patch}"
+ AC_MSG_ERROR([setools requires sepol version $version_min_sepol or greater (found $version_found_sepol)])
+ fi
+ AC_MSG_RESULT([yes])
+ sepol_srcdir="${sepol_srcdir}/src"
+ sepol_src_save_CFLAGS="${CFLAGS}"
+ sepol_src_save_CPPFLAGS="${CPPFLAGS}"
+ CFLAGS="${CFLAGS} -I${sepol_srcdir}/../include"
+ CPPFLAGS="${CPPFLAGS} -I${sepol_srcdir}/../include"
+ AC_CHECK_HEADER([sepol/policydb/policydb.h], , AC_MSG_ERROR([could not find sepol source tree]))
+ CFLAGS="${sepol_src_save_CFLAGS}"
+ CPPFLAGS="${sepol_src_save_CPPFLAGS}"
+ AC_CHECK_FILE([${sepol_srcdir}/libsepol.a],,
+ AC_MSG_ERROR([could not find precompiled libsepol.a]))
+ sepol_devel_incdir="${sepol_srcdir}/../include"
+fi
+SELINUX_CFLAGS="-I${sepol_devel_incdir} -I${selinux_devel_incdir}"
+AC_SUBST(SELINUX_CFLAGS)
+AC_SUBST(SELINUX_LIB_FLAG)
+
+dnl ** check for certain features of libsepol
+
+sepol_save_CFLAGS="${CFLAGS}"
+sepol_save_CPPFLAGS="${CPPFLAGS}"
+CFLAGS="${CFLAGS} ${SELINUX_CFLAGS} ${SELINUX_LIB_FLAG}"
+CPPFLAGS="${CPPFLAGS} ${SELINUX_CFLAGS}"
+
+dnl check for user and role mapping, added in libsepol version 2.0.29
+AC_MSG_CHECKING([for user and role mappings])
+AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE([
+#include <sepol/policydb/expand.h>
+int main () {
+ return role_set_expand(NULL, NULL, NULL, NULL);
+}])],
+ sepol_new_user_role_mapping="yes",
+ sepol_new_user_role_mapping="no")
+AC_MSG_RESULT([${sepol_new_user_role_mapping}])
+
+dnl check for permissive types, added in libsepol version 2.0.26
+AC_CHECK_DECL([TYPE_FLAGS_PERMISSIVE],
+ sepol_new_permissive_types="yes",
+ sepol_new_permissive_types="no",
+ [#include <sepol/policydb/policydb.h>])
+
+dnl check for modified AV tab behavior, introduced in libsepol version 2.0.20
+AC_CHECK_DECL([avtab_alloc],
+ sepol_new_avtab="yes",
+ sepol_new_avtab="no",
+ [#include <sepol/policydb/conditional.h>])
+
+dnl check for policycap, added in libsepol version 2.0.18
+AC_CHECK_MEMBER([struct policydb.policycaps.node],
+ sepol_new_policycaps="yes",
+ sepol_new_policycaps="no",
+ [#include <sepol/policydb/policydb.h>])
+
+dnl check for broken boolmap behavior in libsepol version 2.0.2
+AC_CHECK_DECL([cond_node_create], sepol_check_boolmap="yes", sepol_check_boolmap="no", [#include <sepol/policydb/conditional.h>])
+if test ${sepol_check_boolmap} = "yes"; then
+ dnl check for boolmap parameter that is fixed in version 2.0.3 through
+ dnl 2.0.28. version 2.0.29 (see check above) implies that this check
+ dnl will succeed.
+ if test ${sepol_new_user_role_mapping} = "no"; then
+ AC_MSG_CHECKING([for corrected libsepol boolean expand behavior])
+ AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE([
+#include <sepol/policydb/expand.h>
+int main () {
+ return expand_module_avrules(NULL, NULL, NULL, NULL, NULL, 0, 0);
+}])],
+ AC_MSG_RESULT([yes]),
+ AC_MSG_ERROR([this version of libsepol is incompatible with SETools]))
+ fi
+ sepol_new_expand_boolmap="yes"
+else
+ sepol_new_expand_boolmap="no"
+fi
+
+dnl check for new error code format, added in libsepol 2.0.0
+AC_CHECK_HEADER([sepol/errcodes.h],
+ sepol_new_errcodes="yes",
+ sepol_new_errcodes="no")
+
+AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([
+#include <sepol/policydb/policydb.h>
+#include <stdio.h>
+#include <stdlib.h>
+int main(void) {
+ FILE *f = fopen("conftest.data", "w");
+ if (f != NULL && fprintf(f, "%d", POLICYDB_VERSION_MAX) > 0) {
+ fclose(f);
+ exit(EXIT_SUCCESS);
+ }
+ exit(EXIT_FAILURE);
+}])],
+ sepol_policy_version_max=`cat conftest.data`,
+ AC_MSG_FAILURE([could not determine maximum libsepol policy version]))
+AC_DEFINE_UNQUOTED(SEPOL_POLICY_VERSION_MAX, ${sepol_policy_version_max}, [maximum policy version supported by libsepol])
+CFLAGS="${sepol_save_CFLAGS}"
+CPPFLAGS="${sepol_save_CPPFLAGS}"
+
+if test ${use_selinux} = "yes"; then
+dnl Locate selinux policy root directory
+ AC_MSG_CHECKING([for selinux policy root])
+ changequote(<<<,>>>)dnl
+ cat > ./conftest.c <<EOF
+#include <stdlib.h>
+#include <stdio.h>
+#include <selinux/selinux.h>
+int
+main ()
+{
+ const char *selinux_dir = selinux_policy_root();
+ fputs(selinux_dir, stdout);
+ return 0;
+}
+EOF
+ changequote([,])dnl
+ selinux_save_CFLAGS="${CFLAGS}"
+ CFLAGS="${SELINUX_CFLAGS} ${SELINUX_LIB_FLAG} -lselinux -lsepol ${CFLAGS}"
+ gcc ${CFLAGS} -o conftest conftest.c >&5
+ selinux_policy_dir=`./conftest`
+ AC_MSG_RESULT(${selinux_policy_dir})
+ CFLAGS="${selinux_save_CFLAGS}"
+fi
+
+dnl check for policy source
+if test ${use_selinux} = "yes"; then
+ AC_ARG_WITH(default-policy,
+ AC_HELP_STRING([--with-default-policy],
+ [path to default policy file]),
+ selinux_default_policy="$withval",
+ selinux_default_policy="${selinux_policy_dir}/src/policy/policy.conf")
+ AC_MSG_CHECKING([for policy source])
+ if test ! -r "${selinux_default_policy}" ; then
+ AC_MSG_WARN([could not read default policy ${selinux_default_policy}])
+ else
+ AC_MSG_RESULT(${selinux_default_policy})
+ fi
+fi
+
+AC_ARG_WITH(test-policies,
+ AC_HELP_STRING([--with-test-policies],
+ [directory containing test policies (developer only flag)]),
+ test_policies="$withval",
+ test_policies=".")
+
+AC_CACHE_SAVE
+
+dnl check for pkg-config
+AC_CHECK_PROG(has_pkg_config, pkg-config, yes, no)
+if test ${has_pkg_config} = "no"; then
+ AC_MSG_ERROR([pkg-config not found])
+fi
+
+AC_MSG_CHECKING([for libxml])
+pkg-config --exists 'libxml-2.0'
+if test $? -ne 0 ; then
+ AC_MSG_ERROR([setools requires libxml-2.0])
+fi
+XML_CFLAGS=`pkg-config --cflags libxml-2.0`
+XML_LIBS=`pkg-config --libs libxml-2.0`
+AC_MSG_RESULT([yes])
+AC_SUBST(XML_CFLAGS)
+AC_SUBST(XML_LIBS)
+
+AC_MSG_CHECKING([for sqlite3])
+pkg-config --exists 'sqlite3 >= 3.2.0'
+if test $? -ne 0 ; then
+ AC_MSG_ERROR([setools requires sqlite3 3.2.0 or greater])
+fi
+SQLITE3_CFLAGS=`pkg-config --cflags sqlite3`
+SQLITE3_LIBS=`pkg-config --libs sqlite3`
+AC_MSG_RESULT([yes])
+AC_SUBST(SQLITE3_CFLAGS)
+AC_SUBST(SQLITE3_LIBS)
+
+SEAUDIT_LIB_FLAG+=" ${XML_LIBS}"
+SEFS_LIB_FLAG+=" ${SQLITE3_LIBS}"
+
+gtk_version_2_8=1
+if test "x${build_gui}" = xyes; then
+
+ AC_MSG_CHECKING([for GTK])
+ pkg-config --atleast-version=2.4 gtk+-2.0
+ if test $? -ne 0; then
+ AC_MSG_ERROR([setools requires GTK+ 2.4 or greater])
+ fi
+ pkg-config --atleast-version=2.8 gtk+-2.0
+ gtk_version_2_8=$?
+ GTK_CFLAGS=`pkg-config --cflags gtk+-2.0`
+ GTK_LIBS=`pkg-config --libs gtk+-2.0`
+ AC_MSG_RESULT([yes])
+ AC_SUBST(GTK_CFLAGS)
+ AC_SUBST(GTK_LIBS)
+
+ AC_MSG_CHECKING([for libglade])
+ pkg-config --exists 'libglade-2.0'
+ if test $? -ne 0; then
+ AC_MSG_ERROR([setools requires libglade-2.0; make sure libglade2-devel is installed])
+ fi
+ GLADE_CFLAGS=`pkg-config --cflags libglade-2.0`
+ GLADE_LIBS=`pkg-config --libs libglade-2.0`
+ AC_MSG_RESULT([yes])
+ AC_SUBST(GLADE_CFLAGS)
+ AC_SUBST(GLADE_LIBS)
+
+ AC_MSG_CHECKING([for gdk-pixbuf])
+ pkg-config --exists 'gdk-pixbuf-2.0'
+ if test $? -ne 0; then
+ AC_MSG_ERROR([setools requires gdk-pixbuf-2.0])
+ fi
+ PIXBUF_CFLAGS=`pkg-config --cflags gdk-pixbuf-2.0`
+ PIXBUF_LIBS=`pkg-config --libs gdk-pixbuf-2.0`
+ AC_MSG_RESULT([yes])
+ AC_SUBST(PIXBUF_CFLAGS)
+ AC_SUBST(PIXBUF_LIBS)
+
+ AC_MSG_CHECKING([for gthread])
+ pkg-config --exists 'gthread-2.0'
+ if test $? -ne 0; then
+ AC_MSG_ERROR([setools requires gthread-2.0])
+ fi
+ GTHREAD_CFLAGS=`pkg-config --cflags gthread-2.0`
+ GTHREAD_LIBS=`pkg-config --libs gthread-2.0`
+ AC_MSG_RESULT([yes])
+ AC_SUBST(GTHREAD_CFLAGS)
+ AC_SUBST(GTHREAD_LIBS)
+fi
+
+have_cunit="no"
+AC_CHECK_LIB(cunit,
+ CU_initialize_registry,
+ [have_cunit="yes"
+ CUNIT_LIB_FLAG="-lcunit"],
+ [AC_CHECK_LIB(cunit,
+ CU_curses_run_tests,
+ [have_cunit="yes"
+ CUNIT_LIB_FLAG="-lcunit -lncurses"],
+ AC_MSG_WARN([Compatible CUnit not found; "make check" will fail.]),
+ -lncurses)
+ ]
+)
+AC_SUBST([CUNIT_LIB_FLAG])
+
+AC_CHECK_LIB(bz2,
+ BZ2_bzReadOpen, ,
+ AC_MSG_ERROR([could not find libbz2 - make sure bzip2-libs is installed]),
+ -lbz2
+)
+
+#AC_MSG_CHECKING([for FUSE])
+#pkg-config --exists fuse
+#if test $? -ne 0; then
+# AC_MSG_WARN([FUSE not found; libsefs tests will not be built])
+# have_fuse="no"
+#else
+# FUSE_CFLAGS=`pkg-config --cflags fuse`
+# FUSE_LIBS=`pkg-config --libs fuse`
+# AC_SUBST(FUSE_CFLAGS)
+# AC_SUBST(FUSE_LIBS)
+# have_fuse="yes"
+# AC_MSG_RESULT([yes])
+#fi
+
+dnl set up variables here
+if test ${use_selinux} = "yes"; then
+ AC_DEFINE(LIBSELINUX, 1, [enable libselinux-specific code])
+fi
+
+AM_CONDITIONAL(DO_SWIGIFY, test ${do_swigify} = "yes")
+AM_CONDITIONAL(DO_SWIGIFY_PYTHON, test ${do_swigify_python} = "yes")
+AM_CONDITIONAL(DO_SWIGIFY_JAVA, test ${do_swigify_java} = "yes")
+AM_CONDITIONAL(DO_SWIGIFY_TCL, test ${do_swigify_tcl} = "yes")
+AM_CONDITIONAL(COPY_BWIDGET, test ${copy_bwidget} = "yes")
+AM_CONDITIONAL(BUILD_APOL, test ${build_apol} = "yes")
+AM_CONDITIONAL(BUILD_GUI, test ${build_gui} = "yes")
+#AM_CONDITIONAL(DO_FUSE, test ${have_fuse} = "yes")
+
+profile_install_dir='${setoolsdir}/sechecker-profiles'
+
+AC_SUBST(VERSION)
+AC_SUBST(javadir)
+AC_SUBST(sepol_srcdir)
+AC_SUBST(libqpol_soversion)
+AC_SUBST(libqpol_version)
+AC_SUBST(libqpol_soname)
+AC_SUBST(libqpol_pyswig_soname)
+AC_SUBST(libqpol_jswig_soname)
+AC_SUBST(libqpol_tswig_soname)
+AC_SUBST(libapol_soversion)
+AC_SUBST(libapol_version)
+AC_SUBST(libapol_soname)
+AC_SUBST(libapol_pyswig_soname)
+AC_SUBST(libapol_jswig_soname)
+AC_SUBST(libapol_tswig_soname)
+AC_SUBST(libpoldiff_soversion)
+AC_SUBST(libpoldiff_version)
+AC_SUBST(libpoldiff_soname)
+AC_SUBST(libpoldiff_pyswig_soname)
+AC_SUBST(libpoldiff_jswig_soname)
+AC_SUBST(libpoldiff_tswig_soname)
+AC_SUBST(libsefs_soversion)
+AC_SUBST(libsefs_version)
+AC_SUBST(libsefs_soname)
+AC_SUBST(libsefs_pyswig_soname)
+AC_SUBST(libsefs_jswig_soname)
+AC_SUBST(libsefs_tswig_soname)
+AC_SUBST(libseaudit_soversion)
+AC_SUBST(libseaudit_version)
+AC_SUBST(libseaudit_soname)
+AC_SUBST(libseaudit_pyswig_soname)
+AC_SUBST(libseaudit_jswig_soname)
+AC_SUBST(libseaudit_tswig_soname)
+AC_SUBST(setoolsdir)
+AC_SUBST(selinux_policy_dir)
+AC_SUBST(selinux_default_policy)
+AC_SUBST(profile_install_dir)
+
+AC_DEFINE_UNQUOTED(LIBAPOL_VERSION_STRING, "${libapol_version}", [libapol version])
+AC_DEFINE_UNQUOTED(LIBQPOL_VERSION_STRING, "${libqpol_version}", [libqpol version])
+AC_DEFINE_UNQUOTED(LIBPOLDIFF_VERSION_STRING, "${libpoldiff_version}", [libpoldiff version])
+AC_DEFINE_UNQUOTED(LIBSEFS_VERSION_STRING, "${libsefs_version}", [libsefs version])
+AC_DEFINE_UNQUOTED(LIBSEAUDIT_VERSION_STRING, "${libseaudit_version}", [libapol version])
+AC_DEFINE_UNQUOTED(TEST_POLICIES, "${test_policies}", [location of testing policies])
+
+if test ${sepol_new_errcodes} != "yes" ; then
+ AC_DEFINE(SEPOL_ENOMEM, HASHTAB_OVERFLOW, [remap of libsepol 1.x.x define to 2.x.x])
+ AC_DEFINE(SEPOL_EEXIST, HASHTAB_PRESENT, [remap of libsepol 1.x.x define to 2.x.x])
+else
+ AC_DEFINE(HAVE_SEPOL_ERRCODES, 1, [if libsepol has errcodes.h])
+fi
+if test ${sepol_new_expand_boolmap} == "yes" ; then
+ AC_DEFINE(HAVE_SEPOL_BOOLMAP, 1, [sepol's new expand boolmap behavior])
+fi
+if test ${sepol_new_policycaps} == "yes"; then
+ AC_DEFINE(HAVE_SEPOL_POLICYCAPS, 1, [if libsepol has policycaps])
+fi
+if test ${sepol_new_avtab} == "yes"; then
+ AC_DEFINE(SEPOL_DYNAMIC_AVTAB, 1, [if avtab sizes are calculated dynamically by loader or are hardcoded])
+fi
+if test ${sepol_new_permissive_types} == "yes"; then
+ AC_DEFINE(HAVE_SEPOL_PERMISSIVE_TYPES, 1, [if types can be marked as permissive])
+fi
+if test ${sepol_new_user_role_mapping} == "yes"; then
+ AC_DEFINE(HAVE_SEPOL_USER_ROLE_MAPPING, 1, [if users and roles are mapped during policy expansion])
+fi
+
+if test ${use_shared} == "yes"; then
+ AC_DEFINE(LINK_SHARED, 1, [link programs using shared libraries])
+else
+ AC_DEFINE(LINK_SHARED, 0, [link programs using shared libraries])
+fi
+
+if test ${gtk_version_2_8} -eq 0 ; then
+ AC_DEFINE(GTK_2_8, 1, [GTK+ is version 2.8+])
+fi
+
+# For older version of BWidget, overcome bug in NoteBook::bindtabs and
+# add a new -autopost option to ComboBox.
+AC_CONFIG_COMMANDS([config.tcl], [[
+ echo "" > ./config.tcl
+ if test ${bwidget_ver} = 1.7; then
+ cat >> ./config.tcl <<EOF
+proc tcl_config_patch_bwidget {} {
+ # explicitly pull in old BWidget code before patching
+ NoteBook .foo ; destroy .foo
+ ComboBox .foo ; destroy .foo
+ MainFrame .foo ; destroy .foo
+EOF
+ cat ${ac_top_srcdir}/packages/combobox.tcl >> ./config.tcl
+ cat ${ac_top_srcdir}/packages/mainframe.tcl >> ./config.tcl
+ cat ${ac_top_srcdir}/packages/notebook.tcl >> ./config.tcl
+ echo "}" >> ./config.tcl
+ fi
+ echo "proc tcl_config_init {} {" >> ./config.tcl
+ if test "x${enable_debug}" = xyes; then
+ cat >> ./config.tcl <<EOF
+ namespace eval ::tkcon {
+ variable OPT
+ variable PRIV
+ set PRIV(showOnStartup) 0
+ set PRIV(root) .console
+ set PRIV(protocol) {tkcon hide}
+ set OPT(exec) ""
+ }
+ if {![catch {uplevel \#0 source tkcon.tcl}]} {
+ package require tkcon
+ bind . <F8> {tkcon show}
+ }
+EOF
+ fi
+ echo "}" >> ./config.tcl
+ echo "proc tcl_config_get_version {} {" >> ./config.tcl
+ echo " return ${version}" >> ./config.tcl
+ echo "}" >> ./config.tcl
+]], [bwidget_ver=$bwidget_ver; enable_debug=$enable_debug; version=$VERSION])
+
+AC_CONFIG_FILES([Makefile VERSION \
+ libqpol/Makefile libqpol/src/Makefile libqpol/include/Makefile libqpol/include/qpol/Makefile libqpol/tests/Makefile \
+ libqpol/swig/Makefile libqpol/swig/python/Makefile libqpol/swig/java/Makefile libqpol/swig/java/MANIFEST.MF libqpol/swig/tcl/Makefile \
+ libapol/Makefile libapol/src/Makefile libapol/include/Makefile libapol/include/apol/Makefile libapol/tests/Makefile \
+ libapol/swig/Makefile libapol/swig/python/Makefile libapol/swig/java/Makefile libapol/swig/java/MANIFEST.MF libapol/swig/tcl/Makefile \
+ libpoldiff/Makefile libpoldiff/src/Makefile libpoldiff/include/Makefile libpoldiff/include/poldiff/Makefile libpoldiff/tests/Makefile \
+ libpoldiff/swig/Makefile libpoldiff/swig/python/Makefile libpoldiff/swig/java/Makefile libpoldiff/swig/java/MANIFEST.MF libpoldiff/swig/tcl/Makefile \
+ libsefs/Makefile libsefs/src/Makefile libsefs/include/Makefile libsefs/include/sefs/Makefile libsefs/tests/Makefile \
+ libsefs/swig/Makefile libsefs/swig/python/Makefile libsefs/swig/java/Makefile libsefs/swig/tcl/Makefile libsefs/swig/java/MANIFEST.MF \
+ libseaudit/Makefile libseaudit/src/Makefile libseaudit/include/Makefile libseaudit/include/seaudit/Makefile libseaudit/tests/Makefile \
+ libseaudit/swig/Makefile libseaudit/swig/python/Makefile libseaudit/swig/java/Makefile libseaudit/swig/java/MANIFEST.MF libseaudit/swig/tcl/Makefile \
+ secmds/Makefile \
+ apol/Makefile \
+ sechecker/Makefile \
+ seaudit/Makefile \
+ sediff/Makefile \
+ man/Makefile \
+ debian/Makefile \
+ packages/Makefile packages/rpm/Makefile \
+ packages/libqpol.pc packages/libapol.pc packages/libpoldiff.pc packages/libseaudit.pc packages/libsefs.pc])
+
+AC_OUTPUT
+
+echo "*** Configuration complete. Do \`make help' to get a list of SETools targets."
diff --git a/debian/Makefile.am b/debian/Makefile.am
new file mode 100644
index 0000000..fc50db3
--- /dev/null
+++ b/debian/Makefile.am
@@ -0,0 +1,24 @@
+dist_noinst_DATA = \
+ changelog \
+ compat \
+ control \
+ copyright \
+ docs \
+ libsetools-dev.install \
+ libsetools.install \
+ libsetools-java.install \
+ libsetools-jni.install \
+ libsetools-jni.postinst \
+ libsetools-jni.postrm \
+ libsetools.postinst \
+ libsetools.postrm \
+ libsetools-python.install \
+ libsetools-python.postinst \
+ libsetools-tcl.install \
+ rules \
+ setools-console.install \
+ setools.install \
+ setools.menu \
+ setools.postinst \
+ setools.postrm \
+ watch
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..2748e4a
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,362 @@
+setools (3.3.5-tresys0) unstable; urgency=low
+
+ * Merged SETools 3.3.5 release into this package.
+
+ -- J. Tang <jtang@tresys.com> Fri, 15 Aug 2008 12:00:00 -0500
+
+setools (3.3.4-0ubuntu1) hardy; urgency=low
+
+ * Merged SETools 3.3.4 release into this package.
+
+ -- J. Tang <jtang@tresys.com> Fri, 7 Mar 2008 12:00:00 -0500
+
+setools (3.3.3-0ubuntu1) hardy; urgency=low
+
+ * Merged SETools 3.3.3 release into this package.
+
+ -- J. Tang <jtang@tresys.com> Thu, 21 Feb 2008 12:00:00 -0500
+
+setools (3.3.2-0ubuntu3) hardy; urgency=low
+
+ * debian/control
+ - Changed from java2-compiler to java2-sdk in build depends
+ * debian/rules
+ - Set JAVA_HOME environment variable for icedtea
+ - Set the --with-java-prefix configure flag
+
+ -- Joseph Jackson IV <jjacksoniv@fluxbuntu.org> Sat, 02 Feb 2008 20:44:56 -0500
+
+setools (3.3.2-0ubuntu2) hardy; urgency=low
+
+ * debian/control
+ - Update Debian Maintainer field
+
+ -- Joseph Jackson IV <jjacksoniv@fluxbuntu.org> Sat, 02 Feb 2008 19:04:33 -0500
+
+setools (3.3.2-0ubuntu1) hardy; urgency=low
+
+ * Merged SETools 3.3.2 release into this package.
+
+ -- J. Tang <jtang@tresys.com> Tue, 29 Jan 2008 12:00:00 -0500
+
+setools (2.4-3) unstable; urgency=low
+
+ * Bug fix: "symlink error on /usr/share/setools/seaudit-report.conf",
+ thanks to Kevin Mark (Closes: #396495).
+
+ -- Manoj Srivastava <srivasta@debian.org> Mon, 6 Nov 2006 10:12:40 -0600
+
+setools (2.4-2) unstable; urgency=low
+
+ * Since we have flex scanners built into our libapol shared libraries,
+ and that needs to link with the flex library, which is static;
+ therefore we need to explicitly link with libfl_pic, which has the
+ position independent code required for all components of a shared
+ library.
+ * Split up setools into multiple packages, so that the shared libraries
+ and devel tools can be provided separately. Split up the single
+ package setools into several library packages, since it was pointed
+ out to me that the package was in violation of §8.2 of the Debian
+ policy. Now each library has a runtime and a dev package, so each
+ library can develop at its own pace.
+ * Bug fix: "setools: FTBFS (amd64): libfl.a(libyywrap.o): relocation
+ R_X86_64_32 against `a local symbol' can not be used when making a
+ shared object; recompile with -fPIC", thanks to Andreas Jochens
+ (Closes: #373170).
+ * Bug fix: "setools: Please adjust SELinux spelling to that of other
+ SELinux-related packages", thanks to Sven Mueller (Closes: #376613).
+ * Provide md5sums
+ * Also, tweak some file paths to better reflect a default Debian
+ installation.
+
+ -- Manoj Srivastava <srivasta@debian.org> Thu, 7 Sep 2006 19:54:50 -0500
+
+setools (2.4-1) unstable; urgency=low
+
+ * New upstream release
+ apol:
+ File contexts tab now allows for MLS range searching if
+ the loaded database is from a MLS filesystem.
+ Policy statistics dialog now shows MLS and ocontexts
+ summaries.
+ libapol:
+ Added support for loading base policies containing optionals.
+ Added support for searching range transitions containing
+ attributes.
+ libseaudit:
+ Bugfix to support parsing FC5-style audit logs.
+ seaudit:
+ Added date filters.
+ secmds:
+ Added support to indexcon and searchcon for MLS filesytems.
+ Added support to findcon and replcon for MLS filesystems.
+ sechecker:
+ Added incomplete network access (inc_net_access) module.
+ Added unreachable domains (unreachable_doms) module.
+ Added impossible range transitions (imp_range_trans) module.
+ sesearch:
+ Allow user to search range transitions by attributes and
+ indirect matching.
+ Added RBAC searching.
+
+ -- Manoj Srivastava <srivasta@debian.org> Sun, 7 May 2006 00:16:22 -0500
+
+setools (2.3-1) unstable; urgency=low
+
+ * New upstream release
+ apol:
+ - added new MLS components tab for sensitivities, levels, and
+ categories.
+ - changed users tab to support ranges and default levels.
+ - added range transition tab for searching range transition rules.
+ - added new tab for network context components.
+ - added new tab for file system context components.
+ libapol:
+ - added binpol support for MLS, network contexts, and file system
+ contexts.
+ seinfo:
+ - added command line options for MLS components.
+ - added command line options for network contexts and file system contexts.
+ sesearch:
+ - added command line option for searching for rules by conditional boolean name.
+ seaudit:
+ - added new column in the log view for the 'comm'
+ - field found in auditd log files.
+ - added filters for the 'comm' field and 'message' field.
+ manpages:
+ -added manpages for all tools.
+
+ -- Manoj Srivastava <srivasta@debian.org> Tue, 7 Feb 2006 11:54:03 -0600
+
+setools (2.2-2) unstable; urgency=low
+
+ * Added a preinst check to see if we should remove the obsolete conffile
+ /etc/setools/seuser.conf
+
+ -- Manoj Srivastava <srivasta@debian.org> Sat, 31 Dec 2005 14:51:20 -0600
+
+setools (2.2-1) unstable; urgency=low
+
+ * New upstream release
+ libapol:
+ replaced the original dta algorithm with a new one to properly
+ support complements in rules. added new structures to support the
+ separation of diff elements. added support for parsing additional
+ policy components in source policies.
+ sediff:
+ enhanced the GUI for display and separation of diff elements. added
+ the ability to rename types.
+ sechecker:
+ added a new tool - a commandline modular and extensible policy
+ checker program
+ seuser: removed - deprecated
+ sepcut: removed - deprecated
+
+ -- Manoj Srivastava <srivasta@debian.org> Fri, 30 Dec 2005 01:32:46 -0600
+
+setools (2.1.3-1) unstable; urgency=low
+
+ * New upstream release, fixed a mls bug in the source parser.
+
+ -- Manoj Srivastava <srivasta@debian.org> Sun, 16 Oct 2005 23:58:07 -0500
+
+setools (2.1.2-1) unstable; urgency=low
+
+ * New upstream release
+
+
+ -- Manoj Srivastava <srivasta@debian.org> Thu, 15 Sep 2005 02:00:01 -0500
+
+setools (2.1.1-1) unstable; urgency=low
+
+ * New upstream release. Excerpted changes:
+ * Version 2.1.1
+ libseaudit: updated code to compile with gcc-4.0.0
+ minor bug fixes
+ sediff: updated code to compile with gcc-4.0.0
+ seaudit: updated code to compile with gcc-4.0.0
+ libsefs: updated code to compile with gcc-4.0.0
+ libapol: updated code to compile with gcc-4.0.0
+ minor bug fixes
+ seuser: updated code to compile with gcc-4.0.0
+ * Version 2.1.0
+ apol: improved direct relabel analysis algorithm
+ libapol: added policy version 19 support
+ sediff: added role transitions, improved role allow
+ added conditional expression differences
+
+ -- Manoj Srivastava <srivasta@debian.org> Wed, 1 Jun 2005 23:51:27 -0500
+
+setools (2.0-1) unstable; urgency=low
+
+ * New upstream release
+ * libsefs:
+ Converted to use an on-disk SQLite database backend and
+ re-designed API to provide the functionality to other
+ applications, such as apol.
+ * libapol:
+ Added support for analyzing direct file relabels.
+ Added support for analyzing relationship between two types.
+ Integrated use of hashtable structures for easily analyzing
+ differences between policies.
+ Minor bug fixes.
+ * libseuser:
+ Minor bug fixes.
+ * apol:
+ New analysis module for performing direct file relabel
+ analysis.
+ New analysis module for analyzing the relationship be-
+ tween two types.
+ New interface added for viewing file contexts from an
+ SELinux filesystem.
+ Improvements to domain transition analysis interface.
+ Minor bug fixes and GUI tweaks.
+ * secmds:
+ Updated indexcon/searchcon to use an on-disk SQLite database
+ in order to decrease memory use. These changes are not
+ backwards-compatible.
+ * seaudit:
+ Integrated reporting functionality into GUI.
+ Minor GUI tweaks.
+ * sediff:
+ New gtk GUI and command-line tools for analyzing the semantic
+ differences between two policies. The semantic difference
+ of a policy is different from the syntactic difference in
+ that it shows the cumulative effect of rules rather than
+ doing a line-by-line comparison.
+
+ -- Manoj Srivastava <srivasta@debian.org> Sun, 13 Mar 2005 00:40:06 -0600
+
+setools (1.5.1-1) unstable; urgency=low
+
+ * New upstream release.
+ *apol: Fixed compatibility with tcl 8.3.
+ *libsefs: Fixed compile problem on PPC.
+ *secmds: Fixed fatal error in replcon.
+ *setools: Reverted to static linking and fixed various small bugs.
+
+ * apol:
+ Advanced options added to forward domain
+ transition analysis module for performing
+ more granular searching of transitions to
+ domains using specified classes, permissions
+ and target types.
+ Minor bug fixes and improvements.
+
+ * libapol:
+ Fixed to handle new libapol user structs.
+ Enhanced forward domain transition analysis to
+ perform more granular searching using specified
+ classes, permissions and target types.
+ Minor bug fixes.
+
+ * libseuser:
+ General clean up of the policy components.
+ Fixed handling of users to be consistent with rest.
+
+ * seaudit:
+ New tool (seaudit-report) for generating customized
+ reports on SE Linux audit messages using saved
+ seaudit view files. This tool is highly configurable
+ and can effectively integrate with the LogWatch
+ application for automating SE Linux audit log reporting.
+ Added feature for exporting audit messages to a
+ file, as well as viewing all components of an audit
+ message within a text view.
+
+ * libseaudit:
+ Updated library to store audit header information, such as
+ the system call timestamp and serial number.
+ Fixed parse errors for new logs.
+
+ * New tool (indexcon) for creating a snapshot of security
+ contexts for SE Linux filesystem entities.
+ * New tool (searchcon) for searching the SE Linux filesystem
+ database that was created using indexcon.
+
+
+ -- Manoj Srivastava <srivasta@debian.org> Thu, 13 Jan 2005 01:25:23 -0600
+
+setools (1.4.1-2) unstable; urgency=low
+
+ * Added build-depends libselinux1-dev, thanks for the NMU LaMont.
+
+ -- Russell Coker <russell@coker.com.au> Tue, 13 Jul 2004 19:35:00 +1000
+
+setools (1.4.1-1) unstable; urgency=low
+
+ * New upstream.
+
+ -- Russell Coker <russell@coker.com.au> Thu, 8 Jul 2004 12:54:00 +1000
+
+setools (1.4-1) unstable; urgency=low
+
+ * New upstream.
+
+ -- Russell Coker <russell@coker.com.au> Thu, 3 Jun 2004 12:49:00 +1000
+
+setools (1.3-1) unstable; urgency=low
+
+ * New upstream.
+
+ -- Russell Coker <russell@coker.com.au> Fri, 16 Apr 2004 20:16:00 +1000
+
+setools (1.2.1-1) unstable; urgency=low
+
+ * New upstream.
+
+ * Make it build-depend on tk8.4-dev.
+ Closes: 231876
+
+ -- Russell Coker <russell@coker.com.au> Thu, 26 Feb 2004 22:07:00 +1100
+
+setools (1.2-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Russell Coker <russell@coker.com.au> Fri, 6 Feb 2004 15:16:00 +1100
+
+setools (1.1.1-3) unstable; urgency=low
+
+ * Fixed build-depends.
+ Closes: #230830
+
+ -- Russell Coker <russell@coker.com.au> Tue, 3 Feb 2004 10:16:00 +1100
+
+setools (1.1.1-2) unstable; urgency=low
+
+ * Fixed build-depends.
+ Closes: #229170
+
+ -- Russell Coker <russell@coker.com.au> Sun, 25 Jan 2004 15:48:00 +1100
+
+setools (1.1.1-1) unstable; urgency=low
+
+ * New upstream.
+
+ -- Russell Coker <russell@coker.com.au> Tue, 6 Jan 2004 12:50:00 +1100
+
+setools (1.1-3) unstable; urgency=low
+
+ * Upload again with orig archive.
+
+ -- Russell Coker <russell@coker.com.au> Fri, 2 Jan 2004 21:48:00 +1100
+
+setools (1.1-2) unstable; urgency=low
+
+ * Added copyright file.
+
+ -- Russell Coker <russell@coker.com.au> Fri, 2 Jan 2004 18:24:00 +1100
+
+setools (1.1-1) unstable; urgency=low
+
+ * New upstream.
+
+ -- Russell Coker <russell@coker.com.au> Tue, 23 Dec 2003 16:35:00 +1100
+
+setools (1.0.1-1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Russell Coker <russell@coker.com.au> Wed, 19 Nov 2003 21:45:00 +1100
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..19cbe65
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,183 @@
+Source: setools
+Priority: optional
+Maintainer: J. Tang <jtang@tresys.com>
+Build-Depends: cdbs, debhelper (>= 5), autotools-dev, pkg-config,
+ g++ (>= 4.0), libstdc++-dev, autoconf (>= 2.59), libtool, flex, bison,
+ libselinux1-dev (>= 1.30), libsepol1-dev (>= 2.0.29),
+ libxml2-dev, libsqlite3-dev (>= 3.2),
+ swig (>= 1.3.28), python-dev (>= 2.3), python-central, java-gcj-compat-dev,
+ tcl8.4-dev (>= 8.4.9) | tcl-dev (>= 8.4.9),
+ tk8.4-dev (>= 8.4.9) | tk-dev (>= 8.4.9),
+ libglib2.0-dev, libgtk2.0-dev (>= 2.8), libglade2-dev
+Standards-Version: 3.7.2
+Section: utils
+
+Package: setools
+Section: utils
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends},
+ libsetools-tcl (= ${Source-Version}),
+ tk8.4 (>= 8.4.9) | wish, bwidget (>= 1.8), menu
+Recommends: setools-console
+Suggests: logwatch
+Provides: setools-gui
+Description: Collection of graphical tools for SELinux policy analysis
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes the following graphical tools:
+ .
+ * apol policy analysis tool
+ * seaudit audit log analysis tool
+ * sediffx semantic policy difference tool
+
+Package: libsetools
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Suggests: doxygen
+Description: SELinux policy analysis libraries
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes the following run-time libraries:
+ .
+ * libapol policy analysis library
+ * libpoldiff semantic policy difference library
+ * libqpol library that abstracts policy internals
+ * libseaudit parse and filter SELinux audit messages in log files
+ * libsefs SELinux file contexts library
+
+Package: libsetools-python
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, python (>= 2.3)
+XS-Python-Version: current
+XB-Python-Version: current
+Description: SETools Python bindings
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes Python bindings for the following libraries:
+ .
+ * libapol policy analysis library
+ * libpoldiff semantic policy difference library
+ * libqpol library that abstracts policy internals
+ * libseaudit parse and filter SELinux audit messages in log files
+ * libsefs SELinux file contexts library
+
+Package: libsetools-java
+Section: libs
+Architecture: any
+Depends: java-gcj-compat | java2-runtime, libsetools-jni (= ${binary:Version})
+Description: SETools Java bindings (architecture-independent)
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes Java bindings for the following libraries:
+ .
+ * libapol policy analysis library
+ * libpoldiff semantic policy difference library
+ * libqpol library that abstracts policy internals
+ * libseaudit parse and filter SELinux audit messages in log files
+ * libsefs SELinux file contexts library
+
+Package: libsetools-jni
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: SETools Java bindings (architecture-dependent)
+ This package includes the architecture-dependent library files needed
+ for libsetools-java.
+
+Package: libsetools-tcl
+Section: libs
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: SETools Tcl bindings
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes Tcl bindings for the following libraries:
+ .
+ * libapol policy analysis library
+ * libpoldiff semantic policy difference library
+ * libqpol library that abstracts policy internals
+ * libseaudit parse and filter SELinux audit messages in log files
+ * libsefs SELinux file contexts library
+
+Package: libsetools-dev
+Section: libdevel
+Architecture: any
+Depends: libsetools (= ${Source-Version})
+Description: SETools development files
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes header files and archives for the following
+ libraries:
+ .
+ * libapol policy analysis library
+ * libpoldiff semantic policy difference library
+ * libqpol library that abstracts policy internals
+ * libseaudit parse and filter SELinux audit messages in log files
+ * libsefs SELinux file contexts library
+
+Package: setools-console
+Section: utils
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Recommends: setools
+Description: Collection of console tools for SELinux policy analysis
+ SETools is a collection of graphical tools, command-line tools, and
+ libraries designed to facilitate SELinux policy analysis.
+ .
+ This package includes the following console tools:
+ .
+ * seaudit-report audit log analysis tool
+ * sechecker SELinux policy checking tool
+ * secmds command line tools: seinfo, sesearch, findcon,
+ replcon, and indexcon
+ * sediff semantic policy difference tool
+
+Package: libapol1
+Architecture: all
+Depends: libsetools
+Description: transitional package to smooth renaming to libsetools
+ This package installs libsetools, and can be safely removed
+ afterwards.
+
+Package: libseaudit1
+Architecture: all
+Depends: libsetools
+Description: transitional package to smooth renaming to libsetools
+ This package installs libsetools, and can be safely removed
+ afterwards.
+
+Package: libsefs1
+Architecture: all
+Depends: libsetools
+Description: transitional package to smooth renaming to libsetools
+ This package installs libsetools, and can be safely removed
+ afterwards.
+
+Package: libapol-dev
+Architecture: all
+Depends: libsetools-dev
+Description: transitional package to smooth renaming to libsetools-dev
+ This package installs libsetools-dev, and can be safely removed
+ afterwards.
+
+Package: libseaudit-dev
+Architecture: all
+Depends: libsetools-dev
+Description: transitional package to smooth renaming to libsetools-dev
+ This package installs libsetools-dev, and can be safely removed
+ afterwards.
+
+Package: libsefs-dev
+Architecture: all
+Depends: libsetools-dev
+Description: transitional package to smooth renaming to libsetools-dev
+ This package installs libsetools-dev, and can be safely removed
+ afterwards.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..cff8edd
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,48 @@
+Upstream Author: Tresys Technology <setools@tresys.com>
+
+Copyright: 2001-2008, Tresys Technology, LLC
+
+
+The SETools package contains files under two licenses. Most files are
+copyright as:
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, or version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL-2;
+ if not, write to the Free Software Foundation, Inc., 51 Franklin St,
+ Fifth Floor, Boston, MA 02110-1301 USA
+
+On Debian systems, the complete text of the GNU General Public
+License, version 2, can be found in /usr/share/common-licenses/GPL-2.
+
+
+Libraries, their source files, and their header files, are copyright as:
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+On Debian systems, the complete text of the GNU Lesser General Public
+License, can be found in /usr/share/common-licenses/LGPL-2.1.
+
+The Debian packaging is (C) 2008, Tresys Technology
+<setools@tresys.com> and is licensed under the GPL, see
+`/usr/share/common-licenses/GPL'.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..7273b60
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,5 @@
+NEWS
+README
+TODO
+AUTHORS
+KNOWN-BUGS
diff --git a/debian/libsetools-dev.install b/debian/libsetools-dev.install
new file mode 100644
index 0000000..369954d
--- /dev/null
+++ b/debian/libsetools-dev.install
@@ -0,0 +1,7 @@
+/usr/lib/*.so
+/usr/lib/pkgconfig/*
+/usr/include/apol/*
+/usr/include/poldiff/*
+/usr/include/qpol/*
+/usr/include/seaudit/*
+/usr/include/sefs/*
diff --git a/debian/libsetools-java.install b/debian/libsetools-java.install
new file mode 100644
index 0000000..edb6c6a
--- /dev/null
+++ b/debian/libsetools-java.install
@@ -0,0 +1,2 @@
+/usr/share/java/*
+/usr/share/setools-3.3/*jar
diff --git a/debian/libsetools-jni.install b/debian/libsetools-jni.install
new file mode 100644
index 0000000..3bf14c3
--- /dev/null
+++ b/debian/libsetools-jni.install
@@ -0,0 +1 @@
+/usr/lib/jni/*
diff --git a/debian/libsetools-jni.postinst b/debian/libsetools-jni.postinst
new file mode 100644
index 0000000..b1a382d
--- /dev/null
+++ b/debian/libsetools-jni.postinst
@@ -0,0 +1,41 @@
+#!/bin/sh
+# postinst script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/libsetools-jni.postrm b/debian/libsetools-jni.postrm
new file mode 100644
index 0000000..8a95093
--- /dev/null
+++ b/debian/libsetools-jni.postrm
@@ -0,0 +1,39 @@
+#!/bin/sh
+# postrm script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/libsetools-python.install b/debian/libsetools-python.install
new file mode 100644
index 0000000..175a404
--- /dev/null
+++ b/debian/libsetools-python.install
@@ -0,0 +1,2 @@
+/usr/lib/python*/site-packages/setools/*so*
+/usr/lib/python*/site-packages/setools/*.py
diff --git a/debian/libsetools-python.postinst b/debian/libsetools-python.postinst
new file mode 100644
index 0000000..b1a382d
--- /dev/null
+++ b/debian/libsetools-python.postinst
@@ -0,0 +1,41 @@
+#!/bin/sh
+# postinst script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/libsetools-tcl.install b/debian/libsetools-tcl.install
new file mode 100644
index 0000000..553b972
--- /dev/null
+++ b/debian/libsetools-tcl.install
@@ -0,0 +1,5 @@
+/usr/lib/setools/apol/*
+/usr/lib/setools/poldiff/*
+/usr/lib/setools/qpol/*
+/usr/lib/setools/seaudit/*
+/usr/lib/setools/sefs/*
diff --git a/debian/libsetools.install b/debian/libsetools.install
new file mode 100644
index 0000000..c42eda9
--- /dev/null
+++ b/debian/libsetools.install
@@ -0,0 +1,5 @@
+/usr/lib/libapol.so.*
+/usr/lib/libpoldiff.so.*
+/usr/lib/libqpol.so.*
+/usr/lib/libseaudit.so.*
+/usr/lib/libsefs.so.*
diff --git a/debian/libsetools.postinst b/debian/libsetools.postinst
new file mode 100644
index 0000000..b1a382d
--- /dev/null
+++ b/debian/libsetools.postinst
@@ -0,0 +1,41 @@
+#!/bin/sh
+# postinst script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/libsetools.postrm b/debian/libsetools.postrm
new file mode 100644
index 0000000..8a95093
--- /dev/null
+++ b/debian/libsetools.postrm
@@ -0,0 +1,39 @@
+#!/bin/sh
+# postrm script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..ace7434
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,25 @@
+#!/usr/bin/make -f
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/autotools.mk
+
+DEB_CONFIGURE_EXTRA_FLAGS = \
+ --with-tcl=/usr/lib/tcl8.4 --with-tk=/usr/lib/tk8.4 \
+ --with-tkinclude=/usr/include/tcl8.4 \
+ --enable-swig-java --enable-swig-python --enable-swig-tcl \
+ --disable-bwidget-check
+
+DEB_DH_INSTALL_SOURCEDIR = $(CURDIR)/debian/tmp
+
+install/libsetools-jni::
+ mkdir -p $(CURDIR)/debian/tmp/usr/lib/jni
+ for i in libjapol libjpoldiff libjqpol libjseaudit libjsefs; do \
+ mv $(CURDIR)/debian/tmp/usr/lib/$${i}* $(CURDIR)/debian/tmp/usr/lib/jni; \
+ done
+
+install/python-setools::
+ chmod 0644 $(CURDIR)/debian/tmp/usr/lib/setools/*/pkgIndex.tcl
+
+install/setools-console::
+ $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install-logwatch
+ chmod 0755 $(CURDIR)/debian/tmp/usr/share/setools-3.3/seaudit-report-service
diff --git a/debian/setools-console.install b/debian/setools-console.install
new file mode 100644
index 0000000..42e2be7
--- /dev/null
+++ b/debian/setools-console.install
@@ -0,0 +1,24 @@
+/etc/logwatch/conf/logfiles/seaudit-report-group.conf
+/etc/logwatch/conf/services/seaudit-report-service.conf
+/etc/logwatch/scripts/services/seaudit-report-service
+/usr/bin/findcon
+/usr/bin/indexcon
+/usr/bin/replcon
+/usr/bin/seaudit-report
+/usr/bin/sechecker
+/usr/bin/sediff
+/usr/bin/seinfo
+/usr/bin/sesearch
+/usr/share/man/man1/findcon.1
+/usr/share/man/man1/indexcon.1
+/usr/share/man/man1/replcon.1
+/usr/share/man/man1/sechecker.1
+/usr/share/man/man1/sediff.1
+/usr/share/man/man1/seinfo.1
+/usr/share/man/man1/sesearch.1
+/usr/share/man/man8/seaudit-report.8
+/usr/share/setools-3.3/seaudit-report-service
+/usr/share/setools-3.3/seaudit-report.conf
+/usr/share/setools-3.3/seaudit-report.css
+/usr/share/setools-3.3/sechecker-profiles/*
+/usr/share/setools-3.3/sechecker_help.txt
diff --git a/debian/setools.install b/debian/setools.install
new file mode 100644
index 0000000..69aef35
--- /dev/null
+++ b/debian/setools.install
@@ -0,0 +1,19 @@
+/usr/bin/apol
+/usr/bin/sediffx
+/usr/lib/setools/apol_tcl/*
+/usr/sbin/seaudit
+/usr/share/man/man1/apol.1
+/usr/share/man/man1/sediffx.1
+/usr/share/man/man8/seaudit.8
+/usr/share/setools-3.3/*.glade
+/usr/share/setools-3.3/*.png
+/usr/share/setools-3.3/apol.gif
+/usr/share/setools-3.3/apol_help.txt
+/usr/share/setools-3.3/domaintrans_help.txt
+/usr/share/setools-3.3/dot_seaudit
+/usr/share/setools-3.3/file_relabel_help.txt
+/usr/share/setools-3.3/infoflow_help.txt
+/usr/share/setools-3.3/seaudit_help.txt
+/usr/share/setools-3.3/sediff_help.txt
+/usr/share/setools-3.3/types_relation_help.txt
+/usr/share/setools-3.3/apol_perm_mapping*
diff --git a/debian/setools.menu b/debian/setools.menu
new file mode 100644
index 0000000..5b1021a
--- /dev/null
+++ b/debian/setools.menu
@@ -0,0 +1,6 @@
+?package(setools):needs="X11" section="Apps/System"\
+ title="apol" command="/usr/bin/apol"
+?package(setools):needs="X11" section="Apps/System"\
+ title="sediff" command="/usr/bin/sediff"
+?package(setools):needs="X11" section="Apps/System/Monitoring"\
+ title="seaudit" command="/usr/sbin/seaudit"
diff --git a/debian/setools.postinst b/debian/setools.postinst
new file mode 100644
index 0000000..b1a382d
--- /dev/null
+++ b/debian/setools.postinst
@@ -0,0 +1,41 @@
+#!/bin/sh
+# postinst script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ configure)
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/setools.postrm b/debian/setools.postrm
new file mode 100644
index 0000000..8a95093
--- /dev/null
+++ b/debian/setools.postrm
@@ -0,0 +1,39 @@
+#!/bin/sh
+# postrm script for myq
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <overwriter>
+# <overwriter-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..7612396
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,11 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 3 file
+version=3
+
+# Uncomment to examine a Webpage
+# <Webpage URL> <string match>
+http://oss.tresys.com/projects/setools/wiki/download setools-(.*)\.tar\.gz
diff --git a/libapol/Makefile.am b/libapol/Makefile.am
new file mode 100644
index 0000000..7bead2a
--- /dev/null
+++ b/libapol/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libapol.a libapol.so:
+ $(MAKE) -C src $@
diff --git a/libapol/include/Makefile.am b/libapol/include/Makefile.am
new file mode 100644
index 0000000..c3a820c
--- /dev/null
+++ b/libapol/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = apol
diff --git a/libapol/include/apol/Makefile.am b/libapol/include/apol/Makefile.am
new file mode 100644
index 0000000..0883c10
--- /dev/null
+++ b/libapol/include/apol/Makefile.am
@@ -0,0 +1,35 @@
+apoldir = $(includedir)/apol
+
+apol_HEADERS = \
+ avrule-query.h \
+ bool-query.h \
+ bst.h \
+ class-perm-query.h \
+ condrule-query.h \
+ constraint-query.h \
+ context-query.h \
+ domain-trans-analysis.h \
+ fscon-query.h \
+ infoflow-analysis.h \
+ isid-query.h \
+ mls-query.h \
+ mls_level.h \
+ mls_range.h \
+ netcon-query.h \
+ perm-map.h \
+ permissive-query.h \
+ polcap-query.h \
+ policy.h \
+ policy-path.h \
+ policy-query.h \
+ range_trans-query.h \
+ rbacrule-query.h \
+ relabel-analysis.h \
+ render.h \
+ role-query.h \
+ terule-query.h \
+ type-query.h \
+ types-relation-analysis.h \
+ user-query.h \
+ util.h \
+ vector.h
diff --git a/libapol/include/apol/avrule-query.h b/libapol/include/apol/avrule-query.h
new file mode 100644
index 0000000..1f4b072
--- /dev/null
+++ b/libapol/include/apol/avrule-query.h
@@ -0,0 +1,373 @@
+/**
+ * @file
+ *
+ * Routines to query access vector rules of a policy. These are
+ * allow, neverallow, auditallow, and dontaudit rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_AVRULE_QUERY_H
+#define APOL_AVRULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_avrule_query apol_avrule_query_t;
+
+/**
+ * Execute a query against all access vector rules within the policy.
+ *
+ * @param p Policy within which to look up avrules.
+ * @param a Structure containing parameters for query. If this is
+ * NULL then return all avrules.
+ * @param v Reference to a vector of qpol_avrule_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v);
+
+/**
+ * Execute a query against all syntactic access vector rules within
+ * the policy. If the policy has line numbers, then the returned list
+ *
+ * @param p Policy within which to look up avrules. The policy must
+ * be capable of having syntactic rules.
+ * @param a Structure containing parameters for query. If this is
+ * NULL then return all avrules.
+ * @param v Reference to a vector of qpol_syn_avrule_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_syn_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new avrule query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all avrules within the policy. The caller must call
+ * apol_avrule_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized avrule query structure, or NULL upon error.
+ */
+ extern apol_avrule_query_t *apol_avrule_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced avrule query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param a Reference to a avrule query structure to destroy.
+ */
+ extern void apol_avrule_query_destroy(apol_avrule_query_t ** a);
+
+/**
+ * Set an avrule query to search only certain access vector rules
+ * within the policy. This is a bitmap; use the constants in
+ * libqpol/avrule_query.h (QPOL_RULE_ALLOW, etc.) to give the rule
+ * selections.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param rules Bitmap to indicate which rules to search, or 0 to
+ * search all rules.
+ *
+ * @return Always 0.
+ */
+ extern int apol_avrule_query_set_rules(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int rules);
+
+/**
+ * Set an avrule query to return rules whose source symbol matches
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param symbol Limit query to rules with this symbol as their
+ * source, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_avrule_query_set_source(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set an avrule query to return rules whose source symbol is matched as a type
+ * or an attribute. The symbol will match both types and attributes by default.
+ * @see apol_avrule_query_set_source() to set the symbol to match.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE
+ * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component
+ * to match.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_avrule_query_set_source_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component);
+
+/**
+ * Set an avrule query to return rules whose target symbol matches
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param symbol Limit query to rules with this symbol as their
+ * target, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_avrule_query_set_target(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set an avrule query to return rules whose target symbol is matched as a type
+ * or an attribute. The symbol will match both types and attributes by default.
+ * @see apol_avrule_query_set_target() to set the symbol to match.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE
+ * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component
+ * to match.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_avrule_query_set_target_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component);
+
+/**
+ * Set an avrule query to return rules with this object (non-common)
+ * class. If more than one class are appended to the query, the
+ * rule's class must be one of those appended. (I.e., the rule's
+ * class must be a member of the query's classes.) Pass a NULL to
+ * clear all classes. Note that this performs straight string
+ * comparison, ignoring the regex flag.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param obj_class Name of object class to add to search set, or NULL
+ * to clear all classes.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_avrule_query_append_class(const apol_policy_t * p, apol_avrule_query_t * a, const char *obj_class);
+
+/**
+ * Set an avrule query to return rules with this permission. By
+ * default, if more than one permission are appended to the query, at
+ * least one of the rule's permissions must be one of those appended;
+ * that is, the intersection of query's and rule's permissions must be
+ * non-empty. (This behavior can be changed.) Pass a NULL to clear
+ * all permissions. Note that this performs a straight string
+ * comparison, ignoring the regex flag.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param perm Name of permission to add to search set, or NULL to
+ * clear all permissions.
+ *
+ * @return 0 on success, negative on error.
+ *
+ * @see apol_avrule_query_set_all_perms()
+ */
+ extern int apol_avrule_query_append_perm(const apol_policy_t * p, apol_avrule_query_t * a, const char *perm);
+
+/**
+ * Set an avrule query to return rules that are in conditionals and
+ * whose conditional uses a particular boolean variable.
+ * Unconditional rules will not be returned.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param bool_name Name of boolean that conditional must contain. If
+ * NULL then search all rules.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_avrule_query_set_bool(const apol_policy_t * p, apol_avrule_query_t * a, const char *bool_name);
+
+/**
+ * Set an avrule query to search only enabled rules within the policy.
+ * These include rules that are unconditional and those within enabled
+ * conditionals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param is_enabled Non-zero to search only enabled rules, 0 to
+ * search all rules.
+ *
+ * @return Always 0.
+ */
+ extern int apol_avrule_query_set_enabled(const apol_policy_t * p, apol_avrule_query_t * a, int is_enabled);
+
+/**
+ * Normally, if more than one permission are added to the query then
+ * all returned rules will have <em>at least one</em> of those
+ * permissions. If the all_perms flag is set, then returned rules
+ * will have <em>all</em> of the given permissions. This flag does
+ * nothing if no permissions are given.
+ *
+ * <em>Note:</em> If calling apol_syn_avrule_get_by_query(), the
+ * returned results may not be what is expected. For a given
+ * source-target-class triplet, all of the associated permissions are
+ * unioned together prior to executing the avrule query. Although a
+ * given syntactic AV rule might not have all of the matched
+ * permissions, the union of the rules' permissions will them. For
+ * example, consider these two allow rules:
+ *
+ *<pre>allow A B : C p1;
+ *allow A B : C p2;</pre>
+ *
+ * If the avrule query has both permissions p1 and p2 and the
+ * all_perms flag is set, then both of these syntactic rules will be
+ * returned by apol_syn_avrule_get_by_query().
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param all_perms Non-zero to match all permissions, zero to match
+ * any permission.
+ *
+ * @return Always 0.
+ *
+ * @see apol_avrule_query_append_perm()
+ */
+ extern int apol_avrule_query_set_all_perms(const apol_policy_t * p, apol_avrule_query_t * a, int all_perms);
+
+/**
+ * Set an avrule query to treat the source symbol as any. That is,
+ * use the same symbol for either source or target of a rule. This
+ * flag does nothing if the source symbol is not set.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param is_any Non-zero to use source symbol for any field, 0 to
+ * keep source as only source.
+ *
+ * @return Always 0.
+ */
+ extern int apol_avrule_query_set_source_any(const apol_policy_t * p, apol_avrule_query_t * a, int is_any);
+
+/**
+ * Set an avrule query to use regular expression searching for source
+ * and target types/attributes. Strings will be treated as regexes
+ * instead of literals. Matching will occur against the type name or
+ * any of its aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a AV rule query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_avrule_query_set_regex(const apol_policy_t * p, apol_avrule_query_t * a, int is_regex);
+
+/**
+ * Given a single avrule, return a newly allocated vector of
+ * qpol_syn_avrule_t pointers (relative to the given policy) which
+ * comprise that rule. The vector will be sorted by line numbers if
+ * the policy has line numbers. If the given perms vector is non-NULL
+ * and non-empty, then only return syntactic rules with at least one
+ * permission listed within the perms vector.
+ *
+ * @param p Policy from which to obtain syntactic rules.
+ * @param rule AV rule to convert.
+ * @param perms If non-NULL and non-empty, a list of permission
+ * strings. Returned syn avrules will have at least one permission in
+ * common with this list.
+ *
+ * @return A newly allocated vector of syn_avrule_t pointers. The
+ * caller is responsible for calling apol_vector_destroy() afterwards.
+ */
+ extern apol_vector_t *apol_avrule_to_syn_avrules(const apol_policy_t * p, const qpol_avrule_t * rule,
+ const apol_vector_t * perms);
+
+/**
+ * Given a vector of avrules (qpol_avrule_t pointers), return a newly
+ * allocated vector of qpol_syn_avrule_t pointers (relative to the
+ * given policy) which comprise all of those rules. The returned
+ * vector will be sorted by line numbers if the policy has line
+ * numbers. Also, it will not have any duplicate syntactic rules. If
+ * the given perms vector is non-NULL and non-empty, then only return
+ * syntactic rules with at least one permission listed within the
+ * perms vector.
+ *
+ * @param p Policy from which to obtain syntactic rules.
+ * @param rules Vector of AV rules to convert.
+ * @param perms If non-NULL and non-empty, a list of permission
+ * strings. Returned syn avrules will have at least one permission in
+ * common with this list.
+ *
+ * @return A newly allocated vector of syn_avrule_t pointers. The
+ * caller is responsible for calling apol_vector_destroy() afterwards.
+ */
+ extern apol_vector_t *apol_avrule_list_to_syn_avrules(const apol_policy_t * p, const apol_vector_t * rules,
+ const apol_vector_t * perms);
+
+/**
+ * Render an avrule to a string.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param rule The rule to render.
+ *
+ * @return a newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+ */
+ extern char *apol_avrule_render(const apol_policy_t * policy, const qpol_avrule_t * rule);
+
+/**
+ * Render a syntactic avrule to a string.
+ *
+ * @param policy Policy handler to report errors.
+ * @param rule The rule to render.
+ *
+ * @return a newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+*/
+ extern char *apol_syn_avrule_render(const apol_policy_t * policy, const qpol_syn_avrule_t * rule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/bool-query.h b/libapol/include/apol/bool-query.h
new file mode 100644
index 0000000..a735eec
--- /dev/null
+++ b/libapol/include/apol/bool-query.h
@@ -0,0 +1,105 @@
+/**
+ * @file
+ * Public Interface for querying conditional booleans of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_BOOL_QUERY_H
+#define APOL_BOOL_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_bool_query apol_bool_query_t;
+
+/******************** booleans queries ********************/
+
+/**
+ * Execute a query against all booleans within the policy.
+ *
+ * @param p Policy within which to look up booleans.
+ * @param b Structure containing parameters for query. If this is
+ * NULL then return all booleans.
+ * @param v Reference to a vector of qpol_bool_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_bool_get_by_query(const apol_policy_t * p, apol_bool_query_t * b, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new boolean query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all booleans within the policy. The caller must call
+ * apol_bool_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized boolean query structure, or NULL upon error.
+ */
+ extern apol_bool_query_t *apol_bool_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced boolean query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param b Reference to a boolean query structure to destroy.
+ */
+ extern void apol_bool_query_destroy(apol_bool_query_t ** b);
+
+/**
+ * Set a boolean query to return only booleans that match this name.
+ * This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param b Boolean query to set.
+ * @param name Limit query to only booleans with this name, or NULL to
+ * unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_bool_query_set_bool(const apol_policy_t * p, apol_bool_query_t * b, const char *name);
+
+/**
+ * Set a boolean query to use regular expression searching for all of
+ * its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param b Boolean query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_bool_query_set_regex(const apol_policy_t * p, apol_bool_query_t * b, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_BOOL_QUERY_H */
diff --git a/libapol/include/apol/bst.h b/libapol/include/apol/bst.h
new file mode 100644
index 0000000..ca21a76
--- /dev/null
+++ b/libapol/include/apol/bst.h
@@ -0,0 +1,178 @@
+/**
+ * @file
+ * Contains the API for a binary search tree. The tree guarantees
+ * uniqueness of all entries within. Note that BST functions are not
+ * thread-safe. Use this if you need uniqueness in items; use
+ * vectors otherwise because they are faster.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_BST_H
+#define APOL_BST_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+ typedef struct apol_bst apol_bst_t;
+
+ typedef int (apol_bst_comp_func) (const void *a, const void *b, void *data);
+ typedef void (apol_bst_free_func) (void *elem);
+
+#include "vector.h"
+
+/**
+ * Allocate and initialize an empty binary search tree. The tree
+ * must have a comparison function, used when comparing nodes so as
+ * to determine how to sort them.
+ *
+ * @param cmp A comparison call back for the type of element stored
+ * in the BST. The expected return value from this function is less
+ * than, equal to, or greater than 0 if the first argument is less
+ * than, equal to, or greater than the second respectively. If this
+ * is NULL then do pointer address comparison.
+ * @param fr Function to call when destroying the tree. Each
+ * element of the tree will be passed into this function; it should
+ * free the memory used by that element. If this parameter is NULL,
+ * the elements will not be freed.
+ *
+ * @return A pointer to a newly created BST on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_bst_destroy() to free memory used.
+ */
+ extern apol_bst_t *apol_bst_create(apol_bst_comp_func * cmp, apol_bst_free_func * fr);
+
+/**
+ * Free a BST and any memory used by it. This will recursively
+ * invoke the free function that was stored within the tree when it
+ * was created.
+ *
+ * @param b Pointer to the BST to free. The pointer will be set to
+ * NULL afterwards. If already NULL then this function does nothing.
+ */
+ extern void apol_bst_destroy(apol_bst_t ** b);
+
+/**
+ * Allocate and return a vector that has been initialized with the
+ * contents of a binary search tree. If change_owner is zero then
+ * this function will make a <b>shallow copy of the BST's
+ * contents</b>; the BST will still <em>own</em> the objects.
+ * Otherwise the victor will gain ownership of the items; the BST can
+ * then be destroyed safely without affecting the vector. (The
+ * resulting vector will be sorted as per the BST's comparison
+ * function.)
+ *
+ * @param b Binary search tree from which to copy.
+ * @param change_owner If zero then do a shallow copy, else change
+ * item ownership.
+ *
+ * @return A pointer to a newly created vector on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_vector_destroy() to free memory used
+ * by the vector.
+ */
+ extern apol_vector_t *apol_bst_get_vector(apol_bst_t * b, int change_owner);
+
+/**
+ * Get the number of elements stored in the BST.
+ *
+ * @param b The BST from which to get the number of elements. Must
+ * be non-NULL.
+ *
+ * @return The number of elements in the BST; if b is NULL, return
+ * 0 and set errno.
+ */
+ extern size_t apol_bst_get_size(const apol_bst_t * v);
+
+/**
+ * Find an element within a BST and return it.
+ *
+ * @param b The BST from which to get the element.
+ * @param elem The element to find. (This will be the second
+ * parameter to the comparison function given in apol_bst_create().)
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater (the function given in apol_bst_create()).
+ * @param elem Location to write the found element. This value is
+ * undefined if the key did not match any elements.
+ *
+ * @return 0 if element was found, or < 0 if not found.
+ */
+ extern int apol_bst_get_element(const apol_bst_t * b, const void *elem, void *data, void **result);
+
+/**
+ * Insert an element to the BST. If the element already exists then
+ * do not insert it again.
+ *
+ * @param b The BST to which to add the element.
+ * @param elem The element to add.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater (the function given in apol_bst_create()).
+ *
+ * @return 0 if the item was inserted, 1 if the item already exists
+ * (and thus not inserted). On failure return < 0, set errno, and b
+ * will be unchanged.
+ */
+ extern int apol_bst_insert(apol_bst_t * b, void *elem, void *data);
+
+/**
+ * Insert an element into the BST, and then get the element back out.
+ * If the element did not already exist, then this function behaves
+ * the same as apol_bst_insert(). If however the element did exist,
+ * then the passed in element is freed (as per the BST's free
+ * function) and then the existing element is returned.
+ *
+ * @param b The BST to which to add the element.
+ * @param elem Reference to an element to add. If the element is
+ * new, then the pointer remains unchanged. Otherwise set the
+ * reference to the element already within the tree.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater (the function given in apol_bst_create()).
+ *
+ * @return 0 if the item was inserted, 1 if the item already exists
+ * (and thus not inserted). On failure return < 0, set errno, and b
+ * will be unchanged.
+ */
+ extern int apol_bst_insert_and_get(apol_bst_t * b, void **elem, void *data);
+
+/**
+ * Map a function across all the elements of the BST. Mapping occurs in
+ * the sorted order as defined by the original comparison function.
+ *
+ * @param node BST upon which to map against.
+ * @param fn Function pointer that takes 2 arguments, first is a
+ * pointer to the data in a node of the BST, second is an arbitrary
+ * data element. The function may change the BST node, but it must
+ * not affect the node's sorting order within the tree. This function
+ * should return >= 0 on success; a return of < 0 signals error and
+ * ends the mapping over the tree.
+
+ * @return Result of the last call to fn() (i.e., >= 0 on success < 0 on
+ * failure). If the tree is empty then return 0.
+ */
+ extern int apol_bst_inorder_map(const apol_bst_t * b, int (*fn) (void *, void *), void *data);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_BST_H */
diff --git a/libapol/include/apol/class-perm-query.h b/libapol/include/apol/class-perm-query.h
new file mode 100644
index 0000000..abb2fbb
--- /dev/null
+++ b/libapol/include/apol/class-perm-query.h
@@ -0,0 +1,255 @@
+/**
+ * @file
+ *
+ * Routines to query classes, commons, and permissions of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_CLASS_PERM_QUERY_H
+#define APOL_CLASS_PERM_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_class_query apol_class_query_t;
+ typedef struct apol_common_query apol_common_query_t;
+ typedef struct apol_perm_query apol_perm_query_t;
+
+/******************** object class queries ********************/
+
+/**
+ * Execute a query against all classes within the policy. The results
+ * will only contain object classes, not common classes.
+ *
+ * @param p Policy within which to look up classes.
+ * @param c Structure containing parameters for query. If this is
+ * NULL then return all object classes.
+ * @param v Reference to a vector of qpol_class_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_class_get_by_query(const apol_policy_t * p, apol_class_query_t * c, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new class query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all object classes within the policy. The caller must
+ * call apol_class_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized class query structure, or NULL upon error.
+ */
+ extern apol_class_query_t *apol_class_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced class query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param c Reference to a class query structure to destroy.
+ */
+ extern void apol_class_query_destroy(apol_class_query_t ** c);
+
+/**
+ * Set a class query to return only object classes that match this
+ * name. This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Class query to set.
+ * @param name Limit query to only classes with this name, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_class_query_set_class(const apol_policy_t * p, apol_class_query_t * c, const char *name);
+
+/**
+ * Set a class query to return only object classes that inherit from a
+ * particular common class. Queries will not match classes without
+ * commons if this option is set. This function duplicates the
+ * incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Class query to set.
+ * @param name Limit query to only classes that inherit from this
+ * common class, or NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_class_query_set_common(const apol_policy_t * p, apol_class_query_t * c, const char *name);
+
+/**
+ * Set a class query to use regular expression searching for all of
+ * its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Class query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_class_query_set_regex(const apol_policy_t * p, apol_class_query_t * c, int is_regex);
+
+/******************** common class queries ********************/
+
+/**
+ * Execute a query against all common classes within the policy. The
+ * results will only contain common classes, not object classes.
+ *
+ * @param p Policy within which to look up common classes.
+ * @param c Structure containing parameters for query. If this is
+ * NULL then return all common classes.
+ * @param v Reference to a vector of qpol_common_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_common_get_by_query(const apol_policy_t * p, apol_common_query_t * c, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new common query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all common classes within the policy. The caller must
+ * call apol_common_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized common query structure, or NULL upon error.
+ */
+ extern apol_common_query_t *apol_common_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced common query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param c Reference to a common query structure to destroy.
+ */
+ extern void apol_common_query_destroy(apol_common_query_t ** c);
+
+/**
+ * Set a common query to return only common classes that match this
+ * name. This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Common query to set.
+ * @param name Limit query to only commons with this name, or NULL to
+ * unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_common_query_set_common(const apol_policy_t * p, apol_common_query_t * c, const char *name);
+
+/**
+ * Set a common query to use regular expression searching for all of
+ * its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Class query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_common_query_set_regex(const apol_policy_t * p, apol_common_query_t * c, int is_regex);
+
+/******************** permission queries ********************/
+
+/**
+ * Execute a query against all permissions (both those declared in
+ * classes as well as commons) within the policy. The results will
+ * contain char pointers to permission names. Thus if the same
+ * permission name is declared within multiple classes (e.g.,
+ * <tt>file/read</tt> and <tt>socket/read</tt>) then only one instance
+ * of <tt>read</tt> is returned.
+ *
+ * @param p Policy within which to look up permissions.
+ * @param pq Structure containing parameters for query. If this is
+ * NULL then return all permissions.
+ * @param v Reference to a vector of character pointers. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_perm_get_by_query(const apol_policy_t * p, apol_perm_query_t * pq, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new permission query structure. All fields
+ * are initialized, such that running this blank query results in
+ * returning all permissions within the policy. The caller must call
+ * apol_perm_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized permission query structure, or NULL upon
+ * error.
+ */
+ extern apol_perm_query_t *apol_perm_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced permission
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param pq Reference to a permission query structure to destroy.
+ */
+ extern void apol_perm_query_destroy(apol_perm_query_t ** pq);
+
+/**
+ * Set a permission query to return only permissions that match this
+ * name. This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param pq Permission query to set.
+ * @param name Limit query to only permissions with this name, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_perm_query_set_perm(const apol_policy_t * p, apol_perm_query_t * pq, const char *name);
+
+/**
+ * Set a permission query to use regular expression searching for all
+ * of its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param pq Permission query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_perm_query_set_regex(const apol_policy_t * p, apol_perm_query_t * pq, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/condrule-query.h b/libapol/include/apol/condrule-query.h
new file mode 100644
index 0000000..16bbb14
--- /dev/null
+++ b/libapol/include/apol/condrule-query.h
@@ -0,0 +1,119 @@
+/**
+ * @file
+ *
+ * Routines to query conditional expressions and conditional rules of
+ * a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_CONDRULE_QUERY_H
+#define APOL_CONDRULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_cond_query apol_cond_query_t;
+
+/**
+ * Execute a query against all conditional expressions within the
+ * policy.
+ *
+ * @param p Policy within which to look up expressions.
+ * @param c Structure containing parameters for query. If this is
+ * NULL then return all expressions.
+ * @param v Reference to a vector of qpol_cond_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_cond_get_by_query(const apol_policy_t * p, apol_cond_query_t * c, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new cond query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all conditional expressions within the policy. The
+ * caller must call apol_cond_query_destroy() upon the return value
+ * afterwards.
+ *
+ * @return An initialized cond query structure, or NULL upon error.
+ */
+ extern apol_cond_query_t *apol_cond_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced cond query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param c Reference to a cond query structure to destroy.
+ */
+ extern void apol_cond_query_destroy(apol_cond_query_t ** c);
+
+/**
+ * Set a cond query to search only conditional expressions that use a
+ * certain boolean variable.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Cond rule query to set.
+ * @param name Limit query to expressions with this boolean, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_cond_query_set_bool(const apol_policy_t * p, apol_cond_query_t * c, const char *name);
+
+/**
+ * Set a cond query to use regular expression searching for all of its
+ * fields. Strings will be treated as regexes instead of literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Cond rule query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_cond_query_set_regex(const apol_policy_t * p, apol_cond_query_t * c, int is_regex);
+
+/**
+ * Given a conditional node, allocate and return a string
+ * representation of its conditional expression.
+ *
+ * @param p Policy handler, to report errors.
+ * @param cond Conditional node whose expression to render.
+ *
+ * @return A newly malloc()'d string representation of conditonal
+ * expression, or NULL on failure. The caller is responsible for
+ * calling free() on the returned string.
+ */
+ extern char *apol_cond_expr_render(const apol_policy_t * p, const qpol_cond_t * cond);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/constraint-query.h b/libapol/include/apol/constraint-query.h
new file mode 100644
index 0000000..9e5b077
--- /dev/null
+++ b/libapol/include/apol/constraint-query.h
@@ -0,0 +1,188 @@
+/**
+ * @file
+ *
+ * Routines to query constraint and validatetrans statements in a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_CONSTRAINT_QUERY_H
+#define APOL_CONSTRAINT_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+
+ typedef struct apol_constraint_query apol_constraint_query_t;
+ typedef struct apol_validatetrans_query apol_validatetrans_query_t;
+
+/******************** constraint queries ********************/
+
+/**
+ * Execute a query against all constraints within the policy.
+ *
+ * @param p Policy within which to look up constraints.
+ * @param c Structure containing parameters for query. If this is
+ * NULL then return all constraints.
+ * @param v Reference to a vector of qpol_constraint_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_constraint_get_by_query(const apol_policy_t * p, apol_constraint_query_t * c, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new constraint query structure. All fields
+ * are initialized, such that running this blank query results in
+ * returning all constraints within the policy. The caller must call
+ * apol_constraint_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized constraint query structure, or NULL upon
+ * error.
+ */
+ extern apol_constraint_query_t *apol_constraint_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced constraint
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param c Reference to a constraint query structure to destroy.
+ */
+ extern void apol_constraint_query_destroy(apol_constraint_query_t ** c);
+
+/**
+ * Set a constraint query to return only constraints that use object
+ * classes that match this name. This function duplicates the
+ * incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Constraint query to set.
+ * @param name Limit query to only classes with this name, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_constraint_query_set_class(const apol_policy_t * p, apol_constraint_query_t * c, const char *name);
+
+/**
+ * Set a constraint query to return only constraints that employ
+ * permissions that match this name. This function duplicates the
+ * incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Constraint query to set.
+ * @param name Limit query to only permissions with this name, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_constraint_query_set_perm(const apol_policy_t * p, apol_constraint_query_t * c, const char *name);
+
+/**
+ * Set a constraint query to use regular expression searching for all
+ * of its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Constraint query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_constraint_query_set_regex(const apol_policy_t * p, apol_constraint_query_t * c, int is_regex);
+
+/******************** validatetrans queries ********************/
+
+/**
+ * Execute a query against all validatetrans statements within the
+ * policy.
+ *
+ * @param p Policy within which to look up validatetrans statements.
+ * @param vr Structure containing parameters for query. If this is
+ * NULL then return all validatetrans statements.
+ * @param v Reference to a vector of qpol_validatetrans_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_validatetrans_get_by_query(const apol_policy_t * p, apol_validatetrans_query_t * vt, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new validatetrans query structure. All
+ * fields are initialized, such that running this blank query results
+ * in returning all validatetrans within the policy. The caller must
+ * call apol_validatetrans_query_destroy() upon the return value
+ * afterwards.
+ *
+ * @return An initialized validatetrans query structure, or NULL upon
+ * error.
+ */
+ extern apol_validatetrans_query_t *apol_validatetrans_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced validatetrans
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param vt Reference to a validatetrans query structure to destroy.
+ */
+ extern void apol_validatetrans_query_destroy(apol_validatetrans_query_t ** vt);
+
+/**
+ * Set a validatetrans query to return only validatetrans that use
+ * object classes that match this name. This function duplicates the
+ * incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param vt Validatetrans query to set.
+ * @param name Limit query to only classes with this name, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_validatetrans_query_set_class(const apol_policy_t * p, apol_validatetrans_query_t * vt, const char *name);
+
+/**
+ * Set a validatetrans query to use regular expression searching for
+ * all of its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param vt Validatetrans query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_validatetrans_query_set_regex(const apol_policy_t * p, apol_validatetrans_query_t * vt, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/context-query.h b/libapol/include/apol/context-query.h
new file mode 100644
index 0000000..2ee269c
--- /dev/null
+++ b/libapol/include/apol/context-query.h
@@ -0,0 +1,261 @@
+/**
+ * @file
+ * Public interface for querying and manipulating a context.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_CONTEXT_QUERY_H
+#define APOL_CONTEXT_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "mls-query.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_context apol_context_t;
+
+/**
+ * Allocate and return a new context structure. All fields are
+ * initialized to nothing. The caller must call
+ * apol_context_destroy() upon the return value afterwards.
+ *
+ * @return An initialized context structure, or NULL upon error.
+ */
+ extern apol_context_t *apol_context_create(void);
+
+/**
+ * Allocate and return a new context structure, initialized from an
+ * existing qpol_context_t. The caller must call
+ * apol_context_destroy() upon the return value afterwards.
+ *
+ * @param p Policy from which the qpol_context_t was obtained.
+ * @param context The libqpol context for which to create a new apol
+ * context. This context will not be altered by this call.
+ *
+ * @return An initialized context structure, or NULL upon error.
+ */
+ extern apol_context_t *apol_context_create_from_qpol_context(const apol_policy_t * p, const qpol_context_t * context);
+
+/**
+ * Take a literal context string that may be missing components (e.g.,
+ * <b>user_u::type_t:s0:c0.c127</b>), fill in a newly allocated
+ * apol_context_t, and return it. If there is a MLS range component
+ * to the context, it will <b>not</b> expanded. The caller must call
+ * apol_context_destroy() upon the return value afterwards.
+ *
+ * Because this function creates a context without the benefit of a
+ * policy, its range is incomplete. Call apol_context_convert() to
+ * complete it.
+ *
+ * @param context_string Pointer to a string representing a (possibly
+ * incomplete) context, or NULL upon error.
+ *
+ * @return An initialized context structure, or NULL upon error.
+ */
+ extern apol_context_t *apol_context_create_from_literal(const char *context_string);
+
+/**
+ * Deallocate all memory associated with a context structure and then
+ * set it to NULL. This function does nothing if the context is
+ * already NULL.
+ *
+ * @param context Reference to a context structure to destroy.
+ */
+ extern void apol_context_destroy(apol_context_t ** context);
+
+/**
+ * Set the user field of a context structure. This function
+ * duplicates the incoming string.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param context Context to modify.
+ * @param user New user field to set, or NULL to unset this field.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_context_set_user(const apol_policy_t * p, apol_context_t * context, const char *user);
+
+/**
+ * Set the role field of a context structure. This function
+ * duplicates the incoming string.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param context Context to modify.
+ * @param role New role field to set, or NULL to unset this field.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_context_set_role(const apol_policy_t * p, apol_context_t * context, const char *role);
+
+/**
+ * Set the type field of a context structure. This function
+ * duplicates the incoming string.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param context Context to modify.
+ * @param type New type field to set, or NULL to unset this field.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_context_set_type(const apol_policy_t * p, apol_context_t * context, const char *type);
+
+/**
+ * Set the range field of a context structure. This function takes
+ * ownership of the range, such that the caller must not modify nor
+ * destroy it afterwards.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param context Context to modify.
+ * @param range New range field to set, or NULL to unset this field.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_context_set_range(const apol_policy_t * p, apol_context_t * context, apol_mls_range_t * range);
+
+/**
+ * Get the user field of a context structure.
+ *
+ * @param context Context to query.
+ *
+ * @return Context's user, or NULL if not set or upon error. Do not
+ * modify this string.
+ */
+ extern const char *apol_context_get_user(const apol_context_t * context);
+
+/**
+ * Get the role field of a context structure.
+ *
+ * @param context Context to query.
+ *
+ * @return Context's role, or NULL if not set or upon error. Do not
+ * modify this string.
+ */
+ extern const char *apol_context_get_role(const apol_context_t * context);
+
+/**
+ * Get the type field of a context structure.
+ *
+ * @param context Context to query.
+ *
+ * @return Context's type, or NULL if not set or upon error. Do not
+ * modify this string.
+ */
+ extern const char *apol_context_get_type(const apol_context_t * context);
+
+/**
+ * Get the range field of a context structure.
+ *
+ * @param context Context to query.
+ *
+ * @return Context's range, or NULL if not set or upon error. Do not
+ * modify this structure.
+ */
+ extern const apol_mls_range_t *apol_context_get_range(const apol_context_t * context);
+
+/**
+ * Compare two contexts, determining if one matches the other. The
+ * search context may have empty elements that indicate not to compare
+ * that field. Types will be matched if the two or any of their
+ * aliases are the same. The last parameter gives how to match ranges
+ * (assuming that search has a range); it must be one of
+ * APOL_QUERY_SUB, APOL_QUERY_SUPER, APOL_QUERY_EXACT or
+ * APOL_QUERY_INTERSECT as per apol_mls_range_compare(). If a context
+ * is not valid according to the policy then this function returns -1.
+ * If search is NULL then comparison always succeeds.
+ *
+ * @param p Policy within which to look up policy and MLS information.
+ * @param target Target context to compare.
+ * @param search Source context to compare.
+ * @param range_compare_type Specifies how to compare the ranges.
+ *
+ * @return 1 If comparison succeeds, 0 if not; -1 on error.
+ */
+ extern int apol_context_compare(const apol_policy_t * p,
+ const apol_context_t * target, const apol_context_t * search,
+ unsigned int range_compare_type);
+
+/**
+ * Given a complete context (user, role, type, and range if policy is
+ * MLS), determine if it is legal according to the supplied policy.
+ * (Check that the user has that role, the role has that type, etc.)
+ * This function will convert from aliases to canonical forms as
+ * necessary.
+ *
+ * @param p Policy within which to look up context information.
+ * @param context Context to check.
+ *
+ * @return 1 If context is legal, 0 if not; -1 on error.
+ */
+ extern int apol_context_validate(const apol_policy_t * p, const apol_context_t * context);
+
+/**
+ * Given a partial context, determine if it is legal according to the
+ * supplied policy. For fields that are not specified, assume that
+ * they would be legal. For example, if a user is given but not a
+ * role, then return truth if the user is in the policy. If the
+ * context is NULL then this function returns 1. This function will
+ * convert from aliases to canonical forms as necessary.
+ *
+ * @param p Policy within which to look up context information.
+ * @param context Context to check.
+ *
+ * @return 1 If context is legal, 0 if not; -1 on error.
+ */
+ extern int apol_context_validate_partial(const apol_policy_t * p, const apol_context_t * context);
+
+/**
+ * Given a context, allocate and return a string that represents the
+ * context. This function does not check if the context is valid or
+ * not. An asterisk ("*") represents fields that have not been set.
+ * For example, if a context has the role object_r but has no user nor
+ * type set, it will be rendered as "<sample>*:object_r:*</sample>"
+ * (assuming the given policy is not MLS).
+ *
+ * @param p Policy within which to look up MLS range information. If
+ * NULL, then attempt to treat the range as incomplete.
+ * @param context Context to render.
+ *
+ * @return A newly allocated string on success, which the caller must
+ * free afterwards. Upon error return NULL.
+ */
+ extern char *apol_context_render(const apol_policy_t * p, const apol_context_t * context);
+
+/**
+ * Given a context, convert the range within it (as per
+ * apol_mls_range_convert()) to a complete range. If the context has
+ * no range or has no literal range then do nothing.
+ *
+ * @param p Policy containing category information.
+ * @param context Context to convert.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_context_convert(const apol_policy_t * p, apol_context_t * context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_CONTEXT_QUERY_H */
diff --git a/libapol/include/apol/domain-trans-analysis.h b/libapol/include/apol/domain-trans-analysis.h
new file mode 100644
index 0000000..2e05748
--- /dev/null
+++ b/libapol/include/apol/domain-trans-analysis.h
@@ -0,0 +1,427 @@
+/**
+ * @file
+ *
+ * Routines to perform a domain transition analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_DOMAIN_TRANS_ANALYSIS_H
+#define APOL_DOMAIN_TRANS_ANALYSIS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_domain_trans_analysis apol_domain_trans_analysis_t;
+ typedef struct apol_domain_trans_result apol_domain_trans_result_t;
+
+#define APOL_DOMAIN_TRANS_DIRECTION_FORWARD 0x01
+#define APOL_DOMAIN_TRANS_DIRECTION_REVERSE 0x02
+
+#define APOL_DOMAIN_TRANS_SEARCH_VALID 0x01
+#define APOL_DOMAIN_TRANS_SEARCH_INVALID 0x02
+#define APOL_DOMAIN_TRANS_SEARCH_BOTH (APOL_DOMAIN_TRANS_SEARCH_VALID|APOL_DOMAIN_TRANS_SEARCH_INVALID)
+
+/******************* table operation functions ****************************/
+
+/**
+ * Build the table of domain transitions for a policy if not already built.
+ * @param policy The policy for which to build the table; if the table
+ * already exists for this policy, nothing is done.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the table will be destroyed.
+ */
+ extern int apol_policy_build_domain_trans_table(apol_policy_t * policy);
+
+/**
+ * @deprecated Use apol_policy_build_domain_trans_table().
+ */
+ extern int apol_policy_domain_trans_table_build(apol_policy_t * policy) __attribute__ ((deprecated));
+
+/**
+ * Reset the state of the domain transition table in a policy. This
+ * is needed because by default subsequent calls to
+ * apol_domain_trans_analysis_do() will not produce results generated
+ * in a previous call. If calls are to be considered independent or
+ * calls in a different direction are desired, call this function
+ * prior to apol_domain_trans_analysis_do(). If the table was not
+ * built yet then this function does nothing.
+ *
+ * @param policy Policy containing the table for which the state
+ * should be reset.
+ */
+ extern void apol_policy_reset_domain_trans_table(apol_policy_t * policy);
+
+/**
+ * @deprecated Use apol_policy_reset_domain_trans_table().
+ */
+ extern void apol_domain_trans_table_reset(apol_policy_t * policy) __attribute__ ((deprecated));
+
+/*************** functions to do domain transition anslysis ***************/
+
+/**
+ * Allocate and return a new domain transition analysis structure. All
+ * fields are cleared; one must fill in the details of the analysis
+ * before running it. The caller must call apol_domain_trans_analysis_destroy()
+ * upon the return value afterwards.
+ * @return An initialized domain transition analysis structure, or NULL
+ * upon error; if an error occurs errno will be set.
+ */
+ extern apol_domain_trans_analysis_t *apol_domain_trans_analysis_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced domain transition
+ * analysis structure, and then set it to NULL. This function does nothing if
+ * the analysis is already NULL.
+ * @param dta Reference to a domain transition analysis structure to destroy.
+ */
+ extern void apol_domain_trans_analysis_destroy(apol_domain_trans_analysis_t ** dta);
+
+/**
+ * Set the direction of the transitions with respect to the start type.
+ * Must be either APOL_DOMAIN_TRANS_DIRECTION_FORWARD
+ * or APOL_DOMAIN_TRANS_DIRECTION_REVERSE.
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param direction The direction to analyze using one of the two values above.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ */
+ extern int apol_domain_trans_analysis_set_direction(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ unsigned char direction);
+
+/**
+ * Set the analysis to search for transitions based upon whether they
+ * would be permitted. The value must be one of APOL_DOMAIN_TRANS_SEARCH_*
+ * defined above. The default for a newly created analysis is to search
+ * for only valid transitions (i.e. APOL_DOMAIN_TRANS_SEARCH_VALID).
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param valid One of APOL_DOMAIN_TRANS_SEARCH_*.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ */
+ extern int apol_domain_trans_analysis_set_valid(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ unsigned char valid);
+
+/**
+ * Set the analysis to begin searching using a given type. This function
+ * must be called prior to running the analysis. If a previous type
+ * was set, it will be free()'d first.
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param type_name Name of the type from which to begin searching.
+ * Must be non-NULL. This string will be duplicated.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ */
+ extern int apol_domain_trans_analysis_set_start_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *type_name);
+
+/**
+ * Set the analysis to return only types matching a regular expression.
+ * Note that the regular expression will also match types' aliases.
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param result Only return results matching this regular expression, or
+ * NULL to return all types.
+ * @return 0 on success, and < 0 on failure; if the call fails,
+ * errno will be set.
+ */
+ extern int apol_domain_trans_analysis_set_result_regex(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *regex);
+
+/**
+ * Set the analysis to return only types having access (via allow
+ * rules) to this type. <b>This is only valid for forward
+ * analysis.</b> If more than one type is appended to the query, the
+ * resulting type must have access to at least one of the appended
+ * types. Pass a NULL to clear all previously appended types. <b>If
+ * access types are appended, the caller must also call
+ * apol_domain_trans_analysis_append_class() at least once with
+ * a valid class and apol_domain_trans_analysis_append_perm() at
+ * least once with a valid permission.</b>
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param type_name Type to which a result must have access.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ */
+ extern int apol_domain_trans_analysis_append_access_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *type_name);
+
+/**
+ * Set the analysis to return only types having access (via allow
+ * rules) to this class with the given permission. <b>This is only
+ * valid for forward analysis.</b> If more than one class is appended
+ * to the query, the resulting type must have access to at least one
+ * of the appended classes. If more than one permission is appended
+ * for the same class, the resulting type must have at least one of
+ * the appended permissions for that class. Pass a NULL to both
+ * strings to clear all previously appended classes and
+ * permissions. <b>If access classes and permissions are appended,
+ * the caller must also call
+ * apol_domain_trans_analysis_append_access_type() at least once with
+ * a valid type.</b>
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param class_name The class to which a result must have access.
+ * @param perm_name The permission which a result must have
+ * for the given class.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ * @deprecated This function has been split into
+ * apol_domain_trans_analysis_append_class() and
+ * apol_domain_trans_analysis_append_perm()
+ */
+ extern int apol_domain_trans_analysis_append_class_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *class_name, const char *perm_name)
+ __attribute__ ((deprecated));
+
+/**
+ * Set the analysis to return only types having access (via allow
+ * rules) to this class. <b>This is only valid for forward
+ * analysis.</b> If more than one class is appended to the query, the
+ * resulting type must have access to at least one of the appended
+ * classes. Pass a NULL to clear all previously appended classes.
+ * <b>If access classes are appended, the caller must also call
+ * apol_domain_trans_analysis_append_access_type() at least once with
+ * a valid type and apol_domain_trans_analysis_append_perm() with a
+ * valid permission.</b>
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param class_name The class to which a result must have access.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ */
+ extern int apol_domain_trans_analysis_append_class(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *class_name);
+
+/**
+ * Set the analysis to return only types having access (via allow
+ * rules) to this permission. <b>This is only valid for forward
+ * analysis.</b> If more than one permission is appended the
+ * resulting type must have at least one of the appended permissions.
+ * Pass a NULL to clear all previously appended permissions. <b>If
+ * access permissions are appended, the caller must also call
+ * apol_domain_trans_analysis_append_access_type() at least once with
+ * a valid type and apol_domain_trans_analysis_append_class() at
+ * least once with a valid class.</b>
+ * @param policy Policy handler, to report errors.
+ * @param dta Domain transition analysis to set.
+ * @param perm_name The permission which a result must have.
+ * @return 0 on success, and < 0 on error; if the call fails,
+ * errno will be set and dta will be unchanged.
+ */
+ extern int apol_domain_trans_analysis_append_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *perm_name);
+
+/**
+ * Execute a domain transition analysis against a particular policy.
+ * @param policy Policy containing the table to use.
+ * @param dta A non-NULL structure containng parameters for analysis.
+ * @param results A reference pointer to a vector of
+ * apol_domain_trans_result_t. The vector will be allocated by this
+ * function. The caller must call apol_vector_destroy()
+ * afterwards. This will be set to NULL upon error.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *results will be NULL.
+ *
+ * @see apol_policy_reset_domain_trans_table()
+ */
+ extern int apol_domain_trans_analysis_do(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t ** results);
+
+/***************** functions for accessing results ************************/
+
+/**
+ * Return the start type of the transition in an
+ * apol_domain_trans_result node. The caller should not free the
+ * returned pointer. If the transition in the node is not valid
+ * there may be no start type in which case NULL is returned.
+ * @param dtr Domain transition result node.
+ * @return Pointer to the start type of the transition.
+ */
+ extern const qpol_type_t *apol_domain_trans_result_get_start_type(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the entrypoint type of the transition in an
+ * apol_domain_trans_result node. The caller should not free the
+ * returned pointer. If the transition in the node is not valid
+ * there may be no entrypoint in which case NULL is returned.
+ * @param dtr Domain transition result node.
+ * @return Pointer to the entrypoint type of the transition.
+ */
+ extern const qpol_type_t *apol_domain_trans_result_get_entrypoint_type(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the end type of the transition in an apol_domain_trans_result
+ * node. The caller should not free the returned pointer. If the transition
+ * in the node is not valid there may be no end type in which case NULL
+ * is returned.
+ * @param dtr Domain transition result node.
+ * @return Pointer to the start type of the transition.
+ */
+ extern const qpol_type_t *apol_domain_trans_result_get_end_type(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the vector of process transition rules (qpol_avrule_t
+ * pointers) in an apol_domain_trans_result node. The caller should
+ * not free the returned pointer. If the transition is invalid then
+ * the returned vector will be empty.
+ * @param dtr Domain transition result node.
+ * @return Vector of qpol_avrule_t relative to the policy originally
+ * used to generate the results.
+ */
+ extern const apol_vector_t *apol_domain_trans_result_get_proc_trans_rules(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the vector of file entrypoint rules (qpol_avrule_t
+ * pointers) in an apol_domain_trans_result node. The caller should
+ * not free the returned pointer. If the transition is invalid then
+ * the returned vector will be empty.
+ * @return Vector of qpol_avrule_t relative to the policy originally
+ * used to generate the results.
+ */
+ extern const apol_vector_t *apol_domain_trans_result_get_entrypoint_rules(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the vector of file execute rules (qpol_avrule_t pointers)
+ * in an apol_domain_trans_result node. The caller should not free
+ * the returned pointer. If the transition is invalid then the
+ * returned vector will be empty.
+ * @return Vector of qpol_avrule_t relative to the policy originally
+ * used to generate the results.
+ */
+ extern const apol_vector_t *apol_domain_trans_result_get_exec_rules(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the vector of process setexec rules (qpol_avrule_t
+ * pointers) in an apol_domain_trans_result node. The caller should
+ * not free the returned pointer. For all policies of version 15 or
+ * later a transition requires either a setexec rule or a
+ * type_transition rule to be valid. Valid transitions may have
+ * both; if there is no rule, this function returns an empty vector.
+ * @param dtr Domain transition result node.
+ * @return Vector of qpol_avrule_t relative to the policy originally
+ * used to generate the results.
+ */
+ extern const apol_vector_t *apol_domain_trans_result_get_setexec_rules(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the vector of type_transition rules (qpol_terule_t
+ * pointers) in an apol_domain_trans_result node. The caller should
+ * not free the returned pointer. For all policies of version 15 or
+ * later a transition requires either a setexec rule or a
+ * type_transition rule to be valid. Valid transitions may have
+ * both; if there is no rule, this function returns an empty vector.
+ * @param dtr Domain transition result node.
+ * @return Vector of qpol_terule_t relative to the policy originally
+ * used to generate the results.
+ */
+ extern const apol_vector_t *apol_domain_trans_result_get_type_trans_rules(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Determine if the transition in an apol_domain_trans_result node is valid.
+ * @param dtr Domain transition result node.
+ * @return 0 if invalid and non-zero if valid. If dtr is NULL, returns 0.
+ */
+ extern int apol_domain_trans_result_is_trans_valid(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Return the vector of access rules which satisfied the access
+ * types, classes, and permissions specified in the query. This is a
+ * vector of qpol_avrule_t pointers. The caller <b>should not</b>
+ * call apol_vector_destroy() upon the returned vector. This vector
+ * is only populated if access criteria were specified in the
+ * analysis.
+ *
+ * @param dtr Domain transition result node.
+ * @return Pointer to a vector of rules relative to the policy originally
+ * used to generate the results.
+ */
+ extern const apol_vector_t *apol_domain_trans_result_get_access_rules(const apol_domain_trans_result_t * dtr);
+
+/**
+ * Do a deep copy (i.e., a clone) of an apol_domain_trans_result_t
+ * object. The caller is responsible for calling
+ * apol_domain_trans_result_destroy() upon the returned value.
+ *
+ * @param result Pointer to a domain trans result structure to
+ * destroy.
+ *
+ * @return A clone of the passed in result node, or NULL upon error.
+ */
+ extern apol_domain_trans_result_t *apol_domain_trans_result_create_from_domain_trans_result(const apol_domain_trans_result_t
+ * in);
+
+/**
+ * Free all memory used by an apol_domain_trans_result_t object and
+ * set it to NULL. This does nothing if the pointer is already NULL.
+ * <b>This should only be called for results created by
+ * apol_domain_trans_result_create_from_domain_trans_result() and not
+ * those returned from within vectors.</b>
+ *
+ * @param res Reference pointer to a result to destroy.
+ */
+ extern void apol_domain_trans_result_destroy(apol_domain_trans_result_t ** res);
+
+/************************ utility functions *******************************/
+/* define the following for rule type */
+#define APOL_DOMAIN_TRANS_RULE_PROC_TRANS 0x01
+#define APOL_DOMAIN_TRANS_RULE_EXEC 0x02
+#define APOL_DOMAIN_TRANS_RULE_EXEC_NO_TRANS 0x04
+#define APOL_DOMAIN_TRANS_RULE_ENTRYPOINT 0x08
+#define APOL_DOMAIN_TRANS_RULE_TYPE_TRANS 0x10
+#define APOL_DOMAIN_TRANS_RULE_SETEXEC 0x20
+
+/**
+ * Verify that a transition using the given three types is valid in the given
+ * policy. If not valid, return a value indicating the missing rules. If any
+ * type is NULL, rules that would contain that type are considered missing. A
+ * valid transition requires a process transition, an entrypoint, and an
+ * execute rule. If the policy is version 15 or later it also requires either
+ * a setexec rule or a type_transition rule. The value
+ * APOL_DOMAIN_TRANS_RULE_EXEC_NO_TRANS is not returned by this function.
+ *
+ * @param policy The policy containing the domain transition table to
+ * consult. Must be non-NULL.
+ * @param start_dom The starting domain of the transition. May be NULL.
+ * @param ep_type The entrypoint of the transition. May be NULL.
+ * @param end_dom The ending domain of the transition. May be NULL.
+ *
+ * @return 0 if the transition is valid, < 0 on error, or a bit-wise
+ * or'ed set of APOL_DOMAIN_TRANS_RULE_* from above (always > 0)
+ * representing the rules missing from the transition.
+ */
+ extern int apol_domain_trans_table_verify_trans(apol_policy_t * policy, const qpol_type_t * start_dom,
+ const qpol_type_t * ep_type, const qpol_type_t * end_dom);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_DOMAIN_TRANS_ANALYSIS_H */
diff --git a/libapol/include/apol/fscon-query.h b/libapol/include/apol/fscon-query.h
new file mode 100644
index 0000000..18d0920
--- /dev/null
+++ b/libapol/include/apol/fscon-query.h
@@ -0,0 +1,249 @@
+/**
+ * @file
+ * Public Interface for querying genfscons and fs_uses of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_FSCON_QUERY_H
+#define APOL_FSCON_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include "context-query.h"
+#include <string.h>
+#include <qpol/policy.h>
+
+ typedef struct apol_genfscon_query apol_genfscon_query_t;
+ typedef struct apol_fs_use_query apol_fs_use_query_t;
+
+/******************** genfscon queries ********************/
+
+/**
+ * Execute a query against all genfscons within the policy. The
+ * returned genfscons will be unordered.
+ *
+ * @param p Policy within which to look up genfscons.
+ * @param g Structure containing parameters for query. If this is
+ * NULL then return all genfscons.
+ * @param v Reference to a vector of qpol_genfscon_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_genfscon_get_by_query(const apol_policy_t * p, const apol_genfscon_query_t * g, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new genfscon query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all genfscons within the policy. The caller must call
+ * apol_genfscon_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized genfscon query structure, or NULL upon
+ * error.
+ */
+ extern apol_genfscon_query_t *apol_genfscon_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced genfscon
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param g Reference to a genfscon query structure to destroy.
+ */
+ extern void apol_genfscon_query_destroy(apol_genfscon_query_t ** g);
+
+/**
+ * Set a genfscon query to return only genfscons that act upon this
+ * filesystem.
+ *
+ * @param p Policy handler, to report errors.
+ * @param g Genfscon query to set.
+ * @param fs Limit query to only genfscons with this filesystem, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_genfscon_query_set_filesystem(const apol_policy_t * p, apol_genfscon_query_t * g, const char *fs);
+
+/**
+ * Set a genfscon query to return only genfscons that act upon this
+ * relative path. If the path includes a trailing slash, the search
+ * will ingore that slash.
+ *
+ * @param p Policy handler, to report errors.
+ * @param g Genfscon query to set.
+ * @param path Limit query to only genfscons with this path, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_genfscon_query_set_path(const apol_policy_t * p, apol_genfscon_query_t * g, const char *path);
+
+/**
+ * Set a genfscon query to return only genfscons that act upon this
+ * object class.
+ *
+ * @param p Policy handler, to report errors.
+ * @param g Genfscon query to set.
+ * @param class Limit query to only genfscons with this object class,
+ * which must be one of QPOL_CLASS_BLK_FILE, QPOL_CLASS_CHR_FILE,
+ * etc., or negative to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_genfscon_query_set_objclass(const apol_policy_t * p, apol_genfscon_query_t * g, int objclass);
+
+/**
+ * Set a genfscon query to return only genfscons matching a context.
+ * This function takes ownership of the context, such that the caller
+ * must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param g Genfscon query to set.
+ * @param context Limit query to only genfscons matching this context,
+ * or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_genfscon_query_set_context(const apol_policy_t * p,
+ apol_genfscon_query_t * g, apol_context_t * context, unsigned int range_match);
+
+/**
+ * Creates a string containing the textual representation of
+ * a genfscon type.
+ * @param p Reference to a policy.
+ * @param genfscon Reference to the genfscon statement to be rendered.
+ *
+ * @return A newly allocated string on success, caller must free;
+ * NULL on error.
+ */
+ extern char *apol_genfscon_render(const apol_policy_t * p, const qpol_genfscon_t * genfscon);
+
+/******************** fs_use queries ********************/
+
+/**
+ * Execute a query against all fs_uses within the policy. The
+ * returned fs_use statements will be unordered.
+ *
+ * @param p Policy within which to look up fs_use statements.
+ * @param f Structure containing parameters for query. If this is
+ * NULL then return all fs_use statements.
+ * @param v Reference to a vector of qpol_fs_use_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_fs_use_get_by_query(const apol_policy_t * p, const apol_fs_use_query_t * f, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new fs_use query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all fs_use statements within the policy. The caller must
+ * call apol_fs_use_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized fs_use query structure, or NULL upon error.
+ */
+ extern apol_fs_use_query_t *apol_fs_use_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced fs_use query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param f Reference to a fs_use query structure to destroy.
+ */
+ extern void apol_fs_use_query_destroy(apol_fs_use_query_t ** f);
+
+/**
+ * Set a fs_use query to return only fs_use statements that act upon
+ * this filesystem.
+ *
+ * @param p Policy handler, to report errors.
+ * @param f fs_use query to set.
+ * @param fs Limit query to only fs_use statements with this
+ * filesystem, or NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_fs_use_query_set_filesystem(const apol_policy_t * p, apol_fs_use_query_t * f, const char *fs);
+
+/**
+ * Set a fs_use query to return only fs_use statements with this
+ * behavior.
+ *
+ * @param p Policy handler, to report errors.
+ * @param f fs_use query to set.
+ * @param behavior Limit query to only fs_use statements with this
+ * object class, which must be one of QPOL_FS_USE_XATTR,
+ * QPOL_FS_USE_TRANS, etc., or negative to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_fs_use_query_set_behavior(const apol_policy_t * p, apol_fs_use_query_t * f, int behavior);
+
+/**
+ * Set a fs_use query to return only fs_use statements matching a
+ * context. This function takes ownership of the context, such that
+ * the caller must not modify nor destroy it afterwards. Note that if
+ * a context is set, then the resulting query will never return
+ * fs_use_psid statements.
+ *
+ * @param p Policy handler, to report errors.
+ * @param f fs_use query to set.
+ * @param context Limit query to only fs_use statements matching this
+ * context, or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_fs_use_query_set_context(const apol_policy_t * p,
+ apol_fs_use_query_t * f, apol_context_t * context, unsigned int range_match);
+
+/**
+ * Creates a string containing the textual representation of
+ * a fs_use type.
+ * @param p Reference to a policy.
+ * @param fsuse Reference to the fs_use statement to be rendered.
+ *
+ * @return A newly allocated string on success, caller must free;
+ * NULL on error.
+ */
+ extern char *apol_fs_use_render(const apol_policy_t * p, const qpol_fs_use_t * fsuse);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_FSCON_QUERY_H */
diff --git a/libapol/include/apol/infoflow-analysis.h b/libapol/include/apol/infoflow-analysis.h
new file mode 100644
index 0000000..61b00a9
--- /dev/null
+++ b/libapol/include/apol/infoflow-analysis.h
@@ -0,0 +1,387 @@
+/**
+ * @file
+ *
+ * Routines to perform an information flow analysis, both direct and
+ * transitive flows.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_INFOFLOW_ANALYSIS_H
+#define APOL_INFOFLOW_ANALYSIS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+/*
+ * Information flows can be either direct (A -> B) or transitive (A ->
+ * {stuff} -> B).
+ */
+#define APOL_INFOFLOW_MODE_DIRECT 0x01
+#define APOL_INFOFLOW_MODE_TRANS 0x02
+
+/*
+ * All operations are mapped in either an information flow in or an
+ * information flow out (using the permission map). These defines are
+ * for the two flow directions plus flows in both or either direction
+ * for queries and query results.
+ */
+#define APOL_INFOFLOW_IN 0x01
+#define APOL_INFOFLOW_OUT 0x02
+#define APOL_INFOFLOW_BOTH (APOL_INFOFLOW_IN|APOL_INFOFLOW_OUT)
+#define APOL_INFOFLOW_EITHER 0x04
+
+ typedef struct apol_infoflow_graph apol_infoflow_graph_t;
+ typedef struct apol_infoflow_analysis apol_infoflow_analysis_t;
+ typedef struct apol_infoflow_result apol_infoflow_result_t;
+ typedef struct apol_infoflow_step apol_infoflow_step_t;
+
+/**
+ * Deallocate all space associated with a particular information flow
+ * graph, including the pointer itself. Afterwards set the pointer to
+ * NULL.
+ *
+ * @param g Reference to an apol_infoflow_graph_t to destroy.
+ */
+ extern void apol_infoflow_graph_destroy(apol_infoflow_graph_t ** g);
+
+/********** functions to do information flow analysis **********/
+
+/**
+ * Execute an information flow analysis against a particular policy.
+ * The policy must have had a permission map loaded via
+ * apol_policy_open_permmap(), else this analysis will abort
+ * immediately.
+ *
+ * @param p Policy within which to look up allow rules.
+ * @param ia A non-NULL structure containing parameters for analysis.
+ * @param v Reference to a vector of apol_infoflow_result_t. The
+ * vector will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon
+ * error.
+ * @param g Reference to the information flow graph constructed for
+ * the given infoflow analysis object. The graph will be allocated by
+ * this function; the caller is responsible for calling
+ * apol_infoflow_graph_destroy() afterwards. This will be set to NULL
+ * upon error.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_do(const apol_policy_t * p,
+ const apol_infoflow_analysis_t * ia, apol_vector_t ** v, apol_infoflow_graph_t ** g);
+
+/**
+ * Execute an information flow analysis against a particular policy
+ * and a pre-built information flow graph. The analysis will keep the
+ * same criteria that were used to build the graph, sans differing
+ * starting type.
+ *
+ * @param p Policy within which to look up allow rules.
+ * @param g Existing information flow graph to analyze.
+ * @param type New string from which to begin analysis.
+ * @param v Reference to a vector of apol_infoflow_result_t. The
+ * vector will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_do_more(const apol_policy_t * p, apol_infoflow_graph_t * g, const char *type,
+ apol_vector_t ** v);
+
+/**
+ * Prepare an existing transitive infoflow graph to do further
+ * searches upon two specific start and end types. The analysis is by
+ * way of a BFS with random restarts; thus each call to
+ * apol_infoflow_analysis_trans_further_next() may possibly return
+ * additional paths. This function is needed to prepare the pool of
+ * initial states for the search.
+ *
+ * @param p Policy from which infoflow rules derived.
+ * @param g Existing transitive infoflow graph. If it was already
+ * prepared then those values will be first destroyed.
+ * @param start_type String from which to begin further analysis.
+ * @param end_type String for target infoflow paths.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_infoflow_analysis_trans_further_prepare(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type,
+ const char *end_type);
+
+/**
+ * Find further transitive infoflow paths by way of a random restart.
+ * The infoflow graph must be first prepared by first calling
+ * apol_infoflow_analysis_trans_further_prepare(). This function will
+ * append to vector v any new results it finds.
+ *
+ * @param p Policy from which infoflow rules derived.
+ * @param g Prepared transitive infoflow graph.
+ * @param v Pointer to a vector of existing apol_infoflow_result_t
+ * pointers. If this functions finds additional unique results it
+ * will append them to this vector. If the pointer is NULL then this
+ * will allocate and return a new vector. It is the caller's
+ * responsibility to call apol_vector_destroy() afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_infoflow_analysis_trans_further_next(const apol_policy_t * p, apol_infoflow_graph_t * g,
+ apol_vector_t ** v);
+
+/********** functions to create/modify an analysis object **********/
+
+/**
+ * Allocate and return a new information analysis structure. All
+ * fields are cleared; one must fill in the details of the analysis
+ * before running it. The caller must call
+ * apol_infoflow_analysis_destroy() upon the return value afterwards.
+ *
+ * @return An initialized information flow analysis structure, or NULL
+ * upon error.
+ */
+ extern apol_infoflow_analysis_t *apol_infoflow_analysis_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced information
+ * flow analysis, and then set it to NULL. This function does nothing
+ * if the analysis is already NULL.
+ *
+ * @param ia Reference to an infoflow analysis structure to destroy.
+ */
+ extern void apol_infoflow_analysis_destroy(apol_infoflow_analysis_t ** ia);
+
+/**
+ * Set an information flow analysis mode to be either direct or
+ * transitive. This must be one of the values
+ * APOL_INFOFLOW_MODE_DIRECT, or APOL_INFOFLOW_MODE_TRANS. This
+ * function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param ia Infoflow analysis to set.
+ * @param mode Analysis mode, either direct or transitive.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_set_mode(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int mode);
+
+/**
+ * Set an information flow analysis to search in a specific direction.
+ * For direct infoflow analysis this must be one of the values
+ * APOL_INFOFLOW_IN, APOL_INFOFLOW_OUT, APOL_INFOFLOW_BOTH, or
+ * APOL_INFOFLOW_EITHER; transitive infoflow only permits the first
+ * two. This function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param ia Infoflow analysis to set.
+ * @param dir Direction to analyze, using one of the defines above.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_set_dir(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int dir);
+
+/**
+ * Set an information flow analysis to begin searching using a given
+ * type. This function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param ia Infoflow anlysis to set.
+ * @param name Begin searching types with this non-NULL name.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_set_type(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *name);
+
+/**
+ * Set an information flow analysis to return paths that only go
+ * through this intermediate type. If more than one type is appended
+ * to the analysis, every step of a return path will go through at
+ * least one of the types. These intermediate types are ignored when
+ * running a direct information flow analysis.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param ia Infoflow analysis to set.
+ * @param type Intermediate type which a result must flow through. If
+ * NULL, then clear all existing intermediate types. (All paths will
+ * be returned.)
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_append_intermediate(const apol_policy_t * p, apol_infoflow_analysis_t * ia,
+ const char *type);
+
+/**
+ * Set an information flow analysis to return only rules with this
+ * object (non-common) class and permission. If more than one
+ * class/perm pair is appended to the query, every rule's class and
+ * permissions must be one of those appended. (I.e., the rule will be
+ * a member of the analysis's class/perm pairs.)
+ *
+ * @param policy Policy handler, to report errors.
+ * @param ia Infoflow analysis to set.
+ * @param class_name The class to which a result must have access. If
+ * NULL, then accept all class/perm pairs.
+ * @param perm_name The permission which a result must have for the
+ * given class. This may be NULL if class_name is also NULL.
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_append_class_perm(const apol_policy_t * p,
+ apol_infoflow_analysis_t * ia, const char *class_name,
+ const char *perm_name);
+
+/**
+ * Set an information flow analysis to return only rules with at least
+ * one permission whose weight is greater than or equal to the given
+ * minimum. Permission weights are retrieved from the currently
+ * loaded permission map. If the given minimum exceeds
+ * APOL_PERMMAP_MAX_WEIGHT it will be clamped to that value.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param ia Infoflow analysis to set.
+ * @param min_weight Minimum weight for rules, or negative to accept
+ * all rules.
+ * @return Always 0.
+ */
+ extern int apol_infoflow_analysis_set_min_weight(const apol_policy_t * p, apol_infoflow_analysis_t * ia, int min_weight);
+
+/**
+ * Set an information flow analysis to return only types matching a
+ * regular expression. Note that the regexp will also match types'
+ * aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param ia Information flow anlysis to set.
+ * @param result Only return types matching this regular expression, or
+ * NULL to return all types
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_infoflow_analysis_set_result_regex(const apol_policy_t * p, apol_infoflow_analysis_t * ia,
+ const char *result);
+
+/*************** functions to access infoflow results ***************/
+
+/**
+ * Return the direction of an information flow result. This will be
+ * one of APOL_INFOFLOW_IN, APOL_INFOFLOW_OUT, or APOL_INFOFLOW_BOTH.
+ *
+ * @param result Infoflow result from which to get direction.
+ * @return Direction of result or zero on error.
+ */
+ extern unsigned int apol_infoflow_result_get_dir(const apol_infoflow_result_t * result);
+
+/**
+ * Return the start type of an information flow result. The caller
+ * should not free the returned pointer.
+ *
+ * @param result Infoflow result from which to get start type.
+ * @return Pointer to the start type of the infoflow or NULL on error.
+ */
+ extern const qpol_type_t *apol_infoflow_result_get_start_type(const apol_infoflow_result_t * result);
+
+/**
+ * Return the end type of an information flow result. The caller
+ * should not free the returned pointer.
+ *
+ * @param result Infoflow result from which to get end type.
+ * @return Pointer to the end type of the infoflow or NULL on error.
+ */
+ extern const qpol_type_t *apol_infoflow_result_get_end_type(const apol_infoflow_result_t * result);
+
+/**
+ * Return the length of an information flow result. This represents
+ * how easily information flows from the start to end type, where
+ * lower numbers are easier than higher numbers. This is dependent
+ * upon the weights assigned in the currently loaded permission map.
+ *
+ * @param result Infoflow result from which to get length.
+ * @return Length of result or zero on error.
+ */
+ extern unsigned int apol_infoflow_result_get_length(const apol_infoflow_result_t * result);
+
+/**
+ * Return the vector of infoflow steps for a particular information
+ * flow result. This is a vector of apol_infoflow_step_t pointers.
+ * The caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. Note that for a direct infoflow analysis this
+ * vector will consist of exactly one step; for transitive analysis
+ * the vector will have multiple steps.
+ *
+ * @param result Infoflow result from which to get steps.
+ *
+ * @return Pointer to a vector of steps found between the result's
+ * start and end types or NULL on error.
+ */
+ extern const apol_vector_t *apol_infoflow_result_get_steps(const apol_infoflow_result_t * result);
+
+/**
+ * Return the starting type for an information flow step. The caller
+ * should not free the returned pointer.
+ *
+ * @param step Infoflow step from which to get start type.
+ * @return Pointer to the start type for this infoflow step or NULL on error.
+ */
+ extern const qpol_type_t *apol_infoflow_step_get_start_type(const apol_infoflow_step_t * step);
+
+/**
+ * Return the ending type for an information flow step. The caller
+ * should not free the returned pointer.
+ *
+ * @param step Infoflow step from which to get end type.
+ * @return Pointer to the start type for this infoflow step or NULL on error.
+ */
+ extern const qpol_type_t *apol_infoflow_step_get_end_type(const apol_infoflow_step_t * step);
+
+/**
+ * Return the weight of an information flow step. For a direct
+ * infoflow analysis the weight is zero. For a transitive
+ * analysis this is an integer value that quantatizes the amount of
+ * information that could flow between the start and end types; it is
+ * based upon the currently opened permission map. It will be a value
+ * between APOL_PERMMAP_MIN_WEIGHT and APOL_PERMMAP_MAX_WEIGHT,
+ * inclusive.
+ *
+ * @param step Infoflow step from which to get weight.
+ * @return Weight of step or < 0 on error.
+ */
+ extern int apol_infoflow_step_get_weight(const apol_infoflow_step_t * step);
+
+/**
+ * Return the vector of access rules for a particular information
+ * step. This is a vector of qpol_avrule_t pointers. The caller
+ * <b>should not</b> call apol_vector_destroy() upon the returned
+ * vector.
+ *
+ * @param step Infoflow flow step from which to get rules.
+ *
+ * @return Pointer to a vector of rules relative to the policy originally
+ * used to generate the results or NULL on error.
+ */
+ extern const apol_vector_t *apol_infoflow_step_get_rules(const apol_infoflow_step_t * step);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/isid-query.h b/libapol/include/apol/isid-query.h
new file mode 100644
index 0000000..6c66ae9
--- /dev/null
+++ b/libapol/include/apol/isid-query.h
@@ -0,0 +1,111 @@
+/**
+ * @file
+ * Public Interface for querying initial SIDs of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_ISID_QUERY_H
+#define APOL_ISID_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include "context-query.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_isid_query apol_isid_query_t;
+
+/******************** isid queries ********************/
+
+/**
+ * Execute a query against all initial SIDs within the policy. The
+ * returned isids will be unordered.
+ *
+ * @param p Policy within which to look up initial SIDs.
+ * @param i Structure containing parameters for query. If this is
+ * NULL then return all isids.
+ * @param v Reference to a vector of qpol_isid_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_isid_get_by_query(const apol_policy_t * p, const apol_isid_query_t * i, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new isid query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all initial SIDs within the policy. The caller must call
+ * apol_isid_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized isid query structure, or NULL upon error.
+ */
+ extern apol_isid_query_t *apol_isid_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced isid query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param i Reference to an isid query structure to destroy.
+ */
+ extern void apol_isid_query_destroy(apol_isid_query_t ** i);
+
+/**
+ * Set an isid query to return only initial SIDs with this name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param i isid query to set.
+ * @param name Limit query to only initial SIDs with this name, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_isid_query_set_name(const apol_policy_t * p, apol_isid_query_t * i, const char *name);
+
+/**
+ * Set an isid query to return only initial SIDs matching a context.
+ * This function takes ownership of the context, such that the caller
+ * must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param i isid query to set.
+ * @param context Limit query to only initial SIDs matching this
+ * context, or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_isid_query_set_context(const apol_policy_t * p,
+ apol_isid_query_t * i, apol_context_t * context, unsigned int range_match);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_ISID_QUERY_H */
diff --git a/libapol/include/apol/mls-query.h b/libapol/include/apol/mls-query.h
new file mode 100644
index 0000000..22ee916
--- /dev/null
+++ b/libapol/include/apol/mls-query.h
@@ -0,0 +1,228 @@
+/**
+ * @file
+ * Public interface for querying MLS components, and for
+ * sensitivities and categories within a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_MLS_QUERY_H
+#define APOL_MLS_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "mls_level.h"
+#include "mls_range.h"
+#include "vector.h"
+
+ typedef struct apol_level_query apol_level_query_t;
+ typedef struct apol_cat_query apol_cat_query_t;
+
+/* MLS comparisons function will return one of the following on
+ success or -1 on error */
+#define APOL_MLS_EQ 0
+#define APOL_MLS_DOM 1
+#define APOL_MLS_DOMBY 2
+#define APOL_MLS_INCOMP 3
+
+/**
+ * Determine if two sensitivities are actually the same. Either level
+ * or both could be using a sensitivity's alias, thus straight string
+ * comparison is not sufficient.
+ *
+ * @param p Policy within which to look up MLS information.
+ * @param sens1 First sensitivity to compare.
+ * @param sens2 Second sensitivity to compare.
+ *
+ * @return 1 If comparison succeeds, 0 if not; -1 on error.
+ */
+ extern int apol_mls_sens_compare(const apol_policy_t * p, const char *sens1, const char *sens2);
+
+/**
+ * Determine if two categories are actually the same. Either category
+ * or both could be using a category's alias, thus straight string
+ * comparison is not sufficient.
+ *
+ * @param p Policy within which to look up MLS information.
+ * @param cat1 First category to compare.
+ * @param cat2 Second category to compare.
+ *
+ * @return 1 If comparison succeeds, 0 if not; -1 on error.
+ */
+ extern int apol_mls_cats_compare(const apol_policy_t * p, const char *cat1, const char *cat2);
+
+/******************** level queries ********************/
+
+/**
+ * Execute a query against all levels within the policy. The results
+ * will only contain levels, not sensitivity aliases. The returned
+ * levels will be unordered.
+ *
+ * @param p Policy within which to look up levels.
+ * @param l Structure containing parameters for query. If this is
+ * NULL then return all levels.
+ * @param v Reference to a vector of qpol_level_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon
+ * upon error. Note that the vector may be empty if the policy is
+ * not an MLS policy.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_level_get_by_query(const apol_policy_t * p, apol_level_query_t * l, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new level query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all levels within the policy. The caller must call
+ * apol_level_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized level query structure, or NULL upon error.
+ */
+ extern apol_level_query_t *apol_level_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced level query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param l Reference to a level query structure to destroy.
+ */
+ extern void apol_level_query_destroy(apol_level_query_t ** l);
+
+/**
+ * Set a level query to return only levels that match this name. The
+ * name may be either a sensitivity or one of its aliases. This
+ * function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param l Level query to set.
+ * @param name Limit query to only sensitivities or aliases with this
+ * name, or NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_level_query_set_sens(const apol_policy_t * p, apol_level_query_t * l, const char *name);
+
+/**
+ * Set a level query to return only levels contain a particular
+ * category. The name may be either a category or one of its aliases.
+ * This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param l Level query to set.
+ * @param name Limit query to levels containing this category or
+ * alias, or NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_level_query_set_cat(const apol_policy_t * p, apol_level_query_t * l, const char *name);
+
+/**
+ * Set a level query to use regular expression searching for all of
+ * its fields. Strings will be treated as regexes instead of
+ * literals. Matching will occur against the sensitivity name or any
+ * of its aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param l Level query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_level_query_set_regex(const apol_policy_t * p, apol_level_query_t * l, int is_regex);
+
+/******************** category queries ********************/
+
+/**
+ * Execute a query against all categories within the policy. The
+ * results will only contain categories, not aliases. The returned
+ * categories will be unordered.
+ *
+ * @param p Policy within which to look up categories.
+ * @param c Structure containing parameters for query. If this is
+ * NULL then return all categories.
+ * @param v Reference to a vector of qpol_cat_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon
+ * upon error. Note that the vector could be empty if the policy is
+ * not an MLS policy.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_cat_get_by_query(const apol_policy_t * p, apol_cat_query_t * c, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new category query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all categories within the policy. The caller must call
+ * apol_cat_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized category query structure, or NULL upon
+ * error.
+ */
+ extern apol_cat_query_t *apol_cat_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced category
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param c Reference to a category query structure to destroy.
+ */
+ extern void apol_cat_query_destroy(apol_cat_query_t ** c);
+
+/**
+ * Set a category query to return only categories that match this
+ * name. The name may be either a category or one of its aliases.
+ * This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Category query to set.
+ * @param name Limit query to only categories or aliases with this
+ * name, or NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_cat_query_set_cat(const apol_policy_t * p, apol_cat_query_t * c, const char *name);
+
+/**
+ * Set a category query to use regular expression searching for all of
+ * its fields. Strings will be treated as regexes instead of literals.
+ * Matching will occur against the category name or any of its
+ * aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param c Category query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_cat_query_set_regex(const apol_policy_t * p, apol_cat_query_t * c, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_MLS_QUERY_H */
diff --git a/libapol/include/apol/mls_level.h b/libapol/include/apol/mls_level.h
new file mode 100644
index 0000000..12e86b4
--- /dev/null
+++ b/libapol/include/apol/mls_level.h
@@ -0,0 +1,261 @@
+/**
+ * @file
+ * Public interface for representing and manipulating an
+ * apol_mls_level object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_MLS_LEVEL_H
+#define APOL_MLS_LEVEL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_mls_level apol_mls_level_t;
+
+/**
+ * Allocate and return a new MLS level structure. All fields are
+ * initialized to nothing. The caller must call
+ * apol_mls_level_destroy() upon the return value afterwards.
+ *
+ * @return An initialized MLS level structure, or NULL upon error.
+ */
+ extern apol_mls_level_t *apol_mls_level_create(void);
+
+/**
+ * Allocate and return an MLS level structure, initialized by an
+ * existing apol_mls_level_t object. The caller must call
+ * apol_mls_level_destroy() upon the return value afterwards.
+ *
+ * @param level Level to copy. If NULL then the returned MLS level
+ * will be initialized to nothing.
+ *
+ * @return An initialized MLS level structure, or NULL upon error.
+ */
+ extern apol_mls_level_t *apol_mls_level_create_from_mls_level(const apol_mls_level_t * level);
+
+/**
+ * Take a MLS level string (e.g., <b>S0:C0.C127</b>) and parse it.
+ * Fill in a newly allocated apol_mls_level_t and return it. This
+ * function needs a policy to resolve dots within categories. If the
+ * string represents an illegal level then return NULL. The caller
+ * must call apol_mls_level_destroy() upon the returned value
+ * afterwards.
+ *
+ * @param p Policy within which to validate mls_level_string.
+ * @param mls_level_string Pointer to a string representing a valid
+ * MLS level.
+ *
+ * @return A filled in MLS level structure, or NULL upon error.
+ */
+ extern apol_mls_level_t *apol_mls_level_create_from_string(const apol_policy_t * p, const char *mls_level_string);
+
+/**
+ * Take a literal MLS level string (e.g., <b>S0:C0.C127</b>), fill in
+ * a newly allocated apol_mls_level_t and return it. The category
+ * portion of the level will <strong>not</strong> be expanded (i.e.,
+ * dots will not be resolved). The caller must call
+ * apol_mls_level_destroy() upon the returned value afterwards.
+ *
+ * Because this function creates a level without the benefit of a
+ * policy, its category list is "incomplete" and thus most operations
+ * will fail. All functions other than apol_mls_level_render(),
+ * apol_mls_level_convert(), and apol_mls_level_is_literal() will
+ * result in error. Call apol_mls_level_convert() to make a literal
+ * MLS level complete, so that it can be used in all functions.
+ *
+ * @param mls_level_string Pointer to a string representing a
+ * (possibly invalid) MLS level.
+ *
+ * @return A filled in MLS level structure, or NULL upon error.
+ */
+ extern apol_mls_level_t *apol_mls_level_create_from_literal(const char *mls_level_string);
+
+/**
+ * Create a new apol_mls_level_t and initialize it with a
+ * qpol_mls_level_t. The caller must call apol_mls_level_destroy()
+ * upon the returned value afterwards.
+ *
+ * @param p Policy from which the qpol_mls_level_t was obtained.
+ * @param qpol_level The libqpol level for which to create a new
+ * apol level. This level will not be altered by this call.
+ *
+ * @return A MLS level structure initialized to the value of
+ * qpol_level, or NULL upon error.
+ */
+ extern apol_mls_level_t *apol_mls_level_create_from_qpol_mls_level(const apol_policy_t * p,
+ const qpol_mls_level_t * qpol_level);
+
+/**
+ * Create a new apol_mls_level_t and initialize it with a
+ * qpol_level_t. The caller must call apol_mls_level_destroy()
+ * upon the returned value afterwards.
+ *
+ * @param p Policy from which the qpol_level_t was obtained.
+ * @param qpol_level The libqpol level for which to create a new
+ * apol level. This level will not be altered by this call.
+ *
+ * @return A MLS level structure initialized to the value of
+ * qpol_level, or NULL upon error.
+ */
+ apol_mls_level_t *apol_mls_level_create_from_qpol_level_datum(const apol_policy_t * p, const qpol_level_t * qpol_level);
+
+/**
+ * Deallocate all memory associated with a MLS level structure and
+ * then set it to NULL. This function does nothing if the level is
+ * already NULL.
+ *
+ * @param level Reference to a MLS level structure to destroy.
+ */
+ extern void apol_mls_level_destroy(apol_mls_level_t ** level);
+
+/**
+ * Set the sensitivity component of an MLS level structure. This
+ * function duplicates the incoming string.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param level MLS level to modify.
+ * @param sens New sensitivity component to set, or NULL to unset this
+ * field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_mls_level_set_sens(const apol_policy_t * p, apol_mls_level_t * level, const char *sens);
+
+/**
+ * Get the sensitivity component of an MLS level structure.
+ *
+ * @param level MLS level to query.
+ *
+ * @return The sensitivity, or NULL upon error if it has not yet been
+ * set. Do not modify the return value.
+ */
+ extern const char *apol_mls_level_get_sens(const apol_mls_level_t * level);
+
+/**
+ * Add a category component of an MLS level structure. This function
+ * duplicates the incoming string.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param level MLS level to modify.
+ * @param cats New category component to append.
+ *
+ * @return 0 on success or < 0 on failure.
+ */
+ extern int apol_mls_level_append_cats(const apol_policy_t * p, apol_mls_level_t * level, const char *cats);
+
+/**
+ * Get the category component of an MLS level structure. This will be
+ * a vector of strings, sorted alphabetically.
+ *
+ * @param level MLS level to query.
+ *
+ * @return Vector of categories, or NULL upon error. Be aware that
+ * the vector could be empty if no categories have been set. Do not
+ * modify the return value.
+ */
+ extern const apol_vector_t *apol_mls_level_get_cats(const apol_mls_level_t * level);
+
+/**
+ * Compare two levels and determine their relationship to each other.
+ * Both levels must have their respective sensitivity and categories
+ * set. Levels may contain aliases in place of primary names. If
+ * level2 is NULL then this always returns APOL_MLS_EQ.
+ *
+ * @param p Policy within which to look up MLS information.
+ * @param target Target MLS level to compare.
+ * @param search Source MLS level to compare.
+ *
+ * @return One of APOL_MLS_EQ, APOL_MLS_DOM, APOL_MLS_DOMBY, or
+ * APOL_MLS_INCOMP; < 0 on error.
+ *
+ * @see apol_mls_level_validate()
+ */
+ extern int apol_mls_level_compare(const apol_policy_t * p, const apol_mls_level_t * level1,
+ const apol_mls_level_t * level2);
+
+/**
+ * Given a level, determine if it is legal according to the supplied
+ * policy. This function will convert from aliases to canonical forms
+ * as necessary.
+ *
+ * @param h Error reporting handler.
+ * @param p Policy within which to look up MLS information.
+ * @param level Level to check.
+ *
+ * @return 1 If level is legal, 0 if not; < 0 on error.
+ *
+ * @see apol_mls_level_compare()
+ */
+ extern int apol_mls_level_validate(const apol_policy_t * p, const apol_mls_level_t * level);
+
+/**
+ * Creates a string containing the textual representation of
+ * a MLS level.
+ * @param p Policy from which the MLS level is a member. If NULL,
+ * then attempt to treat the level as an incomplete level (as per
+ * apol_mls_level_create_from_literal()).
+ * @param level MLS level to render.
+ *
+ * @return A newly allocated string, or NULL upon error. The caller
+ * is responsible for calling free() upon the return value.
+ */
+ extern char *apol_mls_level_render(const apol_policy_t * p, const apol_mls_level_t * level);
+
+/**
+ * Given a policy and a MLS level created by
+ * apol_mls_level_create_from_literal(), convert the level to have a
+ * valid ("complete") list of categories. This will take the literal
+ * string stored within the level and resolve its category lists, such
+ * as by expanding dots. The level will keep its literal string, so
+ * that it may be converted again if given a different policy.
+ *
+ * @param p Policy containing category information.
+ * @param level MLS level to convert.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_mls_level_convert(const apol_policy_t * p, apol_mls_level_t * level);
+
+/**
+ * Determine if a level is literal (i.e., created from
+ * apol_mls_level_create_from_literal()). Note that converting a
+ * literal level (apol_mls_level_convert()) completes the level, but
+ * it is still a literal level.
+ *
+ * @param level Level to query.
+ *
+ * @return > 0 value if the level is literal, 0 if not, < 0 if unknown
+ * or upon error.
+ */
+ extern int apol_mls_level_is_literal(const apol_mls_level_t * level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_MLS_LEVEL_H */
diff --git a/libapol/include/apol/mls_range.h b/libapol/include/apol/mls_range.h
new file mode 100644
index 0000000..49dade7
--- /dev/null
+++ b/libapol/include/apol/mls_range.h
@@ -0,0 +1,270 @@
+/**
+ * @file
+ * Public interface for representing and manipulating the
+ * apol_mls_range object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_MLS_RANGE_H
+#define APOL_MLS_RANGE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "mls_level.h"
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_mls_range apol_mls_range_t;
+
+/**
+ * Allocate and return a new MLS range structure. All fields are
+ * initialized to nothing. The caller must call
+ * apol_mls_range_destroy() upon the return value afterwards.
+ *
+ * @return An initialized MLS range structure, or NULL upon error.
+ */
+ extern apol_mls_range_t *apol_mls_range_create(void);
+
+/**
+ * Allocate and return a new MLS range structure, initialized by an
+ * existing apol_mls_range_t. The caller must call
+ * apol_mls_range_destroy() upon the return value afterwards.
+ *
+ * @param range Range to copy. If NULL then the returned MLS range
+ * will be initialized to nothing.
+ *
+ * @return An initialized MLS range structure, or NULL upon error.
+ */
+ extern apol_mls_range_t *apol_mls_range_create_from_mls_range(const apol_mls_range_t * range);
+
+/**
+ * Take a MLS range string (e.g., <b>S0:C0.C10-S1:C0.C127</b>) and
+ * parse it. Fill in a newly allocated apol_mls_range_t and return
+ * it. This function needs a policy to resolve dots within categories
+ * and to ensure that the high level dominates the low. If the string
+ * represents an illegal range then return NULL. The caller must call
+ * apol_mls_range_destroy() upon the returned value afterwards.
+ *
+ * @param p Policy within which to validate mls_range_string.
+ * @param mls_range_string Pointer to a string representing a valid
+ * MLS range.
+ *
+ * @return A filled in MLS range structure, or NULL upon error.
+ */
+ extern apol_mls_range_t *apol_mls_range_create_from_string(const apol_policy_t * p, const char *mls_range_string);
+
+/**
+ * Take a literal MLS range string (e.g.,
+ * <b>S0:C0.C10-S1:C0.C127</b>), fill in a newly allocated
+ * apol_mls_range_t and return it. The category portions of the
+ * levels will <strong>not</strong> be expanded (i.e., dots will not
+ * be resolved); likewise there is no check that the high level
+ * dominates the low. The caller must call apol_mls_range_destroy()
+ * upon the returned value afterwards.
+ *
+ * Because this function creates a range without the benefit of a
+ * policy, its levels are "incomplete" and thus most operations will
+ * fail. Call apol_mls_range_convert() to make a literal MLS range
+ * complete, so that it can be used in all functions.
+ *
+ * @param mls_range_string Pointer to a string representing a
+ * (possibly invalid) MLS range.
+ *
+ * @return A filled in MLS range structure, or NULL upon error.
+ */
+ extern apol_mls_range_t *apol_mls_range_create_from_literal(const char *mls_range_string);
+
+/**
+ * Create a new apol_mls_range_t and initialize it with a
+ * qpol_mls_range_t. The caller must call apol_mls_range_destroy()
+ * upon the return value afterwards.
+ *
+ * @param p Policy from which the qpol_mls_range_t was obtained.
+ * @param qpol_range The libqpol range for which to create a new
+ * apol range. This range will not be altered by this call.
+ *
+ * @return A MLS range structure initialized to the value of
+ * qpol_range, or NULL upon error.
+ */
+ extern apol_mls_range_t *apol_mls_range_create_from_qpol_mls_range(const apol_policy_t * p,
+ const qpol_mls_range_t * qpol_range);
+
+/**
+ * Deallocate all memory associated with a MLS range structure and
+ * then set it to NULL. This function does nothing if the range is
+ * already NULL.
+ *
+ * @param range Reference to a MLS range structure to destroy.
+ */
+ extern void apol_mls_range_destroy(apol_mls_range_t ** range);
+
+/**
+ * Set the low level component of a MLS range structure. This
+ * function takes ownership of the level, such that the caller must
+ * not modify nor destroy it afterwards. It is legal to pass in the
+ * same pointer for the range's low and high level.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param range MLS range to modify.
+ * @param level New low level for range, or NULL to unset this field.
+ *
+ * @return 0 on success or < 0 on failure.
+ */
+ extern int apol_mls_range_set_low(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level);
+
+/**
+ * Set the high level component of a MLS range structure. This
+ * function takes ownership of the level, such that the caller must
+ * not modify nor destroy it afterwards. It is legal to pass in the
+ * same pointer for the range's low and high level.
+ *
+ * @param p Error reporting handler, or NULL to use default handler.
+ * @param range MLS range to modify.
+ * @param level New high level for range, or NULL to unset this field.
+ *
+ * @return 0 on success or < 0 on failure.
+ */
+ extern int apol_mls_range_set_high(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level);
+
+/**
+ * Get the low level component of a MLS range structure.
+ *
+ * @param range MLS range to query.
+ *
+ * @return Low level, or NULL upon error or if not yet set. Do not
+ * modify the return value.
+ */
+ extern const apol_mls_level_t *apol_mls_range_get_low(const apol_mls_range_t * range);
+
+/**
+ * Get the high level component of a MLS range structure.
+ *
+ * @param range MLS range to query.
+ *
+ * @return High level, or NULL upon error or if not yet set. Do not
+ * modify the return value.
+ */
+ extern const apol_mls_level_t *apol_mls_range_get_high(const apol_mls_range_t * range);
+
+/**
+ * Compare two ranges, determining if one matches the other. The
+ * fifth parameter gives how to match the ranges. For APOL_QUERY_SUB,
+ * if search is a subset of target. For APOL_QUERY_SUPER, if search
+ * is a superset of target. Other valid compare types are
+ * APOL_QUERY_EXACT and APOL_QUERY_INTERSECT. If a range is not valid
+ * according to the policy then this function returns -1. If search
+ * is NULL then comparison always succeeds.
+ *
+ * @param p Policy within which to look up MLS information.
+ * @param target Target MLS range to compare.
+ * @param search Source MLS range to compare.
+ * @param range_compare_type Specifies how to compare the ranges.
+ *
+ * @return 1 If comparison succeeds, 0 if not; -1 on error.
+ */
+ extern int apol_mls_range_compare(const apol_policy_t * p,
+ const apol_mls_range_t * target, const apol_mls_range_t * search,
+ unsigned int range_compare_type);
+
+/**
+ * Determine if a range completely contains a subrange given a certain
+ * policy. If a range is not valid according to the policy then this
+ * function returns -1.
+ *
+ * @param p Policy within which to look up MLS information.
+ * @param range Parent range to compare.
+ * @param subrange Child range to which compare.
+ *
+ * @return 1 If comparison succeeds, 0 if not; -1 on error.
+ */
+ extern int apol_mls_range_contain_subrange(const apol_policy_t * p, const apol_mls_range_t * range,
+ const apol_mls_range_t * subrange);
+/**
+ * Given a range, determine if it is legal according to the supplied
+ * policy. This function will convert from aliases to canonical forms
+ * as necessary.
+ *
+ * @param p Policy within which to look up MLS information.
+ * @param range Range to check.
+ *
+ * @return 1 If range is legal, 0 if not; -1 on error.
+ */
+ extern int apol_mls_range_validate(const apol_policy_t * p, const apol_mls_range_t * range);
+
+/**
+ * Given a range, return a vector of levels (type apol_mls_level_t *)
+ * that constitutes that range. The vector will be sorted in policy order.
+ *
+ * @param p Policy from which the level and category definitions reside.
+ * @param range Range to expand.
+ *
+ * @return Vector of levels, or NULL upon error. The caller is
+ * responsible for calling apol_vector_destroy() upon the returned
+ * value, passing apol_mls_level_free() as the second parameter.
+ */
+ extern apol_vector_t *apol_mls_range_get_levels(const apol_policy_t * p, const apol_mls_range_t * range);
+
+/**
+ * Creates a string containing the textual representation of
+ * a MLS range.
+ *
+ * @param p Policy from which the MLS range is a member. If NULL,
+ * then attempt to treat the range's levels as incomplete levels (as
+ * per apol_mls_level_create_from_literal()).
+ * @param range MLS range to render.
+ *
+ * @return A newly allocated string, or NULL upon error. The caller
+ * is responsible for calling free() upon the return value.
+ */
+ extern char *apol_mls_range_render(const apol_policy_t * p, const apol_mls_range_t * range);
+
+/**
+ * Given a range, convert any literal MLS levels within it (as per
+ * apol_mls_level_convert()) to a complete level. If the range has no
+ * levels or has no literal levels then do nothing.
+ *
+ * @param p Policy containing category information.
+ * @param range Range to convert.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_mls_range_convert(const apol_policy_t * p, apol_mls_range_t * range);
+
+/**
+ * Determine if the range contains any literal levels. (Levels that
+ * have been converted are still considered literal.)
+ *
+ * @param range Range to query.
+ *
+ * @return > 0 value if the range has a literal level, 0 if not, < 0
+ * if unknown or upon error.
+ */
+ extern int apol_mls_range_is_literal(const apol_mls_range_t * range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_MLS_RANGE_H */
diff --git a/libapol/include/apol/netcon-query.h b/libapol/include/apol/netcon-query.h
new file mode 100644
index 0000000..b74b229
--- /dev/null
+++ b/libapol/include/apol/netcon-query.h
@@ -0,0 +1,364 @@
+/**
+ * @file
+ * Public Interface for querying portcons, netifcons, and nodecons of
+ * a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_NETCON_QUERY_H
+#define APOL_NETCON_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include "context-query.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_portcon_query apol_portcon_query_t;
+ typedef struct apol_netifcon_query apol_netifcon_query_t;
+ typedef struct apol_nodecon_query apol_nodecon_query_t;
+
+/******************** portcon queries ********************/
+
+/**
+ * Execute a query against all portcons within the policy. The
+ * returned portcons will be unordered.
+ *
+ * @param p Policy within which to look up portcons.
+ * @param po Structure containing parameters for query. If this is
+ * NULL then return all portcons.
+ * @param v Reference to a vector of qpol_portcon_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_portcon_get_by_query(const apol_policy_t * p, const apol_portcon_query_t * po, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new portcon query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all portcons within the policy. The caller must call
+ * apol_portcon_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized portcon query structure, or NULL upon error.
+ */
+ extern apol_portcon_query_t *apol_portcon_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced portcon
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param po Reference to a portcon query structure to destroy.
+ */
+ extern void apol_portcon_query_destroy(apol_portcon_query_t ** po);
+
+/**
+ * Set a portcon query to return only portcons that use this protocol.
+ *
+ * @param p Policy handler, to report errors.
+ * @param po Portcon query to set.
+ * @param proto Limit query to only portcons with this protocol, or
+ * negative to unset this field.
+ *
+ * @return Always 0.
+ */
+ extern int apol_portcon_query_set_protocol(const apol_policy_t * p, apol_portcon_query_t * po, int proto);
+
+/**
+ * Set a portcon query to return only portcons with this as their low
+ * port.
+ *
+ * @param p Policy handler, to report errors.
+ * @param po Portcon query to set.
+ * @param low Limit query to only portcons with this low port, or
+ * negative to unset this field.
+ *
+ * @return Always 0.
+ */
+ extern int apol_portcon_query_set_low(const apol_policy_t * p, apol_portcon_query_t * po, int low);
+
+/**
+ * Set a portcon query to return only portcons with this as their high
+ * port.
+ *
+ * @param p Policy handler, to report errors.
+ * @param po Portcon query to set.
+ * @param high Limit query to only portcons with this high port, or
+ * negative to unset this field.
+ *
+ * @return Always 0.
+ */
+ extern int apol_portcon_query_set_high(const apol_policy_t * p, apol_portcon_query_t * po, int high);
+
+/**
+ * Set a portcon query to return only portcons matching a context.
+ * This function takes ownership of the context, such that the caller
+ * must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param po Portcon query to set.
+ * @param context Limit query to only portcons matching this context,
+ * or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_portcon_query_set_context(const apol_policy_t * p,
+ apol_portcon_query_t * po, apol_context_t * context, unsigned int range_match);
+
+/**
+ * Creates a string containing the textual representation of
+ * a portcon type.
+ * @param p Reference to a policy.
+ * @param portcon Reference to the portcon statement to be rendered.
+ *
+ * @return A newly allocated string on success, caller must free;
+ * NULL on error.
+ */
+ extern char *apol_portcon_render(const apol_policy_t * p, const qpol_portcon_t * portcon);
+
+/******************** netifcon queries ********************/
+
+/**
+ * Execute a query against all netifcons within the policy. The
+ * returned netifcons will be unordered.
+ *
+ * @param p Policy within which to look up netifcons.
+ * @param n Structure containing parameters for query. If this is
+ * NULL then return all netifcons.
+ * @param v Reference to a vector of qpol_netifcon_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards,. This will be set to NULL upon
+ * no results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_netifcon_get_by_query(const apol_policy_t * p, const apol_netifcon_query_t * n, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new netifcon query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all netifcons within the policy. The caller must call
+ * apol_netifcon_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized netifcon query structure, or NULL upon
+ * error.
+ */
+ extern apol_netifcon_query_t *apol_netifcon_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced netifcon
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param n Reference to a netifcon query structure to destroy.
+ */
+ extern void apol_netifcon_query_destroy(apol_netifcon_query_t ** n);
+
+/**
+ * Set a netifcon query to return only netifcons that use this device.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Netifcon query to set.
+ * @param dev Limit query to only netifcons that use this device, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_netifcon_query_set_device(const apol_policy_t * p, apol_netifcon_query_t * n, const char *dev);
+
+/**
+ * Set a netifcon query to return only netifcons matching this context
+ * for its interface. This function takes ownership of the context,
+ * such that the caller must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Netifcon query to set.
+ * @param context Limit query to only netifcon matching this context
+ * for its interface, or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_netifcon_query_set_if_context(const apol_policy_t * p,
+ apol_netifcon_query_t * n, apol_context_t * context,
+ unsigned int range_match);
+
+/**
+ * Set a netifcon query to return only netifcons matching this context
+ * for its messages. This function takes ownership of the context,
+ * such that the caller must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Netifcon query to set.
+ * @param context Limit query to only netifcon matching this context
+ * for its messages, or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_netifcon_query_set_msg_context(const apol_policy_t * p,
+ apol_netifcon_query_t * n, apol_context_t * context,
+ unsigned int range_match);
+
+/**
+ * Creates a string containing the textual representation of
+ * a netifcon type.
+ * @param p Reference to a policy.
+ * @param netifcon Reference to the netifcon statement to be rendered.
+ *
+ * @return A newly allocated string on success, caller must free;
+ * NULL on error.
+ */
+ extern char *apol_netifcon_render(const apol_policy_t * p, const qpol_netifcon_t * netifcon);
+
+/******************** nodecon queries ********************/
+
+/**
+ * Execute a query against all nodecons within the policy. The
+ * returned nodecons will be unordered.
+ *
+ * @param p Policy within which to look up nodecons.
+ * @param n Structure containing parameters for query. If this is
+ * NULL then return all nodecons.
+ * @param v Reference to a vector of qpol_nodecon_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_nodecon_get_by_query(const apol_policy_t * p, const apol_nodecon_query_t * n, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new nodecon query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all nodecons within the policy. The caller must call
+ * apol_nodecon_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized nodecon query structure, or NULL upon
+ * error.
+ */
+ extern apol_nodecon_query_t *apol_nodecon_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced nodecon
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param n Reference to a nodecon query structure to destroy.
+ */
+ extern void apol_nodecon_query_destroy(apol_nodecon_query_t ** n);
+
+/**
+ * Set a nodecon query to return only nodecons with this protocol,
+ * either IPv4 or IPv6.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Nodecon query to set.
+ * @param proto Limit query to only this protocol, either QPOL_IPV4 or
+ * QPOL_IPV6, or a negative value to unset this field.
+ *
+ * @return 0 if protocol was valid, -1 on error.
+ */
+ extern int apol_nodecon_query_set_protocol(const apol_policy_t * p, apol_nodecon_query_t * n, int proto);
+
+/**
+ * Set a nodecon query to return only nodecons with this address. If
+ * the protocol is QPOL_IPV4 then only the first element of the
+ * address array is used, for QPOL_IPV6 all four are used.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Nodecon query to set.
+ * @param addr Array of no more than four elements representing the
+ * address, or NULL to unset this field. This function will make a
+ * copy of the array.
+ * @param proto Format of address, either QPOL_IPV4 or QPOL_IPV6.
+ * This parameter is ignored if addr is NULL.
+ *
+ * @return 0 if protocol was valid, -1 on error.
+ */
+ extern int apol_nodecon_query_set_addr(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * addr, int proto);
+
+/**
+ * Set a nodecon query to return only nodecons with this netmask. If
+ * the protocol is QPOL_IPV4 then only the first element of the mask
+ * array is used, for QPOL_IPV6 all four are used.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Nodecon query to set.
+ * @param mask Array of no more than four elements representing the
+ * netmask, or NULL to unset this field. This function will make a
+ * copy of the array.
+ * @param proto Format of mask, either QPOL_IPV4 or QPOL_IPV6. This
+ * parameter is ignored if mask is NULL.
+ *
+ * @return 0 if protocol was valid, -1 on error.
+ */
+ extern int apol_nodecon_query_set_mask(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * mask, int proto);
+
+/**
+ * Set a nodecon query to return only nodecons matching this context.
+ * This function takes ownership of the context, such that the caller
+ * must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param n Nodecon query to set.
+ * @param context Limit query to only nodecons matching this context,
+ * or NULL to unset this field.
+ * @param range_match Specifies how to match the MLS range within the
+ * context. This must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if context is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_nodecon_query_set_context(const apol_policy_t * p,
+ apol_nodecon_query_t * n, apol_context_t * context, unsigned int range_match);
+
+/**
+ * Creates a string containing the textual representation of
+ * a nodecon type.
+ * @param p Reference to a policy.
+ * @param nodecon Reference to the nodecon statement to be rendered.
+ *
+ * @return A newly allocated string on success, caller must free;
+ * NULL on error.
+ */
+ extern char *apol_nodecon_render(const apol_policy_t * p, const qpol_nodecon_t * nodecon);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_NETCON_QUERY_H */
diff --git a/libapol/include/apol/perm-map.h b/libapol/include/apol/perm-map.h
new file mode 100644
index 0000000..ea2c0e7
--- /dev/null
+++ b/libapol/include/apol/perm-map.h
@@ -0,0 +1,137 @@
+/**
+ * @file
+ *
+ * Permission mapping routines for libapol. These maps assoicate all
+ * object class permissions with read, write, read&write, and none
+ * access. These maps are used, for example, by an information flow
+ * analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_PERMMAP_H
+#define APOL_PERMMAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+
+#define APOL_PERMMAP_MAX_WEIGHT 10
+#define APOL_PERMMAP_MIN_WEIGHT 1
+
+#define APOL_PERMMAP_UNMAPPED 0x00 /* defined object/perm, but no map */
+#define APOL_PERMMAP_READ 0x01
+#define APOL_PERMMAP_WRITE 0x02
+#define APOL_PERMMAP_BOTH (APOL_PERMMAP_READ | APOL_PERMMAP_WRITE)
+#define APOL_PERMMAP_NONE 0x10
+
+/**
+ * Read a permission map from a file into a policy. If there is a
+ * non-fatal error while loading (e.g., file declared an object class
+ * that does not exist within the policy) then generate a warning
+ * string and send it to the error handler stored within the policy.
+ *
+ * If a permission map was already loaded, then the existing one will
+ * be destroyed.
+ *
+ * @param p Policy to which store permission map.
+ * @param filename Name of file containing permission map.
+ *
+ * @return 0 on success, > 0 on success with warnings, < 0 on error.
+ */
+ extern int apol_policy_open_permmap(apol_policy_t * p, const char *filename);
+
+/**
+ * @deprecated Use apol_policy_open_permmap().
+ */
+ extern int apol_permmap_load(apol_policy_t * p, const char *filename) __attribute__ ((deprecated));
+
+/**
+ * Write the contents of permission map to a file. Any existing file
+ * will be overwritten.
+ *
+ * @param p Policy containing permission map.
+ * @param filename Destination filename.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_policy_save_permmap(const apol_policy_t * p, const char *filename);
+
+/**
+ * @deprecated Use apol_policy_save_permmap().
+ */
+ extern int apol_permmap_save(apol_policy_t * p, const char *filename) __attribute__ ((deprecated));
+
+/**
+ * Given a class and permission name, look up that permission mapping
+ * within a policy's permission map. Set the reference variables map
+ * and weight to the mapping.
+ *
+ * @param p Policy containing permission map.
+ * @param class_name Name of class to find.
+ * @param perm_name Permission within class to find.
+ * @param map Location to store mapping, one of APOL_PERMMAP_UNMAPPED,
+ * etc.
+ * @param weight Weight of this permission, a value between
+ * APOL_PERMMAP_MIN_WEIGHT and APOL_PERMMAP_MAX_WEIGHT, inclusive.
+ *
+ * @return 0 if class and permission were found, < 0 on error or if
+ * not found.
+ */
+ extern int apol_policy_get_permmap(const apol_policy_t * p, const char *class_name, const char *perm_name, int *map,
+ int *weight);
+
+/**
+ * @deprecated Use apol_policy_get_permmap().
+ */
+ extern int apol_permmap_get(apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight)
+ __attribute__ ((deprecated));
+
+/**
+ * Given a class and permission name, set that permission's map and
+ * weight within the policy's permission map.
+ *
+ * @param p Policy containing permission map.
+ * @param class_name Name of class to find.
+ * @param perm_name Permission within class to find.
+ * @param map New map value, one of APOL_PERMMAP_UNMAPPED, etc.
+ * @param weight New weight of this permission. If the value will be
+ * clamped to be between APOL_PERMMAP_MIN_WEIGHT and
+ * APOL_PERMMAP_MAX_WEIGHT, inclusive.
+ *
+ * @return 0 if permission map was changed, < 0 on error or if not
+ * found.
+ */
+ extern int apol_policy_set_permmap(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight);
+
+/**
+ * @deprecated Use apol_policy_set_permmap().
+ */
+ extern int apol_permmap_set(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight)
+ __attribute__ ((deprecated));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*APOL_PERMMAP_H */
diff --git a/libapol/include/apol/permissive-query.h b/libapol/include/apol/permissive-query.h
new file mode 100644
index 0000000..988ea47
--- /dev/null
+++ b/libapol/include/apol/permissive-query.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ *
+ * Routines to query permissive types in policy.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_PERMISSIVE_QUERY_H
+#define APOL_PERMISSIVE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_permissive_query apol_permissive_query_t;
+
+/**
+ * Execute a query against all permissive types within the policy. The results
+ * will only contain permissive types, not aliases nor attributes.
+ *
+ * @param p Policy within which to look up permissive types.
+ * @param t Structure containing parameters for query. If this is
+ * NULL then return all permissive types.
+ * @param v Reference to a vector of qpol_permissive_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_permissive_get_by_query(const apol_policy_t * p, apol_permissive_query_t * t, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new permissive query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all permissive types within the policy. The caller must call
+ * apol_permissive_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized permissive query structure, or NULL upon error.
+ */
+ extern apol_permissive_query_t *apol_permissive_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced permissive query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param t Reference to a permissive query structure to destroy.
+ */
+ extern void apol_permissive_query_destroy(apol_permissive_query_t ** t);
+
+/**
+ * Set a permissive query to return only permissive types that match this name. This function
+ * duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t Permissive query to set.
+ * @param name Limit query to only permissive types with this name, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_permissive_query_set_name(const apol_policy_t * p, apol_permissive_query_t * t, const char *name);
+
+/**
+ * Set a permissive query to use regular expression searching for all of its
+ * fields. Strings will be treated as regexes instead of literals.
+ * Matching will occur against the permissive type name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t Permissive query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_permissive_query_set_regex(const apol_policy_t * p, apol_permissive_query_t * t, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/polcap-query.h b/libapol/include/apol/polcap-query.h
new file mode 100644
index 0000000..b8f7d6b
--- /dev/null
+++ b/libapol/include/apol/polcap-query.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ *
+ * Routines to query policy capabilities in policy.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_POLCAP_QUERY_H
+#define APOL_POLCAP_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_polcap_query apol_polcap_query_t;
+
+/**
+ * Execute a query against all policy capabilities within the policy.
+ *
+ * @param p Policy within which to look up policy capabilities.
+ * @param t Structure containing parameters for query. If this is
+ * NULL then return all policy capabilities.
+ * @param v Reference to a vector of qpol_polcap_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_polcap_get_by_query(const apol_policy_t * p, apol_polcap_query_t * t, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new polcap query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all policy capabilities within the policy. The caller must call
+ * apol_polcap_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized polcap query structure, or NULL upon error.
+ */
+ extern apol_polcap_query_t *apol_polcap_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced polcap query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param t Reference to a polcap query structure to destroy.
+ */
+ extern void apol_polcap_query_destroy(apol_polcap_query_t ** t);
+
+/**
+ * Set a polcap query to return only policy capabilities that match this name. This function
+ * duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t Polcap query to set.
+ * @param name Limit query to only policy capabilities with this name, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_polcap_query_set_name(const apol_policy_t * p, apol_polcap_query_t * t, const char *name);
+
+/**
+ * Set a polcap query to use regular expression searching for all of its
+ * fields. Strings will be treated as regexes instead of literals.
+ * Matching will occur against the policy capability name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t Polcap query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_polcap_query_set_regex(const apol_policy_t * p, apol_polcap_query_t * t, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/policy-path.h b/libapol/include/apol/policy-path.h
new file mode 100644
index 0000000..771fdf5
--- /dev/null
+++ b/libapol/include/apol/policy-path.h
@@ -0,0 +1,194 @@
+/**
+ * @file
+ *
+ * An opaque structure that represents a policy "path". A policy path
+ * may really be a base policy and a number of modules, thus a single
+ * string is not sufficient.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_POLICY_PATH_H
+#define APOL_POLICY_PATH_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "vector.h"
+
+ typedef struct apol_policy_path apol_policy_path_t;
+
+/**
+ * Type of policy this path represents - either a single path, for a
+ * monolithic policy, or a path + multiple modules for modular policy.
+ */
+ typedef enum apol_policy_path_type
+ {
+ APOL_POLICY_PATH_TYPE_MONOLITHIC = 0,
+ APOL_POLICY_PATH_TYPE_MODULAR
+ } apol_policy_path_type_e;
+
+/**
+ * Create a policy path from scratch. The resulting object represents
+ * the file or files needed to load a policy.
+ *
+ * @param path_type Type of policy to represent.
+ * @param path Primary path name. For modular policies this is the
+ * base policy's path.
+ * @param modules Vector of strings representing modules' paths. The
+ * vector can be NULL to mean no modules. This parameter is ignored
+ * if path_type is not APOL_POLICY_PATH_TYPE_MODULAR. The function
+ * will duplicate the vector and its contents.
+ *
+ * @return An apol_policy_path object, or NULL upon error.
+ */
+ extern apol_policy_path_t *apol_policy_path_create(apol_policy_path_type_e path_type, const char *path,
+ const apol_vector_t * modules);
+
+/**
+ * Create a policy path, initialized from another policy path. This
+ * function recursively duplicates all data within the original path.
+ *
+ * @param path Policy path to duplicate.
+ *
+ * @return An apol_policy_path object, or NULL upon error.
+ */
+ extern apol_policy_path_t *apol_policy_path_create_from_policy_path(const apol_policy_path_t * path);
+
+/**
+ * Create a policy path, initialize by the contents of a <em>policy
+ * path list</em> file. Call apol_policy_path_to_filename() to write
+ * a policy path list to disk.
+ *
+ * @param filename Name of the file containing a policy path list.
+ *
+ * @return An apol_policy_path object, or NULL upon error.
+ */
+ extern apol_policy_path_t *apol_policy_path_create_from_file(const char *filename);
+
+/**
+ * Create a policy path, initialized by a special path format string.
+ * Call apol_policy_path_to_string() to create this string.
+ *
+ * @param path_string String containing initialization data for the
+ * object.
+ *
+ * @return An apol_policy_path object, or NULL upon error.
+ */
+ extern apol_policy_path_t *apol_policy_path_create_from_string(const char *path_string);
+
+/**
+ * Destroy the referencened policy path object.
+ *
+ * @param path Policy path to destroy. The pointer will be set to
+ * NULL afterwards. (If pointer is already NULL then do nothing.)
+ */
+ extern void apol_policy_path_destroy(apol_policy_path_t ** path);
+
+/**
+ * Compare two policy paths, determining if one is different than the
+ * other. The returned value is stable, in that it may be used as the
+ * basis for sorting a list of policy paths. Monolithic policies are
+ * considered "less than" modular policies.
+ *
+ * @param a First policy path to compare.
+ * @param b Second policy path to compare.
+ *
+ * @return < 0 if path A is "less than" B, > 0 if A is "greater than"
+ * B, or 0 if equivalent or undeterminable.
+ */
+ extern int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b);
+
+/**
+ * Get the type of policy this path object represents.
+ *
+ * @param path Policy path object to query.
+ *
+ * @return Type of policy the object represents.
+ */
+ extern apol_policy_path_type_e apol_policy_path_get_type(const apol_policy_path_t * path);
+
+/**
+ * Get the primary path name from a path object. For monolithic
+ * policies this is the path to the policy. For modular policies this
+ * is the base policy path.
+ *
+ * @param path Policy path object to query.
+ *
+ * @return Primary path, or NULL upon error. Do not modify
+ * this string.
+ */
+ extern const char *apol_policy_path_get_primary(const apol_policy_path_t * path);
+
+/**
+ * Get the list of modules from a path object. This will be a vector
+ * of strings. It is an error to call this function for non-modular
+ * policies.
+ *
+ * @param path Policy path object to query.
+ *
+ * @return Vector of module paths, or NULL upon error. Do not modify
+ * this vector or its contents. Note that the vector could be empty.
+ */
+ extern const apol_vector_t *apol_policy_path_get_modules(const apol_policy_path_t * path);
+
+/**
+ * Write a human-readable <em>policy path list</em> to disk. This
+ * file describes a policy path and is suitable as input to
+ * apol_policy_path_create_from_file().
+ *
+ * @param path Policy path to write to disk.
+ * @param filename Name of the file to write policy path list. If the
+ * file already exists it will be overwritten.
+ *
+ * @return 0 on successful write, < 0 on error.
+ */
+ extern int apol_policy_path_to_file(const apol_policy_path_t * path, const char *filename);
+
+/**
+ * Encode a path object into a specially formatted string. The
+ * resulting string is suitable as input to
+ * apol_policy_path_create_from_string().
+ *
+ * @param path Policy path object to encode.
+ *
+ * @return Formatted string for the path object, or NULL upon error.
+ * The caller is responsible for calling free() upon the returned
+ * value.
+ */
+ extern char *apol_policy_path_to_string(const apol_policy_path_t * path);
+
+/**
+ * Determine if a file is a policy path list.
+ *
+ * @param filename Name of the file to test.
+ *
+ * @return > 0 if the file is a policy path list, 0 if it is not,
+ * and < 0 on error.
+ */
+ extern int apol_file_is_policy_path_list(const char *filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/policy-query.h b/libapol/include/apol/policy-query.h
new file mode 100644
index 0000000..315f70e
--- /dev/null
+++ b/libapol/include/apol/policy-query.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * Routines to query parts of a policy. For each component and rule
+ * there is a query structure to specify the details of the query.
+ * Analyses are also included by this header file.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_POLICY_QUERY_H
+#define APOL_POLICY_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Many libapol queries act upon MLS contexts. Use these defines to
+ * specify set operations upon contexts.
+ */
+#define APOL_QUERY_SUB 0x02 /**< The range specified by the query is a subset of the target range */
+#define APOL_QUERY_SUPER 0x04 /**< The range specified by the query is a superset of the target range */
+#define APOL_QUERY_EXACT (APOL_QUERY_SUB|APOL_QUERY_SUPER) /**< The range specified by the query matches the target range exactly. */
+#define APOL_QUERY_INTERSECT 0x08 /* query overlaps any part of rule range */
+#define APOL_QUERY_FLAGS \
+ (APOL_QUERY_SUB | APOL_QUERY_SUPER | APOL_QUERY_EXACT | \
+ APOL_QUERY_INTERSECT)
+
+/* The AV rule search and TE rule search use these flags when
+ * specifying what kind of symbol is being searched. Strings are
+ * normally interpreted either as a type or as an attribute; the behavior
+ * can be changed to use only types or only attributes.
+ */
+#define APOL_QUERY_SYMBOL_IS_TYPE 0x01
+#define APOL_QUERY_SYMBOL_IS_ATTRIBUTE 0x02
+
+#include <qpol/policy.h>
+
+#include "type-query.h"
+#include "class-perm-query.h"
+#include "role-query.h"
+#include "user-query.h"
+#include "bool-query.h"
+#include "isid-query.h"
+#include "mls-query.h"
+#include "netcon-query.h"
+#include "fscon-query.h"
+#include "context-query.h"
+#include "permissive-query.h"
+#include "polcap-query.h"
+
+#include "avrule-query.h"
+#include "terule-query.h"
+#include "condrule-query.h"
+#include "rbacrule-query.h"
+#include "range_trans-query.h"
+#include "constraint-query.h"
+
+#include "domain-trans-analysis.h"
+#include "infoflow-analysis.h"
+#include "relabel-analysis.h"
+#include "types-relation-analysis.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/policy.h b/libapol/include/apol/policy.h
new file mode 100644
index 0000000..7b26af8
--- /dev/null
+++ b/libapol/include/apol/policy.h
@@ -0,0 +1,166 @@
+/**
+ * @file
+ *
+ * Public interface for SELinux policies. This function declares
+ * apol_policy, a structure that groups a qpol_policy with other
+ * structures needed by libapol. Almost all setools files will need
+ * to #include this header.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_POLICY_H
+#define APOL_POLICY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy-path.h"
+#include <stdarg.h>
+#include <qpol/policy.h>
+
+ typedef struct apol_policy apol_policy_t;
+
+ typedef void (*apol_callback_fn_t) (void *varg, const apol_policy_t * p, int level, const char *fmt, va_list argp);
+
+/**
+ * When creating an apol_policy, load all components except rules
+ * (both AV and TE rules). For modular policies, this affects both
+ * the base policy and subsequent modules.
+ * @deprecated use QPOL_POLICY_OPTION_NO_RULES instead
+ */
+#define APOL_POLICY_OPTION_NO_RULES QPOL_POLICY_OPTION_NO_RULES
+
+/**
+ * Create a new apol_policy initialized from one or more policy files.
+ *
+ * @param path Policy path object specifying which policy file or
+ * files to load.
+ * @param options Bitfield specifying options for the returned policy.
+ * Valid options are QPOL_POLICY_OPTION_* from <qpol/policy.h>.
+ * @param msg_callback Callback to invoke as errors/warnings are
+ * generated. If NULL, then write messages to standard error.
+ * @param varg Value to be passed as the first parameter to the
+ * callback function.
+ *
+ * @return A newly allocated policy that may be used for analysis, or
+ * NULL upon error. The caller is responsible for calling
+ * apol_policy_destroy() upon the returned value afterwards.
+ */
+ extern apol_policy_t *apol_policy_create_from_policy_path(const apol_policy_path_t * path, const int options,
+ apol_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Deallocate all memory associated with a policy, including all
+ * auxillary data structures, and then set it to NULL. Does nothing
+ * if the pointer is already NULL.
+ *
+ * @param policy Policy to destroy, if not already NULL.
+ */
+ extern void apol_policy_destroy(apol_policy_t ** policy);
+
+/**
+ * Given a policy, return the policy type. This will be one of
+ * QPOL_POLICY_KERNEL_SOURCE, QPOL_POLICY_KERNEL_BINARY, or
+ * QPOL_POLICY_MODULE_BINARY. (You will need to #include
+ * <qpol/policy.h> to get these definitions.)
+ *
+ * @param policy Policy to which check.
+ *
+ * @return The policy type, or < 0 upon error.
+ */
+ extern int apol_policy_get_policy_type(const apol_policy_t * policy);
+
+/**
+ * Given a policy, return a pointer to the underlying qpol_policy.
+ * This is needed, for example, to access details of particulary qpol
+ * components.
+ *
+ * @param policy Policy containing qpol policy.
+ *
+ * @return Pointer to underlying qpol policy, or NULL on error. Do
+ * not free() or otherwise destroy this pointer.
+ */
+ extern qpol_policy_t *apol_policy_get_qpol(const apol_policy_t * policy);
+
+/**
+ * Given a policy, return 1 if the policy within is MLS, 0 if not. If
+ * it cannot be determined or upon error, return < 0.
+ *
+ * @param p Policy to which check.
+ * @return 1 if policy is MLS, 0 if not, < 0 upon error.
+ */
+ extern int apol_policy_is_mls(const apol_policy_t * p);
+
+/**
+ * Given a policy, allocate and return a string that describes the
+ * policy (policy version, source/binary, mls/non-mls).
+ *
+ * @param p Policy to check.
+ * @return String that describes policy, or NULL upon error. The
+ * caller must free() this afterwards.
+ */
+ extern char *apol_policy_get_version_type_mls_str(const apol_policy_t * p);
+
+#define APOL_MSG_ERR 1
+#define APOL_MSG_WARN 2
+#define APOL_MSG_INFO 3
+
+/**
+ * Write a message to the callback stored within an apol error
+ * handler. If the msg_callback field is empty, then the default
+ * message callback will be used.
+ *
+ * @param p Error reporting handler. If NULL then write message to
+ * stderr.
+ * @param level Severity of message, one of APOL_MSG_ERR,
+ * APOL_MSG_WARN, or APOL_MSG_INFO.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+ extern void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt, ...);
+
+ __attribute__ ((format(printf, 3, 4))) extern void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt,
+ ...);
+
+/**
+ * Invoke a apol_policy_t's callback for an error, passing it a format
+ * string and arguments.
+ */
+#define ERR(p, format, ...) apol_handle_msg(p, APOL_MSG_ERR, format, __VA_ARGS__)
+
+/**
+ * Invoke a apol_policy_t's callback for a warning, passing it a
+ * format string and arguments.
+ */
+#define WARN(p, format, ...) apol_handle_msg(p, APOL_MSG_WARN, format, __VA_ARGS__)
+
+/**
+ * Invoke a apol_policy_t's callback for an informational messag,
+ * passing it a format string and arguments.
+ */
+#define INFO(p, format, ...) apol_handle_msg(p, APOL_MSG_INFO, format, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/range_trans-query.h b/libapol/include/apol/range_trans-query.h
new file mode 100644
index 0000000..e3113c5
--- /dev/null
+++ b/libapol/include/apol/range_trans-query.h
@@ -0,0 +1,198 @@
+/**
+ * @file
+ *
+ * Routines to query range transition rules of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_RANGE_TRANS_QUERY_H
+#define APOL_RANGE_TRANS_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "mls_range.h"
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_range_trans_query apol_range_trans_query_t;
+
+/**
+ * Execute a query against all range transition rules within the
+ * policy.
+ *
+ * @param p Policy within which to look up terules.
+ * @param r Structure containing parameters for query. If this is
+ * NULL then return all range transitions.
+ * @param v Reference to a vector of qpol_range_trans_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon
+ * error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_range_trans_get_by_query(const apol_policy_t * p, const apol_range_trans_query_t * r, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new range trans query structure. All fields
+ * are initialized, such that running this blank query results in
+ * returning all range transitions within the policy. The caller must
+ * call apol_range_trans_query_destroy() upon the return value
+ * afterwards.
+ *
+ * @return An initialized range trans structure, or NULL upon error.
+ */
+ extern apol_range_trans_query_t *apol_range_trans_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced range trans
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param r Reference to a range trans query structure to destroy.
+ */
+ extern void apol_range_trans_query_destroy(apol_range_trans_query_t ** r);
+
+/**
+ * Set a range trans query to return rules whose source symbol matches
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Range trans rule query to set.
+ * @param symbol Limit query to rules with this symbol as their
+ * source, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_range_trans_query_set_source(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set a range trans query to return rules whose target symbol matches
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Range trans query to set.
+ * @param symbol Limit query to rules with this symbol as their
+ * target, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_range_trans_query_set_target(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set a range trans query to return rules whose object class matches
+ * symbol. If more than one class are appended to the query, the
+ * rule's class must be one of those appended. (I.e., the rule's
+ * class must be a member of the query's classes.) Pass a NULL to
+ * clear all classes. Note that this performs straight string
+ * comparison, ignoring the regex flag.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Range trans query to set.
+ * @param obj_class Name of object class to add to search set, or NULL
+ * to clear all classes.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_range_trans_query_append_class(const apol_policy_t * p, apol_range_trans_query_t * r,
+ const char *obj_class);
+
+/**
+ * Set a range trans query to return only rules matching a MLS range.
+ * This function takes ownership of the range, such that the caller
+ * must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Range trans query to set.
+ * @param range Limit query to only rules matching this range, or NULL
+ * to unset this field.
+ * @param range_match Specifies how to match a rules to a range. This
+ * must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if range is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_range_trans_query_set_range(const apol_policy_t * p,
+ apol_range_trans_query_t * r, apol_mls_range_t * range,
+ unsigned int range_match);
+
+/**
+ * Set a range trans query to treat the source symbol as any. That
+ * is, use the same symbol for either source or target of a rule.
+ * This flag does nothing if the source symbol is not set.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Range trans rule query to set.
+ * @param is_any Non-zero to use source symbol for any field, 0 to
+ * keep source as only source.
+ *
+ * @return Always 0.
+ */
+ extern int apol_range_trans_query_set_source_any(const apol_policy_t * p, apol_range_trans_query_t * r, int is_any);
+
+/**
+ * Set a range trans query to use regular expression searching for
+ * source and target types/attributes. Strings will be treated as
+ * regexes instead of literals. Matching will occur against the type
+ * name or any of its aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Range trans rule query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_range_trans_query_set_regex(const apol_policy_t * p, apol_range_trans_query_t * t, int is_regex);
+
+/**
+ * Render a range transition to a string.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param rule The rule to render.
+ *
+ * @return a newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+ */
+ extern char *apol_range_trans_render(const apol_policy_t * policy, const qpol_range_trans_t * rule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/rbacrule-query.h b/libapol/include/apol/rbacrule-query.h
new file mode 100644
index 0000000..61822ee
--- /dev/null
+++ b/libapol/include/apol/rbacrule-query.h
@@ -0,0 +1,279 @@
+/**
+ * @file
+ *
+ * Routines to query (role) allow and role_transition rules of a
+ * policy. This does not include access vector's allow rules, which
+ * are found in avrule-query.h.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_RBACRULE_QUERY_H
+#define APOL_RBACRULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_role_allow_query apol_role_allow_query_t;
+ typedef struct apol_role_trans_query apol_role_trans_query_t;
+
+/******************** (role) allow queries ********************/
+
+/**
+ * Execute a query against all (role) allow rules within the policy.
+ *
+ * @param p Policy within which to look up allow rules.
+ * @param r Structure containing parameters for query. If this is
+ * NULL then return all allow rules.
+ * @param v Reference to a vector of qpol_role_allow_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_role_allow_get_by_query(const apol_policy_t * p, const apol_role_allow_query_t * r, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new role allow query structure. All fields
+ * are initialized, such that running this blank query results in
+ * returning all (role) allows within the policy. The caller must
+ * call apol_role_allow_query_destroy() upon the return value
+ * afterwards.
+ *
+ * @return An initialized role allow query structure, or NULL upon
+ * error.
+ */
+ extern apol_role_allow_query_t *apol_role_allow_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced role allow
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param r Reference to a role allow query structure to destroy.
+ */
+ extern void apol_role_allow_query_destroy(apol_role_allow_query_t ** r);
+
+/**
+ * Set a role allow query to return rules with a particular source
+ * role.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role allow query to set.
+ * @param role Limit query to rules with this role as their source, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_allow_query_set_source(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role);
+
+/**
+ * Set a role allow query to return rules with a particular target
+ * role. This field is ignored if
+ * apol_role_allow_query_set_source_any() is set to non-zero.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role allow query to set.
+ * @param role Limit query to rules with this role as their target, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_allow_query_set_target(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role);
+
+/**
+ * Set a role allow query to treat the source role as any. That is,
+ * use the same symbol for either source or target of a (role) allow
+ * rule. This flag does nothing if the source role is not set.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role allow query to set.
+ * @param is_any Non-zero to use source symbol for any field, 0 to
+ * keep source as only source.
+ *
+ * @return Always 0.
+ */
+ extern int apol_role_allow_query_set_source_any(const apol_policy_t * p, apol_role_allow_query_t * r, int is_any);
+
+/**
+ * Set a role allow query to use regular expression searching for
+ * source and target fields. Strings will be treated as regexes
+ * instead of literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role allow query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_role_allow_query_set_regex(const apol_policy_t * p, apol_role_allow_query_t * r, int is_regex);
+
+/**
+ * Render a role allow rule to a string.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param rule The rule to render.
+ *
+ * @return a newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+ */
+ extern char *apol_role_allow_render(const apol_policy_t * policy, const qpol_role_allow_t * rule);
+
+/******************** role_transition queries ********************/
+
+/**
+ * Execute a query against all role_transition rules within the
+ * policy.
+ *
+ * @param p Policy within which to look up role_transition rules.
+ * @param r Structure containing parameters for query. If this is
+ * NULL then return all role_transition rules.
+ * @param v Reference to a vector of qpol_role_trans_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_role_trans_get_by_query(const apol_policy_t * p, const apol_role_trans_query_t * r, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new role trans query structure. All fields
+ * are initialized, such that running this blank query results in
+ * returning all role_transitions within the policy. The caller must
+ * call apol_role_trans_query_destroy() upon the return value
+ * afterwards.
+ *
+ * @return An initialized role trans query structure, or NULL upon
+ * error.
+ */
+ extern apol_role_trans_query_t *apol_role_trans_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced role trans
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param r Reference to a role trans query structure to destroy.
+ */
+ extern void apol_role_trans_query_destroy(apol_role_trans_query_t ** r);
+
+/**
+ * Set a role trans query to return rules with a particular source
+ * role.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role trans query to set.
+ * @param role Limit query to rules with this role as their source, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_trans_query_set_source(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role);
+
+/**
+ * Set a role trans query to return rules with a particular target
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role trans query to set.
+ * @param symbol Limit query to rules with this type or attribute as
+ * their target, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_trans_query_set_target(const apol_policy_t * p, apol_role_trans_query_t * r, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set a role trans query to return rules with a particular default
+ * role. This field is ignored if
+ * apol_role_trans_query_set_source_any() is set to non-zero.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role trans query to set.
+ * @param role Limit query to rules with this role as their default, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_trans_query_set_default(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role);
+
+/**
+ * Set a role trans query to treat the source role as any. That is,
+ * use the same symbol for either source or default of a
+ * role_transition rule. This flag does nothing if the source role is
+ * not set. Note that a role_transition's target is a type, so thus
+ * this flag does not affect its searching.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role trans query to set.
+ * @param is_any Non-zero to use source symbol for source or default
+ * field, 0 to keep source as only source.
+ *
+ * @return Always 0.
+ */
+ extern int apol_role_trans_query_set_source_any(const apol_policy_t * p, apol_role_trans_query_t * r, int is_any);
+
+/**
+ * Set a role trans query to use regular expression searching for
+ * source, target, and default fields. Strings will be treated as
+ * regexes instead of literals. For the target type, matching will
+ * occur against the type name or any of its aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role trans query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_role_trans_query_set_regex(const apol_policy_t * p, apol_role_trans_query_t * r, int is_regex);
+
+/**
+ * Render a role_transition rule to a string.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param rule The rule to render.
+ *
+ * @return A newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+ */
+ extern char *apol_role_trans_render(const apol_policy_t * policy, const qpol_role_trans_t * rule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/relabel-analysis.h b/libapol/include/apol/relabel-analysis.h
new file mode 100644
index 0000000..1aed049
--- /dev/null
+++ b/libapol/include/apol/relabel-analysis.h
@@ -0,0 +1,258 @@
+/**
+ * @file
+ *
+ * Routines to perform a direct relabelling analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_RELABEL_ANALYSIS_H
+#define APOL_RELABEL_ANALYSIS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+/* defines for direction flag */
+#define APOL_RELABEL_DIR_TO 0x01
+#define APOL_RELABEL_DIR_FROM 0x02
+#define APOL_RELABEL_DIR_BOTH (APOL_RELABEL_DIR_TO|APOL_RELABEL_DIR_FROM)
+#define APOL_RELABEL_DIR_SUBJECT 0x04
+
+ typedef struct apol_relabel_analysis apol_relabel_analysis_t;
+ typedef struct apol_relabel_result apol_relabel_result_t;
+ typedef struct apol_relabel_result_pair apol_relabel_result_pair_t;
+
+/******************** functions to do relabel analysis ********************/
+
+/**
+ * Execute a relabel analysis against a particular policy.
+ *
+ * @param p Policy within which to look up allow rules.
+ * @param r A non-NULL structure containing parameters for analysis.
+ * @param v Reference to a vector of apol_relabel_result_t. The
+ * vector will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_relabel_analysis_do(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new relabel analysis structure. All fields
+ * are cleared; one must fill in the details of the analysis before
+ * running it. The caller must call apol_relabel_analysis_destroy()
+ * upon the return value afterwards.
+ *
+ * @return An initialized relabel analysis structure, or NULL upon
+ * error.
+ */
+ extern apol_relabel_analysis_t *apol_relabel_analysis_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced relabel
+ * analysis, and then set it to NULL. This function does nothing if
+ * the analysis is already NULL.
+ *
+ * @param r Reference to a relabel analysis structure to destroy.
+ */
+ extern void apol_relabel_analysis_destroy(apol_relabel_analysis_t ** r);
+
+/**
+ * Set a relabel analysis to search in a specific direction. This
+ * function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Relabel analysis to set.
+ * @param dir Direction to analyze, one of the APOL_RELABEL_DIR_TO,
+ * APOL_RELABEL_DIR_FROM, APOL_RELABEL_DIR_BOTH, or
+ * APOL_RELABEL_DIR_SUBJECT.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_relabel_analysis_set_dir(const apol_policy_t * p, apol_relabel_analysis_t * r, unsigned int dir);
+
+/**
+ * Set a relabel analysis to begin searching using a given type. This
+ * function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Relabel anlysis to set.
+ * @param name Begin searching types with this non-NULL name.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_relabel_analysis_set_type(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *name);
+
+/**
+ * Set a relabel analysis to return rules with this object
+ * (non-common) class. If more than one class is appended to the
+ * query, the rule's class must be one of those appended. (I.e., the
+ * rule's class must be a member of the analysis's classes.) Pass a
+ * NULL to clear all classes.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Relabel analysis to set.
+ * @param class Name of object class to add to search set, or NULL to
+ * clear all classes.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_relabel_analysis_append_class(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *obj_class);
+
+/**
+ * Set a relabel analysis to return rules with this subject as their
+ * source type. If more than one subject is appended to the query,
+ * the rule's source must be one of those appended. (I.e., the rule's
+ * source must be a member of the analysis's subject.) Pass a NULL to
+ * clear all types. Note that these subjects are ignored when doing
+ * subject relabel analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Relabel analysis to set.
+ * @param subject Name of type to add to search set, or NULL to clear
+ * all subjects.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_relabel_analysis_append_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *subject);
+
+/**
+ * Set a relabel analysis to return only types matching a regular
+ * expression. Note that the regexp will also match types' aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Relabel anlysis to set.
+ * @param result Only return types matching this regular expression, or
+ * NULL to return all types
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_relabel_analysis_set_result_regex(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *result);
+
+/******************** functions to access relabel results ********************/
+
+/**
+ * Return the relabelto vector embedded within an apol_relabel_result
+ * node. This is a vector of apol_relabel_result_pair_t objects. The
+ * caller shall not call apol_vector_destroy() upon this pointer.
+ *
+ * @param r Relabel result node.
+ *
+ * @return Pointer to a vector of rule pairs, relative to the policy
+ * originally used to generate the relabelling result.
+ */
+ extern const apol_vector_t *apol_relabel_result_get_to(const apol_relabel_result_t * r);
+
+/**
+ * Return the relabelfrom vector embedded within an
+ * apol_relabel_result node. This is a vector of
+ * apol_relabel_result_pair_t objects. The caller shall not call
+ * apol_vector_destroy() upon this pointer.
+ *
+ * @param r Relabel result node.
+ *
+ * @return Pointer to a vector of rule pairs, relative to the policy
+ * originally used to generate the relabelling result.
+ */
+ extern const apol_vector_t *apol_relabel_result_get_from(const apol_relabel_result_t * r);
+
+/**
+ * Return the relabelboth vector embedded within an
+ * apol_relabel_result node. This is a vector of
+ * apol_relabel_result_pair_t objects. The caller shall not call
+ * apol_vector_destroy() upon this pointer.
+ *
+ * @param r Relabel result node.
+ *
+ * @return Pointer to a vector of rule pairs, relative to the policy
+ * originally used to generate the relabelling result.
+ */
+ extern const apol_vector_t *apol_relabel_result_get_both(const apol_relabel_result_t * r);
+
+/**
+ * Return the resulting type for an apol_relabel_result node.
+ *
+ * @param r Relabel result node.
+ *
+ * @return Pointer to a result type.
+ */
+ extern const qpol_type_t *apol_relabel_result_get_result_type(const apol_relabel_result_t * r);
+
+/**
+ * Return the first rule from an apol_relabel_result_pair object.
+ *
+ * For object mode analysis, this is the rule that affects the
+ * starting type. Either that type or one of its attributes will be
+ * the target type for the returned rule.
+ *
+ * For subject mode analysis, this is a rule affects the starting
+ * subject. Either that subject or one of its attributes will be the
+ * source type for the returned rule.
+ *
+ * @param p Relabel result pair object.
+ *
+ * @return Rule affecting the starting type/subject.
+ */
+ extern const qpol_avrule_t *apol_relabel_result_pair_get_ruleA(const apol_relabel_result_pair_t * p);
+
+/**
+ * Return the other rule from an apol_relabel_result_pair object.
+ *
+ * For object mode analysis, this is the rule that affects the
+ * resulting type. Either that type or one of its attributes will be
+ * the target type for the returned rule.
+ *
+ * For subject mode analysis, the returned pointer will be NULL.
+ *
+ * @param p Relabel result pair object.
+ *
+ * @return Rule affecting the resulting type/subject (for object mode)
+ * or NULL (for subject mode).
+ */
+ extern const qpol_avrule_t *apol_relabel_result_pair_get_ruleB(const apol_relabel_result_pair_t * p);
+
+/**
+ * Return the intermediate type for an apol_relabel_result_pair
+ * object.
+ *
+ * For object mode analysis, this is the source type for the first
+ * rule; it also will be the source type for the other rule.
+ *
+ * For subject mode analysis, the returned pointer will be NULL.
+ *
+ * @param p Relabel result pair object.
+ *
+ * @return Intermediate type for relabel result (for object mode) or
+ * NULL (for subject mode).
+ */
+ extern const qpol_type_t *apol_relabel_result_pair_get_intermediate_type(const apol_relabel_result_pair_t * p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/render.h b/libapol/include/apol/render.h
new file mode 100644
index 0000000..0580813
--- /dev/null
+++ b/libapol/include/apol/render.h
@@ -0,0 +1,82 @@
+/**
+ * @file
+ *
+ * Public interfaces that renders things that are not already covered
+ * by one of the query files. Unless otherwise stated, all functions
+ * return a newly allocated string, which the caller is responsible
+ * for free()ing afterwards.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_RENDER_H
+#define APOL_RENDER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "mls-query.h"
+#include <qpol/policy.h>
+#include <stdlib.h>
+
+/**
+ * Given an IPv4 address (or mask) in qpol byte order, allocate and
+ * return a string representing that address.
+ *
+ * @param p Reference to a policy, for reporting errors
+ * @param addr Address (or mask) to render.
+ *
+ * @return A newly allocated string, which the caller must free.
+ * Returns NULL on error.
+ */
+ extern char *apol_ipv4_addr_render(const apol_policy_t * p, uint32_t addr[4]);
+
+/**
+ * Given an IPv6 address (or mask) in qpol byte order, allocate and
+ * return a string representing that address.
+ *
+ * @param p Reference to a policy, for reporting errors
+ * @param addr Address (or mask) to render.
+ *
+ * @return A newly allocated string, which the caller must free.
+ * Returns NULL on error.
+ */
+ extern char *apol_ipv6_addr_render(const apol_policy_t * p, uint32_t addr[4]);
+
+/**
+ * Creates a string containing the textual representation of
+ * a security context.
+ * @param p Reference to a policy.
+ * @param context Reference to the security context to be rendered.
+ *
+ * @return A newly allocated string on success, caller must free;
+ * NULL on error.
+ */
+ extern char *apol_qpol_context_render(const apol_policy_t * p, const qpol_context_t * context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/role-query.h b/libapol/include/apol/role-query.h
new file mode 100644
index 0000000..ad80490
--- /dev/null
+++ b/libapol/include/apol/role-query.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * Public Interface for querying roles of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_ROLE_QUERY_H
+#define APOL_ROLE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_role_query apol_role_query_t;
+
+/******************** role queries ********************/
+
+/**
+ * Execute a query against all roles within the policy.
+ *
+ * @param p Policy within which to look up roles.
+ * @param r Structure containing parameters for query. If this is
+ * NULL then return all roles.
+ * @param v Reference to a vector of qpol_role_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_role_get_by_query(const apol_policy_t * p, apol_role_query_t * r, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new role query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all roles within the policy. The caller must call
+ * apol_role_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized role query structure, or NULL upon error.
+ */
+ extern apol_role_query_t *apol_role_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced role query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param r Reference to a role query structure to destroy.
+ */
+ extern void apol_role_query_destroy(apol_role_query_t ** r);
+
+/**
+ * Set a role query to return only roles that match this name. This
+ * function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role query to set.
+ * @param name Limit query to only roles with this name, or NULL to
+ * unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_query_set_role(const apol_policy_t * p, apol_role_query_t * r, const char *name);
+
+/**
+ * Set a role query to return only roles containing this type or one
+ * of its aliases. This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role query to set.
+ * @param name Limit query to only roles with this type, or NULL to
+ * unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_role_query_set_type(const apol_policy_t * p, apol_role_query_t * r, const char *name);
+
+/**
+ * Set a role query to use regular expression searching for all of its
+ * fields. Strings will be treated as regexes instead of literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param r Role query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_role_query_set_regex(const apol_policy_t * p, apol_role_query_t * r, int is_regex);
+
+/**
+ * See if the role passed in includes the type that is the
+ * second parameter.
+ * @param p Policy handler, to report errors.
+ * @param r Role to check if type is included in it.
+ * @param t Type that is checked against all types that are in role
+ * @return 1 if the type is included in the role, 0 if it's not, < 0 on error
+*/
+ extern int apol_role_has_type(const apol_policy_t * p, const qpol_role_t * r, const qpol_type_t * t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_ROLE_QUERY_H */
diff --git a/libapol/include/apol/terule-query.h b/libapol/include/apol/terule-query.h
new file mode 100644
index 0000000..4b1e474
--- /dev/null
+++ b/libapol/include/apol/terule-query.h
@@ -0,0 +1,321 @@
+/**
+ * @file
+ *
+ * Routines to query type enforcement rules of a policy. These are
+ * type_transition, type_member, and type_change rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_TERULE_QUERY_H
+#define APOL_TERULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_terule_query apol_terule_query_t;
+
+/**
+ * Execute a query against all type enforcement rules within the policy.
+ *
+ * @param p Policy within which to look up terules.
+ * @param t Structure containing parameters for query. If this is
+ * NULL then return all terules.
+ * @param v Reference to a vector of qpol_terule_t. The vector will
+ * be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v);
+
+/**
+ * Execute a query against all syntactic type enforcement rules within
+ * the policy. If the policy has line numbers, then the returned list
+ * will be sorted increasingly by line number.
+ *
+ * @param p Policy within which to look up terules. <b>Must be a
+ * source policy.</b>
+ * @param t Structure containing parameters for query. If this is
+ * NULL then return all terules.
+ * @param v Reference to a vector of qpol_syn_terule_t. The vector
+ * will be allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_syn_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new terule query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all terules within the policy. The caller must call
+ * apol_terule_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized terule query structure, or NULL upon error.
+ */
+ extern apol_terule_query_t *apol_terule_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced terule query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param t Reference to a terule query structure to destroy.
+ */
+ extern void apol_terule_query_destroy(apol_terule_query_t ** t);
+
+/**
+ * Set a terule query to search only certain type enforcement rules
+ * within the policy. This is a bitmap; use the constants in
+ * libqpol/terule_query.h (QPOL_RULE_TYPE_TRANS, etc.) to give the
+ * rule selections.
+ *
+ * @param p Policy handler, to report errors.
+ * @param te TE rule query to set.
+ * @param rules Bitmap to indicate which rules to search, or 0 to
+ * search all rules.
+ *
+ * @return Always 0.
+ */
+ extern int apol_terule_query_set_rules(const apol_policy_t * p, apol_terule_query_t * t, unsigned int rules);
+
+/**
+ * Set a terule query to return rules whose source symbol matches
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param symbol Limit query to rules with this symbol as their
+ * source, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_set_source(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set an terule query to return rules whose source symbol is matched as a type
+ * or an attribute. The symbol will match both types and attributes by default.
+ * @see apol_avrule_query_set_source() to set the symbol to match.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE
+ * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component
+ * to match.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_set_source_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component);
+
+/**
+ * Set a terule query to return rules whose target symbol matches
+ * symbol. Symbol may be a type or attribute; if it is an alias then
+ * the query will convert it to its primary prior to searching. If
+ * is_indirect is non-zero then the search will be done indirectly.
+ * If the symbol is a type, then the query matches rules with one of
+ * the type's attributes. If the symbol is an attribute, then it
+ * matches rule with any of the attribute's types.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param symbol Limit query to rules with this symbol as their
+ * target, or NULL to unset this field.
+ * @param is_indirect If non-zero, perform indirect matching.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_set_target(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol,
+ int is_indirect);
+
+/**
+ * Set an terule query to return rules whose target symbol is matched as a type
+ * or an attribute. The symbol will match both types and attributes by default.
+ * @see apol_avrule_query_set_source() to set the symbol to match.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param component Bit-wise or'ed set of APOL_QUERY_SYMBOL_IS_TYPE
+ * and APOL_QUERY_SYMBOL_IS_ATTRIBUTE indicating the type of component
+ * to match.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_set_target_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component);
+
+/**
+ * Set a terule query to return rules with this default type. The
+ * symbol may be a type or any of its aliases; it may not be an
+ * attribute.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param type Name of default type to search.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_set_default(const apol_policy_t * p, apol_terule_query_t * t, const char *type);
+
+/**
+ * Set at terule query to return rules with this object (non-common)
+ * class. If more than one class are appended to the query, the
+ * rule's class must be one of those appended. (I.e., the rule's
+ * class must be a member of the query's classes.) Pass a NULL to
+ * clear all classes. Note that this performs straight string
+ * comparison, ignoring the regex flag.
+
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param obj_class Name of object class to add to search set.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_append_class(const apol_policy_t * p, apol_terule_query_t * t, const char *obj_class);
+
+/**
+ * Set a terule query to return rules that are in conditionals and
+ * whose conditional uses a particular boolean variable.
+ * Unconditional rules will not be returned.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param bool_name Name of boolean that conditional must contain. If
+ * NULL then search all rules.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_terule_query_set_bool(const apol_policy_t * p, apol_terule_query_t * t, const char *bool_name);
+
+/**
+ * Set a terule query to search only enabled rules within the policy.
+ * These include rules that are unconditional and those within enabled
+ * conditionals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param is_enabled Non-zero to search only enabled rules, 0 to
+ * search all rules.
+ *
+ * @return Always 0.
+ */
+ extern int apol_terule_query_set_enabled(const apol_policy_t * p, apol_terule_query_t * t, int is_enabled);
+
+/**
+ * Set a terule query to treat the source symbol as any. That is, use
+ * the same symbol for either source, target, or default of a rule.
+ * This flag does nothing if the source symbol is not set.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param is_any Non-zero to use source symbol for any field, 0 to
+ * keep source as only source.
+ *
+ * @return Always 0.
+ */
+ extern int apol_terule_query_set_source_any(const apol_policy_t * p, apol_terule_query_t * t, int is_any);
+
+/**
+ * Set a terule query to use regular expression searching for source
+ * and target types/attributes and default type. Strings will be
+ * treated as regexes instead of literals. Matching will occur against
+ * the type name or any of its aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t TE rule query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_terule_query_set_regex(const apol_policy_t * p, apol_terule_query_t * t, int is_regex);
+
+/**
+ * Given a single terule, return a newly allocated vector of
+ * qpol_syn_terule_t pointers (relative to the given policy) which
+ * comprise that rule. The vector will be sorted by line numbers if
+ * the policy has line numbers.
+ *
+ * @param p Policy from which to obtain syntactic rules.
+ * @param rule TE rule to convert.
+ *
+ * @return A newly allocated vector of syn_terule_t pointers. The
+ * caller is responsible for calling apol_vector_destroy() afterwards.
+ */
+ extern apol_vector_t *apol_terule_to_syn_terules(const apol_policy_t * p, const qpol_terule_t * rule);
+
+/**
+ * Given a vector of terules (qpol_terule_t pointers), return a newly
+ * allocated vector of qpol_syn_terule_t pointers (relative to the
+ * given policy) which comprise all of those rules. The returned
+ * vector will be sorted by line numbers if the policy has line
+ * numbers. Also, it will not have any duplicate syntactic rules.
+ *
+ * @param p Policy from which to obtain syntactic rules.
+ * @param rules Vector of TE rules to convert.
+ *
+ * @return A newly allocated vector of syn_terule_t pointers. The
+ * caller is responsible for calling apol_vector_destroy() afterwards.
+ */
+ extern apol_vector_t *apol_terule_list_to_syn_terules(const apol_policy_t * p, const apol_vector_t * rules);
+
+/**
+ * Render a terule to a string.
+ *
+ * @param policy Policy handler, to report errors.
+ * @param rule The rule to render.
+ *
+ * @return a newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+ */
+ extern char *apol_terule_render(const apol_policy_t * policy, const qpol_terule_t * rule);
+
+/**
+ * Render a syntactic terule to a string.
+ *
+ * @param policy Policy handler to report errors.
+ * @param rule The rule to render.
+ *
+ * @return a newly malloc()'d string representation of the rule, or NULL on
+ * failure; if the call fails, errno will be set. The caller is responsible
+ * for calling free() on the returned string.
+*/
+ extern char *apol_syn_terule_render(const apol_policy_t * policy, const qpol_syn_terule_t * rule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/type-query.h b/libapol/include/apol/type-query.h
new file mode 100644
index 0000000..a571b19
--- /dev/null
+++ b/libapol/include/apol/type-query.h
@@ -0,0 +1,172 @@
+/**
+ * @file
+ *
+ * Routines to query types and attributes of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_TYPE_QUERY_H
+#define APOL_TYPE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_type_query apol_type_query_t;
+ typedef struct apol_attr_query apol_attr_query_t;
+
+/******************** type queries ********************/
+
+/**
+ * Execute a query against all types within the policy. The results
+ * will only contain types, not aliases nor attributes.
+ *
+ * @param p Policy within which to look up types.
+ * @param t Structure containing parameters for query. If this is
+ * NULL then return all types.
+ * @param v Reference to a vector of qpol_type_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_type_get_by_query(const apol_policy_t * p, apol_type_query_t * t, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new type query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all types within the policy. The caller must call
+ * apol_type_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized type query structure, or NULL upon error.
+ */
+ extern apol_type_query_t *apol_type_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced type query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param t Reference to a type query structure to destroy.
+ */
+ extern void apol_type_query_destroy(apol_type_query_t ** t);
+
+/**
+ * Set a type query to return only types that match this name. The
+ * name may be either a type or one of its aliases. This function
+ * duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t Type query to set.
+ * @param name Limit query to only types or aliases with this name, or
+ * NULL to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_type_query_set_type(const apol_policy_t * p, apol_type_query_t * t, const char *name);
+
+/**
+ * Set a type query to use regular expression searching for all of its
+ * fields. Strings will be treated as regexes instead of literals.
+ * Matching will occur against the type name or any of its aliases.
+ *
+ * @param p Policy handler, to report errors.
+ * @param t Type query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_type_query_set_regex(const apol_policy_t * p, apol_type_query_t * t, int is_regex);
+
+/******************** attribute queries ********************/
+
+/**
+ * Execute a query against all attributes within the policy. The
+ * results will only contain attributes, not types nor aliases.
+ *
+ * @param p Policy within which to look up attributes.
+ * @param a Structure containing parameters for query. If this is
+ * NULL then return all attributes.
+ * @param v Reference to a vector of qpol_type_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_attr_get_by_query(const apol_policy_t * p, apol_attr_query_t * a, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new attribute query structure. All fields
+ * are initialized, such that running this blank query results in
+ * returning all attributes within the policy. The caller must call
+ * apol_attr_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized attribute query structure, or NULL upon error.
+ */
+ extern apol_attr_query_t *apol_attr_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced attribute
+ * query, and then set it to NULL. This function does nothing if the
+ * query is already NULL.
+ *
+ * @param a Reference to an attribute query structure to destroy.
+ */
+ extern void apol_attr_query_destroy(apol_attr_query_t ** a);
+
+/**
+ * Set an attribute query to return only attributes that match this
+ * name. This function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a Attribute query to set.
+ * @param name Limit query to only attributes with this name, or NULL
+ * to unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_attr_query_set_attr(const apol_policy_t * p, apol_attr_query_t * a, const char *name);
+
+/**
+ * Set an attribute query to use regular expression searching for all
+ * of its fields. Strings will be treated as regexes instead of
+ * literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param a Attribute query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_attr_query_set_regex(const apol_policy_t * p, apol_attr_query_t * a, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/types-relation-analysis.h b/libapol/include/apol/types-relation-analysis.h
new file mode 100644
index 0000000..1278072
--- /dev/null
+++ b/libapol/include/apol/types-relation-analysis.h
@@ -0,0 +1,380 @@
+/**
+ * @file
+ *
+ * Routines to perform a two-types relationship analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_TYPES_RELATION_ANALYSIS_H
+#define APOL_TYPES_RELATION_ANALYSIS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include <qpol/policy.h>
+
+/* Specify a which types relationship analysis/analyses to run using
+ * these bit values.
+ */
+#define APOL_TYPES_RELATION_COMMON_ATTRIBS 0x0001
+#define APOL_TYPES_RELATION_COMMON_ROLES 0x0002
+#define APOL_TYPES_RELATION_COMMON_USERS 0x0004
+#define APOL_TYPES_RELATION_SIMILAR_ACCESS 0x0010
+#define APOL_TYPES_RELATION_DISSIMILAR_ACCESS 0x0020
+#define APOL_TYPES_RELATION_ALLOW_RULES 0x0100
+#define APOL_TYPES_RELATION_TYPE_RULES 0x0200
+#define APOL_TYPES_RELATION_DOMAIN_TRANS_AB 0x0400
+#define APOL_TYPES_RELATION_DOMAIN_TRANS_BA 0x0800
+#define APOL_TYPES_RELATION_DIRECT_FLOW 0x1000
+#define APOL_TYPES_RELATION_TRANS_FLOW_AB 0x4000
+#define APOL_TYPES_RELATION_TRANS_FLOW_BA 0x8000
+
+ typedef struct apol_types_relation_analysis apol_types_relation_analysis_t;
+ typedef struct apol_types_relation_result apol_types_relation_result_t;
+ typedef struct apol_types_relation_access apol_types_relation_access_t;
+
+/********** functions to do types relation analysis **********/
+
+/**
+ * Execute a two types relationship analysis against a particular
+ * policy.
+ *
+ * @param p Policy within which to look up relationships.
+ * @param tr A non-NULL structure containing parameters for analysis.
+ * @param r Reference to a apol_types_relation_result_t. The object
+ * will be allocated by this function. The caller must call
+ * apol_types_relation_result_destroy() afterwards. This will be set
+ * to NULL upon no results or upon error.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_types_relation_analysis_do(apol_policy_t * p,
+ const apol_types_relation_analysis_t * tr, apol_types_relation_result_t ** r);
+
+/**
+ * Allocate and return a new two types relationship analysis
+ * structure. All fields are cleared; one must fill in the details of
+ * the analysis before running it. The caller must call
+ * apol_types_relation_analysis_destroy() upon the return value
+ * afterwards.
+ *
+ * @return An initialized two types relationship analysis structure, or
+ * NULL upon error.
+ */
+ extern apol_types_relation_analysis_t *apol_types_relation_analysis_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced types relation
+ * analysis, and then set it to NULL. This function does nothing if
+ * the analysis is already NULL.
+ *
+ * @param tr Reference to a types relation analysis structure to
+ * destroy.
+ */
+ extern void apol_types_relation_analysis_destroy(apol_types_relation_analysis_t ** tr);
+
+/**
+ * Set a types relation analysis to begin analysis from this first
+ * type. This function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param tr Types relation analysis to set.
+ * @param name Perform analysis with this non-NULL name.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_types_relation_analysis_set_first_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr,
+ const char *name);
+
+/**
+ * Set a types relation analysis to begin analysis from this other
+ * type. This function must be called prior to running the analysis.
+ *
+ * @param p Policy handler, to report errors.
+ * @param tr Types relation analysis to set.
+ * @param name Perform analysis with this other non-NULL name.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_types_relation_analysis_set_other_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr,
+ const char *name);
+
+/**
+ * Set a types relation analysis to run the specified
+ * analysis/analyses. This is a bitmap; use the defines
+ * APOL_TYPES_RELATION_COMMON_ATTRIBUTES etc. to specify which one(s)
+ * to run.
+ *
+ * @param p Policy handler, to report errors.
+ * @param tr Types relation analysis to set.
+ * @param analyses Bitmap to indicate which analyses to run, or 0 to
+ * run them all.
+ *
+ * @return Always 0.
+ */
+ extern int apol_types_relation_analysis_set_analyses(const apol_policy_t * p, apol_types_relation_analysis_t * tr,
+ unsigned int analyses);
+
+/*************** functions to access types relation results ***************/
+
+/**
+ * Deallocate all memory associated with a types relation analysis
+ * result, including the pointer itself. This function does nothing
+ * if the result is already NULL.
+ *
+ * @param result Reference to a types relation result structure to
+ * destroy.
+ */
+ extern void apol_types_relation_result_destroy(apol_types_relation_result_t ** result);
+
+/**
+ * Return the vector of attributes common to the two types. This is a
+ * vector of qpol_type_t pointers. The caller <b>should not</b> call
+ * apol_vector_destroy() upon the returned vector. If the user did
+ * not request this analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get common
+ * attributes.
+ *
+ * @return Vector of common attributes, or NULL if analysis was not
+ * run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_attributes(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of roles common to the two types. This is a
+ * vector of qpol_role_t pointers. The caller <b>should not</b> call
+ * apol_vector_destroy() upon the returned vector. If the user did
+ * not request this analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get common roles.
+ *
+ * @return Vector of common roles, or NULL if analysis was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_roles(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of users common to the two types. This is a
+ * vector of qpol_user_t pointers. The caller <b>should not</b> call
+ * apol_vector_destroy() upon the returned vector. If the user did
+ * not request this analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get common users.
+ *
+ * @return Vector of common users, or NULL if analysis was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_users(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of accesses similar to the two types. This is a
+ * vector of apol_types_relation_access_t pointers. The vector will
+ * contain only the rules that the first type had. Call
+ * apol_types_relation_result_get_similar_other() to get the
+ * complementary vector (i.e., both vectors will have the same types).
+ * The caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get similar accesses.
+ *
+ * @return Vector of similar accesses, or NULL if analysis was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_similar_first(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of accesses similar to the two types. This is a
+ * vector of apol_types_relation_access_t pointers. The vector will
+ * contain only the rules that the other type had. Call
+ * apol_types_relation_result_get_similar_first() to get the
+ * complementary vector (i.e., both vectors will have the same types).
+ * The caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get similar accesses.
+ *
+ * @return Vector of similar accesses, or NULL if analysis was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_similar_other(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of accesses dissimilar for the first type (i.e.,
+ * types that the first type reaches that the other type does not).
+ * This is a vector of apol_types_relation_access_t pointers. The
+ * caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get dissimilar
+ * accesses.
+ *
+ * @return Vector of dissimilar accesses, or NULL if analysis was not
+ * run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_dissimilar_first(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of accesses dissimilar for the other type (i.e.,
+ * types that the other type reaches that the first type does not).
+ * This is a vector of apol_types_relation_access_t pointers. The
+ * caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get dissimilar
+ * accesses.
+ *
+ * @return Vector of dissimilar accesses, or NULL if analysis was not
+ * run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_dissimilar_other(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of allow rules involving both types (allow one
+ * type to the other). This is a vector of qpol_avrule_t pointers.
+ * The caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get rules.
+ *
+ * @return Vector of allow rules, or NULL if analysis was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_allowrules(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of type transition / type change rules involving
+ * both types. This is a vector of qpol_terule_t pointers. The
+ * caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get rules.
+ *
+ * @return Vector of type enforcement rules, or NULL if analysis was
+ * not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_typerules(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of apol_infoflow_result_t pointers corresponding
+ * to a direct information flow analysis between both types. The
+ * caller <b>should not</b> call apol_vector_destroy() upon the
+ * returned vector. If the user did not request this analysis then
+ * the return value will be NULL.
+ *
+ * @param result Types relation result from which to get information
+ * flows.
+ *
+ * @return Vector of infoflow results, or NULL if analysis was not
+ * run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_directflows(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of apol_infoflow_result_t pointers corresponding
+ * to a transitive information flow analysis between the first type to
+ * the other. The caller <b>should not</b> call apol_vector_destroy()
+ * upon the returned vector. If the user did not request this
+ * analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get information
+ * flows.
+ *
+ * @return Vector of infoflow results, or NULL if analysis was not
+ * run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_transflowsAB(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of apol_infoflow_result_t pointers corresponding
+ * to a transitive information flow analysis between the other type to
+ * the first. The caller <b>should not</b> call apol_vector_destroy()
+ * upon the returned vector. If the user did not request this
+ * analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get information
+ * flows.
+ *
+ * @return Vector of infoflow results, or NULL if analysis was not
+ * run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_transflowsBA(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of apol_domain_trans_result_t pointers
+ * corresponding to a domain transition analysis between the first
+ * type to the other. The caller <b>should not</b> call
+ * apol_vector_destroy() upon the returned vector. If the user did
+ * not request this analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get domain
+ * transitions.
+ *
+ * @return Vector of domain transition results, or NULL if analysis
+ * was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_domainsAB(const apol_types_relation_result_t * result);
+
+/**
+ * Return the vector of apol_domain_trans_result_t pointers
+ * corresponding to a domain transition analysis between the other
+ * type to the first. The caller <b>should not</b> call
+ * apol_vector_destroy() upon the returned vector. If the user did
+ * not request this analysis then the return value will be NULL.
+ *
+ * @param result Types relation result from which to get domain
+ * transitions.
+ *
+ * @return Vector of domain transition results, or NULL if analysis
+ * was not run.
+ */
+ extern const apol_vector_t *apol_types_relation_result_get_domainsBA(const apol_types_relation_result_t * result);
+
+/**
+ * Given a types relation access node, return the type stored within.
+ *
+ * @param a Types relation access node.
+ *
+ * @return Pointer to the type stored within.
+ */
+ extern const qpol_type_t *apol_types_relation_access_get_type(const apol_types_relation_access_t * a);
+
+/**
+ * Given a types relation access node, return the vector of
+ * qpol_avrule_t pointers stored within.
+ *
+ * @param a Types relation access node.
+ *
+ * @return Pointer to the vector of rules. The caller <b>must not</b>
+ * destroy this vector.
+ */
+ extern const apol_vector_t *apol_types_relation_access_get_rules(const apol_types_relation_access_t * a);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/user-query.h b/libapol/include/apol/user-query.h
new file mode 100644
index 0000000..9038bf4
--- /dev/null
+++ b/libapol/include/apol/user-query.h
@@ -0,0 +1,150 @@
+/**
+ * @file
+ * Public Interface for querying users of a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_USER_QUERY_H
+#define APOL_USER_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "policy.h"
+#include "vector.h"
+#include "mls-query.h"
+#include <qpol/policy.h>
+
+ typedef struct apol_user_query apol_user_query_t;
+
+/******************** user queries ********************/
+
+/**
+ * Execute a query against all users within the policy.
+ *
+ * @param p Policy within which to look up users.
+ * @param u Structure containing parameters for query. If this is
+ * NULL then return all users.
+ * @param v Reference to a vector of qpol_user_t. The vector will be
+ * allocated by this function. The caller must call
+ * apol_vector_destroy() afterwards. This will be set to NULL upon no
+ * results or upon error.
+ *
+ * @return 0 on success (including none found), negative on error.
+ */
+ extern int apol_user_get_by_query(const apol_policy_t * p, apol_user_query_t * u, apol_vector_t ** v);
+
+/**
+ * Allocate and return a new user query structure. All fields are
+ * initialized, such that running this blank query results in
+ * returning all users within the policy. The caller must call
+ * apol_user_query_destroy() upon the return value afterwards.
+ *
+ * @return An initialized user query structure, or NULL upon error.
+ */
+ extern apol_user_query_t *apol_user_query_create(void);
+
+/**
+ * Deallocate all memory associated with the referenced user query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ *
+ * @param u Reference to a user query structure to destroy.
+ */
+ extern void apol_user_query_destroy(apol_user_query_t ** u);
+
+/**
+ * Set a user query to return only users that match this name. This
+ * function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param u User query to set.
+ * @param name Limit query to only users this name, or NULL to unset
+ * this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_user_query_set_user(const apol_policy_t * p, apol_user_query_t * u, const char *name);
+
+/**
+ * Set a user query to return only users containing this role. This
+ * function duplicates the incoming name.
+ *
+ * @param p Policy handler, to report errors.
+ * @param u User query to set.
+ * @param role Limit query to only users with this role, or NULL to
+ * unset this field.
+ *
+ * @return 0 on success, negative on error.
+ */
+ extern int apol_user_query_set_role(const apol_policy_t * p, apol_user_query_t * u, const char *role);
+
+/**
+ * Set a user query to return only users containing this default
+ * level. This function takes ownership of the level, such that the
+ * caller must not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param u User query to which set.
+ * @param level Limit query to only users with this level as their
+ * default, or NULL to unset this field.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_user_query_set_default_level(const apol_policy_t * p, apol_user_query_t * u, apol_mls_level_t * level);
+
+/**
+ * Set a user query to return only users matching a MLS range. This
+ * function takes ownership of the range, such that the caller must
+ * not modify nor destroy it afterwards.
+ *
+ * @param p Policy handler, to report errors.
+ * @param u User query to set.
+ * @param range Limit query to only users matching this range, or NULL
+ * to unset this field.
+ * @param range_match Specifies how to match a user to a range. This
+ * must be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. This parameter is ignored if range is NULL.
+ *
+ * @return Always returns 0.
+ */
+ extern int apol_user_query_set_range(const apol_policy_t * p, apol_user_query_t * u, apol_mls_range_t * range,
+ unsigned int range_match);
+
+/**
+ * Set a user query to use regular expression searching for all of its
+ * fields. Strings will be treated as regexes instead of literals.
+ *
+ * @param p Policy handler, to report errors.
+ * @param u User query to set.
+ * @param is_regex Non-zero to enable regex searching, 0 to disable.
+ *
+ * @return Always 0.
+ */
+ extern int apol_user_query_set_regex(const apol_policy_t * p, apol_user_query_t * u, int is_regex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_USER_QUERY_H */
diff --git a/libapol/include/apol/util.h b/libapol/include/apol/util.h
new file mode 100644
index 0000000..99db168
--- /dev/null
+++ b/libapol/include/apol/util.h
@@ -0,0 +1,336 @@
+/**
+ * @file
+ *
+ * Miscellaneous, uncategorized functions for libapol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2001-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_UTIL_H
+#define APOL_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "vector.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libapol_get_version(void);
+
+/**
+ * Given a portcon protocol, return a read-only string that describes
+ * that protocol.
+ *
+ * @param protocol Portcon protocol, one of IPPROTO_TCP or IPPROTO_UDP
+ * from netinet/in.h.
+ *
+ * @return A string that describes the protocol, or NULL if the
+ * protocol is invalid. <b>Do not free() this string.</b>
+ */
+ extern const char *apol_protocol_to_str(uint8_t protocol);
+
+/**
+ * Given the name of a portcon protocol, return its numeric value.
+ *
+ * @param protocol_str Portcon protocol, one of "tcp", "TCP", "udp", or "UDP".
+ *
+ * @return Numeric value for the protocol, one of IPPROTO_TCP or IPPROTO_UDP
+ * from netinet/in.h. Upon error return 0.
+ */
+ extern uint8_t apol_str_to_protocol(const char *protocol_str);
+
+/**
+ * Given a string representing and IP value (mask or address, IPv4 or
+ * IPv6), write to an array that value in the same bit order that
+ * qpol uses. If the IP was in IPv4 format, only write to the first
+ * element and zero the remainder.
+ *
+ * @param str A string representing and IP value, either in IPv4 or
+ * IPv6 format.
+ * @param ip Array to which write converted value.
+ *
+ * @return QPOL_IPV4 if the string is in IPv4 format, QPOL_IPV6 if
+ * in IPv6, < 0 on error.
+ */
+ extern int apol_str_to_internal_ip(const char *str, uint32_t ip[4]);
+
+/**
+ * Given a genfscon object class, return a read-only string that
+ * describes that class.
+ *
+ * @param objclass Object class, one of QPOL_CLASS_BLK_FILE,
+ * QPOL_CLASS_CHR_FILE, etc.
+ *
+ * @return A string that describes the object class, or NULL if the
+ * object class is invalid. <b>Do not free() this string.</b>
+ *
+ * @see <qpol/genfscon_query.h> for a list of valid object classes.
+ */
+ extern const char *apol_objclass_to_str(uint32_t objclass);
+
+/**
+ * Given a string representing a genfscon object class, return its
+ * numeric identifier. Valid strings may be obtained by calling
+ * apol_objclass_to_str().
+ *
+ * @param objclass Object class, one of "any", "file", etc.
+ *
+ * @return Numeric identifier for object class, or 0 if unknown.
+ *
+ * @see <qpol/genfscon_query.h> for a list of valid object classes.
+ */
+ extern uint32_t apol_str_to_objclass(const char *objclass);
+
+/**
+ * Given a fs_use behavior type, return a read-only string that
+ * describes that fs_use behavior.
+ *
+ * @param behavior A fs_use behavior, one of QPOL_FS_USE_PSID,
+ * QPOL_FS_USE_XATTR, etc.
+ *
+ * @return A string that describes the behavior, or NULL if the
+ * behavior is invalid. <b>Do not free() this string.</b>
+ */
+ extern const char *apol_fs_use_behavior_to_str(uint32_t behavior);
+
+/**
+ * Given a fs_use behavior string, return its numeric value.
+ *
+ * @param behavior A fs_use behavior, one of "fs_use_psid",
+ * "fs_use_xattr", etc.
+ *
+ * @return A numeric representation for the behavior, one of
+ * QPOL_FS_USE_PSID, QPOL_FS_USE_XATTR, etc, or < 0 if the string is
+ * invalid.
+ */
+ extern int apol_str_to_fs_use_behavior(const char *behavior);
+
+/**
+ * Given a rule type, return a read-only string that describes that
+ * rule.
+ *
+ * @param rule_type A policy rule type, one of QPOL_RULE_ALLOW,
+ * QPOL_RULE_TYPE_CHANGE, etc.
+ *
+ * @return A string that describes the rule, or NULL if the rule_type
+ * is invalid. <b>Do not free() this string.</b>
+ */
+ extern const char *apol_rule_type_to_str(uint32_t rule_type);
+
+/**
+ * Given a conditional expression type, return a read-only string that
+ * describes that operator.
+ *
+ * @param expr_type An expression type, one of QPOL_COND_EXPR_BOOL,
+ * QPOL_COND_EXPR_NOT, etc.
+ *
+ * @return A string that describes the expression, or NULL if the
+ * expr_type is invalid. <b>Do not free() this string.</b>
+ */
+ extern const char *apol_cond_expr_type_to_str(uint32_t expr_type);
+
+/**
+ * Given a file name, search and return that file's path on the
+ * running system. First search the present working directory, then
+ * the directory at APOL_INSTALL_DIR (an environment variable), then
+ * apol's install dir.
+ *
+ * @param file_name File to find.
+ *
+ * @return File's path, or NULL if not found. Caller must free() this
+ * string afterwards.
+ */
+ extern char *apol_file_find(const char *file_name);
+
+/**
+ * Given a file name, search and return that file's full path
+ * (directory + file name) on the running system. First search the
+ * present working directory, then the directory at APOL_INSTALL_DIR
+ * (an environment variable), then apol's install dir.
+ *
+ * @param file_name File to find.
+ *
+ * @return File's path + file name, or NULL if not found. Caller must
+ * free() this string afterwards.
+ */
+ extern char *apol_file_find_path(const char *file_name);
+
+/**
+ * Given a file name for a user configuration, search and return that
+ * file's path + file name in the user's home directory.
+ *
+ * @param file_name File to find.
+ *
+ * @return File's path + file name, or NULL if not found. Caller must
+ * free() this string afterwards.
+ */
+ extern char *apol_file_find_user_config(const char *file_name);
+
+/**
+ * Given a file name, read the file's contents into a newly allocated
+ * buffer. The caller must free() this buffer afterwards.
+ *
+ * @param fname Name of file to read.
+ * @param buf Reference to a newly allocated buffer.
+ * @param len Reference to the number of bytes read.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int apol_file_read_to_buffer(const char *fname, char **buf, size_t * len);
+/**
+ * Given a file pointer into a config file, read and return the value
+ * for the given config var. The caller must free() the returned
+ * string afterwards.
+ *
+ * @param var Name of configuration variable to obtain.
+ * @param fp An open file pointer into a configuration file. This
+ * function will not maintain the pointer's current location.
+ *
+ * @return A newly allocated string containing the variable's value,
+ * or NULL if not found or error.
+ */
+ extern char *apol_config_get_var(const char *var, FILE * fp);
+
+/**
+ * Given a string of tokens, allocate and return a vector of strings
+ * initialized to those tokens.
+ *
+ * @param s String to split.
+ * @param delim Delimiter for tokens, as per strsep(3).
+ *
+ * @return A newly allocated vector of strings containing the
+ * variable's values, or NULL if not found or error. Note that the
+ * vector could be empty if the config var does not exist or has an
+ * empty value. The caller must call apol_vector_destroy()
+ * afterwards.
+ */
+ extern apol_vector_t *apol_str_split(const char *s, const char *delim);
+
+/**
+ * Given a vector of strings, allocate and return a string that joins
+ * the vector using the given separator. The caller is responsible
+ * for free()ing the string afterwards.
+ *
+ * @param list Vector of strings to join.
+ * @param delim Delimiter character(s) for the concatenated string.
+ *
+ * @return An allocated concatenated string, or NULL upon error. If
+ * the list is empty then return an empty string. The caller is
+ * responsible for calling free() upon the return value.
+ */
+ extern char *apol_str_join(const apol_vector_t * list, const char *delim);
+
+/**
+ * Given a mutable string, modify the string by removing both starting
+ * and trailing whitespace characters.
+ *
+ * @param str String to modify.
+ */
+ extern void apol_str_trim(char *str);
+
+/**
+ * Append a string to an existing dynamic mutable string, expanding
+ * the target string if necessary. The caller must free() the target
+ * string. If tgt is NULL then initially allocate the resulting
+ * string.
+ *
+ * @param tgt Reference to a string to modify, or NULL to create a new
+ * string.
+ * @param tgt_sz Pointer to number of bytes currently allocated to
+ * tgt. This will be updated with the new string size. If *tgt is
+ * NULL then this existing value is ignored. (It will still be updated
+ * afterwards).
+ * @param str String to append.
+ *
+ * @return 0 on success. On error, return < 0 and set errno; tgt will be
+ * free()d and set to NULL, tgt_sz will be set to 0.
+ */
+ extern int apol_str_append(char **tgt, size_t * tgt_sz, const char *str);
+
+/**
+ * Append a string to an existing dynamic mutable string, expanding
+ * the target string if necessary. The string to append is computed
+ * using the format string, as per printf(3). The caller must free()
+ * the target string. If tgt is NULL then initially allocate the
+ * resulting string.
+ *
+ * @param tgt Reference to a string to modify, or NULL to create a new
+ * string.
+ * @param tgt_sz Pointer to number of bytes currently allocated to
+ * tgt. This will be updated with the new string size. If *tgt is
+ * NULL then the existing value is ignored. (It will still be updated
+ * afterwards).
+ * @param fmt Format for the string with which append, as per
+ * printf(3).
+ *
+ * @return 0 on success. On error, return < 0 and set errno; tgt will be
+ * free()d and set to NULL, tgt_sz will be set to 0.
+ */
+ extern int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...);
+
+/* declaration duplicated below to satisfy doxygen */
+ extern int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...) __attribute__ ((format(printf, 3, 4)));
+
+/**
+ * Test whether a given string is only white space.
+ *
+ * @param str String to test.
+ * @return 1 if string is either NULL or only whitespace, 0 otherwise.
+ */
+ extern int apol_str_is_only_white_space(const char *str);
+
+/**
+ * Wrapper around strcmp for use in vector and BST comparison functions.
+ *
+ * @param a String to compare.
+ * @param b The other string to compare.
+ * @param unused Not used. (exists to match expected function signature)
+ *
+ * @return Less than, equal to, or greater than 0 if string a is found
+ * to be less than, identical to, or greater than string b
+ * respectively.
+ */
+ extern int apol_str_strcmp(const void *a, const void *b, void *unused __attribute__ ((unused)));
+
+/**
+ * Wrapper around strdup for use in vector and BST cloning functions.
+ *
+ * @param elem String to duplicate.
+ * @param unused Not used. (exists to match expected function signature)
+ *
+ * @return A new string that is a duplicate of elem, or NULL upon error.
+ */
+ extern void *apol_str_strdup(const void *elem, void *unused __attribute__ ((unused)));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/include/apol/vector.h b/libapol/include/apol/vector.h
new file mode 100644
index 0000000..f690bf0
--- /dev/null
+++ b/libapol/include/apol/vector.h
@@ -0,0 +1,335 @@
+/**
+ * @file
+ * Contains the API for a generic vector. Note that vector functions
+ * are not thread-safe.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_VECTOR_H
+#define APOL_VECTOR_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+#include <qpol/iterator.h>
+
+ typedef struct apol_vector apol_vector_t;
+
+ typedef int (apol_vector_comp_func) (const void *a, const void *b, void *data);
+ typedef void (apol_vector_free_func) (void *elem);
+ typedef void *(apol_vector_dup_func) (const void *elem, void *data);
+
+/**
+ * Allocate and initialize an empty vector with default
+ * capacity.
+ *
+ * @param fr Function to call when destroying the vector. Each
+ * element of the vector will be passed into this function; it should
+ * free the memory used by that element. If this parameter is NULL,
+ * the elements will not be freed.
+ *
+ * @return A pointer to a newly created vector on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_vector_destroy() to free memory used.
+ */
+ extern apol_vector_t *apol_vector_create(apol_vector_free_func * fr);
+
+/**
+ * Allocate and initialize an empty vector with starting capacity of
+ * cap.
+ *
+ * @param cap The starting capacity to allocate for the internal
+ * array.
+ * @param fr Function to call when destroying the vector. Each
+ * element of the vector will be passed into this function; it should
+ * free the memory used by that element. If this parameter is NULL,
+ * the elements will not be freed.
+ *
+ * @return A pointer to a newly created vector on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_vector_destroy() to free memory used.
+ */
+ extern apol_vector_t *apol_vector_create_with_capacity(size_t cap, apol_vector_free_func * fr);
+
+/**
+ * Allocate and return a vector that has been initialized with the
+ * contents of a qpol iterator. <b>This function merely makes a
+ * shallow copy of the iterator's contents</b>; any memory ownership
+ * restrictions imposed by the iterator apply to this vector as well.
+ * Also note that this function begins copying from the iterator's
+ * current position, leaving the iterator at its end position
+ * afterwards.
+ *
+ * @param iter qpol iterator from which to obtain vector's contents.
+ * @param fr Function to call when destroying the vector. Each
+ * element of the vector will be passed into this function; it should
+ * free the memory used by that element. If this parameter is NULL,
+ * the elements will not be freed.
+ *
+ * @return A pointer to a newly created vector on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_vector_destroy() to free memory used.
+ */
+ extern apol_vector_t *apol_vector_create_from_iter(qpol_iterator_t * iter, apol_vector_free_func * fr);
+
+/**
+ * Allocate and return a vector that has been initialized with the
+ * contents of another vector.
+ *
+ * @param v Vector from which to copy.
+ * @param dup If NULL, then make a shallow copy of the original
+ * vector's contents. Otherwise this function will be called upon
+ * for each element from the original vector; the return value will
+ * be the value stored in the new vector.
+ * @param data Arbitrary data to pass as dup's second parameter.
+ * @param fr Function to call when destroying the new vector. Each
+ * element of the vector will be passed into this function; it should
+ * free the memory used by that element. If this parameter is NULL,
+ * the elements will not be freed.
+ *
+ * @return A pointer to a newly created vector on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_vector_destroy() to free memory used.
+ */
+ extern apol_vector_t *apol_vector_create_from_vector(const apol_vector_t * v, apol_vector_dup_func * dup, void *data,
+ apol_vector_free_func * fr);
+
+/**
+ * Allocate and return a vector that has been initialized with the
+ * contents common to two other vectors. <b>This function merely
+ * makes a shallow copy of the vectors' contents</b>; any memory
+ * ownership restrictions imposed by the original vectors apply to
+ * this new vector as well. Note that if a source vector contains
+ * duplicate elements the returned vector may (or may not) have
+ * duplicates as well. If the caller does not want duplicate entries
+ * then apol_vector_sort_uniquify() should be called afterwards.
+ *
+ * @param v1 First vector from which to compute the intersection.
+ * @param v2 Other vector to compute intersection.
+ * @param cmp A comparison call back for the type of element stored
+ * in the vector. The expected return value from this function is
+ * less than, equal to, or greater than 0 if the first argument is
+ * less than, equal to, or greater than the second respectively. If
+ * this is NULL then do pointer address comparison.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater.
+ *
+ * @return A pointer to a newly created vector on success and NULL on
+ * failure. If the call fails, errno will be set. The caller is
+ * responsible for calling apol_vector_destroy() to free memory used.
+ */
+ extern apol_vector_t *apol_vector_create_from_intersection(const apol_vector_t * v1,
+ const apol_vector_t * v2, apol_vector_comp_func * cmp,
+ void *data);
+
+/**
+ * Free a vector and any memory used by it. This will recursively
+ * invoke the free function that was stored within the vector when it
+ * was created.
+ *
+ * @param v Pointer to the vector to free. The pointer will be set
+ * to NULL afterwards. If already NULL then this function does
+ * nothing.
+ */
+ extern void apol_vector_destroy(apol_vector_t ** v);
+
+/**
+ * Get the number of elements in the vector.
+ *
+ * @param v The vector from which to get the number of elements.
+ * Must be non-NULL.
+ *
+ * @return The number of elements in the vector; if v is NULL,
+ * returns 0.
+ */
+ extern size_t apol_vector_get_size(const apol_vector_t * v);
+
+/**
+ * Get the current capacity of the vector.
+ *
+ * @param v The vector from which to get the current capacity. Must
+ * be non-NULL.
+ *
+ * @return The capacity of the vector; this value will be greater or
+ * equal to the number of elements in the vector. If v is NULL,
+ * returns 0.
+ */
+ extern size_t apol_vector_get_capacity(const apol_vector_t * v);
+
+/**
+ * Get the element at the requested index.
+ *
+ * @param v The vector from which to get the element.
+ * @param idx The index of the desired element.
+ *
+ * @return A pointer to the element requested. If v is NULL or idx is
+ * out of range, returns NULL and sets errno.
+ */
+ extern void *apol_vector_get_element(const apol_vector_t * v, size_t idx);
+
+/**
+ * Find an element within a vector, returning its index within the vector.
+ *
+ * @param v The vector from which to get the element.
+ * @param elem The element to find.
+ * @param cmp A comparison call back for the type of element stored
+ * in the vector. The first parameter will be an existing element
+ * from the vector; next will be elem and then data. The expected
+ * return value from this function is less than, equal to, or greater
+ * than 0 if the first argument is less than, equal to, or greater
+ * than the second respectively. For use in this function the return
+ * value is only checked for 0 or non-zero return. If this is NULL
+ * then do pointer address comparison.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater.
+ * @param i Index into vector where element was found. This value is
+ * undefined if the element was not found.
+ *
+ * @return 0 if element was found, or < 0 if not found.
+ */
+ extern int apol_vector_get_index(const apol_vector_t * v, const void *elem, apol_vector_comp_func * cmp, void *data,
+ size_t * i);
+
+/**
+ * Add an element to the end of a vector.
+ *
+ * @param v The vector to which to add the element.
+ * @param elem The element to add. Once added the element will be
+ * the last element in the vector.
+ *
+ * @return 0 on success and < 0 on failure. If the call fails, errno
+ * will be set and v will be unchanged.
+ */
+ extern int apol_vector_append(apol_vector_t * v, void *elem);
+
+/**
+ * Add an element to the end of a vector unless that element is equal
+ * to an existing element.
+ *
+ * @param v The vector to which to add the element.
+ * @param elem The element to add; must be non-NULL.
+ * @param cmp A comparison call back for the type of element stored
+ * in the vector. The expected return value from this function is
+ * less than, equal to, or greater than 0 if the first argument is
+ * less than, equal to, or greater than the second respectively. For
+ * use in this function the return value is only checked for 0 or
+ * non-zero return. If this is NULL then do pointer address
+ * comparison.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater.
+ *
+ * @return 0 on success, < 0 on failure, and > 0 if the element
+ * already exists in the vector. If the call fails or the element
+ * already exists errno will be set.
+ */
+ extern int apol_vector_append_unique(apol_vector_t * v, void *elem, apol_vector_comp_func * cmp, void *data);
+
+/**
+ * Concatenate two vectors. Appends all elements of src to dest.
+ * <b>NOTE: No type checking is done for elements in the two
+ * vectors.</b> Elements are not deep copies.
+ * @param dest Vector to which to append elements.
+ * @param src Vector containing elements to append.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and dest's contents will be reverted.
+ */
+ extern int apol_vector_cat(apol_vector_t * dest, const apol_vector_t * src);
+
+/**
+ * Remove an element from a vector, and renumber all subsequent
+ * elements. <b>This does not free memory that was used by the
+ * removed element</b>; the caller is responsible for doing that.
+ *
+ * @param v Vector containing element.
+ * @param idx Index to the element to remove.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and v's contents will be reverted.
+ */
+ extern int apol_vector_remove(apol_vector_t * v, const size_t idx);
+
+/**
+ * Compare two vectors, determining if one is different than the
+ * other. This uses a callback to compare elements across the
+ * vectors.
+ *
+ * @param a First vector to compare.
+ * @param b Second vector to compare.
+ * @param cmp A comparison call back for the type of element stored
+ * in the vector. The expected return value from this function is
+ * less than, equal to, or greater than 0 if the first argument is
+ * less than, equal to, or greater than the second respectively. If
+ * this is NULL then do pointer address comparison.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater.
+ * @param i Reference to where to store the index of the first
+ * detected difference. The value is undefined if vectors are
+ * equivalent (return value of 0). Note that the index may be
+ * greater than a vector's size if the vectors are of unequal
+ * lengths.
+ *
+ * @return < 0 if vector A is less than B, > 0 if A is greater than
+ * B, or 0 if equivalent.
+ */
+ extern int apol_vector_compare(const apol_vector_t * a, const apol_vector_t * b, apol_vector_comp_func * cmp, void *data,
+ size_t * i);
+
+/**
+ * Sort the vector's elements within place, using an unstable sorting
+ * algorithm.
+ *
+ * @param v The vector to sort.
+ * @param cmp A comparison call back for the type of element stored
+ * in the vector. The expected return value from this function is
+ * less than, equal to, or greater than 0 if the first argument is
+ * less than, equal to, or greater than the second respectively. If
+ * this is NULL then treat the vector's contents as unsigned integers
+ * and sort in increasing order.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater.
+ */
+ extern void apol_vector_sort(apol_vector_t * v, apol_vector_comp_func * cmp, void *data);
+
+/**
+ * Sort the vector's elements within place (see apol_vector_sort()),
+ * and then compact vector by removing duplicate entries. The
+ * vector's free function will be used to free the memory used by
+ * non-unique elements.
+ *
+ * @param v The vector to sort.
+ * @param cmp A comparison call back for the type of element stored
+ * in the vector. The expected return value from this function is
+ * less than, equal to, or greater than 0 if the first argument is
+ * less than, equal to, or greater than the second respectively. If
+ * this is NULL then treat the vector's contents as unsigned integers
+ * and sort in increasing order.
+ * @param data Arbitrary data to pass as the comparison function's
+ * third paramater.
+ */
+ extern void apol_vector_sort_uniquify(apol_vector_t * v, apol_vector_comp_func * cmp, void *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APOL_VECTOR_H */
diff --git a/libapol/src/Makefile.am b/libapol/src/Makefile.am
new file mode 100644
index 0000000..3fa4f06
--- /dev/null
+++ b/libapol/src/Makefile.am
@@ -0,0 +1,75 @@
+lib_LIBRARIES = libapol.a
+setoolsdir = @setoolsdir@
+
+apolso_DATA = libapol.so.@libapol_version@
+apolsodir = $(libdir)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -fpic -I$(srcdir)/../include \
+ -DAPOL_INSTALL_DIR='"${setoolsdir}"' \
+ -DLIBAPOL_POLICY_INSTALL_DIR='"@selinux_policy_dir@/policy"' \
+ -DLIBAPOL_DEFAULT_POLICY='"@selinux_default_policy@"'
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libapol_a_SOURCES = \
+ avrule-query.c \
+ bool-query.c \
+ bst.c \
+ class-perm-query.c \
+ condrule-query.c \
+ constraint-query.c \
+ context-query.c \
+ domain-trans-analysis.c domain-trans-analysis-internal.h \
+ fscon-query.c \
+ infoflow-analysis.c infoflow-analysis-internal.h \
+ isid-query.c \
+ mls-query.c \
+ mls_level.c \
+ mls_range.c \
+ netcon-query.c \
+ perm-map.c \
+ permissive-query.c \
+ polcap-query.c \
+ policy.c \
+ policy-path.c \
+ policy-query.c \
+ queue.c \
+ range_trans-query.c \
+ rbacrule-query.c \
+ relabel-analysis.c \
+ render.c \
+ role-query.c \
+ terule-query.c \
+ type-query.c \
+ types-relation-analysis.c \
+ user-query.c \
+ util.c \
+ vector.c vector-internal.h \
+ policy-query-internal.h queue.h
+
+libapol_a_DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so
+
+libapol_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libapol_a_SOURCES)))
+LIBAPOL_SONAME = @libapol_soname@
+
+dist_noinst_DATA = libapol.map
+
+$(apolso_DATA): $(libapol_so_OBJS) libapol.map
+ $(CC) -shared -o $@ $(libapol_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBAPOL_SONAME),--version-script=$(srcdir)/libapol.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so
+ $(LN_S) -f $@ @libapol_soname@
+ $(LN_S) -f $@ libapol.so
+
+libapol.so: $(apolso_DATA)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+install-data-hook:
+ cd $(DESTDIR)$(apolsodir) && $(LN_S) -f $(apolso_DATA) @libapol_soname@
+ cd $(DESTDIR)$(apolsodir) && $(LN_S) -f $(apolso_DATA) libapol.so
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libapol.so @libapol_soname@ $(apolso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(apolsodir)/$(apolso_DATA) $(DESTDIR)$(apolsodir)/@libapol_soname@ $(DESTDIR)$(apolsodir)/libapol.so
diff --git a/libapol/src/avrule-query.c b/libapol/src/avrule-query.c
new file mode 100644
index 0000000..c409e30
--- /dev/null
+++ b/libapol/src/avrule-query.c
@@ -0,0 +1,1209 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about access vector
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <apol/bst.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <string.h>
+
+struct apol_avrule_query
+{
+ char *source, *target, *bool_name;
+ apol_vector_t *classes, *perms;
+ unsigned int rules;
+ unsigned int flags;
+};
+
+/**
+ * Common semantic rule selection routine used in get*rule_by_query.
+ * @param p Policy to search.
+ * @param v Vector of rules to populate (of type qpol_avrule_t).
+ * @param rule_type Mask of rules to search.
+ * @param flags Query options as specified by the apol_avrule_query.
+ * @param source_list If non-NULL, list of types to use as source.
+ * If NULL, accept all types.
+ * @param target_list If non-NULL, list of types to use as target.
+ * If NULL, accept all types.
+ * @param class_list If non-NULL, list of classes to use.
+ * If NULL, accept all classes.
+ * @param perm_list If non-NULL, list of permisions to use.
+ * If NULL, accept all permissions.
+ * @param bool_name If non-NULL, find conditional rules affected by this boolean.
+ * If NULL, all rules will be considered (including unconditional rules).
+ * @return 0 on success and < 0 on failure.
+ */
+static int rule_select(const apol_policy_t * p, apol_vector_t * v, uint32_t rule_type, unsigned int flags,
+ const apol_vector_t * source_list, const apol_vector_t * target_list, const apol_vector_t * class_list,
+ const apol_vector_t * perm_list, const char *bool_name)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ const int only_enabled = flags & APOL_QUERY_ONLY_ENABLED;
+ const int is_regex = flags & APOL_QUERY_REGEX;
+ const int source_as_any = flags & APOL_QUERY_SOURCE_AS_ANY;
+ size_t num_perms_to_match = 1;
+ int retv = -1;
+ regex_t *bool_regex = NULL;
+
+ if ((flags & APOL_QUERY_MATCH_ALL_PERMS) && perm_list != NULL) {
+ num_perms_to_match = apol_vector_get_size(perm_list);
+ }
+ if (qpol_policy_get_avrule_iter(p->p, rule_type, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_avrule_t *rule;
+ uint32_t is_enabled;
+ const qpol_cond_t *cond = NULL;
+ int match_source = 0, match_target = 0, match_bool = 0;
+ size_t match_perm = 0, i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (qpol_avrule_get_is_enabled(p->p, rule, &is_enabled) < 0) {
+ goto cleanup;
+ }
+ if (!is_enabled && only_enabled) {
+ continue;
+ }
+
+ if (bool_name != NULL) {
+ if (qpol_avrule_get_cond(p->p, rule, &cond) < 0) {
+ goto cleanup;
+ }
+ if (cond == NULL) {
+ continue; /* skip unconditional rule */
+ }
+ match_bool = apol_compare_cond_expr(p, cond, bool_name, is_regex, &bool_regex);
+ if (match_bool < 0) {
+ goto cleanup;
+ } else if (match_bool == 0) {
+ continue;
+ }
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_type_t *source_type;
+ if (qpol_avrule_get_source_type(p->p, rule, &source_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target has been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_avrule_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+
+ if (!match_target) {
+ continue;
+ }
+
+ if (class_list != NULL) {
+ const qpol_class_t *obj_class;
+ if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) {
+ continue;
+ }
+ }
+
+ if (perm_list != NULL) {
+ for (i = 0; i < apol_vector_get_size(perm_list) && match_perm < num_perms_to_match; i++) {
+ char *perm = (char *)apol_vector_get_element(perm_list, i);
+ if (qpol_avrule_get_perm_iter(p->p, rule, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ int match = apol_compare_iter(p, perm_iter, perm, 0, NULL, 1);
+ if (match < 0) {
+ goto cleanup;
+ } else if (match > 0) {
+ match_perm++;
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+ } else {
+ match_perm = num_perms_to_match;
+ }
+ if (match_perm < num_perms_to_match) {
+ continue;
+ }
+
+ if (apol_vector_append(v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retv = 0;
+ cleanup:
+ apol_regex_destroy(&bool_regex);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retv;
+}
+
+int apol_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v)
+{
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *perm_list = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ *v = NULL;
+ unsigned int flags = 0;
+
+ uint32_t rule_type = QPOL_RULE_ALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT;
+// if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_NEVERALLOW)) {
+ rule_type |= QPOL_RULE_NEVERALLOW;
+// }
+ if (a != NULL) {
+ if (a->rules != 0) {
+ rule_type &= a->rules;
+ }
+ flags = a->flags;
+ is_regex = a->flags & APOL_QUERY_REGEX;
+ bool_name = a->bool_name;
+ if (a->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_type_list(p, a->source, is_regex,
+ a->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((a->flags & APOL_QUERY_SOURCE_AS_ANY) && a->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (a->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, a->target, is_regex,
+ a->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((a->
+ flags & (APOL_QUERY_TARGET_TYPE | APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (a->classes != NULL &&
+ apol_vector_get_size(a->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, a->classes)) == NULL) {
+ goto cleanup;
+ }
+ if (a->perms != NULL && apol_vector_get_size(a->perms) > 0) {
+ perm_list = a->perms;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, perm_list, bool_name)) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ apol_vector_destroy(&class_list);
+ /* don't destroy perm_list - it points to query's permission list */
+ return retval;
+}
+
+int apol_syn_avrule_get_by_query(const apol_policy_t * p, const apol_avrule_query_t * a, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *perm_list = NULL, *syn_v = NULL;
+ apol_vector_t *target_types_list = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ regex_t *bool_regex = NULL;
+ *v = NULL;
+ size_t i;
+ unsigned int flags = 0;
+
+ if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ uint32_t rule_type = QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT;
+ if (a != NULL) {
+ if (a->rules != 0) {
+ rule_type &= a->rules;
+ }
+ flags = a->flags;
+ is_regex = a->flags & APOL_QUERY_REGEX;
+ bool_name = a->bool_name;
+ if (a->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_syn_type_list(p, a->source, is_regex,
+ a->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((a->flags & APOL_QUERY_SOURCE_AS_ANY) && a->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (a->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_syn_type_list(p, a->target, is_regex,
+ a->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((a->flags & (APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (a->classes != NULL &&
+ apol_vector_get_size(a->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, a->classes)) == NULL) {
+ goto cleanup;
+ }
+ if (a->perms != NULL && apol_vector_get_size(a->perms) > 0) {
+ perm_list = a->perms;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, perm_list, bool_name)) {
+ goto cleanup;
+ }
+
+ syn_v = apol_avrule_list_to_syn_avrules(p, *v, perm_list);
+ if (!syn_v) {
+ goto cleanup;
+ }
+ apol_vector_destroy(v);
+ *v = syn_v;
+ syn_v = NULL;
+
+ /* if both fields are indirect skip post filtering type sets */
+ if ((a->flags & APOL_QUERY_SOURCE_INDIRECT) && (a->flags & (APOL_QUERY_TARGET_INDIRECT | APOL_QUERY_SOURCE_AS_ANY))) {
+ retval = 0;
+ goto cleanup;
+ }
+ /* if not searching by source or target we are done */
+ if (!source_list && !target_list) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ if (source_list && !(a->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ apol_vector_destroy(&source_list);
+ source_list =
+ apol_query_create_candidate_type_list(p, a->source, is_regex, 0,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!source_list)
+ goto cleanup;
+ }
+ if (target_list && (source_as_any || !(a->flags & APOL_QUERY_TARGET_INDIRECT))) {
+ if (source_as_any) {
+ target_list = source_list;
+ } else {
+ apol_vector_destroy(&target_list);
+ target_list =
+ apol_query_create_candidate_type_list(p, a->target, is_regex, 0,
+ ((a->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!target_list)
+ goto cleanup;
+ }
+ }
+ if (target_list) {
+ target_types_list = apol_vector_create_from_vector(target_list, NULL, NULL, NULL);
+ if (!target_types_list) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ qpol_type_t *type = NULL;
+ for (i = 0; i < apol_vector_get_size(target_types_list); i++) {
+ type = apol_vector_get_element(target_types_list, i);
+ unsigned char isattr = 0;
+ qpol_type_get_isattr(p->p, type, &isattr);
+ if (isattr) {
+ apol_vector_remove(target_types_list, i);
+ i--;
+ }
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(*v); i++) {
+ qpol_syn_avrule_t *srule = apol_vector_get_element(*v, i);
+ const qpol_type_set_t *stypes = NULL, *ttypes = NULL;
+ int uses_source = 0, uses_target = 0;
+ uint32_t is_self = 0;
+ qpol_syn_avrule_get_source_type_set(p->p, srule, &stypes);
+ qpol_syn_avrule_get_target_type_set(p->p, srule, &ttypes);
+ qpol_syn_avrule_get_is_target_self(p->p, srule, &is_self);
+ if (source_list && !(a->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ uses_source = apol_query_type_set_uses_types_directly(p, stypes, source_list);
+ if (uses_source < 0)
+ goto cleanup;
+ } else if (source_list && a->flags & APOL_QUERY_SOURCE_INDIRECT) {
+ uses_source = 1;
+ } else if (!source_list) {
+ uses_source = 1;
+ }
+
+ if (target_list
+ && !((a->flags & APOL_QUERY_TARGET_INDIRECT) || (source_as_any && a->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = apol_query_type_set_uses_types_directly(p, ttypes, target_list);
+ if (uses_target < 0)
+ goto cleanup;
+ if (is_self) {
+ uses_target |= apol_query_type_set_uses_types_directly(p, stypes, target_types_list);
+ if (uses_target < 0)
+ goto cleanup;
+ }
+ } else if (target_list && ((a->flags & APOL_QUERY_TARGET_INDIRECT)
+ || (source_as_any && a->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = 1;
+ } else if (!target_list) {
+ uses_target = 1;
+ }
+
+ if (!((uses_source && uses_target) || (source_as_any && (uses_source || uses_target)))) {
+ apol_vector_remove(*v, i);
+ i--;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&syn_v);
+ apol_vector_destroy(&source_list);
+ apol_vector_destroy(&target_types_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ apol_vector_destroy(&class_list);
+ /* don't destroy perm_list - it points to query's permission list */
+ apol_regex_destroy(&bool_regex);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_avrule_query_t *apol_avrule_query_create(void)
+{
+ apol_avrule_query_t *a = calloc(1, sizeof(apol_avrule_query_t));
+ if (a != NULL) {
+ a->rules = ~0U;
+ a->flags =
+ (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE | APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE);
+ }
+ return a;
+}
+
+void apol_avrule_query_destroy(apol_avrule_query_t ** a)
+{
+ if (*a != NULL) {
+ free((*a)->source);
+ free((*a)->target);
+ free((*a)->bool_name);
+ apol_vector_destroy(&(*a)->classes);
+ apol_vector_destroy(&(*a)->perms);
+ free(*a);
+ *a = NULL;
+ }
+}
+
+int apol_avrule_query_set_rules(const apol_policy_t * p __attribute__ ((unused)), apol_avrule_query_t * a, unsigned int rules)
+{
+ if (rules != 0) {
+ a->rules = rules;
+ } else {
+ a->rules = ~0U;
+ }
+ return 0;
+}
+
+int apol_avrule_query_set_source(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &a->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT);
+ return apol_query_set(p, &a->source, NULL, symbol);
+}
+
+int apol_avrule_query_set_source_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component)
+{
+ if (!a || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_SOURCE_TYPE);
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SOURCE_ATTRIBUTE);
+ return 0;
+}
+
+int apol_avrule_query_set_target(const apol_policy_t * p, apol_avrule_query_t * a, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &a->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &a->target, NULL, symbol);
+}
+
+int apol_avrule_query_set_target_component(const apol_policy_t * p, apol_avrule_query_t * a, unsigned int component)
+{
+ if (!a || !(component && APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_TARGET_TYPE);
+ apol_query_set_flag(p, &a->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_TARGET_ATTRIBUTE);
+ return 0;
+}
+
+int apol_avrule_query_append_class(const apol_policy_t * p, apol_avrule_query_t * a, const char *obj_class)
+{
+ char *s = NULL;
+ if (obj_class == NULL) {
+ apol_vector_destroy(&a->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (a->classes == NULL && (a->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(a->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+int apol_avrule_query_append_perm(const apol_policy_t * p, apol_avrule_query_t * a, const char *perm)
+{
+ char *s;
+ if (perm == NULL) {
+ apol_vector_destroy(&a->perms);
+ } else if ((s = strdup(perm)) == NULL ||
+ (a->perms == NULL && (a->perms = apol_vector_create(free)) == NULL) || apol_vector_append(a->perms, s) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_avrule_query_set_bool(const apol_policy_t * p, apol_avrule_query_t * a, const char *bool_name)
+{
+ return apol_query_set(p, &a->bool_name, NULL, bool_name);
+}
+
+int apol_avrule_query_set_enabled(const apol_policy_t * p, apol_avrule_query_t * a, int is_enabled)
+{
+ return apol_query_set_flag(p, &a->flags, is_enabled, APOL_QUERY_ONLY_ENABLED);
+}
+
+int apol_avrule_query_set_all_perms(const apol_policy_t * p, apol_avrule_query_t * a, int match_all)
+{
+ return apol_query_set_flag(p, &a->flags, match_all, APOL_QUERY_MATCH_ALL_PERMS);
+}
+
+int apol_avrule_query_set_source_any(const apol_policy_t * p, apol_avrule_query_t * a, int is_any)
+{
+ return apol_query_set_flag(p, &a->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_avrule_query_set_regex(const apol_policy_t * p, apol_avrule_query_t * a, int is_regex)
+{
+ return apol_query_set_regex(p, &a->flags, is_regex);
+}
+
+/**
+ * Comparison function for two syntactic avrules. Will return -1 if
+ * a's line number is before b's, 1 if b is greater.
+ */
+static int apol_syn_avrule_comp(const void *a, const void *b, void *data)
+{
+ qpol_syn_avrule_t *r1 = (qpol_syn_avrule_t *) a;
+ qpol_syn_avrule_t *r2 = (qpol_syn_avrule_t *) b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ unsigned long num1, num2;
+ if (qpol_syn_avrule_get_lineno(p->p, r1, &num1) < 0 || qpol_syn_avrule_get_lineno(p->p, r2, &num2) < 0) {
+ return 0;
+ }
+ if (num1 != num2) {
+ return (int)num1 - (int)num2;
+ }
+ return (int)((char *)r1 - (char *)r2);
+}
+
+apol_vector_t *apol_avrule_to_syn_avrules(const apol_policy_t * p, const qpol_avrule_t * rule, const apol_vector_t * perms)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ qpol_syn_avrule_t *syn_avrule;
+ char *perm;
+ size_t i;
+ int retval = -1, error = 0, found_perm = 0;
+ if (qpol_avrule_get_syn_avrule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ found_perm = 0;
+ if (perms != NULL && apol_vector_get_size(perms) > 0) {
+ if (qpol_syn_avrule_get_perm_iter(p->p, syn_avrule, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_index(perms, perm, apol_str_strcmp, NULL, &i) == 0) {
+ found_perm = 1;
+ break;
+ }
+ }
+ } else {
+ found_perm = 1;
+ }
+ if (found_perm && apol_vector_append(v, syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ /* explicit cast to void* since vector's arbitrary data is non-const */
+ apol_vector_sort_uniquify(v, apol_syn_avrule_comp, (void *)p);
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *apol_avrule_list_to_syn_avrules(const apol_policy_t * p, const apol_vector_t * rules, const apol_vector_t * perms)
+{
+ apol_bst_t *b = NULL;
+ qpol_avrule_t *rule;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_avrule_t *syn_avrule;
+ char *perm;
+ apol_vector_t *tmp_v = NULL, *v = NULL;
+ size_t i, x;
+ int retval = -1, error = 0, found_perm = 0;
+
+ if ((b = apol_bst_create(apol_syn_avrule_comp, NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(rules); i++) {
+ rule = apol_vector_get_element(rules, i);
+ if (qpol_avrule_get_syn_avrule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ /* explicit cast to void* since bst's arbitrary data is non-const */
+ if (apol_bst_insert(b, syn_avrule, (void *)p) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ if ((tmp_v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (perms == NULL || apol_vector_get_size(perms) == 0) {
+ v = tmp_v;
+ tmp_v = NULL;
+ } else {
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(tmp_v); i++) {
+ syn_avrule = apol_vector_get_element(tmp_v, i);
+ found_perm = 0;
+ if (qpol_syn_avrule_get_perm_iter(p->p, syn_avrule, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&perm) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_index(perms, perm, apol_str_strcmp, NULL, &x) == 0) {
+ found_perm = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (found_perm && apol_vector_append(v, syn_avrule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&tmp_v);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+char *apol_avrule_render(const apol_policy_t * policy, const qpol_avrule_t * rule)
+{
+ char *tmp = NULL;
+ const char *rule_type_str, *tmp_name = NULL;
+ int error = 0;
+ uint32_t rule_type = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL;
+ size_t tmp_sz = 0, num_perms = 0;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_avrule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT))) {
+ ERR(policy, "%s", "Invalid AV rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get AV rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type */
+ if (qpol_avrule_get_source_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target type */
+ if (qpol_avrule_get_target_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s : ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object class */
+ if (qpol_avrule_get_object_class(policy->p, rule, &obj_class)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* perms */
+ if (qpol_avrule_get_perm_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &num_perms)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (num_perms > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ char *perm_name = NULL;
+ if (qpol_iterator_get_item(iter, (void **)&perm_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", perm_name)) {
+ error = errno;
+ free(perm_name);
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ free(perm_name);
+ tmp_name = NULL;
+ }
+ if (num_perms > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ";")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ qpol_iterator_destroy(&iter);
+ return tmp;
+
+ err:
+ free(tmp);
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+}
+
+char *apol_syn_avrule_render(const apol_policy_t * policy, const qpol_syn_avrule_t * rule)
+{
+ char *tmp = NULL;
+ const char *rule_type_str, *tmp_name = NULL;
+ int error = 0;
+ uint32_t rule_type = 0, star = 0, comp = 0, self = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL, *iter2 = NULL;
+ size_t tmp_sz = 0, iter_sz = 0, iter2_sz = 0;
+ const qpol_type_set_t *set = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_syn_avrule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT))) {
+ ERR(policy, "%s", "Invalid AV rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get AV rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type set */
+ if (qpol_syn_avrule_get_source_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ /* target type set */
+ if (qpol_syn_avrule_get_target_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_avrule_get_is_target_self(policy->p, rule, &self)) {
+ error = errno;
+ goto err;
+ }
+ if (iter_sz + iter2_sz + self > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (self) {
+ if (apol_str_append(&tmp, &tmp_sz, "self ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (iter_sz + iter2_sz + self > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ": ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object classes */
+ if (qpol_syn_avrule_get_class_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&obj_class)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ /* permissions */
+ if (qpol_syn_avrule_get_perm_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ";")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return tmp;
+
+ err:
+ free(tmp);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/bool-query.c b/libapol/src/bool-query.c
new file mode 100644
index 0000000..8147c3a
--- /dev/null
+++ b/libapol/src/bool-query.c
@@ -0,0 +1,111 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about conditional
+ * booleans within a policy. The caller obtains a query object, fills
+ * in its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_bool_query
+{
+ char *bool_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** booleans queries ********************/
+
+int apol_bool_get_by_query(const apol_policy_t * p, apol_bool_query_t * b, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_bool_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_bool_t *qbool;
+ if (qpol_iterator_get_item(iter, (void **)&qbool) < 0) {
+ goto cleanup;
+ }
+ if (b != NULL) {
+ const char *bool_name;
+ int compval;
+ if (qpol_bool_get_name(p->p, qbool, &bool_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, bool_name, b->bool_name, b->flags, &(b->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, qbool)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_bool_query_t *apol_bool_query_create(void)
+{
+ return calloc(1, sizeof(apol_bool_query_t));
+}
+
+void apol_bool_query_destroy(apol_bool_query_t ** b)
+{
+ if (*b != NULL) {
+ free((*b)->bool_name);
+ apol_regex_destroy(&(*b)->regex);
+ free(*b);
+ *b = NULL;
+ }
+}
+
+int apol_bool_query_set_bool(const apol_policy_t * p, apol_bool_query_t * b, const char *name)
+{
+ return apol_query_set(p, &b->bool_name, &b->regex, name);
+}
+
+int apol_bool_query_set_regex(const apol_policy_t * p, apol_bool_query_t * b, int is_regex)
+{
+ return apol_query_set_regex(p, &b->flags, is_regex);
+}
diff --git a/libapol/src/bst.c b/libapol/src/bst.c
new file mode 100644
index 0000000..df0beb6
--- /dev/null
+++ b/libapol/src/bst.c
@@ -0,0 +1,352 @@
+/**
+ * @file
+ * Contains the implementation of a generic binary search tree. The
+ * tree is implemented as a red-black tree, as inspired by Julienne
+ * Walker (http://eternallyconfuzzled.com/tuts/redblack.html).
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <apol/bst.h>
+#include <apol/vector.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vector-internal.h"
+
+typedef struct bst_node
+{
+ void *elem;
+ int is_red;
+ struct bst_node *child[2];
+} bst_node_t;
+
+/**
+ * Generic binary search tree structure. Stores elements as void*.
+ */
+struct apol_bst
+{
+ /** Comparison function for nodes. */
+ apol_bst_comp_func *cmp;
+ /** Destroy function for the nodes, or NULL to not free each node. */
+ apol_bst_free_func *fr;
+ /** The number of elements currently stored in the bst. */
+ size_t size;
+ /** Pointer to top of the tree. */
+ bst_node_t *head;
+};
+
+apol_bst_t *apol_bst_create(apol_bst_comp_func * cmp, apol_bst_free_func * fr)
+{
+ apol_bst_t *b = NULL;
+ if ((b = calloc(1, sizeof(*b))) == NULL) {
+ return NULL;
+ }
+ b->cmp = cmp;
+ b->fr = fr;
+ return b;
+}
+
+/**
+ * Free the data stored within a bst node, recurse through the node's
+ * children, and then the node itself.
+ *
+ * @param node Node to free. If NULL then do stop recursing.
+ * @param fr Callback to free a node's data. If NULL then do not free
+ * the data.
+ */
+static void bst_node_free(bst_node_t * node, apol_bst_free_func * fr)
+{
+ if (node != NULL) {
+ if (fr != NULL) {
+ fr(node->elem);
+ }
+ bst_node_free(node->child[0], fr);
+ bst_node_free(node->child[1], fr);
+ free(node);
+ }
+}
+
+void apol_bst_destroy(apol_bst_t ** b)
+{
+ if (!b || !(*b))
+ return;
+ bst_node_free((*b)->head, (*b)->fr);
+ (*b)->head = NULL;
+ free(*b);
+ *b = NULL;
+}
+
+/**
+ * Given a BST node, traverse the node infix, appending the node's
+ * element to vector v.
+ *
+ * @param node BST node to recurse.
+ * @param v Vector to which append.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int bst_node_to_vector(bst_node_t * node, apol_vector_t * v)
+{
+ int retval;
+ if (node == NULL) {
+ return 0;
+ }
+ if ((retval = bst_node_to_vector(node->child[0], v)) < 0) {
+ return retval;
+ }
+ if ((retval = apol_vector_append(v, node->elem)) < 0) {
+ return retval;
+ }
+ return bst_node_to_vector(node->child[1], v);
+}
+
+apol_vector_t *apol_bst_get_vector(apol_bst_t * b, int change_owner)
+{
+ apol_vector_t *v = NULL;
+ if (!b) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((v = apol_vector_create_with_capacity(b->size, NULL)) == NULL) {
+ return NULL;
+ }
+ if (bst_node_to_vector(b->head, v) < 0) {
+ int error = errno;
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ if (change_owner) {
+ vector_set_free_func(v, b->fr);
+ b->fr = NULL;
+ }
+ return v;
+}
+
+size_t apol_bst_get_size(const apol_bst_t * b)
+{
+ if (!b) {
+ errno = EINVAL;
+ return 0;
+ } else {
+ return b->size;
+ }
+}
+
+int apol_bst_get_element(const apol_bst_t * b, const void *elem, void *data, void **result)
+{
+ bst_node_t *node;
+ int compval;
+ if (!b || !result) {
+ errno = EINVAL;
+ return -1;
+ }
+ node = b->head;
+ while (node != NULL) {
+ if (b->cmp != NULL) {
+ compval = b->cmp(node->elem, elem, data);
+ } else {
+ char *p1 = (char *)node->elem;
+ char *p2 = (char *)elem;
+ if (p1 < p2) {
+ compval = -1;
+ } else if (p1 > p2) {
+ compval = 1;
+ } else {
+ compval = 0;
+ }
+ }
+ if (compval == 0) {
+ *result = node->elem;
+ return 0;
+ } else if (compval > 0) {
+ node = node->child[0];
+ } else {
+ node = node->child[1];
+ }
+ }
+ return -1;
+}
+
+/**
+ * Allocate and return a new BST node, with data set to elem and color
+ * to red. Also increment the tree's size.
+ *
+ * @param b BST size to increment.
+ * @param elem Value for the node.
+ *
+ * @return Allocated BST node, which the caller must insert, or NULL
+ * on error.
+ */
+static bst_node_t *bst_node_make(apol_bst_t * b, void *elem)
+{
+ bst_node_t *new_node;
+ if ((new_node = calloc(1, sizeof(*new_node))) == NULL) {
+ return NULL;
+ }
+ new_node->elem = elem;
+ new_node->is_red = 1;
+ b->size++;
+ return new_node;
+}
+
+/**
+ * Determines if a node is red or not.
+ *
+ * @param node Node to check. If NULL then treat the node as black.
+ *
+ * @return 0 if the node is black, 1 if red.
+ */
+static int bst_node_is_red(bst_node_t * node)
+{
+ return node != NULL && node->is_red;
+}
+
+static bst_node_t *bst_rotate_single(bst_node_t * root, int dir)
+{
+ bst_node_t *save = root->child[!dir];
+ root->child[!dir] = save->child[dir];
+ save->child[dir] = root;
+ root->is_red = 1;
+ save->is_red = 0;
+ return save;
+}
+
+static bst_node_t *bst_rotate_double(bst_node_t * root, int dir)
+{
+ root->child[!dir] = bst_rotate_single(root->child[!dir], !dir);
+ return bst_rotate_single(root, dir);
+}
+
+static bst_node_t *bst_insert_recursive(apol_bst_t * b, bst_node_t * root, void **elem, void *data, apol_bst_free_func * fr,
+ int *not_uniq)
+{
+ int compval, dir;
+ if (root == NULL) {
+ if ((root = bst_node_make(b, *elem)) == NULL) {
+ *not_uniq = -1;
+ return NULL;
+ }
+ *not_uniq = 0;
+ } else {
+ if (b->cmp != NULL) {
+ compval = b->cmp(root->elem, *elem, data);
+ } else {
+ char *p1 = (char *)root->elem;
+ char *p2 = (char *)(*elem);
+ if (p1 < p2) {
+ compval = -1;
+ } else if (p1 > p2) {
+ compval = 1;
+ } else {
+ compval = 0;
+ }
+ }
+ if (compval == 0) {
+ /* already exists */
+ if (fr != NULL) {
+ fr(*elem);
+ }
+ *elem = root->elem;
+ *not_uniq = 1;
+ return root;
+ } else if (compval > 0) {
+ dir = 0;
+ } else {
+ dir = 1;
+ }
+ root->child[dir] = bst_insert_recursive(b, root->child[dir], elem, data, fr, not_uniq);
+ if (*not_uniq != 0) {
+ return root;
+ }
+
+ /* rebalance tree */
+ if (bst_node_is_red(root->child[dir])) {
+ if (bst_node_is_red(root->child[!dir])) {
+ /* recolor myself and children. note
+ * that this can't be reached if a
+ * child is NULL */
+ root->is_red = 1;
+ root->child[0]->is_red = 0;
+ root->child[1]->is_red = 0;
+ } else {
+ if (bst_node_is_red(root->child[dir]->child[dir])) {
+ root = bst_rotate_single(root, !dir);
+ } else if (bst_node_is_red(root->child[dir]->child[!dir])) {
+ root = bst_rotate_double(root, !dir);
+ }
+ }
+ }
+ }
+ return root;
+}
+
+int apol_bst_insert(apol_bst_t * b, void *elem, void *data)
+{
+ int retval = -1;
+ if (!b || !elem) {
+ errno = EINVAL;
+ return -1;
+ }
+ b->head = bst_insert_recursive(b, b->head, &elem, data, NULL, &retval);
+ if (retval >= 0) {
+ b->head->is_red = 0;
+ }
+ return retval;
+}
+
+int apol_bst_insert_and_get(apol_bst_t * b, void **elem, void *data)
+{
+ int retval = -1;
+ if (!b || !elem) {
+ errno = EINVAL;
+ return -1;
+ }
+ b->head = bst_insert_recursive(b, b->head, elem, data, b->fr, &retval);
+ if (retval >= 0) {
+ b->head->is_red = 0;
+ }
+ return retval;
+}
+
+static int bst_inorder_map(const bst_node_t * node, int (*fn) (void *, void *), void *data)
+{
+ int retval;
+ if (node == NULL) {
+ return 0;
+ }
+ if ((retval = bst_inorder_map(node->child[0], fn, data)) < 0) {
+ return retval;
+ }
+ if ((retval = fn(node->elem, data)) < 0) {
+ return retval;
+ }
+ return bst_inorder_map(node->child[1], fn, data);
+}
+
+int apol_bst_inorder_map(const apol_bst_t * b, int (*fn) (void *, void *), void *data)
+{
+ if (b == NULL || fn == NULL)
+ return -1;
+ return bst_inorder_map(b->head, fn, data);
+}
diff --git a/libapol/src/class-perm-query.c b/libapol/src/class-perm-query.c
new file mode 100644
index 0000000..938994d
--- /dev/null
+++ b/libapol/src/class-perm-query.c
@@ -0,0 +1,327 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about classes, commons,
+ * and permissions within a policy. The caller obtains a query
+ * object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <errno.h>
+#include <string.h>
+
+struct apol_class_query
+{
+ char *class_name, *common_name;
+ unsigned int flags;
+ regex_t *class_regex, *common_regex;
+};
+
+struct apol_common_query
+{
+ char *common_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+struct apol_perm_query
+{
+ char *perm_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** class queries ********************/
+
+int apol_class_get_by_query(const apol_policy_t * p, apol_class_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ int retval = -1, append_class;
+ *v = NULL;
+ if (qpol_policy_get_class_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_class_t *class_datum;
+ if (qpol_iterator_get_item(iter, (void **)&class_datum) < 0) {
+ goto cleanup;
+ }
+ append_class = 1;
+ if (c != NULL) {
+ const char *class_name, *common_name = NULL;
+ const qpol_common_t *common_datum;
+ int compval;
+ if (qpol_class_get_name(p->p, class_datum, &class_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, class_name, c->class_name, c->flags, &(c->class_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (qpol_class_get_common(p->p, class_datum, &common_datum) < 0) {
+ goto cleanup;
+ }
+ if (common_datum == NULL) {
+ if (c->common_name != NULL && c->common_name[0] != '\0') {
+ continue;
+ }
+ } else {
+ if (qpol_common_get_name(p->p, common_datum, &common_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, common_name, c->common_name, c->flags, &(c->common_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ }
+ if (append_class && apol_vector_append(*v, class_datum)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_class_query_t *apol_class_query_create(void)
+{
+ return calloc(1, sizeof(apol_class_query_t));
+}
+
+void apol_class_query_destroy(apol_class_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->class_name);
+ free((*c)->common_name);
+ apol_regex_destroy(&(*c)->class_regex);
+ apol_regex_destroy(&(*c)->common_regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_class_query_set_class(const apol_policy_t * p, apol_class_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->class_name, &c->class_regex, name);
+}
+
+int apol_class_query_set_common(const apol_policy_t * p, apol_class_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->common_name, &c->common_regex, name);
+}
+
+int apol_class_query_set_regex(const apol_policy_t * p, apol_class_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+/******************** common queries ********************/
+
+int apol_common_get_by_query(const apol_policy_t * p, apol_common_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_common_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_common_t *common_datum;
+ if (qpol_iterator_get_item(iter, (void **)&common_datum) < 0) {
+ goto cleanup;
+ }
+ if (c != NULL) {
+ const char *common_name = NULL;
+ int compval;
+ if (qpol_common_get_name(p->p, common_datum, &common_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, common_name, c->common_name, c->flags, &(c->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, common_datum)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_common_query_t *apol_common_query_create(void)
+{
+ return calloc(1, sizeof(apol_common_query_t));
+}
+
+void apol_common_query_destroy(apol_common_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->common_name);
+ apol_regex_destroy(&(*c)->regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_common_query_set_common(const apol_policy_t * p, apol_common_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->common_name, &c->regex, name);
+}
+
+int apol_common_query_set_regex(const apol_policy_t * p, apol_common_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+/******************** permission queries ********************/
+
+int apol_perm_get_by_query(const apol_policy_t * p, apol_perm_query_t * pq, apol_vector_t ** v)
+{
+ qpol_iterator_t *class_iter = NULL, *common_iter = NULL, *perm_iter = NULL;
+ int retval = -1, compval;
+ char *perm_name;
+ *v = NULL;
+ if (qpol_policy_get_class_iter(p->p, &class_iter) < 0 || qpol_policy_get_common_iter(p->p, &common_iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(class_iter); qpol_iterator_next(class_iter)) {
+ qpol_class_t *class_datum;
+ if (qpol_iterator_get_item(class_iter, (void **)&class_datum) < 0 ||
+ qpol_class_get_perm_iter(p->p, class_datum, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) {
+ goto cleanup;
+ }
+ if (pq == NULL) {
+ compval = 1;
+ } else {
+ compval = apol_compare(p, perm_name, pq->perm_name, pq->flags, &(pq->regex));
+ }
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1 && apol_vector_append_unique(*v, perm_name, apol_str_strcmp, NULL) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+
+ for (; !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) {
+ qpol_common_t *common_datum;
+ if (qpol_iterator_get_item(common_iter, (void **)&common_datum) < 0 ||
+ qpol_common_get_perm_iter(p->p, common_datum, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) {
+ goto cleanup;
+ }
+ if (pq == NULL) {
+ compval = 1;
+ } else {
+ compval = apol_compare(p, perm_name, pq->perm_name, pq->flags, &(pq->regex));
+ }
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1 && apol_vector_append_unique(*v, perm_name, apol_str_strcmp, NULL) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&class_iter);
+ qpol_iterator_destroy(&common_iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_perm_query_t *apol_perm_query_create(void)
+{
+ return calloc(1, sizeof(apol_perm_query_t));
+}
+
+void apol_perm_query_destroy(apol_perm_query_t ** pq)
+{
+ if (*pq != NULL) {
+ free((*pq)->perm_name);
+ apol_regex_destroy(&(*pq)->regex);
+ free(*pq);
+ *pq = NULL;
+ }
+}
+
+int apol_perm_query_set_perm(const apol_policy_t * p, apol_perm_query_t * pq, const char *name)
+{
+ return apol_query_set(p, &pq->perm_name, &pq->regex, name);
+}
+
+int apol_perm_query_set_regex(const apol_policy_t * p, apol_perm_query_t * pq, int is_regex)
+{
+ return apol_query_set_regex(p, &pq->flags, is_regex);
+}
diff --git a/libapol/src/condrule-query.c b/libapol/src/condrule-query.c
new file mode 100644
index 0000000..451b617
--- /dev/null
+++ b/libapol/src/condrule-query.c
@@ -0,0 +1,182 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about conditional
+ * expressions rules within a policy. The caller obtains a query
+ * object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <errno.h>
+
+struct apol_cond_query
+{
+ char *bool_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_cond_get_by_query(const apol_policy_t * p, apol_cond_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ *v = NULL;
+
+ if (qpol_policy_get_cond_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_cond_t *cond;
+ if (qpol_iterator_get_item(iter, (void **)&cond) < 0) {
+ goto cleanup;
+ }
+ if (c != NULL) {
+ int keep_cond = apol_compare_cond_expr(p, cond, c->bool_name, c->flags, &c->regex);
+ if (keep_cond < 0) {
+ goto cleanup;
+ } else if (keep_cond == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, cond)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_cond_query_t *apol_cond_query_create(void)
+{
+ return calloc(1, sizeof(apol_cond_query_t));
+}
+
+void apol_cond_query_destroy(apol_cond_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->bool_name);
+ apol_regex_destroy(&(*c)->regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_cond_query_set_bool(const apol_policy_t * p, apol_cond_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->bool_name, &c->regex, name);
+}
+
+int apol_cond_query_set_regex(const apol_policy_t * p, apol_cond_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+char *apol_cond_expr_render(const apol_policy_t * p, const qpol_cond_t * cond)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_cond_expr_node_t *expr = NULL;
+ char *tmp = NULL;
+ const char *bool_name = NULL;
+ int error = 0;
+ size_t tmp_sz = 0, i;
+ uint32_t expr_type = 0;
+ qpol_bool_t *cond_bool = NULL;
+
+ if (!p || !cond) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (qpol_cond_get_expr_node_iter(p->p, cond, &iter) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&expr)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_cond_expr_node_get_expr_type(p->p, expr, &expr_type)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ if (apol_str_append(&tmp, &tmp_sz, apol_cond_expr_type_to_str(expr_type))) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_cond_expr_node_get_bool(p->p, expr, &cond_bool)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_bool_get_name(p->p, cond_bool, &bool_name)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_str_append(&tmp, &tmp_sz, bool_name)) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (apol_str_append(&tmp, &tmp_sz, " ")) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ /* remove trailing space */
+ i = strlen(tmp);
+ if (i > 1) {
+ tmp[i - 1] = '\0';
+ }
+ qpol_iterator_destroy(&iter);
+ return tmp;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ free(tmp);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/constraint-query.c b/libapol/src/constraint-query.c
new file mode 100644
index 0000000..5495975
--- /dev/null
+++ b/libapol/src/constraint-query.c
@@ -0,0 +1,217 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about constraint and
+ * validatetrans statements within a policy. The caller obtains a
+ * query object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <errno.h>
+#include <string.h>
+
+struct apol_constraint_query
+{
+ char *class_name, *perm_name;
+ unsigned int flags;
+ regex_t *class_regex, *perm_regex;
+};
+
+struct apol_validatetrans_query
+{
+ char *class_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** constraint queries ********************/
+
+int apol_constraint_get_by_query(const apol_policy_t * p, apol_constraint_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *perm_iter = NULL;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_constraint_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_constraint_t *constraint;
+ if (qpol_iterator_get_item(iter, (void **)&constraint) < 0) {
+ goto cleanup;
+ }
+ if (c != NULL) {
+ const qpol_class_t *class_datum;
+ const char *class_name;
+ int compval;
+ if (qpol_constraint_get_class(p->p, constraint, &class_datum) < 0 ||
+ qpol_class_get_name(p->p, class_datum, &class_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, class_name, c->class_name, c->flags, &(c->class_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ free(constraint);
+ continue;
+ }
+
+ if (qpol_constraint_get_perm_iter(p->p, constraint, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare_iter(p, perm_iter, c->perm_name, c->flags, &(c->perm_regex), 1);
+ qpol_iterator_destroy(&perm_iter);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ free(constraint);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, constraint)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+apol_constraint_query_t *apol_constraint_query_create(void)
+{
+ return calloc(1, sizeof(apol_constraint_query_t));
+}
+
+void apol_constraint_query_destroy(apol_constraint_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->class_name);
+ free((*c)->perm_name);
+ apol_regex_destroy(&(*c)->class_regex);
+ apol_regex_destroy(&(*c)->perm_regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_constraint_query_set_class(const apol_policy_t * p, apol_constraint_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->class_name, &c->class_regex, name);
+}
+
+int apol_constraint_query_set_perm(const apol_policy_t * p, apol_constraint_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->perm_name, &c->perm_regex, name);
+}
+
+int apol_constraint_query_set_regex(const apol_policy_t * p, apol_constraint_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
+
+/******************** validatetrans queries ********************/
+
+int apol_validatetrans_get_by_query(const apol_policy_t * p, apol_validatetrans_query_t * vt, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_validatetrans_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_validatetrans_t *validatetrans;
+ if (qpol_iterator_get_item(iter, (void **)&validatetrans) < 0) {
+ goto cleanup;
+ }
+ if (vt != NULL) {
+ const qpol_class_t *class_datum;
+ const char *class_name;
+ int compval;
+ if (qpol_validatetrans_get_class(p->p, validatetrans, &class_datum) < 0 ||
+ qpol_class_get_name(p->p, class_datum, &class_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, class_name, vt->class_name, vt->flags, &(vt->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ free(validatetrans);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, validatetrans)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_validatetrans_query_t *apol_validatetrans_query_create(void)
+{
+ return calloc(1, sizeof(apol_validatetrans_query_t));
+}
+
+void apol_validatetrans_query_destroy(apol_validatetrans_query_t ** vt)
+{
+ if (*vt != NULL) {
+ free((*vt)->class_name);
+ apol_regex_destroy(&(*vt)->regex);
+ free(*vt);
+ *vt = NULL;
+ }
+}
+
+int apol_validatetrans_query_set_class(const apol_policy_t * p, apol_validatetrans_query_t * vt, const char *name)
+{
+ return apol_query_set(p, &vt->class_name, &vt->regex, name);
+}
+
+int apol_validatetrans_query_set_regex(const apol_policy_t * p, apol_validatetrans_query_t * vt, int is_regex)
+{
+ return apol_query_set_regex(p, &vt->flags, is_regex);
+}
diff --git a/libapol/src/context-query.c b/libapol/src/context-query.c
new file mode 100644
index 0000000..90c7fbe
--- /dev/null
+++ b/libapol/src/context-query.c
@@ -0,0 +1,477 @@
+/**
+ * @file
+ * Implementation for querying aspects of a context.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <apol/render.h>
+
+struct apol_context
+{
+ char *user, *role, *type;
+ apol_mls_range_t *range;
+};
+
+apol_context_t *apol_context_create(void)
+{
+ return calloc(1, sizeof(apol_context_t));
+}
+
+apol_context_t *apol_context_create_from_qpol_context(const apol_policy_t * p, const qpol_context_t * context)
+{
+ apol_context_t *c = NULL;
+ const qpol_user_t *user;
+ const qpol_role_t *role;
+ const qpol_type_t *type;
+ const qpol_mls_range_t *range;
+ const char *user_name, *role_name, *type_name;
+ apol_mls_range_t *apol_range = NULL;
+ if ((c = apol_context_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ if (qpol_context_get_user(p->p, context, &user) < 0 ||
+ qpol_context_get_role(p->p, context, &role) < 0 ||
+ qpol_context_get_type(p->p, context, &type) < 0 || qpol_context_get_range(p->p, context, &range) < 0) {
+ goto err;
+ }
+ if (qpol_user_get_name(p->p, user, &user_name) < 0 ||
+ qpol_role_get_name(p->p, role, &role_name) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) {
+ goto err;
+ }
+ if (qpol_policy_has_capability(p->p, QPOL_CAP_MLS)) {
+ /* if the policy is MLS then convert the range, else
+ * rely upon the default value of NULL */
+ if ((apol_range = apol_mls_range_create_from_qpol_mls_range(p, range)) == NULL) {
+ goto err;
+ }
+ }
+ if (apol_context_set_user(p, c, user_name) < 0 ||
+ apol_context_set_role(p, c, role_name) < 0 ||
+ apol_context_set_type(p, c, type_name) < 0 || apol_context_set_range(p, c, apol_range) < 0) {
+ goto err;
+ }
+ return c;
+ err:
+ apol_mls_range_destroy(&apol_range);
+ apol_context_destroy(&c);
+ return NULL;
+}
+
+apol_context_t *apol_context_create_from_literal(const char *context_string)
+{
+ apol_context_t *c = NULL;
+ bool is_context_compiled = false;
+ regex_t context_regex;
+ const size_t nmatch = 5;
+ regmatch_t pmatch[nmatch];
+
+ if ((c = apol_context_create()) == NULL) {
+ goto err;
+ }
+
+ if (regcomp(&context_regex, "^([^:]*):([^:]*):([^:]*):?(.*)$", REG_EXTENDED) != 0) {
+ goto err;
+ }
+ is_context_compiled = true;
+
+ if (regexec(&context_regex, context_string, nmatch, pmatch, 0) != 0) {
+ errno = EIO;
+ goto err;
+ }
+
+ const char *s;
+ size_t len;
+
+ assert(pmatch[1].rm_so == 0);
+ s = context_string + pmatch[1].rm_so;
+ len = pmatch[1].rm_eo - pmatch[1].rm_so; // no +1 to avoid copying colon
+ if (len != 0 && *s != '*' && (c->user = strndup(s, len)) == NULL) {
+ goto err;
+ }
+
+ assert(pmatch[2].rm_so != -1);
+ s = context_string + pmatch[2].rm_so;
+ len = pmatch[2].rm_eo - pmatch[2].rm_so; // no +1 to avoid copying colon
+ if (len != 0 && *s != '*' && (c->role = strndup(s, len)) == NULL) {
+ goto err;
+ }
+
+ assert(pmatch[3].rm_so != -1);
+ s = context_string + pmatch[3].rm_so;
+ len = pmatch[3].rm_eo - pmatch[3].rm_so; // no +1 to avoid copying colon
+ if (len != 0 && *s != '*' && (c->type = strndup(s, len)) == NULL) {
+ goto err;
+ }
+
+ if (pmatch[4].rm_so != -1) {
+ s = context_string + pmatch[4].rm_so;
+ len = pmatch[4].rm_eo - pmatch[4].rm_so;
+ if (len != 0 && *s != '*' && (c->range = apol_mls_range_create_from_literal(s)) == NULL) {
+ goto err;
+ }
+ }
+
+ regfree(&context_regex);
+ return c;
+
+ err:
+ apol_context_destroy(&c);
+ if (is_context_compiled) {
+ regfree(&context_regex);
+ }
+ return NULL;
+}
+
+void apol_context_destroy(apol_context_t ** context)
+{
+ if (*context != NULL) {
+ free((*context)->user);
+ free((*context)->role);
+ free((*context)->type);
+ apol_mls_range_destroy(&((*context)->range));
+ free(*context);
+ *context = NULL;
+ }
+}
+
+int apol_context_set_user(const apol_policy_t * p, apol_context_t * context, const char *user)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (user != context->user) {
+ free(context->user);
+ context->user = NULL;
+ if (user != NULL && (context->user = strdup(user)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_context_set_role(const apol_policy_t * p, apol_context_t * context, const char *role)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (role != context->role) {
+ free(context->role);
+ context->role = NULL;
+ if (role != NULL && (context->role = strdup(role)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_context_set_type(const apol_policy_t * p, apol_context_t * context, const char *type)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (type != context->type) {
+ free(context->type);
+ context->type = NULL;
+ if (type != NULL && (context->type = strdup(type)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_context_set_range(const apol_policy_t * p, apol_context_t * context, apol_mls_range_t * range)
+{
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (range != context->range) {
+ apol_mls_range_destroy(&(context->range));
+ context->range = range;
+ }
+ return 0;
+}
+
+const char *apol_context_get_user(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->user;
+}
+
+const char *apol_context_get_role(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->role;
+}
+
+const char *apol_context_get_type(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->type;
+}
+
+const apol_mls_range_t *apol_context_get_range(const apol_context_t * context)
+{
+ if (context == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return context->range;
+}
+
+int apol_context_compare(const apol_policy_t * p, const apol_context_t * target, const apol_context_t * search,
+ unsigned int range_compare_type)
+{
+ uint32_t value0, value1;
+ if (p == NULL || target == NULL || search == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (target->user != NULL && search->user != NULL) {
+ const qpol_user_t *user0, *user1;
+ if (qpol_policy_get_user_by_name(p->p,
+ target->user, &user0) < 0 ||
+ qpol_policy_get_user_by_name(p->p,
+ search->user, &user1) < 0 ||
+ qpol_user_get_value(p->p, user0, &value0) < 0 || qpol_user_get_value(p->p, user1, &value1) < 0) {
+ return -1;
+ }
+ if (value0 != value1) {
+ return 0;
+ }
+ }
+ if (target->role != NULL && search->role != NULL) {
+ const qpol_role_t *role0, *role1;
+ if (qpol_policy_get_role_by_name(p->p,
+ target->role, &role0) < 0 ||
+ qpol_policy_get_role_by_name(p->p,
+ search->role, &role1) < 0 ||
+ qpol_role_get_value(p->p, role0, &value0) < 0 || qpol_role_get_value(p->p, role1, &value1) < 0) {
+ return -1;
+ }
+ if (value0 != value1) {
+ return 0;
+ }
+ }
+ if (target->type != NULL && search->type != NULL) {
+ const qpol_type_t *type0, *type1;
+ if (qpol_policy_get_type_by_name(p->p,
+ target->type, &type0) < 0 ||
+ qpol_policy_get_type_by_name(p->p,
+ search->type, &type1) < 0 ||
+ qpol_type_get_value(p->p, type0, &value0) < 0 || qpol_type_get_value(p->p, type1, &value1) < 0) {
+ return -1;
+ }
+ if (value0 != value1) {
+ return 0;
+ }
+ }
+ if (target->range != NULL && search->range != NULL) {
+ return apol_mls_range_compare(p, target->range, search->range, range_compare_type);
+ }
+ return 1;
+}
+
+int apol_context_validate(const apol_policy_t * p, const apol_context_t * context)
+{
+ if (context == NULL ||
+ context->user == NULL ||
+ context->role == NULL || context->type == NULL || (apol_policy_is_mls(p) && context->range == NULL)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ return apol_context_validate_partial(p, context);
+}
+
+int apol_context_validate_partial(const apol_policy_t * p, const apol_context_t * context)
+{
+ apol_user_query_t *user_query = NULL;
+ apol_role_query_t *role_query = NULL;
+ apol_vector_t *user_v = NULL, *role_v = NULL;
+ const qpol_user_t *user;
+ const qpol_type_t *type;
+ const qpol_mls_range_t *user_range;
+ apol_mls_range_t *user_apol_range = NULL;
+ int retval = -1, retval2;
+
+ if (context == NULL) {
+ return 1;
+ }
+ if (context->user != NULL) {
+ if ((user_query = apol_user_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ }
+ if (apol_user_query_set_user(p, user_query, context->user) < 0 ||
+ (context->role != NULL && apol_user_query_set_role(p, user_query, context->role) < 0) ||
+ apol_user_get_by_query(p, user_query, &user_v) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_size(user_v) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ if (context->role != NULL) {
+ if ((role_query = apol_role_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ }
+ if (apol_role_query_set_role(p, role_query, context->role) < 0 ||
+ (context->type != NULL && apol_role_query_set_type(p, role_query, context->type) < 0) ||
+ apol_role_get_by_query(p, role_query, &role_v) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_size(role_v) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ if (context->type != NULL) {
+ if (qpol_policy_get_type_by_name(p->p, context->type, &type) < 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ if (apol_policy_is_mls(p) && context->range != NULL) {
+ retval2 = apol_mls_range_validate(p, context->range);
+ if (retval2 != 1) {
+ retval = retval2;
+ goto cleanup;
+ }
+ /* next check that the user has access to this context */
+ if (context->user != NULL) {
+ if (qpol_policy_get_user_by_name(p->p, context->user, &user) < 0 ||
+ qpol_user_get_range(p->p, user, &user_range) < 0) {
+ goto cleanup;
+ }
+ user_apol_range = apol_mls_range_create_from_qpol_mls_range(p, user_range);
+ if (user_apol_range == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ retval2 = apol_mls_range_compare(p, user_apol_range, context->range, APOL_QUERY_SUB);
+ if (retval2 != 1) {
+ retval = retval2;
+ goto cleanup;
+ }
+ }
+ }
+ retval = 1;
+ cleanup:
+ apol_user_query_destroy(&user_query);
+ apol_role_query_destroy(&role_query);
+ apol_vector_destroy(&user_v);
+ apol_vector_destroy(&role_v);
+ apol_mls_range_destroy(&user_apol_range);
+ return retval;
+}
+
+char *apol_context_render(const apol_policy_t * p, const apol_context_t * context)
+{
+ char *buf = NULL, *range_str = NULL;
+ size_t buf_sz = 0;
+
+ if (context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (p == NULL && !apol_mls_range_is_literal(context->range)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&buf, &buf_sz, "%s:", (context->user != NULL ? context->user : "*")) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ if (apol_str_appendf(&buf, &buf_sz, "%s:", (context->role != NULL ? context->role : "*")) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ if (apol_str_append(&buf, &buf_sz, (context->type != NULL ? context->type : "*")) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ if ((p != NULL && apol_policy_is_mls(p)) || (p == NULL)) {
+ if (context->range == NULL) {
+ range_str = strdup("*");
+ } else {
+ range_str = apol_mls_range_render(p, context->range);
+ }
+ if (range_str == NULL) {
+ goto err_return;
+ }
+ if (apol_str_appendf(&buf, &buf_sz, ":%s", range_str) != 0) {
+ ERR(p, "%s", strerror(errno));
+ goto err_return;
+ }
+ free(range_str);
+ }
+ return buf;
+
+ err_return:
+ free(buf);
+ free(range_str);
+ return NULL;
+}
+
+int apol_context_convert(const apol_policy_t * p, apol_context_t * context)
+{
+ if (p == NULL || context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (context->range != NULL) {
+ return apol_mls_range_convert(p, context->range);
+ }
+ return 0;
+}
diff --git a/libapol/src/domain-trans-analysis-internal.h b/libapol/src/domain-trans-analysis-internal.h
new file mode 100644
index 0000000..2c49bed
--- /dev/null
+++ b/libapol/src/domain-trans-analysis-internal.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * Protected routines for domain transition analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_DOMAIN_TRANS_ANALYSIS_INTERNAL_H
+#define APOL_DOMAIN_TRANS_ANALYSIS_INTERNAL_H
+
+/**
+ * Free all memory associated with a domain transition result, including
+ * the pointer itself. This function does nothing if the result is NULL.
+ * @param dtr Pointer to a domain transition result structure to free.
+ */
+void domain_trans_result_free(void *dtr);
+
+#endif
diff --git a/libapol/src/domain-trans-analysis.c b/libapol/src/domain-trans-analysis.c
new file mode 100644
index 0000000..3fef3b8
--- /dev/null
+++ b/libapol/src/domain-trans-analysis.c
@@ -0,0 +1,2076 @@
+/**
+ * @file
+ *
+ * Routines to perform a domain transition analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include "domain-trans-analysis-internal.h"
+#include <apol/domain-trans-analysis.h>
+#include <apol/bst.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+/* private data structure definitions */
+struct apol_domain_trans_table
+{
+ apol_bst_t *domain_table;
+ apol_bst_t *entrypoint_table;
+};
+
+typedef struct dom_node
+{
+ const qpol_type_t *type;
+ apol_bst_t *process_transition_tree;
+ apol_bst_t *entrypoint_tree;
+ apol_vector_t *setexec_rules;
+} dom_node_t;
+
+typedef struct ep_node
+{
+ const qpol_type_t *type;
+ apol_bst_t *execute_tree;
+ apol_bst_t *type_transition_tree;
+} ep_node_t;
+
+typedef struct avrule_node
+{
+ const qpol_type_t *type;
+ const qpol_avrule_t *rule;
+ bool used;
+} avrule_node_t;
+
+typedef struct terule_node
+{
+ const qpol_type_t *src;
+ const qpol_type_t *dflt;
+ const qpol_terule_t *rule;
+ bool used;
+} terule_node_t;
+
+/* public data structure definitions */
+struct apol_domain_trans_analysis
+{
+ unsigned char direction;
+ unsigned char valid;
+ char *start_type;
+ char *result;
+ apol_vector_t *access_types;
+ apol_vector_t *access_classes;
+ apol_vector_t *access_perms;
+ regex_t *result_regex;
+};
+
+struct apol_domain_trans_result
+{
+ const qpol_type_t *start_type;
+ const qpol_type_t *ep_type;
+ const qpol_type_t *end_type;
+ apol_vector_t *proc_trans_rules;
+ apol_vector_t *ep_rules;
+ apol_vector_t *exec_rules;
+ apol_vector_t *setexec_rules;
+ apol_vector_t *type_trans_rules;
+ bool valid;
+ /** if access filters used list of rules that satisfy
+ * the filter criteria (of type qpol_avrule_t) */
+ apol_vector_t *access_rules;
+};
+
+/* private functions */
+/* avrule_node */
+static int avrule_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const avrule_node_t *an = a;
+ const avrule_node_t *bn = b;
+ ssize_t retv = (const char *)an->type - (const char *)bn->type;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ retv = (const char *)an->rule - (const char *)bn->rule;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ return 0;
+}
+
+static int avrule_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ avrule_node_t *an = a;
+ if (!a)
+ return -1;
+ an->used = false;
+ return 0;
+}
+
+static avrule_node_t *avrule_node_create(const qpol_type_t * type, const qpol_avrule_t * rule)
+{
+ avrule_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->type = type;
+ n->rule = rule;
+
+ return n;
+}
+
+/* terule_node */
+static int terule_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const terule_node_t *an = a;
+ const terule_node_t *bn = b;
+ ssize_t retv = (const char *)an->src - (const char *)bn->src;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ retv = (const char *)an->dflt - (const char *)bn->dflt;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ retv = (const char *)an->rule - (const char *)bn->rule;
+ if (retv > 0)
+ return 1;
+ else if (retv < 0)
+ return -1;
+ return 0;
+}
+
+static int terule_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ terule_node_t *an = a;
+ if (!a)
+ return -1;
+ an->used = false;
+ return 0;
+}
+
+static terule_node_t *terule_node_create(const qpol_type_t * src, const qpol_type_t * dflt, const qpol_terule_t * rule)
+{
+ terule_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->src = src;
+ n->dflt = dflt;
+ n->rule = rule;
+
+ return n;
+}
+
+/* dom_node */
+static int dom_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const dom_node_t *an = a;
+ const dom_node_t *bn = b;
+
+ if ((const char *)(an->type) < (const char *)(bn->type))
+ return -1;
+ else if ((const char *)(an->type) > (const char *)(bn->type))
+ return 1;
+ return 0;
+}
+
+static void dom_node_free(void *x)
+{
+ if (!x)
+ return;
+ apol_bst_destroy(&(((dom_node_t *) x)->process_transition_tree));
+ apol_bst_destroy(&(((dom_node_t *) x)->entrypoint_tree));
+ apol_vector_destroy(&(((dom_node_t *) x)->setexec_rules));
+ free(x);
+}
+
+static int dom_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ dom_node_t *an = a;
+ if (!a)
+ return -1;
+
+ if (apol_bst_inorder_map(an->process_transition_tree, avrule_node_reset, NULL) < 0)
+ return -1;
+ if (apol_bst_inorder_map(an->entrypoint_tree, avrule_node_reset, NULL) < 0)
+ return -1;
+
+ return 0;
+}
+
+static dom_node_t *dom_node_create(const qpol_type_t * type)
+{
+ dom_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->type = type;
+ if (!(n->process_transition_tree = apol_bst_create(avrule_node_cmp, free)) ||
+ !(n->entrypoint_tree = apol_bst_create(avrule_node_cmp, free)) || !(n->setexec_rules = apol_vector_create(NULL))) {
+ apol_bst_destroy(&n->process_transition_tree);
+ apol_bst_destroy(&n->entrypoint_tree);
+ apol_vector_destroy(&n->setexec_rules);
+ free(n);
+ return NULL;
+ }
+
+ return n;
+}
+
+/* ep_node */
+static int ep_node_cmp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const ep_node_t *an = a;
+ const ep_node_t *bn = b;
+
+ if ((const char *)(an->type) < (const char *)(bn->type))
+ return -1;
+ else if ((const char *)(an->type) > (const char *)(bn->type))
+ return 1;
+ return 0;
+}
+
+static void ep_node_free(void *x)
+{
+ if (!x)
+ return;
+ apol_bst_destroy(&(((ep_node_t *) x)->type_transition_tree));
+ apol_bst_destroy(&(((ep_node_t *) x)->execute_tree));
+ free(x);
+}
+
+static int ep_node_reset(void *a, void *b __attribute__ ((unused)))
+{
+ ep_node_t *an = a;
+ if (!a)
+ return -1;
+
+ if (apol_bst_inorder_map(an->execute_tree, avrule_node_reset, NULL) < 0)
+ return -1;
+ if (apol_bst_inorder_map(an->type_transition_tree, terule_node_reset, NULL) < 0)
+ return -1;
+ return 0;
+}
+
+static ep_node_t *ep_node_create(const qpol_type_t * type)
+{
+ ep_node_t *n = calloc(1, sizeof(*n));
+ if (!n)
+ return NULL;
+
+ n->type = type;
+ if (!(n->execute_tree = apol_bst_create(avrule_node_cmp, free)) ||
+ !(n->type_transition_tree = apol_bst_create(terule_node_cmp, free))) {
+ apol_bst_destroy(&n->execute_tree);
+ apol_bst_destroy(&n->type_transition_tree);
+ free(n);
+ return NULL;
+ }
+
+ return n;
+}
+
+/* table */
+static apol_domain_trans_table_t *apol_domain_trans_table_new(apol_policy_t * policy)
+{
+ apol_domain_trans_table_t *new_table = NULL;
+ int error;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ new_table = (apol_domain_trans_table_t *) calloc(1, sizeof(apol_domain_trans_table_t));
+ if (!new_table) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ if (!(new_table->domain_table = apol_bst_create(dom_node_cmp, dom_node_free))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto cleanup;
+ }
+ if (!(new_table->entrypoint_table = apol_bst_create(ep_node_cmp, ep_node_free))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto cleanup;
+ }
+
+ return new_table;
+ cleanup:
+ domain_trans_table_destroy(&new_table);
+ errno = error;
+ return NULL;
+}
+
+static int table_add_avrule(apol_policy_t * policy, apol_domain_trans_table_t * dta_table, const qpol_avrule_t * rule)
+{
+ qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ const qpol_type_t *src;
+ const qpol_type_t *tgt;
+ qpol_avrule_get_source_type(qp, rule, &src);
+ qpol_avrule_get_target_type(qp, rule, &tgt);
+ apol_vector_t *sources = apol_query_expand_type(policy, src);
+ apol_vector_t *targets = apol_query_expand_type(policy, tgt);
+ bool exec = false, ep = false, proc_trans = false, setexec = false;
+ qpol_iterator_t *iter = NULL;
+ int error = 0;
+ qpol_avrule_get_perm_iter(qp, rule, &iter);
+ if (!iter || !sources || !targets) {
+ error = errno;
+ goto err;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ void *x;
+ qpol_iterator_get_item(iter, &x);
+ char *perm = x;
+ if (!strcmp("execute", perm))
+ exec = true;
+ if (!strcmp("entrypoint", perm))
+ ep = true;
+ if (!strcmp("transition", perm))
+ proc_trans = true;
+ if (!strcmp("setexec", perm))
+ setexec = true;
+ free(x);
+ }
+ qpol_iterator_destroy(&iter);
+
+ if (proc_trans || ep || setexec) {
+ for (size_t i = 0; i < apol_vector_get_size(sources); i++) {
+ dom_node_t *dnode = NULL;
+ dom_node_t dummy = { apol_vector_get_element(sources, i), NULL, NULL, NULL };
+ if (apol_bst_get_element(dta_table->domain_table, &dummy, NULL, (void **)&dnode)) {
+ dom_node_t *new_dnode = NULL;
+ if (!(new_dnode = dom_node_create(dummy.type)) ||
+ apol_bst_insert(dta_table->domain_table, (void *)new_dnode, NULL)) {
+ error = errno;
+ dom_node_free(new_dnode);
+ goto err;
+ }
+ dnode = new_dnode;
+ }
+ if (setexec) {
+ if (apol_vector_append_unique(dnode->setexec_rules, (void *)rule, NULL, NULL)) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (size_t j = 0; j < apol_vector_get_size(targets); j++) {
+ if (proc_trans) {
+ avrule_node_t *new_node =
+ avrule_node_create((const qpol_type_t *)apol_vector_get_element(targets, j), rule);
+ if (!new_node ||
+ apol_bst_insert_and_get(dnode->process_transition_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ if (ep) {
+ avrule_node_t *new_node =
+ avrule_node_create((const qpol_type_t *)apol_vector_get_element(targets, j), rule);
+ if (!new_node ||
+ apol_bst_insert_and_get(dnode->entrypoint_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ }
+ }
+ }
+ if (exec) {
+ for (size_t i = 0; i < apol_vector_get_size(targets); i++) {
+ ep_node_t *enode = NULL;
+ ep_node_t dummy = { apol_vector_get_element(targets, i), NULL, NULL };
+ if (apol_bst_get_element(dta_table->entrypoint_table, &dummy, NULL, (void **)&enode)) {
+ ep_node_t *new_enode = NULL;
+ if (!(new_enode = ep_node_create(dummy.type)) ||
+ apol_bst_insert(dta_table->entrypoint_table, (void *)new_enode, NULL)) {
+ error = errno;
+ ep_node_free(new_enode);
+ goto err;
+ }
+ enode = new_enode;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(sources); j++) {
+ avrule_node_t *new_node =
+ avrule_node_create((const qpol_type_t *)apol_vector_get_element(sources, j), rule);
+ if (!new_node || apol_bst_insert_and_get(enode->execute_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ }
+ }
+
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ return 0;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ errno = error;
+ return -1;
+}
+
+static int table_add_terule(apol_policy_t * policy, apol_domain_trans_table_t * dta_table, const qpol_terule_t * rule)
+{
+ qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ const qpol_type_t *src;
+ const qpol_type_t *tgt;
+ const qpol_type_t *dflt;
+ qpol_terule_get_source_type(qp, rule, &src);
+ qpol_terule_get_target_type(qp, rule, &tgt);
+ qpol_terule_get_default_type(qp, rule, &dflt);
+ apol_vector_t *sources = apol_query_expand_type(policy, src);
+ apol_vector_t *targets = apol_query_expand_type(policy, tgt);
+ int error = 0;
+ for (size_t i = 0; i < apol_vector_get_size(targets); i++) {
+ ep_node_t *enode = NULL;
+ ep_node_t dummy = { apol_vector_get_element(targets, i), NULL, NULL };
+ if (apol_bst_get_element(dta_table->entrypoint_table, &dummy, NULL, (void **)&enode)) {
+ ep_node_t *new_enode = NULL;
+ if (!(new_enode = ep_node_create(dummy.type)) ||
+ apol_bst_insert(dta_table->entrypoint_table, (void *)new_enode, NULL)) {
+ error = errno;
+ ep_node_free(new_enode);
+ goto err;
+ }
+ enode = new_enode;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(sources); j++) {
+ terule_node_t *new_node =
+ terule_node_create((const qpol_type_t *)apol_vector_get_element(sources, j), dflt, rule);
+ if (apol_bst_insert_and_get(enode->type_transition_tree, (void **)&new_node, NULL) < 0) {
+ error = errno;
+ free(new_node);
+ goto err;
+ }
+ }
+ }
+
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ return 0;
+ err:
+ apol_vector_destroy(&sources);
+ apol_vector_destroy(&targets);
+ errno = error;
+ return -1;
+}
+
+/* result */
+apol_domain_trans_result_t *domain_trans_result_create()
+{
+ apol_domain_trans_result_t *res = calloc(1, sizeof(*res));
+ if (!res)
+ return NULL;
+
+ int error = 0;
+ if (!(res->proc_trans_rules = apol_vector_create(NULL)) || !(res->ep_rules = apol_vector_create(NULL)) ||
+ !(res->exec_rules = apol_vector_create(NULL)) || !(res->setexec_rules = apol_vector_create(NULL)) ||
+ !(res->type_trans_rules = apol_vector_create(NULL))) {
+ error = errno;
+ goto err;
+ }
+
+ return res;
+ err:
+ apol_domain_trans_result_destroy(&res);
+ errno = error;
+ return NULL;
+}
+
+/* public functions */
+/* table */
+int apol_policy_build_domain_trans_table(apol_policy_t * policy)
+{
+ int error = 0;
+ apol_avrule_query_t *avq = NULL;
+ apol_terule_query_t *teq = NULL;
+ apol_vector_t *avrules = NULL;
+ apol_vector_t *terules = NULL;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (policy->domain_trans_table) {
+ return 0; /* already built */
+ }
+
+ apol_domain_trans_table_t *dta_table = policy->domain_trans_table = apol_domain_trans_table_new(policy);
+ if (!policy->domain_trans_table) {
+ error = errno;
+ goto err;
+ }
+
+ avq = apol_avrule_query_create();
+ apol_avrule_query_set_rules(policy, avq, QPOL_RULE_ALLOW);
+ apol_avrule_query_append_class(policy, avq, "file");
+ apol_avrule_query_append_class(policy, avq, "process");
+ apol_avrule_query_append_perm(policy, avq, "execute");
+ apol_avrule_query_append_perm(policy, avq, "entrypoint");
+ apol_avrule_query_append_perm(policy, avq, "transition");
+ apol_avrule_query_append_perm(policy, avq, "setexec");
+ if (apol_avrule_get_by_query(policy, avq, &avrules)) {
+ error = errno;
+ goto err;
+ }
+ apol_avrule_query_destroy(&avq);
+ for (size_t i = 0; i < apol_vector_get_size(avrules); i++) {
+ if (table_add_avrule(policy, dta_table, (const qpol_avrule_t *)apol_vector_get_element(avrules, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ apol_vector_destroy(&avrules);
+
+ teq = apol_terule_query_create();
+ apol_terule_query_set_rules(policy, teq, QPOL_RULE_TYPE_TRANS);
+ apol_terule_query_append_class(policy, teq, "process");
+ if (apol_terule_get_by_query(policy, teq, &terules)) {
+ error = errno;
+ goto err;
+ }
+ apol_terule_query_destroy(&teq);
+ for (size_t i = 0; i < apol_vector_get_size(terules); i++) {
+ if (table_add_terule(policy, dta_table, (const qpol_terule_t *)apol_vector_get_element(terules, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ apol_vector_destroy(&terules);
+
+ return 0;
+
+ err:
+ apol_avrule_query_destroy(&avq);
+ apol_vector_destroy(&avrules);
+ apol_terule_query_destroy(&teq);
+ apol_vector_destroy(&terules);
+ domain_trans_table_destroy(&dta_table);
+ policy->domain_trans_table = NULL;
+ errno = error;
+ return -1;
+}
+
+int apol_policy_domain_trans_table_build(apol_policy_t * policy)
+{
+ return apol_policy_build_domain_trans_table(policy);
+}
+
+void domain_trans_table_destroy(apol_domain_trans_table_t ** table)
+{
+ if (!table || !(*table))
+ return;
+
+ apol_bst_destroy(&(*table)->domain_table);
+ apol_bst_destroy(&(*table)->entrypoint_table);
+ free(*table);
+ *table = NULL;
+}
+
+void apol_policy_reset_domain_trans_table(apol_policy_t * policy)
+{
+ if (!policy || !policy->domain_trans_table)
+ return;
+ apol_bst_inorder_map(policy->domain_trans_table->domain_table, dom_node_reset, NULL);
+ apol_bst_inorder_map(policy->domain_trans_table->entrypoint_table, ep_node_reset, NULL);
+ return;
+}
+
+void apol_domain_trans_table_reset(apol_policy_t * policy)
+{
+ apol_policy_reset_domain_trans_table(policy);
+}
+
+/* analysis */
+apol_domain_trans_analysis_t *apol_domain_trans_analysis_create(void)
+{
+ apol_domain_trans_analysis_t *new_dta = NULL;
+ int error = 0;
+
+ if (!(new_dta = calloc(1, sizeof(apol_domain_trans_analysis_t)))) {
+ error = errno;
+ goto err;
+ }
+
+ new_dta->valid = APOL_DOMAIN_TRANS_SEARCH_VALID; /* by default search only valid transitions */
+
+ return new_dta;
+
+ err:
+ apol_domain_trans_analysis_destroy(&new_dta);
+ errno = error;
+ return NULL;
+}
+
+void apol_domain_trans_analysis_destroy(apol_domain_trans_analysis_t ** dta)
+{
+ if (!dta || !(*dta))
+ return;
+
+ free((*dta)->start_type);
+ free((*dta)->result);
+ apol_vector_destroy(&((*dta)->access_types));
+ apol_vector_destroy(&((*dta)->access_classes));
+ apol_vector_destroy(&((*dta)->access_perms));
+ apol_regex_destroy(&((*dta)->result_regex));
+ free(*dta);
+ *dta = NULL;
+}
+
+int apol_domain_trans_analysis_set_direction(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ unsigned char direction)
+{
+ if (!dta || (direction != APOL_DOMAIN_TRANS_DIRECTION_FORWARD && direction != APOL_DOMAIN_TRANS_DIRECTION_REVERSE)) {
+ ERR(policy, "Error setting analysis direction: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ dta->direction = direction;
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_set_valid(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, unsigned char valid)
+{
+ if (!dta || valid & ~(APOL_DOMAIN_TRANS_SEARCH_BOTH)) {
+ ERR(policy, "Error setting analysis validity flag: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ dta->valid = valid;
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_set_start_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *type_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta || !type_name) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(tmp = strdup(type_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ free(dta->start_type);
+ dta->start_type = tmp;
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_set_result_regex(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, const char *regex)
+{
+ if (!dta) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!regex) {
+ apol_regex_destroy(&dta->result_regex);
+ return 0;
+ }
+
+ return apol_query_set(policy, &dta->result, &dta->result_regex, regex);
+}
+
+int apol_domain_trans_analysis_append_access_type(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *type_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta) {
+ ERR(policy, "Error appending type to analysis: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!type_name) {
+ apol_vector_destroy(&dta->access_types);
+ return 0;
+ }
+
+ if (!dta->access_types) {
+ if (!(dta->access_types = apol_vector_create(free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (!(tmp = strdup(type_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (apol_vector_append(dta->access_types, tmp)) {
+ error = errno;
+ free(tmp);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_append_class_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *class_name, const char *perm_name)
+{
+ if (apol_domain_trans_analysis_append_class(policy, dta, class_name))
+ return -1;
+ return apol_domain_trans_analysis_append_perm(policy, dta, perm_name);
+}
+
+int apol_domain_trans_analysis_append_class(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ const char *class_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta) {
+ ERR(policy, "Error appending class to analysis: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!class_name) {
+ apol_vector_destroy(&dta->access_classes);
+ return 0;
+ }
+
+ if (!dta->access_classes) {
+ if (!(dta->access_classes = apol_vector_create(free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (!(tmp = strdup(class_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (apol_vector_append(dta->access_classes, tmp)) {
+ error = errno;
+ free(tmp);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int apol_domain_trans_analysis_append_perm(const apol_policy_t * policy, apol_domain_trans_analysis_t * dta, const char *perm_name)
+{
+ char *tmp = NULL;
+ int error = 0;
+
+ if (!dta) {
+ ERR(policy, "Error appending perm to analysis: %s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!perm_name) {
+ apol_vector_destroy(&dta->access_perms);
+ return 0;
+ }
+
+ if (!dta->access_perms) {
+ if (!(dta->access_perms = apol_vector_create(free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (!(tmp = strdup(perm_name))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (apol_vector_append(dta->access_perms, tmp)) {
+ error = errno;
+ free(tmp);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+static bool requires_setexec_or_type_trans(apol_policy_t * policy)
+{
+ const qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ unsigned int policy_version = 0;
+ qpol_policy_get_policy_version(qp, &policy_version);
+ int is_modular = qpol_policy_has_capability(policy->p, QPOL_CAP_MODULES);
+ return (policy_version >= 15 || is_modular);
+}
+
+struct rule_map_data
+{
+ const qpol_type_t *search;
+ const qpol_type_t *dflt;
+ apol_vector_t *node_list;
+ bool is_avnode;
+};
+
+static int node_list_map_fn(void *node, void *data)
+{
+ struct rule_map_data *rm = data;
+ if (rm->is_avnode) {
+ avrule_node_t *anode = node;
+ if (anode->type == rm->search && !anode->used)
+ if (apol_vector_append(rm->node_list, node))
+ return -1;
+ return 0;
+ } else {
+ terule_node_t *tnode = node;
+ if ((!rm->search || (rm->search == tnode->src)) && (!rm->dflt || (rm->dflt == tnode->dflt)) &&
+ rm->search != rm->dflt && !tnode->used)
+ if (apol_vector_append(rm->node_list, node))
+ return -1;
+ return 0;
+ }
+}
+
+static apol_vector_t *find_avrules_in_node(void *node, unsigned int rule_type, const qpol_type_t * search)
+{
+ int error = 0;
+ apol_vector_t *rule_nodes = apol_vector_create(NULL); //shallow copies only
+ struct rule_map_data data = { search, NULL, rule_nodes, true };
+ switch (rule_type) {
+ case APOL_DOMAIN_TRANS_RULE_PROC_TRANS:
+ {
+ dom_node_t *dnode = node;
+ if (apol_bst_inorder_map(dnode->process_transition_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case APOL_DOMAIN_TRANS_RULE_ENTRYPOINT:
+ {
+ dom_node_t *dnode = node;
+ if (apol_bst_inorder_map(dnode->entrypoint_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case APOL_DOMAIN_TRANS_RULE_EXEC:
+ {
+ ep_node_t *enode = node;
+ if (apol_bst_inorder_map(enode->execute_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ default:
+ {
+ error = EINVAL;
+ goto err;
+ }
+ }
+
+ return rule_nodes;
+
+ err:
+ apol_vector_destroy(&rule_nodes);
+ errno = error;
+ return NULL;
+}
+
+static apol_vector_t *find_terules_in_node(ep_node_t * node, const qpol_type_t * search, const qpol_type_t * dflt)
+{
+ int error = 0;
+ apol_vector_t *rule_nodes = apol_vector_create(NULL); //shallow copies only
+ struct rule_map_data data = { search, dflt, rule_nodes, false };
+ if (apol_bst_inorder_map(node->type_transition_tree, node_list_map_fn, (void *)&data) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ return rule_nodes;
+
+ err:
+ apol_vector_destroy(&rule_nodes);
+ errno = error;
+ return NULL;
+}
+
+static apol_domain_trans_result_t *find_result(apol_vector_t * local_results, const qpol_type_t * src, const qpol_type_t * tgt,
+ const qpol_type_t * dflt)
+{
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->start_type == src && res->end_type == dflt && res->ep_type == tgt)
+ return res;
+ }
+ return NULL;
+}
+
+static int domain_trans_table_find_orphan_type_transitions(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t * local_results)
+{
+ int error = 0;
+ const qpol_type_t *search = NULL;
+ qpol_policy_get_type_by_name(apol_policy_get_qpol(policy), dta->start_type, &search);
+ apol_domain_trans_result_t *tmp_result = NULL;
+ //walk ep table
+ apol_vector_t *epnodes = apol_bst_get_vector(policy->domain_trans_table->entrypoint_table, 0);
+ if (!epnodes)
+ return -1;
+ for (size_t i = 0; i < apol_vector_get_size(epnodes); i++) {
+ ep_node_t *node = apol_vector_get_element(epnodes, i);
+ //find any unused type transitions
+ apol_vector_t *ttnodes = NULL;
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_FORWARD)
+ ttnodes = find_terules_in_node(node, search, NULL);
+ else
+ ttnodes = find_terules_in_node(node, NULL, search);
+ for (size_t j = 0; j < apol_vector_get_size(ttnodes); j++) {
+ bool add = false;
+ terule_node_t *tn = apol_vector_get_element(ttnodes, j);
+ tn->used = true;
+ //if missing an entrypoint rule this transition may have already been added to the results
+ tmp_result = find_result(local_results, tn->src, node->type, tn->dflt);
+ if (!tmp_result) {
+ add = true;
+ tmp_result = domain_trans_result_create();
+ }
+ if (!tmp_result) {
+ error = errno;
+ apol_vector_destroy(&ttnodes);
+ goto err;
+ }
+ tmp_result->start_type = tn->src;
+ tmp_result->end_type = tn->dflt;
+ tmp_result->ep_type = node->type;
+ //check for exec
+ apol_vector_t *execrules =
+ find_avrules_in_node((void *)node, APOL_DOMAIN_TRANS_RULE_EXEC, tmp_result->start_type);
+ for (size_t k = 0; k < apol_vector_get_size(execrules); k++) {
+ avrule_node_t *n = apol_vector_get_element(execrules, k);
+ if (apol_vector_append(tmp_result->exec_rules, (void *)n->rule)) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ if (!add)
+ tmp_result = NULL;
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ //check for proc_trans and setexec
+ dom_node_t dummy = { tmp_result->start_type, NULL, NULL, NULL };
+ dom_node_t *start_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&start_node);
+ if (start_node) {
+ //only copy setexec_rules if a new result will be added
+ if (add && apol_vector_get_size(start_node->setexec_rules)) {
+ if (apol_vector_cat(tmp_result->setexec_rules, start_node->setexec_rules)) {
+ error = errno;
+ goto err;
+ }
+ }
+ //add any unused proc_trans rules
+ apol_vector_t *proc_trans_rules =
+ find_avrules_in_node((void *)start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS,
+ tmp_result->end_type);
+ for (size_t k = 0; k < apol_vector_get_size(proc_trans_rules); k++) {
+ avrule_node_t *avr = apol_vector_get_element(proc_trans_rules, k);
+ if (apol_vector_append(tmp_result->proc_trans_rules, (void *)avr->rule)) {
+ error = errno;
+ if (!add)
+ tmp_result = NULL;
+ apol_vector_destroy(&proc_trans_rules);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&proc_trans_rules);
+ apol_vector_sort_uniquify(tmp_result->proc_trans_rules, NULL, NULL);
+ }
+ if (add) {
+ if (apol_vector_append(local_results, (void *)tmp_result)) {
+ error = errno;
+ goto err;
+ }
+ }
+ tmp_result = NULL;
+ }
+ apol_vector_destroy(&ttnodes);
+ }
+ apol_vector_destroy(&epnodes);
+
+ return 0;
+
+ err:
+ apol_vector_destroy(&epnodes);
+ apol_domain_trans_result_destroy(&tmp_result);
+ errno = error;
+ return -1;
+}
+
+static int domain_trans_table_get_all_forward_trans(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t * local_results, const qpol_type_t * start_type)
+{
+ int error = 0;
+ //create template result this will hold common data for each step and be copied as needed
+ apol_domain_trans_result_t *tmpl_result = domain_trans_result_create();
+ if (!tmpl_result) {
+ error = errno;
+ goto err;
+ }
+ //find start node
+ dom_node_t dummy = { start_type, NULL, NULL, NULL };
+ dom_node_t *start_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&start_node);
+ if (start_node) {
+ tmpl_result->start_type = start_type;
+ //if needed and present record setexec
+ if (requires_setexec_or_type_trans(policy) && apol_vector_get_size(start_node->setexec_rules)) {
+ if (apol_vector_cat(tmpl_result->setexec_rules, start_node->setexec_rules)) {
+ error = errno;
+ goto err;
+ }
+ }
+ //check all proc trans to build list of end types
+ apol_vector_t *proc_trans_rules = apol_bst_get_vector(start_node->process_transition_tree, 0);
+ apol_vector_t *potential_end_types = apol_vector_create(NULL);
+ for (size_t i = 0; i < apol_vector_get_size(proc_trans_rules); i++) {
+ avrule_node_t *ptnode = apol_vector_get_element(proc_trans_rules, i);
+ apol_vector_append(potential_end_types, (void *)ptnode->type);
+ }
+ apol_vector_destroy(&proc_trans_rules);
+ apol_vector_sort_uniquify(potential_end_types, NULL, NULL);
+ //for each end check ep
+ for (size_t i = 0; i < apol_vector_get_size(potential_end_types); i++) {
+ dummy.type = tmpl_result->end_type = apol_vector_get_element(potential_end_types, i);
+ dom_node_t *end_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&end_node);
+ const qpol_type_t *end_type = dummy.type;
+ if (end_type == start_type)
+ continue;
+ //get all proc trans rules for ths end (may be multiple due to attributes)
+ apol_vector_t *ptrules =
+ find_avrules_in_node((void *)start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, end_type);
+ apol_vector_destroy(&tmpl_result->proc_trans_rules);
+ tmpl_result->proc_trans_rules = apol_vector_create(NULL);
+ for (size_t j = 0; j < apol_vector_get_size(ptrules); j++) {
+ avrule_node_t *pt_ent = apol_vector_get_element(ptrules, j);
+ pt_ent->used = true;
+ if (apol_vector_append(tmpl_result->proc_trans_rules, (void *)pt_ent->rule)) {
+ error = errno;
+ apol_vector_destroy(&ptrules);
+ apol_vector_destroy(&potential_end_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&ptrules);
+ apol_vector_sort_uniquify(tmpl_result->proc_trans_rules, NULL, NULL);
+ if (end_node) {
+ //collect potential entrypoint types
+ apol_vector_t *eprules = apol_bst_get_vector(end_node->entrypoint_tree, 0);
+ apol_vector_t *potential_ep_types = apol_vector_create(NULL);
+ if (!eprules || !potential_ep_types) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_end_types);
+ goto err;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(eprules); j++) {
+ avrule_node_t *epr = apol_vector_get_element(eprules, j);
+ if (apol_vector_append(potential_ep_types, (void *)epr->type)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(potential_ep_types, NULL, NULL);
+ //for each ep find exec by start
+ for (size_t j = 0; j < apol_vector_get_size(potential_ep_types); j++) {
+ tmpl_result->ep_type = apol_vector_get_element(potential_ep_types, j);
+ ep_node_t edummy =
+ { (const qpol_type_t *)apol_vector_get_element(potential_ep_types, j), NULL, NULL };
+ ep_node_t *epnode = NULL;
+ apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&edummy, NULL,
+ (void **)&epnode);
+ //get all entrypoint rules for ths end (may be multiple due to attributes)
+ apol_vector_destroy(&tmpl_result->ep_rules);
+ tmpl_result->ep_rules = apol_vector_create(NULL);
+ if (!tmpl_result->ep_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ eprules = find_avrules_in_node((void *)end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT,
+ tmpl_result->ep_type);
+ for (size_t k = 0; k < apol_vector_get_size(eprules); k++) {
+ avrule_node_t *ep_ent = apol_vector_get_element(eprules, k);
+ ep_ent->used = true;
+ if (apol_vector_append(tmpl_result->ep_rules, (void *)ep_ent->rule)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(tmpl_result->ep_rules, NULL, NULL);
+ if (epnode) {
+ //if present find tt
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ apol_vector_t *ttrules = find_terules_in_node(epnode, start_type, end_type);
+ for (size_t l = 0; l < apol_vector_get_size(ttrules); l++) {
+ terule_node_t *tn = apol_vector_get_element(ttrules, l);
+ if (apol_vector_append(tmpl_result->type_trans_rules, (void *)tn->rule)) {
+ error = errno;
+ apol_vector_destroy(&ttrules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&ttrules);
+ apol_vector_sort_uniquify(tmpl_result->type_trans_rules, NULL, NULL);
+ //find execute rules
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->exec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ apol_vector_t *execrules =
+ find_avrules_in_node(epnode, APOL_DOMAIN_TRANS_RULE_EXEC, start_type);
+ if (apol_vector_get_size(execrules)) {
+ for (size_t l = 0; l < apol_vector_get_size(execrules); l++) {
+ avrule_node_t *xnode = apol_vector_get_element(execrules, l);
+ //do not mark xnode as used here; it is valid to re-use it.
+ if (apol_vector_append
+ (tmpl_result->exec_rules, (void *)xnode->rule)) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ apol_vector_sort_uniquify(tmpl_result->exec_rules, NULL, NULL);
+ //found everything possible add a result
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset execute rules
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->exec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset type transition rules
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ } else {
+ //have proc_trans and entrypoint but no execute
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ } else {
+ //have proc_trans and entrypoint but no execute
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ //reset entrypoint rules
+ apol_vector_destroy(&tmpl_result->ep_rules);
+ tmpl_result->ep_rules = apol_vector_create(NULL);
+ if (!tmpl_result->ep_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_end_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&potential_ep_types);
+ } else {
+ //have proc_trans but end has no ep
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ goto err;
+ }
+ }
+ }
+ apol_vector_destroy(&potential_end_types);
+ //validate all
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->start_type && res->ep_type && res->end_type && apol_vector_get_size(res->proc_trans_rules) &&
+ apol_vector_get_size(res->ep_rules) && apol_vector_get_size(res->exec_rules) &&
+ (requires_setexec_or_type_trans(policy)
+ ? (apol_vector_get_size(res->setexec_rules) || apol_vector_get_size(res->type_trans_rules)) : true)) {
+ res->valid = true;
+ }
+ }
+ }
+ //iff looking for invalid find orphan type_transition rules
+ if (dta->valid & APOL_DOMAIN_TRANS_SEARCH_INVALID) {
+ if (domain_trans_table_find_orphan_type_transitions(policy, dta, local_results)) {
+ error = errno;
+ goto err;
+ }
+ }
+ apol_domain_trans_result_destroy(&tmpl_result);
+
+ return 0;
+ err:
+ apol_domain_trans_result_destroy(&tmpl_result);
+ errno = error;
+ return -1;
+}
+
+static int domain_trans_table_get_all_reverse_trans(apol_policy_t * policy, apol_domain_trans_analysis_t * dta,
+ apol_vector_t * local_results, const qpol_type_t * end_type)
+{
+ int error = 0;
+ //create template result this will hold common data for each step and be copied as needed
+ apol_domain_trans_result_t *tmpl_result = domain_trans_result_create();
+ if (!tmpl_result) {
+ error = errno;
+ goto err;
+ }
+ //find end node
+ dom_node_t dummy = { end_type, NULL, NULL, NULL };
+ dom_node_t *end_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL, (void **)&end_node);
+ if (end_node) {
+ tmpl_result->end_type = end_type;
+ //collect potential entrypoint types
+ apol_vector_t *eprules = apol_bst_get_vector(end_node->entrypoint_tree, 0);
+ apol_vector_t *potential_ep_types = apol_vector_create(NULL);
+ if (!eprules || !potential_ep_types) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ goto err;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(eprules); j++) {
+ avrule_node_t *epr = apol_vector_get_element(eprules, j);
+ if (apol_vector_append(potential_ep_types, (void *)epr->type)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(potential_ep_types, NULL, NULL);
+ for (size_t i = 0; i < apol_vector_get_size(potential_ep_types); i++) {
+ tmpl_result->ep_type = apol_vector_get_element(potential_ep_types, i);
+ //get all ep rules for this end (may be multiple due to attributes)
+ eprules = find_avrules_in_node((void *)end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, tmpl_result->ep_type);
+ apol_vector_destroy(&tmpl_result->ep_rules);
+ tmpl_result->ep_rules = apol_vector_create(NULL);
+ for (size_t j = 0; j < apol_vector_get_size(eprules); j++) {
+ avrule_node_t *ep_ent = apol_vector_get_element(eprules, j);
+ ep_ent->used = true;
+ if (apol_vector_append(tmpl_result->ep_rules, (void *)ep_ent->rule)) {
+ error = errno;
+ apol_vector_destroy(&eprules);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&eprules);
+ apol_vector_sort_uniquify(tmpl_result->ep_rules, NULL, NULL);
+ ep_node_t edummy = { tmpl_result->ep_type, NULL, NULL };
+ ep_node_t *epnode = NULL;
+ apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&edummy, NULL, (void **)&epnode);
+ //for each ep find exec rules to generate list of potential start types
+ if (epnode) {
+ apol_vector_t *execrules = apol_bst_get_vector(epnode->execute_tree, 0);
+ apol_vector_t *potential_start_types = apol_vector_create(NULL);
+ if (!execrules || !potential_start_types) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ goto err;
+ }
+ for (size_t k = 0; k < apol_vector_get_size(execrules); k++) {
+ avrule_node_t *n = apol_vector_get_element(execrules, k);
+ if (apol_vector_append(potential_start_types, (void *)n->type)) {
+ error = errno;
+ apol_vector_destroy(&execrules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&execrules);
+ apol_vector_sort_uniquify(potential_start_types, NULL, NULL);
+ for (size_t k = 0; k < apol_vector_get_size(potential_start_types); k++) {
+ tmpl_result->start_type = apol_vector_get_element(potential_start_types, k);
+ //no transition to self
+ if (tmpl_result->end_type == tmpl_result->start_type)
+ continue;
+ //get all execute rule for this start type
+ apol_vector_t *exec_rules =
+ find_avrules_in_node((void *)epnode, APOL_DOMAIN_TRANS_RULE_EXEC,
+ tmpl_result->start_type);
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ for (size_t l = 0; l < apol_vector_get_size(exec_rules); l++) {
+ avrule_node_t *n = apol_vector_get_element(exec_rules, l);
+ n->used = true;
+ if (apol_vector_append(tmpl_result->exec_rules, (void *)n->rule)) {
+ error = errno;
+ apol_vector_destroy(&exec_rules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&exec_rules);
+ apol_vector_sort_uniquify(tmpl_result->exec_rules, NULL, NULL);
+ //check for type transition rules
+ apol_vector_t *ttrules =
+ find_terules_in_node(epnode, tmpl_result->start_type, tmpl_result->end_type);
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&ttrules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ for (size_t l = 0; l < apol_vector_get_size(ttrules); l++) {
+ terule_node_t *n = apol_vector_get_element(ttrules, l);
+ n->used = true;
+ if (apol_vector_append(tmpl_result->type_trans_rules, (void *)n->rule)) {
+ error = errno;
+ apol_vector_destroy(&ttrules);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&ttrules);
+ apol_vector_sort_uniquify(tmpl_result->type_trans_rules, NULL, NULL);
+ dummy.type = tmpl_result->start_type;
+ dom_node_t *start_node = NULL;
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&dummy, NULL,
+ (void **)&start_node);
+ if (start_node) {
+ //for each start check setexec if needed
+ if (requires_setexec_or_type_trans(policy)) {
+ apol_vector_destroy(&tmpl_result->setexec_rules);
+ tmpl_result->setexec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->setexec_rules ||
+ apol_vector_cat(tmpl_result->setexec_rules,
+ start_node->setexec_rules)) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ //for each start find pt
+ apol_vector_destroy(&tmpl_result->proc_trans_rules);
+ tmpl_result->proc_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->proc_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ apol_vector_t *pt_rules = NULL;
+ pt_rules =
+ find_avrules_in_node(start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS,
+ tmpl_result->end_type);
+ if (apol_vector_get_size(pt_rules)) {
+ for (size_t l = 0; l < apol_vector_get_size(pt_rules); l++) {
+ avrule_node_t *n = apol_vector_get_element(pt_rules, l);
+ apol_vector_append(tmpl_result->proc_trans_rules, (void *)n->rule);
+ }
+ apol_vector_destroy(&pt_rules);
+ apol_vector_sort_uniquify(tmpl_result->proc_trans_rules, NULL, NULL);
+ // have all possible rules add this entry
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset process transition rules
+ apol_vector_destroy(&tmpl_result->proc_trans_rules);
+ tmpl_result->proc_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->proc_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset setexec rules
+ apol_vector_destroy(&tmpl_result->setexec_rules);
+ tmpl_result->setexec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->setexec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ } else {
+ //have entrypoint and execute rules but no process transition rule
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result
+ (tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ apol_vector_destroy(&pt_rules);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&pt_rules);
+ } else {
+ //have entrypoint and execute rules but no process transition rule
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ //reset execute rules
+ apol_vector_destroy(&tmpl_result->exec_rules);
+ tmpl_result->exec_rules = apol_vector_create(NULL);
+ if (!tmpl_result->exec_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ //reset type transition rules
+ apol_vector_destroy(&tmpl_result->type_trans_rules);
+ tmpl_result->type_trans_rules = apol_vector_create(NULL);
+ if (!tmpl_result->type_trans_rules) {
+ error = errno;
+ apol_vector_destroy(&potential_start_types);
+ apol_vector_destroy(&potential_ep_types);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&potential_start_types);
+ } else {
+ //have entrypoint but no exec
+ apol_domain_trans_result_t *tmp =
+ apol_domain_trans_result_create_from_domain_trans_result(tmpl_result);
+ if (!tmp || apol_vector_append(local_results, (void *)tmp)) {
+ error = errno;
+ apol_domain_trans_result_destroy(&tmp);
+ goto err;
+ }
+ }
+ }
+ apol_vector_destroy(&potential_ep_types);
+
+ //validate all
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->start_type && res->ep_type && res->end_type && apol_vector_get_size(res->proc_trans_rules) &&
+ apol_vector_get_size(res->ep_rules) && apol_vector_get_size(res->exec_rules) &&
+ (requires_setexec_or_type_trans(policy)
+ ? (apol_vector_get_size(res->setexec_rules) || apol_vector_get_size(res->type_trans_rules)) : true)) {
+ res->valid = true;
+ }
+ }
+ }
+ //iff looking for invalid find orphan type_transition rules
+ if (dta->valid & APOL_DOMAIN_TRANS_SEARCH_INVALID) {
+ if (domain_trans_table_find_orphan_type_transitions(policy, dta, local_results)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ apol_domain_trans_result_destroy(&tmpl_result);
+ return 0;
+
+ err:
+ apol_domain_trans_result_destroy(&tmpl_result);
+ errno = error;
+ return -1;
+}
+
+int apol_domain_trans_analysis_do(apol_policy_t * policy, apol_domain_trans_analysis_t * dta, apol_vector_t ** results)
+{
+ apol_vector_t *local_results = NULL;
+ apol_avrule_query_t *accessq = NULL;
+ int error = 0;
+ if (!results)
+ *results = NULL;
+ if (!policy || !dta || !results) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* build table if not already present */
+ if (!(policy->domain_trans_table)) {
+ if (apol_policy_build_domain_trans_table(policy))
+ return -1; /* errors already reported by build function */
+ }
+
+ /* validate analysis options */
+ if (dta->direction == 0 || dta->valid & ~(APOL_DOMAIN_TRANS_SEARCH_BOTH) || !(dta->start_type)) {
+ error = EINVAL;
+ ERR(policy, "%s", strerror(EINVAL));
+ goto err;
+ }
+ size_t num_atypes = apol_vector_get_size(dta->access_types);
+ size_t num_aclasses = apol_vector_get_size(dta->access_classes);
+ size_t num_aprems = apol_vector_get_size(dta->access_perms);
+ if ((num_atypes == 0 && (num_aclasses != 0 || num_aprems != 0)) ||
+ (num_aclasses == 0 && (num_atypes != 0 || num_aprems != 0)) ||
+ (num_aprems == 0 && (num_aclasses != 0 || num_atypes != 0))) {
+ error = EINVAL;
+ ERR(policy, "%s", strerror(EINVAL));
+ goto err;
+ }
+
+ /* get starting type */
+ const qpol_type_t *start_type = NULL;
+ if (qpol_policy_get_type_by_name(policy->p, dta->start_type, &start_type)) {
+ error = errno;
+ ERR(policy, "Unable to perform analysis: Invalid starting type %s", dta->start_type);
+ goto err;
+ }
+ unsigned char isattr = 0;
+ qpol_type_get_isattr(policy->p, start_type, &isattr);
+ if (isattr) {
+ ERR(policy, "%s", "Attributes are not valid here.");
+ error = EINVAL;
+ goto err;
+ }
+
+ local_results = apol_vector_create(domain_trans_result_free);
+ /* get all transitions for the requested direction */
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_REVERSE) {
+ if (domain_trans_table_get_all_reverse_trans(policy, dta, local_results, start_type)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ if (domain_trans_table_get_all_forward_trans(policy, dta, local_results, start_type)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ /* if requested, filter by validity */
+ if (dta->valid != APOL_DOMAIN_TRANS_SEARCH_BOTH) {
+ for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (res->valid != (dta->valid == APOL_DOMAIN_TRANS_SEARCH_VALID)) {
+ apol_vector_remove(local_results, i);
+ domain_trans_result_free(res);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ /* if filtering by result type, do that now */
+ if (dta->result) {
+ for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) {
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ const qpol_type_t *type = NULL;
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_REVERSE) {
+ type = res->start_type;
+ } else {
+ type = res->end_type;
+ }
+ int compval = apol_compare_type(policy, type, dta->result, APOL_QUERY_REGEX, &dta->result_regex);
+ if (compval < 0) {
+ error = errno;
+ goto err;
+ } else if (compval > 0) {
+ i++;
+ } else {
+ apol_vector_remove(local_results, i);
+ domain_trans_result_free(res);
+ }
+ }
+ }
+
+ /* finally do access filtering */
+ if (dta->direction == APOL_DOMAIN_TRANS_DIRECTION_FORWARD && num_atypes && num_aclasses && num_aprems) {
+ accessq = apol_avrule_query_create();
+ apol_avrule_query_set_rules(policy, accessq, QPOL_RULE_ALLOW);
+ for (size_t i = 0; i < num_aclasses; i++) {
+ if (apol_avrule_query_append_class
+ (policy, accessq, (char *)apol_vector_get_element(dta->access_classes, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (size_t i = 0; i < num_aprems; i++) {
+ if (apol_avrule_query_append_perm(policy, accessq, (char *)apol_vector_get_element(dta->access_perms, i))) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (size_t i = 0; i < apol_vector_get_size(local_results); /* increment later */ ) {
+ const char *end_name = NULL;
+ apol_domain_trans_result_t *res = apol_vector_get_element(local_results, i);
+ if (qpol_type_get_name(apol_policy_get_qpol(policy), res->end_type, &end_name) ||
+ apol_avrule_query_set_source(policy, accessq, end_name, 1)) {
+ error = errno;
+ goto err;
+ }
+ apol_vector_t *tmp_access = apol_vector_create(NULL);
+ for (size_t j = 0; j < num_atypes; j++) {
+ if (apol_avrule_query_set_target
+ (policy, accessq, (char *)apol_vector_get_element(dta->access_types, j), 1)) {
+ error = errno;
+ apol_vector_destroy(&tmp_access);
+ goto err;
+ }
+ apol_vector_t *cur_tgt_v = NULL;
+ apol_avrule_get_by_query(policy, accessq, &cur_tgt_v);
+ apol_vector_cat(tmp_access, cur_tgt_v);
+ apol_vector_destroy(&cur_tgt_v);
+ }
+ if (apol_vector_get_size(tmp_access)) {
+ res->access_rules = tmp_access;
+ tmp_access = NULL;
+ i++;
+ } else {
+ apol_vector_remove(local_results, i);
+ domain_trans_result_free(res);
+ }
+ apol_vector_destroy(&tmp_access);
+ }
+ apol_avrule_query_destroy(&accessq);
+ }
+
+ *results = apol_vector_create(domain_trans_result_free);
+ if (!(*results)) {
+ error = errno;
+ goto err;
+ }
+ for (size_t i = 0; i < apol_vector_get_size(local_results); i++) {
+ apol_domain_trans_result_t *res =
+ apol_domain_trans_result_create_from_domain_trans_result((apol_domain_trans_result_t *)
+ apol_vector_get_element(local_results, i));
+ if (!res || apol_vector_append(*results, (void *)res)) {
+ error = errno;
+ domain_trans_result_free(res);
+ goto err;
+ }
+ }
+ apol_vector_destroy(&local_results);
+
+ return 0;
+ err:
+ apol_vector_destroy(&local_results);
+ apol_vector_destroy(results);
+ apol_avrule_query_destroy(&accessq);
+ errno = error;
+ return -1;
+}
+
+/* result */
+
+const qpol_type_t *apol_domain_trans_result_get_start_type(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->start_type;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const qpol_type_t *apol_domain_trans_result_get_entrypoint_type(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->ep_type;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const qpol_type_t *apol_domain_trans_result_get_end_type(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->end_type;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_proc_trans_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->proc_trans_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_entrypoint_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->ep_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_exec_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->exec_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_setexec_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->setexec_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_type_trans_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->type_trans_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+int apol_domain_trans_result_is_trans_valid(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->valid;
+ } else {
+ errno = EINVAL;
+ return 0;
+ }
+}
+
+const apol_vector_t *apol_domain_trans_result_get_access_rules(const apol_domain_trans_result_t * dtr)
+{
+ if (dtr) {
+ return dtr->access_rules;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+int apol_domain_trans_table_verify_trans(apol_policy_t * policy, const qpol_type_t * start_dom, const qpol_type_t * ep_type,
+ const qpol_type_t * end_dom)
+{
+ int missing_rules = 0;
+
+ if (!policy || !policy->domain_trans_table) {
+ errno = EINVAL;
+ return -1;
+ }
+ //reset the table
+ apol_policy_reset_domain_trans_table(policy);
+ //find nodes for each type
+ dom_node_t start_dummy = { start_dom, NULL, NULL, NULL };
+ dom_node_t *start_node = NULL;
+ if (start_dom)
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&start_dummy, NULL, (void **)&start_node);
+ ep_node_t ep_dummy = { ep_type, NULL, NULL };
+ ep_node_t *ep_node = NULL;
+ if (ep_type)
+ apol_bst_get_element(policy->domain_trans_table->entrypoint_table, (void *)&ep_dummy, NULL, (void **)&ep_node);
+ dom_node_t end_dummy = { end_dom, NULL, NULL, NULL };
+ dom_node_t *end_node = NULL;
+ if (end_dom)
+ apol_bst_get_element(policy->domain_trans_table->domain_table, (void *)&end_dummy, NULL, (void **)&end_node);
+
+ bool tt = false, sx = false, ex = false, pt = false, ep = false;
+
+ //find process transition rule
+ if (start_node && end_dom) {
+ apol_vector_t *v = find_avrules_in_node(start_node, APOL_DOMAIN_TRANS_RULE_PROC_TRANS, end_dom);
+ if (apol_vector_get_size(v))
+ pt = true;
+ apol_vector_destroy(&v);
+ }
+ //find execute rule
+ if (start_dom && ep_node) {
+ apol_vector_t *v = find_avrules_in_node(ep_node, APOL_DOMAIN_TRANS_RULE_EXEC, start_dom);
+ if (apol_vector_get_size(v))
+ ex = true;
+ apol_vector_destroy(&v);
+ }
+ //find entrypoint rules
+ if (end_node && ep_type) {
+ apol_vector_t *v = find_avrules_in_node(end_node, APOL_DOMAIN_TRANS_RULE_ENTRYPOINT, ep_type);
+ if (apol_vector_get_size(v))
+ ep = true;
+ apol_vector_destroy(&v);
+ }
+ if (requires_setexec_or_type_trans(policy)) {
+ //find setexec rule
+ if (start_node)
+ if (apol_vector_get_size(start_node->setexec_rules))
+ sx = true;
+ //find type_transition rule
+ if (ep_node && start_dom && end_dom) {
+ apol_vector_t *v = find_terules_in_node(ep_node, start_dom, end_dom);
+ if (apol_vector_get_size(v)) {
+ tt = true;
+ }
+ apol_vector_destroy(&v);
+ }
+ } else {
+ //old policy version - pretend these exist
+ tt = sx = true;
+ }
+
+ if (!(pt && ep && ex && (tt || sx))) {
+ if (!pt)
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_PROC_TRANS;
+ if (!ep)
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_ENTRYPOINT;
+ if (!ex)
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_EXEC;
+ if (!tt && !sx) {
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_SETEXEC;
+ //do not report type_transition as missing if there is one for another entrypoint as this would be invalid
+ const char *start_name = NULL, *end_name = NULL;
+ qpol_type_get_name(apol_policy_get_qpol(policy), start_dom, &start_name);
+ qpol_type_get_name(apol_policy_get_qpol(policy), end_dom, &end_name);
+ apol_terule_query_t *tq = NULL;
+ if (!start_name || !end_name || !(tq = apol_terule_query_create())) {
+ return -1;
+ }
+ apol_terule_query_set_rules(policy, tq, QPOL_RULE_TYPE_TRANS);
+ apol_terule_query_set_source(policy, tq, start_name, 1);
+ apol_terule_query_set_default(policy, tq, end_name);
+ apol_vector_t *v = NULL;
+ if (apol_terule_get_by_query(policy, tq, &v)) {
+ apol_terule_query_destroy(&tq);
+ return -1;
+ }
+ apol_terule_query_destroy(&tq);
+ if (!apol_vector_get_size(v))
+ missing_rules |= APOL_DOMAIN_TRANS_RULE_TYPE_TRANS;
+ apol_vector_destroy(&v);
+ }
+ }
+
+ return missing_rules;
+}
+
+apol_domain_trans_result_t *apol_domain_trans_result_create_from_domain_trans_result(const apol_domain_trans_result_t * result)
+{
+ apol_domain_trans_result_t *new_r = NULL;
+ int retval = -1;
+ if ((new_r = calloc(1, sizeof(*new_r))) == NULL) {
+ goto cleanup;
+ }
+ if (result->proc_trans_rules != NULL &&
+ (new_r->proc_trans_rules = apol_vector_create_from_vector(result->proc_trans_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->ep_rules != NULL
+ && (new_r->ep_rules = apol_vector_create_from_vector(result->ep_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->exec_rules != NULL
+ && (new_r->exec_rules = apol_vector_create_from_vector(result->exec_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->setexec_rules != NULL
+ && (new_r->setexec_rules = apol_vector_create_from_vector(result->setexec_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->type_trans_rules != NULL &&
+ (new_r->type_trans_rules = apol_vector_create_from_vector(result->type_trans_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ if (result->access_rules != NULL
+ && (new_r->access_rules = apol_vector_create_from_vector(result->access_rules, NULL, NULL, NULL)) == NULL) {
+ goto cleanup;
+ }
+ new_r->start_type = result->start_type;
+ new_r->ep_type = result->ep_type;
+ new_r->end_type = result->end_type;
+ new_r->valid = result->valid;
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ domain_trans_result_free(new_r);
+ return NULL;
+ }
+ return new_r;
+}
+
+/******************** protected functions ********************/
+
+void domain_trans_result_free(void *dtr)
+{
+ apol_domain_trans_result_t *res = (apol_domain_trans_result_t *) dtr;
+
+ if (!res)
+ return;
+
+ apol_vector_destroy(&res->proc_trans_rules);
+ apol_vector_destroy(&res->ep_rules);
+ apol_vector_destroy(&res->exec_rules);
+ apol_vector_destroy(&res->setexec_rules);
+ apol_vector_destroy(&res->type_trans_rules);
+ apol_vector_destroy(&res->access_rules);
+ free(res);
+}
+
+void apol_domain_trans_result_destroy(apol_domain_trans_result_t ** res)
+{
+ if (!res || !(*res))
+ return;
+ domain_trans_result_free((void *)*res);
+ *res = NULL;
+}
diff --git a/libapol/src/fscon-query.c b/libapol/src/fscon-query.c
new file mode 100644
index 0000000..be3c7d3
--- /dev/null
+++ b/libapol/src/fscon-query.c
@@ -0,0 +1,437 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about genfscons and
+ * fs_use statements within a policy. The caller obtains a query
+ * object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <apol/render.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+struct apol_genfscon_query
+{
+ char *fs, *path;
+ uint32_t objclass;
+ bool objclass_set;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+struct apol_fs_use_query
+{
+ char *fs;
+ uint32_t behavior;
+ bool behavior_set;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+/******************** genfscon queries ********************/
+
+int apol_genfscon_get_by_query(const apol_policy_t * p, const apol_genfscon_query_t * g, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ qpol_genfscon_t *genfscon = NULL;
+ *v = NULL;
+ if (qpol_policy_get_genfscon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&genfscon) < 0) {
+ goto cleanup;
+ }
+ if (g != NULL) {
+ const char *fs, *path;
+ uint32_t objclass;
+ const qpol_context_t *context;
+ if (qpol_genfscon_get_name(p->p, genfscon, &fs) < 0 ||
+ qpol_genfscon_get_path(p->p, genfscon, &path) < 0 ||
+ qpol_genfscon_get_class(p->p, genfscon, &objclass) < 0 ||
+ qpol_genfscon_get_context(p->p, genfscon, &context) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, fs, g->fs, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(genfscon);
+ continue;
+ }
+ retval2 = apol_compare(p, path, g->path, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(genfscon);
+ continue;
+ }
+ if (g->objclass_set && g->objclass != objclass) {
+ free(genfscon);
+ continue;
+ }
+ retval2 = apol_compare_context(p, context, g->context, g->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(genfscon);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, genfscon)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ free(genfscon);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_genfscon_query_t *apol_genfscon_query_create(void)
+{
+ apol_genfscon_query_t *g = calloc(1, sizeof(apol_genfscon_query_t));
+ if (g != NULL) {
+ g->objclass = -1;
+ }
+ return g;
+}
+
+void apol_genfscon_query_destroy(apol_genfscon_query_t ** g)
+{
+ if (*g != NULL) {
+ free((*g)->fs);
+ free((*g)->path);
+ apol_context_destroy(&((*g)->context));
+ free(*g);
+ *g = NULL;
+ }
+}
+
+int apol_genfscon_query_set_filesystem(const apol_policy_t * p, apol_genfscon_query_t * g, const char *fs)
+{
+ return apol_query_set(p, &g->fs, NULL, fs);
+}
+
+int apol_genfscon_query_set_path(const apol_policy_t * p, apol_genfscon_query_t * g, const char *path)
+{
+ int tmp = apol_query_set(p, &g->path, NULL, path);
+ if (!tmp && g->path) {
+ if (strlen(g->path) > 1 && g->path[strlen(g->path) - 1] == '/')
+ g->path[strlen(g->path) - 1] = 0;
+ }
+ return tmp;
+}
+
+int apol_genfscon_query_set_objclass(const apol_policy_t * p, apol_genfscon_query_t * g, int objclass)
+{
+ if (objclass < 0) {
+ g->objclass = 0;
+ g->objclass_set = false;
+ } else {
+ switch (objclass) {
+ case QPOL_CLASS_BLK_FILE:
+ case QPOL_CLASS_CHR_FILE:
+ case QPOL_CLASS_DIR:
+ case QPOL_CLASS_FIFO_FILE:
+ case QPOL_CLASS_FILE:
+ case QPOL_CLASS_LNK_FILE:
+ case QPOL_CLASS_SOCK_FILE:
+ case QPOL_CLASS_ALL:
+ {
+ g->objclass = objclass;
+ g->objclass_set = true;
+ break;
+ }
+ default:
+ ERR(p, "%s", "Invalid object class given.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_genfscon_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_genfscon_query_t * g, apol_context_t * context, unsigned int range_match)
+{
+ if (g->context != NULL) {
+ apol_context_destroy(&g->context);
+ }
+ g->context = context;
+ g->flags = (g->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_genfscon_render(const apol_policy_t * p, const qpol_genfscon_t * genfscon)
+{
+ char *line = NULL, *retval = NULL;
+ const qpol_context_t *ctxt = NULL;
+ char *context_str = NULL;
+ const char *type_str = NULL;
+ const char *name = NULL, *path = NULL;
+ uint32_t fclass;
+
+ if (!genfscon || !p)
+ goto cleanup;
+
+ if (qpol_genfscon_get_name(p->p, genfscon, &name))
+ goto cleanup;
+ if (qpol_genfscon_get_path(p->p, genfscon, &path))
+ goto cleanup;
+ if (qpol_genfscon_get_class(p->p, genfscon, &fclass))
+ return NULL;
+ if (qpol_genfscon_get_context(p->p, genfscon, &ctxt))
+ goto cleanup;
+
+ switch (fclass) {
+ case QPOL_CLASS_DIR:
+ type_str = " -d ";
+ break;
+ case QPOL_CLASS_CHR_FILE:
+ type_str = " -c ";
+ break;
+ case QPOL_CLASS_BLK_FILE:
+ type_str = " -b ";
+ break;
+ case QPOL_CLASS_FILE:
+ type_str = " -- ";
+ break;
+ case QPOL_CLASS_FIFO_FILE:
+ type_str = " -p ";
+ break;
+ case QPOL_CLASS_LNK_FILE:
+ type_str = " -l ";
+ break;
+ case QPOL_CLASS_SOCK_FILE:
+ type_str = " -s ";
+ break;
+ case QPOL_CLASS_ALL:
+ type_str = " ";
+ break;
+ default:
+ goto cleanup;
+ break;
+ }
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str)
+ goto cleanup;
+
+ if (asprintf(&line, "genfscon %s %s %s %s", name, path, type_str, context_str) < 0) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ retval = line;
+ cleanup:
+ free(context_str);
+ if (retval != line) {
+ free(line);
+ }
+ return retval;
+}
+
+/******************** fs_use queries ********************/
+
+int apol_fs_use_get_by_query(const apol_policy_t * p, const apol_fs_use_query_t * f, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ const qpol_fs_use_t *fs_use = NULL;
+ *v = NULL;
+ if (qpol_policy_get_fs_use_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&fs_use) < 0) {
+ goto cleanup;
+ }
+ if (f != NULL) {
+ const char *fs;
+ uint32_t behavior;
+ const qpol_context_t *context = NULL;
+ if (qpol_fs_use_get_name(p->p, fs_use, &fs) < 0 || qpol_fs_use_get_behavior(p->p, fs_use, &behavior) < 0) {
+ goto cleanup;
+ }
+ if (behavior != QPOL_FS_USE_PSID && qpol_fs_use_get_context(p->p, fs_use, &context) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, fs, f->fs, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ if (f->behavior_set && f->behavior != behavior) {
+ continue;
+ }
+ /* recall that fs_use_psid statements do not
+ * have contexts */
+ if (f->context != NULL && behavior == QPOL_FS_USE_PSID) {
+ retval2 = 0;
+ } else {
+ retval2 = apol_compare_context(p, context, f->context, f->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ }
+ }
+ if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)fs_use)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_fs_use_query_t *apol_fs_use_query_create(void)
+{
+ apol_fs_use_query_t *f = calloc(1, sizeof(apol_fs_use_query_t));
+ if (f != NULL) {
+ f->behavior = -1;
+ }
+ return f;
+}
+
+void apol_fs_use_query_destroy(apol_fs_use_query_t ** f)
+{
+ if (*f != NULL) {
+ free((*f)->fs);
+ apol_context_destroy(&((*f)->context));
+ free(*f);
+ *f = NULL;
+ }
+}
+
+int apol_fs_use_query_set_filesystem(const apol_policy_t * p, apol_fs_use_query_t * f, const char *fs)
+{
+ return apol_query_set(p, &f->fs, NULL, fs);
+}
+
+int apol_fs_use_query_set_behavior(const apol_policy_t * p, apol_fs_use_query_t * f, int behavior)
+{
+ if (behavior < 0) {
+ f->behavior = 0;
+ f->behavior_set = false;
+ } else {
+ switch (behavior) {
+ case QPOL_FS_USE_XATTR:
+ case QPOL_FS_USE_TASK:
+ case QPOL_FS_USE_TRANS:
+ case QPOL_FS_USE_GENFS:
+ case QPOL_FS_USE_NONE:
+ case QPOL_FS_USE_PSID:
+ {
+ f->behavior = behavior;
+ f->behavior_set = true;
+ break;
+ }
+ default:
+ ERR(p, "%s", "Invalid fs_use behavior given.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_fs_use_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_fs_use_query_t * f, apol_context_t * context, unsigned int range_match)
+{
+ if (f->context != NULL) {
+ apol_context_destroy(&f->context);
+ }
+ f->context = context;
+ f->flags = (f->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_fs_use_render(const apol_policy_t * p, const qpol_fs_use_t * fsuse)
+{
+ char *context_str = NULL;
+ char *line = NULL, *retval = NULL;
+ const char *behavior_str = NULL;
+ const char *fsname = NULL;
+ const qpol_context_t *ctxt = NULL;
+ uint32_t behavior;
+
+ if (qpol_fs_use_get_behavior(p->p, fsuse, &behavior))
+ goto cleanup;
+ if ((behavior_str = apol_fs_use_behavior_to_str(behavior)) == NULL) {
+ ERR(p, "%s", "Could not get behavior string.");
+ goto cleanup;
+ }
+
+ if (qpol_fs_use_get_name(p->p, fsuse, &fsname))
+ goto cleanup;
+
+ if (behavior == QPOL_FS_USE_PSID) {
+ context_str = strdup("");
+ } else {
+ if (qpol_fs_use_get_context(p->p, fsuse, &ctxt))
+ goto cleanup;
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str) {
+ goto cleanup;
+ }
+ }
+ if (asprintf(&line, "%s %s %s", behavior_str, fsname, context_str) < 0) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ retval = line;
+ cleanup:
+ free(context_str);
+ if (retval != line) {
+ free(line);
+ }
+ return retval;
+}
diff --git a/libapol/src/infoflow-analysis-internal.h b/libapol/src/infoflow-analysis-internal.h
new file mode 100644
index 0000000..61fdd85
--- /dev/null
+++ b/libapol/src/infoflow-analysis-internal.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * Protected routines for information flow analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_INFOFLOW_ANALYSIS_INTERNAL_H
+#define APOL_INFOFLOW_ANALYSIS_INTERNAL_H
+
+/**
+ * Do a deep copy (i.e., a clone) of an apol_infoflow_result_t object.
+ * The caller is responsible for calling apol_infoflow_result_free()
+ * upon the returned value.
+ *
+ * @param result Pointer to an infoflow result structure to destroy.
+ *
+ * @return A clone of the passed in result node, or NULL upon error.
+ */
+extern apol_infoflow_result_t *infoflow_result_create_from_infoflow_result(const apol_infoflow_result_t * result);
+
+/**
+ * Free all memory associated with an information flow analysis
+ * result, including the pointer itself. This function does nothing
+ * if the result is already NULL.
+ *
+ * @param result Pointer to an infoflow result structure to destroy.
+ */
+extern void infoflow_result_free(void *result);
+
+#endif
diff --git a/libapol/src/infoflow-analysis.c b/libapol/src/infoflow-analysis.c
new file mode 100644
index 0000000..dad9a34
--- /dev/null
+++ b/libapol/src/infoflow-analysis.c
@@ -0,0 +1,2245 @@
+/**
+ * @file
+ * Implementation of the information flow analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include "infoflow-analysis-internal.h"
+#include "queue.h"
+#include <apol/bst.h>
+#include <apol/perm-map.h>
+
+#include <assert.h>
+#include <config.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+
+/*
+ * Nodes in the graph represent either a type used in the source
+ * of an allow rule or the target: these defines are used to
+ * represent which.
+ */
+#define APOL_INFOFLOW_NODE_SOURCE 0x1
+#define APOL_INFOFLOW_NODE_TARGET 0x2
+
+/*
+ * These defines are used to color nodes in the graph algorithms.
+ */
+#define APOL_INFOFLOW_COLOR_WHITE 0
+#define APOL_INFOFLOW_COLOR_GREY 1
+#define APOL_INFOFLOW_COLOR_BLACK 2
+#define APOL_INFOFLOW_COLOR_RED 3
+
+typedef struct apol_infoflow_node apol_infoflow_node_t;
+typedef struct apol_infoflow_edge apol_infoflow_edge_t;
+
+struct apol_infoflow_graph
+{
+ /** vector of apol_infoflow_node_t */
+ apol_vector_t *nodes;
+ /** vector of apol_infoflow_edge_t */
+ apol_vector_t *edges;
+ /** temporary BST of apol_infoflow_node_t used while building
+ * the graph */
+ apol_bst_t *nodes_bst;
+
+ unsigned int mode, direction;
+ regex_t *regex;
+
+ /** vector of apol_infoflow_node_t, used for random restarts
+ * for further transitive analysis */
+ apol_vector_t *further_start;
+ /** vector of apol_infoflow_node_t of targets, used for
+ * further transitive analysis */
+ apol_vector_t *further_end;
+ size_t current_start;
+#ifdef HAVE_RAND_R
+ unsigned int seed;
+#endif
+};
+
+struct apol_infoflow_node
+{
+ const qpol_type_t *type;
+ /** one of APOL_INFOFLOW_NODE_SOURCE or APOL_INFOFLOW_NODE_TARGET */
+ int node_type;
+ /** vector of apol_infoflow_edge_t, pointing into the graph */
+ apol_vector_t *in_edges;
+ /** vector of apol_infoflow_edge_t, pointing into the graph */
+ apol_vector_t *out_edges;
+ unsigned char color;
+ apol_infoflow_node_t *parent;
+ int distance;
+};
+
+struct apol_infoflow_edge
+{
+ /** vector of qpol_avrule_t, pointing into the policy */
+ apol_vector_t *rules;
+ /** pointer into a node within the graph */
+ apol_infoflow_node_t *start_node;
+ /** pointer into a node within the graph */
+ apol_infoflow_node_t *end_node;
+ int length;
+};
+
+/**
+ * apol_infoflow_analysis_h encapsulates all of the paramaters of a
+ * query. It should always be allocated with
+ * apol_infoflow_analysis_create() and deallocated with
+ * apol_infoflow_analysis_destroy(). Limiting by ending_types,
+ * obj_classes, intermed types, obj_class permissions is optional - if
+ * the vector is empty then no limiting is done.
+ *
+ * All of the vectors except end_types should contain the items that
+ * you want to not appear in the results. end_types lists the types
+ * that you do want to appear.
+ */
+struct apol_infoflow_analysis
+{
+ unsigned int mode, direction;
+ char *type, *result;
+ apol_vector_t *intermed, *class_perms;
+ int min_weight;
+};
+
+/**
+ * The results of running an infoflow, either direct or transitive, is
+ * a path from start_type to end_type. The path consists of a vector
+ * of intermediate steps.
+ */
+struct apol_infoflow_result
+{
+ const qpol_type_t *start_type, *end_type;
+ /** vector of apol_infoflow_step_t */
+ apol_vector_t *steps;
+ unsigned int direction;
+ unsigned int length;
+};
+
+/**
+ * Each result consists of multiple steps, representing the steps
+ * taken from the original start to end types. Along each step there
+ * is a vector of rules. For a direct infoflow analysis there will be
+ * exactly one step, and that flow's start type is the same as the
+ * original result's start_type. Likewise the end_types will be the
+ * same.
+ */
+struct apol_infoflow_step
+{
+ const qpol_type_t *start_type, *end_type;
+ /** vector of qpol_avrule_t */
+ apol_vector_t *rules;
+ int weight;
+};
+
+/**
+ * Deallocate all space associated with an apol_infoflow_step_t,
+ * including the pointer itself. Does nothing if the pointer is
+ * already NULL.
+ *
+ * @param step Infoflow step to free.
+ */
+static void apol_infoflow_step_free(void *step)
+{
+ if (step != NULL) {
+ apol_infoflow_step_t *s = (apol_infoflow_step_t *) step;
+ apol_vector_destroy(&s->rules);
+ free(s);
+ }
+}
+
+/******************** random number routines ********************/
+
+/**
+ * Initialize the pseudo-random number generator to be used during
+ * further transitive analysis.
+ *
+ * @param g Transitive infoflow graph.
+ */
+static void apol_infoflow_srand(apol_infoflow_graph_t * g)
+{
+#ifdef HAVE_RAND_R
+ g->seed = (int)time(NULL);
+#else
+ srand((int)time(NULL));
+#endif
+}
+
+/**
+ * Return a pseudo-random integer between 0 and RAND_MAX, for use
+ * during further transitive analysis. If the system supports it,
+ * this function will use rand_r() so that this library remains
+ * reentrant and thread-safe.
+ *
+ * @param g Transitive infoflow graph.
+ *
+ * @return Integer between 0 and RAND_MAX.
+ */
+static int apol_infoflow_rand(apol_infoflow_graph_t * g)
+{
+#ifdef HAVE_RAND_R
+ return rand_r(&g->seed);
+#else
+ return rand();
+#endif
+}
+
+/******************** infoflow graph node routines ********************/
+
+/**
+ * Given a pointer to an apol_infoflow_node_t, free its space
+ * including the pointer itself. Does nothing if the pointer is
+ * already NULL.
+ *
+ * @param data Node to free.
+ */
+static void apol_infoflow_node_free(void *data)
+{
+ apol_infoflow_node_t *node = (apol_infoflow_node_t *) data;
+ if (node != NULL) {
+ /* the edges themselves are owned by the graph, not by
+ * the node */
+ apol_vector_destroy(&node->in_edges);
+ apol_vector_destroy(&node->out_edges);
+ free(node);
+ }
+}
+
+struct apol_infoflow_node_key
+{
+ const qpol_type_t *type;
+ int node_type;
+};
+
+/**
+ * Given an infoflow node and a key, returns 0 if they are the same,
+ * non-zero if not.
+ *
+ * @param a Existing node within the infoflow graph.
+ * @param b <i>Unused.</i>
+ * @param data Pointer to a struct infoflow_node_key.
+ *
+ * @return 0 if the key matches a, non-zero if not.
+ */
+static int apol_infoflow_node_compare(const void *a, const void *b __attribute__ ((unused)), void *data)
+{
+ apol_infoflow_node_t *node = (apol_infoflow_node_t *) a;
+ struct apol_infoflow_node_key *key = (struct apol_infoflow_node_key *)data;
+ if (node->type != key->type) {
+ return (int)((char *)node->type - (char *)key->type);
+ }
+ return node->node_type - key->node_type;
+}
+
+/**
+ * Attempt to allocate a new node, add it to the infoflow graph, and
+ * return a pointer to it. If there already exists a node with the
+ * same type then reuse that node.
+ *
+ * @param p Policy handler, for reporting error.
+ * @param g Infoflow to which add the node.
+ * @param type Type for the new node.
+ * @param node_type Node type, one of APOL_INFOFLOW_NODE_SOURCE or
+ * APOL_INFOFLOW_NODE_TARGET.
+ *
+ * @return Pointer an allocated node within the infoflow graph, or
+ * NULL upon error.
+ */
+static apol_infoflow_node_t *apol_infoflow_graph_create_node(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const qpol_type_t * type, int node_type)
+{
+ struct apol_infoflow_node_key key = { type, node_type };
+ apol_infoflow_node_t *node = NULL;
+ if (apol_bst_get_element(g->nodes_bst, NULL, &key, (void **)&node) == 0) {
+ return node;
+ }
+ if ((node = calloc(1, sizeof(*node))) == NULL ||
+ (node->in_edges = apol_vector_create(NULL)) == NULL || (node->out_edges = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_infoflow_node_free(node);
+ return NULL;
+ }
+ node->type = type;
+ node->node_type = node_type;
+ if (apol_bst_insert(g->nodes_bst, node, &key) != 0) {
+ ERR(p, "%s", strerror(errno));
+ apol_infoflow_node_free(node);
+ return NULL;
+ }
+ return node;
+}
+
+/**
+ * Attempt to allocate a new node, add it to the infoflow graph, and
+ * return a pointer to it. If there already exists a node with the
+ * same type then reuse that node.
+ *
+ * @param p Policy handler, for reporting error.
+ * @param g Infoflow to which add the node.
+ * @param type Type for the new node. If this is an attribute then it
+ * will be expanded into its component types.
+ * @param types If non-NULL, a BST of qpol_type_t pointers. Only
+ * create and return nodes which are members of this tree.
+ * @param node_type Node type, one of APOL_INFOFLOW_NODE_SOURCE or
+ * APOL_INFOFLOW_NODE_TARGET.
+ *
+ * @return Vector of nodes (type apol_infoflow_node_t *) within the
+ * infoflow graph, or NULL upon error. The caller is responsible for
+ * calling apol_vector_destroy() upon the return value.
+ */
+static apol_vector_t *apol_infoflow_graph_create_nodes(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const qpol_type_t * type, apol_bst_t * types,
+ int node_type)
+{
+ unsigned char isattr;
+ apol_vector_t *v = NULL;
+ apol_infoflow_node_t *node = NULL;
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ return NULL;
+ }
+ if (isattr && g->mode != APOL_INFOFLOW_MODE_DIRECT) {
+ qpol_iterator_t *iter = NULL;
+ qpol_type_t *t;
+ size_t len;
+ if (qpol_type_get_type_iter(p->p, type, &iter) < 0 ||
+ qpol_iterator_get_size(iter, &len) < 0 || (v = apol_vector_create_with_capacity(len, NULL)) == NULL) {
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&t);
+ void *result;
+ if (types != NULL && apol_bst_get_element(types, t, NULL, &result) < 0) {
+ continue;
+ }
+ if ((node = apol_infoflow_graph_create_node(p, g, t, node_type)) == NULL || apol_vector_append(v, node) < 0) {
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ } else {
+ /* for a direct search, do not expand types; the
+ * algorithm will do that with
+ * apol_infoflow_graph_get_nodes_for_type() and
+ * apol_infoflow_analysis_direct_expand(). for
+ * transitive searches the \a types BST was checked in
+ * apol_infoflow_graph_check_types() if \a type is
+ * just a type.
+ */
+ if ((v = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ return NULL;
+ }
+ if ((node = apol_infoflow_graph_create_node(p, g, type, node_type)) == NULL || apol_vector_append(v, node) < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ }
+ return v;
+}
+
+/******************** infoflow graph edge routines ********************/
+
+/**
+ * Given a pointer to an apol_infoflow_edge_t, free its space
+ * including the pointer itself. Does nothing if the pointer is
+ * already NULL.
+ *
+ * @param data Edge to free.
+ */
+static void apol_infoflow_edge_free(void *data)
+{
+ apol_infoflow_edge_t *edge = (apol_infoflow_edge_t *) data;
+ if (edge != NULL) {
+ apol_vector_destroy(&edge->rules);
+ free(edge);
+ }
+}
+
+struct apol_infoflow_edge_key
+{
+ apol_infoflow_node_t *start_node, *end_node;
+};
+
+/**
+ * Given an infoflow edge and a key, returns 0 if they are the same,
+ * non-zero if not.
+ *
+ * @param a Existing edge within the infoflow graph.
+ * @param b <i>Unused.</i>
+ * @param data Pointer to a struct infoflow_edge_key.
+ *
+ * @return 0 if the key matches a, non-zero if not.
+ */
+static int apol_infoflow_edge_compare(const void *a, const void *b __attribute__ ((unused)), void *data)
+{
+ apol_infoflow_edge_t *edge = (apol_infoflow_edge_t *) a;
+ struct apol_infoflow_edge_key *key = (struct apol_infoflow_edge_key *)data;
+ if (key->start_node != NULL && edge->start_node != key->start_node) {
+ return (int)((char *)edge->start_node - (char *)key->start_node);
+ }
+ if (key->end_node != NULL && edge->end_node != key->end_node) {
+ return (int)((char *)edge->end_node - (char *)key->end_node);
+ }
+ return 0;
+}
+
+/**
+ * Attempt to allocate a new edge, add it to the infoflow graph, and
+ * return a pointer to it. If there already exists a edge from the
+ * start node to the end node then reuse that edge.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to which add the edge.
+ * @param start_node Starting node for the edge.
+ * @param end_node Ending node for the edge.
+ * @param len Length of edge (proportionally inverse of permission weight)
+ *
+ * @return Pointer an allocated node within the infoflow graph, or
+ * NULL upon error.
+ */
+static apol_infoflow_edge_t *apol_infoflow_graph_create_edge(const apol_policy_t * p,
+ apol_infoflow_graph_t * g __attribute__ ((unused)),
+ apol_infoflow_node_t * start_node,
+ apol_infoflow_node_t * end_node, int len)
+{
+ struct apol_infoflow_edge_key key = { NULL, end_node };
+ size_t i;
+ apol_infoflow_edge_t *edge = NULL;
+ if (apol_vector_get_index(start_node->out_edges, NULL, apol_infoflow_edge_compare, &key, &i) == 0) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(start_node->out_edges, i);
+ if (edge->length < len) {
+ edge->length = len;
+ }
+ return edge;
+ }
+ if ((edge = calloc(1, sizeof(*edge))) == NULL || (edge->rules = apol_vector_create(NULL)) == NULL ||
+ apol_vector_append(g->edges, edge) < 0) {
+ ERR(p, "%s", strerror(errno));
+ apol_infoflow_edge_free(edge);
+ return NULL;
+ }
+ edge->start_node = start_node;
+ edge->end_node = end_node;
+ edge->length = len;
+ if (apol_vector_append(start_node->out_edges, edge) < 0 || apol_vector_append(end_node->in_edges, edge) < 0) {
+ /* don't free the edge -- it is owned by the graph */
+ ERR(p, "%s", strerror(errno));
+ return NULL;
+ }
+ return edge;
+}
+
+/******************** infoflow graph creation routines ********************/
+
+/**
+ * Take an avrule within a policy and possibly add it to the infoflow
+ * graph. The rule's source and target type sets are expanded. If
+ * the rule is to be added, then add its end nodes as necessary, and
+ * an edge connecting those nodes as necessary, and then add the rule
+ * to the edge.
+ *
+ * @param p Policy containing rules.
+ * @param g Information flow graph being created.
+ * @param rule AV rule to use.
+ * @param types BST of qpol_type_t pointers; while adding avrules to
+ * the graph, only add those whose source and/or target is a member of
+ * \a types, if \a types is non-NULL.
+ * @param found_read Non-zero to indicate that this rule performs a
+ * read operation.
+ * @param read_len Length of the edge to create (proportionally
+ * inverse of permission weight).
+ * @param found_write Non-zero to indicate that this rule performs a
+ * write operation.
+ * @param write_len Length of the edge to create (proportionally
+ * inverse of permission weight).
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_connect_nodes(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ const qpol_avrule_t * rule,
+ apol_bst_t * types, int found_read, int read_len, int found_write, int write_len)
+{
+ const qpol_type_t *src_type, *tgt_type;
+ apol_vector_t *src_nodes = NULL, *tgt_nodes = NULL;
+ size_t i, j;
+ apol_infoflow_node_t *src_node, *tgt_node;
+ apol_infoflow_edge_t *edge;
+ int retval = -1;
+
+ if (qpol_avrule_get_source_type(p->p, rule, &src_type) < 0 || qpol_avrule_get_target_type(p->p, rule, &tgt_type) < 0) {
+ goto cleanup;
+ }
+
+ if ((src_nodes = apol_infoflow_graph_create_nodes(p, g, src_type, types, APOL_INFOFLOW_NODE_SOURCE)) == NULL) {
+ goto cleanup;
+ }
+ if ((tgt_nodes = apol_infoflow_graph_create_nodes(p, g, tgt_type, types, APOL_INFOFLOW_NODE_TARGET)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(src_nodes); i++) {
+ src_node = apol_vector_get_element(src_nodes, i);
+ for (j = 0; j < apol_vector_get_size(tgt_nodes); j++) {
+ tgt_node = apol_vector_get_element(tgt_nodes, j);
+ if (found_read) {
+ if ((edge = apol_infoflow_graph_create_edge(p, g, tgt_node, src_node, read_len)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_vector_append(edge->rules, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ if (found_write) {
+ if ((edge = apol_infoflow_graph_create_edge(p, g, src_node, tgt_node, write_len)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_vector_append(edge->rules, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&src_nodes);
+ apol_vector_destroy(&tgt_nodes);
+ return retval;
+}
+
+/**
+ * Given a policy and a partially completed infoflow graph, create the
+ * nodes and edges associated with a particular rule.
+ *
+ * @param p Policy from which to create the infoflow graph.
+ * @param g Infoflow graph being created.
+ * @param rule AV rule to add.
+ * @param types BST of qpol_type_t pointers; while adding avrules to
+ * the graph, only add those whose source and/or target is a member of
+ * \a types, if \a types is non-NULL.
+ * @param max_len Maximum permission length (i.e., inverse of
+ * permission weight) to consider when deciding to add this rule or
+ * not.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_create_avrule(const apol_policy_t * p, apol_infoflow_graph_t * g, const qpol_avrule_t * rule,
+ apol_bst_t * types, int max_len)
+{
+ const qpol_class_t *obj_class;
+ qpol_iterator_t *perm_iter = NULL;
+ const char *obj_class_name;
+ char *perm_name;
+ int found_read = 0, found_write = 0, perm_error = 0;
+ int read_len = INT_MAX, write_len = INT_MAX;
+ int retval = -1;
+ if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0 ||
+ qpol_class_get_name(p->p, obj_class, &obj_class_name) < 0 || qpol_avrule_get_perm_iter(p->p, rule, &perm_iter) < 0) {
+ goto cleanup;
+ }
+
+ /* find read or write flows for each object class/perm pair */
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ int perm_map, perm_weight, len;
+
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_name) < 0) {
+ goto cleanup;
+ }
+ if (apol_policy_get_permmap(p, obj_class_name, perm_name, &perm_map, &perm_weight) < 0) {
+ goto cleanup;
+ }
+ free(perm_name);
+ if (perm_map == APOL_PERMMAP_UNMAPPED) {
+ perm_error = 1;
+ continue;
+ }
+ len = APOL_PERMMAP_MAX_WEIGHT - perm_weight + 1;
+ if (len < APOL_PERMMAP_MIN_WEIGHT) {
+ len = APOL_PERMMAP_MIN_WEIGHT;
+ } else if (len > APOL_PERMMAP_MAX_WEIGHT) {
+ len = APOL_PERMMAP_MAX_WEIGHT;
+ }
+ if (perm_map & APOL_PERMMAP_READ) {
+ if (len < read_len && len <= max_len) {
+ found_read = 1;
+ read_len = len;
+ }
+ }
+ if (perm_map & APOL_PERMMAP_WRITE) {
+ if (len < write_len && len <= max_len) {
+ found_write = 1;
+ write_len = len;
+ }
+ }
+ }
+
+ /* if we have found any flows then connect them within the graph */
+ if ((found_read || found_write) &&
+ apol_infoflow_graph_connect_nodes(p, g, rule, types, found_read, read_len, found_write, write_len) < 0) {
+ goto cleanup;
+ }
+ if (perm_error) {
+ WARN(p, "%s", "Not all of the permissions found had associated permission maps.");
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ return retval;
+}
+
+/**
+ * Given a vector of strings representing types, return a BST of
+ * qpol_type_t pointers consisting of those types, those types'
+ * attributes, and those types' aliases.
+ *
+ * @param p Policy within which to look up types,
+ * @param v Vector of type strings.
+ *
+ * @return BST of qpol_type_t pointers, or NULL on error. The caller
+ * is responsible for calling apol_bst_destroy() upon the returned
+ * value.
+ */
+static apol_bst_t *apol_infoflow_graph_create_required_types(const apol_policy_t * p, const apol_vector_t * v)
+{
+ apol_bst_t *types = NULL;
+ apol_vector_t *expanded_types = NULL;
+ size_t i;
+ char *s;
+ int retval = -1;
+ if ((types = apol_bst_create(NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ s = (char *)apol_vector_get_element(v, i);
+ expanded_types = apol_query_create_candidate_type_list(p, s, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH);
+ if (expanded_types == NULL) {
+ goto cleanup;
+ }
+ for (size_t j = 0; j < apol_vector_get_size(expanded_types); j++) {
+ qpol_type_t *t = (qpol_type_t *) apol_vector_get_element(expanded_types, j);
+ if (apol_bst_insert(types, t, NULL) < 0) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ apol_vector_destroy(&expanded_types);
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&expanded_types);
+ if (retval != 0) {
+ apol_bst_destroy(&types);
+ }
+ return types;
+}
+
+/**
+ * Determine if an av rule matches a list of qpol_type_t pointers.
+ * Both the source and target of the rule must be in the list.
+ *
+ * @param p Policy to which look up classes and permissions.
+ * @param rule AV rule to check.
+ * @param types BST of qpol_type_t, of which both the source and
+ * target types must be members. If NULL allow all types.
+ *
+ * @return 1 if rule matches, 0 if not, < 0 on error.
+ */
+static int apol_infoflow_graph_check_types(const apol_policy_t * p, const qpol_avrule_t * rule, const apol_bst_t * types)
+{
+ const qpol_type_t *source, *target;
+ void *result;
+ int retval = -1;
+ if (types == NULL) {
+ retval = 1;
+ goto cleanup;
+ }
+ if (qpol_avrule_get_source_type(p->p, rule, &source) < 0 || qpol_avrule_get_target_type(p->p, rule, &target) < 0) {
+ goto cleanup;
+ }
+ if (apol_bst_get_element(types, source, NULL, &result) < 0 || apol_bst_get_element(types, target, NULL, &result) < 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ retval = 1;
+ cleanup:
+ return retval;
+}
+
+/**
+ * Determine if an av rule matches a list of apol_obj_perm_t. The
+ * rule's class must match at least one item in the list, and at least
+ * one of the rule's permissions must be on the list.
+ *
+ * @param p Policy to which look up classes and permissions.
+ * @param rule AV rule to check.
+ * @param class_perms Vector of apol_obj_perm_t, of which rule's class
+ * and permissions must be a member. If NULL or empty then allow all
+ * classes and permissions.
+ *
+ * @return 1 if rule matches, 0 if not, < 0 on error.
+ */
+static int apol_infoflow_graph_check_class_perms(const apol_policy_t * p, const qpol_avrule_t * rule,
+ const apol_vector_t * class_perms)
+{
+ const qpol_class_t *obj_class;
+ const char *obj_name;
+ char *perm;
+ qpol_iterator_t *iter = NULL;
+ apol_obj_perm_t *obj_perm = NULL;
+ apol_vector_t *obj_perm_v = NULL;
+ size_t i;
+ int retval = -1;
+
+ if (class_perms == NULL || apol_vector_get_size(class_perms) == 0) {
+ retval = 1;
+ goto cleanup;
+ }
+ if (qpol_avrule_get_object_class(p->p, rule, &obj_class) < 0 || qpol_class_get_name(p->p, obj_class, &obj_name) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(class_perms); i++) {
+ obj_perm = (apol_obj_perm_t *) apol_vector_get_element(class_perms, i);
+ if (strcmp(apol_obj_perm_get_obj_name(obj_perm), obj_name) == 0) {
+ obj_perm_v = apol_obj_perm_get_perm_vector(obj_perm);
+ break;
+ }
+ }
+ if (i >= apol_vector_get_size(class_perms)) {
+ retval = 0; /* no matching class */
+ goto cleanup;
+ }
+ if (qpol_avrule_get_perm_iter(p->p, rule, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(obj_perm_v, perm, apol_str_strcmp, NULL, &i) == 0) {
+ free(perm);
+ retval = 1;
+ goto cleanup;
+ }
+ free(perm);
+ }
+ retval = 0; /* no matching perm */
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given a particular information flow analysis object, generate an
+ * infoflow graph relative to a particular policy. This graph is
+ * customized for the particular analysis.
+ *
+ * @param p Policy from which to create the infoflow graph.
+ * @param ia Parameters to tune the created graph.
+ * @param g Reference to where to store the graph. The caller is
+ * responsible for calling apol_infoflow_graph_destroy() upon this.
+ *
+ * @return 0 if the graph was created, < 0 on error. Upon error *g
+ * will be set to NULL.
+ */
+static int apol_infoflow_graph_create(const apol_policy_t * p, const apol_infoflow_analysis_t * ia, apol_infoflow_graph_t ** g)
+{
+ apol_bst_t *types = NULL;
+ qpol_iterator_t *iter = NULL;
+ int max_len = APOL_PERMMAP_MAX_WEIGHT - ia->min_weight + 1;
+ int compval, retval = -1;
+
+ *g = NULL;
+ if (p->pmap == NULL) {
+ ERR(p, "%s", "A permission map must be loaded prior to building the infoflow graph.");
+ goto cleanup;
+ }
+
+ INFO(p, "%s", "Generating information flow graph.");
+ if (ia->mode == APOL_INFOFLOW_MODE_TRANS && ia->intermed != NULL &&
+ (types = apol_infoflow_graph_create_required_types(p, ia->intermed)) == NULL) {
+ goto cleanup;
+ }
+
+ if ((*g = calloc(1, sizeof(**g))) == NULL ||
+ ((*g)->nodes_bst = apol_bst_create(apol_infoflow_node_compare, apol_infoflow_node_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ (*g)->mode = ia->mode;
+ (*g)->direction = ia->direction;
+ if (ia->result != NULL && ia->result[0] != '\0') {
+ if (((*g)->regex = malloc(sizeof(regex_t))) == NULL || regcomp((*g)->regex, ia->result, REG_EXTENDED | REG_NOSUB)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (((*g)->edges = apol_vector_create(apol_infoflow_edge_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (qpol_policy_get_avrule_iter(p->p, QPOL_RULE_ALLOW, &iter) < 0) {
+ goto cleanup;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_avrule_t *rule;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+ compval = apol_infoflow_graph_check_types(p, rule, types);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ compval = apol_infoflow_graph_check_class_perms(p, rule, ia->class_perms);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (apol_infoflow_graph_create_avrule(p, *g, rule, types, max_len) < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (((*g)->nodes = apol_bst_get_vector((*g)->nodes_bst, 1)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ apol_bst_destroy(&(*g)->nodes_bst);
+ retval = 0;
+ cleanup:
+ apol_bst_destroy(&types);
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_infoflow_graph_destroy(g);
+ }
+ return retval;
+}
+
+void apol_infoflow_graph_destroy(apol_infoflow_graph_t ** g)
+{
+ if (g != NULL && *g != NULL) {
+ apol_bst_destroy(&(*g)->nodes_bst);
+ apol_vector_destroy(&(*g)->nodes);
+ apol_vector_destroy(&(*g)->edges);
+ apol_vector_destroy(&(*g)->further_start);
+ apol_vector_destroy(&(*g)->further_end);
+ apol_regex_destroy(&(*g)->regex);
+ free(*g);
+ *g = NULL;
+ }
+}
+
+/*************** infoflow graph direct analysis routines ***************/
+
+/**
+ * Given a graph and a target type, append to vector v all nodes
+ * (apol_infoflow_node_t) within the graph that use that type, one of
+ * that type's aliases, or one of that type's attributes. This will
+ * also implicitly permutate across all of the type's object classes.
+ *
+ * @param p Error reporting handler.
+ * @param g Information flow graph containing nodes.
+ * @param type Target type name to find.
+ * @param v Initialized vector to which append nodes.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_get_nodes_for_type(const apol_policy_t * p, const apol_infoflow_graph_t * g, const char *type,
+ apol_vector_t * v)
+{
+ size_t i, j;
+ apol_vector_t *cand_list = NULL;
+ int retval = -1;
+ if ((cand_list = apol_query_create_candidate_type_list(p, type, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ apol_infoflow_node_t *node;
+ node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ if (apol_vector_get_index(cand_list, node->type, NULL, NULL, &j) == 0 && apol_vector_append(v, node) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&cand_list);
+ return retval;
+}
+
+/**
+ * Return a usable infoflow result object. If there already exists a
+ * result object within vector v with the same start and ending type
+ * then reuse that object. Otherwise allocate and return a new
+ * infoflow result with its start and end type fields set.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param v Non-null vector of infoflow results.
+ * @param start_type Starting type for returned infoflow result object.
+ * @param end_type Starting type for returned infoflow result object.
+ *
+ * @return A usable infoflow result object, or NULL upon error.
+ */
+static apol_infoflow_result_t *apol_infoflow_direct_get_result(const apol_policy_t * p,
+ apol_vector_t * v, const qpol_type_t * start_type,
+ const qpol_type_t * end_type)
+{
+ size_t i;
+ apol_infoflow_result_t *r;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ r = (apol_infoflow_result_t *) apol_vector_get_element(v, i);
+ if (r->start_type == start_type && r->end_type == end_type) {
+ return r;
+ }
+ }
+ if ((r = calloc(1, sizeof(*r))) == NULL || (r->steps = apol_vector_create(apol_infoflow_step_free)) == NULL
+ || apol_vector_append(v, r) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ infoflow_result_free(r);
+ return NULL;
+ }
+ r->start_type = start_type;
+ r->end_type = end_type;
+ r->length = INT_MAX;
+ return r;
+}
+
+/**
+ * Append the rules on an edge to a direct infoflow result.
+ *
+ * @param p Policy containing rules.
+ * @param edge Infoflow edge containing rules.
+ * @param direction Direction of flow, one of APOL_INFOFLOW_IN, etc.
+ * @param result Infoflow result to modify.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_direct_define(const apol_policy_t * p,
+ const apol_infoflow_edge_t * edge, unsigned int direction, apol_infoflow_result_t * result)
+{
+ apol_infoflow_step_t *step = NULL;
+ if (apol_vector_get_size(result->steps) == 0) {
+ if ((step = calloc(1, sizeof(*step))) == NULL ||
+ (step->rules = apol_vector_create(NULL)) == NULL || apol_vector_append(result->steps, step) < 0) {
+ apol_infoflow_step_free(step);
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ step->start_type = result->start_type;
+ step->end_type = result->end_type;
+ step->weight = 0;
+ } else {
+ step = (apol_infoflow_step_t *) apol_vector_get_element(result->steps, 0);
+ }
+ if (apol_vector_cat(step->rules, edge->rules) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ result->direction |= direction;
+ //TODO: check that edge->lenght can be safely unsigned
+ if (edge->length < (int)result->length) {
+ result->length = edge->length;
+ }
+ return 0;
+}
+
+/**
+ * Given the regular expression compiled into the graph object and a
+ * type, determine if that regex matches that type or any of the
+ * type's aliases.
+ *
+ * @param p Policy containing type names.
+ * @param g Graph object containing regex.
+ * @param type Type to check against.
+ *
+ * @return 1 if comparison succeeds, 0 if not, < 0 on error.
+ */
+static int apol_infoflow_graph_compare(const apol_policy_t * p, apol_infoflow_graph_t * g, const qpol_type_t * type)
+{
+ const char *type_name;
+ qpol_iterator_t *alias_iter = NULL;
+ int compval = 0;
+ if (g->regex == NULL) {
+ return 1;
+ }
+ if (qpol_type_get_name(p->p, type, &type_name) < 0) {
+ return -1;
+ }
+ if (regexec(g->regex, type_name, 0, NULL, 0) == 0) {
+ return 1;
+ }
+ /* also check for matches against any of target's aliases */
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ return -1;
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) {
+ char *iter_name;
+ if (qpol_iterator_get_item(alias_iter, (void **)&iter_name) < 0) {
+ compval = -1;
+ break;
+ }
+ if (regexec(g->regex, iter_name, 0, NULL, 0) == 0) {
+ compval = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+/**
+ * For each result object in vector working_results, append a
+ * duplicate of it to vector results if (a) the infoflow analysis
+ * object direction is not BOTH or (b) the result object's direction
+ * is BOTH. Regardless of success or error, it is safe to destroy
+ * either vector without concern of double-free()ing things.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param working_results Vector of infoflow results to check.
+ * @param direction Direction of search.
+ * @param results Vector to which append duplicated infoflow results.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_results_check_both(const apol_policy_t * p,
+ const apol_vector_t * working_results, unsigned int direction, apol_vector_t * results)
+{
+ size_t i;
+ apol_infoflow_result_t *r, *new_r;
+ for (i = 0; i < apol_vector_get_size(working_results); i++) {
+ r = (apol_infoflow_result_t *) apol_vector_get_element(working_results, i);
+ if (direction != APOL_INFOFLOW_BOTH || r->direction == APOL_INFOFLOW_BOTH) {
+ if ((new_r = calloc(1, sizeof(*new_r))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ memcpy(new_r, r, sizeof(*new_r));
+ r->steps = NULL;
+ if (apol_vector_append(results, new_r) < 0) {
+ infoflow_result_free(new_r);
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Given a start node, an edge, and flow direction, add an infoflow
+ * results to a vector. If the node on the other end of the edge is
+ * an attribute, first expand the attribute to its component types.
+ * If a regular expression is compiled into the infoflow graph, apply
+ * that regex match against candidate end node types prior to creating
+ * result nodes.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_node Starting node.
+ * @param edge An edge from start_node.
+ * @param flow_dir Direction of search, either APOL_INFOFLOW_IN or
+ * APOL_INFOFLOW_OUT.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_direct_expand(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start_node,
+ apol_infoflow_edge_t * edge, unsigned int flow_dir, apol_vector_t * results)
+{
+ apol_infoflow_node_t *end_node;
+ unsigned char isattr;
+ qpol_iterator_t *iter = NULL;
+ const qpol_type_t *type;
+ apol_infoflow_result_t *r;
+ int retval = -1, compval;
+
+ if (edge->start_node == start_node) {
+ end_node = edge->end_node;
+ } else {
+ end_node = edge->start_node;
+ }
+ if (qpol_type_get_isattr(p->p, end_node->type, &isattr) < 0) {
+ goto cleanup;
+ }
+ if (isattr) {
+ if (qpol_type_get_type_iter(p->p, end_node->type, &iter) < 0) {
+ goto cleanup;
+ }
+ if (qpol_iterator_end(iter)) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ /* always do this loop once, either if end_node is an attribute or not */
+ do {
+ if (isattr) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ qpol_iterator_next(iter);
+ } else {
+ type = end_node->type;
+ }
+ compval = apol_infoflow_graph_compare(p, g, type);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((r = apol_infoflow_direct_get_result(p, results, start_node->type, type)) == NULL ||
+ apol_infoflow_direct_define(p, edge, flow_dir, r) < 0) {
+ goto cleanup;
+ }
+ } while (isattr && !qpol_iterator_end(iter));
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Perform a direct information flow analysis upon the given infoflow
+ * graph.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_type Type from which to begin search.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_direct(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type, apol_vector_t * results)
+{
+ apol_vector_t *nodes = NULL;
+ size_t i, j;
+ apol_infoflow_node_t *node;
+ apol_infoflow_edge_t *edge;
+ apol_vector_t *working_results = NULL;
+ int retval = -1;
+
+ if ((nodes = apol_vector_create(NULL)) == NULL || (working_results = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, nodes) < 0) {
+ goto cleanup;
+ }
+
+ if (g->direction == APOL_INFOFLOW_IN || g->direction == APOL_INFOFLOW_EITHER || g->direction == APOL_INFOFLOW_BOTH) {
+ for (i = 0; i < apol_vector_get_size(nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(nodes, i);
+ for (j = 0; j < apol_vector_get_size(node->in_edges); j++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(node->in_edges, j);
+ if (apol_infoflow_analysis_direct_expand(p, g, node, edge, APOL_INFOFLOW_IN, working_results) < 0) {
+ goto cleanup;
+ }
+ }
+ }
+ }
+ if (g->direction == APOL_INFOFLOW_OUT || g->direction == APOL_INFOFLOW_EITHER || g->direction == APOL_INFOFLOW_BOTH) {
+ for (i = 0; i < apol_vector_get_size(nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(nodes, i);
+ for (j = 0; j < apol_vector_get_size(node->out_edges); j++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(node->out_edges, j);
+ if (apol_infoflow_analysis_direct_expand(p, g, node, edge, APOL_INFOFLOW_OUT, working_results) < 0) {
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ if (apol_infoflow_results_check_both(p, working_results, g->direction, results) < 0) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&nodes);
+ apol_vector_destroy(&working_results);
+ return retval;
+}
+
+/*************** infoflow graph transitive analysis routines ***************/
+
+/**
+ * Prepare an infoflow graph for a transitive analysis by coloring its
+ * nodes and setting its parent and distance. For the start node
+ * color it red; for all others color them white.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to initialize.
+ * @param start Node from which to begin analysis.
+ * @param q Queue of apol_infoflow_node_t pointers to which search.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_trans_init(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_queue_t * q)
+{
+ size_t i;
+ apol_infoflow_node_t *node;
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ node->parent = NULL;
+ if (node == start) {
+ node->color = APOL_INFOFLOW_COLOR_RED;
+ node->distance = 0;
+ if (apol_queue_insert(q, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ } else {
+ node->color = APOL_INFOFLOW_COLOR_WHITE;
+ node->distance = INT_MAX;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Prepare an infoflow graph for furher transitive analysis by
+ * coloring its nodes and setting its parent and distance. For the
+ * start node color it grey; for all others color them white.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to initialize.
+ * @param start Node from which to begin analysis.
+ * @param q Queue of apol_infoflow_node_t pointers to which search.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_graph_trans_further_init(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_queue_t * q)
+{
+ size_t i;
+ apol_infoflow_node_t *node;
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ node->parent = NULL;
+ if (node == start) {
+ node->color = APOL_INFOFLOW_COLOR_GREY;
+ node->distance = 0;
+ if (apol_queue_insert(q, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ } else {
+ node->color = APOL_INFOFLOW_COLOR_WHITE;
+ node->distance = -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Given a colored infoflow graph from apol_infoflow_analysis_trans(),
+ * find the shortest path from the end node to the start node.
+ * Allocate and return a vector of apol_infoflow_node_t that lists the
+ * nodes from the end to start.
+ *
+ * @param p Policy from which infoflow graph was generated.
+ * @param g Infoflow graph that has been colored.
+ * @param start_node Starting node for the path
+ * @param end_node Ending node to which to find a path.
+ * @param path Reference to a vector that will be allocated and filled
+ * with apol_infoflow_node_t pointers from the graph. The path will
+ * be in reverse order (i.e., from end node to a start node). Upon
+ * error this will be set to NULL.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_trans_path(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start_node, apol_infoflow_node_t * end_node, apol_vector_t ** path)
+{
+ int retval = -1;
+ apol_infoflow_node_t *next_node = end_node;
+ if ((*path = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ while (1) {
+ if (apol_vector_append(*path, next_node) < 0) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (next_node == start_node) {
+ break;
+ }
+ if (next_node == NULL || apol_vector_get_size(*path) >= apol_vector_get_size(g->nodes)) {
+ ERR(p, "%s", "Infinite loop in trans_path.");
+ errno = EPERM;
+ goto cleanup;
+ }
+ next_node = next_node->parent;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(path);
+ }
+ return retval;
+}
+
+/**
+ * Given a node within an infoflow graph, return the edge that
+ * connects it to next_node.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph from which to find edge.
+ * @param node Starting node.
+ * @param next_node Ending node.
+ *
+ * @return Edge connecting node to next_node, or NULL on error.
+ */
+static apol_infoflow_edge_t *apol_infoflow_trans_find_edge(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * node, apol_infoflow_node_t * next_node)
+{
+ apol_vector_t *v;
+ apol_infoflow_edge_t *edge;
+ size_t i;
+
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ v = node->out_edges;
+ } else {
+ v = node->in_edges;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(v, i);
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ if (edge->start_node == node && edge->end_node == next_node) {
+ return edge;
+ }
+ } else {
+ if (edge->end_node == node && edge->start_node == next_node) {
+ return edge;
+ }
+
+ }
+ }
+ ERR(p, "%s", "Did not find an edge.");
+ return NULL;
+}
+
+/**
+ * Given a path of nodes, define a new infoflow result that represents
+ * that path. The given path is a list of nodes that must be in
+ * reverse order (i.e., from end node to start node) and must have at
+ * least 2 elements within.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Graph from which the node path originated.
+ * @param path Vector of apol_infoflow_node_t representing an infoflow
+ * path.
+ * @param end_type Ending type for the path.
+ * @param result Reference pointer to where to store result. The
+ * caller is responsible for calling apol_infoflow_result_free() upon
+ * the returned value. Upon error this will be set to NULL.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_trans_define(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_vector_t * path, const qpol_type_t * end_type, apol_infoflow_result_t ** result)
+{
+ apol_infoflow_step_t *step = NULL;
+ size_t path_len = apol_vector_get_size(path), i;
+ apol_infoflow_node_t *node, *next_node;
+ apol_infoflow_edge_t *edge;
+ int retval = -1, length = 0;
+ *result = NULL;
+
+ if (((*result) = calloc(1, sizeof(**result))) == NULL ||
+ ((*result)->steps = apol_vector_create_with_capacity(path_len, apol_infoflow_step_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ (*result)->end_type = end_type;
+ /* build in reverse order because path is from end node to
+ * start node */
+ node = (apol_infoflow_node_t *) apol_vector_get_element(path, path_len - 1);
+ (*result)->start_type = node->type;
+ (*result)->direction = g->direction;
+ for (i = path_len - 1; i > 0; i--, node = next_node) {
+ next_node = (apol_infoflow_node_t *) apol_vector_get_element(path, i - 1);
+ edge = apol_infoflow_trans_find_edge(p, g, node, next_node);
+ if (edge == NULL) {
+ goto cleanup;
+ }
+ length += edge->length;
+ if ((step = calloc(1, sizeof(*step))) == NULL ||
+ (step->rules = apol_vector_create_from_vector(edge->rules, NULL, NULL, NULL)) == NULL ||
+ apol_vector_append((*result)->steps, step) < 0) {
+ apol_infoflow_step_free(step);
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ step->start_type = edge->start_node->type;
+ step->end_type = edge->end_node->type;
+ step->weight = APOL_PERMMAP_MAX_WEIGHT - edge->length + 1;
+ }
+ (*result)->length = length;
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ infoflow_result_free(*result);
+ *result = NULL;
+ }
+ return retval;
+}
+
+/**
+ * Compares two apol_infoflow_step_t objects, returning 0 if they have
+ * the same contents, non-zero or not. This is a callback function to
+ * apol_vector_compare().
+ *
+ * @param a First apol_infoflow_step_t to compare.
+ * @param b Other apol_infoflow_step_t to compare.
+ * @param data Unused.
+ *
+ * @return 0 if the steps are the same, non-zero if different.
+ */
+static int apol_infoflow_trans_step_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const apol_infoflow_step_t *step_a = (const apol_infoflow_step_t *)a;
+ const apol_infoflow_step_t *step_b = (const apol_infoflow_step_t *)b;
+ size_t i;
+ if (step_a->start_type != step_b->start_type) {
+ return (int)((char *)step_a->start_type - (char *)step_b->start_type);
+ }
+ if (step_a->end_type != step_b->end_type) {
+ return (int)((char *)step_a->end_type - (char *)step_b->end_type);
+ }
+ return apol_vector_compare(step_a->rules, step_b->rules, NULL, NULL, &i);
+}
+
+/**
+ * Given a path, append to the results vector a new
+ * apol_infoflow_result object - but only if there is not already a
+ * result describing the same path.
+ *
+ * @param p Policy handler, for reporting errors.
+ * @param g Infoflow graph to which create results.
+ * @param path Vector of apol_infoflow_node_t describing a path from
+ * an end node to a starting node.
+ * @param end_type Ending type for the path.
+ * @param results Vector of apol_infoflow_result_t to possibly append
+ * a new result.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_trans_append(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_vector_t * path, const qpol_type_t * end_type, apol_vector_t * results)
+{
+ apol_infoflow_result_t *new_r = NULL, *r;
+ size_t i, j;
+ int compval, retval = -1;
+
+ if (apol_infoflow_trans_define(p, g, path, end_type, &new_r) < 0) {
+ goto cleanup;
+ }
+
+ /* First we look for duplicate paths */
+ for (i = 0; i < apol_vector_get_size(results); i++) {
+ r = (apol_infoflow_result_t *) apol_vector_get_element(results, i);
+ if (r->end_type != end_type ||
+ r->direction != new_r->direction || apol_vector_get_size(r->steps) != apol_vector_get_size(new_r->steps)) {
+ break;
+ }
+ compval = apol_vector_compare(r->steps, new_r->steps, apol_infoflow_trans_step_comp, NULL, &j);
+ /* found a dup TODO - make certain all of the object
+ * class / rules are kept */
+ if (compval == 0) {
+ infoflow_result_free(new_r);
+ new_r = NULL;
+ retval = 0;
+ goto cleanup;
+ }
+ }
+
+ /* If we are here the newly built path is unique. */
+ if (apol_vector_append(results, new_r) < 0) {
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ infoflow_result_free(new_r);
+ }
+ return retval;
+}
+
+/**
+ * Given a start and end node, add a trans infoflow results to a
+ * vector. If a regular expression is compiled into the infoflow
+ * graph, apply that regex match against candidate end node types
+ * prior to creating result nodes.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_node Starting node.
+ * @param end_node Ending node.
+ * @param results Non-NULL vector to which append infoflow result.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success (including no result actually added), or < 0
+ * on error.
+ */
+static int apol_infoflow_analysis_trans_expand(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start_node,
+ apol_infoflow_node_t * end_node, apol_vector_t * results)
+{
+ unsigned char isattr;
+ apol_vector_t *path = NULL;
+ int retval = -1, compval;
+
+ if (qpol_type_get_isattr(p->p, end_node->type, &isattr) < 0) {
+ goto cleanup;
+ }
+ assert(isattr == 0);
+ if (start_node->type == end_node->type) {
+ return 0;
+ }
+ compval = apol_infoflow_graph_compare(p, g, end_node->type);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ return 0;
+ }
+ if (apol_infoflow_trans_path(p, g, start_node, end_node, &path) < 0 ||
+ apol_infoflow_trans_append(p, g, path, end_node->type, results) < 0) {
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&path);
+ return retval;
+}
+
+/**
+ * Perform a transitive information flow analysis upon the given
+ * infoflow graph starting from some particular node within the graph.
+ *
+ * This is a label correcting shortest path algorithm; see Bertsekas,
+ * D. P., "A Simple and Fast Label Correcting Algorithm for Shortest
+ * Paths," Networks, Vol. 23, pp. 703-709, 1993. for more information.
+ * A label correcting algorithm is needed instead of the more common
+ * Dijkstra label setting algorithm to correctly handle the the cycles
+ * that are possible in these graphs.
+ *
+ * This algorithm finds the shortest path between a given start node
+ * and all other nodes in the graph. Any paths that it finds it
+ * appends to the iflow_transitive_t structure. This is a basic label
+ * correcting algorithm with 1 optimization. It uses the D'Esopo-Pape
+ * method for node selection in the node queue. Why is this faster?
+ * The paper referenced above says "No definitive explanation has been
+ * given." They have fancy graphs to show that it is faster though
+ * and the important part is that the worst case isn't much worse that
+ * N^2 - much better than an n^3 transitive closure. Additionally,
+ * most normal sparse graphs are significantly better than the worst
+ * case.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start Node from which to begin search.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_trans_shortest_path(const apol_policy_t * p,
+ apol_infoflow_graph_t * g,
+ apol_infoflow_node_t * start, apol_vector_t * results)
+{
+ apol_vector_t *edge_list;
+ apol_queue_t *queue = NULL;
+ apol_infoflow_node_t *node, *cur_node;
+ apol_infoflow_edge_t *edge;
+ size_t i;
+ int retval = -1;
+
+ if ((queue = apol_queue_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_trans_init(p, g, start, queue) < 0) {
+ goto cleanup;
+ }
+
+ while ((cur_node = apol_queue_remove(queue)) != NULL) {
+ cur_node->color = APOL_INFOFLOW_COLOR_GREY;
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ edge_list = cur_node->out_edges;
+ } else {
+ edge_list = cur_node->in_edges;
+ }
+ for (i = 0; i < apol_vector_get_size(edge_list); i++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(edge_list, i);
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ node = edge->end_node;
+ } else {
+ node = edge->start_node;
+ }
+ if (node == start) {
+ continue;
+ }
+
+ if (node->distance > cur_node->distance + edge->length) {
+ node->distance = cur_node->distance + edge->length;
+ node->parent = cur_node;
+ /* If this node has been inserted into
+ * the queue before insert it at the
+ * beginning, otherwise it goes to the
+ * end. See the comment at the
+ * beginning of the function for
+ * why. */
+ if (node->color != APOL_INFOFLOW_COLOR_RED) {
+ if (node->color == APOL_INFOFLOW_COLOR_GREY) {
+ if (apol_queue_push(queue, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ } else {
+ if (apol_queue_insert(queue, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ node->color = APOL_INFOFLOW_COLOR_RED;
+ }
+ }
+ }
+ }
+
+ /* Find all of the paths and add them to the results vector */
+ for (i = 0; i < apol_vector_get_size(g->nodes); i++) {
+ cur_node = (apol_infoflow_node_t *) apol_vector_get_element(g->nodes, i);
+ if (cur_node->parent == NULL || cur_node == start) {
+ continue;
+ }
+ if (apol_infoflow_analysis_trans_expand(p, g, start, cur_node, results) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_queue_destroy(&queue);
+ return retval;
+}
+
+/**
+ * Perform a transitive information flow analysis upon the given
+ * infoflow graph.
+ *
+ * @param p Policy to analyze.
+ * @param g Information flow graph to analyze.
+ * @param start_type Type from which to begin search.
+ * @param results Non-NULL vector to which append infoflow results.
+ * The caller is responsible for calling apol_infoflow_results_free()
+ * upon each element afterwards.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_infoflow_analysis_trans(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type, apol_vector_t * results)
+{
+ apol_vector_t *start_nodes = NULL;
+ apol_infoflow_node_t *start_node;
+ size_t i;
+ int retval = -1;
+
+ if (g->direction != APOL_INFOFLOW_IN && g->direction != APOL_INFOFLOW_OUT) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if ((start_nodes = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, start_nodes) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(start_nodes); i++) {
+ start_node = (apol_infoflow_node_t *) apol_vector_get_element(start_nodes, i);
+ if (apol_infoflow_analysis_trans_shortest_path(p, g, start_node, results) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&start_nodes);
+ return retval;
+}
+
+/**
+ * Given a vector, allocate and return a new vector with the elements
+ * shuffled about. This will make a shallow copy of the original
+ * vector's elements.
+ *
+ * @param p Policy handler, for error reporting.
+ * @param g Transitive infoflow graph containing PRNG object.
+ * @param v Vector to shuffle.
+ *
+ * @return A newly allocated vector with shuffled elements, or NULL
+ * upon error. The caller must call apol_vector_destroy() upon the
+ * returned value.
+ */
+static apol_vector_t *apol_infoflow_trans_further_shuffle(const apol_policy_t * p, apol_infoflow_graph_t * g, apol_vector_t * v)
+{
+ size_t i, j, size;
+ void **deck = NULL, *tmp;
+ apol_vector_t *new_v = NULL;
+ int retval = -1;
+ size = apol_vector_get_size(v);
+ if ((new_v = apol_vector_create_with_capacity(size, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (size == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ if ((deck = malloc(size * sizeof(*deck))) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < size; i++) {
+ deck[i] = apol_vector_get_element(v, i);
+ }
+ for (i = size - 1; i > 0; i--) {
+ j = (size_t) ((apol_infoflow_rand(g) / (RAND_MAX + 1.0)) * i);
+ tmp = deck[i];
+ deck[i] = deck[j];
+ deck[j] = tmp;
+ }
+ for (i = 0; i < size; i++) {
+ if (apol_vector_append(new_v, deck[i]) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ free(deck);
+ if (retval != 0) {
+ apol_vector_destroy(&new_v);
+ }
+ return new_v;
+}
+
+static int apol_infoflow_analysis_trans_further(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, apol_infoflow_node_t * start, apol_vector_t * results)
+{
+ apol_vector_t *edge_list = NULL;
+ apol_queue_t *queue = NULL;
+ apol_infoflow_node_t *node, *cur_node;
+ apol_infoflow_edge_t *edge;
+ size_t i;
+ int retval = -1;
+
+ if ((queue = apol_queue_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_trans_further_init(p, g, start, queue) < 0) {
+ goto cleanup;
+ }
+
+ while ((cur_node = apol_queue_remove(queue)) != NULL) {
+ if (cur_node != start &&
+ apol_vector_get_index(g->further_end, cur_node, NULL, NULL, &i) == 0 &&
+ apol_infoflow_analysis_trans_expand(p, g, start, cur_node, results) < 0) {
+ goto cleanup;
+ }
+ cur_node->color = APOL_INFOFLOW_COLOR_BLACK;
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ edge_list = cur_node->out_edges;
+ } else {
+ edge_list = cur_node->in_edges;
+ }
+ edge_list = apol_infoflow_trans_further_shuffle(p, g, edge_list);
+ if (edge_list == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(edge_list); i++) {
+ edge = (apol_infoflow_edge_t *) apol_vector_get_element(edge_list, i);
+ if (g->direction == APOL_INFOFLOW_OUT) {
+ node = edge->end_node;
+ } else {
+ node = edge->start_node;
+ }
+ if (node->color == APOL_INFOFLOW_COLOR_WHITE) {
+ node->color = APOL_INFOFLOW_COLOR_GREY;
+ node->distance = cur_node->distance + 1;
+ node->parent = cur_node;
+ if (apol_queue_push(queue, node) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_destroy(&edge_list);
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&edge_list);
+ apol_queue_destroy(&queue);
+ return retval;
+}
+
+/******************** infoflow analysis object routines ********************/
+
+int apol_infoflow_analysis_do(const apol_policy_t * p, const apol_infoflow_analysis_t * ia, apol_vector_t ** v,
+ apol_infoflow_graph_t ** g)
+{
+ int retval = -1;
+ if (v != NULL) {
+ *v = NULL;
+ }
+ if (g != NULL) {
+ *g = NULL;
+ }
+ if (p == NULL || ia == NULL || v == NULL || g == NULL || ia->mode == 0 || ia->direction == 0) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_create(p, ia, g) < 0) {
+ goto cleanup;
+ }
+ INFO(p, "%s", "Searching information flow graph.");
+ retval = apol_infoflow_analysis_do_more(p, *g, ia->type, v);
+ cleanup:
+ if (retval != 0) {
+ apol_infoflow_graph_destroy(g);
+ }
+ return retval;
+}
+
+int apol_infoflow_analysis_do_more(const apol_policy_t * p, apol_infoflow_graph_t * g, const char *type, apol_vector_t ** v)
+{
+ const qpol_type_t *start_type;
+ int retval = -1;
+ if (v != NULL) {
+ *v = NULL;
+ }
+ if (p == NULL || g == NULL || type == NULL || v == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ if (apol_query_get_type(p, type, &start_type) < 0) {
+ goto cleanup;
+ }
+
+ if ((*v = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if ((g->mode == APOL_INFOFLOW_MODE_DIRECT &&
+ apol_infoflow_analysis_direct(p, g, type, *v) < 0) ||
+ (g->mode == APOL_INFOFLOW_MODE_TRANS && apol_infoflow_analysis_trans(p, g, type, *v) < 0)) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ return retval;
+}
+
+int apol_infoflow_analysis_trans_further_prepare(const apol_policy_t * p,
+ apol_infoflow_graph_t * g, const char *start_type, const char *end_type)
+{
+ const qpol_type_t *stype, *etype;
+ int retval = -1;
+
+ apol_infoflow_srand(g);
+ if (apol_query_get_type(p, start_type, &stype) < 0 || apol_query_get_type(p, end_type, &etype) < 0) {
+ goto cleanup;
+ }
+ if (g->mode != APOL_INFOFLOW_MODE_TRANS) {
+ ERR(p, "%s", "May only perform further infoflow analysis when the graph is transitive.");
+ goto cleanup;
+ }
+ apol_vector_destroy(&g->further_start);
+ apol_vector_destroy(&g->further_end);
+ if ((g->further_start = apol_vector_create(NULL)) == NULL || (g->further_end = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_infoflow_graph_get_nodes_for_type(p, g, start_type, g->further_start) < 0 ||
+ apol_infoflow_graph_get_nodes_for_type(p, g, end_type, g->further_end) < 0) {
+ goto cleanup;
+ }
+ g->current_start = 0;
+ retval = 0;
+ cleanup:
+ return retval;
+}
+
+int apol_infoflow_analysis_trans_further_next(const apol_policy_t * p, apol_infoflow_graph_t * g, apol_vector_t ** v)
+{
+ apol_infoflow_node_t *start_node;
+ int retval = -1;
+ if (p == NULL || g == NULL || v == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (*v == NULL) {
+ *v = apol_vector_create(infoflow_result_free);
+ }
+ if (g->further_start == NULL) {
+ ERR(p, "%s", "Infoflow graph was not prepared yet.");
+ goto cleanup;
+ }
+ start_node = apol_vector_get_element(g->further_start, g->current_start);
+ if (apol_infoflow_analysis_trans_further(p, g, start_node, *v) < 0) {
+ goto cleanup;
+ }
+ g->current_start++;
+ if (g->current_start >= apol_vector_get_size(g->further_start)) {
+ g->current_start = 0;
+ }
+ retval = 0;
+ cleanup:
+ return retval;
+}
+
+apol_infoflow_analysis_t *apol_infoflow_analysis_create(void)
+{
+ return calloc(1, sizeof(apol_infoflow_analysis_t));
+}
+
+void apol_infoflow_analysis_destroy(apol_infoflow_analysis_t ** ia)
+{
+ if (*ia != NULL) {
+ free((*ia)->type);
+ free((*ia)->result);
+ apol_vector_destroy(&(*ia)->intermed);
+ apol_vector_destroy(&(*ia)->class_perms);
+ free(*ia);
+ *ia = NULL;
+ }
+}
+
+int apol_infoflow_analysis_set_mode(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int mode)
+{
+ switch (mode) {
+ case APOL_INFOFLOW_MODE_DIRECT:
+ case APOL_INFOFLOW_MODE_TRANS:
+ {
+ ia->mode = mode;
+ break;
+ }
+ default:
+ {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_dir(const apol_policy_t * p, apol_infoflow_analysis_t * ia, unsigned int dir)
+{
+ switch (dir) {
+ case APOL_INFOFLOW_IN:
+ case APOL_INFOFLOW_OUT:
+ case APOL_INFOFLOW_BOTH:
+ case APOL_INFOFLOW_EITHER:
+ {
+ ia->direction = dir;
+ break;
+ }
+ default:
+ {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_type(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *name)
+{
+ if (name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &ia->type, NULL, name);
+}
+
+static int compare_class_perm_by_class_name(const void *in_op, const void *class_name, void *unused __attribute__ ((unused)))
+{
+ const apol_obj_perm_t *op = (const apol_obj_perm_t *)in_op;
+ const char *name = (const char *)class_name;
+
+ return strcmp(apol_obj_perm_get_obj_name(op), name);
+}
+
+int apol_infoflow_analysis_append_intermediate(const apol_policy_t * policy, apol_infoflow_analysis_t * ia, const char *type)
+{
+ char *tmp = NULL;
+ if (type == NULL) {
+ apol_vector_destroy(&ia->intermed);
+ return 0;
+ }
+ if (ia->intermed == NULL && (ia->intermed = apol_vector_create(free)) == NULL) {
+ ERR(policy, "Error appending type to analysis: %s", strerror(ENOMEM));
+ return -1;
+ }
+ if ((tmp = strdup(type)) == NULL || apol_vector_append(ia->intermed, tmp) < 0) {
+ free(tmp);
+ ERR(policy, "Error appending type to analysis: %s", strerror(ENOMEM));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_append_class_perm(const apol_policy_t * p,
+ apol_infoflow_analysis_t * ia, const char *class_name, const char *perm_name)
+{
+ apol_obj_perm_t *op = NULL;
+ size_t i;
+
+ if (p == NULL || ia == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (class_name == NULL) {
+ apol_vector_destroy(&ia->class_perms);
+ return 0;
+ }
+ if (perm_name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (ia->class_perms == NULL && (ia->class_perms = apol_vector_create(apol_obj_perm_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+
+ if (apol_vector_get_index(ia->class_perms, (void *)class_name, compare_class_perm_by_class_name, NULL, &i) < 0) {
+ if (perm_name) {
+ if ((op = apol_obj_perm_create()) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ if (apol_obj_perm_set_obj_name(op, class_name) ||
+ apol_obj_perm_append_perm(op, perm_name) || apol_vector_append(ia->class_perms, op)) {
+ ERR(p, "%s", strerror(errno));
+ apol_obj_perm_free(op);
+ return -1;
+ }
+ } else {
+ return 0; /* nothing to clear; done */
+ }
+ } else {
+ op = apol_vector_get_element(ia->class_perms, i);
+ if (apol_obj_perm_append_perm(op, perm_name)) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_min_weight(const apol_policy_t * p
+ __attribute__ ((unused)), apol_infoflow_analysis_t * ia, int min_weight)
+{
+ if (min_weight <= 0) {
+ ia->min_weight = 0;
+ } else if (min_weight >= APOL_PERMMAP_MAX_WEIGHT) {
+ ia->min_weight = APOL_PERMMAP_MAX_WEIGHT;
+ } else {
+ ia->min_weight = min_weight;
+ }
+ return 0;
+}
+
+int apol_infoflow_analysis_set_result_regex(const apol_policy_t * p, apol_infoflow_analysis_t * ia, const char *result)
+{
+ return apol_query_set(p, &ia->result, NULL, result);
+}
+
+/*************** functions to access infoflow results ***************/
+
+unsigned int apol_infoflow_result_get_dir(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return 0;
+ }
+ return result->direction;
+}
+
+const qpol_type_t *apol_infoflow_result_get_start_type(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return result->start_type;
+}
+
+const qpol_type_t *apol_infoflow_result_get_end_type(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return result->end_type;
+}
+
+unsigned int apol_infoflow_result_get_length(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return 0;
+ }
+ assert(result->length != 0);
+ return result->length;
+}
+
+const apol_vector_t *apol_infoflow_result_get_steps(const apol_infoflow_result_t * result)
+{
+ if (!result) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return result->steps;
+}
+
+const qpol_type_t *apol_infoflow_step_get_start_type(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return step->start_type;
+}
+
+const qpol_type_t *apol_infoflow_step_get_end_type(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return step->end_type;
+}
+
+int apol_infoflow_step_get_weight(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return -1;
+ }
+ return step->weight;
+}
+
+const apol_vector_t *apol_infoflow_step_get_rules(const apol_infoflow_step_t * step)
+{
+ if (!step) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return step->rules;
+}
+
+/******************** protected functions ********************/
+
+apol_infoflow_result_t *infoflow_result_create_from_infoflow_result(const apol_infoflow_result_t * result)
+{
+ apol_infoflow_result_t *new_r = NULL;
+ apol_infoflow_step_t *step, *new_step;
+ size_t i;
+ int retval = -1;
+
+ if ((new_r = calloc(1, sizeof(*new_r))) == NULL ||
+ (new_r->steps = apol_vector_create_with_capacity(apol_vector_get_size(result->steps), apol_infoflow_step_free)) == NULL)
+ {
+ goto cleanup;
+ }
+ new_r->start_type = result->start_type;
+ new_r->end_type = result->end_type;
+ new_r->direction = result->direction;
+ new_r->length = result->length;
+ for (i = 0; i < apol_vector_get_size(result->steps); i++) {
+ step = (apol_infoflow_step_t *) apol_vector_get_element(result->steps, i);
+ if ((new_step = calloc(1, sizeof(*new_step))) == NULL ||
+ (new_step->rules = apol_vector_create_from_vector(step->rules, NULL, NULL, NULL)) == NULL ||
+ apol_vector_append(new_r->steps, new_step) < 0) {
+ apol_infoflow_step_free(new_step);
+ goto cleanup;
+ }
+ new_step->start_type = step->start_type;
+ new_step->end_type = step->end_type;
+ new_step->weight = step->weight;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ infoflow_result_free(new_r);
+ return NULL;
+ }
+ return new_r;
+}
+
+void infoflow_result_free(void *result)
+{
+ if (result != NULL) {
+ apol_infoflow_result_t *r = (apol_infoflow_result_t *) result;
+ apol_vector_destroy(&r->steps);
+ free(r);
+ }
+}
diff --git a/libapol/src/isid-query.c b/libapol/src/isid-query.c
new file mode 100644
index 0000000..9cc6211
--- /dev/null
+++ b/libapol/src/isid-query.c
@@ -0,0 +1,123 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about initial SIDs
+ * within a policy. The caller obtains a query object, fills in its
+ * parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_isid_query
+{
+ char *name;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+/******************** genfscon queries ********************/
+
+int apol_isid_get_by_query(const apol_policy_t * p, const apol_isid_query_t * i, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ const qpol_isid_t *isid = NULL;
+ *v = NULL;
+ if (qpol_policy_get_isid_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&isid) < 0) {
+ goto cleanup;
+ }
+ if (i != NULL) {
+ const char *name;
+ const qpol_context_t *context;
+ if (qpol_isid_get_name(p->p, isid, &name) < 0 || qpol_isid_get_context(p->p, isid, &context) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, name, i->name, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ retval2 = apol_compare_context(p, context, i->context, i->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)isid)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_isid_query_t *apol_isid_query_create(void)
+{
+ return calloc(1, sizeof(apol_isid_query_t));
+}
+
+void apol_isid_query_destroy(apol_isid_query_t ** i)
+{
+ if (*i != NULL) {
+ free((*i)->name);
+ apol_context_destroy(&((*i)->context));
+ free(*i);
+ *i = NULL;
+ }
+}
+
+int apol_isid_query_set_name(const apol_policy_t * p, apol_isid_query_t * i, const char *name)
+{
+ return apol_query_set(p, &i->name, NULL, name);
+}
+
+int apol_isid_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_isid_query_t * i, apol_context_t * context, unsigned int range_match)
+{
+ if (i->context != NULL) {
+ apol_context_destroy(&i->context);
+ }
+ i->context = context;
+ i->flags = (i->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
diff --git a/libapol/src/libapol.map b/libapol/src/libapol.map
new file mode 100644
index 0000000..4894374
--- /dev/null
+++ b/libapol/src/libapol.map
@@ -0,0 +1,86 @@
+VERS_4.0{
+ global:
+ apol_attr_*;
+ apol_avrule_*;
+ apol_bool_*;
+ apol_bst_*;
+ apol_cat_*;
+ apol_class_*;
+ apol_common_*;
+ apol_cond_*;
+ apol_config_*;
+ apol_constraint_*;
+ apol_context_*;
+ apol_domain_*;
+ apol_file_*;
+ apol_fs_use_*;
+ apol_genfscon_*;
+ apol_get_*;
+ apol_handle_msg;
+ apol_infoflow_*;
+ apol_ipv4_addr_render;
+ apol_ipv6_addr_render;
+ apol_isid_*;
+ apol_level_*;
+ apol_mls_*;
+ apol_netifcon_*;
+ apol_nodecon_*;
+ apol_objclass_to_str;
+ apol_perm_*;
+ apol_permmap_*;
+ apol_policy_*;
+ apol_policy_path_*;
+ apol_portcon_*;
+ apol_protocol_to_str;
+ apol_qpol_context_render;
+ apol_range_trans_*;
+ apol_relabel_*;
+ apol_role_*;
+ apol_role_allow_*;
+ apol_role_trans_*;
+ apol_rule_type_to_str;
+ apol_str_*;
+ apol_syn_*;
+ apol_terule_*;
+ apol_type_*;
+ apol_types_relation_*;
+ apol_user_*;
+ apol_validatetrans_*;
+ apol_vector_*;
+ libapol_get_version;
+ local: *;
+};
+
+VERS_4.1{
+ global:
+ apol_avrule_query_set_all_perms;
+ apol_bst_inorder_map;
+ apol_context_convert;
+ apol_context_create_from_literal;
+ apol_domain_trans_analysis_append_class;
+ apol_domain_trans_analysis_append_perm;
+ apol_mls_level_convert;
+ apol_mls_level_create_from_literal;
+ apol_mls_level_is_literal;
+ apol_mls_level_validate;
+ apol_mls_range_convert;
+ apol_mls_range_create_from_literal;
+ apol_mls_range_create_from_string;
+ apol_mls_range_is_literal;
+ apol_nodecon_query_set_protocol;
+ apol_policy_build_domain_trans_table;
+ apol_policy_get_permmap;
+ apol_policy_open_permmap;
+ apol_policy_reset_domain_trans_table;
+ apol_policy_save_permmap;
+ apol_policy_set_permmap;
+ apol_portcon_query_set_protocol;
+ apol_str_to_protocol;
+ apol_str_to_objclass;
+} VERS_4.0;
+
+VERS_4.2{
+ global:
+ apol_permissive_*;
+ apol_polcap_*;
+} VERS_4.1;
diff --git a/libapol/src/mls-query.c b/libapol/src/mls-query.c
new file mode 100644
index 0000000..6fccc54
--- /dev/null
+++ b/libapol/src/mls-query.c
@@ -0,0 +1,248 @@
+/**
+ * @file
+ * Implementation for querying MLS components.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <qpol/iterator.h>
+
+#include "policy-query-internal.h"
+#include <apol/vector.h>
+
+struct apol_level_query
+{
+ char *sens_name, *cat_name;
+ unsigned int flags;
+ regex_t *sens_regex, *cat_regex;
+};
+
+struct apol_cat_query
+{
+ char *cat_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_mls_sens_compare(const apol_policy_t * p, const char *sens1, const char *sens2)
+{
+ const qpol_level_t *level_datum1, *level_datum2;
+ if (qpol_policy_get_level_by_name(p->p, sens1, &level_datum1) < 0 ||
+ qpol_policy_get_level_by_name(p->p, sens2, &level_datum2) < 0) {
+ return -1;
+ }
+ if (level_datum1 == level_datum2) {
+ return 1;
+ }
+ return 0;
+}
+
+int apol_mls_cats_compare(const apol_policy_t * p, const char *cat1, const char *cat2)
+{
+ const qpol_cat_t *qcat1, *qcat2;
+ if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) {
+ return -1;
+ }
+ if (qcat1 == qcat2) {
+ return 1;
+ }
+ return 0;
+}
+
+/******************** level queries ********************/
+
+int apol_level_get_by_query(const apol_policy_t * p, apol_level_query_t * l, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter, *cat_iter = NULL;
+ int retval = -1, append_level;
+ *v = NULL;
+ if (qpol_policy_get_level_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_level_t *level;
+ unsigned char isalias;
+ if (qpol_iterator_get_item(iter, (void **)&level) < 0 || qpol_level_get_isalias(p->p, level, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ append_level = 1;
+ if (l != NULL) {
+ int compval = apol_compare_level(p,
+ level, l->sens_name,
+ l->flags, &(l->sens_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (qpol_level_get_cat_iter(p->p, level, &cat_iter) < 0) {
+ goto cleanup;
+ }
+ append_level = 0;
+ for (; !qpol_iterator_end(cat_iter); qpol_iterator_next(cat_iter)) {
+ qpol_cat_t *cat;
+ if (qpol_iterator_get_item(cat_iter, (void **)&cat) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare_cat(p, cat, l->cat_name, l->flags, &(l->cat_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ append_level = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&cat_iter);
+ }
+ if (append_level && apol_vector_append(*v, level)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&cat_iter);
+ return retval;
+}
+
+apol_level_query_t *apol_level_query_create(void)
+{
+ return calloc(1, sizeof(apol_level_query_t));
+}
+
+void apol_level_query_destroy(apol_level_query_t ** l)
+{
+ if (*l != NULL) {
+ free((*l)->sens_name);
+ free((*l)->cat_name);
+ apol_regex_destroy(&(*l)->sens_regex);
+ apol_regex_destroy(&(*l)->cat_regex);
+ free(*l);
+ *l = NULL;
+ }
+}
+
+int apol_level_query_set_sens(const apol_policy_t * p, apol_level_query_t * l, const char *name)
+{
+ return apol_query_set(p, &l->sens_name, &l->sens_regex, name);
+}
+
+int apol_level_query_set_cat(const apol_policy_t * p, apol_level_query_t * l, const char *name)
+{
+ return apol_query_set(p, &l->cat_name, &l->cat_regex, name);
+}
+
+int apol_level_query_set_regex(const apol_policy_t * p, apol_level_query_t * l, int is_regex)
+{
+ return apol_query_set_regex(p, &l->flags, is_regex);
+}
+
+/******************** category queries ********************/
+
+int apol_cat_get_by_query(const apol_policy_t * p, apol_cat_query_t * c, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_cat_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_cat_t *cat;
+ unsigned char isalias;
+ if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_isalias(p->p, cat, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ if (c != NULL) {
+ int compval = apol_compare_cat(p,
+ cat, c->cat_name,
+ c->flags, &(c->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, cat)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_cat_query_t *apol_cat_query_create(void)
+{
+ return calloc(1, sizeof(apol_cat_query_t));
+}
+
+void apol_cat_query_destroy(apol_cat_query_t ** c)
+{
+ if (*c != NULL) {
+ free((*c)->cat_name);
+ apol_regex_destroy(&(*c)->regex);
+ free(*c);
+ *c = NULL;
+ }
+}
+
+int apol_cat_query_set_cat(const apol_policy_t * p, apol_cat_query_t * c, const char *name)
+{
+ return apol_query_set(p, &c->cat_name, &c->regex, name);
+}
+
+int apol_cat_query_set_regex(const apol_policy_t * p, apol_cat_query_t * c, int is_regex)
+{
+ return apol_query_set_regex(p, &c->flags, is_regex);
+}
diff --git a/libapol/src/mls_level.c b/libapol/src/mls_level.c
new file mode 100644
index 0000000..26a1469
--- /dev/null
+++ b/libapol/src/mls_level.c
@@ -0,0 +1,771 @@
+/**
+ * @file
+ * Implementation of apol_mls_level class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <apol/mls_level.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "policy-query-internal.h"
+
+#include <qpol/iterator.h>
+#include <apol/vector.h>
+
+struct apol_mls_level
+{
+ char *sens;
+ apol_vector_t *cats; // if NULL, then level is incomplete
+ char *literal_cats;
+};
+
+/********************* miscellaneous routines *********************/
+
+/* Given a category datum and a category name, returns < 0 if a has
+ * higher value than b, > 0 if b is higher according to the given
+ * policy. If the two are equal or upon error, return 0.
+ */
+static int apol_mls_cat_vector_compare(const void *a, const void *b, void *data)
+{
+ const qpol_cat_t *cat1 = a;
+ const char *cat2_name = b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ const qpol_cat_t *cat2;
+ uint32_t cat_value1, cat_value2;
+ if (qpol_policy_get_cat_by_name(p->p, cat2_name, &cat2) < 0) {
+ return 0;
+ }
+ if (qpol_cat_get_value(p->p, cat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, cat2, &cat_value2) < 0) {
+ return 0;
+ }
+ return (cat_value2 - cat_value1);
+}
+
+/**
+ * Given two category names, returns < 0 if a has higher value than b,
+ * > 0 if b is higher. The comparison is against the categories'
+ * values according to the supplied policy. If the two are equal or
+ * upon error, return 0.
+ *
+ * @param a First category name to compare.
+ * @param b Other name to compare.
+ * @param data Pointer to a policy to which use for comparison.
+ *
+ * @return <0, 0, or >0 if a is less than, equal, or greater than b,
+ * respectively.
+ */
+static int apol_mls_cat_name_compare(const void *a, const void *b, void *data)
+{
+ const char *cat1 = a;
+ const char *cat2 = b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ const qpol_cat_t *qcat1, *qcat2;
+ uint32_t cat_value1, cat_value2;
+ if (qpol_policy_get_cat_by_name(p->p, cat1, &qcat1) < 0 || qpol_policy_get_cat_by_name(p->p, cat2, &qcat2) < 0) {
+ return 0;
+ }
+ if (qpol_cat_get_value(p->p, qcat1, &cat_value1) < 0 || qpol_cat_get_value(p->p, qcat2, &cat_value2) < 0) {
+ return 0;
+ }
+ return (cat_value1 - cat_value2);
+}
+
+/********************* level *********************/
+
+apol_mls_level_t *apol_mls_level_create(void)
+{
+ apol_mls_level_t *l;
+ if ((l = calloc(1, sizeof(*l))) == NULL || (l->cats = apol_vector_create(free)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_mls_level(const apol_mls_level_t * level)
+{
+ apol_mls_level_t *l;
+ if ((l = calloc(1, sizeof(*l))) == NULL) {
+ return NULL;
+ }
+ if (level != NULL) {
+ if ((level->sens != NULL) && (l->sens = strdup(level->sens)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ if ((level->cats != NULL) &&
+ (l->cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ if ((level->literal_cats != NULL) && (l->literal_cats = strdup(level->literal_cats)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ }
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_string(const apol_policy_t * p, const char *mls_level_string)
+{
+ if (p == NULL || mls_level_string == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_level_string);
+ if (l == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return NULL;
+ }
+
+ if (apol_mls_level_convert(p, l) < 0) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ free(l->literal_cats);
+ l->literal_cats = NULL;
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_literal(const char *mls_level_string)
+{
+ apol_mls_level_t *l;
+ char *colon;
+ if (mls_level_string == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((l = calloc(1, sizeof(*l))) == NULL) {
+ return NULL;
+ }
+ if ((colon = strchr(mls_level_string, ':')) != NULL) {
+ // both a sensitivity and 1 or more categories
+ if (colon == mls_level_string) {
+ apol_mls_level_destroy(&l);
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((l->sens = strndup(mls_level_string, colon - mls_level_string)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ // store everything after the colon as the category string
+ if ((l->literal_cats = strdup(colon + 1)) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ apol_str_trim(l->literal_cats);
+ } else {
+ // no category, just a sensitivity
+ if ((l->sens = strdup(mls_level_string)) == NULL || (l->literal_cats = strdup("")) == NULL) {
+ apol_mls_level_destroy(&l);
+ return NULL;
+ }
+ }
+ apol_str_trim(l->sens);
+ return l;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_qpol_mls_level(const apol_policy_t * p, const qpol_mls_level_t * qpol_level)
+{
+ apol_mls_level_t *lvl = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_cat_t *tmp_cat = NULL;
+ const char *tmp = NULL;
+ int error = 0;
+
+ if (!p || !qpol_level) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto err;
+ }
+
+ if ((lvl = apol_mls_level_create()) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_mls_level_get_sens_name(p->p, qpol_level, &tmp) || qpol_mls_level_get_cat_iter(p->p, qpol_level, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_set_sens(p, lvl, tmp) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_cat) < 0 || qpol_cat_get_name(p->p, tmp_cat, &tmp) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, lvl, tmp) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ qpol_iterator_destroy(&iter);
+ return lvl;
+
+ err:
+ apol_mls_level_destroy(&lvl);
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+}
+
+apol_mls_level_t *apol_mls_level_create_from_qpol_level_datum(const apol_policy_t * p, const qpol_level_t * qpol_level)
+{
+ apol_mls_level_t *lvl = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_cat_t *tmp_cat = NULL;
+ const char *tmp = NULL;
+ int error = 0;
+
+ if (!p || !qpol_level) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((lvl = apol_mls_level_create()) == NULL) {
+ ERR(p, "%s", strerror(error));
+ return NULL;
+ }
+ if (qpol_level_get_name(p->p, qpol_level, &tmp)) {
+ error = errno;
+ goto err;
+ }
+ if ((lvl->sens = strdup(tmp)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ if (qpol_level_get_cat_iter(p->p, qpol_level, &iter)) {
+ error = errno;
+ goto err;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_cat_get_name(p->p, tmp_cat, &tmp)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, lvl, tmp)) {
+ error = errno;
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return lvl;
+
+ err:
+ apol_mls_level_destroy(&lvl);
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+}
+
+static void mls_level_free(void *level)
+{
+ if (level != NULL) {
+ apol_mls_level_t *l = level;
+ free(l->sens);
+ apol_vector_destroy(&l->cats);
+ free(l->literal_cats);
+ free(l);
+ }
+}
+
+void apol_mls_level_destroy(apol_mls_level_t ** level)
+{
+ if (!level || !(*level))
+ return;
+ mls_level_free(*level);
+ *level = NULL;
+}
+
+int apol_mls_level_set_sens(const apol_policy_t * p, apol_mls_level_t * level, const char *sens)
+{
+ if (!level) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ return apol_query_set(p, &level->sens, NULL, sens);
+}
+
+const char *apol_mls_level_get_sens(const apol_mls_level_t * level)
+{
+ if (!level) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return level->sens;
+}
+
+int apol_mls_level_append_cats(const apol_policy_t * p, apol_mls_level_t * level, const char *cats)
+{
+ char *new_cat = NULL;
+ if (!level || !cats || level->cats == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (level->cats == NULL && (level->cats = apol_vector_create(free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ if ((new_cat = strdup(cats)) == NULL || apol_vector_append(level->cats, (void *)new_cat) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(new_cat);
+ return -1;
+ }
+ apol_vector_sort(level->cats, apol_str_strcmp, NULL);
+ return 0;
+}
+
+const apol_vector_t *apol_mls_level_get_cats(const apol_mls_level_t * level)
+{
+ if (!level || level->cats == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return level->cats;
+}
+
+int apol_mls_level_compare(const apol_policy_t * p, const apol_mls_level_t * l1, const apol_mls_level_t * l2)
+{
+ const qpol_level_t *level_datum1, *level_datum2;
+ int level1_sens, level2_sens, sens_cmp;
+ size_t l1_size, l2_size, i, j;
+ int m_list, ucat = 0;
+ apol_vector_t *cat_list_master, *cat_list_subset;
+ if (l2 == NULL) {
+ return APOL_MLS_EQ;
+ }
+ if ((l1 != NULL && l1->cats == NULL) || (l2->cats == NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (qpol_policy_get_level_by_name(p->p, l1->sens, &level_datum1) < 0 ||
+ qpol_policy_get_level_by_name(p->p, l2->sens, &level_datum2) < 0) {
+ return -1;
+ }
+
+ /* compare the level's senstitivity value */
+ if (qpol_level_get_value(p->p, level_datum1, (uint32_t *) (&level1_sens)) < 0 ||
+ qpol_level_get_value(p->p, level_datum2, (uint32_t *) (&level2_sens)) < 0) {
+ return -1;
+ }
+ sens_cmp = level1_sens - level2_sens;
+
+ /* determine if all the categories in one level are in the other set */
+ l1_size = apol_vector_get_size(l1->cats);
+ l2_size = apol_vector_get_size(l2->cats);
+ if (l1_size < l2_size) {
+ m_list = 2;
+ cat_list_master = l2->cats;
+ cat_list_subset = l1->cats;
+ } else {
+ m_list = 1;
+ cat_list_master = l1->cats;
+ cat_list_subset = l2->cats;
+ }
+ for (i = 0; i < apol_vector_get_size(cat_list_subset); i++) {
+ char *cat = (char *)apol_vector_get_element(cat_list_subset, i);
+ if (apol_vector_get_index(cat_list_master, cat, apol_mls_cat_name_compare, (void *)p, &j) < 0) {
+ ucat = 1;
+ break;
+ }
+ }
+
+ if (!sens_cmp && !ucat && l1_size == l2_size)
+ return APOL_MLS_EQ;
+ if (sens_cmp >= 0 && m_list == 1 && !ucat)
+ return APOL_MLS_DOM;
+ if (sens_cmp <= 0 && (m_list == 2 || l1_size == l2_size) && !ucat)
+ return APOL_MLS_DOMBY;
+ return APOL_MLS_INCOMP;
+}
+
+int apol_mls_level_validate(const apol_policy_t * p, const apol_mls_level_t * level)
+{
+ const qpol_level_t *level_datum;
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *cat_vector;
+ int retval = -1;
+ size_t i, j;
+
+ if (p == NULL || level == NULL || level->cats == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (level->sens == NULL) {
+ return 0;
+ }
+ if (qpol_policy_get_level_by_name(p->p, level->sens, &level_datum) < 0 ||
+ qpol_level_get_cat_iter(p->p, level_datum, &iter) < 0) {
+ return -1;
+ }
+ if ((cat_vector = apol_vector_create_from_iter(iter, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(level->cats); i++) {
+ char *cat_name = (char *)apol_vector_get_element(level->cats, i);
+ if (apol_vector_get_index(cat_vector, cat_name, apol_mls_cat_vector_compare, (void *)p, &j) < 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+
+ retval = 1;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&cat_vector);
+ return retval;
+}
+
+char *apol_mls_level_render(const apol_policy_t * p, const apol_mls_level_t * level)
+{
+ char *rt = NULL;
+ const char *name = NULL, *sens_name = NULL, *cat_name = NULL;
+ char *retval = NULL;
+ int cur;
+ const qpol_cat_t *cur_cat = NULL, *next_cat = NULL;
+ uint32_t cur_cat_val, next_cat_val, far_cat_val;
+ apol_vector_t *cats = NULL;
+ size_t sz = 0, n_cats = 0, i;
+
+ if (!level || (p == NULL && level->cats != NULL)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ sens_name = level->sens;
+ if (!sens_name)
+ goto cleanup;
+ if (apol_str_append(&rt, &sz, sens_name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (level->cats != NULL) {
+ if ((cats = apol_vector_create_from_vector(level->cats, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ n_cats = apol_vector_get_size(cats);
+ }
+ if (n_cats == 0) {
+ if (level->literal_cats != NULL && level->literal_cats[0] != '\0') {
+ if (apol_str_appendf(&rt, &sz, ":%s", level->literal_cats)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ }
+ retval = rt;
+ goto cleanup;
+ }
+ apol_vector_sort(cats, apol_mls_cat_name_compare, (void *)p);
+
+ cat_name = (char *)apol_vector_get_element(cats, 0);
+ if (!cat_name)
+ goto cleanup;
+
+ if (apol_str_appendf(&rt, &sz, ":%s", cat_name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = 0; /* current value to compare with cat[i] */
+ for (i = 1; i < n_cats; i++) { /* we've already appended the first category */
+ /* get the value of cats[cur] */
+ cat_name = (char *)apol_vector_get_element(cats, cur);
+ if (qpol_policy_get_cat_by_name(p->p, cat_name, &cur_cat))
+ goto cleanup;
+ if (qpol_cat_get_value(p->p, cur_cat, &cur_cat_val))
+ goto cleanup;
+
+ /* get the value of cats[i] */
+ cat_name = (char *)apol_vector_get_element(cats, i);
+ if (qpol_policy_get_cat_by_name(p->p, cat_name, &next_cat))
+ goto cleanup;
+ if (qpol_cat_get_value(p->p, next_cat, &next_cat_val))
+ goto cleanup;
+
+ if (next_cat_val == cur_cat_val + 1) {
+ if (i + 1 == n_cats) { /* last category is next; append "." */
+ if (qpol_cat_get_name(p->p, next_cat, &name))
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, ".%s", name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = i;
+ } else {
+ const qpol_cat_t *far_cat = NULL; /* category 2 in front of cur */
+ cat_name = (char *)apol_vector_get_element(cats, i + 1);
+ if (qpol_policy_get_cat_by_name(p->p, cat_name, &far_cat))
+ goto cleanup;
+ if (qpol_cat_get_value(p->p, far_cat, &far_cat_val))
+ goto cleanup;
+ if (far_cat_val == cur_cat_val + 2) {
+ cur++;
+ } else { /* far_cat isn't consecutive wrt cur/next_cat; append it */
+ if (qpol_cat_get_name(p->p, next_cat, &name))
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, ".%s", name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = i;
+ }
+ }
+ } else { /* next_cat isn't consecutive to cur_cat; append it */
+ if (qpol_cat_get_name(p->p, next_cat, &name))
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, ", %s", name)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ cur = i;
+ }
+ }
+
+ retval = rt;
+ cleanup:
+ apol_vector_destroy(&cats);
+ if (retval != rt) {
+ free(rt);
+ }
+ return retval;
+}
+
+int apol_mls_level_convert(const apol_policy_t * p, apol_mls_level_t * level)
+{
+ const char *tmp, *cat_name;
+ char **tokens = NULL, *next = NULL;
+ size_t num_tokens = 1, i;
+ qpol_iterator_t *iter = NULL;
+ const qpol_level_t *sens = NULL;
+ const qpol_cat_t *cat1 = NULL, *cat2 = NULL, *tmp_cat = NULL;
+ uint32_t val1 = 0, val2 = 0, tmp_val = 0;
+ unsigned char tmp_isalias = 0;
+
+ int error = 0;
+ if (p == NULL || level == NULL || level->literal_cats == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ apol_vector_destroy(&level->cats);
+ if (level->literal_cats[0] == '\0') {
+ if ((level->cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ return 0;
+ }
+
+ for (tmp = level->literal_cats; *tmp; tmp++) {
+ if ((next = strchr(tmp, ','))) {
+ tmp = next;
+ num_tokens++;
+ }
+ }
+ tokens = calloc(num_tokens, sizeof(char *));
+ if (!tokens) {
+ error = errno;
+ ERR(p, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ if ((level->cats = apol_vector_create_with_capacity(num_tokens, free)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ for (tmp = level->literal_cats, i = 0; *tmp && i < num_tokens; tmp++) {
+ if (isspace(*tmp))
+ continue;
+ next = strchr(tmp, ',');
+ if (next) {
+ tokens[i] = strndup(tmp, next - tmp);
+ if (!tokens[i]) {
+ error = errno;
+ goto err;
+ }
+ tmp = next;
+ next = NULL;
+ i++;
+ } else {
+ tokens[i] = strdup(tmp);
+ if (!tokens[i]) {
+ error = errno;
+ ERR(p, "%s", strerror(ENOMEM));
+ goto err;
+ }
+ i++;
+ if (i != num_tokens) {
+ error = EIO;
+ goto err;
+ }
+ }
+ }
+
+ if (qpol_policy_get_level_by_name(p->p, level->sens, &sens)) {
+ error = errno;
+ goto err;
+ }
+
+ for (i = 0; i < num_tokens; i++) {
+ next = strchr(tokens[i], '.');
+ if (next) {
+ *next = '\0';
+ next++;
+
+ /* get end points of cat range */
+ if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_policy_get_cat_by_name(p->p, next, &cat2)) {
+ error = errno;
+ goto err;
+ }
+
+ /* get end point values */
+ if (qpol_cat_get_value(p->p, cat1, &val1)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_cat_get_value(p->p, cat2, &val2)) {
+ error = errno;
+ goto err;
+ }
+ if (val1 >= val2) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, level, tokens[i])) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_policy_get_cat_iter(p->p, &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&tmp_cat)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_cat_get_isalias(p->p, tmp_cat, &tmp_isalias)) {
+ error = errno;
+ goto err;
+ }
+ if (tmp_isalias)
+ continue;
+ if (qpol_cat_get_value(p->p, tmp_cat, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ if (tmp_val > val1 && tmp_val < val2) {
+ if (qpol_cat_get_name(p->p, tmp_cat, &cat_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, level, cat_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ if (apol_mls_level_append_cats(p, level, next)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ if (qpol_policy_get_cat_by_name(p->p, tokens[i], &cat1)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_mls_level_append_cats(p, level, tokens[i])) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+
+ if (tokens) {
+ for (i = 0; i < num_tokens; i++)
+ free(tokens[i]);
+ free(tokens);
+ }
+
+ qpol_iterator_destroy(&iter);
+ return 0;
+
+ err:
+ if (tokens) {
+ for (i = 0; i < num_tokens; i++)
+ free(tokens[i]);
+ free(tokens);
+ }
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return -1;
+}
+
+int apol_mls_level_is_literal(const apol_mls_level_t * level)
+{
+ if (level == NULL) {
+ return -1;
+ }
+ if (level->literal_cats != NULL) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/libapol/src/mls_range.c b/libapol/src/mls_range.c
new file mode 100644
index 0000000..cefe8ac
--- /dev/null
+++ b/libapol/src/mls_range.c
@@ -0,0 +1,641 @@
+/**
+ * @file
+ * Implementation of apol_mls_range class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <apol/mls_range.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "policy-query-internal.h"
+
+#include <qpol/iterator.h>
+#include <apol/vector.h>
+
+struct apol_mls_range
+{
+ apol_mls_level_t *low, *high;
+};
+
+apol_mls_range_t *apol_mls_range_create(void)
+{
+ return calloc(1, sizeof(apol_mls_range_t));
+}
+
+apol_mls_range_t *apol_mls_range_create_from_mls_range(const apol_mls_range_t * range)
+{
+ apol_mls_range_t *r;
+ if ((r = apol_mls_range_create()) == NULL) {
+ return NULL;
+ }
+ if (range != NULL &&
+ ((r->low = apol_mls_level_create_from_mls_level(range->low)) == NULL ||
+ (r->high = apol_mls_level_create_from_mls_level(range->high)) == NULL)) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ return r;
+}
+
+apol_mls_range_t *apol_mls_range_create_from_string(const apol_policy_t * p, const char *mls_range_string)
+{
+ if (p == NULL || mls_range_string == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_mls_range_t *r = apol_mls_range_create();
+ if (r == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return NULL;
+ }
+ char *dash;
+ if ((dash = strchr(mls_range_string, '-')) == NULL) {
+ // just a low level
+ apol_mls_level_t *l = apol_mls_level_create_from_string(p, mls_range_string);
+ if (l == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->low = l;
+ } else {
+ // both a low and a high level
+ if (dash == mls_range_string) {
+ apol_mls_range_destroy(&r);
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ char *s = strndup(mls_range_string, dash - mls_range_string);
+ if (s == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ apol_mls_level_t *l = apol_mls_level_create_from_string(p, s);
+ if (l == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ free(s);
+ return NULL;
+ }
+ r->low = l;
+ free(s);
+ l = NULL;
+
+ if ((l = apol_mls_level_create_from_string(p, dash + 1)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->high = l;
+ }
+
+ if (apol_mls_range_validate(p, r) <= 0) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ return r;
+}
+
+apol_mls_range_t *apol_mls_range_create_from_literal(const char *mls_range_string)
+{
+ if (mls_range_string == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_mls_range_t *r = apol_mls_range_create();
+ if (r == NULL) {
+ return NULL;
+ }
+ char *dash;
+ if ((dash = strchr(mls_range_string, '-')) == NULL) {
+ // just a low level
+ apol_mls_level_t *l = apol_mls_level_create_from_literal(mls_range_string);
+ if (l == NULL) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->low = l;
+ } else {
+ // both a low and a high level
+ if (dash == mls_range_string) {
+ apol_mls_range_destroy(&r);
+ errno = EINVAL;
+ return NULL;
+ }
+ char *s = strndup(mls_range_string, dash - mls_range_string);
+ if (s == NULL) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ apol_mls_level_t *l = apol_mls_level_create_from_literal(s);
+ if (l == NULL) {
+ apol_mls_range_destroy(&r);
+ free(s);
+ return NULL;
+ }
+ r->low = l;
+ free(s);
+ l = NULL;
+
+ if ((l = apol_mls_level_create_from_literal(dash + 1)) == NULL) {
+ apol_mls_range_destroy(&r);
+ return NULL;
+ }
+ r->high = l;
+ }
+ return r;
+}
+
+apol_mls_range_t *apol_mls_range_create_from_qpol_mls_range(const apol_policy_t * p, const qpol_mls_range_t * qpol_range)
+{
+ apol_mls_range_t *apol_range = NULL;
+ const qpol_mls_level_t *tmp = NULL;
+ apol_mls_level_t *tmp_lvl = NULL;
+ int error = 0;
+
+ if (!p || !qpol_range) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ apol_range = calloc(1, sizeof(apol_mls_range_t));
+ if (!apol_range) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return NULL;
+ }
+
+ /* low */
+ if (qpol_mls_range_get_low_level(p->p, qpol_range, &tmp) ||
+ !(tmp_lvl = apol_mls_level_create_from_qpol_mls_level(p, tmp)) || apol_mls_range_set_low(p, apol_range, tmp_lvl)) {
+ error = errno;
+ apol_mls_level_destroy(&tmp_lvl);
+ goto err;
+ }
+ tmp_lvl = NULL;
+
+ /* high */
+ if (qpol_mls_range_get_high_level(p->p, qpol_range, &tmp) ||
+ !(tmp_lvl = apol_mls_level_create_from_qpol_mls_level(p, tmp)) || apol_mls_range_set_high(p, apol_range, tmp_lvl)) {
+ error = errno;
+ apol_mls_level_destroy(&tmp_lvl);
+ goto err;
+ }
+
+ return apol_range;
+
+ err:
+ apol_mls_range_destroy(&apol_range);
+ errno = error;
+ return NULL;
+}
+
+void apol_mls_range_destroy(apol_mls_range_t ** range)
+{
+ if (!range || !(*range))
+ return;
+
+ if ((*range)->low != (*range)->high) {
+ apol_mls_level_destroy(&((*range)->high));
+ }
+ apol_mls_level_destroy(&((*range)->low));
+ free(*range);
+ *range = NULL;
+}
+
+int apol_mls_range_set_low(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level)
+{
+ if (!range) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (range->low != level) {
+ apol_mls_level_destroy(&(range->low));
+ range->low = level;
+ }
+ return 0;
+}
+
+int apol_mls_range_set_high(const apol_policy_t * p, apol_mls_range_t * range, apol_mls_level_t * level)
+{
+ if (!range) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (range->high != level) {
+ if (range->low != range->high) {
+ apol_mls_level_destroy(&(range->high));
+ }
+ range->high = level;
+ }
+ return 0;
+}
+
+const apol_mls_level_t *apol_mls_range_get_low(const apol_mls_range_t * range)
+{
+ if (!range) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->low;
+}
+
+const apol_mls_level_t *apol_mls_range_get_high(const apol_mls_range_t * range)
+{
+ if (!range) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->high;
+}
+
+int apol_mls_range_compare(const apol_policy_t * p, const apol_mls_range_t * target, const apol_mls_range_t * search,
+ unsigned int range_compare_type)
+{
+ int ans1 = -1, ans2 = -1;
+ if (search == NULL) {
+ return 1;
+ }
+ if (p == NULL || target == NULL || target->low == NULL || search->low == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ /* FIX ME: intersect does not work */
+ if ((range_compare_type & APOL_QUERY_SUB) || (range_compare_type & APOL_QUERY_INTERSECT)) {
+ ans1 = apol_mls_range_contain_subrange(p, target, search);
+ if (ans1 < 0) {
+ return -1;
+ }
+ }
+ if ((range_compare_type & APOL_QUERY_SUPER) || (range_compare_type & APOL_QUERY_INTERSECT)) {
+ ans2 = apol_mls_range_contain_subrange(p, search, target);
+ if (ans2 < 0) {
+ return -1;
+ }
+ }
+ /* EXACT has to come first because its bits are both SUB and SUPER */
+ if ((range_compare_type & APOL_QUERY_EXACT) == APOL_QUERY_EXACT) {
+ return (ans1 && ans2);
+ } else if (range_compare_type & APOL_QUERY_SUB) {
+ return ans1;
+ } else if (range_compare_type & APOL_QUERY_SUPER) {
+ return ans2;
+ } else if (range_compare_type & APOL_QUERY_INTERSECT) {
+ return (ans1 || ans2);
+ }
+ ERR(p, "%s", "Invalid range compare type argument.");
+ errno = EINVAL;
+ return -1;
+}
+
+static int apol_mls_range_does_include_level(const apol_policy_t * p, const apol_mls_range_t * range,
+ const apol_mls_level_t * level)
+{
+ int high_cmp = -1, low_cmp = -1;
+
+ if (range->low != range->high) {
+ low_cmp = apol_mls_level_compare(p, range->low, level);
+ if (low_cmp < 0) {
+ return -1;
+ }
+ }
+ const apol_mls_level_t *high_level = (range->high != NULL ? range->high : range->low);
+ high_cmp = apol_mls_level_compare(p, high_level, level);
+ if (high_cmp < 0) {
+ return -1;
+ }
+
+ if (high_cmp == APOL_MLS_EQ || high_cmp == APOL_MLS_DOM) {
+ if ((low_cmp == APOL_MLS_EQ || low_cmp == APOL_MLS_DOMBY) && range->low != high_level) {
+ return 1;
+ } else if (range->low == high_level) {
+ return apol_mls_sens_compare(p, apol_mls_level_get_sens(range->low), apol_mls_level_get_sens(level));
+ }
+ }
+
+ return 0;
+}
+
+int apol_mls_range_contain_subrange(const apol_policy_t * p, const apol_mls_range_t * range, const apol_mls_range_t * subrange)
+{
+ if (p == NULL || apol_mls_range_validate(p, subrange) != 1) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ /* parent range validity will be checked via
+ * apol_mls_range_include_level() */
+
+ if (apol_mls_range_does_include_level(p, range, subrange->low)) {
+ if (subrange->high == NULL || apol_mls_range_does_include_level(p, range, subrange->high)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int apol_mls_range_validate(const apol_policy_t * p, const apol_mls_range_t * range)
+{
+ int retv;
+
+ if (p == NULL || range == NULL || range->low == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((retv = apol_mls_level_validate(p, range->low)) != 1) {
+ return retv;
+ }
+
+ if (range->high == NULL) {
+ return retv;
+ }
+ if (range->high != range->low && (retv = apol_mls_level_validate(p, range->high)) != 1) {
+ return retv;
+ }
+
+ /* both low and high levels exist, so now check that high
+ * dominates low */
+ retv = apol_mls_level_compare(p, range->low, range->high);
+ if (retv < 0) {
+ return -1;
+ } else if (retv != APOL_MLS_EQ && retv != APOL_MLS_DOMBY) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int mls_range_comp(const void *a, const void *b, void *data)
+{
+ const apol_mls_level_t *l1 = a;
+ const apol_mls_level_t *l2 = b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const qpol_level_t *l;
+ uint32_t low_value, high_value;
+ qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(l1), &l);
+ qpol_level_get_value(q, l, &low_value);
+ qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(l2), &l);
+ qpol_level_get_value(q, l, &high_value);
+ assert(low_value != 0 && high_value != 0);
+ return low_value - high_value;
+}
+
+static int mls_level_name_to_cat_comp(const void *a, const void *b, void *data)
+{
+ const qpol_cat_t *cat = a;
+ const char *name = (const char *)b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const char *cat_name = "";
+ qpol_cat_get_name(q, cat, &cat_name);
+ return strcmp(name, cat_name);
+}
+
+static void mls_level_free(void *elem)
+{
+ apol_mls_level_t *level = elem;
+ apol_mls_level_destroy(&level);
+}
+
+apol_vector_t *apol_mls_range_get_levels(const apol_policy_t * p, const apol_mls_range_t * range)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ apol_vector_t *v = NULL, *catv = NULL;
+ const qpol_level_t *l;
+ uint32_t low_value, high_value, value;
+ int error = 0;
+ qpol_iterator_t *iter = NULL, *catiter = NULL;
+
+ if (p == NULL || range == NULL || range->low == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ apol_mls_level_t *low_level, *high_level;
+ low_level = range->low;
+ if (range->high == NULL) {
+ high_level = low_level;
+ } else {
+ high_level = range->high;
+ }
+ if (qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(low_level), &l) < 0 ||
+ qpol_level_get_value(q, l, &low_value) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_policy_get_level_by_name(q, apol_mls_level_get_sens(high_level), &l) < 0 ||
+ qpol_level_get_value(q, l, &high_value) < 0) {
+ error = errno;
+ goto err;
+ }
+ assert(low_value <= high_value);
+ if ((v = apol_vector_create(mls_level_free)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_policy_get_level_iter(q, &iter) < 0) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const char *name;
+ apol_mls_level_t *ml;
+ if (qpol_iterator_get_item(iter, (void **)&l) < 0 ||
+ qpol_level_get_value(q, l, &value) < 0 || qpol_level_get_name(q, l, &name) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (value < low_value || value > high_value) {
+ continue;
+ }
+ if ((ml = apol_mls_level_create()) == NULL || (apol_mls_level_set_sens(p, ml, name) < 0)) {
+ error = errno;
+ apol_mls_level_destroy(&ml);
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+
+ if (qpol_level_get_cat_iter(q, l, &catiter) < 0 || (catv = apol_vector_create_from_iter(catiter, NULL)) == NULL) {
+ error = errno;
+ goto err;
+ }
+
+ const apol_vector_t *high_cats = apol_mls_level_get_cats(high_level);
+ for (size_t i = 0; i < apol_vector_get_size(high_cats); i++) {
+ char *cat_name = apol_vector_get_element(high_cats, i);
+
+ size_t j;
+ /* do not add categories that are not members of
+ the level */
+ if (apol_vector_get_index(catv, cat_name, mls_level_name_to_cat_comp, q, &j) < 0) {
+ /* this category is not legal under the given policy */
+ continue;
+ }
+ if (apol_mls_level_append_cats(p, ml, cat_name) < 0) {
+ error = errno;
+ apol_mls_level_destroy(&ml);
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ qpol_iterator_destroy(&catiter);
+ apol_vector_destroy(&catv);
+
+ if (apol_vector_append(v, ml) < 0) {
+ error = errno;
+ apol_mls_level_destroy(&ml);
+ ERR(p, "%s", strerror(error));
+ goto err;
+ }
+ }
+ apol_vector_sort(v, mls_range_comp, q);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&catiter);
+ apol_vector_destroy(&catv);
+ return v;
+ err:
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&catiter);
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&catv);
+ errno = error;
+ return NULL;
+}
+
+char *apol_mls_range_render(const apol_policy_t * p, const apol_mls_range_t * range)
+{
+ char *rt = NULL, *retval = NULL;
+ char *sub_str = NULL;
+ int retv;
+ size_t sz = 0;
+
+ if (!range || range->low == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto cleanup;
+ }
+ if (p == NULL && apol_mls_range_is_literal(range) != 1) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ if ((sub_str = apol_mls_level_render(p, range->low)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_str_append(&rt, &sz, sub_str)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ free(sub_str);
+ sub_str = NULL;
+ if (range->high == NULL) {
+ /* no high level set, so skip the rest of this render
+ * function */
+ retval = rt;
+ goto cleanup;
+ }
+ if (p == NULL) {
+ // no policy, so assume that high level dominates low level
+ retv = APOL_MLS_DOM;
+ } else {
+ retv = apol_mls_level_compare(p, range->low, range->high);
+ if (retv < 0) {
+ goto cleanup;
+ }
+ }
+ /* if (high level != low level) */
+ if ((retv == APOL_MLS_DOM || retv == APOL_MLS_DOMBY) && range->high != NULL) {
+ sub_str = apol_mls_level_render(p, range->high);
+ if (!sub_str)
+ goto cleanup;
+ if (apol_str_appendf(&rt, &sz, " - %s", sub_str)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ retval = rt;
+ cleanup:
+ if (retval != rt) {
+ free(rt);
+ }
+ free(sub_str);
+ return retval;
+}
+
+int apol_mls_range_convert(const apol_policy_t * p, apol_mls_range_t * range)
+{
+ if (p == NULL || range == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_mls_level_t *low = range->low;
+ apol_mls_level_t *high = range->high;
+ int retval;
+ if (low != NULL) {
+ retval = apol_mls_level_convert(p, low);
+ if (retval < 0) {
+ return retval;
+ }
+ }
+ if (high != NULL && high != low) {
+ retval = apol_mls_level_convert(p, high);
+ if (retval < 0) {
+ return retval;
+ }
+ }
+ return 0;
+}
+
+int apol_mls_range_is_literal(const apol_mls_range_t * range)
+{
+ if (range == NULL) {
+ return -1;
+ }
+ int ret;
+ if ((ret = apol_mls_level_is_literal(range->low)) != 0) {
+ return ret;
+ }
+ if (range->high != NULL) {
+ ret = apol_mls_level_is_literal(range->high);
+ }
+ return ret;
+}
diff --git a/libapol/src/netcon-query.c b/libapol/src/netcon-query.c
new file mode 100644
index 0000000..7faf0de
--- /dev/null
+++ b/libapol/src/netcon-query.c
@@ -0,0 +1,592 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about portcons,
+ * netifcons, and nodecons within a policy. The caller obtains a
+ * query object, fills in its parameters, and then runs the query; it
+ * obtains a vector of results. Searches are conjunctive -- all
+ * fields of the search query must match for a datum to be added to
+ * the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <apol/render.h>
+
+#include <errno.h>
+#include <string.h>
+
+struct apol_portcon_query
+{
+ int proto;
+ int low, high;
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+struct apol_netifcon_query
+{
+ char *dev;
+ apol_context_t *if_context, *msg_context;
+ unsigned int if_flags, msg_flags;
+};
+
+struct apol_nodecon_query
+{
+ char proto, addr_proto, mask_proto;
+ uint32_t addr[4], mask[4];
+ apol_context_t *context;
+ unsigned int flags;
+};
+
+/******************** portcon queries ********************/
+
+int apol_portcon_get_by_query(const apol_policy_t * p, const apol_portcon_query_t * po, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ *v = NULL;
+ if (qpol_policy_get_portcon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_portcon_t *portcon;
+ if (qpol_iterator_get_item(iter, (void **)&portcon) < 0) {
+ goto cleanup;
+ }
+ if (po != NULL) {
+ uint16_t low, high;
+ uint8_t proto;
+ const qpol_context_t *context;
+ if (qpol_portcon_get_low_port(p->p,
+ portcon, &low) < 0 ||
+ qpol_portcon_get_high_port(p->p,
+ portcon, &high) < 0 ||
+ qpol_portcon_get_protocol(p->p,
+ portcon, &proto) < 0 || qpol_portcon_get_context(p->p, portcon, &context) < 0)
+ {
+ goto cleanup;
+ }
+ if ((po->low >= 0 && ((uint16_t) po->low) != low) ||
+ (po->high >= 0 && ((uint16_t) po->high) != high) || (po->proto >= 0 && ((uint8_t) po->proto) != proto))
+ {
+ continue;
+ }
+ retval2 = apol_compare_context(p, context, po->context, po->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, portcon)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_portcon_query_t *apol_portcon_query_create(void)
+{
+ apol_portcon_query_t *po = calloc(1, sizeof(*po));
+ if (po == NULL) {
+ return NULL;
+ }
+ po->proto = po->low = po->high = -1;
+ return po;
+}
+
+void apol_portcon_query_destroy(apol_portcon_query_t ** po)
+{
+ if (*po != NULL) {
+ apol_context_destroy(&((*po)->context));
+ free(*po);
+ *po = NULL;
+ }
+}
+
+int apol_portcon_query_set_protocol(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int proto)
+{
+ po->proto = proto;
+ return 0;
+}
+
+/**
+ * @deprecated Use apol_portcon_query_set_protocol() instead.
+ */
+int apol_portcon_query_set_proto(apol_policy_t * p, apol_portcon_query_t * po, int proto)
+{
+ return apol_portcon_query_set_protocol(p, po, proto);
+}
+int apol_portcon_query_set_proto(apol_policy_t * p, apol_portcon_query_t * po, int proto) __attribute__ ((deprecated));
+
+int apol_portcon_query_set_low(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int low)
+{
+ po->low = low;
+ return 0;
+}
+
+int apol_portcon_query_set_high(const apol_policy_t * p __attribute__ ((unused)), apol_portcon_query_t * po, int high)
+{
+ po->high = high;
+ return 0;
+}
+
+int apol_portcon_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_portcon_query_t * po, apol_context_t * context, unsigned int range_match)
+{
+ if (po->context != NULL) {
+ apol_context_destroy(&po->context);
+ }
+ po->context = context;
+ po->flags = (po->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_portcon_render(const apol_policy_t * p, const qpol_portcon_t * portcon)
+{
+ char *line = NULL, *retval = NULL;
+ char *buff = NULL;
+ const char *proto_str = NULL;
+ char *context_str = NULL;
+ const qpol_context_t *ctxt = NULL;
+ uint16_t low_port, high_port;
+ uint8_t proto;
+
+ const size_t bufflen = 50; /* arbitrary size big enough to hold port no. */
+ if (!portcon || !p)
+ goto cleanup;
+
+ buff = (char *)calloc(bufflen + 1, sizeof(char));
+ if (!buff) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if (qpol_portcon_get_protocol(p->p, portcon, &proto))
+ goto cleanup;
+
+ if ((proto_str = apol_protocol_to_str(proto)) == NULL) {
+ ERR(p, "%s", "Could not get protocol string.");
+ goto cleanup;
+ }
+ if (qpol_portcon_get_low_port(p->p, portcon, &low_port))
+ goto cleanup;
+ if (qpol_portcon_get_high_port(p->p, portcon, &high_port))
+ goto cleanup;
+ if (low_port == high_port)
+ snprintf(buff, bufflen, "%d", low_port);
+ else
+ snprintf(buff, bufflen, "%d-%d", low_port, high_port);
+
+ if (qpol_portcon_get_context(p->p, portcon, &ctxt))
+ goto cleanup;
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str)
+ goto cleanup;
+
+ line = (char *)calloc(4 + strlen("portcon") + strlen(proto_str) + strlen(buff) + strlen(context_str), sizeof(char));
+ if (!line) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ sprintf(line, "portcon %s %s %s", proto_str, buff, context_str);
+
+ retval = line;
+ cleanup:
+ free(buff);
+ free(context_str);
+ if (retval != line) {
+ free(line);
+ }
+ return retval;
+}
+
+/******************** netifcon queries ********************/
+
+int apol_netifcon_get_by_query(const apol_policy_t * p, const apol_netifcon_query_t * n, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ *v = NULL;
+ if (qpol_policy_get_netifcon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_netifcon_t *netifcon;
+ if (qpol_iterator_get_item(iter, (void **)&netifcon) < 0) {
+ goto cleanup;
+ }
+ if (n != NULL) {
+ const char *name;
+ const qpol_context_t *ifcon, *msgcon;
+ if (qpol_netifcon_get_name(p->p, netifcon, &name) < 0 ||
+ qpol_netifcon_get_if_con(p->p, netifcon, &ifcon) < 0 ||
+ qpol_netifcon_get_msg_con(p->p, netifcon, &msgcon) < 0) {
+ goto cleanup;
+ }
+ retval2 = apol_compare(p, name, n->dev, 0, NULL);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ retval2 = apol_compare_context(p, ifcon, n->if_context, n->if_flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ retval2 = apol_compare_context(p, msgcon, n->msg_context, n->msg_flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)netifcon)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_netifcon_query_t *apol_netifcon_query_create(void)
+{
+ return calloc(1, sizeof(apol_netifcon_query_t));
+}
+
+void apol_netifcon_query_destroy(apol_netifcon_query_t ** n)
+{
+ if (*n != NULL) {
+ free((*n)->dev);
+ apol_context_destroy(&((*n)->if_context));
+ apol_context_destroy(&((*n)->msg_context));
+ free(*n);
+ *n = NULL;
+ }
+}
+
+int apol_netifcon_query_set_device(const apol_policy_t * p, apol_netifcon_query_t * n, const char *dev)
+{
+ return apol_query_set(p, &n->dev, NULL, dev);
+}
+
+int apol_netifcon_query_set_if_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_netifcon_query_t * n, apol_context_t * context, unsigned int range_match)
+{
+ if (n->if_context != NULL) {
+ apol_context_destroy(&n->if_context);
+ }
+ n->if_context = context;
+ n->if_flags = (n->if_flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+int apol_netifcon_query_set_msg_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_netifcon_query_t * n, apol_context_t * context, unsigned int range_match)
+{
+ if (n->msg_context != NULL) {
+ apol_context_destroy(&n->msg_context);
+ }
+ n->msg_context = context;
+ n->msg_flags = (n->msg_flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_netifcon_render(const apol_policy_t * p, const qpol_netifcon_t * netifcon)
+{
+ char *line = NULL, *retval = NULL;
+ char *devcon_str = NULL;
+ char *pktcon_str = NULL;
+ const char *iface_str = NULL;
+ const qpol_context_t *ctxt = NULL;
+
+ if (!netifcon || !p)
+ goto cleanup;
+
+ if (qpol_netifcon_get_if_con(p->p, netifcon, &ctxt))
+ goto cleanup;
+ devcon_str = apol_qpol_context_render(p, ctxt);
+ if (!devcon_str)
+ goto cleanup;
+
+ if (qpol_netifcon_get_msg_con(p->p, netifcon, &ctxt))
+ goto cleanup;
+ pktcon_str = apol_qpol_context_render(p, ctxt);
+ if (!pktcon_str) {
+ goto cleanup;
+ }
+
+ if (qpol_netifcon_get_name(p->p, netifcon, &iface_str))
+ return NULL;
+ line = (char *)calloc(4 + strlen(iface_str) + strlen(devcon_str) + strlen(pktcon_str) + strlen("netifcon"), sizeof(char));
+ if (!line) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ sprintf(line, "netifcon %s %s %s", iface_str, devcon_str, pktcon_str);
+
+ retval = line;
+ cleanup:
+ free(devcon_str);
+ free(pktcon_str);
+ return retval;
+}
+
+/******************** nodecon queries ********************/
+
+int apol_nodecon_get_by_query(const apol_policy_t * p, const apol_nodecon_query_t * n, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1, retval2;
+ qpol_nodecon_t *nodecon = NULL;
+ *v = NULL;
+ if (qpol_policy_get_nodecon_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&nodecon) < 0) {
+ goto cleanup;
+ }
+ if (n != NULL) {
+ unsigned char proto, proto_a, proto_m;
+ uint32_t *addr, *mask;
+ const qpol_context_t *con;
+ if (qpol_nodecon_get_protocol(p->p, nodecon, &proto) < 0 ||
+ qpol_nodecon_get_addr(p->p, nodecon, &addr, &proto_a) < 0 ||
+ qpol_nodecon_get_mask(p->p, nodecon, &mask, &proto_m) < 0 ||
+ qpol_nodecon_get_context(p->p, nodecon, &con) < 0) {
+ goto cleanup;
+ }
+ if (n->proto >= 0 && n->proto != proto) {
+ free(nodecon);
+ continue;
+ }
+ if (n->addr_proto >= 0 &&
+ (n->addr_proto != proto_a ||
+ (proto_a == QPOL_IPV4 && memcmp(n->addr, addr, 1 * sizeof(uint32_t)) != 0) ||
+ (proto_a == QPOL_IPV6 && memcmp(n->addr, addr, 4 * sizeof(uint32_t)) != 0))) {
+ free(nodecon);
+ continue;
+ }
+ if (n->mask_proto >= 0 &&
+ (n->mask_proto != proto_m ||
+ (proto_m == QPOL_IPV4 && memcmp(n->mask, mask, 1 * sizeof(uint32_t)) != 0) ||
+ (proto_m == QPOL_IPV6 && memcmp(n->mask, mask, 4 * sizeof(uint32_t)) != 0))) {
+ free(nodecon);
+ continue;
+ }
+ retval2 = apol_compare_context(p, con, n->context, n->flags);
+ if (retval2 < 0) {
+ goto cleanup;
+ } else if (retval2 == 0) {
+ free(nodecon);
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, nodecon)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ free(nodecon);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_nodecon_query_t *apol_nodecon_query_create(void)
+{
+ apol_nodecon_query_t *n = calloc(1, sizeof(apol_nodecon_query_t));
+ if (n != NULL) {
+ n->proto = n->addr_proto = n->mask_proto = -1;
+ }
+ return n;
+}
+
+void apol_nodecon_query_destroy(apol_nodecon_query_t ** n)
+{
+ if (*n != NULL) {
+ apol_context_destroy(&((*n)->context));
+ free(*n);
+ *n = NULL;
+ }
+}
+
+int apol_nodecon_query_set_protocol(const apol_policy_t * p, apol_nodecon_query_t * n, int proto)
+{
+ if (proto == QPOL_IPV4 || proto == QPOL_IPV6) {
+ n->proto = (char)proto;
+ } else if (proto < 0) {
+ n->proto = -1;
+ } else {
+ ERR(p, "Invalid protocol value %d.", proto);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * @deprecated Use apol_nodecon_query_set_protocol() instead.
+ */
+int apol_nodecon_query_set_proto(apol_policy_t * p, apol_nodecon_query_t * n, int proto)
+{
+ return apol_nodecon_query_set_protocol(p, n, proto);
+}
+int apol_nodecon_query_set_proto(apol_policy_t * p, apol_nodecon_query_t * n, int proto) __attribute__ ((deprecated));
+
+int apol_nodecon_query_set_addr(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * addr, int proto)
+{
+ if (addr == NULL) {
+ n->addr_proto = -1;
+ } else {
+ if (proto == QPOL_IPV4) {
+ memcpy(n->addr, addr, 1 * sizeof(uint32_t));
+ } else if (proto == QPOL_IPV6) {
+ memcpy(n->addr, addr, 4 * sizeof(uint32_t));
+ } else {
+ ERR(p, "Invalid protocol value %d.", proto);
+ return -1;
+ }
+ n->addr_proto = (char)proto;
+ }
+ return 0;
+}
+
+int apol_nodecon_query_set_mask(const apol_policy_t * p, apol_nodecon_query_t * n, uint32_t * mask, int proto)
+{
+ if (mask == NULL) {
+ n->mask_proto = -1;
+ } else {
+ if (proto == QPOL_IPV4) {
+ memcpy(n->mask, mask, 1 * sizeof(uint32_t));
+ } else if (proto == QPOL_IPV6) {
+ memcpy(n->mask, mask, 4 * sizeof(uint32_t));
+ } else {
+ ERR(p, "Invalid protocol value %d.", proto);
+ return -1;
+ }
+ n->mask_proto = (char)proto;
+ }
+ return 0;
+}
+
+int apol_nodecon_query_set_context(const apol_policy_t * p __attribute__ ((unused)),
+ apol_nodecon_query_t * n, apol_context_t * context, unsigned int range_match)
+{
+ if (n->context != NULL) {
+ apol_context_destroy(&n->context);
+ }
+ n->context = context;
+ n->flags = (n->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+char *apol_nodecon_render(const apol_policy_t * p, const qpol_nodecon_t * nodecon)
+{
+ char *line = NULL, *retval = NULL;
+ char *context_str = NULL;
+ char *addr_str = NULL;
+ char *mask_str = NULL;
+ const qpol_context_t *ctxt = NULL;
+ unsigned char protocol, addr_proto, mask_proto;
+ uint32_t *addr = NULL, *mask = NULL;
+
+ if (!nodecon || !p)
+ goto cleanup;
+
+ if (qpol_nodecon_get_protocol(p->p, nodecon, &protocol))
+ goto cleanup;
+ if (qpol_nodecon_get_addr(p->p, nodecon, &addr, &addr_proto))
+ goto cleanup;
+ if (qpol_nodecon_get_mask(p->p, nodecon, &mask, &mask_proto))
+ goto cleanup;
+ switch (protocol) {
+ case QPOL_IPV4:
+ if ((addr_str = apol_ipv4_addr_render(p, addr)) == NULL || (mask_str = apol_ipv4_addr_render(p, mask)) == NULL) {
+ goto cleanup;
+ }
+ break;
+ case QPOL_IPV6:
+ if ((addr_str = apol_ipv6_addr_render(p, addr)) == NULL || (mask_str = apol_ipv6_addr_render(p, mask)) == NULL) {
+ goto cleanup;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (qpol_nodecon_get_context(p->p, nodecon, &ctxt))
+ goto cleanup;
+ context_str = apol_qpol_context_render(p, ctxt);
+ if (!context_str)
+ goto cleanup;
+
+ line = (char *)calloc(4 + strlen("nodecon") + strlen(addr_str) + strlen(mask_str) + strlen(context_str), sizeof(char));
+ if (!line) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ sprintf(line, "nodecon %s %s %s", addr_str, mask_str, context_str);
+
+ retval = line;
+ cleanup:
+ free(addr_str);
+ free(mask_str);
+ free(context_str);
+ return retval;
+}
diff --git a/libapol/src/perm-map.c b/libapol/src/perm-map.c
new file mode 100644
index 0000000..01c4c32
--- /dev/null
+++ b/libapol/src/perm-map.c
@@ -0,0 +1,697 @@
+/**
+ * @file
+ *
+ * Implementation of permission mapping routines.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <apol/perm-map.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+/* use 8k line size */
+#define APOL_LINE_SZ 8192
+
+struct apol_permmap
+{
+ unsigned char mapped; /* true if this class's permissions
+ * were mapped from a file, false if
+ * using default values */
+ apol_vector_t *classes; /* list of apol_permmap_class_t */
+};
+
+/* There is one apol_permmap_class per object class. */
+typedef struct apol_permmap_class
+{
+ unsigned char mapped; /* mask */
+ /** pointer to within a qpol_policy_t that represents this class */
+ const qpol_class_t *c;
+ /** vector of apol_permmap_perm, an element for each permission bit */
+ apol_vector_t *perms;
+} apol_permmap_class_t;
+
+/**
+ * Permission maps: For each object class we need to map all permisions
+ * to either read and/or write, or non similar as is done for the MLS stuff.
+ * This allows us to determine information flow. These mappings will be
+ * loadable so that users can re-map them as they see fit.
+ */
+typedef struct apol_permmap_perm
+{
+ /** name of permission */
+ char *name;
+ /** one of APOL_PERMMAP_READ, etc. */
+ unsigned char map;
+ /** the weight (importance) of this perm. (least) 1 - 10 (most) */
+ int weight;
+} apol_permmap_perm_t;
+
+/* some perms unmapped */
+#define APOL_PERMMAP_RET_UNMAPPED_PERM 0x01
+/* some objects unmapped */
+#define APOL_PERMMAP_RET_UNMAPPED_OBJ 0x02
+/* some perms from file unknown and ignored */
+#define APOL_PERMMAP_RET_UNKNOWN_PERM 0x04
+/* some object from file unknown and ignored */
+#define APOL_PERMMAP_RET_UNKNOWN_OBJ 0x08
+/* not enough classes/perms */
+#define APOL_PERMMAP_RET_NOT_ENOUGH 0x10
+
+/**
+ * Deallocate all space used by an apol_permmap_perm_t, including the
+ * pointer itself.
+ *
+ * @param elem Pointer to free. If NULL then do nothing.
+ */
+static void permmap_perm_free(void *elem)
+{
+ if (elem != NULL) {
+ apol_permmap_perm_t *p = (apol_permmap_perm_t *) elem;
+ free(p->name);
+ free(p);
+ }
+}
+
+/**
+ * Deallocate all space used by an apol_permmap_class_t, including the
+ * pointer itself.
+ *
+ * @param elem Pointer to free. If NULL then do nothing.
+ */
+static void permmap_class_free(void *elem)
+{
+ if (elem != NULL) {
+ apol_permmap_class_t *c = (apol_permmap_class_t *) elem;
+ apol_vector_destroy(&c->perms);
+ free(c);
+ }
+}
+
+/**
+ * Allocate and return a new apol_permmap_perm_t.
+ *
+ * @param name Name of the permission. This function will duplicate
+ * the string.
+ * @param map Direction of information flow. This must be one of
+ * APOL_PERMMAP_UNMAPPED, APOL_PERMMAP_READ, etc.
+ * @param weight Weight of the permission. This must be an integer
+ * from APOL_PERMMAP_MIN_WEIGHT to APOL_PERMMAP_MAX_WEIGHT, inclusive.
+ *
+ * @return A newly allocated apol_permmap_perm_t, or NULL on out of
+ * memory. The caller is responsible for deallocating this pointer
+ * via apol_permmap_perm_free().
+ */
+static apol_permmap_perm_t *apol_permmap_perm_create(const char *name, unsigned char map, int weight)
+{
+ apol_permmap_perm_t *pp;
+ if ((pp = calloc(1, sizeof(*pp))) == NULL) {
+ return NULL;
+ }
+ if ((pp->name = strdup(name)) == NULL) {
+ free(pp);
+ return NULL;
+ }
+ pp->map = map;
+ pp->weight = weight;
+ return pp;
+}
+
+/**
+ * Allocate and return a new permission map from a policy, and
+ * allocates space for defined object classes.
+ *
+ * @param p Policy from which to create permission map.
+ *
+ * @return A newly allocated map, or NULL on error. The caller is
+ * responsible for deallocating this pointer via permmap_destroy().
+ */
+static apol_permmap_t *apol_permmap_create_from_policy(const apol_policy_t * p)
+{
+ apol_permmap_t *t = NULL;
+ qpol_iterator_t *class_iter = NULL, *perm_iter = NULL, *common_iter = NULL;
+ size_t num_obj_classes;
+ int retval = -1;
+
+ if (p == NULL) {
+ goto cleanup;
+ }
+
+ if ((t = (apol_permmap_t *) calloc(1, sizeof(*t))) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_policy_get_class_iter(p->p, &class_iter) < 0 || qpol_iterator_get_size(class_iter, &num_obj_classes) < 0) {
+ goto cleanup;
+ }
+ t->mapped = 0;
+ if ((t->classes = apol_vector_create_with_capacity(num_obj_classes, permmap_class_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(class_iter); qpol_iterator_next(class_iter)) {
+ const qpol_class_t *c;
+ const qpol_common_t *common;
+ apol_permmap_class_t *pc = NULL;
+ apol_permmap_perm_t *pp = NULL;
+ size_t num_unique_perms, num_common_perms = 0;
+ char *name;
+ if (qpol_iterator_get_item(class_iter, (void **)&c) < 0 ||
+ qpol_class_get_perm_iter(p->p, c, &perm_iter) < 0 ||
+ qpol_iterator_get_size(perm_iter, &num_unique_perms) < 0 || qpol_class_get_common(p->p, c, &common) < 0) {
+ goto cleanup;
+ }
+ if (common != NULL &&
+ (qpol_common_get_perm_iter(p->p, common, &common_iter) < 0 ||
+ qpol_iterator_get_size(common_iter, &num_common_perms) < 0)) {
+ goto cleanup;
+ }
+ if ((pc = calloc(1, sizeof(*pc))) == NULL || apol_vector_append(t->classes, pc) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ permmap_class_free(pc);
+ goto cleanup;
+ }
+ pc->mapped = 0;
+ pc->c = c;
+ if ((pc->perms = apol_vector_create_with_capacity(num_unique_perms + num_common_perms, permmap_perm_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ /* initialize with all the class's unique permissions
+ * from provided policy */
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&name) < 0) {
+ goto cleanup;
+ }
+ if ((pp = apol_permmap_perm_create(name, 0, (char)APOL_PERMMAP_MIN_WEIGHT)) == NULL ||
+ apol_vector_append(pc->perms, pp) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ permmap_perm_free(pp);
+ goto cleanup;
+ }
+ }
+ /* next initialize with common permissions */
+ for (; common_iter != NULL && !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) {
+ if (qpol_iterator_get_item(common_iter, (void **)&name) < 0) {
+ goto cleanup;
+ }
+ if ((pp = apol_permmap_perm_create(name, 0, (char)APOL_PERMMAP_MIN_WEIGHT)) == NULL ||
+ apol_vector_append(pc->perms, pp) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ permmap_perm_free(pp);
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ qpol_iterator_destroy(&common_iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&class_iter);
+ qpol_iterator_destroy(&perm_iter);
+ qpol_iterator_destroy(&common_iter);
+ if (retval < 0) {
+ permmap_destroy(&t);
+ }
+ return t;
+}
+
+void permmap_destroy(apol_permmap_t ** p)
+{
+ if (p == NULL || *p == NULL)
+ return;
+ apol_vector_destroy(&(*p)->classes);
+ free(*p);
+ *p = NULL;
+}
+
+/**
+ * Searches through the permission map within a policy, returning the
+ * record for a given object class.
+ *
+ * @param p Policy containing permission map.
+ * @param target Target class name.
+ *
+ * @return Pointer to the class within the permission map, or NULL if
+ * not found or on error.
+ */
+static apol_permmap_class_t *find_permmap_class(const apol_policy_t * p, const char *target)
+{
+ size_t i;
+ const qpol_class_t *target_class;
+ if (qpol_policy_get_class_by_name(p->p, target, &target_class) < 0) {
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) {
+ apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i);
+ if (pc->c == target_class) {
+ return pc;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Searches through the permission map's class, returning the record
+ * for a given permission.
+ *
+ * @param p Policy to use, for error handling.
+ * @param pc Permission map class to search.
+ * @param target Target class name.
+ *
+ * @return Pointer to the permission record within the class, or NULL
+ * if not found or on error.
+ */
+static apol_permmap_perm_t *find_permmap_perm(const apol_policy_t * p
+ __attribute__ ((unused)), const apol_permmap_class_t * pc, const char *target)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(pc->perms); i++) {
+ apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, i);
+ if (strcmp(pp->name, target) == 0) {
+ return pp;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Given a character representation, return its numerical permission
+ * map type.
+ *
+ * @param p Policy containing error handler.
+ * @param perm_name Name of the permission.
+ * @param mapid Character representing map type.
+ *
+ * @return One of APOL_PERMMAP_READ, etc, or APOL_PERMMAP_UNMAPPED on error.
+ */
+static char convert_map_char(const apol_policy_t * p, const char *perm_name, char mapid)
+{
+ switch (mapid) {
+ case 'r':
+ case 'R':
+ return APOL_PERMMAP_READ;
+ case 'w':
+ case 'W':
+ return APOL_PERMMAP_WRITE;
+ case 'b':
+ case 'B':
+ return APOL_PERMMAP_BOTH;
+ case 'n':
+ case 'N':
+ return APOL_PERMMAP_NONE;
+ default:
+ ERR(p, "Invalid map character '%c' for permission %s; permission will be unmapped.", mapid, perm_name);
+ return APOL_PERMMAP_UNMAPPED;
+ }
+}
+
+/**
+ * Goes through a policy's permission map to check that all classes
+ * had an entry within the recently read permission map file.
+ *
+ * @param p Policy containing permission map to check.
+ *
+ * @return 1 if all classes had entries, 0 if any did not.
+ */
+static int are_all_classes_mapped(const apol_policy_t * p)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) {
+ apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i);
+ if (pc->mapped == 0) {
+ const char *class_name;
+ if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) {
+ return 0;
+ }
+ WARN(p, "Some permissions were unmapped for class %s.", class_name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Goes through a class's permissions to check that had an entry
+ * within the recently read permission map file.
+ *
+ * @param p Policy containing permission map to check.
+ * @param pc Class to check.
+ *
+ * @return 1 if all permissions had entries, 0 if any did not.
+ */
+static int are_all_perms_mapped(const apol_policy_t * p, const apol_permmap_class_t * pc)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(pc->perms); i++) {
+ apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, i);
+ if (pp->map == 0) {
+ const char *class_name;
+ if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) {
+ return 0;
+ }
+ WARN(p, "Permission %s was unmapped for class %s.", pp->name, class_name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Parse the individual permission definitions for a given class. If
+ * pc is not NULL then store them into a apol_permmap_class_t,
+ * otherwise discard the read values. If a permission was read, but
+ * does not have an entry within pc, then generate a warning and
+ * continue. Finally, check that all permissions for the given class
+ * have been mapped; if any have not then generate a warning.
+ *
+ * @param p Policy containing permission map.
+ * @param fp File pointer that contains permission map data.
+ * @param num_perms Number of permissions expected to be read.
+ * @param pc Destination to store data; if NULL then do not store data.
+ *
+ * @return 0 on success, > 0 on success but with warnings, < 0 on
+ * error.
+ */
+static int parse_permmap_class(apol_policy_t * p, FILE * fp, size_t num_perms, apol_permmap_class_t * pc)
+{
+ char line[APOL_LINE_SZ], perm_name[APOL_LINE_SZ], *line_ptr = NULL;
+ size_t perms_read = 0;
+ int retval = 0;
+
+ while (fgets(line, sizeof(line), fp) != NULL && perms_read < num_perms) {
+ char mapid;
+ int perm_weight, new_weight;
+ apol_permmap_perm_t *pp;
+
+ line_ptr = line;
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] == '#' || apol_str_is_only_white_space(line_ptr))
+ continue;
+ perms_read++;
+ if (sscanf(line_ptr, "%s %c %d", perm_name, &mapid, &perm_weight) != 3) {
+ /* This may be a perm map file w/o perm weighting. */
+ if (sscanf(line_ptr, "%s %c", perm_name, &mapid) != 2) {
+ ERR(p, "Permission map has an invalid line: \"%s\"", line_ptr);
+ return -1;
+ }
+ perm_weight = APOL_PERMMAP_MAX_WEIGHT;
+ }
+ if (strcmp(perm_name, "class") == 0) {
+ ERR(p, "There were supposed to be %zu permissions, but only %zu were found.", num_perms, perms_read);
+ return -1;
+ }
+ new_weight = perm_weight;
+ if (perm_weight > APOL_PERMMAP_MAX_WEIGHT) {
+ new_weight = APOL_PERMMAP_MAX_WEIGHT;
+ } else if (perm_weight < APOL_PERMMAP_MIN_WEIGHT) {
+ new_weight = APOL_PERMMAP_MIN_WEIGHT;
+ }
+ if (new_weight != perm_weight) {
+ WARN(p, "Permission %s's weight %d is invalid. Setting it to %d instead.", perm_name, perm_weight,
+ new_weight);
+ perm_weight = new_weight;
+ }
+ if (pc != NULL) {
+ if ((pp = find_permmap_perm(p, pc, perm_name)) == NULL) {
+ WARN(p,
+ "Permission %s was defined in the permission map file but not within the policy. It will be ignored.",
+ perm_name);
+ retval |= APOL_PERMMAP_RET_UNKNOWN_PERM;
+ } else {
+ pp->weight = perm_weight;
+ pp->map = convert_map_char(p, perm_name, mapid);
+ }
+ }
+ }
+ if (perms_read != num_perms) {
+ WARN(p, "There were supposed to be %zu permissions, but only %zu were found.", num_perms, perms_read);
+ retval |= APOL_PERMMAP_RET_NOT_ENOUGH;
+ }
+ if (pc != NULL && !are_all_perms_mapped(p, pc)) {
+ retval |= APOL_PERMMAP_RET_UNMAPPED_PERM;
+ }
+ return retval;
+}
+
+/**
+ * Parse the permission map found within a file pointer, storing the
+ * information into the map within a policy. If there is a non-fatal
+ * error while loading (e.g., file declared an object class that does
+ * not exist within the policy) then generate a warning string and
+ * send it to the error handler stored within the policy.
+ *
+ * @param p Policy containing a newly allocated permission map.
+ * @param fp File pointer that contains permission map data.
+ *
+ * @return 0 on success, > 0 on success but with warnings, < 0 on
+ * error.
+ */
+static int parse_permmap(apol_policy_t * p, FILE * fp)
+{
+ char line[APOL_LINE_SZ], class_name[APOL_LINE_SZ], *line_ptr = NULL;
+ size_t num_classes = 0, num_perms = 0;
+ size_t i;
+ int retval = 0;
+
+ /* first read number of classes */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ line_ptr = line;;
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] != '#' && (sscanf(line_ptr, "%zu", &num_classes) == 1)) {
+ break;
+ }
+ }
+ if (num_classes == 0) {
+ ERR(p, "%s", "No object classes were defined in the permission map file.");
+ return -1;
+ }
+
+ /* next read each class */
+ for (i = 0; i < num_classes; i++) {
+ apol_permmap_class_t *pc;
+ int found_class_decl = 0, rt;
+ while (fgets(line, APOL_LINE_SZ, fp) != NULL) {
+ line_ptr = line;
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] != '#' && (sscanf(line_ptr, "%*s %s %zu", class_name, &num_perms) == 2)) {
+ found_class_decl = 1;
+ break;
+ }
+ }
+ if (!found_class_decl) {
+ WARN(p, "Permission map file was supposed to have %zu classes, but only %zu were found.", num_classes, i);
+ return APOL_PERMMAP_RET_NOT_ENOUGH;
+ }
+ if ((pc = find_permmap_class(p, class_name)) == NULL) {
+ WARN(p,
+ "Object class %s was defined in the permission map file but not within the policy. It will be ignored.",
+ class_name);
+ /* skip to next record */
+ parse_permmap_class(p, fp, num_perms, NULL);
+ retval |= APOL_PERMMAP_RET_UNKNOWN_OBJ;
+ } else {
+ if ((rt = parse_permmap_class(p, fp, num_perms, pc)) < 0) {
+ return -1;
+ }
+ pc->mapped = 1;
+ retval |= rt;
+ }
+ }
+ return retval;
+}
+
+int apol_policy_open_permmap(apol_policy_t * p, const char *filename)
+{
+ FILE *outfile = NULL;
+ int retval = -1, rt = 0;
+
+ if (p == NULL || filename == NULL) {
+ goto cleanup;
+ }
+ permmap_destroy(&p->pmap);
+ if ((p->pmap = apol_permmap_create_from_policy(p)) == NULL) {
+ goto cleanup;
+ }
+
+ if ((outfile = fopen(filename, "r")) == NULL) {
+ ERR(p, "Could not open permission map %s for reading: %s", filename, strerror(errno));
+ goto cleanup;
+ }
+
+ if ((rt = parse_permmap(p, outfile)) < 0) {
+ goto cleanup;
+ }
+
+ /* check that all classes have been mapped */
+ if (rt == 0 && !are_all_classes_mapped(p)) {
+ rt = APOL_PERMMAP_RET_UNMAPPED_OBJ;
+ }
+ p->pmap->mapped = 1;
+
+ retval = rt;
+ cleanup:
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ return retval;
+}
+
+int apol_permmap_load(apol_policy_t * p, const char *filename)
+{
+ return apol_policy_open_permmap(p, filename);
+}
+
+int apol_policy_save_permmap(const apol_policy_t * p, const char *filename)
+{
+ time_t ltime;
+ size_t i, j;
+ FILE *outfile = NULL;
+ int retval = -1;
+
+ if (p == NULL || p->pmap == NULL || filename == NULL)
+ goto cleanup;
+
+ if ((outfile = fopen(filename, "w")) == NULL) {
+ ERR(p, "Could not open permission map %s for writing: %s", filename, strerror(errno));
+ goto cleanup;
+ }
+
+ if (time(&ltime) == (time_t) - 1) {
+ ERR(p, "Could not get time: %s", strerror(errno));
+ goto cleanup;
+ }
+ if (fprintf(outfile, "# Auto-generated by apol on %s\n", ctime(&ltime)) < 0 ||
+ fprintf(outfile, "#\n# permission map file\n\n\n") < 0 ||
+ fprintf(outfile, "Number of classes (mapped?: %s):\n", (p->pmap->mapped ? "yes" : "no")) < 0 ||
+ fprintf(outfile, "%zu\n", apol_vector_get_size(p->pmap->classes)) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(p->pmap->classes); i++) {
+ apol_permmap_class_t *pc = apol_vector_get_element(p->pmap->classes, i);
+ const char *class_name;
+ if (qpol_class_get_name(p->p, pc->c, &class_name) < 0) {
+ goto cleanup;
+ }
+ if (fprintf(outfile, "\nclass %s %zu\n", class_name, apol_vector_get_size(pc->perms)) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (j = 0; j < apol_vector_get_size(pc->perms); j++) {
+ apol_permmap_perm_t *pp = apol_vector_get_element(pc->perms, j);
+ char *s;
+ if (fprintf(outfile, "%s%18s ", pp->map & APOL_PERMMAP_UNMAPPED ? "#" : "", pp->name) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+ switch (pp->map) {
+ case APOL_PERMMAP_READ:
+ s = "r";
+ break;
+ case APOL_PERMMAP_WRITE:
+ s = "w";
+ break;
+ case APOL_PERMMAP_BOTH:
+ s = "b";
+ break;
+ case APOL_PERMMAP_NONE:
+ s = "n";
+ break;
+ case APOL_PERMMAP_UNMAPPED:
+ s = "u";
+ break;
+ default:
+ s = "?";
+ }
+ if (fprintf(outfile, "%s %10d\n", s, pp->weight) < 0) {
+ ERR(p, "Write error: %s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ return retval;
+}
+
+int apol_permmap_save(apol_policy_t * p, const char *filename)
+{
+ return apol_policy_save_permmap(p, filename);
+}
+
+int apol_policy_get_permmap(const apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight)
+{
+ apol_permmap_class_t *pc;
+ apol_permmap_perm_t *pp;
+ if (p == NULL || p->pmap == NULL) {
+ return -1;
+ }
+ if ((pc = find_permmap_class(p, class_name)) == NULL || (pp = find_permmap_perm(p, pc, perm_name)) == NULL) {
+ ERR(p, "Could not find permission %s in class %s.", perm_name, class_name);
+ return -1;
+ }
+ *map = pp->map;
+ *weight = pp->weight;
+ return 0;
+}
+
+int apol_permmap_get(apol_policy_t * p, const char *class_name, const char *perm_name, int *map, int *weight)
+{
+ return apol_policy_get_permmap(p, class_name, perm_name, map, weight);
+}
+
+int apol_policy_set_permmap(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight)
+{
+ apol_permmap_class_t *pc;
+ apol_permmap_perm_t *pp;
+ if (p == NULL || p->pmap == NULL) {
+ return -1;
+ }
+ if ((pc = find_permmap_class(p, class_name)) == NULL || (pp = find_permmap_perm(p, pc, perm_name)) == NULL) {
+ ERR(p, "Could not find permission %s in class %s.", perm_name, class_name);
+ return -1;
+ }
+ pp->map = map;
+ if (weight > APOL_PERMMAP_MAX_WEIGHT) {
+ weight = APOL_PERMMAP_MAX_WEIGHT;
+ } else if (weight < APOL_PERMMAP_MIN_WEIGHT) {
+ weight = APOL_PERMMAP_MIN_WEIGHT;
+ }
+ pp->weight = weight;
+ return 0;
+}
+
+int apol_permmap_set(apol_policy_t * p, const char *class_name, const char *perm_name, int map, int weight)
+{
+ return apol_policy_set_permmap(p, class_name, perm_name, map, weight);
+}
diff --git a/libapol/src/permissive-query.c b/libapol/src/permissive-query.c
new file mode 100644
index 0000000..1279ee7
--- /dev/null
+++ b/libapol/src/permissive-query.c
@@ -0,0 +1,107 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about permissive types
+ * within a policy. The caller obtains a query object,
+ * fills in its parameters, and then runs the query; it obtains a
+ * vector of results. Searches are conjunctive -- all fields of the
+ * search query must match for a datum to be added to the results
+ * query.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_permissive_query
+{
+ char *permissive_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_permissive_get_by_query(const apol_policy_t * p, apol_permissive_query_t * q, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_permissive_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_permissive_t *permissive;
+ if (qpol_iterator_get_item(iter, (void **)&permissive) < 0) {
+ goto cleanup;
+ }
+ if (q != NULL) {
+ int compval = apol_compare_permissive(p,
+ permissive, q->permissive_name,
+ q->flags, &(q->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)permissive)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_permissive_query_t *apol_permissive_query_create(void)
+{
+ return calloc(1, sizeof(apol_permissive_query_t));
+}
+
+void apol_permissive_query_destroy(apol_permissive_query_t ** q)
+{
+ if (*q != NULL) {
+ free((*q)->permissive_name);
+ apol_regex_destroy(&(*q)->regex);
+ free(*q);
+ *q = NULL;
+ }
+}
+
+int apol_permissive_query_set_name(const apol_policy_t * p, apol_permissive_query_t * q, const char *name)
+{
+ return apol_query_set(p, &q->permissive_name, &q->regex, name);
+}
+
+int apol_permissive_query_set_regex(const apol_policy_t * p, apol_permissive_query_t * q, int is_regex)
+{
+ return apol_query_set_regex(p, &q->flags, is_regex);
+}
+
diff --git a/libapol/src/polcap-query.c b/libapol/src/polcap-query.c
new file mode 100644
index 0000000..3491485
--- /dev/null
+++ b/libapol/src/polcap-query.c
@@ -0,0 +1,107 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about policy capabilities
+ * within a policy. The caller obtains a query object,
+ * fills in its parameters, and then runs the query; it obtains a
+ * vector of results. Searches are conjunctive -- all fields of the
+ * search query must match for a datum to be added to the results
+ * query.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_polcap_query
+{
+ char *polcap_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+int apol_polcap_get_by_query(const apol_policy_t * p, apol_polcap_query_t * q, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_polcap_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_polcap_t *polcap;
+ if (qpol_iterator_get_item(iter, (void **)&polcap) < 0) {
+ goto cleanup;
+ }
+ if (q != NULL) {
+ int compval = apol_compare_polcap(p,
+ polcap, q->polcap_name,
+ q->flags, &(q->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)polcap)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_polcap_query_t *apol_polcap_query_create(void)
+{
+ return calloc(1, sizeof(apol_polcap_query_t));
+}
+
+void apol_polcap_query_destroy(apol_polcap_query_t ** q)
+{
+ if (*q != NULL) {
+ free((*q)->polcap_name);
+ apol_regex_destroy(&(*q)->regex);
+ free(*q);
+ *q = NULL;
+ }
+}
+
+int apol_polcap_query_set_name(const apol_policy_t * p, apol_polcap_query_t * q, const char *name)
+{
+ return apol_query_set(p, &q->polcap_name, &q->regex, name);
+}
+
+int apol_polcap_query_set_regex(const apol_policy_t * p, apol_polcap_query_t * q, int is_regex)
+{
+ return apol_query_set_regex(p, &q->flags, is_regex);
+}
+
diff --git a/libapol/src/policy-path.c b/libapol/src/policy-path.c
new file mode 100644
index 0000000..3633ebd
--- /dev/null
+++ b/libapol/src/policy-path.c
@@ -0,0 +1,409 @@
+/**
+ * @file
+ *
+ * Implementation of policy path object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <apol/policy-path.h>
+#include <apol/util.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *POLICY_PATH_MAGIC = "policy_list";
+static const int POLICY_PATH_MAX_VERSION = 1;
+
+struct apol_policy_path
+{
+ apol_policy_path_type_e path_type;
+ char *base;
+ apol_vector_t *modules;
+};
+
+apol_policy_path_t *apol_policy_path_create(apol_policy_path_type_e path_type, const char *path, const apol_vector_t * modules)
+{
+ apol_policy_path_t *p = NULL;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((p = calloc(1, sizeof(*p))) == NULL) {
+ return NULL;
+ }
+ p->path_type = path_type;
+ if ((p->base = strdup(path)) == NULL) {
+ apol_policy_path_destroy(&p);
+ return NULL;
+ }
+ if (p->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ if (modules == NULL) {
+ p->modules = apol_vector_create(free);
+ } else {
+ p->modules = apol_vector_create_from_vector(modules, apol_str_strdup, NULL, free);
+ }
+ if (p->modules == NULL) {
+ apol_policy_path_destroy(&p);
+ return NULL;
+ }
+ apol_vector_sort_uniquify(p->modules, apol_str_strcmp, NULL);
+ }
+ return p;
+}
+
+apol_policy_path_t *apol_policy_path_create_from_policy_path(const apol_policy_path_t * path)
+{
+ apol_policy_path_t *p;
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ p = apol_policy_path_create(path->path_type, path->base, path->modules);
+ return p;
+}
+
+apol_policy_path_t *apol_policy_path_create_from_file(const char *filename)
+{
+ FILE *f = NULL;
+ apol_policy_path_t *path = NULL;
+ apol_policy_path_type_e path_type;
+ char *line = NULL, *s;
+ apol_vector_t *header_tokens = NULL;
+ size_t len;
+ int read_base = 0, retval = -1, error = 0;
+
+ if (filename == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((f = fopen(filename, "r")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+
+ if (getline(&line, &len, f) < 0) {
+ error = EIO;
+ goto cleanup;
+ }
+ apol_str_trim(line);
+ if (strncmp(line, POLICY_PATH_MAGIC, strlen(POLICY_PATH_MAGIC)) != 0) {
+ error = EIO;
+ goto cleanup;
+ }
+
+ apol_str_trim(line);
+ if ((header_tokens = apol_str_split(line, " ")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(header_tokens) < 3) {
+ error = EIO;
+ goto cleanup;
+ }
+ s = apol_vector_get_element(header_tokens, 1);
+ if (atoi(s) == 0 || atoi(s) > POLICY_PATH_MAX_VERSION) {
+ error = ENOTSUP;
+ goto cleanup;
+ }
+ s = apol_vector_get_element(header_tokens, 2);
+ if (strcmp(s, "monolithic") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ } else if (strcmp(s, "modular") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ } else {
+ error = EIO;
+ goto cleanup;
+ }
+
+ while (getline(&line, &len, f) >= 0) {
+ apol_str_trim(line);
+ if (line[0] == '#') {
+ continue;
+ }
+ if (!read_base) {
+ /* trying to parse a base policy / monolithic policy line */
+ if ((path = apol_policy_path_create(path_type, line, NULL)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ read_base = 1;
+ } else {
+ /* trying to parse a module line */
+ if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ error = EIO;
+ goto cleanup;
+ } else {
+ if ((s = strdup(line)) == NULL || apol_vector_append(path->modules, s) < 0) {
+ error = errno;
+ free(s);
+ goto cleanup;
+ }
+ }
+ }
+ }
+ if (read_base == 0) {
+ error = EIO;
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ if (f != NULL) {
+ fclose(f);
+ }
+ free(line);
+ apol_vector_destroy(&header_tokens);
+ if (retval != 0) {
+ apol_policy_path_destroy(&path);
+ errno = error;
+ }
+ return path;
+}
+
+apol_policy_path_t *apol_policy_path_create_from_string(const char *path_string)
+{
+ apol_policy_path_t *p = NULL;
+ apol_vector_t *tokens = NULL;
+ apol_policy_path_type_e path_type;
+ char *s;
+ size_t i;
+ if (path_string == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((tokens = apol_str_split(path_string, ":")) == NULL) {
+ return NULL;
+ }
+
+ /* first token identifies the path type */
+ if (apol_vector_get_size(tokens) < 2) {
+ apol_vector_destroy(&tokens);
+ return NULL;
+ }
+ s = apol_vector_get_element(tokens, 0);
+ if (strcmp(s, "monolithic") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ } else if (strcmp(s, "modular") == 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ } else {
+ apol_vector_destroy(&tokens);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* second token identifies gives base path */
+ s = apol_vector_get_element(tokens, 1);
+ if ((p = apol_policy_path_create(path_type, s, NULL)) == NULL) {
+ apol_vector_destroy(&tokens);
+ return NULL;
+ }
+
+ if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ /* remainder are module paths */
+ for (i = 2; i < apol_vector_get_size(tokens); i++) {
+ s = apol_vector_get_element(tokens, i);
+ if ((s = strdup(s)) == NULL || apol_vector_append(p->modules, s) < 0) {
+ free(s);
+ apol_vector_destroy(&tokens);
+ apol_policy_path_destroy(&p);
+ return NULL;
+ }
+ }
+ apol_vector_sort_uniquify(p->modules, apol_str_strcmp, NULL);
+ }
+ return p;
+}
+
+void apol_policy_path_destroy(apol_policy_path_t ** path)
+{
+ if (path != NULL && *path != NULL) {
+ free((*path)->base);
+ apol_vector_destroy(&(*path)->modules);
+ free(*path);
+ *path = NULL;
+ }
+}
+
+int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b)
+{
+ int cmp;
+ if (a == NULL || b == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if ((cmp = a->path_type - b->path_type) != 0) {
+ return cmp;
+ }
+ if ((cmp = strcmp(a->base, b->base)) != 0) {
+ return cmp;
+ }
+ if (a->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ /* only compare module vector if that field is relevant */
+ size_t i;
+ cmp = apol_vector_compare(a->modules, b->modules, apol_str_strcmp, NULL, &i);
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+ return 0;
+}
+
+apol_policy_path_type_e apol_policy_path_get_type(const apol_policy_path_t * path)
+{
+ if (path == NULL) {
+ errno = EINVAL;
+ return APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ }
+ return path->path_type;
+}
+
+const char *apol_policy_path_get_primary(const apol_policy_path_t * path)
+{
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return path->base;
+}
+
+const apol_vector_t *apol_policy_path_get_modules(const apol_policy_path_t * path)
+{
+ if (path == NULL || path->path_type != APOL_POLICY_PATH_TYPE_MODULAR) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return path->modules;
+}
+
+int apol_policy_path_to_file(const apol_policy_path_t * path, const char *filename)
+{
+ FILE *f = NULL;
+ char *path_type;
+ size_t i;
+ int retval = -1, error = 0;
+ if (path == NULL || filename == NULL) {
+ errno = EINVAL;
+ goto cleanup;
+ }
+ if ((f = fopen(filename, "w")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ path_type = "modular";
+ } else {
+ path_type = "monolithic";
+ }
+ if (fprintf(f, "%s %d %s\n", POLICY_PATH_MAGIC, POLICY_PATH_MAX_VERSION, path_type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (fprintf(f, "%s\n", path->base) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ for (i = 0; i < apol_vector_get_size(path->modules); i++) {
+ char *m = apol_vector_get_element(path->modules, i);
+ if (fprintf(f, "%s\n", m) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (f != NULL) {
+ fclose(f);
+ }
+ if (retval != 0) {
+ error = errno;
+ }
+ return retval;
+}
+
+char *apol_policy_path_to_string(const apol_policy_path_t * path)
+{
+ char *path_type;
+ char *s = NULL;
+ size_t len = 0, i;
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ path_type = "modular";
+ } else {
+ path_type = "monolithic";
+ }
+ if (apol_str_appendf(&s, &len, "%s:%s", path_type, path->base) < 0) {
+ return NULL;
+ }
+ if (path->path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ for (i = 0; i < apol_vector_get_size(path->modules); i++) {
+ char *m = apol_vector_get_element(path->modules, i);
+ if (apol_str_appendf(&s, &len, ":%s", m) < 0) {
+ return NULL;
+ }
+ }
+ }
+ return s;
+}
+
+int apol_file_is_policy_path_list(const char *filename)
+{
+ FILE *f = NULL;
+ char *line = NULL;
+ size_t len = 0;
+ int retval = -1, error = 0;
+
+ if (filename == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((f = fopen(filename, "r")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+
+ if (getline(&line, &len, f) < 0) {
+ error = EIO;
+ goto cleanup;
+ }
+ apol_str_trim(line);
+ if (strncmp(line, POLICY_PATH_MAGIC, strlen(POLICY_PATH_MAGIC)) != 0) {
+ retval = 0;
+ goto cleanup;
+ }
+ retval = 1;
+
+ cleanup:
+ if (f)
+ fclose(f);
+ free(line);
+ if (retval < 0)
+ errno = error;
+ return retval;
+}
diff --git a/libapol/src/policy-query-internal.h b/libapol/src/policy-query-internal.h
new file mode 100644
index 0000000..657c815
--- /dev/null
+++ b/libapol/src/policy-query-internal.h
@@ -0,0 +1,511 @@
+/**
+ * @file
+ *
+ * Header for routines shared among libapol's queries and analyses.
+ * These routines are declared hidden within the library by way of the
+ * linking map.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_POLICY_QUERY_INTERNAL_H
+#define APOL_POLICY_QUERY_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <config.h>
+
+#include <apol/policy.h>
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+
+#include <regex.h>
+#include <stdlib.h>
+#include <qpol/policy.h>
+
+/* forward declaration. the definition resides within perm-map.c */
+ struct apol_permmap;
+
+/* forward declaration. the definition resides within domain-trans-analysis.c */
+ typedef struct apol_domain_trans_table apol_domain_trans_table_t;
+
+/* declared in perm-map.c */
+ typedef struct apol_permmap apol_permmap_t;
+
+ struct apol_policy
+ {
+ qpol_policy_t *p;
+ apol_callback_fn_t msg_callback;
+ void *msg_callback_arg;
+ int policy_type;
+ /** permission mapping for this policy; mappings loaded as needed */
+ struct apol_permmap *pmap;
+ /** for domain trans analysis; table built as needed */
+ struct apol_domain_trans_table *domain_trans_table;
+ };
+
+/** Every query allows the treatment of strings as regular expressions
+ * instead. Within the query structure are flags; if the first bit
+ * is set then use regex matching instead. */
+#define APOL_QUERY_REGEX 0x01
+
+#define APOL_QUERY_ONLY_ENABLED 0x10
+#define APOL_QUERY_SOURCE_AS_ANY 0x20
+#define APOL_QUERY_SOURCE_INDIRECT 0x40
+#define APOL_QUERY_TARGET_INDIRECT 0x80
+
+#define APOL_QUERY_SYMBOL_IS_BOTH (APOL_QUERY_SYMBOL_IS_TYPE|APOL_QUERY_SYMBOL_IS_ATTRIBUTE)
+#define APOL_QUERY_SOURCE_TYPE 0x100
+#define APOL_QUERY_SOURCE_ATTRIBUTE 0x200
+#define APOL_QUERY_TARGET_TYPE 0x400
+#define APOL_QUERY_TARGET_ATTRIBUTE 0x800
+
+#define APOL_QUERY_MATCH_ALL_PERMS 0x1000
+
+/**
+ * Destroy a compiled regular expression, setting it to NULL
+ * afterwards. Does nothing if the reference is NULL.
+ * @param regex Regular expression to destroy.
+ */
+ void apol_regex_destroy(regex_t ** regex);
+
+/**
+ * Sets a string field within a query, clearing its old contents and
+ * cached regex first. The search name will be duplicated.
+ *
+ * @param p Policy handler.
+ * @param search_name Reference to where to store duplicated name.
+ * @param regex Reference to cached regex; this will be cleared by the
+ * function.
+ * @param name New name to set, or NULL to just clear the field.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int apol_query_set(const apol_policy_t * p, char **query_name, regex_t ** regex, const char *name);
+
+/**
+ * Sets an arbitrary flag for a query structure.
+ *
+ * @param p Policy handler.
+ * @param flags Reference to a flag bitmap.
+ * @param is_flag If non-zero, set flag. Otherwise unset it.
+ * @param flag_value Flag value to set.
+ *
+ * @return Always returns 0.
+ */
+ int apol_query_set_flag(const apol_policy_t * p, unsigned int *flags, const int is_flag, int flag_value);
+
+/**
+ * Sets the regular expression flag for a query structure.
+ *
+ * @param p Policy handler.
+ * @param flags Reference to the regular expression flag.
+ * @param is_regex If non-zero, set regex flag. Otherwise unset it.
+ *
+ * @return Always returns 0.
+ */
+ int apol_query_set_regex(const apol_policy_t * p, unsigned int *flags, const int is_regex);
+
+/**
+ * Determines if a name matches a target symbol name. If flags has
+ * the APOL_QUERY_REGEX bit set, then (1) compile the regular
+ * expression if NULL, and (2) apply it to target. Otherwise do a
+ * string comparison between name and target. If name is NULL and/or
+ * empty then the comparison always succeeds regardless of flags and
+ * regex.
+ *
+ * @param p Policy handler.
+ * @param target Name of target symbol to compare.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare(const apol_policy_t * p, const char *target, const char *name, unsigned int flags, regex_t ** regex);
+
+/**
+ * Given an iterator of strings, checks if name matches any element
+ * within it. If there is a match, either literally or by regular
+ * expression, then return 1. If there are no matches then return 0.
+ *
+ * @param p Policy handler.
+ * @param iter Iterator of strings to match.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ * @param do_free If non-zero free the strings returned by the iterator.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_iter(const apol_policy_t * p, qpol_iterator_t * iter, const char *name,
+ unsigned int flags, regex_t ** regex, int do_free);
+
+/**
+ * Determines if a (partial) type query matches a qpol_type_t,
+ * either the type name or any of its aliases.
+ *
+ * @param p Policy within which to look up types.
+ * @param type Type datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_type(const apol_policy_t * p, const qpol_type_t * type, const char *name, unsigned int flags,
+ regex_t ** type_regex);
+
+/**
+ * Determines if a (partial) permissive query matches a qpol_permissive_t,
+ * by name.
+ *
+ * @param p Policy within which to look up types.
+ * @param type Permissive datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_permissive(const apol_policy_t * p, const qpol_permissive_t * permissive, const char *name, unsigned int flags,
+ regex_t ** type_regex);
+
+/**
+ * Determines if a (partial) polcap query matches a qpol_polcap_t,
+ * by name.
+ *
+ * @param p Policy within which to look up types.
+ * @param type Polcap datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_polcap(const apol_policy_t * p, const qpol_polcap_t * polcap, const char *name, unsigned int flags,
+ regex_t ** type_regex);
+
+/**
+ * Determines if a boolean is used within a particual conditional.
+ *
+ * @param p Policy within which to look up types.
+ * @param cond Conditional to compare against.
+ * @param name Source boolean name from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_cond_expr(const apol_policy_t * p, const qpol_cond_t * cond, const char *name, unsigned int flags,
+ regex_t ** bool_regex);
+
+/**
+ * Determines if a level query matches a qpol_level_t, either
+ * the sensitivity name or any of its aliases.
+ *
+ * @param p Policy within which to look up types.
+ * @param level level datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_level(const apol_policy_t * p, const qpol_level_t * level, const char *name, unsigned int flags,
+ regex_t ** level_regex);
+
+/**
+ * Determines if a category query matches a qpol_cat_t, either
+ * the category name or any of its aliases.
+ *
+ * @param p Policy within which to look up types.
+ * @param cat category datum to compare against.
+ * @param name Source target from which to compare.
+ * @param flags If APOL_QUERY_REGEX bit is set, treat name as a
+ * regular expression.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use; the pointer will be allocated space if regexp is
+ * legal. If NULL, then compile the regexp pattern given by name and
+ * cache it here.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_cat(const apol_policy_t * p, const qpol_cat_t * cat, const char *name, unsigned int flags,
+ regex_t ** cat_regex);
+
+/**
+ * Convenience function that compares a qpol_context_t to a
+ * apol_context_t, based upon the MLS range match given by flags. If
+ * search is NULL then the comparison always succeeds.
+ *
+ * @param p Policy within which to look up types.
+ * @param target Target context to compare.
+ * @param name Source context from which to compare.
+ * @param flags Gives how to match MLS ranges within the contexts.
+ *
+ * @return 1 If comparison succeeds, 0 if not; < 0 on error.
+ */
+ int apol_compare_context(const apol_policy_t * p, const qpol_context_t * target, const apol_context_t * search,
+ unsigned int flags);
+
+/**
+ * Given a type name, obtain its qpol_type_t pointer (relative to a
+ * policy). If the type is really its alias, get its primary instead.
+ * (Attributes are considered to be always primary.)
+ *
+ * @param p Policy in which to look up types.
+ * @param type_name Name of type to find.
+ * @param type Reference to where to store resulting pointer.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int apol_query_get_type(const apol_policy_t * p, const char *type_name, const qpol_type_t ** type);
+
+/**
+ * Given a symbol name (a type, attribute, alias, or a regular
+ * expression string), determine all types/attributes it matches.
+ * Return a vector of qpol_type_t that match. If regex is enabled,
+ * include all types/attributes that match the expression. If
+ * indirect is enabled, expand the candidiates within the vector (all
+ * attributes for a type, all types for an attribute), and then
+ * uniquify the vector.
+ *
+ * @param p Policy in which to look up types.
+ * @param symbol A string describing one or more type/attribute to
+ * which match.
+ * @param do_regex If non-zero, then treat symbol as a regular expression.
+ * @param do_indirect If non-zero, expand types to their attributes
+ * and attributes to their types.
+ * @param ta_flag Bit-wise or of (APOL_QUERY_SYMBOL_IS_TYPE,
+ * APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SYMBOL_IS_BOTH) whether
+ * symbol should be matched against type names or attribute names.
+ *
+ * @return Vector of unique qpol_type_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_type_list(const apol_policy_t * p, const char *symbol, int do_regex,
+ int do_indirect, unsigned int ta_flag);
+
+/**
+ * Given a symbol name (a type, attribute, alias, or a regular
+ * expression string), determine all types/attributes it matches.
+ * Return a vector of qpol_type_t that match. If regex is enabled,
+ * include all types/attributes that match the expression. If
+ * indirect is enabled, expand the candidiates within the vector (all
+ * attributes for a type, all types for an attribute), and then
+ * uniquify the vector. The list will include types needed for syntactic
+ * rule searching.
+ *
+ * @param p Policy in which to look up types. <b>Must be a source policy.</b>
+ * @param symbol A string describing one or more type/attribute to
+ * which match.
+ * @param do_regex If non-zero, then treat symbol as a regular expression.
+ * @param do_indirect If non-zero, expand types to their attributes
+ * and attributes to their types.
+ * @param ta_flag Bit-wise or of (APOL_QUERY_SYMBOL_IS_TYPE,
+ * APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SYMBOL_IS_BOTH) whether
+ * symbol should be matched against type names or attribute names.
+ *
+ * @return Vector of unique qpol_type_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_syn_type_list(const apol_policy_t * p, const char *symbol, int do_regex,
+ int do_indirect, unsigned int ta_flag);
+
+/**
+ * Given a symbol name (a role or a regular expression string),
+ * determine all roles it matches. Return a vector of qpol_role_t
+ * that match. If regex is enabled, include all role that
+ * match the expression.
+ *
+ * @param p Policy in which to look up roles.
+ * @param symbol A string describing one or more role to match.
+ * @param do_regex If non-zero, then treat symbol as a regular expression.
+ *
+ * @return Vector of unique qpol_role_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_role_list(const apol_policy_t * p, char *symbol, int do_regex);
+
+/**
+ * Given a vector of object class strings, determine all of the
+ * classes it matches within the policy. Returns a vector of
+ * qpol_class_t that match. If a string does not match an object
+ * class within the policy then it is ignored.
+ *
+ * @param p Policy in which to look up classes.
+ * @param classes Vector of class strings to convert.
+ *
+ * @return Vector of unique qpol_class_t pointers (relative to policy
+ * within p), or NULL upon error. Caller is responsible for calling
+ * apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_create_candidate_class_list(const apol_policy_t * p, apol_vector_t * classes);
+
+/**
+ * Given a type, return a vector of qpol_type_t pointers to which the
+ * type expands. If the type is just a type or an alias, the vector
+ * will have one element, pointing to the type's primary. If it was
+ * an attribute, the vector will have that attribute's types (but not
+ * the attribute itself).
+ *
+ * @param p Policy in which to look up types.
+ * @param t Type to expand.
+ *
+ * @return Vector of qpol_type_t pointers, or NULL upon error. Caller
+ * is responsible for calling apol_vector_destroy() afterwards.
+ */
+ apol_vector_t *apol_query_expand_type(const apol_policy_t * p, const qpol_type_t * t);
+
+/**
+ * Object class and permission set.
+ * Contains the name of a class and a list of permissions
+ * used by analyses and complex searches to allow permissions
+ * to be specified on a per class basis.
+ */
+ typedef struct apol_obj_perm apol_obj_perm_t;
+
+/**
+ * Allocate and return a new object permission set.
+ * @return a newly allocated object permission set or NULL on error.
+ * Caller is responsible for calling apol_obj_perm_free() to free
+ * memory used.
+ */
+ apol_obj_perm_t *apol_obj_perm_create(void);
+
+/**
+ * Free the memory used by an object permission set.
+ * @param op the object permission set to free.
+ */
+ void apol_obj_perm_free(void *op);
+
+/**
+ * Set the object class name for an object permission set.
+ * If already set free the previous name.
+ * @param op The object permission set for which to set the object name.
+ * @param obj_name New object name to set; this string will be duplicated
+ * by this call. If NULL only free existing name (if any).
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the original object permission set will be unchanged.
+ */
+ int apol_obj_perm_set_obj_name(apol_obj_perm_t * op, const char *obj_name);
+
+/**
+ * Get the object class name from an object permission set.
+ * @param op The object permission set from which to get the class name.
+ * @return The class name or NULL if not set or error. The caller <b>should
+ * NOT</b> free the returned string.
+ */
+ char *apol_obj_perm_get_obj_name(const apol_obj_perm_t * op);
+
+/**
+ * Add a permission to the permission list of an object permission set.
+ * @param op The object permission set to which to add the permission.
+ * @param perm Name of the permission to add, this string will be duplicated.
+ * If NULL clear all permissions. If the permission is already in the list
+ * nothing is done;
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the original object permission set will be unchanged.
+ */
+ int apol_obj_perm_append_perm(apol_obj_perm_t * op, const char *perm);
+
+/**
+ * Get a vector of the permissions in an object permission set.
+ * @param op The object permission set from which to get the permissions.
+ * @return Vector (of type char *) of permission names; the caller
+ * <b>should NOT</b> destroy this vector.
+ */
+ apol_vector_t *apol_obj_perm_get_perm_vector(const apol_obj_perm_t * op);
+
+/**
+ * Comparision function for use with vectors of object permission sets.
+ * @param a first object permission set.
+ * @param b second object permission set.
+ * @param policy apol policy from which the objects and permissions come.
+ * @return < 0, 0, or > 0 if the value of the class of a is less than, equal
+ * to, or greater than that of b respectively.
+ */
+ int apol_obj_perm_compare_class(const void *a, const void *b, void *policy);
+
+/**
+ * Determine if a syntactic type set directly uses any of the types in v.
+ * @param p Policy from which the type set and types come.
+ * @param set Syntactic type set to check.
+ * @param v Vector of types (qpol_type_t) to find in set.
+ * @return 0 if no types in v appear in set, > 0 if at least one type
+ * was found, and < 0 if an error occurred.
+ */
+ int apol_query_type_set_uses_types_directly(const apol_policy_t * p, const qpol_type_set_t * set, const apol_vector_t * v);
+
+/**
+ * Deallocate all space associated with a particular policy's permmap,
+ * including the pointer itself. Afterwards set the pointer to NULL.
+ *
+ * @param p Reference to an apol_permmap_t to destroy.
+ */
+ void permmap_destroy(apol_permmap_t ** p);
+
+/**
+ * Destroy the domain transition table freeing all memory used.
+ * @param table Reference pointer to the table to be destroyed.
+ */
+ void domain_trans_table_destroy(apol_domain_trans_table_t ** table);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libapol/src/policy-query.c b/libapol/src/policy-query.c
new file mode 100644
index 0000000..18152fb
--- /dev/null
+++ b/libapol/src/policy-query.c
@@ -0,0 +1,903 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about different
+ * components of a policy. The caller obtains a query object, fills
+ * in its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+/******************** misc helpers ********************/
+
+void apol_regex_destroy(regex_t ** regex)
+{
+ if (*regex != NULL) {
+ regfree(*regex);
+ free(*regex);
+ *regex = NULL;
+ }
+}
+
+int apol_query_set(const apol_policy_t * p, char **query_name, regex_t ** regex, const char *name)
+{
+ if (*query_name != name) {
+ if (regex != NULL) {
+ apol_regex_destroy(regex);
+ }
+ free(*query_name);
+ *query_name = NULL;
+ if (name != NULL && name[0] != '\0' && ((*query_name) = strdup(name)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_query_set_flag(const apol_policy_t * p __attribute__ ((unused)), unsigned int *flags, const int is_flag, int flag_value)
+{
+ if (is_flag) {
+ *flags |= flag_value;
+ } else {
+ *flags &= ~flag_value;
+ }
+ return 0;
+}
+
+int apol_query_set_regex(const apol_policy_t * p, unsigned int *flags, const int is_regex)
+{
+ return apol_query_set_flag(p, flags, is_regex, APOL_QUERY_REGEX);
+}
+
+/********************* comparison helpers *********************/
+
+int apol_compare(const apol_policy_t * p, const char *target, const char *name, unsigned int flags, regex_t ** regex)
+{
+ if (name == NULL || *name == '\0') {
+ return 1;
+ }
+ char errbuf[1024] = { '\0' };
+ if ((flags & APOL_QUERY_REGEX) && regex != NULL) {
+ if (*regex == NULL) {
+ if ((*regex = malloc(sizeof(**regex))) == NULL) {
+ free(*regex);
+ *regex = NULL;
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ int regretv = regcomp(*regex, name, REG_EXTENDED | REG_NOSUB);
+ if (regretv) {
+ regerror(regretv, *regex, errbuf, 1024);
+ free(*regex);
+ *regex = NULL;
+ ERR(p, "%s", errbuf);
+ return -1;
+ }
+ }
+ if (regexec(*regex, target, 0, NULL, 0) == 0) {
+ return 1;
+ }
+ return 0;
+ } else {
+ if (strcmp(target, name) == 0) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+int apol_compare_iter(const apol_policy_t * p, qpol_iterator_t * iter,
+ const char *name, unsigned int flags, regex_t ** regex, int do_free)
+{
+ int compval;
+ if (name == NULL || *name == '\0') {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ char *iter_name;
+ if (qpol_iterator_get_item(iter, (void **)&iter_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, iter_name, name, flags, regex);
+ if (do_free)
+ free(iter_name);
+ if (compval != 0) {
+ /* matched at least one name, or error */
+ return compval;
+ }
+ }
+ /* no matches */
+ return 0;
+}
+
+int apol_compare_type(const apol_policy_t * p, const qpol_type_t * type, const char *name, unsigned int flags,
+ regex_t ** type_regex)
+{
+ const char *type_name;
+ int compval;
+ qpol_iterator_t *alias_iter = NULL;
+ if (qpol_type_get_name(p->p, type, &type_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, type_name, name, flags, type_regex);
+ if (compval != 0) {
+ return compval;
+ }
+ /* also check if the matches against one of the type's
+ * aliases */
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ return -1;
+ }
+ compval = apol_compare_iter(p, alias_iter, name, flags, type_regex, 0);
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+int apol_compare_permissive(const apol_policy_t * p, const qpol_permissive_t * permissive, const char *name, unsigned int flags,
+ regex_t ** permissive_regex)
+{
+ const char *permissive_name;
+ int compval;
+
+ if (qpol_permissive_get_name(p->p, permissive, &permissive_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, permissive_name, name, flags, permissive_regex);
+
+ return compval;
+}
+
+int apol_compare_polcap(const apol_policy_t * p, const qpol_polcap_t * polcap, const char *name, unsigned int flags,
+ regex_t ** polcap_regex)
+{
+ const char *polcap_name;
+ int compval;
+
+ if (qpol_polcap_get_name(p->p, polcap, &polcap_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, polcap_name, name, flags, polcap_regex);
+
+ return compval;
+}
+
+int apol_compare_cond_expr(const apol_policy_t * p, const qpol_cond_t * cond, const char *name, unsigned int flags,
+ regex_t ** bool_regex)
+{
+ qpol_iterator_t *expr_iter = NULL;
+ int compval = -1;
+ if (qpol_cond_get_expr_node_iter(p->p, cond, &expr_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(expr_iter); qpol_iterator_next(expr_iter)) {
+ qpol_cond_expr_node_t *expr;
+ uint32_t expr_type;
+ qpol_bool_t *qbool;
+ const char *bool_name;
+ if (qpol_iterator_get_item(expr_iter, (void **)&expr) < 0 ||
+ qpol_cond_expr_node_get_expr_type(p->p, expr, &expr_type) < 0) {
+ goto cleanup;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ continue;
+ }
+ if (qpol_cond_expr_node_get_bool(p->p, expr, &qbool) < 0 || qpol_bool_get_name(p->p, qbool, &bool_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, bool_name, name, flags, bool_regex);
+ if (compval != 0) { /* catches both errors and success */
+ goto cleanup;
+ }
+ }
+ compval = 0;
+ cleanup:
+ qpol_iterator_destroy(&expr_iter);
+ return compval;
+}
+
+int apol_compare_level(const apol_policy_t * p, const qpol_level_t * level, const char *name, unsigned int flags,
+ regex_t ** level_regex)
+{
+ const char *level_name;
+ int compval;
+ qpol_iterator_t *alias_iter = NULL;
+ if (qpol_level_get_name(p->p, level, &level_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, level_name, name, flags, level_regex);
+ if (compval != 0) {
+ return compval;
+ }
+ /* also check if the matches against one of the sensitivity's
+ * aliases */
+ if (qpol_level_get_alias_iter(p->p, level, &alias_iter) < 0) {
+ return -1;
+ }
+ compval = apol_compare_iter(p, alias_iter, name, flags, level_regex, 0);
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+int apol_compare_cat(const apol_policy_t * p, const qpol_cat_t * cat, const char *name, unsigned int flags, regex_t ** cat_regex)
+{
+ const char *cat_name;
+ int compval;
+ qpol_iterator_t *alias_iter = NULL;
+ if (qpol_cat_get_name(p->p, cat, &cat_name) < 0) {
+ return -1;
+ }
+ compval = apol_compare(p, cat_name, name, flags, cat_regex);
+ if (compval != 0) {
+ return compval;
+ }
+ /* also check if the matches against one of the category's
+ * aliases */
+ if (qpol_cat_get_alias_iter(p->p, cat, &alias_iter) < 0) {
+ return -1;
+ }
+ compval = apol_compare_iter(p, alias_iter, name, flags, cat_regex, 0);
+ qpol_iterator_destroy(&alias_iter);
+ return compval;
+}
+
+int apol_compare_context(const apol_policy_t * p, const qpol_context_t * target, const apol_context_t * search, unsigned int flags)
+{
+ apol_context_t *apol_context;
+ int retval;
+ if (search == NULL) {
+ return 1;
+ }
+ apol_context = apol_context_create_from_qpol_context(p, target);
+ retval = apol_context_compare(p, apol_context, search, flags);
+ apol_context_destroy(&apol_context);
+ return retval;
+}
+
+/******************** other helpers ********************/
+
+int apol_query_get_type(const apol_policy_t * p, const char *type_name, const qpol_type_t ** type)
+{
+ unsigned char isalias;
+ if (qpol_policy_get_type_by_name(p->p, type_name, type) < 0 || qpol_type_get_isalias(p->p, *type, &isalias) < 0) {
+ return -1;
+ }
+ if (isalias) {
+ const char *primary_name;
+ if (qpol_type_get_name(p->p, *type, &primary_name) < 0 ||
+ qpol_policy_get_type_by_name(p->p, primary_name, type) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Append a non-aliased type to a vector. If the passed in type is an
+ * alias, find its primary type and append that instead.
+ *
+ * @param p Policy in which to look up types.
+ * @param v Vector in which append the non-aliased type.
+ * @param type Type or attribute to append. If this is an alias,
+ * append its primary.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_query_append_type(const apol_policy_t * p, apol_vector_t * v, const qpol_type_t * type)
+{
+ unsigned char isalias;
+ const qpol_type_t *real_type = type;
+ if (qpol_type_get_isalias(p->p, type, &isalias) < 0) {
+ return -1;
+ }
+ if (isalias) {
+ const char *primary_name;
+ if (qpol_type_get_name(p->p, type, &primary_name) < 0 ||
+ qpol_policy_get_type_by_name(p->p, primary_name, &real_type) < 0) {
+ return -1;
+ }
+ }
+ if (apol_vector_append(v, (void *)real_type) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ return -1;
+ }
+ return 0;
+}
+
+apol_vector_t *apol_query_create_candidate_type_list(const apol_policy_t * p, const char *symbol, int do_regex, int do_indirect,
+ unsigned int ta_flag)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_type_t *type;
+ regex_t *regex = NULL;
+ qpol_iterator_t *iter = NULL, *alias_iter = NULL;
+ int retval = -1, error = 0;
+ unsigned char isalias, isattr;
+ const char *type_name;
+ int compval;
+ size_t i, orig_vector_size;
+
+ if (list == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (ta_flag == 0 || (ta_flag & ~APOL_QUERY_SYMBOL_IS_BOTH)) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (!do_regex && apol_query_get_type(p, symbol, &type) == 0) {
+ if (apol_query_append_type(p, list, type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+
+ if (do_regex) {
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ continue;
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) {
+ if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ break;
+ }
+ qpol_iterator_destroy(&alias_iter);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ /* prune to match ta_flag */
+ for (i = 0; i < apol_vector_get_size(list); i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_ATTRIBUTE)) || (!isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_TYPE))) {
+ apol_vector_remove(list, i);
+ i--;
+ }
+ }
+
+ if (do_indirect) {
+ orig_vector_size = apol_vector_get_size(list);
+ for (i = 0; i < orig_vector_size; i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isalias(p->p, type, &isalias) < 0 || qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ if ((isattr &&
+ qpol_type_get_type_iter(p->p, type, &iter) < 0) ||
+ (!isattr && qpol_type_get_attr_iter(p->p, type, &iter) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ }
+
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (regex != NULL) {
+ regfree(regex);
+ free(regex);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&alias_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ errno = error;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_create_candidate_syn_type_list(const apol_policy_t * p, const char *symbol, int do_regex, int do_indirect,
+ unsigned int ta_flag)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_type_t *type;
+ regex_t *regex = NULL;
+ qpol_iterator_t *iter = NULL, *alias_iter = NULL;
+ int retval = -1, error = 0;
+ unsigned char isalias, isattr;
+ const char *type_name;
+ int compval;
+ size_t i, orig_vector_size;
+
+ if (list == NULL) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_ATTRIB_NAMES)
+ || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (ta_flag == 0 || (ta_flag & ~APOL_QUERY_SYMBOL_IS_BOTH)) {
+ error = EINVAL;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ if (!do_regex && apol_query_get_type(p, symbol, &type) == 0) {
+ if (apol_query_append_type(p, list, type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+
+ if (do_regex) {
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(p->p, type, &type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ continue;
+ if (qpol_type_get_alias_iter(p->p, type, &alias_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter)) {
+ if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ compval = apol_compare(p, type_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval && apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (compval)
+ break;
+ }
+ qpol_iterator_destroy(&alias_iter);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ /* prune to match ta_flag */
+ for (i = 0; i < apol_vector_get_size(list); i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_ATTRIBUTE)) || (!isattr && !(ta_flag & APOL_QUERY_SYMBOL_IS_TYPE))) {
+ apol_vector_remove(list, i);
+ i--;
+ }
+ }
+
+ orig_vector_size = apol_vector_get_size(list);
+ for (i = 0; i < orig_vector_size; i++) {
+ type = (qpol_type_t *) apol_vector_get_element(list, i);
+ if (qpol_type_get_isalias(p->p, type, &isalias) < 0 || qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias) {
+ continue;
+ }
+ if (!do_indirect && !isattr)
+ continue;
+ if ((isattr && qpol_type_get_type_iter(p->p, type, &iter) < 0) ||
+ (!isattr && qpol_type_get_attr_iter(p->p, type, &iter) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_query_append_type(p, list, type)) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (regex != NULL) {
+ regfree(regex);
+ free(regex);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&alias_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ list = NULL;
+ errno = error;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_create_candidate_role_list(const apol_policy_t * p, char *symbol, int do_regex)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_role_t *role;
+ regex_t *regex = NULL;
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+
+ if (list == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (!do_regex && qpol_policy_get_role_by_name(p->p, symbol, &role) == 0) {
+ if (apol_vector_append(list, (void *)role) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ if (do_regex) {
+ if (qpol_policy_get_role_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const char *role_name;
+ int compval;
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(p->p, role, &role_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, role_name, symbol, APOL_QUERY_REGEX, &regex);
+ if (compval < 0) {
+ goto cleanup;
+ }
+ if (compval && apol_vector_append(list, (void *)role)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (regex != NULL) {
+ regfree(regex);
+ free(regex);
+ }
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ list = NULL;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_create_candidate_class_list(const apol_policy_t * p, apol_vector_t * classes)
+{
+ apol_vector_t *list = apol_vector_create(NULL);
+ size_t i;
+ int retval = -1;
+
+ if (list == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(classes); i++) {
+ char *class_string = (char *)apol_vector_get_element(classes, i);
+ const qpol_class_t *class;
+ if (qpol_policy_get_class_by_name(p->p, class_string, &class) == 0) {
+ if (apol_vector_append(list, (void *)class) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ apol_vector_destroy(&list);
+ list = NULL;
+ }
+ return list;
+}
+
+apol_vector_t *apol_query_expand_type(const apol_policy_t * p, const qpol_type_t * t)
+{
+ apol_vector_t *v = NULL;
+ int retval = -1;
+ unsigned char isattr;
+ qpol_iterator_t *iter = NULL;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, t, &isattr) < 0) {
+ goto cleanup;
+ }
+ if (!isattr) {
+ if (apol_vector_append(v, (void *)t) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ } else {
+ if (qpol_type_get_type_iter(p->p, t, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_type_t *type;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, type) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ return v;
+}
+
+/******** apol_obj_perm - set of an object with a list of permissions ********/
+
+struct apol_obj_perm
+{
+ char *obj_class; /* name of object class */
+ apol_vector_t *perms; /* vector of permission names */
+};
+
+apol_obj_perm_t *apol_obj_perm_create(void)
+{
+ apol_obj_perm_t *op = calloc(1, sizeof(apol_obj_perm_t));
+ if (!op)
+ return NULL;
+
+ op->perms = apol_vector_create(free);
+ if (!(op->perms)) {
+ free(op);
+ return NULL;
+ }
+
+ return op;
+}
+
+void apol_obj_perm_free(void *op)
+{
+ apol_obj_perm_t *inop = (apol_obj_perm_t *) op;
+ if (inop != NULL) {
+ free(inop->obj_class);
+ apol_vector_destroy(&inop->perms);
+ free(inop);
+ }
+}
+
+int apol_obj_perm_set_obj_name(apol_obj_perm_t * op, const char *obj_name)
+{
+ char *tmp = NULL;
+
+ if (!op) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (obj_name) {
+ if (!(tmp = strdup(obj_name)))
+ return -1;
+ free(op->obj_class);
+ op->obj_class = tmp;
+ } else {
+ free(op->obj_class);
+ op->obj_class = NULL;
+ }
+
+ return 0;
+}
+
+char *apol_obj_perm_get_obj_name(const apol_obj_perm_t * op)
+{
+ if (!op) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return op->obj_class;
+}
+
+int apol_obj_perm_append_perm(apol_obj_perm_t * op, const char *perm)
+{
+ char *tmp = NULL;
+
+ if (!op) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (perm) {
+ if ((tmp = strdup(perm)) == NULL || (op->perms == NULL && (op->perms = apol_vector_create(free)) == NULL)) {
+ free(tmp);
+ return -1;
+ }
+ if (apol_vector_append_unique(op->perms, tmp, apol_str_strcmp, NULL) < 0) {
+ free(tmp);
+ return -1;
+ }
+ } else {
+ apol_vector_destroy(&op->perms);
+ }
+
+ return 0;
+}
+
+apol_vector_t *apol_obj_perm_get_perm_vector(const apol_obj_perm_t * op)
+{
+ if (!op) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return op->perms;
+}
+
+int apol_obj_perm_compare_class(const void *a, const void *b, void *policy)
+{
+ const apol_obj_perm_t *opa = (const apol_obj_perm_t *)a;
+ const apol_obj_perm_t *opb = (const apol_obj_perm_t *)b;
+ apol_policy_t *p = (apol_policy_t *) policy;
+ const qpol_class_t *obja = NULL, *objb = NULL;
+ uint32_t a_val = 0, b_val = 0;
+
+ qpol_policy_get_class_by_name(p->p, opa->obj_class, &obja);
+ qpol_policy_get_class_by_name(p->p, opb->obj_class, &objb);
+ qpol_class_get_value(p->p, obja, &a_val);
+ qpol_class_get_value(p->p, objb, &b_val);
+
+ return (int)(a_val - b_val);
+}
+
+int apol_query_type_set_uses_types_directly(const apol_policy_t * p, const qpol_type_set_t * set, const apol_vector_t * v)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_type_t *type = NULL;
+ size_t i;
+ uint32_t comp;
+
+ if (!p || !set) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (!v || !apol_vector_get_size(v))
+ return 0;
+
+ if (qpol_type_set_get_is_comp(p->p, set, &comp)) {
+ return -1;
+ }
+ if (comp) {
+ if (qpol_type_set_get_subtracted_types_iter(p->p, set, &iter)) {
+ return -1;
+ }
+ } else {
+ if (qpol_type_set_get_included_types_iter(p->p, set, &iter)) {
+ return -1;
+ }
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&type);
+ if (!apol_vector_get_index(v, (void *)type, NULL, NULL, &i)) {
+ qpol_iterator_destroy(&iter);
+ return 1;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ return 0;
+}
diff --git a/libapol/src/policy.c b/libapol/src/policy.c
new file mode 100644
index 0000000..95ab7cd
--- /dev/null
+++ b/libapol/src/policy.c
@@ -0,0 +1,217 @@
+/**
+ * @file
+ *
+ * Public interface for SELinux policies.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <apol/perm-map.h>
+#include <apol/domain-trans-analysis.h>
+
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void apol_handle_default_callback(void *varg __attribute__ ((unused)), const apol_policy_t * p
+ __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case APOL_MSG_INFO:
+ {
+ /* by default do not display these messages */
+ return;
+ }
+ case APOL_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case APOL_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+static void qpol_handle_route_to_callback(void *varg, const qpol_policy_t * policy
+ __attribute__ ((unused)), int level, const char *fmt, va_list ap)
+{
+ apol_policy_t *p = (apol_policy_t *) varg;
+ if (p == NULL) {
+ apol_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else if (p->msg_callback != NULL) {
+ p->msg_callback(p->msg_callback_arg, p, level, fmt, ap);
+ }
+}
+
+apol_policy_t *apol_policy_create_from_policy_path(const apol_policy_path_t * path, const int options,
+ apol_callback_fn_t msg_callback, void *varg)
+{
+ apol_policy_t *policy;
+ const char *primary_path;
+ int policy_type;
+ if (!path) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(policy = calloc(1, sizeof(apol_policy_t)))) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ return NULL; /* errno set by calloc */
+ }
+ if (msg_callback != NULL) {
+ policy->msg_callback = msg_callback;
+ } else {
+ policy->msg_callback = apol_handle_default_callback;
+ }
+ policy->msg_callback_arg = varg;
+ primary_path = apol_policy_path_get_primary(path);
+ INFO(policy, "Loading policy %s.", primary_path);
+ policy_type = qpol_policy_open_from_file(primary_path, &policy->p, qpol_handle_route_to_callback, policy, options);
+ if (policy_type < 0) {
+ ERR(policy, "Unable to open policy %s.", primary_path);
+ apol_policy_destroy(&policy);
+ return NULL; /* qpol sets errno */
+ }
+ policy->policy_type = policy_type;
+
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MODULAR) {
+ if (!qpol_policy_has_capability(policy->p, QPOL_CAP_MODULES)) {
+ INFO(policy, "%s is not a base policy.", primary_path);
+ return policy;
+ }
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(modules); i++) {
+ const char *module_path = apol_vector_get_element(modules, i);
+ qpol_module_t *mod = NULL;
+ INFO(policy, "Loading module %s.", module_path);
+ if (qpol_module_create_from_file(module_path, &mod)) {
+ ERR(policy, "Error loading module %s.", module_path);
+ apol_policy_destroy(&policy);
+ return NULL;
+ }
+ if (qpol_policy_append_module(policy->p, mod)) {
+ ERR(policy, "Error loading module %s.", module_path);
+ apol_policy_destroy(&policy);
+ qpol_module_destroy(&mod);
+ return NULL;
+ }
+ }
+ INFO(policy, "%s", "Linking modules into base policy.");
+ if (qpol_policy_rebuild(policy->p, options)) {
+ apol_policy_destroy(&policy);
+ return NULL;
+ }
+ }
+ return policy;
+}
+
+void apol_policy_destroy(apol_policy_t ** policy)
+{
+ if (policy != NULL && *policy != NULL) {
+ qpol_policy_destroy(&((*policy)->p));
+ permmap_destroy(&(*policy)->pmap);
+ domain_trans_table_destroy(&(*policy)->domain_trans_table);
+ free(*policy);
+ *policy = NULL;
+ }
+}
+
+int apol_policy_get_policy_type(const apol_policy_t * policy)
+{
+ if (policy == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return policy->policy_type;
+}
+
+qpol_policy_t *apol_policy_get_qpol(const apol_policy_t * policy)
+{
+ if (policy == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return policy->p;
+}
+
+int apol_policy_is_mls(const apol_policy_t * p)
+{
+ if (p == NULL) {
+ return -1;
+ }
+ return qpol_policy_has_capability(p->p, QPOL_CAP_MLS);
+}
+
+char *apol_policy_get_version_type_mls_str(const apol_policy_t * p)
+{
+ unsigned int version;
+ char *policy_type, *mls, buf[64];
+ if (qpol_policy_get_policy_version(p->p, &version) < 0) {
+ return NULL;
+ }
+ switch (p->policy_type) {
+ case QPOL_POLICY_KERNEL_SOURCE:
+ policy_type = "source";
+ break;
+ case QPOL_POLICY_KERNEL_BINARY:
+ policy_type = "binary";
+ break;
+ case QPOL_POLICY_MODULE_BINARY:
+ policy_type = "modular";
+ break;
+ default:
+ policy_type = "unknown";
+ break;
+ }
+ if (qpol_policy_has_capability(p->p, QPOL_CAP_MLS)) {
+ mls = "mls";
+ } else {
+ mls = "non-mls";
+ }
+ if (snprintf(buf, sizeof(buf), "v.%u (%s, %s)", version, policy_type, mls) == -1) {
+ return NULL;
+ }
+ return strdup(buf);
+}
+
+void apol_handle_msg(const apol_policy_t * p, int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (p == NULL) {
+ apol_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else if (p->msg_callback != NULL) {
+ p->msg_callback(p->msg_callback_arg, p, level, fmt, ap);
+ }
+ va_end(ap);
+}
diff --git a/libapol/src/queue.c b/libapol/src/queue.c
new file mode 100644
index 0000000..1a3de23
--- /dev/null
+++ b/libapol/src/queue.c
@@ -0,0 +1,125 @@
+/**
+ * @file
+ *
+ * This file is a copy of queue.h from NSA's CVS repository. It has
+ * been modified to follow the setools naming conventions.
+ *
+ * Author : Stephen Smalley (NSA), <sds@epoch.ncsc.mil>
+ *
+ * Implementation of the double-ended queue type.
+ */
+
+#include <stdlib.h>
+#include "queue.h"
+
+apol_queue_t *apol_queue_create(void)
+{
+ apol_queue_t *q;
+
+ q = (apol_queue_t *) malloc(sizeof(apol_queue_t));
+ if (q == NULL)
+ return NULL;
+
+ q->head = q->tail = NULL;
+
+ return q;
+}
+
+int apol_queue_insert(apol_queue_t * q, void *element)
+{
+ apol_queue_node_t *newnode;
+
+ if (!q)
+ return -1;
+
+ newnode = (apol_queue_node_t *) malloc(sizeof(struct apol_queue_node));
+ if (newnode == NULL)
+ return -1;
+
+ newnode->element = element;
+ newnode->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = newnode;
+ } else {
+ q->tail->next = newnode;
+ q->tail = newnode;
+ }
+
+ return 0;
+}
+
+int apol_queue_push(apol_queue_t * q, void *element)
+{
+ apol_queue_node_t *newnode;
+
+ if (!q)
+ return -1;
+
+ newnode = (apol_queue_node_t *) malloc(sizeof(apol_queue_node_t));
+ if (newnode == NULL)
+ return -1;
+
+ newnode->element = element;
+ newnode->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = newnode;
+ } else {
+ newnode->next = q->head;
+ q->head = newnode;
+ }
+
+ return 0;
+}
+
+void *apol_queue_remove(apol_queue_t * q)
+{
+ apol_queue_node_t *node;
+ void *element;
+
+ if (!q)
+ return NULL;
+
+ if (q->head == NULL)
+ return NULL;
+
+ node = q->head;
+ q->head = q->head->next;
+ if (q->head == NULL)
+ q->tail = NULL;
+
+ element = node->element;
+ free(node);
+
+ return element;
+}
+
+void *apol_queue_head(apol_queue_t * q)
+{
+ if (!q)
+ return NULL;
+
+ if (q->head == NULL)
+ return NULL;
+
+ return q->head->element;
+}
+
+void apol_queue_destroy(apol_queue_t ** q)
+{
+ apol_queue_node_t *p, *temp;
+
+ if (!q || *q == NULL)
+ return;
+
+ p = (*q)->head;
+ while (p != NULL) {
+ temp = p;
+ p = p->next;
+ free(temp);
+ }
+
+ free(*q);
+ *q = NULL;
+}
diff --git a/libapol/src/queue.h b/libapol/src/queue.h
new file mode 100644
index 0000000..56eb97b
--- /dev/null
+++ b/libapol/src/queue.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * This file is a copy of queue.h from NSA's CVS repository. It has
+ * been modified to follow the setools naming conventions.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ *
+ * A double-ended queue is a singly linked list of
+ * elements of arbitrary type that may be accessed
+ * at either end.
+ */
+
+#ifndef APOL_QUEUE_H
+#define APOL_QUEUE_H
+
+typedef struct apol_queue_node
+{
+ void *element;
+ struct apol_queue_node *next;
+} apol_queue_node_t;
+
+typedef struct apol_queue
+{
+ apol_queue_node_t *head;
+ apol_queue_node_t *tail;
+} apol_queue_t;
+
+/**
+ * Allocate and return a new queue. The caller is responsible for
+ * calling apol_queue_destroy() upon the return value.
+ *
+ * @return A newly allocated queue, or NULL upon error.
+ */
+apol_queue_t *apol_queue_create(void);
+
+/**
+ * Adds an element to the end of a queue.
+ *
+ * @param q Queue to modify.
+ * @param element Element to append to the end.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int apol_queue_insert(apol_queue_t * q, void *element);
+
+/**
+ * Adds an element to the beginning of a queue.
+ *
+ * @param q Queue to modify.
+ * @param element Element to prepend to the beginning.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int apol_queue_push(apol_queue_t * q, void *element);
+
+/**
+ * Remove the first element from a queue and return the data; the
+ * queue is advanced afterwards. If the queue was empty then return
+ * NULL.
+ *
+ * @return First element of a queue, or NULL if nothing is there.
+ */
+void *apol_queue_remove(apol_queue_t * q);
+
+/**
+ * Return the data within the first element, but do not remove it from
+ * the queue. If the queue was empty then return NULL.
+ *
+ * @return First element of a queue, or NULL if nothing is there.
+ */
+void *apol_queue_head(apol_queue_t * q);
+
+/**
+ * Destroy the referenced queue, but <i>do not</i> attempt to free the
+ * data stored within. (The caller is responsible for doing that.)
+ * Afterwards set the referenced variable to NULL. If the variable is
+ * NULL then do nothing.
+ *
+ * @param Reference to a queue to destroy.
+ */
+void apol_queue_destroy(apol_queue_t ** q);
+
+#endif
+
+/* FLASK */
diff --git a/libapol/src/range_trans-query.c b/libapol/src/range_trans-query.c
new file mode 100644
index 0000000..6ba80b7
--- /dev/null
+++ b/libapol/src/range_trans-query.c
@@ -0,0 +1,318 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about range transition
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_range_trans_query
+{
+ char *source, *target;
+ apol_vector_t *classes;
+ apol_mls_range_t *range;
+ unsigned int flags;
+};
+
+int apol_range_trans_get_by_query(const apol_policy_t * p, const apol_range_trans_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL;
+ apol_mls_range_t *range = NULL;
+ int retval = -1, source_as_any = 0;
+ *v = NULL;
+
+ if (r != NULL) {
+ if (r->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_type_list(p, r->source, r->flags & APOL_QUERY_REGEX,
+ r->flags & APOL_QUERY_SOURCE_INDIRECT,
+ APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (r->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, r->target, r->flags & APOL_QUERY_REGEX,
+ r->flags & APOL_QUERY_TARGET_INDIRECT,
+ APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ if (r->classes != NULL &&
+ apol_vector_get_size(r->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, r->classes)) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_policy_get_range_trans_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_range_trans_t *rule;
+ const qpol_mls_range_t *mls_range;
+ int match_source = 0, match_target = 0, compval;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_type_t *source_type;
+ if (qpol_range_trans_get_source_type(p->p, rule, &source_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target has been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_range_trans_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+
+ if (!match_target) {
+ continue;
+ }
+
+ if (class_list != NULL) {
+ const qpol_class_t *obj_class;
+ if (qpol_range_trans_get_target_class(p->p, rule, &obj_class) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) {
+ continue;
+ }
+ }
+
+ if (qpol_range_trans_get_range(p->p, rule, &mls_range) < 0 ||
+ (range = apol_mls_range_create_from_qpol_mls_range(p, mls_range)) == NULL) {
+ goto cleanup;
+ }
+ if (r)
+ compval = apol_mls_range_compare(p, range, r->range, r->flags);
+ else
+ compval = 1;
+ apol_mls_range_destroy(&range);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+
+ if (apol_vector_append(*v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ apol_vector_destroy(&class_list);
+ qpol_iterator_destroy(&iter);
+ apol_mls_range_destroy(&range);
+ return retval;
+}
+
+apol_range_trans_query_t *apol_range_trans_query_create(void)
+{
+ return calloc(1, sizeof(apol_range_trans_query_t));
+}
+
+void apol_range_trans_query_destroy(apol_range_trans_query_t ** r)
+{
+ if (*r != NULL) {
+ free((*r)->source);
+ free((*r)->target);
+ apol_vector_destroy(&(*r)->classes);
+ apol_mls_range_destroy(&((*r)->range));
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_range_trans_query_set_source(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT);
+ return apol_query_set(p, &r->source, NULL, symbol);
+}
+
+int apol_range_trans_query_set_target(const apol_policy_t * p, apol_range_trans_query_t * r, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &r->target, NULL, symbol);
+}
+
+int apol_range_trans_query_append_class(const apol_policy_t * p, apol_range_trans_query_t * r, const char *obj_class)
+{
+ char *s = NULL;
+ if (obj_class == NULL) {
+ apol_vector_destroy(&r->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (r->classes == NULL && (r->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(r->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+int apol_range_trans_query_set_range(const apol_policy_t * p __attribute__ ((unused)),
+ apol_range_trans_query_t * r, apol_mls_range_t * range, unsigned int range_match)
+{
+ if (r->range != NULL) {
+ apol_mls_range_destroy(&r->range);
+ }
+ r->range = range;
+ r->flags = (r->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+int apol_range_trans_query_set_source_any(const apol_policy_t * p, apol_range_trans_query_t * r, int is_any)
+{
+ return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_range_trans_query_set_regex(const apol_policy_t * p, apol_range_trans_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+char *apol_range_trans_render(const apol_policy_t * policy, const qpol_range_trans_t * rule)
+{
+ char *tmp = NULL;
+ const char *tmp_name = NULL;
+ int error = 0;
+ size_t tmp_sz = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *target_class = NULL;
+ const qpol_mls_range_t *range = NULL;
+ apol_mls_range_t *arange = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* range_transition */
+ if (apol_str_append(&tmp, &tmp_sz, "range_transition ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ return NULL;
+ }
+
+ /* source type */
+ if (qpol_range_trans_get_source_type(policy->p, rule, &type) ||
+ qpol_type_get_name(policy->p, type, &tmp_name) ||
+ apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target type */
+ if (qpol_range_trans_get_target_type(policy->p, rule, &type) ||
+ qpol_type_get_name(policy->p, type, &tmp_name) ||
+ apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " : ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target class */
+ if (qpol_range_trans_get_target_class(policy->p, rule, &target_class) ||
+ qpol_class_get_name(policy->p, target_class, &tmp_name) ||
+ apol_str_append(&tmp, &tmp_sz, tmp_name) || apol_str_append(&tmp, &tmp_sz, " ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* range */
+ if (qpol_range_trans_get_range(policy->p, rule, &range)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (!(arange = apol_mls_range_create_from_qpol_mls_range(policy, range))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ char *tmp_range_str = NULL;
+ if (!(tmp_range_str = apol_mls_range_render(policy, arange))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ apol_mls_range_destroy(&arange);
+ if (apol_str_append(&tmp, &tmp_sz, tmp_range_str) || apol_str_append(&tmp, &tmp_sz, ";")) {
+ free(tmp_range_str);
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ free(tmp_range_str);
+ return tmp;
+
+ err:
+ apol_mls_range_destroy(&arange);
+ free(tmp);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/rbacrule-query.c b/libapol/src/rbacrule-query.c
new file mode 100644
index 0000000..81b0c53
--- /dev/null
+++ b/libapol/src/rbacrule-query.c
@@ -0,0 +1,417 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about type enforcement
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+#include <string.h>
+
+struct apol_role_allow_query
+{
+ char *source, *target;
+ unsigned int flags;
+};
+
+struct apol_role_trans_query
+{
+ char *source, *target, *default_role;
+ unsigned int flags;
+};
+
+/******************** (role) allow queries ********************/
+
+int apol_role_allow_get_by_query(const apol_policy_t * p, const apol_role_allow_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL;
+ int retval = -1, source_as_any = 0;
+ *v = NULL;
+
+ if (r != NULL) {
+ if (r->source != NULL &&
+ (source_list = apol_query_create_candidate_role_list(p, r->source, r->flags & APOL_QUERY_REGEX)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) {
+ target_list = source_list;
+ source_as_any = 1;
+ } else if (r->target != NULL &&
+ (target_list = apol_query_create_candidate_role_list(p, r->target, r->flags & APOL_QUERY_REGEX)) == NULL)
+ {
+ goto cleanup;
+ }
+ }
+ if (qpol_policy_get_role_allow_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_role_allow_t *rule;
+ int match_source = 0, match_target = 0;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_role_t *source_role;
+ if (qpol_role_allow_get_source_role(p->p, rule, &source_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_role, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target has been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_role_t *target_role;
+ if (qpol_role_allow_get_target_role(p->p, rule, &target_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_role, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+ if (!match_target) {
+ continue;
+ }
+
+ if (apol_vector_append(*v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_role_allow_query_t *apol_role_allow_query_create(void)
+{
+ return calloc(1, sizeof(apol_role_allow_query_t));
+}
+
+void apol_role_allow_query_destroy(apol_role_allow_query_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ free((*r)->source);
+ free((*r)->target);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_role_allow_query_set_source(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->source, NULL, role);
+}
+
+int apol_role_allow_query_set_target(const apol_policy_t * p, apol_role_allow_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->target, NULL, role);
+}
+
+int apol_role_allow_query_set_source_any(const apol_policy_t * p, apol_role_allow_query_t * r, int is_any)
+{
+ return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_role_allow_query_set_regex(const apol_policy_t * p, apol_role_allow_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+char *apol_role_allow_render(const apol_policy_t * policy, const qpol_role_allow_t * rule)
+{
+ char *tmp = NULL;
+ const char *source_name = NULL, *target_name = NULL;
+ const qpol_role_t *role = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* source role */
+ if (qpol_role_allow_get_source_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &source_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* target role */
+ if (qpol_role_allow_get_target_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &target_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ if (asprintf(&tmp, "allow %s %s;", source_name, target_name) < 0) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ return tmp;
+}
+
+/******************** role_transition queries ********************/
+
+int apol_role_trans_get_by_query(const apol_policy_t * p, const apol_role_trans_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *source_list = NULL, *target_list = NULL, *default_list = NULL;
+ int retval = -1, source_as_any = 0;
+ *v = NULL;
+
+ if (r != NULL) {
+ if (r->source != NULL &&
+ (source_list = apol_query_create_candidate_role_list(p, r->source, r->flags & APOL_QUERY_REGEX)) == NULL) {
+ goto cleanup;
+ }
+ if (r->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, r->target, r->flags & APOL_QUERY_REGEX,
+ r->flags & APOL_QUERY_TARGET_INDIRECT,
+ APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->flags & APOL_QUERY_SOURCE_AS_ANY) && r->source != NULL) {
+ default_list = source_list;
+ source_as_any = 1;
+ } else if (r->default_role != NULL &&
+ (default_list =
+ apol_query_create_candidate_role_list(p, r->default_role, r->flags & APOL_QUERY_REGEX)) == NULL) {
+ goto cleanup;
+ }
+ }
+ if (qpol_policy_get_role_trans_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_role_trans_t *rule;
+ int match_source = 0, match_target = 0, match_default = 0;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_role_t *source_role;
+ if (qpol_role_trans_get_source_role(p->p, rule, &source_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_role, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target and default have been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_role_trans_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+ if (!match_target) {
+ continue;
+ }
+
+ if (default_list == NULL || (source_as_any && match_source)) {
+ match_default = 1;
+ } else {
+ const qpol_role_t *default_role;
+ if (qpol_role_trans_get_default_role(p->p, rule, &default_role) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(default_list, default_role, NULL, NULL, &i) == 0) {
+ match_default = 1;
+ }
+ }
+ if (!match_default) {
+ continue;
+ }
+
+ if (apol_vector_append(*v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ apol_vector_destroy(&target_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&default_list);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_role_trans_query_t *apol_role_trans_query_create(void)
+{
+ return calloc(1, sizeof(apol_role_trans_query_t));
+}
+
+void apol_role_trans_query_destroy(apol_role_trans_query_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ free((*r)->source);
+ free((*r)->target);
+ free((*r)->default_role);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_role_trans_query_set_source(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->source, NULL, role);
+}
+
+int apol_role_trans_query_set_target(const apol_policy_t * p, apol_role_trans_query_t * r, const char *type, int is_indirect)
+{
+ apol_query_set_flag(p, &r->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &r->target, NULL, type);
+}
+
+int apol_role_trans_query_set_default(const apol_policy_t * p, apol_role_trans_query_t * r, const char *role)
+{
+ return apol_query_set(p, &r->default_role, NULL, role);
+}
+
+int apol_role_trans_query_set_source_any(const apol_policy_t * p, apol_role_trans_query_t * r, int is_any)
+{
+ return apol_query_set_flag(p, &r->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_role_trans_query_set_regex(const apol_policy_t * p, apol_role_trans_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+char *apol_role_trans_render(const apol_policy_t * policy, const qpol_role_trans_t * rule)
+{
+ char *tmp = NULL;
+ const char *source_name = NULL, *target_name = NULL, *default_name = NULL;
+ const qpol_role_t *role = NULL;
+ const qpol_type_t *type = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* source role */
+ if (qpol_role_trans_get_source_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &source_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* target type */
+ if (qpol_role_trans_get_target_type(policy->p, rule, &type)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_type_get_name(policy->p, type, &target_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* default role */
+ if (qpol_role_trans_get_default_role(policy->p, rule, &role)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ if (qpol_role_get_name(policy->p, role, &default_name)) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+
+ if (asprintf(&tmp, "role_transition %s %s %s;", source_name, target_name, default_name) < 0) {
+ ERR(policy, "%s", strerror(errno));
+ return NULL;
+ }
+ return tmp;
+}
diff --git a/libapol/src/relabel-analysis.c b/libapol/src/relabel-analysis.c
new file mode 100644
index 0000000..d1cab99
--- /dev/null
+++ b/libapol/src/relabel-analysis.c
@@ -0,0 +1,813 @@
+/**
+ * @file
+ * Implementation of the direct relabelling analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+#include <string.h>
+
+/* defines for mode */
+#define APOL_RELABEL_MODE_OBJ 0x01
+#define APOL_RELABEL_MODE_SUBJ 0x02
+
+struct apol_relabel_analysis
+{
+ unsigned int mode, direction;
+ char *type, *result;
+ apol_vector_t *classes, *subjects;
+ regex_t *result_regex;
+};
+
+/**
+ * Results are in the form of a list of apol_relabel_result_t nodes.
+ * Each node has three sublists of apol_relabel_result_pair_t.
+ */
+struct apol_relabel_result
+{
+ apol_vector_t *to;
+ apol_vector_t *from;
+ apol_vector_t *both;
+ const qpol_type_t *type;
+};
+
+struct apol_relabel_result_pair
+{
+ const qpol_avrule_t *ruleA, *ruleB;
+ const qpol_type_t *intermed;
+};
+
+#define PERM_RELABELTO "relabelto"
+#define PERM_RELABELFROM "relabelfrom"
+
+/******************** actual analysis rountines ********************/
+
+/**
+ * Given an avrule, determine which relabel direction it has (to,
+ * from, or both).
+ *
+ * @param p Policy containing avrule.
+ * @param avrule Rule to examine.
+ *
+ * @return One of APOL_RELABEL_DIR_TO, APOL_RELABEL_DIR_FROM,
+ * APOL_RELABEL_DIR_BOTH, or < 0 if direction could not be determined.
+ */
+static int relabel_analysis_get_direction(const apol_policy_t * p, const qpol_avrule_t * avrule)
+{
+ qpol_iterator_t *iter;
+ int to = 0, from = 0, retval = -1;
+
+ if (qpol_avrule_get_perm_iter(p->p, avrule, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ char *perm;
+ if (qpol_iterator_get_item(iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (strcmp(perm, PERM_RELABELTO) == 0) {
+ to = 1;
+ } else if (strcmp(perm, PERM_RELABELFROM) == 0) {
+ from = 1;
+ }
+ free(perm);
+ perm = NULL;
+ }
+ if (to && from) {
+ retval = APOL_RELABEL_DIR_BOTH;
+ } else if (to) {
+ retval = APOL_RELABEL_DIR_TO;
+ } else if (from) {
+ retval = APOL_RELABEL_DIR_FROM;
+ }
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given an apol_relabel_result_t node and a qpol_type_t, determine if
+ * the two match.
+ *
+ * @param a Pointer to a apol_relabel_result_t.
+ * @param b Pointer to a type.
+ * @param data (Unused).
+ *
+ * @return 0 if the result node's type matches the given type,
+ * non-zero if not.
+ */
+static int relabel_result_comp_func(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ apol_relabel_result_t *r = (apol_relabel_result_t *) a;
+ qpol_type_t *t = (qpol_type_t *) b;
+ return (int)((char *)r->type - (char *)t);
+}
+
+static void relabel_result_free(void *result)
+{
+ if (result != NULL) {
+ apol_relabel_result_t *r = (apol_relabel_result_t *) result;
+ apol_vector_destroy(&r->to);
+ apol_vector_destroy(&r->from);
+ apol_vector_destroy(&r->both);
+ free(result);
+ }
+}
+
+/**
+ * Given a qpol_type_t pointer, find and return the first
+ * apol_relabel_result_t node within vector v that matches the type.
+ * If there does not exist a node with that type, then allocate a new
+ * one, append it to the vector, and return it. The caller is
+ * expected to eventually call apol_vector_destroy() upon the vector.
+ *
+ * @param p Policy, used for error handling.
+ * @param results A vector of apol_relabel_result_t nodes.
+ * @param type Target type to find.
+ *
+ * @return An apol_relabel_result_t node from which to append results,
+ * or NULL upon error.
+ */
+static apol_relabel_result_t *relabel_result_get_node(const apol_policy_t * p, apol_vector_t * results, const qpol_type_t * type)
+{
+ apol_relabel_result_t *result;
+ size_t i;
+ if (apol_vector_get_index(results, type, relabel_result_comp_func, NULL, &i) == 0) {
+ return (apol_relabel_result_t *) apol_vector_get_element(results, i);
+ }
+ /* make a new result node */
+ if ((result = calloc(1, sizeof(*result))) == NULL ||
+ (result->to = apol_vector_create(free)) == NULL ||
+ (result->from = apol_vector_create(free)) == NULL ||
+ (result->both = apol_vector_create(free)) == NULL || apol_vector_append(results, result) < 0) {
+ ERR(p, "%s", strerror(errno));
+ relabel_result_free(result);
+ return NULL;
+ }
+ result->type = type;
+ return result;
+}
+
+/**
+ * Given a vector of strings representing type names, allocate and
+ * return a vector of qpol_type_t pointers into the given policy for
+ * those types. If a type name is really an alias, obtain and store
+ * its primary instead.
+ *
+ * @param p Policy to which look up types
+ * @param v Vector of strings.
+ *
+ * @return A newly allocated apol_vector_t, which the caller must free
+ * with apol_vector_destroy(). If a type name was not found or upon
+ * other error return NULL.
+ */
+static apol_vector_t *relabel_analysis_get_type_vector(const apol_policy_t * p, const apol_vector_t * v)
+{
+ apol_vector_t *types = NULL;
+ size_t i;
+ int retval = -1;
+
+ if ((types = apol_vector_create_with_capacity(apol_vector_get_size(v), NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ char *s = (char *)apol_vector_get_element(v, i);
+ const qpol_type_t *type;
+ if (apol_query_get_type(p, s, &type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(types, (void *)type)) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval == -1) {
+ apol_vector_destroy(&types);
+ return NULL;
+ }
+ return types;
+}
+
+/**
+ * Given a type, see if it is an element within a vector of
+ * qpol_type_t pointers. If the type is really an attribute, also
+ * check if any of the attribute's types are a member of v. If v is
+ * NULL then the comparison always succeeds.
+ *
+ * @param p Policy to which look up types.
+ * @param v Target vector of qpol_type_t pointers.
+ * @param type Source type to find.
+ *
+ * @return 1 if type is a member of v, 0 if not, < 0 on error.
+ */
+static int relabel_analysis_compare_type_to_vector(const apol_policy_t * p, const apol_vector_t * v, const qpol_type_t * type)
+{
+ size_t i;
+ unsigned char isattr;
+ qpol_iterator_t *iter = NULL;
+ int retval = -1;
+ if (v == NULL || apol_vector_get_index(v, type, NULL, NULL, &i) == 0) {
+ retval = 1; /* found it */
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0) {
+ goto cleanup;
+ }
+ if (!isattr) { /* not an attribute, so comparison failed */
+ retval = 0;
+ goto cleanup;
+ }
+ if (qpol_type_get_type_iter(p->p, type, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_type_t *t;
+ if (qpol_iterator_get_item(iter, (void **)&t) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(v, t, NULL, NULL, &i) == 0) {
+ retval = 1;
+ goto cleanup;
+ }
+ }
+ retval = 0; /* no matches */
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given two avrules, possbily append it to the object results vector
+ * onto the appropriate rules vector. The decision to actually append
+ * or not is dependent upon the filtering options stored within the
+ * relabel analysis object.
+ *
+ * @param p Policy containing avrule.
+ * @param r Relabel analysis query object, containing filtering options.
+ * @param ruleA First AV rule to add.
+ * @param ruleB Other AV rule to add.
+ * @param result Results vector being built.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int append_avrules_to_object_vector(const apol_policy_t * p,
+ apol_relabel_analysis_t * r,
+ const qpol_avrule_t * ruleA, const qpol_avrule_t * ruleB, apol_vector_t * results)
+{
+ const qpol_type_t *sourceA, *sourceB, *target, *intermed;
+ unsigned char isattrA, isattrB;
+ apol_vector_t *target_v = NULL, *result_list;
+ size_t i;
+ apol_relabel_result_t *result;
+ apol_relabel_result_pair_t *pair = NULL;
+ int retval = -1, compval, dirA, dirB;
+ if (qpol_avrule_get_target_type(p->p, ruleB, &target) < 0 || (target_v = apol_query_expand_type(p, target)) == NULL) {
+ goto cleanup;
+ }
+ if (qpol_avrule_get_source_type(p->p, ruleA, &sourceA) < 0 ||
+ qpol_avrule_get_source_type(p->p, ruleB, &sourceB) < 0 ||
+ qpol_type_get_isattr(p->p, sourceA, &isattrA) < 0 || qpol_type_get_isattr(p->p, sourceB, &isattrB) < 0) {
+ goto cleanup;
+ }
+ /* If both rules use the same attribute, retain the attribute
+ * to minimize the number of results and to indicate that all
+ * types with that attribute have the permission to relabel. */
+ if ((isattrA && isattrB) || !isattrA) {
+ intermed = sourceA;
+ } else {
+ intermed = sourceB;
+ }
+ for (i = 0; i < apol_vector_get_size(target_v); i++) {
+ target = (qpol_type_t *) apol_vector_get_element(target_v, i);
+ /* exclude if B(t) does not match search criteria */
+ compval = apol_compare_type(p, target, r->type, 0, NULL);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ continue; /* don't care about relabels to itself */
+ }
+ compval = apol_compare_type(p, target, r->result, APOL_QUERY_REGEX, &r->result_regex);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((result = relabel_result_get_node(p, results, target)) == NULL) {
+ goto cleanup;
+ }
+ if ((dirA = relabel_analysis_get_direction(p, ruleA)) < 0 || (dirB = relabel_analysis_get_direction(p, ruleB)) < 0) {
+ goto cleanup;
+ }
+ if ((pair = calloc(1, sizeof(*pair))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (dirA == APOL_RELABEL_DIR_BOTH && dirB == APOL_RELABEL_DIR_BOTH) {
+ result_list = result->both;
+ pair->ruleA = ruleA;
+ pair->ruleB = ruleB;
+ } else if (dirA == APOL_RELABEL_DIR_FROM || dirB == APOL_RELABEL_DIR_TO) {
+ result_list = result->to;
+ pair->ruleA = ruleA;
+ pair->ruleB = ruleB;
+ } else {
+ result_list = result->from;
+ pair->ruleA = ruleB;
+ pair->ruleB = ruleA;
+ }
+ pair->intermed = intermed;
+ if ((apol_vector_append(result_list, pair)) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ pair = NULL;
+ }
+ retval = 0;
+ cleanup:
+ free(pair);
+ apol_vector_destroy(&target_v);
+ return retval;
+}
+
+/**
+ * Search through sets av and bv, finding pairs of avrules that
+ * satisfy a relabel and adding those pairs to result vector v.
+ *
+ * @param p Policy containing avrules.
+ * @param r Relabel analysis query object.
+ * @param v Vector of apol_relabel_result_t nodes.
+ * @param av Vector of qpol_avrule_t pointers.
+ * @param bv Vector of qpol_avrule_t pointers.
+ * @param subjects_v Vector of permitted qpol_type_t subjects, or NULL
+ * to allow all types.
+ *
+ * @return 0 on success, < 0 upon error.
+ */
+static int relabel_analysis_matchup(const apol_policy_t * p,
+ apol_relabel_analysis_t * r,
+ apol_vector_t * av, apol_vector_t * bv, const apol_vector_t * subjects_v, apol_vector_t * v)
+{
+ const qpol_avrule_t *a_avrule, *b_avrule;
+ const qpol_type_t *a_source, *a_target, *b_source, *b_target, *start_type;
+ const qpol_class_t *a_class, *b_class;
+ apol_vector_t *start_v = NULL;
+ size_t i, j;
+ int compval, retval = -1;
+
+ if (apol_query_get_type(p, r->type, &start_type) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(av); i++) {
+ a_avrule = apol_vector_get_element(av, i);
+ if (qpol_avrule_get_source_type(p->p, a_avrule, &a_source) < 0 ||
+ qpol_avrule_get_target_type(p->p, a_avrule, &a_target) < 0 ||
+ qpol_avrule_get_object_class(p->p, a_avrule, &a_class) < 0) {
+ goto cleanup;
+ }
+ compval = relabel_analysis_compare_type_to_vector(p, subjects_v, a_source);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((start_v = apol_query_expand_type(p, a_source)) == NULL) {
+ goto cleanup;
+ }
+
+ /* check if there exists a B s.t. B(s) = source and
+ * B(t) != r->type and B(o) = A(o) */
+ for (j = 0; j < apol_vector_get_size(bv); j++) {
+ b_avrule = apol_vector_get_element(bv, j);
+ if (qpol_avrule_get_source_type(p->p, b_avrule, &b_source) < 0 ||
+ qpol_avrule_get_target_type(p->p, b_avrule, &b_target) < 0 ||
+ qpol_avrule_get_object_class(p->p, b_avrule, &b_class) < 0) {
+ goto cleanup;
+ }
+ if (relabel_analysis_compare_type_to_vector(p, start_v, b_source) != 1 ||
+ b_target == start_type || a_class != b_class) {
+ continue;
+ }
+ if (append_avrules_to_object_vector(p, r, a_avrule, b_avrule, v) < 0) {
+ goto cleanup;
+ }
+ }
+ apol_vector_destroy(&start_v);
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&start_v);
+ return retval;
+}
+
+/**
+ * Get a list of allow rules, whose target type matches r->type and
+ * whose permission is <i>opposite</i> of the direction given (e.g.,
+ * relabelfrom if given DIR_TO). Only include rules whose class is a
+ * member of r->classes and whose source is a member of subjects_v.
+ *
+ * @param p Policy to which look up rules.
+ * @param r Structure containing parameters for subject relabel analysis.
+ * @param v Target vector to which append discovered rules.
+ * @param direction Relabelling direction to search.
+ * @param subjects_v If not NULL, then a vector of qpol_type_t pointers.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int relabel_analysis_object(const apol_policy_t * p,
+ apol_relabel_analysis_t * r,
+ apol_vector_t * v, unsigned int direction, const apol_vector_t * subjects_v)
+{
+ apol_avrule_query_t *a = NULL, *b = NULL;
+ apol_vector_t *a_rules = NULL, *b_rules = NULL;
+ char *perm1, *perm2;
+ size_t i;
+ int retval = -1;
+
+ if (direction == APOL_RELABEL_DIR_TO) {
+ perm1 = PERM_RELABELFROM;
+ perm2 = PERM_RELABELTO;
+ } else {
+ perm1 = PERM_RELABELTO;
+ perm2 = PERM_RELABELFROM;
+ }
+
+ if ((a = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, a, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_target(p, a, r->type, 1) < 0 || apol_avrule_query_append_perm(p, a, perm1) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) {
+ if (apol_avrule_query_append_class(p, a, apol_vector_get_element(r->classes, i)) < 0) {
+ goto cleanup;
+ }
+ }
+ if (apol_avrule_get_by_query(p, a, &a_rules) < 0) {
+ goto cleanup;
+ }
+
+ if ((b = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, b, QPOL_RULE_ALLOW) < 0 || apol_avrule_query_append_perm(p, b, perm2) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) {
+ if (apol_avrule_query_append_class(p, b, apol_vector_get_element(r->classes, i)) < 0) {
+ goto cleanup;
+ }
+ }
+ if (apol_avrule_get_by_query(p, b, &b_rules) < 0) {
+ goto cleanup;
+ }
+
+ if (relabel_analysis_matchup(p, r, a_rules, b_rules, subjects_v, v) < 0) {
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&a);
+ apol_vector_destroy(&a_rules);
+ apol_avrule_query_destroy(&b);
+ apol_vector_destroy(&b_rules);
+ return retval;
+}
+
+/**
+ * Given an avrule, possbily append it to the subject results vector
+ * onto the appropriate rules vector. The decision to actually append
+ * or not is dependent upon the filtering options stored within the
+ * relabel analysis object.
+ *
+ * @param p Policy containing avrule.
+ * @param r Relabel analysis query object, containing filtering options.
+ * @param avrule AV rule to add.
+ * @param result Results vector being built.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int append_avrule_to_subject_vector(const apol_policy_t * p,
+ apol_relabel_analysis_t * r, const qpol_avrule_t * avrule, apol_vector_t * results)
+{
+ const qpol_type_t *target;
+ apol_vector_t *target_v = NULL, *result_list = NULL;
+ size_t i;
+ apol_relabel_result_t *result;
+ apol_relabel_result_pair_t *pair = NULL;
+ int retval = -1, dir, compval;
+ if ((dir = relabel_analysis_get_direction(p, avrule)) < 0) {
+ goto cleanup;
+ }
+ if (qpol_avrule_get_target_type(p->p, avrule, &target) < 0 || (target_v = apol_query_expand_type(p, target)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(target_v); i++) {
+ target = (qpol_type_t *) apol_vector_get_element(target_v, i);
+ compval = apol_compare_type(p, target, r->type, 0, NULL);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ continue; /* don't care about relabels to itself */
+ }
+ compval = apol_compare_type(p, target, r->result, APOL_QUERY_REGEX, &r->result_regex);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if ((result = relabel_result_get_node(p, results, target)) == NULL) {
+ goto cleanup;
+ }
+ if ((pair = calloc(1, sizeof(*pair))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ pair->ruleA = avrule;
+ pair->ruleB = NULL;
+ pair->intermed = NULL;
+ switch (dir) {
+ case APOL_RELABEL_DIR_TO:
+ result_list = result->to;
+ break;
+ case APOL_RELABEL_DIR_FROM:
+ result_list = result->from;
+ break;
+ case APOL_RELABEL_DIR_BOTH:
+ result_list = result->both;
+ break;
+ }
+ if ((apol_vector_append(result_list, pair)) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ pair = NULL;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&target_v);
+ free(pair);
+ return retval;
+}
+
+/**
+ * Get a list of all allow rules, whose source type matches r->type
+ * and whose permission list has either "relabelto" or "relabelfrom".
+ * Only include rules whose class is a member of r->classes. Add
+ * instances of those to the result vector.
+ *
+ * @param p Policy to which look up rules.
+ * @param r Structure containing parameters for subject relabel analysis.
+ * @param v Target vector to which append discovered rules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int relabel_analysis_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t * v)
+{
+ apol_avrule_query_t *a = NULL;
+ apol_vector_t *avrules_v = NULL;
+ const qpol_avrule_t *avrule;
+ size_t i;
+ int retval = -1;
+
+ if ((a = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, a, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_source(p, a, r->type, 1) < 0 ||
+ apol_avrule_query_append_perm(p, a, PERM_RELABELTO) < 0 || apol_avrule_query_append_perm(p, a, PERM_RELABELFROM) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; r->classes != NULL && i < apol_vector_get_size(r->classes); i++) {
+ if (apol_avrule_query_append_class(p, a, apol_vector_get_element(r->classes, i)) < 0) {
+ goto cleanup;
+ }
+ }
+ if (apol_avrule_get_by_query(p, a, &avrules_v) < 0) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < apol_vector_get_size(avrules_v); i++) {
+ avrule = (qpol_avrule_t *) apol_vector_get_element(avrules_v, i);
+ if (append_avrule_to_subject_vector(p, r, avrule, v) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&a);
+ apol_vector_destroy(&avrules_v);
+ return retval;
+}
+
+/******************** public functions below ********************/
+
+int apol_relabel_analysis_do(const apol_policy_t * p, apol_relabel_analysis_t * r, apol_vector_t ** v)
+{
+ apol_vector_t *subjects_v = NULL;
+ const qpol_type_t *start_type;
+ int retval = -1;
+ *v = NULL;
+
+ if (r->mode == 0 || r->type == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if (apol_query_get_type(p, r->type, &start_type) < 0) {
+ goto cleanup;
+ }
+
+ if ((*v = apol_vector_create(relabel_result_free)) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if (r->mode == APOL_RELABEL_MODE_OBJ) {
+ if (r->subjects != NULL && (subjects_v = relabel_analysis_get_type_vector(p, r->subjects)) == NULL) {
+ goto cleanup;
+ }
+ if ((r->direction & APOL_RELABEL_DIR_TO) && relabel_analysis_object(p, r, *v, APOL_RELABEL_DIR_TO, subjects_v) < 0) {
+ goto cleanup;
+ }
+ if ((r->direction & APOL_RELABEL_DIR_FROM) &&
+ relabel_analysis_object(p, r, *v, APOL_RELABEL_DIR_FROM, subjects_v) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (relabel_analysis_subject(p, r, *v) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&subjects_v);
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ return retval;
+}
+
+apol_relabel_analysis_t *apol_relabel_analysis_create(void)
+{
+ return calloc(1, sizeof(apol_relabel_analysis_t));
+}
+
+void apol_relabel_analysis_destroy(apol_relabel_analysis_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ free((*r)->type);
+ free((*r)->result);
+ apol_vector_destroy(&(*r)->classes);
+ apol_vector_destroy(&(*r)->subjects);
+ apol_regex_destroy(&(*r)->result_regex);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_relabel_analysis_set_dir(const apol_policy_t * p, apol_relabel_analysis_t * r, unsigned int dir)
+{
+ if (p == NULL || r == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+
+ switch (dir) {
+ case APOL_RELABEL_DIR_BOTH:
+ case APOL_RELABEL_DIR_TO:
+ case APOL_RELABEL_DIR_FROM:
+ {
+ r->mode = APOL_RELABEL_MODE_OBJ;
+ r->direction = dir;
+ break;
+ }
+ case APOL_RELABEL_DIR_SUBJECT:
+ {
+ r->mode = APOL_RELABEL_MODE_SUBJ;
+ r->direction = APOL_RELABEL_DIR_BOTH;
+ break;
+ }
+ default:
+ {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int apol_relabel_analysis_set_type(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *name)
+{
+ if (p == NULL || r == NULL || name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &r->type, NULL, name);
+}
+
+int apol_relabel_analysis_append_class(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *obj_class)
+{
+ char *s;
+ if (p == NULL || r == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ if (obj_class == NULL) {
+ apol_vector_destroy(&r->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (r->classes == NULL && (r->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(r->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_relabel_analysis_append_subject(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *subject)
+{
+ char *s;
+ if (p == NULL || r == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ if (subject == NULL) {
+ apol_vector_destroy(&r->subjects);
+ } else if ((s = strdup(subject)) == NULL ||
+ (r->subjects == NULL && (r->subjects = apol_vector_create(free)) == NULL) ||
+ apol_vector_append(r->subjects, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int apol_relabel_analysis_set_result_regex(const apol_policy_t * p, apol_relabel_analysis_t * r, const char *result)
+{
+ return apol_query_set(p, &r->result, &r->result_regex, result);
+}
+
+/******************** functions to access relabel results ********************/
+
+const apol_vector_t *apol_relabel_result_get_to(const apol_relabel_result_t * r)
+{
+ return r->to;
+}
+
+const apol_vector_t *apol_relabel_result_get_from(const apol_relabel_result_t * r)
+{
+ return r->from;
+}
+
+const apol_vector_t *apol_relabel_result_get_both(const apol_relabel_result_t * r)
+{
+ return r->both;
+}
+
+const qpol_type_t *apol_relabel_result_get_result_type(const apol_relabel_result_t * r)
+{
+ return r->type;
+}
+
+const qpol_avrule_t *apol_relabel_result_pair_get_ruleA(const apol_relabel_result_pair_t * p)
+{
+ return p->ruleA;
+}
+
+const qpol_avrule_t *apol_relabel_result_pair_get_ruleB(const apol_relabel_result_pair_t * p)
+{
+ return p->ruleB;
+}
+
+const qpol_type_t *apol_relabel_result_pair_get_intermediate_type(const apol_relabel_result_pair_t * p)
+{
+ return p->intermed;
+}
diff --git a/libapol/src/render.c b/libapol/src/render.c
new file mode 100644
index 0000000..4bc19b6
--- /dev/null
+++ b/libapol/src/render.c
@@ -0,0 +1,158 @@
+/**
+ * @file
+ *
+ * Routines to render various data structures used by libapol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <apol/context-query.h>
+#include <apol/policy.h>
+#include <apol/render.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef WORDS_BIGENDIAN
+extern void swab(const void *from, void *to, ssize_t n);
+#endif
+
+#if LINK_SHARED == 1
+__asm__(".symver apol_ipv4_addr_render_old,apol_ipv4_addr_render@");
+__asm__(".symver apol_ipv4_addr_render_new,apol_ipv4_addr_render@@VERS_4.1");
+#endif
+
+/**
+ * @brief Internal version of apol_ipv4_addr_render() version 4.1
+ *
+ * Implementation of the exported function apol_ipv4_addr_render()
+ * for version 4.1; this symbol name is not exported.
+ */
+char *apol_ipv4_addr_render_new(const apol_policy_t * policydb, uint32_t addr[4])
+{
+ char buf[40], *b;
+ unsigned char *p = (unsigned char *)&(addr[0]);
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ if ((b = strdup(buf)) == NULL) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ }
+ return b;
+}
+
+#if LINK_SHARED == 0
+char *apol_ipv4_addr_render(const apol_policy_t * policydb, uint32_t addr[4])
+{
+ return apol_ipv4_addr_render_new(policydb, addr);
+}
+#endif
+
+/**
+ * @brief Internal version of apol_ipv4_addr_render() version 4.0 or earlier
+ * @deprecated use the 4.1 version.
+ * @see apol_ipv4_addr_render()
+ */
+char *apol_ipv4_addr_render_old(apol_policy_t * policydb, uint32_t addr)
+{
+ char buf[40], *b;
+ unsigned char *p = (unsigned char *)&addr;
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ if ((b = strdup(buf)) == NULL) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ }
+ return b;
+}
+
+char *apol_ipv6_addr_render(const apol_policy_t * policydb, uint32_t addr[4])
+{
+ uint16_t tmp[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ int i, sz = 0, retv;
+ char buf[40], *b; /* 8 * 4 hex digits + 7 * ':' + '\0' == max size of string */
+ int contract = 0, prev_contr = 0, contr_idx_end = -1;
+ for (i = 0; i < 4; i++) {
+ uint32_t a;
+#ifdef WORDS_BIGENDIAN
+ a = addr[i];
+#else
+ swab(addr + i, &a, sizeof(a));
+#endif
+ /* have to use division and mod here, so as to ignore
+ * host system's byte ordering */
+ tmp[2 * i] = a % (1 << 16);
+ tmp[2 * i + 1] = a / (1 << 16);
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (tmp[i] == 0) {
+ contract++;
+ if (i == 7 && contr_idx_end == -1)
+ contr_idx_end = 8;
+ } else {
+ if (contract > prev_contr) {
+ contr_idx_end = i;
+ }
+ prev_contr = contract;
+ contract = 0;
+ }
+ }
+
+ if (prev_contr > contract)
+ contract = prev_contr;
+
+ for (i = 0; i < 8; i++) {
+ if (i == contr_idx_end - contract) {
+ retv = snprintf(buf + sz, 40 - sz, i ? ":" : "::");
+ sz += retv;
+ } else if (i > contr_idx_end - contract && i < contr_idx_end) {
+ continue;
+ } else {
+ retv = snprintf(buf + sz, 40 - sz, i == 7 ? "%04x" : "%04x:", tmp[i]);
+ sz += retv;
+ }
+ }
+
+ buf[sz] = '\0';
+ if ((b = strdup(buf)) == NULL) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ }
+ return b;
+}
+
+char *apol_qpol_context_render(const apol_policy_t * p, const qpol_context_t * context)
+{
+ apol_context_t *c = NULL;
+ char *rendered_context;
+
+ if (p == NULL || context == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((c = apol_context_create_from_qpol_context(p, context)) == NULL) {
+ return NULL;
+ }
+ rendered_context = apol_context_render(p, c);
+ apol_context_destroy(&c);
+ return rendered_context;
+}
diff --git a/libapol/src/role-query.c b/libapol/src/role-query.c
new file mode 100644
index 0000000..aaafe65
--- /dev/null
+++ b/libapol/src/role-query.c
@@ -0,0 +1,167 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about roles within a
+ * policy. The caller obtains a query object, fills in its
+ * parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_role_query
+{
+ char *role_name, *type_name;
+ unsigned int flags;
+ regex_t *role_regex, *type_regex;
+};
+
+/******************** role queries ********************/
+
+int apol_role_get_by_query(const apol_policy_t * p, apol_role_query_t * r, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *type_iter = NULL;
+ int retval = -1, append_role;
+ *v = NULL;
+ if (qpol_policy_get_role_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_role_t *role;
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0) {
+ goto cleanup;
+ }
+ append_role = 1;
+ if (r != NULL) {
+ const char *role_name;
+ int compval;
+ if (qpol_role_get_name(p->p, role, &role_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, role_name, r->role_name, r->flags, &(r->role_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (r->type_name == NULL || r->type_name[0] == '\0') {
+ goto end_of_query;
+ }
+ if (qpol_role_get_type_iter(p->p, role, &type_iter) < 0) {
+ goto cleanup;
+ }
+ append_role = 0;
+ for (; !qpol_iterator_end(type_iter); qpol_iterator_next(type_iter)) {
+ qpol_type_t *type;
+ if (qpol_iterator_get_item(type_iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare_type(p, type, r->type_name, r->flags, &(r->type_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ append_role = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&type_iter);
+ }
+ end_of_query:
+ if (append_role && apol_vector_append(*v, role)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&type_iter);
+ return retval;
+}
+
+apol_role_query_t *apol_role_query_create(void)
+{
+ return calloc(1, sizeof(apol_role_query_t));
+}
+
+void apol_role_query_destroy(apol_role_query_t ** r)
+{
+ if (*r != NULL) {
+ free((*r)->role_name);
+ free((*r)->type_name);
+ apol_regex_destroy(&(*r)->role_regex);
+ apol_regex_destroy(&(*r)->type_regex);
+ free(*r);
+ *r = NULL;
+ }
+}
+
+int apol_role_query_set_role(const apol_policy_t * p, apol_role_query_t * r, const char *name)
+{
+ return apol_query_set(p, &r->role_name, &r->role_regex, name);
+}
+
+int apol_role_query_set_type(const apol_policy_t * p, apol_role_query_t * r, const char *name)
+{
+ return apol_query_set(p, &r->type_name, &r->type_regex, name);
+}
+
+int apol_role_query_set_regex(const apol_policy_t * p, apol_role_query_t * r, int is_regex)
+{
+ return apol_query_set_regex(p, &r->flags, is_regex);
+}
+
+int apol_role_has_type(const apol_policy_t * p, const qpol_role_t * r, const qpol_type_t * t)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_type_t *tmp_type;
+ uint32_t type_value, t_type_value;
+ int retval = -1;
+
+ if (qpol_type_get_value(p->p, t, &t_type_value) < 0 || qpol_role_get_type_iter(p->p, r, &iter) < 0) {
+ goto cleanup;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&tmp_type));
+ qpol_type_get_value(p->p, tmp_type, &type_value);
+ if (t_type_value == type_value) {
+ retval = 1;
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
diff --git a/libapol/src/terule-query.c b/libapol/src/terule-query.c
new file mode 100644
index 0000000..80a7eff
--- /dev/null
+++ b/libapol/src/terule-query.c
@@ -0,0 +1,1049 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about type enforcement
+ * rules within a policy. The caller obtains a query object, fills in
+ * its parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include <apol/bst.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <string.h>
+
+struct apol_terule_query
+{
+ char *source, *target, *default_type, *bool_name;
+ apol_vector_t *classes;
+ unsigned int rules;
+ unsigned int flags;
+};
+
+/**
+ * Common semantic rule selection routine used in get*rule_by_query.
+ * @param p Policy to search.
+ * @param v Vector of rules to populate (of type qpol_terule_t).
+ * @param rule_type Mask of rules to search.
+ * @param flags Query options as specified by the apol_terule_query.
+ * @param source_list If non-NULL, list of types to use as source.
+ * If NULL, accept all types.
+ * @param target_list If non-NULL, list of types to use as target.
+ * If NULL, accept all types.
+ * @param class_list If non-NULL, list of classes to use.
+ * If NULL, accept all classes.
+ * @param default_list If non-NULL, list of types to use as default.
+ * If NULL, accept all types.
+ * @param bool_name If non-NULL, find conditional rules affected by this boolean.
+ * If NULL, all rules will be considered (including unconditional rules).
+ * @return 0 on success and < 0 on failure.
+ */
+static int rule_select(const apol_policy_t * p, apol_vector_t * v, uint32_t rule_type, unsigned int flags,
+ const apol_vector_t * source_list, const apol_vector_t * target_list, const apol_vector_t * class_list,
+ const apol_vector_t * default_list, const char *bool_name)
+{
+ qpol_iterator_t *iter = NULL;
+ int only_enabled = flags & APOL_QUERY_ONLY_ENABLED;
+ int is_regex = flags & APOL_QUERY_REGEX;
+ int source_as_any = flags & APOL_QUERY_SOURCE_AS_ANY;
+ int retv = -1;
+ regex_t *bool_regex = NULL;
+
+ if (qpol_policy_get_terule_iter(p->p, rule_type, &iter) < 0) {
+ goto cleanup;
+ }
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_terule_t *rule;
+ uint32_t is_enabled;
+ const qpol_cond_t *cond = NULL;
+ int match_source = 0, match_target = 0, match_default = 0, match_bool = 0;
+ size_t i;
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0) {
+ goto cleanup;
+ }
+
+ if (qpol_terule_get_is_enabled(p->p, rule, &is_enabled) < 0) {
+ goto cleanup;
+ }
+ if (!is_enabled && only_enabled) {
+ continue;
+ }
+
+ if (bool_name != NULL) {
+ if (qpol_terule_get_cond(p->p, rule, &cond) < 0) {
+ goto cleanup;
+ }
+ if (cond == NULL) {
+ continue; /* skip unconditional rule */
+ }
+ match_bool = apol_compare_cond_expr(p, cond, bool_name, is_regex, &bool_regex);
+ if (match_bool < 0) {
+ goto cleanup;
+ } else if (match_bool == 0) {
+ continue;
+ }
+ }
+
+ if (source_list == NULL) {
+ match_source = 1;
+ } else {
+ const qpol_type_t *source_type;
+ if (qpol_terule_get_source_type(p->p, rule, &source_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(source_list, source_type, NULL, NULL, &i) == 0) {
+ match_source = 1;
+ }
+ }
+
+ /* if source did not match, but treating source symbol
+ * as any field, then delay rejecting this rule until
+ * the target and default have been checked */
+ if (!source_as_any && !match_source) {
+ continue;
+ }
+
+ if (target_list == NULL || (source_as_any && match_source)) {
+ match_target = 1;
+ } else {
+ const qpol_type_t *target_type;
+ if (qpol_terule_get_target_type(p->p, rule, &target_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(target_list, target_type, NULL, NULL, &i) == 0) {
+ match_target = 1;
+ }
+ }
+
+ if (!source_as_any && !match_target) {
+ continue;
+ }
+
+ if (default_list == NULL || (source_as_any && match_source) || (source_as_any && match_target)) {
+ match_default = 1;
+ } else {
+ const qpol_type_t *default_type;
+ if (qpol_terule_get_default_type(p->p, rule, &default_type) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(default_list, default_type, NULL, NULL, &i) == 0) {
+ match_default = 1;
+ }
+ }
+
+ if (!source_as_any && !match_default) {
+ continue;
+ }
+ /* at least one thing must match if source_as_any was given */
+ if (source_as_any && (!match_source && !match_target && !match_default)) {
+ continue;
+ }
+
+ if (class_list != NULL) {
+ const qpol_class_t *obj_class;
+ if (qpol_terule_get_object_class(p->p, rule, &obj_class) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_get_index(class_list, obj_class, NULL, NULL, &i) < 0) {
+ continue;
+ }
+ }
+
+ if (apol_vector_append(v, rule)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retv = 0;
+
+ cleanup:
+ apol_regex_destroy(&bool_regex);
+ qpol_iterator_destroy(&iter);
+ return retv;
+}
+
+int apol_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v)
+{
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *default_list = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ *v = NULL;
+ unsigned int flags = 0;
+
+ uint32_t rule_type = QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_MEMBER | QPOL_RULE_TYPE_CHANGE;
+ if (t != NULL) {
+ if (t->rules != 0) {
+ rule_type &= t->rules;
+ }
+ flags = t->flags;
+ is_regex = t->flags & APOL_QUERY_REGEX;
+ bool_name = t->bool_name;
+ if (t->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_type_list(p, t->source, is_regex,
+ t->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((t->flags & APOL_QUERY_SOURCE_AS_ANY) && t->source != NULL) {
+ default_list = target_list = source_list;
+ source_as_any = 1;
+ } else {
+ if (t->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_type_list(p, t->target, is_regex,
+ t->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((t->
+ flags & (APOL_QUERY_TARGET_TYPE | APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (t->default_type != NULL &&
+ (default_list =
+ apol_query_create_candidate_type_list(p, t->default_type, is_regex, 0,
+ APOL_QUERY_SYMBOL_IS_TYPE)) == NULL) {
+ goto cleanup;
+ }
+ }
+ if (t->classes != NULL &&
+ apol_vector_get_size(t->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, t->classes)) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, default_list, bool_name)) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ apol_vector_destroy(&default_list);
+ }
+ apol_vector_destroy(&class_list);
+ return retval;
+}
+
+int apol_syn_terule_get_by_query(const apol_policy_t * p, const apol_terule_query_t * t, apol_vector_t ** v)
+{
+ apol_vector_t *source_list = NULL, *target_list = NULL, *class_list = NULL, *default_list = NULL, *syn_v = NULL;
+ int retval = -1, source_as_any = 0, is_regex = 0;
+ char *bool_name = NULL;
+ *v = NULL;
+ size_t i;
+ unsigned int flags = 0;
+
+ if (!p || !qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SYN_RULES)) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+
+ uint32_t rule_type = QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_MEMBER | QPOL_RULE_TYPE_CHANGE;
+ if (t != NULL) {
+ if (t->rules != 0) {
+ rule_type &= t->rules;
+ }
+ flags = t->flags;
+ is_regex = t->flags & APOL_QUERY_REGEX;
+ bool_name = t->bool_name;
+ if (t->source != NULL &&
+ (source_list =
+ apol_query_create_candidate_syn_type_list(p, t->source, is_regex,
+ t->flags & APOL_QUERY_SOURCE_INDIRECT,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if ((t->flags & APOL_QUERY_SOURCE_AS_ANY) && t->source != NULL) {
+ default_list = target_list = source_list;
+ source_as_any = 1;
+ } else {
+ if (t->target != NULL &&
+ (target_list =
+ apol_query_create_candidate_syn_type_list(p, t->target, is_regex,
+ t->flags & APOL_QUERY_TARGET_INDIRECT,
+ ((t->flags & (APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE))
+ / APOL_QUERY_TARGET_TYPE))) == NULL) {
+ goto cleanup;
+ }
+ if (t->default_type != NULL &&
+ (default_list =
+ apol_query_create_candidate_type_list(p, t->default_type, is_regex, 0,
+ APOL_QUERY_SYMBOL_IS_TYPE)) == NULL) {
+ goto cleanup;
+ }
+ }
+ if (t->classes != NULL &&
+ apol_vector_get_size(t->classes) > 0 &&
+ (class_list = apol_query_create_candidate_class_list(p, t->classes)) == NULL) {
+ goto cleanup;
+ }
+ }
+
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (rule_select(p, *v, rule_type, flags, source_list, target_list, class_list, default_list, bool_name)) {
+ goto cleanup;
+ }
+
+ syn_v = apol_terule_list_to_syn_terules(p, *v);
+ if (!syn_v) {
+ goto cleanup;
+ }
+ apol_vector_destroy(v);
+ *v = syn_v;
+ syn_v = NULL;
+
+ /* if source and target are indirect skip post filtering type sets */
+ if ((t->flags & APOL_QUERY_SOURCE_INDIRECT) && (t->flags & (APOL_QUERY_TARGET_INDIRECT | APOL_QUERY_SOURCE_AS_ANY))) {
+ retval = 0;
+ goto cleanup;
+ }
+ /* if not searching by source, target, or default we are done */
+ if (!source_list && !target_list && !default_list) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ if (source_list && !(t->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ apol_vector_destroy(&source_list);
+ source_list =
+ apol_query_create_candidate_type_list(p, t->source, is_regex, 0,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!source_list)
+ goto cleanup;
+ }
+ if (target_list && (source_as_any || !(t->flags & APOL_QUERY_TARGET_INDIRECT))) {
+ if (source_as_any) {
+ target_list = source_list;
+ } else {
+ apol_vector_destroy(&target_list);
+ target_list =
+ apol_query_create_candidate_type_list(p, t->target, is_regex, 0,
+ ((t->flags & (APOL_QUERY_SOURCE_TYPE |
+ APOL_QUERY_SOURCE_ATTRIBUTE)) /
+ APOL_QUERY_SOURCE_TYPE));
+ if (!target_list)
+ goto cleanup;
+ }
+ }
+ if (source_as_any) {
+ default_list = source_list;
+ }
+
+ for (i = 0; i < apol_vector_get_size(*v); i++) {
+ qpol_syn_terule_t *srule = apol_vector_get_element(*v, i);
+ const qpol_type_set_t *stypes = NULL, *ttypes = NULL;
+ const qpol_type_t *dflt = NULL;
+ size_t j;
+ int uses_source = 0, uses_target = 0, uses_default = 0;
+ qpol_syn_terule_get_source_type_set(p->p, srule, &stypes);
+ qpol_syn_terule_get_target_type_set(p->p, srule, &ttypes);
+ if (source_list && !(t->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ uses_source = apol_query_type_set_uses_types_directly(p, stypes, source_list);
+ if (uses_source < 0)
+ goto cleanup;
+ } else if (source_list && (t->flags & APOL_QUERY_SOURCE_INDIRECT)) {
+ uses_source = 1;
+ } else if (!source_list) {
+ uses_source = 1;
+ }
+
+ if (target_list
+ && !(t->flags & APOL_QUERY_TARGET_INDIRECT || (source_as_any && t->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = apol_query_type_set_uses_types_directly(p, ttypes, target_list);
+ if (uses_target < 0)
+ goto cleanup;
+ } else if (target_list
+ && (t->flags & APOL_QUERY_TARGET_INDIRECT || (source_as_any && t->flags & APOL_QUERY_SOURCE_INDIRECT))) {
+ uses_target = 1;
+ } else if (!target_list) {
+ uses_target = 1;
+ }
+
+ if (default_list) {
+ qpol_syn_terule_get_default_type(p->p, srule, &dflt);
+ if (!apol_vector_get_index(default_list, (void *)dflt, NULL, NULL, &j))
+ uses_default = 1;
+ } else if (!default_list) {
+ uses_default = 1;
+ }
+
+ if (!((uses_source && uses_target && uses_default)
+ || (source_as_any && (uses_source || uses_target || uses_default)))) {
+ apol_vector_remove(*v, i);
+ i--;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ apol_vector_destroy(&syn_v);
+ apol_vector_destroy(&source_list);
+ if (!source_as_any) {
+ apol_vector_destroy(&target_list);
+ apol_vector_destroy(&default_list);
+ }
+ apol_vector_destroy(&class_list);
+ return retval;
+}
+
+apol_terule_query_t *apol_terule_query_create(void)
+{
+ apol_terule_query_t *t = calloc(1, sizeof(apol_terule_query_t));
+ if (t != NULL) {
+ t->rules = ~0U;
+ t->flags =
+ (APOL_QUERY_SOURCE_TYPE | APOL_QUERY_SOURCE_ATTRIBUTE | APOL_QUERY_TARGET_TYPE |
+ APOL_QUERY_TARGET_ATTRIBUTE);
+ }
+ return t;
+}
+
+void apol_terule_query_destroy(apol_terule_query_t ** t)
+{
+ if (*t != NULL) {
+ free((*t)->source);
+ free((*t)->target);
+ free((*t)->default_type);
+ free((*t)->bool_name);
+ apol_vector_destroy(&(*t)->classes);
+ free(*t);
+ *t = NULL;
+ }
+}
+
+int apol_terule_query_set_rules(const apol_policy_t * p __attribute__ ((unused)), apol_terule_query_t * t, unsigned int rules)
+{
+ if (rules != 0) {
+ t->rules = rules;
+ } else {
+ t->rules = ~0U;
+ }
+ return 0;
+}
+
+int apol_terule_query_set_source(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &t->flags, is_indirect, APOL_QUERY_SOURCE_INDIRECT);
+ return apol_query_set(p, &t->source, NULL, symbol);
+}
+
+int apol_terule_query_set_source_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component)
+{
+ if (!t || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_SOURCE_TYPE);
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_SOURCE_ATTRIBUTE);
+ return 0;
+}
+
+int apol_terule_query_set_target(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol, int is_indirect)
+{
+ apol_query_set_flag(p, &t->flags, is_indirect, APOL_QUERY_TARGET_INDIRECT);
+ return apol_query_set(p, &t->target, NULL, symbol);
+}
+
+int apol_terule_query_set_target_component(const apol_policy_t * p, apol_terule_query_t * t, unsigned int component)
+{
+ if (!t || !(component & APOL_QUERY_SYMBOL_IS_BOTH)) {
+ ERR(p, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_TYPE, APOL_QUERY_TARGET_TYPE);
+ apol_query_set_flag(p, &t->flags, component & APOL_QUERY_SYMBOL_IS_ATTRIBUTE, APOL_QUERY_TARGET_ATTRIBUTE);
+ return 0;
+}
+
+int apol_terule_query_set_default(const apol_policy_t * p, apol_terule_query_t * t, const char *symbol)
+{
+ return apol_query_set(p, &t->default_type, NULL, symbol);
+}
+
+int apol_terule_query_append_class(const apol_policy_t * p, apol_terule_query_t * t, const char *obj_class)
+{
+ char *s = NULL;
+ if (obj_class == NULL) {
+ apol_vector_destroy(&t->classes);
+ } else if ((s = strdup(obj_class)) == NULL || (t->classes == NULL && (t->classes = apol_vector_create(free)) == NULL)
+ || apol_vector_append(t->classes, s) < 0) {
+ ERR(p, "%s", strerror(errno));
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+int apol_terule_query_set_bool(const apol_policy_t * p, apol_terule_query_t * t, const char *bool_name)
+{
+ return apol_query_set(p, &t->bool_name, NULL, bool_name);
+}
+
+int apol_terule_query_set_enabled(const apol_policy_t * p, apol_terule_query_t * t, int is_enabled)
+{
+ return apol_query_set_flag(p, &t->flags, is_enabled, APOL_QUERY_ONLY_ENABLED);
+}
+
+int apol_terule_query_set_source_any(const apol_policy_t * p, apol_terule_query_t * t, int is_any)
+{
+ return apol_query_set_flag(p, &t->flags, is_any, APOL_QUERY_SOURCE_AS_ANY);
+}
+
+int apol_terule_query_set_regex(const apol_policy_t * p, apol_terule_query_t * t, int is_regex)
+{
+ return apol_query_set_regex(p, &t->flags, is_regex);
+}
+
+/**
+ * Comparison function for two syntactic terules. Will return -1 if
+ * a's line number is before b's, 1 if b is greater.
+ */
+static int apol_syn_terule_comp(const void *a, const void *b, void *data)
+{
+ qpol_syn_terule_t *r1 = (qpol_syn_terule_t *) a;
+ qpol_syn_terule_t *r2 = (qpol_syn_terule_t *) b;
+ apol_policy_t *p = (apol_policy_t *) data;
+ unsigned long num1, num2;
+ if (qpol_syn_terule_get_lineno(p->p, r1, &num1) < 0 || qpol_syn_terule_get_lineno(p->p, r2, &num2) < 0) {
+ return 0;
+ }
+ if (num1 != num2) {
+ return (int)num1 - (int)num2;
+ }
+ return (int)((char *)r1 - (char *)r2);
+}
+
+apol_vector_t *apol_terule_to_syn_terules(const apol_policy_t * p, const qpol_terule_t * rule)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_terule_t *syn_terule;
+ int retval = -1, error = 0;
+ if (qpol_terule_get_syn_terule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_terule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_append(v, syn_terule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort_uniquify(v, apol_syn_terule_comp, (void *)p);
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *apol_terule_list_to_syn_terules(const apol_policy_t * p, const apol_vector_t * rules)
+{
+ apol_bst_t *b = NULL;
+ qpol_terule_t *rule;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_terule_t *syn_terule;
+ apol_vector_t *v = NULL;
+ size_t i;
+ int retval = -1, error = 0;
+
+ if ((b = apol_bst_create(apol_syn_terule_comp, NULL)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(rules); i++) {
+ rule = apol_vector_get_element(rules, i);
+ if (qpol_terule_get_syn_terule_iter(p->p, rule, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&syn_terule) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_bst_insert(b, syn_terule, (void *)p) < 0) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ if ((v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(p, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ if (retval != 0) {
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+char *apol_terule_render(const apol_policy_t * policy, const qpol_terule_t * rule)
+{
+ char *tmp = NULL;
+ const char *tmp_name = NULL;
+ const char *rule_type_str;
+ int error = 0;
+ size_t tmp_sz = 0;
+ uint32_t rule_type = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_terule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER))) {
+ ERR(policy, "%s", "Invalid TE rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get TE rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type */
+ if (qpol_terule_get_source_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* target type */
+ if (qpol_terule_get_target_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s : ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object class */
+ if (qpol_terule_get_object_class(policy->p, rule, &obj_class)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* default type */
+ if (qpol_terule_get_default_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s;", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return tmp;
+
+ err:
+ free(tmp);
+ errno = error;
+ return NULL;
+}
+
+char *apol_syn_terule_render(const apol_policy_t * policy, const qpol_syn_terule_t * rule)
+{
+ char *tmp = NULL;
+ const char *tmp_name = NULL;
+ const char *rule_type_str;
+ int error = 0;
+ uint32_t rule_type = 0, star = 0, comp = 0;
+ const qpol_type_t *type = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL, *iter2 = NULL;
+ size_t tmp_sz = 0, iter_sz = 0, iter2_sz = 0;
+ const qpol_type_set_t *set = NULL;
+
+ if (!policy || !rule) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* rule type */
+ if (qpol_syn_terule_get_rule_type(policy->p, rule, &rule_type)) {
+ return NULL;
+ }
+ if (!(rule_type &= (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER))) {
+ ERR(policy, "%s", "Invalid TE rule type");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!(rule_type_str = apol_rule_type_to_str(rule_type))) {
+ ERR(policy, "%s", "Could not get TE rule type's string");
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", rule_type_str)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* source type set */
+ if (qpol_syn_terule_get_source_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ /* target type set */
+ if (qpol_syn_terule_get_target_type_set(policy->p, rule, &set)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_is_star(policy->p, set, &star)) {
+ error = errno;
+ goto err;
+ }
+ if (star) {
+ if (apol_str_append(&tmp, &tmp_sz, "* ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ } else {
+ if (qpol_type_set_get_is_comp(policy->p, set, &comp)) {
+ error = errno;
+ goto err;
+ }
+ if (comp) {
+ if (apol_str_append(&tmp, &tmp_sz, "~")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ if (qpol_type_set_get_included_types_iter(policy->p, set, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_set_get_subtracted_types_iter(policy->p, set, &iter2)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz) || qpol_iterator_get_size(iter2, &iter2_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter2); qpol_iterator_next(iter2)) {
+ if (qpol_iterator_get_item(iter2, (void **)&type)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "-%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ if (iter_sz + iter2_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ if (apol_str_append(&tmp, &tmp_sz, ": ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* object classes */
+ if (qpol_syn_terule_get_class_iter(policy->p, rule, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_size(iter, &iter_sz)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "{ ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&obj_class)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_class_get_name(policy->p, obj_class, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s ", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ if (iter_sz > 1) {
+ if (apol_str_append(&tmp, &tmp_sz, "} ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ /* default type */
+ if (qpol_syn_terule_get_default_type(policy->p, rule, &type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_name(policy->p, type, &tmp_name)) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&tmp, &tmp_sz, "%s;", tmp_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return tmp;
+
+ err:
+ free(tmp);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&iter2);
+ errno = error;
+ return NULL;
+}
diff --git a/libapol/src/type-query.c b/libapol/src/type-query.c
new file mode 100644
index 0000000..7ead836
--- /dev/null
+++ b/libapol/src/type-query.c
@@ -0,0 +1,202 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about types and
+ * attributes within a policy. The caller obtains a query object,
+ * fills in its parameters, and then runs the query; it obtains a
+ * vector of results. Searches are conjunctive -- all fields of the
+ * search query must match for a datum to be added to the results
+ * query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_type_query
+{
+ char *type_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+struct apol_attr_query
+{
+ char *attr_name;
+ unsigned int flags;
+ regex_t *regex;
+};
+
+/******************** type queries ********************/
+
+int apol_type_get_by_query(const apol_policy_t * p, apol_type_query_t * t, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_type_t *type;
+ unsigned char isattr, isalias;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0 || qpol_type_get_isalias(p->p, type, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (isattr || isalias) {
+ continue;
+ }
+ if (t != NULL) {
+ int compval = apol_compare_type(p,
+ type, t->type_name,
+ t->flags, &(t->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, (void *)type)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_type_query_t *apol_type_query_create(void)
+{
+ return calloc(1, sizeof(apol_type_query_t));
+}
+
+void apol_type_query_destroy(apol_type_query_t ** t)
+{
+ if (*t != NULL) {
+ free((*t)->type_name);
+ apol_regex_destroy(&(*t)->regex);
+ free(*t);
+ *t = NULL;
+ }
+}
+
+int apol_type_query_set_type(const apol_policy_t * p, apol_type_query_t * t, const char *name)
+{
+ return apol_query_set(p, &t->type_name, &t->regex, name);
+}
+
+int apol_type_query_set_regex(const apol_policy_t * p, apol_type_query_t * t, int is_regex)
+{
+ return apol_query_set_regex(p, &t->flags, is_regex);
+}
+
+/******************** attribute queries ********************/
+
+int apol_attr_get_by_query(const apol_policy_t * p, apol_attr_query_t * a, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter;
+ int retval = -1;
+ *v = NULL;
+ if (qpol_policy_get_type_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_type_t *type;
+ unsigned char isattr, isalias;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0) {
+ goto cleanup;
+ }
+ if (qpol_type_get_isattr(p->p, type, &isattr) < 0 || qpol_type_get_isalias(p->p, type, &isalias) < 0) {
+ goto cleanup;
+ }
+ if (!isattr || isalias) {
+ continue;
+ }
+ if (a != NULL) {
+ const char *attr_name;
+ int compval;
+ if (qpol_type_get_name(p->p, type, &attr_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, attr_name, a->attr_name, a->flags, &(a->regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ if (apol_vector_append(*v, type)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+apol_attr_query_t *apol_attr_query_create(void)
+{
+ return calloc(1, sizeof(apol_attr_query_t));
+}
+
+void apol_attr_query_destroy(apol_attr_query_t ** a)
+{
+ if (*a != NULL) {
+ free((*a)->attr_name);
+ apol_regex_destroy(&(*a)->regex);
+ free(*a);
+ *a = NULL;
+ }
+}
+
+int apol_attr_query_set_attr(const apol_policy_t * p, apol_attr_query_t * a, const char *name)
+{
+ return apol_query_set(p, &a->attr_name, &a->regex, name);
+}
+
+int apol_attr_query_set_regex(const apol_policy_t * p, apol_attr_query_t * a, int is_regex)
+{
+ return apol_query_set_regex(p, &a->flags, is_regex);
+}
diff --git a/libapol/src/types-relation-analysis.c b/libapol/src/types-relation-analysis.c
new file mode 100644
index 0000000..79203d9
--- /dev/null
+++ b/libapol/src/types-relation-analysis.c
@@ -0,0 +1,1143 @@
+/**
+ * @file
+ * Implementation of the two-types relationship analysis.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+#include "domain-trans-analysis-internal.h"
+#include "infoflow-analysis-internal.h"
+
+#include <errno.h>
+#include <string.h>
+
+struct apol_types_relation_analysis
+{
+ char *typeA, *typeB;
+ unsigned int analyses;
+};
+
+struct apol_types_relation_result
+{
+ /** vector of qpol_type_t pointers */
+ apol_vector_t *attribs;
+ /** vector of qpol_role_t pointers */
+ apol_vector_t *roles;
+ /** vector of qpol_user_t pointers */
+ apol_vector_t *users;
+ /** vector af apol_types_relation_access, rules that A has in common with B */
+ apol_vector_t *simA;
+ /** vector af apol_types_relation_access, rules that B has in common with A */
+ apol_vector_t *simB;
+ /** vector af apol_types_relation_access, types that A has that B does not */
+ apol_vector_t *disA;
+ /** vector af apol_types_relation_access, types that B has that A does not */
+ apol_vector_t *disB;
+ /** vector of qpol_avrule_t pointers */
+ apol_vector_t *allows;
+ /** vector of qpol_terule_t pointers */
+ apol_vector_t *types;
+ /** vector of apol_infoflow_result_t */
+ apol_vector_t *dirflows;
+ /** vector of apol_infoflow_result_t from type A to B */
+ apol_vector_t *transAB;
+ /** vector of apol_infoflow_result_t from type B to A */
+ apol_vector_t *transBA;
+ /** vector of apol_domain_trans_result_t from type A to B */
+ apol_vector_t *domsAB;
+ /** vector of apol_domain_trans_result_t from type B to A */
+ apol_vector_t *domsBA;
+};
+
+struct apol_types_relation_access
+{
+ const qpol_type_t *type;
+ /** vector of qpol_avrule_t pointers */
+ apol_vector_t *rules;
+};
+
+/******************** actual analysis rountines ********************/
+
+/**
+ * Find the attributes that both typeA and typeB have. Create a
+ * vector of those attributes (as represented as qpol_type_t pointers
+ * relative to the provided policy) and set r->attribs to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_common_attribs(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB,
+ apol_types_relation_result_t * r)
+{
+ qpol_iterator_t *iA = NULL, *iB = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_attr_iter(p->p, typeA, &iA) < 0 || qpol_type_get_attr_iter(p->p, typeB, &iB) < 0) {
+ goto cleanup;
+ }
+ if ((vA = apol_vector_create_from_iter(iA, NULL)) == NULL ||
+ (vB = apol_vector_create_from_iter(iB, NULL)) == NULL ||
+ (r->attribs = apol_vector_create_from_intersection(vA, vB, NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iA);
+ qpol_iterator_destroy(&iB);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ return retval;
+}
+
+/**
+ * Find the roles whose allowed types include both typeA and typeB.
+ * Create a vector of those roles (as represented as qpol_role_t
+ * pointers relative to the provided policy) and set r->roles to that
+ * vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_common_roles(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_role_query_t *rq = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((rq = apol_role_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_role_query_set_type(p, rq, nameA) < 0 ||
+ apol_role_get_by_query(p, rq, &vA) < 0 ||
+ apol_role_query_set_type(p, rq, nameB) < 0 || apol_role_get_by_query(p, rq, &vB) < 0) {
+ goto cleanup;
+ }
+ if ((r->roles = apol_vector_create_from_intersection(vA, vB, NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ }
+
+ retval = 0;
+ cleanup:
+ apol_role_query_destroy(&rq);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ return retval;
+}
+
+/**
+ * Find the users whose roles have as their allowed types both typeA
+ * and typeB. Create a vector of those users (as represented as
+ * qpol_user_t pointers relative to the provided policy) and set
+ * r->users to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_common_users(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_role_query_t *rq = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ qpol_iterator_t *iter = NULL, *riter = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((rq = apol_role_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_role_query_set_type(p, rq, nameA) < 0 ||
+ apol_role_get_by_query(p, rq, &vA) < 0 ||
+ apol_role_query_set_type(p, rq, nameB) < 0 || apol_role_get_by_query(p, rq, &vB) < 0) {
+ goto cleanup;
+ }
+
+ if ((r->users = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_policy_get_user_iter(p->p, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_user_t *user;
+ size_t i;
+ int inA = 0, inB = 0;
+ if (qpol_iterator_get_item(iter, (void **)&user) < 0) {
+ goto cleanup;
+ }
+ if (qpol_user_get_role_iter(p->p, user, &riter) < 0) {
+ goto cleanup;
+ }
+ for (; (!inA || !inB) && !qpol_iterator_end(riter); qpol_iterator_next(riter)) {
+ qpol_role_t *role;
+ if (qpol_iterator_get_item(riter, (void **)&role) < 0) {
+ goto cleanup;
+ }
+ if (!inA && apol_vector_get_index(vA, role, NULL, NULL, &i) == 0) {
+ inA = 1;
+ }
+ if (!inB && apol_vector_get_index(vB, role, NULL, NULL, &i) == 0) {
+ inB = 1;
+ }
+ }
+ qpol_iterator_destroy(&riter);
+ if (inA && inB && apol_vector_append(r->users, user) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_role_query_destroy(&rq);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&riter);
+ return retval;
+}
+
+/**
+ * Comparison function for a vector of apol_types_relation_access_t
+ * pointers. Returns 0 if the access type at a matches the type b.
+ *
+ * @param a Pointer to an existing apol_types_relation_access_t.
+ * @param b Pointer to a qpol_type_t.
+ * @param data Unused.
+ *
+ * @return 0 if a's type matchs b, non-zero if not.
+ */
+static int apol_types_relation_access_compfunc(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ apol_types_relation_access_t *access = (apol_types_relation_access_t *) a;
+ qpol_type_t *t = (qpol_type_t *) b;
+ return (int)((char *)access->type - (char *)t);
+}
+
+/**
+ * Comparison function for a vector of apol_types_relation_access_t
+ * pointers. Returns 0 if the access type a matches the access type b.
+ *
+ * @param a Pointer to an existing apol_types_relation_access_t.
+ * @param b Pointer to another existing apol_types_relation_access_t.
+ * @param data Unused.
+ *
+ * @return 0 if a's type matchs b, non-zero if not.
+ */
+static int apol_types_relation_access_compfunc2(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ apol_types_relation_access_t *accessA = (apol_types_relation_access_t *) a;
+ apol_types_relation_access_t *accessB = (apol_types_relation_access_t *) b;
+ return (int)((char *)accessA->type - (char *)accessB->type);
+}
+
+/**
+ * Deallocate all space associated with a types relation access node,
+ * including the pointer itself. Does nothing if the pointer is
+ * alread NULL.
+ *
+ * @param data Pointer to an access node to free.
+ */
+static void apol_types_relation_access_free(void *data)
+{
+ apol_types_relation_access_t *a = (apol_types_relation_access_t *) data;
+ if (a != NULL) {
+ apol_vector_destroy(&a->rules);
+ free(a);
+ }
+}
+
+/**
+ * Adds a rule to a vector of apol_types_relation_access_t pointers.
+ * Expands the rule's target type, appending new entries as necessary.
+ *
+ * @param p Policy from which rule originated.
+ * @param r Rule to expand and append.
+ * @param access Vector of apol_types_relation_access_t.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_access_append_rule(const apol_policy_t * p, const qpol_avrule_t * r, apol_vector_t * access)
+{
+ const qpol_type_t *t;
+ apol_vector_t *expanded = NULL;
+ size_t i, j;
+ apol_types_relation_access_t *a;
+ int retval = -1;
+ if (qpol_avrule_get_target_type(p->p, r, &t) < 0 || (expanded = apol_query_expand_type(p, t)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(expanded); i++) {
+ t = apol_vector_get_element(expanded, i);
+ if (apol_vector_get_index(access, t, apol_types_relation_access_compfunc, NULL, &j) == 0) {
+ a = (apol_types_relation_access_t *) apol_vector_get_element(access, j);
+ } else {
+ if ((a = calloc(1, sizeof(*a))) == NULL ||
+ (a->rules = apol_vector_create(NULL)) == NULL || apol_vector_append(access, a) < 0) {
+ ERR(p, "%s", strerror(errno));
+ apol_types_relation_access_free(a);
+ goto cleanup;
+ }
+ a->type = t;
+ }
+ if (apol_vector_append(a->rules, (void *)r) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&expanded);
+ return retval;
+}
+
+/**
+ * The following builds separate databases to hold rules for typeA and
+ * typeB respectively. The database holds a vector of pointers to
+ * apol_types_relation_access_t objects. Then compare access lists
+ * for typeA and typeB, determine common and unique access and have
+ * easy access to the relevant rules.
+ *
+ * @param p Policy to look up av rules.
+ * @param typeA First type to build access list.
+ * @param typeB Other type to build access list.
+ * @param accessesA Vector of apol_types_relation_access_t for typeA.
+ * @param accessesB Vector of apol_types_relation_access_t for typeB.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_create_access_pools(const apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB, apol_vector_t * accessesA, apol_vector_t * accessesB)
+{
+ const char *nameA, *nameB;
+ apol_avrule_query_t *aq = NULL;
+ apol_vector_t *vA = NULL, *vB = NULL;
+ size_t i;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((aq = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, aq, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_source(p, aq, nameA, 1) < 0 ||
+ apol_avrule_get_by_query(p, aq, &vA) < 0 ||
+ apol_avrule_query_set_source(p, aq, nameB, 1) < 0 || apol_avrule_get_by_query(p, aq, &vB) < 0) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(vA); i++) {
+ qpol_avrule_t *r = (qpol_avrule_t *) apol_vector_get_element(vA, i);
+ if (apol_types_relation_access_append_rule(p, r, accessesA) < 0) {
+ goto cleanup;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(vB); i++) {
+ qpol_avrule_t *r = (qpol_avrule_t *) apol_vector_get_element(vB, i);
+ if (apol_types_relation_access_append_rule(p, r, accessesB) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&aq);
+ apol_vector_destroy(&vA);
+ apol_vector_destroy(&vB);
+ return retval;
+}
+
+/**
+ * Allocate a new apol_types_relation_access_t and append it to a
+ * vector. The new access node's type will be set to a's type. The
+ * rules will be a clone of a's rules.
+ *
+ * @param p Policy from which rule originated.
+ * @param a Access node to duplicate.
+ * @param access Vector of apol_types_relation_access_t to append.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_access_append(const apol_policy_t * p, const apol_types_relation_access_t * a,
+ apol_vector_t * access)
+{
+ apol_types_relation_access_t *new_a;
+ int retval = -1;
+ if ((new_a = calloc(1, sizeof(*new_a))) == NULL
+ || (new_a->rules = apol_vector_create_from_vector(a->rules, NULL, NULL, NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ new_a->type = a->type;
+ if (apol_vector_append(access, new_a) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_types_relation_access_free(new_a);
+ }
+ return retval;
+}
+
+/**
+ * Find accesses, both similar and dissimilar, between both typeA and
+ * typeB.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param do_similar 1 if to calculate similar accesses, 0 to skip.
+ * @param do_dissimilar 1 if to calculate dissimilar accesses, 0 to skip.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_accesses(const apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB, int do_similar, int do_dissimilar,
+ apol_types_relation_result_t * r)
+{
+ apol_vector_t *accessesA = NULL, *accessesB = NULL;
+ apol_types_relation_access_t *a, *b;
+ size_t i, j;
+ int retval = -1;
+
+ if ((accessesA = apol_vector_create(apol_types_relation_access_free)) == NULL
+ || (accessesB = apol_vector_create(apol_types_relation_access_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_create_access_pools(p, typeA, typeB, accessesA, accessesB) < 0) {
+ goto cleanup;
+ }
+ apol_vector_sort(accessesA, apol_types_relation_access_compfunc2, NULL);
+ apol_vector_sort(accessesB, apol_types_relation_access_compfunc2, NULL);
+
+ if (do_similar) {
+ if ((r->simA = apol_vector_create(apol_types_relation_access_free)) == NULL
+ || (r->simB = apol_vector_create(apol_types_relation_access_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (do_dissimilar) {
+ if ((r->disA = apol_vector_create(apol_types_relation_access_free)) == NULL
+ || (r->disB = apol_vector_create(apol_types_relation_access_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ /* Step through each element for each access sorted list. If
+ * their types match and if do_similiar, then append the union
+ * of the access rules to the results. If their types do not
+ * match and if do_similar then add to results.
+ */
+ for (i = j = 0; i < apol_vector_get_size(accessesA) && j < apol_vector_get_size(accessesB);) {
+ a = (apol_types_relation_access_t *) apol_vector_get_element(accessesA, i);
+ b = (apol_types_relation_access_t *) apol_vector_get_element(accessesB, j);
+ if (a->type == b->type) {
+ if (do_similar &&
+ (apol_types_relation_access_append(p, a, r->simA) < 0 ||
+ apol_types_relation_access_append(p, b, r->simB) < 0)) {
+ goto cleanup;
+ }
+ i++;
+ j++;
+ } else {
+ if (a->type < b->type) {
+ if (do_dissimilar && apol_types_relation_access_append(p, a, r->disA) < 0) {
+ goto cleanup;
+ }
+ i++;
+ } else {
+ if (do_dissimilar && apol_types_relation_access_append(p, b, r->disB) < 0) {
+ goto cleanup;
+ }
+ j++;
+ }
+ }
+ }
+ for (; do_dissimilar && i < apol_vector_get_size(accessesA); i++) {
+ a = (apol_types_relation_access_t *) apol_vector_get_element(accessesA, i);
+ if (apol_types_relation_access_append(p, a, r->disA) < 0) {
+ goto cleanup;
+ }
+ }
+ for (; do_dissimilar && j < apol_vector_get_size(accessesB); j++) {
+ b = (apol_types_relation_access_t *) apol_vector_get_element(accessesB, j);
+ if (apol_types_relation_access_append(p, b, r->disB) < 0) {
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&accessesA);
+ apol_vector_destroy(&accessesB);
+ return retval;
+}
+
+/**
+ * Find all allow rules that involve both types. Create a vector of
+ * those rules (as represented as qpol_avrule_t pointers relative to
+ * the provided policy) and set r->allows to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_allows(const apol_policy_t * p, const qpol_type_t * typeA, const qpol_type_t * typeB,
+ apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_avrule_query_t *aq = NULL;
+ apol_vector_t *v = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((aq = apol_avrule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_rules(p, aq, QPOL_RULE_ALLOW) < 0 ||
+ apol_avrule_query_set_source(p, aq, nameA, 1) < 0 ||
+ apol_avrule_query_set_target(p, aq, nameB, 1) < 0 || apol_avrule_get_by_query(p, aq, &r->allows) < 0) {
+ goto cleanup;
+ }
+ if (apol_avrule_query_set_source(p, aq, nameB, 1) < 0 ||
+ apol_avrule_query_set_target(p, aq, nameA, 1) < 0 || apol_avrule_get_by_query(p, aq, &v) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_cat(r->allows, v) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_avrule_query_destroy(&aq);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Find all type transition / type change rules that involve both
+ * types. Create a vector of those rules (as represented as
+ * qpol_terule_t pointers relative to the provided policy) and set
+ * r->types to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_types(const apol_policy_t * p, const qpol_type_t * typeA, const qpol_type_t * typeB,
+ apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_terule_query_t *tq = NULL;
+ apol_vector_t *v = NULL, *candidate_types = NULL;
+ const qpol_terule_t *rule;
+ const qpol_type_t *target, *default_type;
+ size_t i, j;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((r->types = apol_vector_create(NULL)) == NULL || (tq = apol_terule_query_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_terule_query_set_rules(p, tq, QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE) < 0 ||
+ apol_terule_query_set_source(p, tq, nameA, 1) < 0 ||
+ apol_terule_get_by_query(p, tq, &v) < 0 ||
+ (candidate_types = apol_query_create_candidate_type_list(p, nameB, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ rule = (qpol_terule_t *) apol_vector_get_element(v, i);
+ if (qpol_terule_get_target_type(p->p, rule, &target) < 0 ||
+ qpol_terule_get_default_type(p->p, rule, &default_type) < 0) {
+ goto cleanup;
+ }
+ if ((apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0 ||
+ apol_vector_get_index(candidate_types, default_type, NULL, NULL, &j) == 0) &&
+ apol_vector_append(r->types, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&candidate_types);
+ if (apol_terule_query_set_source(p, tq, nameB, 1) < 0 ||
+ apol_terule_get_by_query(p, tq, &v) < 0 ||
+ (candidate_types = apol_query_create_candidate_type_list(p, nameA, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ rule = (qpol_terule_t *) apol_vector_get_element(v, i);
+ if (qpol_terule_get_target_type(p->p, rule, &target) < 0 ||
+ qpol_terule_get_default_type(p->p, rule, &default_type) < 0) {
+ goto cleanup;
+ }
+ if ((apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0 ||
+ apol_vector_get_index(candidate_types, default_type, NULL, NULL, &j) == 0) &&
+ apol_vector_append(r->types, (void *)rule) < 0) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ apol_terule_query_destroy(&tq);
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&candidate_types);
+ return retval;
+}
+
+/**
+ * Given a vector of apol_infoflow_result_t objects, deep copy to the
+ * results vector those infoflow results whose target type matches
+ * target_name (or any of target_name's attributes or aliases).
+ *
+ * @param p Policy within which to lookup types.
+ * @param v Vector of existing apol_infoflow_result_t.
+ * @param target_name Target type name.
+ * @param results Vector to which clone matching infoflow results.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_clone_infoflow(const apol_policy_t * p, const apol_vector_t * v, const char *target_name,
+ apol_vector_t * results)
+{
+ apol_vector_t *candidate_types = NULL;
+ const qpol_type_t *target;
+ apol_infoflow_result_t *res, *new_res;
+ size_t i, j;
+ int retval = -1;
+ if ((candidate_types = apol_query_create_candidate_type_list(p, target_name, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ res = (apol_infoflow_result_t *) apol_vector_get_element(v, i);
+ target = apol_infoflow_result_get_end_type(res);
+ if (apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0) {
+ if ((new_res = infoflow_result_create_from_infoflow_result(res)) == NULL ||
+ apol_vector_append(results, new_res) < 0) {
+ infoflow_result_free(new_res);
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&candidate_types);
+ return retval;
+}
+
+/**
+ * Find all direct information flows between the two types. Create a
+ * vector of apol_infoflow_result_t and set r->dirflows to that vector.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_directflow(const apol_policy_t * p,
+ const qpol_type_t * typeA, const qpol_type_t * typeB, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_infoflow_analysis_t *ia = NULL;
+ apol_vector_t *v = NULL;
+ apol_infoflow_graph_t *g = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((r->dirflows = apol_vector_create(infoflow_result_free)) == NULL || (ia = apol_infoflow_analysis_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT) < 0 ||
+ apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_EITHER) < 0 ||
+ apol_infoflow_analysis_set_type(p, ia, nameA) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0) {
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_infoflow(p, v, nameB, r->dirflows) < 0) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ apol_infoflow_analysis_destroy(&ia);
+ apol_infoflow_graph_destroy(&g);
+ return retval;
+}
+
+/**
+ * Find (some) transitive information flows between the two types.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param do_transAB 1 if to find paths from type A to B, 0 to skip.
+ * @param do_transBA 1 if to find paths from type B to A, 0 to skip.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_transflow(const apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB,
+ unsigned int do_transAB, unsigned int do_transBA, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_infoflow_analysis_t *ia = NULL;
+ apol_vector_t *v = NULL;
+ apol_infoflow_graph_t *g = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((ia = apol_infoflow_analysis_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_TRANS) < 0 ||
+ apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_OUT) < 0) {
+ goto cleanup;
+ }
+ if (do_transAB) {
+ if (apol_infoflow_analysis_set_type(p, ia, nameA) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0) {
+ goto cleanup;
+ }
+ if ((r->transAB = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_infoflow(p, v, nameB, r->transAB) < 0) {
+ goto cleanup;
+ }
+ }
+ if (do_transBA) {
+ apol_vector_destroy(&v);
+ if ((do_transAB &&
+ apol_infoflow_analysis_do_more(p, g, nameB, &v) < 0) ||
+ (!do_transAB &&
+ (apol_infoflow_analysis_set_type(p, ia, nameB) < 0 || apol_infoflow_analysis_do(p, ia, &v, &g) < 0))) {
+ goto cleanup;
+ }
+ if ((r->transBA = apol_vector_create(infoflow_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_infoflow(p, v, nameA, r->transBA) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ apol_infoflow_analysis_destroy(&ia);
+ apol_infoflow_graph_destroy(&g);
+ return retval;
+}
+
+/**
+ * Given a vector of apol_domain_trans_result_t objects, deep copy to
+ * the results vector those domain transition results whose target
+ * type matches target_name (or any of target_name's attributes or
+ * aliases).
+ *
+ * @param p Policy within which to lookup types.
+ * @param v Vector of existing apol_domain_trans_result_t.
+ * @param target_name Target type name.
+ * @param results Vector to which clone matching domain transition
+ * results.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_clone_domaintrans(const apol_policy_t * p, const apol_vector_t * v, const char *target_name,
+ apol_vector_t * results)
+{
+ apol_vector_t *candidate_types = NULL;
+ const qpol_type_t *target;
+ apol_domain_trans_result_t *res, *new_res;
+ size_t i, j;
+ int retval = -1;
+ if ((candidate_types = apol_query_create_candidate_type_list(p, target_name, 0, 1, APOL_QUERY_SYMBOL_IS_BOTH)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ res = (apol_domain_trans_result_t *) apol_vector_get_element(v, i);
+ target = apol_domain_trans_result_get_end_type(res);
+ if (apol_vector_get_index(candidate_types, target, NULL, NULL, &j) == 0) {
+ if ((new_res = apol_domain_trans_result_create_from_domain_trans_result(res)) == NULL ||
+ apol_vector_append(results, new_res) < 0) {
+ domain_trans_result_free(new_res);
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&candidate_types);
+ return retval;
+}
+
+/**
+ * Find domain transitions between the two types.
+ *
+ * @param p Policy containing types' information.
+ * @param typeA First type to check.
+ * @param typeB Other type to check.
+ * @param do_domainAB 1 if to find transitions from type A to B, 0 to skip.
+ * @param do_domainBA 1 if to find transitions from type B to A, 0 to skip.
+ * @param r Result structure to fill.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int apol_types_relation_domain(apol_policy_t * p,
+ const qpol_type_t * typeA,
+ const qpol_type_t * typeB,
+ unsigned int do_domainsAB, unsigned int do_domainsBA, apol_types_relation_result_t * r)
+{
+ const char *nameA, *nameB;
+ apol_domain_trans_analysis_t *dta = NULL;
+ apol_vector_t *v = NULL;
+ int retval = -1;
+
+ if (qpol_type_get_name(p->p, typeA, &nameA) < 0 || qpol_type_get_name(p->p, typeB, &nameB) < 0) {
+ goto cleanup;
+ }
+ if ((dta = apol_domain_trans_analysis_create()) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_policy_build_domain_trans_table(p) < 0 ||
+ apol_domain_trans_analysis_set_direction(p, dta, APOL_DOMAIN_TRANS_DIRECTION_FORWARD) < 0) {
+ goto cleanup;
+ }
+ if (do_domainsAB) {
+ apol_policy_reset_domain_trans_table(p);
+ if (apol_domain_trans_analysis_set_start_type(p, dta, nameA) < 0 || apol_domain_trans_analysis_do(p, dta, &v) < 0) {
+ goto cleanup;
+ }
+ if ((r->domsAB = apol_vector_create(domain_trans_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_domaintrans(p, v, nameB, r->domsAB) < 0) {
+ goto cleanup;
+ }
+ }
+ if (do_domainsBA) {
+ apol_vector_destroy(&v);
+ apol_policy_reset_domain_trans_table(p);
+ if (apol_domain_trans_analysis_set_start_type(p, dta, nameB) < 0 || apol_domain_trans_analysis_do(p, dta, &v) < 0) {
+ goto cleanup;
+ }
+ if ((r->domsBA = apol_vector_create(domain_trans_result_free)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (apol_types_relation_clone_domaintrans(p, v, nameA, r->domsBA) < 0) {
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ apol_domain_trans_analysis_destroy(&dta);
+ return retval;
+}
+
+/******************** public functions below ********************/
+
+int apol_types_relation_analysis_do(apol_policy_t * p, const apol_types_relation_analysis_t * tr, apol_types_relation_result_t ** r)
+{
+ const qpol_type_t *typeA, *typeB;
+ unsigned char isattrA, isattrB;
+ unsigned int do_similar_access, do_dissimilar_access;
+ unsigned int do_transAB, do_transBA;
+ unsigned int do_domainAB, do_domainBA;
+ int retval = -1;
+ *r = NULL;
+
+ if (tr->typeA == NULL || tr->typeB == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ goto cleanup;
+ }
+ if (apol_query_get_type(p, tr->typeA, &typeA) < 0 ||
+ apol_query_get_type(p, tr->typeB, &typeB) < 0 ||
+ qpol_type_get_isattr(p->p, typeA, &isattrA) < 0 || qpol_type_get_isattr(p->p, typeB, &isattrB) < 0) {
+ goto cleanup;
+ }
+ if (isattrA) {
+ ERR(p, "Symbol %s is an attribute.", tr->typeA);
+ goto cleanup;
+ }
+ if (isattrB) {
+ ERR(p, "Symbol %s is an attribute.", tr->typeB);
+ goto cleanup;
+ }
+ if ((*r = calloc(1, sizeof(**r))) == NULL) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_COMMON_ATTRIBS) && apol_types_relation_common_attribs(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_COMMON_ROLES) && apol_types_relation_common_roles(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_COMMON_USERS) && apol_types_relation_common_users(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ do_similar_access = tr->analyses & APOL_TYPES_RELATION_SIMILAR_ACCESS;
+ do_dissimilar_access = tr->analyses & APOL_TYPES_RELATION_DISSIMILAR_ACCESS;
+ if ((do_similar_access || do_dissimilar_access) &&
+ apol_types_relation_accesses(p, typeA, typeB, do_similar_access, do_dissimilar_access, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_ALLOW_RULES) && apol_types_relation_allows(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_TYPE_RULES) && apol_types_relation_types(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ if ((tr->analyses & APOL_TYPES_RELATION_DIRECT_FLOW) && apol_types_relation_directflow(p, typeA, typeB, *r) < 0) {
+ goto cleanup;
+ }
+ do_transAB = tr->analyses & APOL_TYPES_RELATION_TRANS_FLOW_AB;
+ do_transBA = tr->analyses & APOL_TYPES_RELATION_TRANS_FLOW_BA;
+ if ((do_transAB || do_transBA) && apol_types_relation_transflow(p, typeA, typeB, do_transAB, do_transBA, *r) < 0) {
+ goto cleanup;
+ }
+ do_domainAB = tr->analyses & APOL_TYPES_RELATION_DOMAIN_TRANS_AB;
+ do_domainBA = tr->analyses & APOL_TYPES_RELATION_DOMAIN_TRANS_BA;
+ if ((do_domainAB || do_domainBA) && apol_types_relation_domain(p, typeA, typeB, do_domainAB, do_domainBA, *r) < 0) {
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_types_relation_result_destroy(r);
+ }
+ return retval;
+}
+
+apol_types_relation_analysis_t *apol_types_relation_analysis_create(void)
+{
+ return calloc(1, sizeof(apol_types_relation_analysis_t));
+}
+
+void apol_types_relation_analysis_destroy(apol_types_relation_analysis_t ** tr)
+{
+ if (*tr != NULL) {
+ free((*tr)->typeA);
+ free((*tr)->typeB);
+ free(*tr);
+ *tr = NULL;
+ }
+}
+
+int apol_types_relation_analysis_set_first_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, const char *name)
+{
+ if (name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &tr->typeA, NULL, name);
+}
+
+int apol_types_relation_analysis_set_other_type(const apol_policy_t * p, apol_types_relation_analysis_t * tr, const char *name)
+{
+ if (name == NULL) {
+ ERR(p, "%s", strerror(EINVAL));
+ return -1;
+ }
+ return apol_query_set(p, &tr->typeB, NULL, name);
+}
+
+int apol_types_relation_analysis_set_analyses(const apol_policy_t * p __attribute__ ((unused)),
+ apol_types_relation_analysis_t * tr, unsigned int analyses)
+{
+ if (analyses != 0) {
+ tr->analyses = analyses;
+ } else {
+ tr->analyses = ~0U;
+ }
+ return 0;
+}
+
+/*************** functions to access type relation results ***************/
+
+void apol_types_relation_result_destroy(apol_types_relation_result_t ** result)
+{
+ if (*result != NULL) {
+ apol_vector_destroy(&(*result)->attribs);
+ apol_vector_destroy(&(*result)->roles);
+ apol_vector_destroy(&(*result)->users);
+ apol_vector_destroy(&(*result)->simA);
+ apol_vector_destroy(&(*result)->simB);
+ apol_vector_destroy(&(*result)->disA);
+ apol_vector_destroy(&(*result)->disB);
+ apol_vector_destroy(&(*result)->allows);
+ apol_vector_destroy(&(*result)->types);
+ apol_vector_destroy(&(*result)->dirflows);
+ apol_vector_destroy(&(*result)->transAB);
+ apol_vector_destroy(&(*result)->transBA);
+ apol_vector_destroy(&(*result)->domsAB);
+ apol_vector_destroy(&(*result)->domsBA);
+ free(*result);
+ *result = NULL;
+ }
+}
+
+const apol_vector_t *apol_types_relation_result_get_attributes(const apol_types_relation_result_t * result)
+{
+ return result->attribs;
+}
+
+const apol_vector_t *apol_types_relation_result_get_roles(const apol_types_relation_result_t * result)
+{
+ return result->roles;
+}
+
+const apol_vector_t *apol_types_relation_result_get_users(const apol_types_relation_result_t * result)
+{
+ return result->users;
+}
+
+const apol_vector_t *apol_types_relation_result_get_similar_first(const apol_types_relation_result_t * result)
+{
+ return result->simA;
+}
+
+const apol_vector_t *apol_types_relation_result_get_similar_other(const apol_types_relation_result_t * result)
+{
+ return result->simB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_dissimilar_first(const apol_types_relation_result_t * result)
+{
+ return result->disA;
+}
+
+const apol_vector_t *apol_types_relation_result_get_dissimilar_other(const apol_types_relation_result_t * result)
+{
+ return result->disB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_allowrules(const apol_types_relation_result_t * result)
+{
+ return result->allows;
+}
+
+const apol_vector_t *apol_types_relation_result_get_typerules(const apol_types_relation_result_t * result)
+{
+ return result->types;
+}
+
+const apol_vector_t *apol_types_relation_result_get_directflows(const apol_types_relation_result_t * result)
+{
+ return result->dirflows;
+}
+
+const apol_vector_t *apol_types_relation_result_get_transflowsAB(const apol_types_relation_result_t * result)
+{
+ return result->transAB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_transflowsBA(const apol_types_relation_result_t * result)
+{
+ return result->transBA;
+}
+
+const apol_vector_t *apol_types_relation_result_get_domainsAB(const apol_types_relation_result_t * result)
+{
+ return result->domsAB;
+}
+
+const apol_vector_t *apol_types_relation_result_get_domainsBA(const apol_types_relation_result_t * result)
+{
+ return result->domsBA;
+}
+
+const qpol_type_t *apol_types_relation_access_get_type(const apol_types_relation_access_t * a)
+{
+ return a->type;
+}
+
+const apol_vector_t *apol_types_relation_access_get_rules(const apol_types_relation_access_t * a)
+{
+ return a->rules;
+}
diff --git a/libapol/src/user-query.c b/libapol/src/user-query.c
new file mode 100644
index 0000000..066e005
--- /dev/null
+++ b/libapol/src/user-query.c
@@ -0,0 +1,198 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to make queries about users within a
+ * policy. The caller obtains a query object, fills in its
+ * parameters, and then runs the query; it obtains a vector of
+ * results. Searches are conjunctive -- all fields of the search
+ * query must match for a datum to be added to the results query.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "policy-query-internal.h"
+
+#include <errno.h>
+
+struct apol_user_query
+{
+ char *user_name, *role_name;
+ apol_mls_level_t *default_level;
+ apol_mls_range_t *range;
+ unsigned int flags;
+ regex_t *user_regex, *role_regex;
+};
+
+/******************** user queries ********************/
+
+int apol_user_get_by_query(const apol_policy_t * p, apol_user_query_t * u, apol_vector_t ** v)
+{
+ qpol_iterator_t *iter = NULL, *role_iter = NULL;
+ apol_mls_level_t *default_level = NULL;
+ apol_mls_range_t *range = NULL;
+ int retval = -1, append_user;
+ *v = NULL;
+ if (qpol_policy_get_user_iter(p->p, &iter) < 0) {
+ return -1;
+ }
+ if ((*v = apol_vector_create(NULL)) == NULL) {
+ ERR(p, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_user_t *user;
+ if (qpol_iterator_get_item(iter, (void **)&user) < 0) {
+ goto cleanup;
+ }
+ append_user = 1;
+ if (u != NULL) {
+ const char *user_name;
+ int compval;
+ const qpol_mls_level_t *mls_default_level;
+ const qpol_mls_range_t *mls_range;
+
+ qpol_iterator_destroy(&role_iter);
+ apol_mls_level_destroy(&default_level);
+ apol_mls_range_destroy(&range);
+
+ if (qpol_user_get_name(p->p, user, &user_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, user_name, u->user_name, u->flags, &(u->user_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ if (qpol_user_get_role_iter(p->p, user, &role_iter) < 0) {
+ goto cleanup;
+ }
+ if (u->role_name != NULL && u->role_name[0] != '\0') {
+ append_user = 0;
+ for (; !qpol_iterator_end(role_iter); qpol_iterator_next(role_iter)) {
+ qpol_role_t *role;
+ const char *role_name;
+ if (qpol_iterator_get_item(role_iter, (void **)&role) < 0 ||
+ qpol_role_get_name(p->p, role, &role_name) < 0) {
+ goto cleanup;
+ }
+ compval = apol_compare(p, role_name, u->role_name, u->flags, &(u->role_regex));
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 1) {
+ append_user = 1;
+ break;
+ }
+ }
+ }
+ if (apol_policy_is_mls(p)) {
+ if (qpol_user_get_dfltlevel(p->p, user, &mls_default_level) < 0 ||
+ (default_level = apol_mls_level_create_from_qpol_mls_level(p, mls_default_level)) == NULL) {
+ goto cleanup;
+ }
+ compval = apol_mls_level_compare(p, default_level, u->default_level);
+ apol_mls_level_destroy(&default_level);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval != APOL_MLS_EQ) {
+ continue;
+ }
+
+ if (qpol_user_get_range(p->p, user, &mls_range) < 0 ||
+ (range = apol_mls_range_create_from_qpol_mls_range(p, mls_range)) == NULL) {
+ goto cleanup;
+ }
+ compval = apol_mls_range_compare(p, range, u->range, u->flags);
+ apol_mls_range_destroy(&range);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval == 0) {
+ continue;
+ }
+ }
+ }
+ if (append_user && apol_vector_append(*v, user)) {
+ ERR(p, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ apol_vector_destroy(v);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&role_iter);
+ apol_mls_level_destroy(&default_level);
+ apol_mls_range_destroy(&range);
+ return retval;
+}
+
+apol_user_query_t *apol_user_query_create(void)
+{
+ return calloc(1, sizeof(apol_user_query_t));
+}
+
+void apol_user_query_destroy(apol_user_query_t ** u)
+{
+ if (*u != NULL) {
+ free((*u)->user_name);
+ free((*u)->role_name);
+ apol_mls_level_destroy(&((*u)->default_level));
+ apol_mls_range_destroy(&((*u)->range));
+ apol_regex_destroy(&(*u)->user_regex);
+ apol_regex_destroy(&(*u)->role_regex);
+ free(*u);
+ *u = NULL;
+ }
+}
+
+int apol_user_query_set_user(const apol_policy_t * p, apol_user_query_t * u, const char *name)
+{
+ return apol_query_set(p, &u->user_name, &u->user_regex, name);
+}
+
+int apol_user_query_set_role(const apol_policy_t * p, apol_user_query_t * u, const char *role)
+{
+ return apol_query_set(p, &u->role_name, &u->role_regex, role);
+}
+
+int apol_user_query_set_default_level(const apol_policy_t * p
+ __attribute__ ((unused)), apol_user_query_t * u, apol_mls_level_t * level)
+{
+ u->default_level = level;
+ return 0;
+}
+
+int apol_user_query_set_range(const apol_policy_t * p __attribute__ ((unused)),
+ apol_user_query_t * u, apol_mls_range_t * range, unsigned int range_match)
+{
+ if (u->range != NULL) {
+ apol_mls_range_destroy(&u->range);
+ }
+ u->range = range;
+ u->flags = (u->flags & ~APOL_QUERY_FLAGS) | range_match;
+ return 0;
+}
+
+int apol_user_query_set_regex(const apol_policy_t * p, apol_user_query_t * u, int is_regex)
+{
+ return apol_query_set_regex(p, &u->flags, is_regex);
+}
diff --git a/libapol/src/util.c b/libapol/src/util.c
new file mode 100644
index 0000000..dd6d300
--- /dev/null
+++ b/libapol/src/util.c
@@ -0,0 +1,659 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2001-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <apol/util.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+/* these are needed for nodecons and IPv4 and IPv6 */
+#include <qpol/nodecon_query.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h> /* needed for portcon's protocol */
+
+/* use 8k line size */
+#define APOL_LINE_SZ 8192
+#define APOL_ENVIRON_VAR_NAME "APOL_INSTALL_DIR"
+
+const char *libapol_get_version(void)
+{
+ return LIBAPOL_VERSION_STRING;
+}
+
+int apol_str_to_internal_ip(const char *str, uint32_t ip[4])
+{
+ bool ipv4 = false;
+ bool ipv6 = false;
+
+ if (!str || !ip) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ip[0] = ip[1] = ip[2] = ip[3] = 0;
+
+ if (strchr(str, '.'))
+ ipv4 = true;
+
+ if (strchr(str, ':'))
+ ipv6 = true;
+
+ if (ipv4 == ipv6) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ipv4) {
+ unsigned char *p = (unsigned char *)&(ip[0]);
+ int seg = 0;
+ uint32_t val = 0; /* value of current segment of address */
+ size_t len = strlen(str), i;
+ for (i = 0; i <= len; i++) {
+ if (str[i] == '.' || str[i] == '\0') {
+ if (val > 255) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ p[seg] = (unsigned char)(0xff & val);
+ seg++;
+ val = 0;
+ if (seg == 4)
+ break;
+ } else if (isdigit(str[i])) {
+ char tmp[2] = { str[i], 0 };
+ val = val * 10 + atoi(tmp);
+ } else {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ } else {
+ struct in6_addr addr;
+ if (inet_pton(AF_INET6, str, &addr) <= 0) {
+ return -1;
+ }
+ memcpy(ip, addr.s6_addr32, 16);
+ }
+
+ return ipv4 ? QPOL_IPV4 : QPOL_IPV6;
+}
+
+const char *apol_objclass_to_str(uint32_t objclass)
+{
+ switch (objclass) {
+ case QPOL_CLASS_BLK_FILE:
+ return "block";
+ case QPOL_CLASS_CHR_FILE:
+ return "char";
+ case QPOL_CLASS_DIR:
+ return "dir";
+ case QPOL_CLASS_FIFO_FILE:
+ return "fifo";
+ case QPOL_CLASS_FILE:
+ return "file";
+ case QPOL_CLASS_LNK_FILE:
+ return "link";
+ case QPOL_CLASS_SOCK_FILE:
+ return "sock";
+ case QPOL_CLASS_ALL:
+ return "any";
+ }
+ return NULL;
+}
+
+uint32_t apol_str_to_objclass(const char *objclass)
+{
+ if (objclass == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (strcmp(objclass, "block") == 0) {
+ return QPOL_CLASS_BLK_FILE;
+ }
+ if (strcmp(objclass, "char") == 0) {
+ return QPOL_CLASS_CHR_FILE;
+ }
+ if (strcmp(objclass, "dir") == 0) {
+ return QPOL_CLASS_DIR;
+ }
+ if (strcmp(objclass, "fifo") == 0) {
+ return QPOL_CLASS_FIFO_FILE;
+ }
+ if (strcmp(objclass, "file") == 0) {
+ return QPOL_CLASS_FILE;
+ }
+ if (strcmp(objclass, "link") == 0) {
+ return QPOL_CLASS_LNK_FILE;
+ }
+ if (strcmp(objclass, "sock") == 0) {
+ return QPOL_CLASS_SOCK_FILE;
+ }
+ if (strcmp(objclass, "any") == 0) {
+ return QPOL_CLASS_ALL;
+ }
+ return 0;
+}
+
+const char *apol_protocol_to_str(uint8_t protocol)
+{
+ switch (protocol) {
+ case IPPROTO_TCP:
+ return "tcp";
+ case IPPROTO_UDP:
+ return "udp";
+ default:
+ errno = EPROTONOSUPPORT;
+ return NULL;
+ }
+}
+
+uint8_t apol_str_to_protocol(const char *protocol_str)
+{
+ if (protocol_str == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (strcmp(protocol_str, "tcp") == 0 || strcmp(protocol_str, "TCP") == 0) {
+ return IPPROTO_TCP;
+ }
+ if (strcmp(protocol_str, "udp") == 0 || strcmp(protocol_str, "UDP") == 0) {
+ return IPPROTO_UDP;
+ }
+ errno = EPROTONOSUPPORT;
+ return 0;
+}
+
+const char *apol_fs_use_behavior_to_str(uint32_t behavior)
+{
+ switch (behavior) {
+ case QPOL_FS_USE_XATTR:
+ return "fs_use_xattr";
+ case QPOL_FS_USE_TASK:
+ return "fs_use_task";
+ case QPOL_FS_USE_TRANS:
+ return "fs_use_trans";
+ case QPOL_FS_USE_GENFS:
+ return "fs_use_genfs";
+ case QPOL_FS_USE_NONE:
+ return "fs_use_none";
+ case QPOL_FS_USE_PSID:
+ return "fs_use_psid";
+ }
+ return NULL;
+}
+
+int apol_str_to_fs_use_behavior(const char *behavior)
+{
+ if (strcmp(behavior, "fs_use_xattr") == 0) {
+ return QPOL_FS_USE_XATTR;
+ } else if (strcmp(behavior, "fs_use_task") == 0) {
+ return QPOL_FS_USE_TASK;
+ } else if (strcmp(behavior, "fs_use_trans") == 0) {
+ return QPOL_FS_USE_TRANS;
+ } else if (strcmp(behavior, "fs_use_genfs") == 0) {
+ return QPOL_FS_USE_GENFS;
+ } else if (strcmp(behavior, "fs_use_none") == 0) {
+ return QPOL_FS_USE_NONE;
+ } else if (strcmp(behavior, "fs_use_psid") == 0) {
+ return QPOL_FS_USE_PSID;
+ }
+ return -1;
+}
+
+const char *apol_rule_type_to_str(uint32_t rule_type)
+{
+ switch (rule_type) {
+ case QPOL_RULE_ALLOW:
+ return "allow";
+ case QPOL_RULE_NEVERALLOW:
+ return "neverallow";
+ case QPOL_RULE_AUDITALLOW:
+ return "auditallow";
+ case QPOL_RULE_DONTAUDIT:
+ return "dontaudit";
+ case QPOL_RULE_TYPE_TRANS:
+ return "type_transition";
+ case QPOL_RULE_TYPE_CHANGE:
+ return "type_change";
+ case QPOL_RULE_TYPE_MEMBER:
+ return "type_member";
+ }
+ return NULL;
+}
+
+const char *apol_cond_expr_type_to_str(uint32_t expr_type)
+{
+ switch (expr_type) {
+ case QPOL_COND_EXPR_BOOL:
+ return "";
+ case QPOL_COND_EXPR_NOT:
+ return "!";
+ case QPOL_COND_EXPR_OR:
+ return "||";
+ case QPOL_COND_EXPR_AND:
+ return "&&";
+ case QPOL_COND_EXPR_XOR:
+ return "^";
+ case QPOL_COND_EXPR_EQ:
+ return "==";
+ case QPOL_COND_EXPR_NEQ:
+ return "!=";
+ }
+ return NULL;
+}
+
+char *apol_file_find(const char *file_name)
+{
+ char *file = NULL, *var = NULL, *dirs[3];
+ size_t i;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* check current directory, environment variable, and then
+ * installed directory */
+ dirs[0] = ".";
+ dirs[1] = getenv(APOL_ENVIRON_VAR_NAME);
+ dirs[2] = APOL_INSTALL_DIR;
+ for (i = 0; i < 3; i++) {
+ if ((var = dirs[i]) != NULL) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ free(file);
+ if (rt == 0) {
+ return strdup(var);
+ }
+ }
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+char *apol_file_find_path(const char *file_name)
+{
+ char *file = NULL, *var = NULL, *dirs[3];
+ size_t i;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* check current directory, environment variable, and then
+ * installed directory */
+ dirs[0] = ".";
+ dirs[1] = getenv(APOL_ENVIRON_VAR_NAME);
+ dirs[2] = APOL_INSTALL_DIR;
+ for (i = 0; i < 3; i++) {
+ if ((var = dirs[i]) != NULL) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ if (rt == 0) {
+ return file;
+ }
+ free(file);
+ }
+ }
+
+ /* didn't find it */
+ return NULL;
+}
+
+char *apol_file_find_user_config(const char *file_name)
+{
+ char *file, *var;
+ int rt;
+
+ if (file_name == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ var = getenv("HOME");
+ if (var) {
+ if (asprintf(&file, "%s/%s", var, file_name) < 0) {
+ return NULL;
+ }
+ rt = access(file, R_OK);
+ if (rt == 0) {
+ return file;
+ } else {
+ free(file);
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+int apol_file_read_to_buffer(const char *fname, char **buf, size_t * len)
+{
+ FILE *file = NULL;
+ const size_t BUF_SIZE = 1024;
+ size_t size = 0, r;
+ char *bufp, *b;
+
+ assert(*buf == NULL);
+ assert(len);
+ *len = 0;
+ while (1) {
+ size += BUF_SIZE;
+ r = 0;
+ b = (char *)realloc(*buf, size * sizeof(char));
+ if (b == NULL) {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ if (file)
+ fclose(file);
+ return -1;
+ }
+ *buf = b;
+ if (!file) {
+ file = fopen(fname, "rb");
+ if (!file) {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ return -1;
+ }
+ }
+ bufp = &((*buf)[size - BUF_SIZE]);
+ r = fread(bufp, sizeof(char), BUF_SIZE, file);
+ *len += r;
+ if (r < BUF_SIZE) {
+ if (feof(file)) {
+ fclose(file);
+ break;
+ } else {
+ free(*buf);
+ *buf = NULL;
+ *len = 0;
+ fclose(file);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+char *apol_config_get_var(const char *var, FILE * fp)
+{
+ char line[APOL_LINE_SZ], t1[APOL_LINE_SZ], t2[APOL_LINE_SZ];
+ char *line_ptr = NULL;
+
+ if (var == NULL || fp == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ rewind(fp);
+ while (fgets(line, APOL_LINE_SZ, fp) != NULL) {
+ if ((line_ptr = strdup(line)) == NULL) {
+ return NULL;
+ }
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] == '#' || sscanf(line_ptr, "%s %[^\n]", t1, t2) != 2 || strcasecmp(var, t1) != 0) {
+ free(line_ptr);
+ continue;
+ } else {
+ free(line_ptr);
+ return strdup(t2);
+ }
+ }
+ return NULL;
+}
+
+apol_vector_t *apol_str_split(const char *s, const char *delim)
+{
+ char *orig_s = NULL, *dup_s = NULL, *v, *token;
+ apol_vector_t *list = NULL;
+ int error = 0;
+
+ if (s == NULL || delim == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((list = apol_vector_create(free)) == NULL || (orig_s = strdup(s)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ v = orig_s;
+ while ((token = strsep(&v, delim)) != NULL) {
+ if (strcmp(token, "") != 0 && !apol_str_is_only_white_space(token)) {
+ if ((dup_s = strdup(token)) == NULL || apol_vector_append(list, dup_s) < 0) {
+ error = errno;
+ free(dup_s);
+ goto cleanup;
+ }
+ }
+ }
+ cleanup:
+ free(orig_s);
+ if (error != 0) {
+ apol_vector_destroy(&list);
+ errno = error;
+ return NULL;
+ }
+ return list;
+}
+
+char *apol_str_join(const apol_vector_t * list, const char *delim)
+{
+ char *val, *s;
+ size_t i, len;
+
+ if (list == NULL || delim == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (apol_vector_get_size(list) == 0) {
+ return strdup("");
+ }
+ s = apol_vector_get_element(list, 0);
+ if ((val = strdup(s)) == NULL) {
+ return NULL;
+ }
+ len = strlen(val) + 1;
+ for (i = 1; i < apol_vector_get_size(list); i++) {
+ s = apol_vector_get_element(list, i);
+ if (apol_str_appendf(&val, &len, "%s%s", delim, s) < 0) {
+ return NULL;
+ }
+ }
+ return val;
+}
+
+/**
+ * Given a string, if the string begins with whitespace then allocate
+ * a new string that does not contain those whitespaces.
+ *
+ * @param str String to modify.
+ */
+static void trim_leading_whitespace(char *str)
+{
+ size_t i, len;
+ for (i = 0; str[i] != '\0' && isspace(str[i]); i++) ;
+ len = strlen(str + i);
+ memmove(str, str + i, len + 1);
+}
+
+/**
+ * Given a mutable string, replace trailing whitespace characters with
+ * null characters.
+ *
+ * @param str String to modify.
+ */
+static void trim_trailing_whitespace(char *str)
+{
+ size_t length;
+ length = strlen(str);
+ while (length > 0 && isspace(str[length - 1])) {
+ str[length - 1] = '\0';
+ length--;
+ }
+}
+
+void apol_str_trim(char *str)
+{
+ if (str == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ trim_leading_whitespace(str);
+ trim_trailing_whitespace(str);
+}
+
+int apol_str_append(char **tgt, size_t * tgt_sz, const char *str)
+{
+ size_t str_len;
+ if (str == NULL || (str_len = strlen(str)) == 0)
+ return 0;
+ if (tgt == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ str_len++;
+ /* target is currently empty */
+ if (*tgt == NULL || *tgt_sz == 0) {
+ *tgt = (char *)malloc(str_len);
+ if (*tgt == NULL) {
+ *tgt_sz = 0;
+ return -1;
+ }
+ *tgt_sz = str_len;
+ strcpy(*tgt, str);
+ return 0;
+ } else {
+ /* tgt has some memory */
+ char *t = (char *)realloc(*tgt, *tgt_sz + str_len);
+ if (t == NULL) {
+ int error = errno;
+ free(*tgt);
+ *tgt = NULL;
+ *tgt_sz = 0;
+ errno = error;
+ return -1;
+ }
+ *tgt = t;
+ *tgt_sz += str_len;
+ strcat(*tgt, str);
+ return 0;
+ }
+}
+
+int apol_str_appendf(char **tgt, size_t * tgt_sz, const char *fmt, ...)
+{
+ va_list ap;
+ int error;
+ if (fmt == NULL || strlen(fmt) == 0)
+ return 0;
+ if (tgt == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ va_start(ap, fmt);
+ /* target is currently empty */
+ if (*tgt == NULL || *tgt_sz == 0) {
+ if (vasprintf(tgt, fmt, ap) < 0) {
+ error = errno;
+ *tgt = NULL;
+ *tgt_sz = 0;
+ va_end(ap);
+ errno = error;
+ return -1;
+ }
+ *tgt_sz = strlen(*tgt) + 1;
+ va_end(ap);
+ return 0;
+ } else {
+ /* tgt has some memory */
+ char *t, *u;
+ size_t str_len;
+ if (vasprintf(&t, fmt, ap) < 0) {
+ error = errno;
+ free(*tgt);
+ *tgt_sz = 0;
+ va_end(ap);
+ errno = error;
+ return -1;
+ }
+ va_end(ap);
+ str_len = strlen(t);
+ if ((u = (char *)realloc(*tgt, *tgt_sz + str_len)) == NULL) {
+ error = errno;
+ free(t);
+ free(*tgt);
+ *tgt_sz = 0;
+ errno = error;
+ return -1;
+ }
+ *tgt = u;
+ *tgt_sz += str_len;
+ strcat(*tgt, t);
+ free(t);
+ return 0;
+ }
+}
+
+int apol_str_is_only_white_space(const char *str)
+{
+ size_t len, i;
+ if (str == NULL)
+ return 0;
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (!isspace(str[i]))
+ return 0;
+ }
+ return 1;
+}
+
+int apol_str_strcmp(const void *a, const void *b, void *unused __attribute__ ((unused)))
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+void *apol_str_strdup(const void *elem, void *unused __attribute__ ((unused)))
+{
+ return strdup((const char *)elem);
+}
diff --git a/libapol/src/vector-internal.h b/libapol/src/vector-internal.h
new file mode 100644
index 0000000..88c4378
--- /dev/null
+++ b/libapol/src/vector-internal.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * Protected routines for the vector class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef APOL_VECTOR_INTERNAL_H
+#define APOL_VECTOR_INTERNAL_H
+
+/**
+ * Change the free function of a vector. Currently, this function is
+ * friends with the BST class; otherwise consider this to be a private
+ * function.
+ */
+void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr);
+
+#endif
diff --git a/libapol/src/vector.c b/libapol/src/vector.c
new file mode 100644
index 0000000..28fc897
--- /dev/null
+++ b/libapol/src/vector.c
@@ -0,0 +1,457 @@
+/**
+ * @file
+ * Contains the implementation of a generic vector.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <apol/vector.h>
+#include "vector-internal.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/** The default initial capacity of a vector; must be a positive integer */
+#define APOL_VECTOR_DFLT_INIT_CAP 10
+
+/**
+ * Generic vector structure. Stores elements as void*.
+ */
+struct apol_vector
+{
+ /** The array of element pointers, which will be resized as needed. */
+ void **array;
+ /** The number of elements currently stored in array. */
+ size_t size;
+ /** The actual amount of space in array. This amount will always
+ * be >= size and will grow exponentially as needed. */
+ size_t capacity;
+ apol_vector_free_func *fr;
+};
+
+apol_vector_t *apol_vector_create(apol_vector_free_func * fr)
+{
+ return apol_vector_create_with_capacity(APOL_VECTOR_DFLT_INIT_CAP, fr);
+}
+
+apol_vector_t *apol_vector_create_with_capacity(size_t cap, apol_vector_free_func * fr)
+{
+ apol_vector_t *v = NULL;
+ int error;
+
+ if (cap < 1) {
+ cap = 1;
+ }
+ v = calloc(1, sizeof(apol_vector_t));
+ if (!v)
+ return NULL;
+ v->array = calloc((v->capacity = cap), sizeof(void *));
+ if (!(v->array)) {
+ error = errno;
+ free(v);
+ errno = error;
+ return NULL;
+ }
+ v->fr = fr;
+ return v;
+}
+
+apol_vector_t *apol_vector_create_from_iter(qpol_iterator_t * iter, apol_vector_free_func * fr)
+{
+ size_t iter_size;
+ apol_vector_t *v;
+ void *item;
+ int error;
+ if (qpol_iterator_get_size(iter, &iter_size) < 0 || (v = apol_vector_create_with_capacity(iter_size, fr)) == NULL) {
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, &item)) {
+ error = errno;
+ free(v);
+ errno = error;
+ return NULL;
+ }
+ apol_vector_append(v, item);
+ }
+ return v;
+}
+
+apol_vector_t *apol_vector_create_from_vector(const apol_vector_t * v, apol_vector_dup_func * dup, void *data,
+ apol_vector_free_func * fr)
+{
+ apol_vector_t *new_v;
+ size_t i;
+ if (v == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((new_v = apol_vector_create_with_capacity(v->capacity, fr)) == NULL) {
+ return NULL;
+ }
+ if (dup == NULL) {
+ memcpy(new_v->array, v->array, v->size * sizeof(void *));
+ } else {
+ for (i = 0; i < v->size; i++) {
+ new_v->array[i] = dup(v->array[i], data);
+ }
+ }
+ new_v->size = v->size;
+ return new_v;
+}
+
+apol_vector_t *apol_vector_create_from_intersection(const apol_vector_t * v1,
+ const apol_vector_t * v2, apol_vector_comp_func * cmp, void *data)
+{
+ apol_vector_t *new_v;
+ size_t i, j;
+ if (v1 == NULL || v2 == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((new_v = apol_vector_create(NULL)) == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < v1->size; i++) {
+ for (j = 0; j < v2->size; j++) {
+ if ((cmp != NULL && cmp(v1->array[i], v2->array[j], data) == 0) ||
+ (cmp == NULL && v1->array[i] == v2->array[j])) {
+ if (apol_vector_append(new_v, v1->array[i]) < 0) {
+ apol_vector_destroy(&new_v);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+ return new_v;
+}
+
+void apol_vector_destroy(apol_vector_t ** v)
+{
+ size_t i = 0;
+
+ if (!v || !(*v))
+ return;
+
+ if ((*v)->fr) {
+ for (i = 0; i < (*v)->size; i++) {
+ (*v)->fr((*v)->array[i]);
+ }
+ }
+ free((*v)->array);
+ (*v)->array = NULL;
+ free(*v);
+ *v = NULL;
+}
+
+size_t apol_vector_get_size(const apol_vector_t * v)
+{
+ if (!v) {
+ errno = EINVAL;
+ return 0;
+ } else {
+ return v->size;
+ }
+}
+
+size_t apol_vector_get_capacity(const apol_vector_t * v)
+{
+ if (!v) {
+ errno = EINVAL;
+ return 0;
+ } else {
+ return v->capacity;
+ }
+}
+
+void *apol_vector_get_element(const apol_vector_t * v, size_t idx)
+{
+ if (!v || !(v->array)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (idx >= v->size) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ return v->array[idx];
+}
+
+/**
+ * Grows a vector, by reallocating additional space for it.
+ *
+ * @param v Vector to which increase its size.
+ *
+ * @return 0 on success, -1 on error.
+ */
+static int apol_vector_grow(apol_vector_t * v)
+{
+ void **tmp;
+ size_t new_capacity = v->capacity;
+ if (new_capacity >= 128) {
+ new_capacity += 128;
+ } else {
+ new_capacity *= 2;
+ }
+ tmp = realloc(v->array, new_capacity * sizeof(void *));
+ if (!tmp) {
+ return -1;
+ }
+ v->capacity = new_capacity;
+ v->array = tmp;
+ return 0;
+}
+
+int apol_vector_get_index(const apol_vector_t * v, const void *elem, apol_vector_comp_func * cmp, void *data, size_t * i)
+{
+ if (!v || !i) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (*i = 0; *i < v->size; (*i)++) {
+ if ((cmp != NULL && cmp(v->array[*i], elem, data) == 0) || (cmp == NULL && elem == v->array[*i])) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int apol_vector_append(apol_vector_t * v, void *elem)
+{
+ if (!v) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (v->size >= v->capacity && apol_vector_grow(v)) {
+ return -1;
+ }
+
+ v->array[v->size] = elem;
+ v->size++;
+
+ return 0;
+}
+
+int apol_vector_append_unique(apol_vector_t * v, void *elem, apol_vector_comp_func * cmp, void *data)
+{
+ size_t i;
+ if (apol_vector_get_index(v, elem, cmp, data, &i) < 0) {
+ return apol_vector_append(v, elem);
+ }
+ errno = EEXIST;
+ return 1;
+}
+
+int apol_vector_compare(const apol_vector_t * a, const apol_vector_t * b, apol_vector_comp_func * cmp, void *data, size_t * i)
+{
+ int compval;
+ if (a == NULL || b == NULL || i == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ size_t a_len = apol_vector_get_size(a);
+ size_t b_len = apol_vector_get_size(b);
+ for (*i = 0; *i < a_len && *i < b_len; (*i)++) {
+ if (cmp != NULL) {
+ compval = cmp(a->array[*i], b->array[*i], data);
+ } else {
+ compval = (int)((char *)a->array[*i] - (char *)b->array[*i]);
+ }
+ if (compval != 0) {
+ return compval;
+ }
+ }
+ if (a_len == b_len) {
+ return 0;
+ } else if (a_len < b_len) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+static size_t vector_qsort_partition(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg)
+{
+ void *pivot = data[last];
+ size_t i = first, j = last;
+ while (i < j) {
+ if (cmp(data[i], pivot, arg) <= 0) {
+ i++;
+ } else {
+ data[j] = data[i];
+ data[i] = data[j - 1];
+ j--;
+ }
+ }
+ data[j] = pivot;
+ return j;
+}
+
+static void vector_qsort(void **data, size_t first, size_t last, apol_vector_comp_func * cmp, void *arg)
+{
+ if (first < last) {
+ size_t i = vector_qsort_partition(data, first, last, cmp, arg);
+ /* need this explicit check here, because i is an
+ * unsigned integer, and subtracting 1 from 0 is
+ * bad */
+ if (i > 0) {
+ vector_qsort(data, first, i - 1, cmp, arg);
+ }
+ vector_qsort(data, i + 1, last, cmp, arg);
+ }
+}
+
+/**
+ * Generic comparison function, which treats elements of the vector as
+ * unsigned integers.
+ */
+static int vector_int_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ char *i = (char *)a;
+ char *j = (char *)b;
+ if (i < j) {
+ return -1;
+ } else if (i > j) {
+ return 1;
+ }
+ return 0;
+}
+
+/* implemented as an in-place quicksort */
+void apol_vector_sort(apol_vector_t * v, apol_vector_comp_func * cmp, void *data)
+{
+ if (!v) {
+ errno = EINVAL;
+ return;
+ }
+ if (cmp == NULL) {
+ cmp = vector_int_comp;
+ }
+ if (v->size > 1) {
+ vector_qsort(v->array, 0, v->size - 1, cmp, data);
+ }
+}
+
+void apol_vector_sort_uniquify(apol_vector_t * v, apol_vector_comp_func * cmp, void *data)
+{
+ if (!v) {
+ errno = EINVAL;
+ return;
+ }
+ if (cmp == NULL) {
+ cmp = vector_int_comp;
+ }
+ if (v->size > 1) {
+ size_t i, j = 0;
+ void **new_array;
+ /* sweep through the array, do a quick compaction,
+ * then sort */
+ for (i = 1; i < v->size; i++) {
+ if (cmp(v->array[i], v->array[j], data) != 0) {
+ /* found a unique element */
+ j++;
+ v->array[j] = v->array[i];
+ } else {
+ /* found a non-unique element */
+ if (v->fr != NULL) {
+ v->fr(v->array[i]);
+ }
+ }
+ }
+ v->size = j + 1;
+
+ apol_vector_sort(v, cmp, data);
+ j = 0;
+ for (i = 1; i < v->size; i++) {
+ if (cmp(v->array[i], v->array[j], data) != 0) {
+ /* found a unique element */
+ j++;
+ v->array[j] = v->array[i];
+ } else {
+ /* found a non-unique element */
+ if (v->fr != NULL) {
+ v->fr(v->array[i]);
+ }
+ }
+ }
+ /* try to realloc vector to save space */
+ v->size = j + 1;
+ if ((new_array = realloc(v->array, v->size * sizeof(void *))) != NULL) {
+ v->array = new_array;
+ v->capacity = v->size;
+ }
+ }
+}
+
+int apol_vector_cat(apol_vector_t * dest, const apol_vector_t * src)
+{
+ size_t i, orig_size, cap;
+ void **a;
+ if (!src || !apol_vector_get_size(src)) {
+ return 0; /* nothing to append */
+ }
+
+ if (!dest) {
+ errno = EINVAL;
+ return -1;
+ }
+ orig_size = apol_vector_get_size(dest);
+ for (i = 0; i < apol_vector_get_size(src); i++)
+ if (apol_vector_append(dest, apol_vector_get_element(src, i))) {
+ /* revert if possible */
+ if (orig_size == 0) {
+ cap = 1;
+ } else {
+ cap = orig_size;
+ }
+ a = realloc(dest->array, cap * sizeof(*a));
+ if (a != NULL) {
+ dest->array = a;
+ }
+ dest->size = orig_size;
+ dest->capacity = cap;
+ return -1;
+ }
+
+ return 0;
+}
+
+int apol_vector_remove(apol_vector_t * v, const size_t idx)
+{
+ if (v == NULL || idx >= v->size) {
+ errno = EINVAL;
+ return -1;
+ }
+ memmove(v->array + idx, v->array + idx + 1, sizeof(v->array[0]) * (v->size - idx - 1));
+ v->size--;
+ return 0;
+}
+
+/******************** friend function below ********************/
+
+void vector_set_free_func(apol_vector_t * v, apol_vector_free_func * fr)
+{
+ v->fr = fr;
+}
diff --git a/libapol/swig/Makefile.am b/libapol/swig/Makefile.am
new file mode 100644
index 0000000..f7c3c06
--- /dev/null
+++ b/libapol/swig/Makefile.am
@@ -0,0 +1,15 @@
+if DO_SWIGIFY_PYTHON
+ MAYBE_PYSWIG = python
+endif
+
+if DO_SWIGIFY_JAVA
+ MAYBE_JSWIG = java
+endif
+
+if DO_SWIGIFY_TCL
+ MAYBE_TCLSWIG = tcl
+endif
+
+SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG)
+
+dist_noinst_DATA = apol.i
diff --git a/libapol/swig/apol.i b/libapol/swig/apol.i
new file mode 100644
index 0000000..ae1262d
--- /dev/null
+++ b/libapol/swig/apol.i
@@ -0,0 +1,3220 @@
+/**
+ * @file
+ * SWIG declarations for libapol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%module apol
+
+%{
+#include <apol/avrule-query.h>
+#include <apol/bool-query.h>
+#include <apol/bst.h>
+#include <apol/class-perm-query.h>
+#include <apol/condrule-query.h>
+#include <apol/constraint-query.h>
+#include <apol/context-query.h>
+#include <apol/domain-trans-analysis.h>
+#include <apol/fscon-query.h>
+#include <apol/infoflow-analysis.h>
+#include <apol/isid-query.h>
+#include <apol/mls-query.h>
+#include <apol/netcon-query.h>
+#include <apol/perm-map.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <apol/policy-query.h>
+#include <apol/range_trans-query.h>
+#include <apol/rbacrule-query.h>
+#include <apol/relabel-analysis.h>
+#include <apol/render.h>
+#include <apol/role-query.h>
+#include <apol/terule-query.h>
+#include <apol/type-query.h>
+#include <apol/types-relation-analysis.h>
+#include <apol/user-query.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+#include <errno.h>
+
+/* Provide hooks so that language-specific modules can define the
+ * callback function, used by the handler in
+ * apol_policy_create_from_policy_path().
+ */
+SWIGEXPORT apol_callback_fn_t apol_swig_message_callback = NULL;
+SWIGEXPORT void * apol_swig_message_callback_arg = NULL;
+
+%}
+
+#ifdef SWIGJAVA
+%javaconst(1);
+
+/* get the java environment so we can throw exceptions */
+%{
+ static JNIEnv *apol_global_jenv;
+ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ (*vm)->AttachCurrentThread(vm, (void **)&apol_global_jenv, NULL);
+ return JNI_VERSION_1_2;
+ }
+%}
+#endif
+
+%include exception.i
+%include stdint.i
+%import qpol.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+#ifdef SWIGJAVA
+
+%exception {
+ apol_global_jenv = jenv;
+ $action
+}
+
+%{
+#define BEGIN_EXCEPTION JNIEnv *local_jenv = apol_global_jenv; {
+#define END_EXCEPTION }
+%}
+/* handle size_t correctly in java as architecture independent */
+%typemap(jni) size_t "jlong"
+%typemap(jtype) size_t "long"
+%typemap(jstype) size_t "long"
+%typemap("javaimports") SWIGTYPE %{import com.tresys.setools.qpol.*;%}
+%typemap(javabody) SWIGTYPE %{
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ public $javaclassname(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ public static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+%}
+/* the following handles the dependencies on qpol */
+%pragma(java) jniclassimports=%{import com.tresys.setools.qpol.*;%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libapol_get_version();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("japol");
+ }
+ }
+%}
+%pragma(java) moduleimports=%{import com.tresys.setools.qpol.*;%}
+#else
+/* not in java so handle size_t as architecture dependent */
+#ifdef SWIGWORDSIZE64
+typedef uint64_t size_t;
+#else
+typedef uint32_t size_t;
+#endif
+%{
+#define BEGIN_EXCEPTION
+#define END_EXCEPTION
+%}
+#endif
+
+#ifdef SWIGJAVA
+
+/* if java, pass the new exception macro to C not just SWIG */
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%inline %{
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%}
+#endif
+
+#ifdef SWIGTCL
+/* implement a custom non thread-safe error handler */
+%{
+static char *message = NULL;
+static void tcl_clear_error(void)
+{
+ free(message);
+ message = NULL;
+}
+static void tcl_throw_error(const char *s)
+{
+ free(message);
+ message = strdup(s);
+}
+static char *tcl_get_error(void)
+{
+ return message;
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+%}
+
+%wrapper %{
+/* Tcl module's initialization routine is expected to be named
+ * Apol_Init(), but the output file will be called libtapol.so instead
+ * of libapol.so. Therefore add an alias from Tapol_Init() to the
+ * real Apol_Init().
+ */
+SWIGEXPORT int Tapol_Init(Tcl_Interp *interp) {
+ return SWIG_init(interp);
+}
+%}
+
+%exception {
+ char *err;
+ tcl_clear_error();
+ $action
+ if ((err = tcl_get_error()) != NULL) {
+ Tcl_Obj *obj = Tcl_NewStringObj(message, -1);
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, obj);
+ goto fail;
+ }
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+#endif
+
+
+/* defines from policy-query.h */
+/* Many libapol queries act upon MLS contexts. Use these defines to
+ * specify set operations upon contexts.
+ */
+#define APOL_QUERY_SUB 0x02 /* query is subset of rule range */
+#define APOL_QUERY_SUPER 0x04 /* query is superset of rule range */
+#define APOL_QUERY_EXACT (APOL_QUERY_SUB|APOL_QUERY_SUPER)
+#define APOL_QUERY_INTERSECT 0x08 /* query overlaps any part of rule range */
+#define APOL_QUERY_FLAGS \
+ (APOL_QUERY_SUB | APOL_QUERY_SUPER | APOL_QUERY_EXACT | \
+ APOL_QUERY_INTERSECT)
+/* The AV rule search and TE rule search use these flags when
+ * specifying what kind of symbol is being searched. Strings are
+ * normally interpreted either as a type or as an attribute; the behavior
+ * can be changed to use only types or only attributes.
+ */
+#define APOL_QUERY_SYMBOL_IS_TYPE 0x01
+#define APOL_QUERY_SYMBOL_IS_ATTRIBUTE 0x02
+
+/* from util.h */
+const char *libapol_get_version(void);
+/* defines from netinet/in.h for ip protocols */
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+const char *apol_protocol_to_str(uint8_t protocol);
+uint8_t apol_str_to_protocol(const char *protocol_str);
+%newobject wrap_apol_str_to_internal_ip(char*);
+%rename(apol_str_to_internal_ip) wrap_apol_str_to_internal_ip;
+%inline %{
+ typedef struct apol_ip {
+ uint32_t ip[4];
+ int proto;
+ } apol_ip_t;
+ apol_ip_t *wrap_apol_str_to_internal_ip(char *str) {
+ apol_ip_t *ip = NULL;
+ BEGIN_EXCEPTION
+ ip = calloc(1, sizeof(*ip));
+ int retv = 0;
+ if (!ip) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ retv = apol_str_to_internal_ip(str, ip->ip);
+ if (retv < 0) {
+ free(ip);
+ SWIG_exception(SWIG_RuntimeError, "Could not convert string to IP");
+ }
+ ip->proto = retv;
+ END_EXCEPTION
+ fail:
+ return ip;
+ }
+%}
+%extend apol_ip_t {
+ apol_ip_t(const char *str) {
+ apol_ip_t *ip = NULL;
+ BEGIN_EXCEPTION
+ ip = calloc(1, sizeof(*ip));
+ int retv = 0;
+ if (!ip) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ retv = apol_str_to_internal_ip(str, ip->ip);
+ if (retv < 0) {
+ free(ip);
+ SWIG_exception(SWIG_RuntimeError, "Could not convert string to IP");
+ }
+ ip->proto = retv;
+ END_EXCEPTION
+ fail:
+ return ip;
+ };
+ ~apol_ip_t() {
+ free(self);
+ };
+ int get_protocol() {
+ return self->proto;
+ };
+}
+
+const char *apol_objclass_to_str(uint32_t objclass);
+uint32_t apol_str_to_objclass(const char *objclass);
+const char *apol_fs_use_behavior_to_str(uint32_t behavior);
+int apol_str_to_fs_use_behavior(const char *behavior);
+const char *apol_rule_type_to_str(uint32_t rule_type);
+const char *apol_cond_expr_type_to_str(uint32_t expr_type);
+%newobject apol_file_find_path(const char *);
+char *apol_file_find_path(const char *file_name);
+
+/* directly include and wrap */
+%newobject apol_ipv4_addr_render(const apol_policy_t *p, uint32_t addr[4]);
+%newobject apol_ipv6_addr_render(const apol_policy_t *p, uint32_t addr[4]);
+%newobject apol_qpol_context_render(const apol_policy_t *p, const qpol_context_t *context);
+%include "apol/render.h"
+
+/* derived vector type here */
+%inline %{
+ typedef struct apol_string_vector apol_string_vector_t;
+%}
+typedef struct apol_vector {} apol_vector_t;
+%extend apol_vector_t {
+ apol_vector_t() {
+ return apol_vector_create(NULL);
+ };
+ apol_vector_t(qpol_iterator_t *iter) {
+ return apol_vector_create_from_iter(iter, NULL);
+ };
+ apol_vector_t(apol_vector_t *v) {
+ return apol_vector_create_from_vector(v, NULL, NULL, NULL);
+ };
+ apol_vector_t(apol_vector_t *a, apol_vector_t *b) {
+ return apol_vector_create_from_intersection(a, b, NULL, NULL);
+ };
+ size_t get_size() {
+ return apol_vector_get_size(self);
+ };
+ size_t get_capacity() {
+ return apol_vector_get_capacity(self);
+ };
+ void *get_element(size_t i) {
+ return apol_vector_get_element(self, i);
+ };
+ ~apol_vector_t() {
+ apol_vector_destroy(&self);
+ };
+ void append(void *x) {
+ BEGIN_EXCEPTION
+ if (apol_vector_append(self, x)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_unique(void *x) {
+ BEGIN_EXCEPTION
+ if (apol_vector_append_unique(self, x, NULL, NULL)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void cat(apol_vector_t *src) {
+ BEGIN_EXCEPTION
+ if (apol_vector_cat(self, src)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void remove(size_t idx) {
+ BEGIN_EXCEPTION
+ if (apol_vector_remove(self, idx)) {
+ SWIG_exception(SWIG_RuntimeError, "Error removing vector element");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void sort() {
+ apol_vector_sort(self, NULL, NULL);
+ };
+ void sort_uniquify() {
+ apol_vector_sort_uniquify(self, NULL, NULL);
+ };
+};
+%rename(apol_vector_compare) wrap_apol_vector_compare;
+%inline %{
+ int wrap_apol_vector_compare(apol_vector_t *a, apol_vector_t *b) {
+ size_t idx; /* tracks first difference - currently dropped */
+ return apol_vector_compare(a, b, NULL, NULL, &idx);
+ }
+%}
+typedef struct apol_string_vector {} apol_string_vector_t;
+%extend apol_string_vector_t {
+ apol_string_vector_t() {
+ return (apol_string_vector_t*)apol_vector_create(free);
+ };
+ apol_string_vector_t(apol_string_vector_t *v) {
+ return (apol_string_vector_t*)apol_vector_create_from_vector((apol_vector_t*)v, apol_str_strdup, NULL, free);
+ };
+ apol_string_vector_t(apol_string_vector_t *a, apol_string_vector_t *b) {
+ return (apol_string_vector_t*)apol_vector_create_from_intersection((apol_vector_t*)a, (apol_vector_t*)b, apol_str_strcmp, NULL);
+ };
+ size_t get_size() {
+ return apol_vector_get_size((apol_vector_t*)self);
+ };
+ size_t get_capacity() {
+ return apol_vector_get_capacity((apol_vector_t*)self);
+ };
+ char *get_element(size_t i) {
+ return (char*)apol_vector_get_element((apol_vector_t*)self, i);
+ };
+ ~apol_string_vector_t() {
+ apol_vector_destroy((apol_vector_t**)&self);
+ };
+ size_t get_index(char *str) {
+ size_t idx;
+ if (apol_vector_get_index((apol_vector_t*)self, str, apol_str_strcmp, NULL, &idx))
+ return apol_vector_get_size((apol_vector_t*)self) + 1;
+ return idx;
+ };
+ void append(char *str) {
+ BEGIN_EXCEPTION
+ char *tmp = strdup(str);
+ if (!tmp || apol_vector_append((apol_vector_t*)self, tmp)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_unique(char *str) {
+ BEGIN_EXCEPTION
+ char *tmp = strdup(str);
+ if (!tmp || apol_vector_append_unique((apol_vector_t*)self, tmp, apol_str_strcmp, NULL)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void cat(apol_string_vector_t *src) {
+ BEGIN_EXCEPTION
+ if (apol_vector_cat((apol_vector_t*)self, (apol_vector_t*)src)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void remove(size_t idx) {
+ BEGIN_EXCEPTION
+ char *x = apol_vector_get_element((apol_vector_t*)self, idx);
+ if (apol_vector_remove((apol_vector_t*)self, idx)) {
+ SWIG_exception(SWIG_RuntimeError, "Error removing vector element");
+ }
+ free(x);
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void sort() {
+ apol_vector_sort((apol_vector_t*)self, apol_str_strcmp, NULL);
+ };
+ void sort_uniquify() {
+ apol_vector_sort_uniquify((apol_vector_t*)self, apol_str_strcmp, NULL);
+ };
+};
+
+/* apol policy path */
+ typedef enum apol_policy_path_type
+{
+ APOL_POLICY_PATH_TYPE_MONOLITHIC = 0,
+ APOL_POLICY_PATH_TYPE_MODULAR
+} apol_policy_path_type_e;
+typedef struct apol_policy_path {} apol_policy_path_t;
+%extend apol_policy_path_t {
+ apol_policy_path_t(apol_policy_path_type_e type, char * primary, apol_string_vector_t *modules = NULL) {
+ apol_policy_path_t *p;
+ BEGIN_EXCEPTION
+ if ((p = apol_policy_path_create(type, primary, (apol_vector_t*)modules)) == NULL) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return p;
+ };
+ apol_policy_path_t(char *path) {
+ apol_policy_path_t *p;
+ BEGIN_EXCEPTION
+ if ((p = apol_policy_path_create_from_file(path)) == NULL) {
+ SWIG_exception(SWIG_RuntimeError, "Input/output error");
+ }
+ END_EXCEPTION
+ fail:
+ return p;
+ };
+ apol_policy_path_t(char *str, int unused) {
+ apol_policy_path_t *p;
+ BEGIN_EXCEPTION
+ if ((p = apol_policy_path_create_from_string(str)) == NULL) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return p;
+ };
+ apol_policy_path_t(apol_policy_path_t *in) {
+ apol_policy_path_t *p;
+ BEGIN_EXCEPTION
+ if ((p = apol_policy_path_create_from_policy_path(in)) == NULL) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return p;
+ };
+ ~apol_policy_path_t() {
+ apol_policy_path_destroy(&self);
+ };
+ apol_policy_path_type_e get_type() {
+ return apol_policy_path_get_type(self);
+ };
+ const char *get_primary() {
+ return apol_policy_path_get_primary(self);
+ };
+ const apol_string_vector_t *get_modules() {
+ return (apol_string_vector_t*)apol_policy_path_get_modules(self);
+ };
+ %newobject to_string();
+ char *to_string() {
+ char *str;
+ BEGIN_EXCEPTION
+ str = apol_policy_path_to_string(self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ void to_file(char *path) {
+ BEGIN_EXCEPTION
+ if (apol_policy_path_to_file(self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Input/outpet error");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+};
+int apol_policy_path_compare(const apol_policy_path_t * a, const apol_policy_path_t * b);
+int apol_file_is_policy_path_list(const char *filename);
+
+/* apol policy */
+typedef struct apol_policy {} apol_policy_t;
+#define APOL_PERMMAP_MAX_WEIGHT 10
+#define APOL_PERMMAP_MIN_WEIGHT 1
+#define APOL_PERMMAP_UNMAPPED 0x00
+#define APOL_PERMMAP_READ 0x01
+#define APOL_PERMMAP_WRITE 0x02
+#define APOL_PERMMAP_BOTH (APOL_PERMMAP_READ | APOL_PERMMAP_WRITE)
+#define APOL_PERMMAP_NONE 0x10
+%extend apol_policy_t {
+ apol_policy_t(apol_policy_path_t *path, int options = 0) {
+ apol_policy_t *p;
+ BEGIN_EXCEPTION
+ p = apol_policy_create_from_policy_path(path, options, apol_swig_message_callback, apol_swig_message_callback_arg);
+ if (!p) {
+ if (errno == ENOMEM) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ } else {
+ SWIG_exception(SWIG_IOError, "Failed to create policy");
+ }
+ }
+ END_EXCEPTION
+ fail:
+ return p;
+ };
+ ~apol_policy_t() {
+ apol_policy_destroy(&self);
+ };
+ int get_policy_type() {
+ return apol_policy_get_policy_type(self);
+ };
+ qpol_policy_t *get_qpol() {
+ return apol_policy_get_qpol(self);
+ };
+ int is_mls() {
+ return apol_policy_is_mls(self);
+ };
+ %newobject get_version_type_mls_str();
+ char *get_version_type_mls_str() {
+ char *str;
+ BEGIN_EXCEPTION
+ str = apol_policy_get_version_type_mls_str(self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ void open_permmap(const char *path) {
+ BEGIN_EXCEPTION
+ if (apol_policy_open_permmap(self, path) < 0) {
+ SWIG_exception(SWIG_RuntimeError, "Error loading permission map");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void save_permmap(const char *path) {
+ BEGIN_EXCEPTION
+ if (apol_policy_save_permmap(self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not save permission map");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ int get_permmap_weight(const char *class_name, const char *perm_name) {
+ int dir, weight;
+ BEGIN_EXCEPTION
+ if (apol_policy_get_permmap(self, class_name, perm_name, &dir, &weight)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get permission map weight");
+ }
+ END_EXCEPTION
+ fail:
+ return weight;
+ };
+ int get_permmap_direction(char *class_name, char *perm_name) {
+ int dir, weight;
+ BEGIN_EXCEPTION
+ if (apol_policy_get_permmap(self, class_name, perm_name, &dir, &weight)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get permission map direction");
+ }
+ END_EXCEPTION
+ fail:
+ return dir;
+ };
+ void set_permmap(const char *class_name, const char *perm_name, int direction, int weight) {
+ BEGIN_EXCEPTION
+ if (apol_policy_set_permmap(self, class_name, perm_name, direction, weight)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set permission mapping");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void build_domain_trans_table() {
+ BEGIN_EXCEPTION
+ if (apol_policy_build_domain_trans_table(self)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not build domain transition table");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void reset_domain_trans_table() {
+ apol_policy_reset_domain_trans_table(self);
+ }
+};
+
+/* apol type query */
+typedef struct apol_type_query {} apol_type_query_t;
+%extend apol_type_query_t {
+ apol_type_query_t() {
+ apol_type_query_t *tq;
+ BEGIN_EXCEPTION
+ tq = apol_type_query_create();
+ if (!tq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return tq;
+ };
+ ~apol_type_query_t() {
+ apol_type_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t *);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_type_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run type query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_type_query_set_type(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_type_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol attribute query */
+typedef struct apol_attr_query {} apol_attr_query_t;
+%extend apol_attr_query_t {
+ apol_attr_query_t() {
+ apol_attr_query_t *aq;
+ BEGIN_EXCEPTION
+ aq = apol_attr_query_create();
+ if (!aq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aq;
+ };
+ ~apol_attr_query_t() {
+ apol_attr_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t *);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_attr_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run attribute query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_attr(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_attr_query_set_attr(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_attr_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol role query */
+typedef struct apol_role_query {} apol_role_query_t;
+%extend apol_role_query_t {
+ apol_role_query_t() {
+ apol_role_query_t *rq;
+ BEGIN_EXCEPTION
+ rq = apol_role_query_create();
+ if (!rq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return rq;
+ };
+ ~apol_role_query_t() {
+ apol_role_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t *);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_role_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run role query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_role(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_role_query_set_role(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_role_query_set_type(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_role_query_set_regex(p, self, regex);
+ };
+};
+int apol_role_has_type(apol_policy_t * p, qpol_role_t * r, qpol_type_t * t);
+
+/* apol class query */
+typedef struct apol_class_query {} apol_class_query_t;
+%extend apol_class_query_t {
+ apol_class_query_t() {
+ apol_class_query_t *cq;
+ BEGIN_EXCEPTION
+ cq = apol_class_query_create();
+ if (!cq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return cq;
+ };
+ ~apol_class_query_t() {
+ apol_class_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_class_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run class query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_class_query_set_class(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_common(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_class_query_set_common(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_class_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol common query */
+typedef struct apol_common_query {} apol_common_query_t;
+%extend apol_common_query_t {
+ apol_common_query_t() {
+ apol_common_query_t *cq;
+ BEGIN_EXCEPTION
+ cq = apol_common_query_create();
+ if (!cq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return cq;
+ };
+ ~apol_common_query_t() {
+ apol_common_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_common_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run common query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_common(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_common_query_set_common(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_common_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol perm query */
+typedef struct apol_perm_query {} apol_perm_query_t;
+%extend apol_perm_query_t {
+ apol_perm_query_t() {
+ apol_perm_query_t *pq;
+ BEGIN_EXCEPTION
+ pq = apol_perm_query_create();
+ if (!pq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return pq;
+ };
+ ~apol_perm_query_t() {
+ apol_perm_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_string_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_perm_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run permission query");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ void set_perm(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_perm_query_set_perm(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_perm_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol bool query */
+typedef struct apol_bool_query {} apol_bool_query_t;
+%extend apol_bool_query_t {
+ apol_bool_query_t() {
+ apol_bool_query_t *bq;
+ BEGIN_EXCEPTION
+ bq = apol_bool_query_create();
+ if (!bq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return bq;
+ };
+ ~apol_bool_query_t() {
+ apol_bool_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_bool_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run boolean query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_bool(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_bool_query_set_bool(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_bool_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol mls level */
+typedef struct apol_mls_level {} apol_mls_level_t;
+%extend apol_mls_level_t {
+ apol_mls_level_t() {
+ apol_mls_level_t *aml;
+ BEGIN_EXCEPTION
+ aml = apol_mls_level_create();
+ if (!aml) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aml;
+ };
+ apol_mls_level_t(apol_mls_level_t *in) {
+ apol_mls_level_t *aml;
+ BEGIN_EXCEPTION
+ aml = apol_mls_level_create_from_mls_level(in);
+ if (!aml) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aml;
+ };
+ apol_mls_level_t(apol_policy_t *p, const char *str) {
+ apol_mls_level_t *aml;
+ BEGIN_EXCEPTION
+ aml = apol_mls_level_create_from_string(p, str);
+ if (!aml) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aml;
+ };
+ apol_mls_level_t(const char *str) {
+ apol_mls_level_t *aml;
+ BEGIN_EXCEPTION
+ aml = apol_mls_level_create_from_literal(str);
+ if (!aml) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aml;
+ };
+ apol_mls_level_t(apol_policy_t *p, qpol_mls_level_t *qml) {
+ apol_mls_level_t *aml;
+ BEGIN_EXCEPTION
+ aml = apol_mls_level_create_from_qpol_mls_level(p, qml);
+ if (!aml) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aml;
+ };
+ apol_mls_level_t(apol_policy_t *p, qpol_level_t *ql) {
+ apol_mls_level_t *aml;
+ BEGIN_EXCEPTION
+ aml = apol_mls_level_create_from_qpol_level_datum(p, ql);
+ if (!aml) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aml;
+ };
+ ~apol_mls_level_t() {
+ apol_mls_level_destroy(&self);
+ };
+ void set_sens(apol_policy_t *p, char *sens) {
+ BEGIN_EXCEPTION
+ if (apol_mls_level_set_sens(p, self, sens)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set level sensitivity");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_sens() {
+ return apol_mls_level_get_sens(self);
+ };
+ void append_cats(apol_policy_t *p, char *cats) {
+ BEGIN_EXCEPTION
+ if (apol_mls_level_append_cats(p, self, cats)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append level category");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_cats() {
+ return (apol_string_vector_t *) apol_mls_level_get_cats(self);
+ };
+ int validate(apol_policy_t *p) {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_mls_level_validate(p, self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not validate level");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+ %newobject render(apol_policy_t*);
+ char *render(apol_policy_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = apol_mls_level_render(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ int convert(apol_policy_t *p) {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_mls_level_convert(p, self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not convert level");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+ int is_literal() {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_mls_level_is_literal(self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if level is literal");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+};
+#define APOL_MLS_EQ 0
+#define APOL_MLS_DOM 1
+#define APOL_MLS_DOMBY 2
+#define APOL_MLS_INCOMP 3
+int apol_mls_level_compare(apol_policy_t * p, const apol_mls_level_t * level1, const apol_mls_level_t * level2);
+int apol_mls_sens_compare(apol_policy_t * p, const char *sens1, const char *sens2);
+int apol_mls_cats_compare(apol_policy_t * p, const char *cat1, const char *cat2);
+%inline %{
+ apol_mls_level_t *apol_mls_level_from_void(void *x) {
+ return (apol_mls_level_t*)x;
+ };
+%}
+
+/* apol mls range */
+#ifdef SWIGPYTHON
+%typemap(in) apol_mls_level_t *lvl {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_mls_level, 0 | 0 );
+ $1 = (apol_mls_level_t*)x;
+}
+#endif
+typedef struct apol_mls_range {} apol_mls_range_t;
+%extend apol_mls_range_t {
+ apol_mls_range_t() {
+ apol_mls_range_t *amr;
+ BEGIN_EXCEPTION
+ amr = apol_mls_range_create();
+ if (!amr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return amr;
+ };
+ apol_mls_range_t(apol_mls_range_t *in) {
+ apol_mls_range_t *amr;
+ BEGIN_EXCEPTION
+ amr = apol_mls_range_create_from_mls_range(in);
+ if (!amr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return amr;
+ };
+ apol_mls_range_t(apol_policy_t *p, const char *s) {
+ apol_mls_range_t *amr;
+ BEGIN_EXCEPTION
+ amr = apol_mls_range_create_from_string(p, s);
+ if (!amr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return amr;
+ };
+ apol_mls_range_t(const char *s) {
+ apol_mls_range_t *amr;
+ BEGIN_EXCEPTION
+ amr = apol_mls_range_create_from_literal(s);
+ if (!amr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return amr;
+ };
+ apol_mls_range_t(apol_policy_t *p, qpol_mls_range_t *in) {
+ apol_mls_range_t *amr;
+ BEGIN_EXCEPTION
+ amr = apol_mls_range_create_from_qpol_mls_range(p, in);
+ if (!amr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return amr;
+ };
+ ~apol_mls_range_t() {
+ apol_mls_range_destroy(&self);
+ };
+ void set_low(apol_policy_t *p, apol_mls_level_t *lvl) {
+ BEGIN_EXCEPTION
+ if (apol_mls_range_set_low(p, self, lvl)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set low level");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_high(apol_policy_t *p, apol_mls_level_t *lvl) {
+ BEGIN_EXCEPTION
+ if (apol_mls_range_set_high(p, self, lvl)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set high level");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_mls_level_t *get_low() {
+ return apol_mls_range_get_low(self);
+ }
+ const apol_mls_level_t *get_high() {
+ return apol_mls_range_get_high(self);
+ }
+ %newobject render(apol_policy_t*);
+ char *render(apol_policy_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = apol_mls_range_render(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ %newobject get_levels(apol_policy_t*);
+ apol_vector_t *get_levels(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = apol_mls_range_get_levels(p, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ int validate(apol_policy_t *p) {
+ int ret = apol_mls_range_validate(p, self);
+ BEGIN_EXCEPTION
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not validate range");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+ int is_literal() {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_mls_range_is_literal(self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if range is literal");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+ int convert(apol_policy_t *p) {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_mls_range_convert(p, self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not convert range");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+};
+int apol_mls_range_compare(apol_policy_t * p, const apol_mls_range_t * target, const apol_mls_range_t * search, unsigned int range_compare_type);
+int apol_mls_range_contain_subrange(apol_policy_t * p, const apol_mls_range_t * range, const apol_mls_range_t * subrange);
+%inline %{
+ apol_mls_range_t *apol_mls_range_from_void(void *x) {
+ return (apol_mls_range_t*)x;
+ };
+%}
+
+/* apol level query */
+typedef struct apol_level_query {} apol_level_query_t;
+%extend apol_level_query_t {
+ apol_level_query_t() {
+ apol_level_query_t * alq;
+ BEGIN_EXCEPTION
+ alq = apol_level_query_create();
+ if (!alq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return alq;
+ };
+ ~apol_level_query_t() {
+ apol_level_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_level_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run level query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_sens(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_level_query_set_sens(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_cat(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_level_query_set_cat(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_level_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol cat query */
+typedef struct apol_cat_query {} apol_cat_query_t;
+%extend apol_cat_query_t {
+ apol_cat_query_t() {
+ apol_cat_query_t * acq;
+ BEGIN_EXCEPTION
+ acq = apol_cat_query_create();
+ if (!acq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return acq;
+ };
+ ~apol_cat_query_t() {
+ apol_cat_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t *);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_cat_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run category query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_cat(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_cat_query_set_cat(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_cat_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol user query */
+#ifdef SWIGPYTHON
+%typemap(in) apol_mls_range_t *rng {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_mls_range, 0 | 0 );
+ $1 = (apol_mls_range_t*)x;
+}
+#endif
+typedef struct apol_user_query {} apol_user_query_t;
+%extend apol_user_query_t {
+ apol_user_query_t() {
+ apol_user_query_t *auq;
+ BEGIN_EXCEPTION
+ auq = apol_user_query_create();
+ if (!auq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return auq;
+ };
+ ~apol_user_query_t() {
+ apol_user_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_user_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run user query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_user(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_user_query_set_user(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_role(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_user_query_set_role(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_default_level(apol_policy_t *p, apol_mls_level_t *lvl) {
+ BEGIN_EXCEPTION
+ if (apol_user_query_set_default_level(p, self, lvl)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_range(apol_policy_t *p, apol_mls_range_t *rng, int range_match) {
+ BEGIN_EXCEPTION
+ if (apol_user_query_set_range(p, self, rng, range_match)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_user_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol context */
+typedef struct apol_context {} apol_context_t;
+%extend apol_context_t {
+ apol_context_t() {
+ apol_context_t *ctx;
+ BEGIN_EXCEPTION
+ ctx = apol_context_create();
+ if (!ctx) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+ apol_context_t(apol_policy_t *p, qpol_context_t *in) {
+ apol_context_t *ctx;
+ BEGIN_EXCEPTION
+ ctx = apol_context_create_from_qpol_context(p, in);
+ if (!ctx) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+ apol_context_t(const char *str) {
+ apol_context_t *ctx;
+ BEGIN_EXCEPTION
+ ctx = apol_context_create_from_literal(str);
+ if (!ctx) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+ ~apol_context_t() {
+ apol_context_destroy(&self);
+ };
+ void set_user(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_context_set_user(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_user() {
+ return apol_context_get_user(self);
+ };
+ void set_role(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_context_set_role(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_role() {
+ return apol_context_get_role(self);
+ };
+ void set_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_context_set_type(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_type() {
+ return apol_context_get_type(self);
+ };
+ void set_range(apol_policy_t *p, apol_mls_range_t *rng) {
+ BEGIN_EXCEPTION
+ if (apol_context_set_range(p, self, rng)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_mls_range_t *get_range() {
+ return apol_context_get_range(self);
+ };
+ int validate(apol_policy_t *p) {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_context_validate(p, self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not validate context");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+ int validate_partial(apol_policy_t *p) {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_context_validate_partial(p, self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not validate context");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+ %newobject render(apol_policy_t*);
+ char *render(apol_policy_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = apol_context_render(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ int convert(apol_policy_t *p) {
+ int ret = -1;
+ BEGIN_EXCEPTION
+ ret = apol_context_convert(p, self);
+ if (ret < 0) {
+ SWIG_exception(SWIG_ValueError, "Could not convert context");
+ }
+ END_EXCEPTION
+ fail:
+ return ret;
+ }
+};
+int apol_context_compare(apol_policy_t * p, apol_context_t * target, apol_context_t * search, unsigned int range_compare_type);
+
+/* apol constraint query */
+typedef struct apol_constraint_query {} apol_constraint_query_t;
+%extend apol_constraint_query_t {
+ apol_constraint_query_t() {
+ apol_constraint_query_t *acq;
+ BEGIN_EXCEPTION
+ acq = apol_constraint_query_create();
+ if (!acq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return acq;
+ };
+ ~apol_constraint_query_t() {
+ apol_constraint_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_constraint_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run constraint query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_constraint_query_set_class(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ }
+ void set_perm(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_constraint_query_set_perm(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ }
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_constraint_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol validatetrans query */
+typedef struct apol_validatetrans_query {} apol_validatetrans_query_t;
+%extend apol_validatetrans_query_t {
+ apol_validatetrans_query_t() {
+ apol_validatetrans_query_t *avq;
+ BEGIN_EXCEPTION
+ avq = apol_validatetrans_query_create();
+ if (!avq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return avq;
+ };
+ ~apol_validatetrans_query_t() {
+ apol_validatetrans_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_validatetrans_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run validatetrans query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_validatetrans_query_set_class(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ }
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_validatetrans_query_set_regex(p, self, regex);
+ };
+};
+
+/* apol genfscon query */
+#ifdef SWIGPYTHON
+%typemap(in) apol_context_t *ctx {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_context, 0 | 0 );
+ $1 = (apol_context_t*)x;
+}
+#endif
+typedef struct apol_genfscon_query {} apol_genfscon_query_t;
+%extend apol_genfscon_query_t {
+ apol_genfscon_query_t() {
+ apol_genfscon_query_t *agq;
+ BEGIN_EXCEPTION
+ agq = apol_genfscon_query_create();
+ if (!agq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return agq;
+ };
+ ~apol_genfscon_query_t() {
+ apol_genfscon_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_genfscon_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run validatetrans query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_filesystem(apol_policy_t *p, char *fs) {
+ BEGIN_EXCEPTION
+ if (apol_genfscon_query_set_filesystem(p, self, fs)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_path(apol_policy_t *p, char *path) {
+ BEGIN_EXCEPTION
+ if (apol_genfscon_query_set_path(p, self, path)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_objclass(apol_policy_t *p, int objclass) {
+ BEGIN_EXCEPTION
+ if (apol_genfscon_query_set_objclass(p, self, objclass)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set object class for genfscon query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_genfscon_query_set_context(p, self, ctx, range_match);
+ };
+};
+%newobject apol_genfscon_render(apol_policy_t *, qpol_genfscon_t *);
+char *apol_genfscon_render(apol_policy_t * p, qpol_genfscon_t * genfscon);
+
+/* apol fs_use query */
+typedef struct apol_fs_use_query {} apol_fs_use_query_t;
+%extend apol_fs_use_query_t {
+ apol_fs_use_query_t() {
+ apol_fs_use_query_t *afq;
+ BEGIN_EXCEPTION
+ afq = apol_fs_use_query_create();
+ if (!afq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return afq;
+ };
+ ~apol_fs_use_query_t() {
+ apol_fs_use_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_fs_use_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run fs_use query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_filesystem(apol_policy_t *p, char *fs) {
+ BEGIN_EXCEPTION
+ if (apol_fs_use_query_set_filesystem(p, self, fs)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_behavior(apol_policy_t *p, int behavior) {
+ BEGIN_EXCEPTION
+ if (apol_fs_use_query_set_behavior(p, self, behavior)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set behavior for fs_use query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_fs_use_query_set_context(p, self, ctx, range_match);
+ };
+};
+%newobject apol_fs_use_render(apol_policy_t*, qpol_fs_use_t*);
+char *apol_fs_use_render(apol_policy_t * p, qpol_fs_use_t * fsuse);
+
+/* apol initial sid query */
+typedef struct apol_isid_query {} apol_isid_query_t;
+%extend apol_isid_query_t {
+ apol_isid_query_t() {
+ apol_isid_query_t *aiq;
+ BEGIN_EXCEPTION
+ aiq = apol_isid_query_create();
+ if (!aiq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aiq;
+ };
+ ~apol_isid_query_t() {
+ apol_isid_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_isid_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run initial sid query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_name(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_isid_query_set_name(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_isid_query_set_context(p, self, ctx, range_match);
+ };
+};
+
+/* apol portcon query */
+typedef struct apol_portcon_query {} apol_portcon_query_t;
+%extend apol_portcon_query_t {
+ apol_portcon_query_t() {
+ apol_portcon_query_t *apq;
+ BEGIN_EXCEPTION
+ apq = apol_portcon_query_create();
+ if (!apq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return apq;
+ };
+ ~apol_portcon_query_t() {
+ apol_portcon_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_portcon_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run portcon query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_protocol(apol_policy_t *p, int protocol) {
+ apol_portcon_query_set_protocol(p, self, protocol);
+ };
+ void set_low(apol_policy_t *p, int port) {
+ apol_portcon_query_set_low(p, self, port);
+ };
+ void set_high(apol_policy_t *p, int port) {
+ apol_portcon_query_set_high(p, self, port);
+ };
+ void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_portcon_query_set_context(p, self, ctx, range_match);
+ };
+};
+%newobject apol_portcon_render(apol_policy_t*, qpol_portcon_t*);
+char *apol_portcon_render(apol_policy_t * p, qpol_portcon_t * portcon);
+
+/* apol netifcon query */
+typedef struct apol_netifcon_query {} apol_netifcon_query_t;
+%extend apol_netifcon_query_t {
+ apol_netifcon_query_t() {
+ apol_netifcon_query_t *anq;
+ BEGIN_EXCEPTION
+ anq = apol_netifcon_query_create();
+ if (!anq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return anq;
+ };
+ ~apol_netifcon_query_t() {
+ apol_netifcon_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_netifcon_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run netifcon query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_device(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_netifcon_query_set_device(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_if_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_netifcon_query_set_if_context(p, self, ctx, range_match);
+ };
+ void set_msg_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_netifcon_query_set_msg_context(p, self, ctx, range_match);
+ };
+};
+%newobject apol_netifcon_render(apol_policy_t*, qpol_netifcon_t*);
+char *apol_netifcon_render(apol_policy_t * p, qpol_netifcon_t * netifcon);
+
+/* apol nodecon query */
+typedef struct apol_nodecon_query {} apol_nodecon_query_t;
+%extend apol_nodecon_query_t {
+ apol_nodecon_query_t() {
+ apol_nodecon_query_t *anq;
+ BEGIN_EXCEPTION
+ anq = apol_nodecon_query_create();
+ if (!anq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return anq;
+ };
+ ~apol_nodecon_query_t() {
+ apol_nodecon_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_nodecon_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run nodecon query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_protocol(apol_policy_t *p, int protocol) {
+ BEGIN_EXCEPTION
+ if (apol_nodecon_query_set_protocol(p, self, protocol)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set protocol for nodecon query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_addr(apol_policy_t *p, uint32_t *addr, int protocol) {
+ BEGIN_EXCEPTION
+ if (apol_nodecon_query_set_addr(p, self, addr, protocol)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set address for nodecon query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_addr(apol_policy_t *p, apol_ip_t *addr) {
+ BEGIN_EXCEPTION
+ if (apol_nodecon_query_set_addr(p, self, addr->ip, addr->proto)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set address for nodecon query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_mask(apol_policy_t *p, uint32_t *mask, int protocol) {
+ BEGIN_EXCEPTION
+ if (apol_nodecon_query_set_mask(p, self, mask, protocol)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set mask for nodecon query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_mask(apol_policy_t *p, apol_ip_t *mask) {
+ BEGIN_EXCEPTION
+ if (apol_nodecon_query_set_mask(p, self, mask->ip, mask->proto)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set mask for nodecon query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_context(apol_policy_t *p, apol_context_t *ctx, int range_match) {
+ apol_nodecon_query_set_context(p, self, ctx, range_match);
+ };
+};
+%newobject apol_nodecon_render(apol_policy_t*, qpol_nodecon_t*);
+char *apol_nodecon_render(apol_policy_t * p, qpol_nodecon_t * nodecon);
+
+/* apol avrule query */
+typedef struct apol_avrule_query {} apol_avrule_query_t;
+%extend apol_avrule_query_t {
+ apol_avrule_query_t() {
+ apol_avrule_query_t *avq;
+ BEGIN_EXCEPTION
+ avq = apol_avrule_query_create();
+ if (!avq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return avq;
+ };
+ ~apol_avrule_query_t() {
+ apol_avrule_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_avrule_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ %newobject run_syn(apol_policy_t*);
+ apol_vector_t *run_syn(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_syn_avrule_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run syn avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_rules(apol_policy_t *p, int rules) {
+ apol_avrule_query_set_rules(p, self, rules);
+ };
+ void set_source(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_set_source(p, self, name, indirect)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source for avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_source_component(apol_policy_t *p, int component) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_set_source_component(p, self, component)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source component for avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_set_target(p, self, name, indirect)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target for avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target_component(apol_policy_t *p, int component) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_set_target_component(p, self, component)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target component for avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_append_class(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append class to avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_perm(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_append_perm(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append permission to avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_bool(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_avrule_query_set_bool(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set boolean for avrule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_enabled(apol_policy_t *p, int enabled) {
+ apol_avrule_query_set_enabled(p, self, enabled);
+ };
+ void set_all_perms(apol_policy_t *p, int all_perms) {
+ apol_avrule_query_set_all_perms(p, self, all_perms);
+ };
+ void set_source_any(apol_policy_t *p, int is_any) {
+ apol_avrule_query_set_source_any(p, self, is_any);
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_avrule_query_set_regex(p, self, regex);
+ };
+};
+%newobject apol_avrule_render(apol_policy_t*, qpol_avrule_t*);
+char *apol_avrule_render(apol_policy_t * policy, qpol_avrule_t * rule);
+%newobject apol_syn_avrule_render(apol_policy_t*, qpol_syn_avrule_t*);
+char *apol_syn_avrule_render(apol_policy_t * policy, qpol_syn_avrule_t * rule);
+%newobject wrap_apol_avrule_to_syn_avrules(apol_policy_t*, qpol_avrule_t*, apol_string_vector_t*);
+%rename(apol_avrule_to_syn_avrules) wrap_apol_avrule_to_syn_avrules;
+%newobject wrap_apol_avrule_list_to_syn_avrules(apol_policy_t*, apol_vector_t*, apol_string_vector_t*);
+%rename(apol_avrule_list_to_syn_avrules) wrap_apol_avrule_list_to_syn_avrules;
+%inline %{
+ apol_vector_t *wrap_apol_avrule_to_syn_avrules(apol_policy_t *p, qpol_avrule_t *rule, apol_string_vector_t *perms) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = apol_avrule_to_syn_avrules(p, rule, (apol_vector_t*)perms);
+ if (!v) {
+ SWIG_exception(SWIG_RuntimeError, "Could not convert avrule to syntactic avrules");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ }
+ apol_vector_t *wrap_apol_avrule_list_to_syn_avrules(apol_policy_t *p, apol_vector_t *rules, apol_string_vector_t *perms) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = apol_avrule_list_to_syn_avrules(p, rules, (apol_vector_t*)perms);
+ if (!v) {
+ SWIG_exception(SWIG_RuntimeError, "Could not convert avrules to syntactic avrules");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ }
+%}
+
+/* apol terule query */
+typedef struct apol_terule_query {} apol_terule_query_t;
+%extend apol_terule_query_t {
+ apol_terule_query_t() {
+ apol_terule_query_t *atq;
+ BEGIN_EXCEPTION
+ atq = apol_terule_query_create();
+ if (!atq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return atq;
+ };
+ ~apol_terule_query_t() {
+ apol_terule_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_terule_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ %newobject run_syn(apol_policy_t*);
+ apol_vector_t *run_syn(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_syn_terule_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_rules(apol_policy_t *p, int rules) {
+ apol_terule_query_set_rules(p, self, rules);
+ };
+ void set_source(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_set_source(p, self, name, indirect)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source for terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_source_component(apol_policy_t *p, int component) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_set_source_component(p, self, component)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source component for terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_set_target(p, self, name, indirect)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target for terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target_component(apol_policy_t *p, int component) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_set_target_component(p, self, component)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target component for terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_append_class(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append class to terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_default(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_set_default(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set default for terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_bool(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_terule_query_set_bool(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set boolean for terule query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_enabled(apol_policy_t *p, int enabled) {
+ apol_terule_query_set_enabled(p, self, enabled);
+ };
+ void set_source_any(apol_policy_t *p, int is_any) {
+ apol_terule_query_set_source_any(p, self, is_any);
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_terule_query_set_regex(p, self, regex);
+ };
+};
+%newobject apol_terule_render(apol_policy_t*, qpol_terule_t*);
+char *apol_terule_render(apol_policy_t * policy, qpol_terule_t * rule);
+%newobject apol_syn_terule_render(apol_policy_t*, qpol_syn_terule_t*);
+char *apol_syn_terule_render(apol_policy_t * policy, qpol_syn_terule_t * rule);
+%newobject apol_terule_to_syn_terules(apol_policy_t*, qpol_terule_t*);
+apol_vector_t *apol_terule_to_syn_terules(apol_policy_t * p, qpol_terule_t * rule);
+%newobject apol_terule_list_to_syn_terules(apol_policy_t*, apol_vector_t*);
+apol_vector_t *apol_terule_list_to_syn_terules(apol_policy_t * p, apol_vector_t * rules);
+
+/* apol cond rule query */
+typedef struct apol_cond_query {} apol_cond_query_t;
+%extend apol_cond_query_t {
+ apol_cond_query_t() {
+ apol_cond_query_t *acq;
+ BEGIN_EXCEPTION
+ acq = apol_cond_query_create();
+ if (!acq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return acq;
+ };
+ ~apol_cond_query_t() {
+ apol_cond_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_cond_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run condiional query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_bool(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_cond_query_set_bool(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set boolean for condiional query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_cond_query_set_regex(p, self, regex);
+ };
+};
+%newobject apol_cond_expr_render(apol_policy_t*, qpol_cond_t*);
+char *apol_cond_expr_render(apol_policy_t * p, qpol_cond_t * cond);
+
+/* apol role allow query */
+typedef struct apol_role_allow_query {} apol_role_allow_query_t;
+%extend apol_role_allow_query_t {
+ apol_role_allow_query_t() {
+ apol_role_allow_query_t *arq;
+ BEGIN_EXCEPTION
+ arq = apol_role_allow_query_create();
+ if (!arq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return arq;
+ };
+ ~apol_role_allow_query_t() {
+ apol_role_allow_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_role_allow_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run role allow query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_source(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_role_allow_query_set_source(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_role_allow_query_set_target(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_source_any(apol_policy_t *p, int is_any) {
+ apol_role_allow_query_set_source_any(p, self, is_any);
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_role_allow_query_set_regex(p, self, regex);
+ };
+};
+%newobject apol_role_allow_render(apol_policy_t*, qpol_role_allow_t*);
+char *apol_role_allow_render(apol_policy_t * policy, qpol_role_allow_t * rule);
+
+/* apol role transition rule query */
+typedef struct apol_role_trans_query {} apol_role_trans_query_t;
+%extend apol_role_trans_query_t {
+ apol_role_trans_query_t() {
+ apol_role_trans_query_t *arq;
+ BEGIN_EXCEPTION
+ arq = apol_role_trans_query_create();
+ if (!arq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return arq;
+ };
+ ~apol_role_trans_query_t() {
+ apol_role_trans_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_role_trans_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run role transition query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_source(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_role_trans_query_set_source(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_role_trans_query_set_target(p, self, name, indirect)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_default(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_role_trans_query_set_default(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_source_any(apol_policy_t *p, int is_any) {
+ apol_role_trans_query_set_source_any(p, self, is_any);
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_role_trans_query_set_regex(p, self, regex);
+ };
+};
+%newobject apol_role_trans_render(apol_policy_t*, qpol_role_trans_t*);
+char *apol_role_trans_render(apol_policy_t * policy, qpol_role_trans_t * rule);
+
+/* apol range transition rule query */
+typedef struct apol_range_trans_query {} apol_range_trans_query_t;
+%extend apol_range_trans_query_t {
+ apol_range_trans_query_t() {
+ apol_range_trans_query_t *arq;
+ BEGIN_EXCEPTION
+ arq = apol_range_trans_query_create();
+ if (!arq) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return arq;
+ };
+ ~apol_range_trans_query_t() {
+ apol_range_trans_query_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_range_trans_get_by_query(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run range transition query");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_source(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_range_trans_query_set_source(p, self, name, indirect)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_target(apol_policy_t *p, char *name, int indirect) {
+ BEGIN_EXCEPTION
+ if (apol_range_trans_query_set_target(p, self, name, indirect)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_range_trans_query_append_class(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append class to range transition query");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_range(apol_policy_t *p, apol_mls_range_t *rng, int range_match) {
+ BEGIN_EXCEPTION
+ if (apol_range_trans_query_set_range(p, self, rng, range_match)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_source_any(apol_policy_t *p, int is_any) {
+ apol_range_trans_query_set_source_any(p, self, is_any);
+ };
+ void set_regex(apol_policy_t *p, int regex) {
+ apol_range_trans_query_set_regex(p, self, regex);
+ };
+};
+%newobject apol_range_trans_render(apol_policy_t*, qpol_range_trans_t*);
+char *apol_range_trans_render(apol_policy_t * policy, qpol_range_trans_t * rule);
+
+/* domain transition analysis */
+#define APOL_DOMAIN_TRANS_DIRECTION_FORWARD 0x01
+#define APOL_DOMAIN_TRANS_DIRECTION_REVERSE 0x02
+#define APOL_DOMAIN_TRANS_SEARCH_VALID 0x01
+#define APOL_DOMAIN_TRANS_SEARCH_INVALID 0x02
+#define APOL_DOMAIN_TRANS_SEARCH_BOTH (APOL_DOMAIN_TRANS_SEARCH_VALID|APOL_DOMAIN_TRANS_SEARCH_INVALID)
+typedef struct apol_domain_trans_analysis {} apol_domain_trans_analysis_t;
+%extend apol_domain_trans_analysis_t {
+ apol_domain_trans_analysis_t() {
+ apol_domain_trans_analysis_t *dta;
+ BEGIN_EXCEPTION
+ dta = apol_domain_trans_analysis_create();
+ if (!dta) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return dta;
+ };
+ ~apol_domain_trans_analysis_t() {
+ apol_domain_trans_analysis_destroy(&self);
+ };
+ void set_direction(apol_policy_t *p, int direction) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_set_direction(p, self, (unsigned char)direction)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set direction for domain transition analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_valid(apol_policy_t *p, int valid) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_set_valid(p, self, (unsigned char)valid)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set valid flag for domain transition analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_start_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_set_start_type(p, self, name)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_result_regex(apol_policy_t *p, char *regex) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_set_result_regex(p, self, regex)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_access_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_append_access_type(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append access type for domain transition analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_class(apol_policy_t *p, char *class_name) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_append_class(p, self, class_name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append access class for domain transition analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_perm(apol_policy_t *p, char *perm_name) {
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_append_perm(p, self, perm_name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append access permission for domain transition analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v = NULL;
+ BEGIN_EXCEPTION
+ if (apol_domain_trans_analysis_do(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run domain transition analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+};
+typedef struct apol_domain_trans_result {} apol_domain_trans_result_t;
+%extend apol_domain_trans_result_t {
+ apol_domain_trans_result_t(apol_domain_trans_result_t *in) {
+ apol_domain_trans_result_t *dtr;
+ BEGIN_EXCEPTION
+ dtr = apol_domain_trans_result_create_from_domain_trans_result(in);
+ if (!dtr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return dtr;
+ };
+ ~apol_domain_trans_result_t() {
+ apol_domain_trans_result_destroy(&self);
+ };
+ const qpol_type_t *get_start_type() {
+ return apol_domain_trans_result_get_start_type(self);
+ };
+ const qpol_type_t *get_entrypoint_type() {
+ return apol_domain_trans_result_get_entrypoint_type(self);
+ };
+ const qpol_type_t *get_end_type() {
+ return apol_domain_trans_result_get_end_type(self);
+ };
+ int get_is_valid() {
+ return apol_domain_trans_result_is_trans_valid(self);
+ };
+ const apol_vector_t *get_proc_trans_rules() {
+ return apol_domain_trans_result_get_proc_trans_rules(self);
+ };
+ const apol_vector_t *get_entrypoint_rules() {
+ return apol_domain_trans_result_get_entrypoint_rules(self);
+ };
+ const apol_vector_t *get_exec_rules() {
+ return apol_domain_trans_result_get_exec_rules(self);
+ };
+ const apol_vector_t *get_setexec_rules() {
+ return apol_domain_trans_result_get_setexec_rules(self);
+ };
+ const apol_vector_t *get_type_trans_rules() {
+ return apol_domain_trans_result_get_type_trans_rules(self);
+ };
+ const apol_vector_t *get_access_rules() {
+ return apol_domain_trans_result_get_access_rules(self);
+ };
+};
+#define APOL_DOMAIN_TRANS_RULE_PROC_TRANS 0x01
+#define APOL_DOMAIN_TRANS_RULE_EXEC 0x02
+#define APOL_DOMAIN_TRANS_RULE_EXEC_NO_TRANS 0x04
+#define APOL_DOMAIN_TRANS_RULE_ENTRYPOINT 0x08
+#define APOL_DOMAIN_TRANS_RULE_TYPE_TRANS 0x10
+#define APOL_DOMAIN_TRANS_RULE_SETEXEC 0x20
+int apol_domain_trans_table_verify_trans(apol_policy_t * policy, qpol_type_t * start_dom, qpol_type_t * ep_type, qpol_type_t * end_dom);
+%inline %{
+ apol_domain_trans_result_t *apol_domain_trans_result_from_void(void *x) {
+ return (apol_domain_trans_result_t*)x;
+ };
+%}
+
+/* apol infoflow analysis */
+#define APOL_INFOFLOW_MODE_DIRECT 0x01
+#define APOL_INFOFLOW_MODE_TRANS 0x02
+#define APOL_INFOFLOW_IN 0x01
+#define APOL_INFOFLOW_OUT 0x02
+#define APOL_INFOFLOW_BOTH (APOL_INFOFLOW_IN|APOL_INFOFLOW_OUT)
+#define APOL_INFOFLOW_EITHER 0x04
+%{
+ typedef struct apol_infoflow {
+ apol_infoflow_graph_t *g;
+ apol_vector_t *v;
+ } apol_infoflow_t;
+ apol_infoflow_t *apol_infoflow_create() {
+ return calloc(1, sizeof(apol_infoflow_t));
+ }
+ void apol_infoflow_destroy(apol_infoflow_t **in) {
+ if (!in || !(*in)) {
+ return;
+ }
+ free(*in); /* NOTE: does not free contents intentionally */
+ *in = NULL;
+ }
+%}
+typedef struct apol_infoflow {} apol_infoflow_t;
+%extend apol_infoflow_t {
+ apol_infoflow_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_infoflow_t() {
+ apol_infoflow_destroy(&self);
+ };
+ %newobject extract_graph();
+ apol_infoflow_graph_t *extract_graph() {
+ apol_infoflow_graph_t *g = self->g;
+ self->g = NULL;
+ return g;
+ };
+ %newobject extract_result_vector();
+ apol_vector_t *extract_result_vector() {
+ apol_vector_t *v = self->v;
+ self->v = NULL;
+ return v;
+ };
+};
+typedef struct apol_infoflow_analysis {} apol_infoflow_analysis_t;
+%extend apol_infoflow_analysis_t {
+ apol_infoflow_analysis_t() {
+ apol_infoflow_analysis_t *aia;
+ BEGIN_EXCEPTION
+ aia = apol_infoflow_analysis_create();
+ if (!aia) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return aia;
+ };
+ ~apol_infoflow_analysis_t() {
+ apol_infoflow_analysis_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_infoflow_t *run(apol_policy_t *p) {
+ apol_infoflow_t *ai = NULL;
+ BEGIN_EXCEPTION
+ ai = apol_infoflow_create();
+ if (!ai) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ if (apol_infoflow_analysis_do(p, self, &ai->v, &ai->g)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run information flow analysis");
+ }
+ END_EXCEPTION
+ return ai;
+ fail:
+ apol_vector_destroy(&ai->v);
+ apol_infoflow_graph_destroy(&ai->g);
+ apol_infoflow_destroy(&ai);
+ return NULL;
+ };
+ void set_mode(apol_policy_t *p, int mode) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_set_mode(p, self, (unsigned int)mode)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set mode for information flow analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_dir(apol_policy_t *p, int direction) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_set_dir(p, self, (unsigned int)direction)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set direction for information flow analysis");
+ END_EXCEPTION
+ }
+ fail:
+ return;
+ };
+ void set_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_set_type(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set type for information flow analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_intermediate(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_append_intermediate(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append intermediate type for information flow analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_class_perm(apol_policy_t *p, char *class_name, char *perm_name) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_append_class_perm(p, self, class_name, perm_name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append class and permission for information flow analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_min_weight(apol_policy_t *p, int weight) {
+ apol_infoflow_analysis_set_min_weight(p, self, weight);
+ };
+ void set_result_regex(apol_policy_t *p, char *regex) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_set_result_regex(p, self, regex)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set result regular expression for information flow analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+};
+typedef struct apol_infoflow_graph {} apol_infoflow_graph_t;
+%extend apol_infoflow_graph_t {
+ apol_infoflow_graph_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_graph_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_infoflow_graph_t() {
+ apol_infoflow_graph_destroy(&self);
+ };
+ %newobject do_more(apol_policy_t*, char*);
+ apol_vector_t *do_more(apol_policy_t *p, char *type) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_do_more(p, self, type, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not do more analysis of information flow graph");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void trans_further_prepare(apol_policy_t *p, char *start_type, char *end_type) {
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_trans_further_prepare(p, self, start_type, end_type)) {
+ SWIG_exception(SWIG_MemoryError, "Error preparing graph for further information flow analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ apol_vector_t *trans_further_next(apol_policy_t *p, apol_vector_t *v) {
+ apol_vector_t *retval = NULL;
+ BEGIN_EXCEPTION
+ if (apol_infoflow_analysis_trans_further_next(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run further analysis");
+ }
+ END_EXCEPTION
+ retval = v;
+ fail:
+ return retval;
+ };
+};
+typedef struct apol_infoflow_result {} apol_infoflow_result_t;
+%extend apol_infoflow_result_t {
+ apol_infoflow_result_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_result_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_infoflow_result_t() {
+ /* no op - vector will destroy */
+ return;
+ };
+ int get_dir() {
+ return (int)apol_infoflow_result_get_dir(self);
+ };
+ const qpol_type_t *get_start_type() {
+ return apol_infoflow_result_get_start_type(self);
+ };
+ const qpol_type_t *get_end_type() {
+ return apol_infoflow_result_get_end_type(self);
+ };
+ int get_length() {
+ return (int) apol_infoflow_result_get_length(self);
+ }
+ const apol_vector_t *get_steps() {
+ return apol_infoflow_result_get_steps(self);
+ };
+};
+%inline %{
+ apol_infoflow_result_t *apol_infoflow_result_from_void(void *x) {
+ return (apol_infoflow_result_t*)x;
+ };
+%}
+typedef struct apol_infoflow_step {} apol_infoflow_step_t;
+%extend apol_infoflow_step_t {
+ apol_infoflow_step_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_infoflow_step_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_infoflow_step_t() {
+ /* no op */
+ return;
+ };
+ const qpol_type_t *get_start_type() {
+ return apol_infoflow_step_get_start_type(self);
+ };
+ const qpol_type_t *get_end_type() {
+ return apol_infoflow_step_get_end_type(self);
+ };
+ int get_weight() {
+ return apol_infoflow_step_get_weight(self);
+ };
+ const apol_vector_t *get_rules() {
+ return apol_infoflow_step_get_rules(self);
+ };
+};
+%inline %{
+ apol_infoflow_step_t *apol_infoflow_step_from_void(void *x) {
+ return (apol_infoflow_step_t*)x;
+ };
+%}
+
+/* apol relabel analysis */
+#define APOL_RELABEL_DIR_TO 0x01
+#define APOL_RELABEL_DIR_FROM 0x02
+#define APOL_RELABEL_DIR_BOTH (APOL_RELABEL_DIR_TO|APOL_RELABEL_DIR_FROM)
+#define APOL_RELABEL_DIR_SUBJECT 0x04
+typedef struct apol_relabel_analysis {} apol_relabel_analysis_t;
+%extend apol_relabel_analysis_t {
+ apol_relabel_analysis_t() {
+ apol_relabel_analysis_t *ara;
+ BEGIN_EXCEPTION
+ ara = apol_relabel_analysis_create();
+ if (!ara) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return ara;
+ };
+ ~apol_relabel_analysis_t() {
+ apol_relabel_analysis_destroy(&self);
+ };
+ %newobject run(apol_policy_t*);
+ apol_vector_t *run(apol_policy_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ if (apol_relabel_analysis_do(p, self, &v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run relabel analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void set_dir(apol_policy_t *p, int direction) {
+ BEGIN_EXCEPTION
+ if (apol_relabel_analysis_set_dir(p, self, (unsigned int)direction)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set direction for relabel analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_relabel_analysis_set_type(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set type for relabel analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_class(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_relabel_analysis_append_class(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append class to relabel analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_subject(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_relabel_analysis_append_subject(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append subject to relabel analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_result_regex(apol_policy_t *p, char *regex) {
+ BEGIN_EXCEPTION
+ if (apol_relabel_analysis_set_result_regex(p, self, regex)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set result regular expression for relabel analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+};
+typedef struct apol_relabel_result {} apol_relabel_result_t;
+%extend apol_relabel_result_t {
+ apol_relabel_result_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_relabel_result_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_relabel_result_t() {
+ /* no op - vector will destroy */
+ return;
+ };
+ const apol_vector_t *get_to() {
+ return apol_relabel_result_get_to(self);
+ };
+ const apol_vector_t *get_from() {
+ return apol_relabel_result_get_from(self);
+ };
+ const apol_vector_t *get_both() {
+ return apol_relabel_result_get_both(self);
+ };
+ const qpol_type_t *get_result_type() {
+ return apol_relabel_result_get_result_type(self);
+ };
+};
+%inline %{
+ apol_relabel_result_t *apol_relabel_result_from_void(void *x) {
+ return (apol_relabel_result_t*)x;
+ };
+%}
+typedef struct apol_relabel_result_pair {} apol_relabel_result_pair_t;
+%extend apol_relabel_result_pair_t {
+ apol_relabel_result_pair_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_relabel_result_pair_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_relabel_result_pair_t() {
+ /* no op - owned and free()'d by apol_relabel_result_t */
+ return;
+ };
+ const qpol_avrule_t *get_ruleA() {
+ return apol_relabel_result_pair_get_ruleA(self);
+ };
+ const qpol_avrule_t *get_ruleB() {
+ return apol_relabel_result_pair_get_ruleB(self);
+ };
+ const qpol_type_t *get_intermediate_type() {
+ return apol_relabel_result_pair_get_intermediate_type(self);
+ };
+};
+%inline %{
+ apol_relabel_result_pair_t *apol_relabel_result_pair_from_void(void *x) {
+ return (apol_relabel_result_pair_t*)x;
+ };
+%}
+
+/* apol type relation analysis */
+#define APOL_TYPES_RELATION_COMMON_ATTRIBS 0x0001
+#define APOL_TYPES_RELATION_COMMON_ROLES 0x0002
+#define APOL_TYPES_RELATION_COMMON_USERS 0x0004
+#define APOL_TYPES_RELATION_SIMILAR_ACCESS 0x0010
+#define APOL_TYPES_RELATION_DISSIMILAR_ACCESS 0x0020
+#define APOL_TYPES_RELATION_ALLOW_RULES 0x0100
+#define APOL_TYPES_RELATION_TYPE_RULES 0x0200
+#define APOL_TYPES_RELATION_DOMAIN_TRANS_AB 0x0400
+#define APOL_TYPES_RELATION_DOMAIN_TRANS_BA 0x0800
+#define APOL_TYPES_RELATION_DIRECT_FLOW 0x1000
+#define APOL_TYPES_RELATION_TRANS_FLOW_AB 0x4000
+#define APOL_TYPES_RELATION_TRANS_FLOW_BA 0x8000
+typedef struct apol_types_relation_analysis {} apol_types_relation_analysis_t;
+%extend apol_types_relation_analysis_t {
+ apol_types_relation_analysis_t() {
+ apol_types_relation_analysis_t *atr;
+ BEGIN_EXCEPTION
+ atr = apol_types_relation_analysis_create();
+ if (!atr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return atr;
+ };
+ ~apol_types_relation_analysis_t() {
+ apol_types_relation_analysis_destroy(&self);
+ }
+ %newobject run(apol_policy_t*);
+ apol_types_relation_result_t *run(apol_policy_t *p) {
+ apol_types_relation_result_t *res;
+ BEGIN_EXCEPTION
+ if (apol_types_relation_analysis_do(p, self, &res)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run types relation analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return res;
+ };
+ void set_first_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_types_relation_analysis_set_first_type(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set first type for types relation analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_other_type(apol_policy_t *p, char *name) {
+ BEGIN_EXCEPTION
+ if (apol_types_relation_analysis_set_other_type(p, self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set other type for types relation analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_analyses(apol_policy_t *p, int analyses) {
+ BEGIN_EXCEPTION
+ if (apol_types_relation_analysis_set_analyses(p, self, (unsigned int)analyses)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set analyses to run for types relation analysis");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+};
+typedef struct apol_types_relation_result {} apol_types_relation_result_t;
+%extend apol_types_relation_result_t {
+ apol_types_relation_result_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_types_relation_result_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_types_relation_result_t() {
+ apol_types_relation_result_destroy(&self);
+ };
+ const apol_vector_t *get_attributes() {
+ return apol_types_relation_result_get_attributes(self);
+ };
+ const apol_vector_t *get_roles() {
+ return apol_types_relation_result_get_roles(self);
+ };
+ const apol_vector_t *get_users() {
+ return apol_types_relation_result_get_users(self);
+ };
+ const apol_vector_t *get_similar_first() {
+ return apol_types_relation_result_get_similar_first(self);
+ };
+ const apol_vector_t *get_similar_other() {
+ return apol_types_relation_result_get_similar_other(self);
+ };
+ const apol_vector_t *get_dissimilar_first() {
+ return apol_types_relation_result_get_dissimilar_first(self);
+ };
+ const apol_vector_t *get_dissimilar_other() {
+ return apol_types_relation_result_get_dissimilar_other(self);
+ };
+ const apol_vector_t *get_allowrules() {
+ return apol_types_relation_result_get_allowrules(self);
+ };
+ const apol_vector_t *get_typerules() {
+ return apol_types_relation_result_get_typerules(self);
+ };
+ const apol_vector_t *get_directflows() {
+ return apol_types_relation_result_get_directflows(self);
+ };
+ const apol_vector_t *get_transflowsAB() {
+ return apol_types_relation_result_get_transflowsAB(self);
+ };
+ const apol_vector_t *get_transflowsBA() {
+ return apol_types_relation_result_get_transflowsBA(self);
+ };
+ const apol_vector_t*get_domainsAB() {
+ return apol_types_relation_result_get_domainsAB(self);
+ };
+ const apol_vector_t*get_domainsBA() {
+ return apol_types_relation_result_get_domainsBA(self);
+ };
+};
+typedef struct apol_types_relation_access {} apol_types_relation_access_t;
+%extend apol_types_relation_access_t {
+ apol_types_relation_access_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create apol_types_relation_access_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~apol_types_relation_access_t() {
+ /* no op - vector will destroy */
+ return;
+ };
+ const qpol_type_t *get_type() {
+ return apol_types_relation_access_get_type(self);
+ };
+ const apol_vector_t *get_rules() {
+ return apol_types_relation_access_get_rules(self);
+ };
+};
+%inline %{
+ apol_types_relation_access_t *apol_types_relation_access_from_void(void *x) {
+ return (apol_types_relation_access_t*)x;
+ };
+%}
+// vim:ft=c
diff --git a/libapol/swig/java/MANIFEST.MF.in b/libapol/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..2c0eee8
--- /dev/null
+++ b/libapol/swig/java/MANIFEST.MF.in
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+
+Name: com/tresys/setools/
+Specification-Title: "SETools Java Libraries"
+Specification-Version: "@VERSION@"
+Specification-Vendor: "Tresys Technology"
+Implementation-Title: "com.tresys.setools.apol"
+Implementation-Version: "@libapol_version@"
+Implementation-Vendor: "Tresys Technology"
+Extension-List: qpol
+qpol-Extension-Name: com.tresys.setools.qpol
+qpol-Implementation-Version: @libqpel_version@
diff --git a/libapol/swig/java/Makefile.am b/libapol/swig/java/Makefile.am
new file mode 100644
index 0000000..ea087b8
--- /dev/null
+++ b/libapol/swig/java/Makefile.am
@@ -0,0 +1,118 @@
+wrappedso_DATA = libjapol.so.@libapol_version@
+wrappedso_SONAME = @libapol_jswig_soname@
+short_name = libjapol.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.apol
+package_dir = $(dir $(subst .,/,$(package_name)))apol
+
+wrappedjar_DATA = apol.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../apol.i
+BUILT_SOURCES = apol_wrap.c \
+ apol_attr_query_t.java \
+ apol_avrule_query_t.java \
+ apol_bool_query_t.java \
+ apol_cat_query_t.java \
+ apol_class_query_t.java \
+ apol_common_query_t.java \
+ apol_cond_query_t.java \
+ apolConstants.java \
+ apol_constraint_query_t.java \
+ apol_context_t.java \
+ apol_domain_trans_analysis_t.java \
+ apol_domain_trans_result_t.java \
+ apol_fs_use_query_t.java \
+ apol_genfscon_query_t.java \
+ apol_infoflow_analysis_t.java \
+ apol_infoflow_graph_t.java \
+ apol_infoflow_result_t.java \
+ apol_infoflow_step_t.java \
+ apol_infoflow_t.java \
+ apol_ip_t.java \
+ apol_isid_query_t.java \
+ apol.java \
+ apolJNI.java \
+ apol_level_query_t.java \
+ apol_mls_level_t.java \
+ apol_mls_range_t.java \
+ apol_netifcon_query_t.java \
+ apol_nodecon_query_t.java \
+ apol_perm_query_t.java \
+ apol_policy_path_t.java \
+ apol_policy_path_type_e.java \
+ apol_policy_t.java \
+ apol_portcon_query_t.java \
+ apol_range_trans_query_t.java \
+ apol_relabel_analysis_t.java \
+ apol_relabel_result_pair_t.java \
+ apol_relabel_result_t.java \
+ apol_role_allow_query_t.java \
+ apol_role_query_t.java \
+ apol_role_trans_query_t.java \
+ apol_string_vector_t.java \
+ apol_terule_query_t.java \
+ apol_type_query_t.java \
+ apol_types_relation_access_t.java \
+ apol_types_relation_analysis_t.java \
+ apol_types_relation_result_t.java \
+ apol_user_query_t.java \
+ apol_validatetrans_query_t.java \
+ apol_vector_t.java \
+ SWIGTYPE_p_unsigned_int.java \
+ SWIGTYPE_p_void.java
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \
+ -classpath $(top_builddir)/libqpol/swig/java/qpol.jar
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_JAVA_OPT) -package $(package_name) -o $@ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/swig $<
+
+$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES))
+
+$(wrappedso_DATA): $(filter %.c, $(BUILT_SOURCES))
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_JAVA_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+# Intentionally do not include SWIGTYPE_p_void.java below so that the
+# Java compiler uses the one created in package
+# com.tresys.setools.qpol instead of the one from package
+# com.tresys.setools.apol.
+java_files = $(filter-out SWIGTYPE_p_void.java, $(filter %.java, $(BUILT_SOURCES)))
+
+classes = $(patsubst %.java, $(package_dir)/%.class, $(java_files))
+
+# Because the Java compiler can generate multiple class files from the
+# same source .java file, putting all of the classes below will result
+# in repeated invocations of javac. Therefore, an alternative is to
+# just depend upon the first class file, and let the Java compiler
+# create the rest of them.
+$(firstword $(classes)): $(java_files)
+ $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^
+
+$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes))
+
+$(wrappedjar_DATA): MANIFEST.MF
+
+$(wrappedjar_DATA): $(classes)
+ $(JAR) cfm $@ MANIFEST.MF $^
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name)
+ $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name)
+ -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA)
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name)
diff --git a/libapol/swig/python/Makefile.am b/libapol/swig/python/Makefile.am
new file mode 100644
index 0000000..649feff
--- /dev/null
+++ b/libapol/swig/python/Makefile.am
@@ -0,0 +1,36 @@
+wrappedso_DATA = _apol.so.@libapol_version@
+wrappedso_SONAME = @libapol_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+wrappedpy_DATA = apol.py
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../apol.i
+BUILT_SOURCES = apol_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_PYTHON_OPT) -o $@ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _apol.so
+
+$(wrappedpy_DATA): $(BUILT_SOURCES)
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _apol.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_apol.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _apol.so apol.pyc
diff --git a/libapol/swig/tcl/Makefile.am b/libapol/swig/tcl/Makefile.am
new file mode 100644
index 0000000..40a206a
--- /dev/null
+++ b/libapol/swig/tcl/Makefile.am
@@ -0,0 +1,36 @@
+wrappedso_DATA = libtapol.so.@libapol_version@
+wrappedso_SONAME = @libapol_tswig_soname@
+short_name = libtapol.so
+wrappedsodir = $(libdir)/setools/apol
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../apol.i
+BUILT_SOURCES = apol_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_TCL_OPT) -pkgversion @libapol_version@ -o $@ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+$(package_SCRIPTS): $(wrappedso_DATA)
+ echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) apol
+ cp $(wrappedso_DATA) $@ apol
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) apol/$(wrappedso_DATA) apol/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libapol/tests/Makefile.am b/libapol/tests/Makefile.am
new file mode 100644
index 0000000..ed5005e
--- /dev/null
+++ b/libapol/tests/Makefile.am
@@ -0,0 +1,23 @@
+TESTS = libapol-tests
+check_PROGRAMS = libapol-tests
+
+libapol_tests_SOURCES = \
+ avrule-tests.c avrule-tests.h \
+ dta-tests.c dta-tests.h \
+ infoflow-tests.c infoflow-tests.h \
+ policy-21-tests.c policy-21-tests.h \
+ role-tests.c role-tests.h \
+ terule-tests.c terule-tests.h \
+ user-tests.c user-tests.h \
+ constrain-tests.c constrain-tests.h \
+ ../../libqpol/src/queue.c ../../libqpol/src/queue.h \
+ libapol-tests.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -DTOP_SRCDIR="\"$(top_srcdir)\""
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libapol_tests_DEPENDENCIES = ../src/libapol.so
diff --git a/libapol/tests/avrule-tests.c b/libapol/tests/avrule-tests.c
new file mode 100644
index 0000000..f86bbe2
--- /dev/null
+++ b/libapol/tests/avrule-tests.c
@@ -0,0 +1,161 @@
+/**
+ * @file
+ *
+ * Test the AV rule queries, both semantic and syntactic searches.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/avrule-query.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <qpol/policy_extend.h>
+#include <stdbool.h>
+
+#define BIN_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.21"
+#define SOURCE_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.conf"
+
+static apol_policy_t *bp = NULL;
+static apol_policy_t *sp = NULL;
+
+static void avrule_basic_syn(void)
+{
+ apol_avrule_query_t *aq = apol_avrule_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(aq);
+
+ int retval;
+ retval = apol_avrule_query_set_rules(sp, aq, QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_syn_avrule_get_by_query(sp, aq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+
+ size_t num_auditallows = 0, num_dontaudits = 0;
+
+ qpol_policy_t *q = apol_policy_get_qpol(sp);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_syn_avrule_t *syn = (const qpol_syn_avrule_t *)apol_vector_get_element(v, i);
+ uint32_t rule_type;
+ retval = qpol_syn_avrule_get_rule_type(q, syn, &rule_type);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ CU_ASSERT(rule_type == QPOL_RULE_AUDITALLOW || rule_type == QPOL_RULE_DONTAUDIT);
+ if (rule_type == QPOL_RULE_AUDITALLOW) {
+ num_auditallows++;
+ } else if (rule_type == QPOL_RULE_DONTAUDIT) {
+ num_dontaudits++;
+ }
+ }
+ CU_ASSERT(num_auditallows == 4 && num_dontaudits == 1);
+ apol_vector_destroy(&v);
+
+ retval = apol_avrule_query_append_class(sp, aq, "unknown class");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ retval = apol_syn_avrule_get_by_query(sp, aq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+ apol_avrule_query_destroy(&aq);
+}
+
+static void avrule_default(void)
+{
+ apol_avrule_query_t *aq = apol_avrule_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(aq);
+
+ int retval;
+ qpol_policy_t *sq = apol_policy_get_qpol(sp);
+
+ apol_vector_t *v = NULL;
+
+ retval = apol_avrule_get_by_query(sp, aq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 396);
+ apol_vector_destroy(&v);
+
+ qpol_policy_rebuild(sq, QPOL_POLICY_OPTION_NO_NEVERALLOWS);
+ retval = apol_avrule_get_by_query(sp, aq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 21);
+ apol_vector_destroy(&v);
+
+ retval = apol_avrule_get_by_query(bp, aq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 21);
+ apol_vector_destroy(&v);
+
+ apol_avrule_query_destroy(&aq);
+}
+
+CU_TestInfo avrule_tests[] = {
+ {"basic syntactic search", avrule_basic_syn}
+ ,
+ {"default query", avrule_default}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int avrule_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, BIN_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((bp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ if (qpol_policy_build_syn_rule_table(apol_policy_get_qpol(sp)) != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int avrule_cleanup()
+{
+ apol_policy_destroy(&bp);
+ apol_policy_destroy(&sp);
+ return 0;
+}
diff --git a/libapol/tests/avrule-tests.h b/libapol/tests/avrule-tests.h
new file mode 100644
index 0000000..f56ab25
--- /dev/null
+++ b/libapol/tests/avrule-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libapol avrule query tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRULE_TESTS_H
+#define AVRULE_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo avrule_tests[];
+extern int avrule_init();
+extern int avrule_cleanup();
+
+#endif
diff --git a/libapol/tests/constrain-tests.c b/libapol/tests/constrain-tests.c
new file mode 100644
index 0000000..3133b5d
--- /dev/null
+++ b/libapol/tests/constrain-tests.c
@@ -0,0 +1,523 @@
+/**
+ * @file
+ *
+ * Test the information flow analysis code.
+ *
+ *
+ * Copyright (C) 2010 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/perm-map.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <stdbool.h>
+#include <string.h>
+#include <apol/constraint-query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/constraint.h>
+#include <libqpol/src/queue.h>
+
+#define CONSTR_SOURCE TEST_POLICIES "/setools-3.3/apol/constrain_test_policy.conf"
+#define CONSTR_BINARY TEST_POLICIES "/setools-3.3/apol/constrain_test_policy.21"
+// Glob won't work, but this gives the idea of where we are trying to go
+#define CONSTR_MODULAR TEST_POLICIES "/setools-3.1/modules/*.pp"
+
+//#define DEBUGTRACE 1
+
+
+/* General concepts: The constraints are stored in the policy by class,
+ * that is, the list of classes stored in the policy has attached to it
+ * whatever constraints affect that class.
+ * The "policy_iter" iterator is a structure which contains a pointer to the
+ * list of classes from the loaded policy, and another pointer to the list of
+ * constraints associated with the current class. This latter pointer is
+ * traversed to its end, at which point the class pointer is updated, and the
+ * new class' list of constraints is put in its place. The switch from one
+ * class to the next is done behind the scenes by the iterator. Thus each time
+ * a new item is retrieved from policy_iter, it needs to have all info (class,
+ * permissions, expression) extracted from it.
+ *
+ * The input file must be a known file. The class and permissions are used as
+ * a key by this test routine to determine what the expected expression will
+ * be. Thus, if the input file is modified, this test becomes invalid. The file
+ * (defined above) resides in the 'testing-policies' repository.
+ *
+ * The statements validatetrans and mlsvalidatetrans, although similar to
+ * constrain and mlsconstrain, are not considered here.
+ *
+ */
+
+// Define data for expected policy. This is a hack, but what I could think of
+// on short notice.
+
+// Similar to struct constraint_expr from sepol/policydb/constraint.h
+// but want char * list of names, not internal representations.
+typedef struct local_expr {
+ uint32_t expr_type;
+ uint32_t attr;
+ uint32_t op;
+ size_t name_count;
+ char **namelist;
+} local_expr_t;
+
+typedef struct constrain_test_list {
+ char **class;
+ char **permissions; // Must end with NULL
+ int test_found;
+ int expr_count;
+ local_expr_t **expr_list;
+} constrain_test_list_t;
+
+// TODO Clean up memory leaks -- all iterators need to be destroyed, check other stuff
+
+
+char *class0 = "file";
+char *perm0[] = { "create", "relabelto", NULL };
+local_expr_t expr00 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_EQ, 0, NULL };
+local_expr_t *expr0[] = { &expr00, NULL };
+
+char *class1 = "lnk_file";
+char *perm1[10] = { "create", "relabelto", NULL };
+local_expr_t expr10 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_NEQ, 0, NULL };
+local_expr_t *expr1[] = { &expr10, NULL };
+
+// This test (test 2) is not expected to be matched
+char *class2 = "fifo_file";
+char *perm2[] = { "create", "relabelto", NULL };
+local_expr_t expr20 = { CEXPR_ATTR, CEXPR_L2H2, CEXPR_DOM, 0, NULL };
+local_expr_t *expr2[] = { &expr20, NULL };
+
+char *class3 = "node";
+char *perm3[] = { "udp_send", NULL };
+local_expr_t expr30 = { CEXPR_ATTR, CEXPR_L1L2, CEXPR_DOM, 0, NULL };
+local_expr_t expr31 = { CEXPR_ATTR, CEXPR_L1H2, CEXPR_DOMBY, 0, NULL };
+local_expr_t expr32 = { CEXPR_AND, 0, 0, 0, NULL };
+local_expr_t *expr3[] = { &expr30, &expr31, &expr32, NULL };
+
+char *class4 = "netif";
+char *perm4[] = { "tcp_send", NULL };
+local_expr_t expr40 = { CEXPR_ATTR, CEXPR_L1L2, CEXPR_DOM, 0, NULL };
+local_expr_t expr41 = { CEXPR_ATTR, CEXPR_L1H2, CEXPR_DOMBY, 0, NULL };
+local_expr_t expr42 = { CEXPR_OR, 0, 0, 0, NULL };
+local_expr_t *expr4[] = { &expr40, &expr41, &expr42, NULL };
+
+char *class5 = "dir";
+char *perm5[] = { "read", NULL };
+char *name50[] = { "sysadm_t", "secadm_t", NULL };
+local_expr_t expr50 = { CEXPR_NAMES, CEXPR_TYPE, CEXPR_EQ, 2, name50 };
+local_expr_t *expr5[] = { &expr50, NULL };
+
+constrain_test_list_t test_list[] = {
+ { &class0, perm0, 0, 1, expr0 },
+ { &class1, perm1, 0, 1, expr1 },
+ { &class2, perm2, 0, 1, expr2 },
+ { &class3, perm3, 0, 3, expr3 },
+ { &class4, perm4, 0, 3, expr4 },
+ { &class5, perm5, 0, 3, expr5 }
+};
+
+typedef struct compare_perm_str {
+ int list_length;
+ int list_found;
+ int q_elements_compared;
+ char **list;
+} compare_perm_str_t;
+
+typedef struct compare_expr_str {
+ int list_length;
+ int list_found;
+ local_expr_t **list;
+} compare_expr_str_t;
+
+
+static apol_policy_t *ps = NULL; // Source policy
+static apol_policy_t *pb = NULL; // Binary policy
+static apol_policy_t *pm = NULL; // Modular policy
+
+
+// Prototypes if needed
+static int compare_item_to_list(void *e, void *v);
+
+
+
+
+static int doprintstr (queue_element_t e, void *p)
+{
+ char *s = (char *)e;
+ // Second arg is not used
+
+ printf ("%s ", s);
+ return 0;
+}
+
+static int compare_expr_list(qpol_policy_t *q, qpol_iterator_t *expr_iter, int expr_count, local_expr_t **le)
+{
+ const qpol_constraint_expr_node_t *expr;
+ int sym_type;
+ int op;
+ int expr_type;
+ int i;
+ int err;
+
+ for (i=0; qpol_iterator_end(expr_iter) == 0; i++, qpol_iterator_next(expr_iter))
+ {
+ expr_type = op = sym_type = 0;
+ if (i >= expr_count) // Hit the end of the list
+ return 1; // Not the right list
+
+ err = qpol_iterator_get_item(expr_iter, (void **)&expr);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_constraint_expr_node_get_op(q, expr, &op);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_constraint_expr_node_get_expr_type(q, expr, &expr_type);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+#ifdef DEBUGTRACE
+ printf ("Expr compare: Policy:attr:%d, op:%d, expr_type:%d\n", sym_type, op, expr_type);
+ printf ("Expr compare: Test:attr:%d, op:%d, expr_type:%d\n", le[i]->attr, le[i]->op, le[i]->expr_type);
+#endif
+ if (sym_type != le[i]->attr)
+ {
+ return 1;
+ }
+ if (op != le[i]->op)
+ {
+ return 1;
+ }
+ if (expr_type != le[i]->expr_type)
+ {
+ return 1;
+ }
+
+ if (expr_type == CEXPR_NAMES) // Need compare name lists
+ {
+ qpol_iterator_t *names_iter=NULL;
+ size_t name_size=0;
+ compare_perm_str_t x;
+
+#ifdef DEBUGTRACE
+ printf ("Found CEXPR_NAMES expression\n");
+#endif
+ x.list_length = le[i]->name_count;
+ x.list = le[i]->namelist;
+ x.list_found = 0;
+ x.q_elements_compared = 0;
+
+ err = qpol_constraint_expr_node_get_names_iter (q, expr, &names_iter);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ err = qpol_iterator_get_size(names_iter, &name_size);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+ CU_ASSERT_TRUE_FATAL(name_size > 0);
+
+ if (name_size != x.list_length) // Want exact match,
+ {
+ qpol_iterator_destroy(&names_iter);
+ return 1;
+ }
+
+ for (; qpol_iterator_end(names_iter) == 0; qpol_iterator_next(names_iter))
+ {
+ char *lname = NULL;
+
+ err = qpol_iterator_get_item (names_iter, (void **)&lname);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ compare_item_to_list (lname, &x);
+ free (lname);
+ }
+
+#ifdef DEBUGTRACE
+ printf ("name list length=%d, list_found=%d, q_elements_compared=%d\n", x.list_length, x.list_found, x.q_elements_compared);
+#endif
+ if ((x.list_length != x.list_found) || (x.list_length != x.q_elements_compared))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int compare_item_to_list(void *e, void *v)
+{
+ char *pe = (char *)e;
+ compare_perm_str_t *x = (compare_perm_str_t *)v;
+ char **permlist = x->list;
+ char *perm;
+
+ CU_ASSERT_PTR_NOT_NULL(permlist);
+ CU_ASSERT_PTR_NOT_NULL(pe);
+
+ while ((perm=*permlist++) != NULL)
+ {
+#ifdef DEBUGTRACE
+ printf ("pe = %s\n", pe);
+ printf ("perm = %s\n", perm);
+#endif
+ if (strcmp(pe, perm) == 0)
+ x->list_found++;
+ }
+ x->q_elements_compared++;
+ return 0;
+}
+
+static int compare_perm_list(queue_t perm_q, char **permissions)
+{
+ compare_perm_str_t x;
+
+ x.list_length = 0;
+ x.list_found = 0;
+ x.q_elements_compared = 0;
+ x.list = permissions;
+
+ while (*permissions++ != NULL)
+ x.list_length++;
+
+#ifdef DEBUGTRACE
+ printf ("list_length = %d\n", x.list_length);
+#endif
+ if (queue_map(perm_q, compare_item_to_list, &x) != 0)
+ return 1;
+
+#ifdef DEBUGTRACE
+ printf ("list length=%d, list_found=%d, q_elements_compared=%d\n", x.list_length, x.list_found, x.q_elements_compared);
+#endif
+ if ((x.list_length != x.list_found) || (x.list_length != x.q_elements_compared))
+ return 1;
+
+ return 0;
+}
+
+static void constrain_test(apol_policy_t *ap)
+{
+ int i;
+ int err=0;
+ const char *class_name = NULL;
+ const char *constrain_type = "?constrain";
+ char *perm_list = "No Perms Extracted";
+ const qpol_constraint_expr_node_t *expr = NULL;
+ qpol_iterator_t *policy_iter = NULL; // Iterates over all constraints in a policy
+ qpol_iterator_t *perm_iter = NULL; // Iterates over permissions in a constraint
+ qpol_iterator_t *expr_iter = NULL; // Iterates over expression in a constraint
+ qpol_policy_t *q = apol_policy_get_qpol(ap);
+ qpol_constraint_t *constraint = NULL;
+ const qpol_class_t *class;
+ size_t n_constraints = 0;
+ size_t counted_constraints = 0;
+ size_t tests_not_found = 0;
+ int test_count = sizeof(test_list) / sizeof(constrain_test_list_t);
+ int tests_matched = 0;
+ int constrains_matched = 0;
+
+ queue_t perm_q; // holds list of permissions, in case more than one
+
+ err = qpol_policy_get_constraint_iter(q, &policy_iter);
+ if (err != 0)
+ {
+ CU_FAIL("Policy iterator not accessible");
+ goto cleanup;
+ }
+ err = qpol_iterator_get_size(policy_iter, &n_constraints);
+ if (err != 0)
+ {
+ CU_FAIL("Policy size computation failed");
+ goto cleanup;
+ }
+
+ CU_ASSERT_EQUAL(n_constraints, 7); // Count of constraints split among all classes
+
+ counted_constraints=0;
+ for (i=0; i<test_count; i++)
+ {
+ test_list[i].test_found = 0;
+ }
+
+ // Iterate through constraints
+ for (; qpol_iterator_end(policy_iter) == 0; qpol_iterator_next(policy_iter))
+ {
+ counted_constraints++;
+ /* The qpol_constraint_t that is returned below consists of
+ * struct qpol_constraint <<<from constraint_query.c
+ * {
+ * const qpol_class_t *obj_class;
+ * constraint_node_t *constr;
+ * };
+ * the qpol_class_t is a pseudonym for class_datum_t from policydb.h
+ * constraint_node_t is defined in sepol/policydb/constraint.h
+ */
+ err = qpol_iterator_get_item(policy_iter, (void **)&constraint);
+ CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen
+
+ err = qpol_constraint_get_class(q, constraint, &class);
+ CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen
+ err = qpol_class_get_name(q, class, &class_name);
+ CU_ASSERT_EQUAL_FATAL(err, 0); // Should never happen
+
+#ifdef DEBUGTRACE
+ printf ("Found class %s\n", class_name);
+#endif
+ // get permission(s)
+ err = qpol_constraint_get_perm_iter (q, constraint, &perm_iter);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ perm_q = queue_create();
+ for (; qpol_iterator_end(perm_iter) == 0; qpol_iterator_next(perm_iter))
+ {
+ err = qpol_iterator_get_item(perm_iter, (void **)&perm_list);
+ CU_ASSERT_EQUAL_FATAL(err,0)
+
+ err = queue_insert (perm_q, perm_list);
+ CU_ASSERT_EQUAL_FATAL(err,0)
+ }
+#ifdef DEBUGTRACE
+ printf ("perms: ");
+ queue_map(perm_q, doprintstr, NULL);
+ printf ("\n");
+#endif
+
+ // get RPN expressions
+ err = qpol_constraint_get_expr_iter (q, constraint, &expr_iter);
+ CU_ASSERT_EQUAL_FATAL(err, 0);
+
+ // At this point, the class, permission list, and expression list (in
+ // the iterator) have been identified. Based on expected class/permission
+ // combinations, find one which matches, and note that it was found.
+ // If not found, count that too.
+ for (i=0; i<test_count; i++)
+ {
+ if (strcmp(*(test_list[i].class), class_name) == 0)
+ {
+ if (compare_perm_list(perm_q, test_list[i].permissions) == 0)
+ {
+ if (compare_expr_list(q, expr_iter, test_list[i].expr_count, test_list[i].expr_list) == 0)
+ {
+ test_list[i].test_found = 1;
+ constrains_matched++;
+ break;
+ }
+#ifdef DEBUGTRACE
+ else
+ {
+ printf ("Mismatch comparing expression list\n");
+ }
+#endif
+ }
+#ifdef DEBUGTRACE
+ else
+ {
+ printf ("Mismatch comparing permission list\n");
+ }
+#endif
+ }
+#ifdef DEBUGTRACE
+ else
+ {
+ printf ("Mismatch comparing classes %s,%s\n", *(test_list[i].class),class_name);
+ }
+#endif
+ }
+ queue_destroy(perm_q);
+ }
+ for (i=0; i<test_count; i++)
+ {
+ if (test_list[i].test_found == 0)
+ {
+ CU_ASSERT_EQUAL(i, 2);
+ }
+ else
+ tests_matched++;
+ }
+#ifdef DEBUGTRACE
+ printf ("tests_matched: %d, constrains_matched: %d, counted_constraints: %d, n_constraints: %d\n", tests_matched, constrains_matched, counted_constraints, n_constraints);
+#endif
+ CU_ASSERT_EQUAL(tests_matched, 5);
+ CU_ASSERT_EQUAL(constrains_matched, 5);
+ CU_ASSERT_EQUAL(counted_constraints, 7);
+ CU_ASSERT_EQUAL(n_constraints, 7);
+
+ CU_PASS();
+
+cleanup:
+ return;
+ // close and destroy iterators/policy pointers
+}
+
+static void constrain_source(void)
+{
+ constrain_test(ps);
+}
+
+static void constrain_binary(void)
+{
+ constrain_test(pb);
+// CU_PASS("Not yet implemented")
+}
+
+
+static void constrain_modular(void)
+{
+ CU_PASS("Not yet implemented")
+}
+
+CU_TestInfo constrain_tests[] = {
+ {"constrain from source policy", constrain_source},
+ {"constrain from binary policy", constrain_binary},
+// {"constrain from modular policy", constrain_modular},
+ CU_TEST_INFO_NULL
+};
+
+int constrain_init()
+{
+ // Probably should move this to individual tests, just fstat policy to see if it is there!
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, CONSTR_SOURCE, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((ps = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, CONSTR_BINARY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((pb = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ return 0;
+}
+
+int constrain_cleanup()
+{
+ apol_policy_destroy(&ps);
+ return 0;
+}
diff --git a/libapol/tests/constrain-tests.h b/libapol/tests/constrain-tests.h
new file mode 100644
index 0000000..e87c020
--- /dev/null
+++ b/libapol/tests/constrain-tests.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ *
+ * Declarations for libapol constraint tests.
+ *
+ *
+ * Copyright (C) 2010 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef CONSTRAIN_TESTS_H
+#define CONSTRAIN_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo constrain_tests[];
+extern int constrain_init();
+extern int constrain_cleanup();
+
+#endif
diff --git a/libapol/tests/dta-tests.c b/libapol/tests/dta-tests.c
new file mode 100644
index 0000000..d25fd82
--- /dev/null
+++ b/libapol/tests/dta-tests.c
@@ -0,0 +1,529 @@
+/**
+ * @file
+ *
+ * Test the new domain transition analysis code introduced in SETools
+ * 3.3.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/avrule-query.h>
+#include <apol/domain-trans-analysis.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define POLICY TEST_POLICIES "/setools-3.3/apol/dta_test.policy.conf"
+
+static apol_policy_t *p = NULL;
+
+static void dta_forward(void)
+{
+ apol_policy_reset_domain_trans_table(p);
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "tuna_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ apol_domain_trans_analysis_destroy(&d);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name, *ep_name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "tuna_t");
+
+ qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(strcmp(name, "boat_t") == 0 || strcmp(name, "sand_t") == 0);
+
+ qt = apol_domain_trans_result_get_entrypoint_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &ep_name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ if (strcmp(name, "boat_t") == 0) {
+ CU_ASSERT_STRING_EQUAL(ep_name, "net_t");
+ } else if (strcmp(name, "sand_t") == 0) {
+ CU_ASSERT(strcmp(ep_name, "reel_t") == 0 || strcmp(ep_name, "wave_t") == 0);
+ }
+ }
+
+ apol_vector_destroy(&v);
+}
+
+static void dta_forward_multi_end(void)
+{
+ apol_policy_reset_domain_trans_table(p);
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "shark_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ apol_domain_trans_analysis_destroy(&d);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 2);
+
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name, *ep_name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "shark_t");
+
+ qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(strcmp(name, "surf_t") == 0 || strcmp(name, "sand_t") == 0);
+
+ qt = apol_domain_trans_result_get_entrypoint_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &ep_name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ CU_ASSERT_STRING_EQUAL(ep_name, "wave_t");
+ }
+
+ apol_vector_destroy(&v);
+}
+
+static void dta_forward_access(void)
+{
+ apol_policy_reset_domain_trans_table(p);
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "tuna_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_access_type(p, d, "boat_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_access_type(p, d, "sand_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_access_type(p, d, "wave_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_class(p, d, "file");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_perm(p, d, "write");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0);
+
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "tuna_t");
+
+ qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "boat_t");
+
+ const apol_vector_t *rules_v = apol_domain_trans_result_get_access_rules(dtr);
+ CU_ASSERT_FATAL(rules_v != NULL && apol_vector_get_size(rules_v) > 0);
+ size_t j;
+ for (j = 0; j < apol_vector_get_size(rules_v); j++) {
+ const qpol_avrule_t *qa = (const qpol_avrule_t *)apol_vector_get_element(rules_v, j);
+ char *render = apol_avrule_render(p, qa);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(render);
+ CU_ASSERT_STRING_EQUAL(render, "allow boat_t wave_t : file { write getattr execute } ;");
+ free(render);
+ }
+ }
+
+ apol_vector_destroy(&v);
+
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "boat_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_access_type(p, d, NULL);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_class(p, d, NULL);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_append_perm(p, d, NULL);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_policy_reset_domain_trans_table(p);
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ apol_domain_trans_analysis_destroy(&d);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0);
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "boat_t");
+
+ qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(strcmp(name, "sand_t") == 0 || strcmp(name, "dock_t") == 0);
+ }
+ apol_vector_destroy(&v);
+}
+
+static void dta_reverse(void)
+{
+ apol_policy_reset_domain_trans_table(p);
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval;
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "sand_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_REVERSE);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ apol_domain_trans_analysis_destroy(&d);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0);
+
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "sand_t");
+
+ qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(strcmp(name, "boat_t") == 0 || strcmp(name, "grouper_t") == 0 || strcmp(name, "shark_t") == 0 ||
+ strcmp(name, "tuna_t") == 0);
+ }
+
+ apol_vector_destroy(&v);
+}
+
+static void dta_reverse_regexp(void)
+{
+ apol_policy_reset_domain_trans_table(p);
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval;
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "sand_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_REVERSE);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_result_regex(p, d, "u");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ apol_domain_trans_analysis_destroy(&d);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0);
+
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ size_t i;
+ bool found_tuna_wave = false, found_grouper_reel = false, found_grouper_wave = false;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name, *ep_name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(name, "sand_t");
+
+ qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(strcmp(name, "tuna_t") == 0 || strcmp(name, "grouper_t") == 0);
+
+ qt = apol_domain_trans_result_get_entrypoint_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &ep_name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ if (strcmp(name, "tuna_t") == 0) {
+ if (strcmp(ep_name, "wave_t") == 0) {
+ found_tuna_wave = true;
+ }
+ } else if (strcmp(name, "grouper_t") == 0) {
+ if (strcmp(ep_name, "reel_t") == 0) {
+ found_grouper_reel = true;
+ } else if (strcmp(ep_name, "wave_t") == 0) {
+ found_grouper_wave = true;
+ }
+ }
+ }
+ CU_ASSERT(found_tuna_wave && found_grouper_reel && found_grouper_wave);
+
+ apol_vector_destroy(&v);
+}
+
+static void dta_reflexive(void)
+{
+ apol_policy_reset_domain_trans_table(p);
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_start_type(p, d, "sand_t");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+
+ retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_REVERSE);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0);
+ size_t i;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ const char *name;
+ retval = qpol_type_get_name(q, qt, &name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_NOT_EQUAL(name, "sand_t");
+ }
+ apol_vector_destroy(&v);
+
+ apol_domain_trans_analysis_destroy(&d);
+}
+
+struct dta_invalid_item
+{
+ const char *start_type;
+ const char *end_type;
+ const char *entrypoint_type;
+ const bool missing_proc_trans;
+ const bool missing_entrypoint;
+ const bool missing_exec;
+ const bool missing_setexec;
+ const bool missing_type_trans;
+ bool used;
+};
+
+static void dta_invalid(void)
+{
+ struct dta_invalid_item items[] = {
+ {"boat_t", "dock_t", "net_t", false, false, true, false, false, false},
+ {"boat_t", "sand_t", "reel_t", false, false, true, false, false, false},
+ {"crab_t", "dock_t", "net_t", false, false, false, true, false, false},
+ {"crab_t", "dock_t", "rope_t", false, false, true, true, false, false},
+ {"crab_t", "dock_t", "wave_t", false, true, true, false, false, false},
+ {"gull_t", "dock_t", "net_t", false, false, false, true, true, false},
+ {"gull_t", "dock_t", "rope_t", false, false, true, true, true, false},
+ {"gull_t", "sand_t", "net_t", true, true, false, false, false, false},
+ {"marlin_t", "boat_t", "line_t", false, false, true, false, false, false},
+ {"marlin_t", "boat_t", "net_t", false, false, true, false, false, false},
+ {"ray_t", "boat_t", "line_t", true, false, true, false, false, false},
+ {"ray_t", "sand_t", "wave_t", true, false, false, false, false, false},
+ {"shark_t", "sand_t", "reel_t", false, false, true, false, false, false},
+ {"tuna_t", "boat_t", "line_t", false, false, true, false, false, false},
+ {"tuna_t", "boat_t", "reel_t", false, true, false, false, false, false},
+ {NULL, NULL, NULL, false, false, false, false, false, false}
+ };
+ const char *start_types[] = {
+ "boat_t", "crab_t", "gull_t", "marlin_t", "ray_t", "shark_t", "tuna_t", NULL
+ };
+ apol_domain_trans_analysis_t *d = apol_domain_trans_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(d);
+ int retval = apol_domain_trans_analysis_set_direction(p, d, APOL_DOMAIN_TRANS_DIRECTION_FORWARD);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_domain_trans_analysis_set_valid(p, d, APOL_DOMAIN_TRANS_SEARCH_INVALID);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ apol_vector_t *v = NULL;
+ struct dta_invalid_item *item;
+ for (const char **start = start_types; *start != NULL; start++) {
+ apol_policy_reset_domain_trans_table(p);
+ retval = apol_domain_trans_analysis_set_start_type(p, d, *start);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ retval = apol_domain_trans_analysis_do(p, d, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) > 0);
+
+ for (size_t i = 0; i < apol_vector_get_size(v); i++) {
+ const apol_domain_trans_result_t *dtr = (const apol_domain_trans_result_t *)apol_vector_get_element(v, i);
+
+ const char *result_start, *result_end, *result_entry;
+
+ const qpol_type_t *qt = apol_domain_trans_result_get_start_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &result_start);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(result_start, *start);
+
+ qt = apol_domain_trans_result_get_end_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &result_end);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ qt = apol_domain_trans_result_get_entrypoint_type(dtr);
+ CU_ASSERT_PTR_NOT_NULL(qt);
+ retval = qpol_type_get_name(q, qt, &result_entry);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ CU_ASSERT(apol_domain_trans_result_is_trans_valid(dtr) == 0);
+
+ for (item = items + 0; item->start_type != NULL; item++) {
+ if (strcmp(result_start, item->start_type) == 0 &&
+ strcmp(result_end, item->end_type) == 0 &&
+ strcmp(result_entry, item->entrypoint_type) == 0 && !item->used) {
+ item->used = true;
+
+ const apol_vector_t *cv;
+ if (item->missing_proc_trans) {
+ cv = apol_domain_trans_result_get_proc_trans_rules(dtr);
+ CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0);
+ }
+ if (item->missing_entrypoint) {
+ cv = apol_domain_trans_result_get_entrypoint_rules(dtr);
+ CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0);
+ }
+ if (item->missing_exec) {
+ cv = apol_domain_trans_result_get_exec_rules(dtr);
+ CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0);
+ }
+ if (item->missing_setexec) {
+ cv = apol_domain_trans_result_get_setexec_rules(dtr);
+ CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0);
+ }
+ if (item->missing_type_trans) {
+ cv = apol_domain_trans_result_get_type_trans_rules(dtr);
+ CU_ASSERT(cv != NULL && apol_vector_get_size(cv) == 0);
+ }
+ break;
+ }
+ }
+ if (item->start_type == NULL) {
+ CU_FAIL();
+ }
+ }
+ apol_vector_destroy(&v);
+ }
+
+ for (item = items + 0; item->start_type != NULL; item++) {
+ CU_ASSERT(item->used);
+ }
+ apol_domain_trans_analysis_destroy(&d);
+}
+
+CU_TestInfo dta_tests[] = {
+ {"dta forward", dta_forward}
+ ,
+ {"dta forward + access", dta_forward_access}
+ ,
+ {"dta forward with multiple endpoints for same entrypoint", dta_forward_multi_end}
+ ,
+ {"dta reverse", dta_reverse}
+ ,
+ {"dta reverse + regexp", dta_reverse_regexp}
+ ,
+ {"dta reflexive", dta_reflexive}
+ ,
+ {"dta invalid transitions", dta_invalid}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int dta_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ int retval = apol_policy_build_domain_trans_table(p);
+ if (retval != 0) {
+ return 1;
+ }
+ return 0;
+}
+
+int dta_cleanup()
+{
+ apol_policy_destroy(&p);
+ return 0;
+}
diff --git a/libapol/tests/dta-tests.h b/libapol/tests/dta-tests.h
new file mode 100644
index 0000000..820b8d2
--- /dev/null
+++ b/libapol/tests/dta-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libapol domain transition analysis tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DTA_TESTS_H
+#define DTA_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo dta_tests[];
+extern int dta_init();
+extern int dta_cleanup();
+
+#endif
diff --git a/libapol/tests/infoflow-tests.c b/libapol/tests/infoflow-tests.c
new file mode 100644
index 0000000..6a74ba6
--- /dev/null
+++ b/libapol/tests/infoflow-tests.c
@@ -0,0 +1,127 @@
+/**
+ * @file
+ *
+ * Test the information flow analysis code.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/infoflow-analysis.h>
+#include <apol/perm-map.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define BIG_POLICY TEST_POLICIES "/snapshots/fc4_targeted.policy.conf"
+#define PERMMAP TOP_SRCDIR "/apol/perm_maps/apol_perm_mapping_ver19"
+
+static apol_policy_t *p = NULL;
+
+static void infoflow_direct_overview(void)
+{
+ apol_infoflow_analysis_t *ia = apol_infoflow_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ia);
+ int retval;
+ retval = apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT);
+ CU_ASSERT(retval == 0);
+ retval = apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_IN);
+ CU_ASSERT(retval == 0);
+ retval = apol_infoflow_analysis_set_type(p, ia, "agp_device_t");
+ CU_ASSERT(retval == 0);
+
+ apol_vector_t *v = NULL;
+ apol_infoflow_graph_t *g = NULL;
+ // no permmap loaded, so analysis run will abort with error
+ retval = apol_infoflow_analysis_do(p, ia, &v, &g);
+ CU_ASSERT(retval < 0);
+
+ retval = apol_policy_open_permmap(p, PERMMAP);
+ CU_ASSERT(retval == 0);
+
+ retval = apol_infoflow_analysis_do(p, ia, &v, &g);
+ CU_ASSERT(retval == 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+ CU_ASSERT(apol_vector_get_size(v) > 0);
+ CU_ASSERT_PTR_NOT_NULL(g);
+
+ apol_infoflow_analysis_destroy(&ia);
+ apol_vector_destroy(&v);
+ apol_infoflow_graph_destroy(&g);
+}
+
+static void infoflow_trans_overview(void)
+{
+ apol_infoflow_analysis_t *ia = apol_infoflow_analysis_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ia);
+ int retval;
+ retval = apol_infoflow_analysis_set_mode(p, ia, APOL_INFOFLOW_MODE_DIRECT);
+ CU_ASSERT(retval == 0);
+ retval = apol_infoflow_analysis_set_dir(p, ia, APOL_INFOFLOW_IN);
+ CU_ASSERT(retval == 0);
+ retval = apol_infoflow_analysis_set_type(p, ia, "local_login_t");
+ CU_ASSERT(retval == 0);
+
+ apol_vector_t *v = NULL;
+ apol_infoflow_graph_t *g = NULL;
+ // permmap was loaded by infoflow_direct_overview()
+ retval = apol_infoflow_analysis_do(p, ia, &v, &g);
+ CU_ASSERT(retval == 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+ CU_ASSERT(apol_vector_get_size(v) > 0);
+ CU_ASSERT_PTR_NOT_NULL(g);
+
+ apol_infoflow_analysis_destroy(&ia);
+ apol_vector_destroy(&v);
+ apol_infoflow_graph_destroy(&g);
+}
+
+CU_TestInfo infoflow_tests[] = {
+ {"infoflow direct overview", infoflow_direct_overview}
+ ,
+ {"infoflow trans overview", infoflow_trans_overview}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int infoflow_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, BIG_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_NEVERALLOWS, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ return 0;
+}
+
+int infoflow_cleanup()
+{
+ apol_policy_destroy(&p);
+ return 0;
+}
diff --git a/libapol/tests/infoflow-tests.h b/libapol/tests/infoflow-tests.h
new file mode 100644
index 0000000..840f3b1
--- /dev/null
+++ b/libapol/tests/infoflow-tests.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * Declarations for libapol infomation flow analysis tests, both
+ * direct and transitive.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INFOFLOW_TESTS_H
+#define INFOFLOW_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo infoflow_tests[];
+extern int infoflow_init();
+extern int infoflow_cleanup();
+
+#endif
diff --git a/libapol/tests/libapol-tests.c b/libapol/tests/libapol-tests.c
new file mode 100644
index 0000000..9b83235
--- /dev/null
+++ b/libapol/tests/libapol-tests.c
@@ -0,0 +1,64 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for libapol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "avrule-tests.h"
+#include "dta-tests.h"
+#include "infoflow-tests.h"
+#include "policy-21-tests.h"
+#include "role-tests.h"
+#include "terule-tests.h"
+#include "constrain-tests.h"
+#include "user-tests.h"
+
+int main(void)
+{
+ if (CU_initialize_registry() != CUE_SUCCESS) {
+ return CU_get_error();
+ }
+
+ CU_SuiteInfo suites[] = {
+ {"Policy Version 21", policy_21_init, policy_21_cleanup, policy_21_tests},
+ {"AV Rule Query", avrule_init, avrule_cleanup, avrule_tests},
+ {"Domain Transition Analysis", dta_init, dta_cleanup, dta_tests},
+ {"Infoflow Analysis", infoflow_init, infoflow_cleanup, infoflow_tests},
+ {"Role Query", role_init, role_cleanup, role_tests},
+ {"TE Rule Query", terule_init, terule_cleanup, terule_tests},
+ {"User Query", user_init, user_cleanup, user_tests},
+ {"Constrain query", constrain_init, constrain_cleanup, constrain_tests},
+ CU_SUITE_INFO_NULL
+ };
+
+ CU_register_suites(suites);
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ unsigned int num_failures = CU_get_number_of_failure_records();
+ CU_cleanup_registry();
+ return (int)num_failures;
+}
diff --git a/libapol/tests/policy-21-tests.c b/libapol/tests/policy-21-tests.c
new file mode 100644
index 0000000..ea07da1
--- /dev/null
+++ b/libapol/tests/policy-21-tests.c
@@ -0,0 +1,181 @@
+/**
+ * @file
+ *
+ * Test features of policy version 21, that were introduced in
+ * SETools 3.2.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <apol/range_trans-query.h>
+
+#define POLICY TEST_POLICIES "/setools-3.2/apol/rangetrans_testing_policy.conf"
+
+static apol_policy_t *p = NULL;
+
+static void policy_21_range_trans_all(void)
+{
+ apol_range_trans_query_t *rt = apol_range_trans_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(rt);
+
+ apol_vector_t *v = NULL;
+ int retval = apol_range_trans_get_by_query(p, rt, &v);
+ apol_range_trans_query_destroy(&rt);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 17);
+ apol_vector_destroy(&v);
+}
+
+static void policy_21_range_trans_process(void)
+{
+ apol_range_trans_query_t *rt = apol_range_trans_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(rt);
+ int retval;
+ retval = apol_range_trans_query_append_class(p, rt, "process");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_range_trans_get_by_query(p, rt, &v);
+ apol_range_trans_query_destroy(&rt);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 10);
+ size_t i;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_range_trans_t *qrt = (const qpol_range_trans_t *)apol_vector_get_element(v, i);
+ const qpol_class_t *c;
+ retval = qpol_range_trans_get_target_class(q, qrt, &c);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ const char *class_name;
+ retval = qpol_class_get_name(q, c, &class_name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(class_name, "process");
+ }
+ apol_vector_destroy(&v);
+}
+
+static void policy_21_range_trans_lnk_file(void)
+{
+ apol_range_trans_query_t *rt = apol_range_trans_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(rt);
+ int retval;
+ retval = apol_range_trans_query_append_class(p, rt, "lnk_file");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_range_trans_get_by_query(p, rt, &v);
+ apol_range_trans_query_destroy(&rt);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 2);
+ size_t i;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_range_trans_t *qrt = (const qpol_range_trans_t *)apol_vector_get_element(v, i);
+ const qpol_class_t *c;
+ retval = qpol_range_trans_get_target_class(q, qrt, &c);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ const char *class_name;
+ retval = qpol_class_get_name(q, c, &class_name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_STRING_EQUAL(class_name, "lnk_file");
+ }
+ apol_vector_destroy(&v);
+}
+
+static void policy_21_range_trans_either(void)
+{
+ apol_range_trans_query_t *rt = apol_range_trans_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(rt);
+ int retval;
+ retval = apol_range_trans_query_append_class(p, rt, "process");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ retval = apol_range_trans_query_append_class(p, rt, "lnk_file");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_range_trans_get_by_query(p, rt, &v);
+ apol_range_trans_query_destroy(&rt);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 12);
+ size_t i;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_range_trans_t *qrt = (const qpol_range_trans_t *)apol_vector_get_element(v, i);
+ const qpol_class_t *c;
+ retval = qpol_range_trans_get_target_class(q, qrt, &c);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ const char *class_name;
+ retval = qpol_class_get_name(q, c, &class_name);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(strcmp(class_name, "process") == 0 || strcmp(class_name, "lnk_file") == 0);
+ }
+ apol_vector_destroy(&v);
+}
+
+static void policy_21_range_trans_socket(void)
+{
+ apol_range_trans_query_t *rt = apol_range_trans_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(rt);
+ int retval;
+ retval = apol_range_trans_query_append_class(p, rt, "socket");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_range_trans_get_by_query(p, rt, &v);
+ apol_range_trans_query_destroy(&rt);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+}
+
+CU_TestInfo policy_21_tests[] = {
+ {"range_trans all", policy_21_range_trans_all},
+ {"range_trans process", policy_21_range_trans_process},
+ {"range_trans lnk_file", policy_21_range_trans_lnk_file},
+ {"range_trans process or lnk_file", policy_21_range_trans_either},
+ {"range_trans socket", policy_21_range_trans_socket},
+ CU_TEST_INFO_NULL
+};
+
+int policy_21_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((p = apol_policy_create_from_policy_path(ppath, QPOL_POLICY_OPTION_NO_RULES, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+ return 0;
+}
+
+int policy_21_cleanup()
+{
+ apol_policy_destroy(&p);
+ return 0;
+}
diff --git a/libapol/tests/policy-21-tests.h b/libapol/tests/policy-21-tests.h
new file mode 100644
index 0000000..dd427ba
--- /dev/null
+++ b/libapol/tests/policy-21-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libapol version 21 policy support.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLICY_21_TESTS_H
+#define POLICY_21_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo policy_21_tests[];
+extern int policy_21_init();
+extern int policy_21_cleanup();
+
+#endif
diff --git a/libapol/tests/role-tests.c b/libapol/tests/role-tests.c
new file mode 100644
index 0000000..3aee323
--- /dev/null
+++ b/libapol/tests/role-tests.c
@@ -0,0 +1,154 @@
+/**
+ * @file
+ *
+ * Test the role queries.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/role-query.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <stdbool.h>
+
+#define SOURCE_POLICY TEST_POLICIES "/setools/apol/role_dom.conf"
+
+static apol_policy_t *sp = NULL;
+static qpol_policy_t *qp = NULL;
+
+static void role_basic(void)
+{
+ apol_role_query_t *q = apol_role_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(q);
+
+ apol_vector_t *v = NULL;
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 26);
+ apol_vector_destroy(&v);
+
+ apol_role_query_set_role(sp, q, "sh");
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+
+ apol_role_query_set_role(sp, q, NULL);
+ apol_role_query_set_type(sp, q, "silly_t");
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 2);
+ bool found_silly = false, found_object = false;
+ for (size_t i = 0; i < apol_vector_get_size(v); i++) {
+ qpol_role_t *r = (qpol_role_t *) apol_vector_get_element(v, i);
+ const char *name;
+ qpol_role_get_name(qp, r, &name);
+ if (strcmp(name, "silly_r") == 0) {
+ found_silly = true;
+ } else if (strcmp(name, "object_r") == 0) {
+ found_object = true;
+ } else {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_silly && found_object);
+ apol_vector_destroy(&v);
+
+ apol_role_query_set_type(sp, q, "not_in_the_policy_t");
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+
+ apol_role_query_destroy(&q);
+}
+
+static void role_regex(void)
+{
+ apol_role_query_t *q = apol_role_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(q);
+ apol_role_query_set_regex(sp, q, 1);
+
+ apol_role_query_set_role(sp, q, "*");
+ apol_vector_t *v = NULL;
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) < 0 && v == NULL);
+
+ apol_role_query_set_role(sp, q, "^sh");
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 2);
+ bool found_shirt = false, found_shoe = false;
+ for (size_t i = 0; i < apol_vector_get_size(v); i++) {
+ qpol_role_t *r = (qpol_role_t *) apol_vector_get_element(v, i);
+ const char *name;
+ qpol_role_get_name(qp, r, &name);
+ if (strcmp(name, "shirt_r") == 0) {
+ found_shirt = true;
+ } else if (strcmp(name, "shoe_r") == 0) {
+ found_shoe = true;
+ } else {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_shirt && found_shoe);
+ apol_vector_destroy(&v);
+
+ apol_role_query_set_role(sp, q, NULL);
+ apol_role_query_set_type(sp, q, "file");
+ CU_ASSERT(apol_role_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 1);
+ qpol_role_t *r = (qpol_role_t *) apol_vector_get_element(v, 0);
+ const char *name;
+ qpol_role_get_name(qp, r, &name);
+ CU_ASSERT_STRING_EQUAL(name, "object_r");
+ apol_vector_destroy(&v);
+
+ apol_role_query_destroy(&q);
+}
+
+CU_TestInfo role_tests[] = {
+ {"basic query", role_basic}
+ ,
+ {"regex query", role_regex}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int role_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ qp = apol_policy_get_qpol(sp);
+
+ return 0;
+}
+
+int role_cleanup()
+{
+ apol_policy_destroy(&sp);
+ return 0;
+}
diff --git a/libapol/tests/role-tests.h b/libapol/tests/role-tests.h
new file mode 100644
index 0000000..0663f9e
--- /dev/null
+++ b/libapol/tests/role-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libapol role query tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ROLE_TESTS_H
+#define ROLE_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo role_tests[];
+extern int role_init();
+extern int role_cleanup();
+
+#endif
diff --git a/libapol/tests/terule-tests.c b/libapol/tests/terule-tests.c
new file mode 100644
index 0000000..f635e02
--- /dev/null
+++ b/libapol/tests/terule-tests.c
@@ -0,0 +1,130 @@
+/**
+ * @file
+ *
+ * Test the TE rule queries, both semantic and syntactic searches.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <apol/terule-query.h>
+#include <qpol/policy_extend.h>
+#include <stdbool.h>
+
+#define BIN_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.21"
+#define SOURCE_POLICY TEST_POLICIES "/setools-3.3/rules/rules-mls.conf"
+
+static apol_policy_t *bp = NULL;
+static apol_policy_t *sp = NULL;
+
+static void terule_basic_syn(void)
+{
+ apol_terule_query_t *tq = apol_terule_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tq);
+
+ int retval;
+ retval = apol_terule_query_set_rules(sp, tq, QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ apol_vector_t *v = NULL;
+ retval = apol_syn_terule_get_by_query(sp, tq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_PTR_NOT_NULL(v);
+
+ size_t num_trans = 0, num_changes = 0, num_members = 0;
+
+ qpol_policy_t *q = apol_policy_get_qpol(sp);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_syn_terule_t *syn = (const qpol_syn_terule_t *)apol_vector_get_element(v, i);
+ uint32_t rule_type;
+ retval = qpol_syn_terule_get_rule_type(q, syn, &rule_type);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(rule_type == QPOL_RULE_TYPE_TRANS || rule_type == QPOL_RULE_TYPE_CHANGE ||
+ rule_type == QPOL_RULE_TYPE_MEMBER);
+
+ if (rule_type == QPOL_RULE_TYPE_TRANS) {
+ num_trans++;
+ } else if (rule_type == QPOL_RULE_TYPE_CHANGE) {
+ num_changes++;
+ } else if (rule_type == QPOL_RULE_TYPE_MEMBER) {
+ num_members++;
+ }
+ }
+ CU_ASSERT(num_trans == 6 && num_changes == 3 && num_members == 4);
+ apol_vector_destroy(&v);
+
+ retval = apol_terule_query_append_class(sp, tq, "cursor");
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+
+ retval = apol_syn_terule_get_by_query(sp, tq, &v);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+ apol_terule_query_destroy(&tq);
+}
+
+CU_TestInfo terule_tests[] = {
+ {"basic syntactic search", terule_basic_syn}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int terule_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, BIN_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((bp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ if (qpol_policy_build_syn_rule_table(apol_policy_get_qpol(sp)) != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int terule_cleanup()
+{
+ apol_policy_destroy(&bp);
+ apol_policy_destroy(&sp);
+ return 0;
+}
diff --git a/libapol/tests/terule-tests.h b/libapol/tests/terule-tests.h
new file mode 100644
index 0000000..fff497a
--- /dev/null
+++ b/libapol/tests/terule-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libapol terule query tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TERULE_TESTS_H
+#define TERULE_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo terule_tests[];
+extern int terule_init();
+extern int terule_cleanup();
+
+#endif
diff --git a/libapol/tests/user-tests.c b/libapol/tests/user-tests.c
new file mode 100644
index 0000000..2d912c0
--- /dev/null
+++ b/libapol/tests/user-tests.c
@@ -0,0 +1,159 @@
+/**
+ * @file
+ *
+ * Test the user queries.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/user-query.h>
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <stdbool.h>
+
+#define SOURCE_POLICY TEST_POLICIES "/setools/apol/user_mls_testing_policy.conf"
+
+static apol_policy_t *sp = NULL;
+static qpol_policy_t *qp = NULL;
+
+static void user_basic(void)
+{
+ apol_user_query_t *q = apol_user_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(q);
+
+ apol_vector_t *v = NULL;
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 10);
+ apol_vector_destroy(&v);
+
+ apol_user_query_set_role(sp, q, "object_r");
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 10);
+ apol_vector_destroy(&v);
+
+ apol_user_query_set_user(sp, q, "sys");
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+
+ apol_user_query_set_user(sp, q, NULL);
+ apol_user_query_set_role(sp, q, "staff_r");
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 3);
+ bool found_staff = false, found_rick = false, found_simple = false;
+ for (size_t i = 0; i < apol_vector_get_size(v); i++) {
+ qpol_user_t *u = (qpol_user_t *) apol_vector_get_element(v, i);
+ const char *name;
+ qpol_user_get_name(qp, u, &name);
+ if (strcmp(name, "staff_u") == 0) {
+ found_staff = true;
+ } else if (strcmp(name, "rick_u") == 0) {
+ found_rick = true;
+ } else if (strcmp(name, "simple_u") == 0) {
+ found_simple = true;
+ } else {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_staff && found_rick && found_simple);
+ apol_vector_destroy(&v);
+
+ apol_user_query_set_role(sp, q, "not_in_the_policy_r");
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 0);
+ apol_vector_destroy(&v);
+
+ apol_user_query_destroy(&q);
+}
+
+static void user_regex(void)
+{
+ apol_user_query_t *q = apol_user_query_create();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(q);
+ apol_user_query_set_regex(sp, q, 1);
+
+ apol_user_query_set_user(sp, q, "*");
+ apol_vector_t *v = NULL;
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) < 0 && v == NULL);
+
+ apol_user_query_set_user(sp, q, "st");
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 3);
+ bool found_staff = false, found_system = false, found_guest = false;
+ for (size_t i = 0; i < apol_vector_get_size(v); i++) {
+ qpol_user_t *u = (qpol_user_t *) apol_vector_get_element(v, i);
+ const char *name;
+ qpol_user_get_name(qp, u, &name);
+ if (strcmp(name, "staff_u") == 0) {
+ found_staff = true;
+ } else if (strcmp(name, "system_u") == 0) {
+ found_system = true;
+ } else if (strcmp(name, "guest_u") == 0) {
+ found_guest = true;
+ } else {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_staff && found_system && found_guest);
+ apol_vector_destroy(&v);
+
+ apol_user_query_set_user(sp, q, NULL);
+ apol_user_query_set_role(sp, q, "user_r");
+ CU_ASSERT(apol_user_get_by_query(sp, q, &v) == 0);
+ CU_ASSERT(v != NULL && apol_vector_get_size(v) == 3);
+ apol_vector_destroy(&v);
+
+ apol_user_query_destroy(&q);
+}
+
+CU_TestInfo user_tests[] = {
+ {"basic query", user_basic}
+ ,
+ {"regex query", user_regex}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int user_init()
+{
+ apol_policy_path_t *ppath = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, SOURCE_POLICY, NULL);
+ if (ppath == NULL) {
+ return 1;
+ }
+
+ if ((sp = apol_policy_create_from_policy_path(ppath, 0, NULL, NULL)) == NULL) {
+ apol_policy_path_destroy(&ppath);
+ return 1;
+ }
+ apol_policy_path_destroy(&ppath);
+
+ qp = apol_policy_get_qpol(sp);
+
+ return 0;
+}
+
+int user_cleanup()
+{
+ apol_policy_destroy(&sp);
+ return 0;
+}
diff --git a/libapol/tests/user-tests.h b/libapol/tests/user-tests.h
new file mode 100644
index 0000000..d725db4
--- /dev/null
+++ b/libapol/tests/user-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libapol user query tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef USER_TESTS_H
+#define USER_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo user_tests[];
+extern int user_init();
+extern int user_cleanup();
+
+#endif
diff --git a/libpoldiff/Makefile.am b/libpoldiff/Makefile.am
new file mode 100644
index 0000000..906041a
--- /dev/null
+++ b/libpoldiff/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libpoldiff.a libpoldiff.so:
+ $(MAKE) -C src $@
diff --git a/libpoldiff/include/Makefile.am b/libpoldiff/include/Makefile.am
new file mode 100644
index 0000000..02aa6be
--- /dev/null
+++ b/libpoldiff/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = poldiff
diff --git a/libpoldiff/include/poldiff/Makefile.am b/libpoldiff/include/poldiff/Makefile.am
new file mode 100644
index 0000000..7c5a42c
--- /dev/null
+++ b/libpoldiff/include/poldiff/Makefile.am
@@ -0,0 +1,20 @@
+poldiffdir = $(includedir)/poldiff
+
+poldiff_HEADERS = \
+ poldiff.h \
+ attrib_diff.h \
+ avrule_diff.h \
+ bool_diff.h \
+ cat_diff.h \
+ class_diff.h \
+ component_record.h \
+ level_diff.h \
+ range_diff.h \
+ range_trans_diff.h \
+ rbac_diff.h \
+ role_diff.h \
+ terule_diff.h \
+ user_diff.h \
+ type_diff.h \
+ type_map.h \
+ util.h
diff --git a/libpoldiff/include/poldiff/attrib_diff.h b/libpoldiff/include/poldiff/attrib_diff.h
new file mode 100644
index 0000000..27d1f12
--- /dev/null
+++ b/libpoldiff/include/poldiff/attrib_diff.h
@@ -0,0 +1,130 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in attributes.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_ATTRIB_DIFF_H
+#define POLDIFF_ATTRIB_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_attrib poldiff_attrib_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for attributes.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_attrib_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of attribute differences from the attribute
+ * difference summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * attribute difference summary.
+ *
+ * @return A vector of elements of type poldiff_attrib_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_attrib_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a attribute.
+ *
+ * @param diff The policy difference structure associated with the
+ * attribute.
+ * @param attrib The attribute from which to generate the string.
+ *
+ * @return A string representation of attribute difference; the
+ * caller is responsible for free()ing this string. On error, return
+ * NULL and set errno.
+ */
+ extern char *poldiff_attrib_to_string(const poldiff_t * diff, const void *attrib);
+
+/**
+ * Get the name of the attribute from an attribute diff.
+ *
+ * @param attrib The attribute from which to get the name.
+ *
+ * @return Name of the attribute on success and NULL on failure; if
+ * the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_attrib_get_name(const poldiff_attrib_t * attrib);
+
+/**
+ * Get the form of difference from an attribute diff.
+ *
+ * @param attrib The attribute from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_attrib_get_form(const void *attrib);
+
+/**
+ * Get a vector of types added to the attribute.
+ *
+ * @param attrib The attribute diff from which to get the types
+ * vector.
+ *
+ * @return A vector of type names (type char *) that are members of
+ * the attribute in the modified policy. If no types were added the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_attrib_get_added_types(const poldiff_attrib_t * attrib);
+
+/**
+ * Get a vector of types removed from the attribute.
+ *
+ * @param attrib The attribute diff from which to get the types
+ * vector.
+ *
+ * @return A vector of type names (type char *) that are members of
+ * the attribute in the original policy. If no types were removed
+ * the size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_attrib_get_removed_types(const poldiff_attrib_t * attrib);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ATTRIB_DIFF_H */
diff --git a/libpoldiff/include/poldiff/avrule_diff.h b/libpoldiff/include/poldiff/avrule_diff.h
new file mode 100644
index 0000000..454bb9c
--- /dev/null
+++ b/libpoldiff/include/poldiff/avrule_diff.h
@@ -0,0 +1,361 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in av rules
+ * (allow, neverallow, auditallow, dontaudit).
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_AVRULE_DIFF_H
+#define POLDIFF_AVRULE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_avrule poldiff_avrule_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for all AV rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_allow(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for AV auditallow rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_auditallow(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for AV dontaudit rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_dontaudit(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for AV neverallow rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_avrule_get_stats_neverallow(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just allow rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_allow(const poldiff_t * diff);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just auditallow rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_auditallow(const poldiff_t * diff);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just dontaudit rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_dontaudit(const poldiff_t * diff);
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary for just neverallow rules.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_avrule_vector_neverallow(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * any av rule.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule.
+ * @param avrule The av rule from which to generate the string.
+ *
+ * @return A string representation of av rule difference; the caller
+ * is responsible for free()ing this string. On error, return NULL
+ * and set errno.
+ */
+ extern char *poldiff_avrule_to_string(const poldiff_t * diff, const void *avrule);
+
+/**
+ * Get the form of difference from any av rule diff.
+ *
+ * @param avrule The av rule from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error.
+ */
+ extern poldiff_form_e poldiff_avrule_get_form(const void *avrule);
+
+/**
+ * Get the type of rule this is from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the rule type.
+ *
+ * @return One of QPOL_RULE_ALLOW etc, suitable for printing via
+ * apol_rule_type_to_str().
+ */
+ extern uint32_t poldiff_avrule_get_rule_type(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the source type from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_avrule_get_source_type(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the target type from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_avrule_get_target_type(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the object class from an av rule diff.
+ *
+ * @param avrule The av rule from which to get the class.
+ *
+ * @return A string for the class. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_avrule_get_object_class(const poldiff_avrule_t * avrule);
+
+/**
+ * Get the conditional expression from an av rule diff. Note that
+ * this really returns a qpol_cond_t and an apol_policy_t, which may
+ * then be used in other routines such as apol_cond_expr_render().
+ *
+ * @param diff Difference structure from which the rule originated.
+ * @param avrule The av rule from which to get the conditional.
+ * @param cond Reference to the rule's conditional pointer, or NULL
+ * if the rule is not conditional. The caller must not free() this
+ * pointer.
+ * @param which_list Reference to which list the rule belongs, either
+ * 1 if in the true branch, 0 if in false. If the rule is not
+ * conditional then this value will be set to 1.
+ * @param p Reference to the policy from which the conditional
+ * originated, or NULL if the rule is not conditional. The caller
+ * must not destroy this pointer.
+ */
+ extern void poldiff_avrule_get_cond(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p);
+
+/**
+ * Get a vector of permissions unmodified by the av rule. This
+ * vector will be non-empty only if the form is
+ * POLDIFF_FORM_MODIFIED.
+ *
+ * @param avrule The av rule diff from which to get the permissions
+ * vector.
+ *
+ * @return A vector of permissions strings (type char *) that both
+ * policies have. If no permissions are common to both policies then
+ * the size of of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_unmodified_perms(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of permissions added to the av rule. If the rule was
+ * added by modified policy then this vector will hold all of the
+ * permissions.
+ *
+ * @param avrule The av rule diff from which to get the permissions
+ * vector.
+ *
+ * @return A vector of permissions strings (type char *) added to the
+ * rule in the modified policy. If no permissions were added the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_added_perms(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of permissions removed from the av rule. If the rule
+ * was removed by modified policy then this vector will hold all of
+ * the permissions.
+ *
+ * @param avrule The av rule diff from which to get the permissions
+ * vector.
+ *
+ * @return A vector of permissions strings (type char *) removed from
+ * the rule in the original policy. If no permissions were removed
+ * the size of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_removed_perms(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this av rule
+ * difference from the original policy. Note that if the form is
+ * POLDIFF_FORM_ADDED or POLDIFF_FORM_ADD_TYPE then this will return NULL.
+ * Also, if the original policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param avrule The av rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the original policy, or NULL if no numbers are available. Do
+ * not destroy or otherwise modify this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_orig_line_numbers(const poldiff_avrule_t * avrule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this av rule
+ * difference from the modified policy. Note that if the form is
+ * POLDIFF_FORM_REMOVED or POLDIFF_FORM_REMOVE_TYPE then this will return
+ * NULL. Also, if the modified policy is a binary policy or line numbers are
+ * not yet enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param avrule The av rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the modified policy, or NULL if no numbers are available. Do
+ * not destroy or otherwise modify this vector.
+ */
+ extern const apol_vector_t *poldiff_avrule_get_mod_line_numbers(const poldiff_avrule_t * avrule);
+
+/**
+ * Given an av rule difference and a permission name, return a vector
+ * of all line numbers (of type unsigned long) from the original
+ * policy; these line numbers correspond to rules that contributed to
+ * the av rule difference and have the given permission. Be aware
+ * that the vector could be empty if the permission was not found.
+ * Note that if the form is POLDIFF_FORM_ADDED or
+ * POLDIFF_FORM_ADD_TYPE then this will return NULL. Also, if the
+ * original policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ *
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param diff Difference object containing policies to query.
+ * @param avrule The av rule diff from which to get line numbers.
+ * @param perm Permission to look up.
+ *
+ * @return A vector of sorted line numbers (type unsigned long) for
+ * the rule in the original policy, or NULL if no numbers are
+ * available. Note that the vector could be empty if the permission
+ * was not found. It is the caller's responsibility to call
+ * apol_vector_destroy() upon the returned value.
+ */
+ extern apol_vector_t *poldiff_avrule_get_orig_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm);
+
+/**
+ * Given an av rule difference and a permission name, return a vector
+ * of all line numbers (of type unsigned long) from the modified
+ * policy; these line numbers correspond to rules that contributed to
+ * the av rule difference and have the given permission. Be aware
+ * that the vector could be empty if the permission was not found.
+ * Note that if the form is POLDIFF_FORM_REMOVED or
+ * POLDIFF_FORM_REMOVE_TYPE then this will return NULL. Also, if the
+ * modified policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ *
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param diff Difference object containing policies to query.
+ * @param avrule The av rule diff from which to get line numbers.
+ * @param perm Permission to look up.
+ *
+ * @return A vector of sorted line numbers (type unsigned long) for
+ * the rule in the modified policy, or NULL if no numbers are
+ * available. Note that the vector could be empty if the permission
+ * was not found. It is the caller's responsibility to call
+ * apol_vector_destroy() upon the returned value.
+ */
+ extern apol_vector_t *poldiff_avrule_get_mod_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_AVRULE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/bool_diff.h b/libpoldiff/include/poldiff/bool_diff.h
new file mode 100644
index 0000000..e64d33c
--- /dev/null
+++ b/libpoldiff/include/poldiff/bool_diff.h
@@ -0,0 +1,146 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in booleans.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_BOOL_DIFF_H
+#define POLDIFF_BOOL_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_bool poldiff_bool_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for bools.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is as
+ * follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * form POLDIFF_FORM_ADD_TYPE, and number of
+ * POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_bool_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of bool differences from the boolean difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * bool difference summary.
+ *
+ * @return A vector of elements of type poldiff_bool_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_bool_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a bool.
+ *
+ * @param diff The policy difference structure associated with the bool.
+ * @param item The bool from which to generate the string.
+ *
+ * @return A string representation of bool difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_bool_to_string(const poldiff_t * diff, const void *boolean);
+
+/**
+ * Get the number of added bools from a policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * number of added bools.
+ *
+ * @return The number of added bools or 0 if not yet run. (The
+ * number of differences could also be zero.)
+ */
+ extern size_t poldiff_get_num_added_bools(const poldiff_t * diff);
+
+/**
+ * Get the number of removed bools from a policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * number of removed bools.
+ *
+ * @return The number of removed bools or 0 if not yet run. (The
+ * number of differences could also be zero.)
+ */
+ extern size_t poldiff_get_num_removed_bools(const poldiff_t * diff);
+
+/**
+ * Get the number of modified bools from a policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * number of modified bools.
+ *
+ * @return The number of modified bools or 0 if not yet run. (The
+ * number of differences could also be zero.)
+ */
+ extern size_t poldiff_get_num_modified_bools(const poldiff_t * diff);
+
+/**
+ * Get the name of the bool from a bool diff.
+ *
+ * @param diff The policy difference structure associated with the
+ * bool diff.
+ * @param cls The bool from which to get the name.
+ *
+ * @return Name of the bool on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_bool_get_name(const poldiff_bool_t * boolean);
+
+/**
+ * Get the form of difference from a bool diff.
+ *
+ * @param diff The policy difference structure associated with the
+ * bool diff.
+ *
+ * @param cls The bool from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_bool_get_form(const void *boolean);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_BOOL_DIFF_H */
diff --git a/libpoldiff/include/poldiff/cat_diff.h b/libpoldiff/include/poldiff/cat_diff.h
new file mode 100644
index 0000000..c845639
--- /dev/null
+++ b/libpoldiff/include/poldiff/cat_diff.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in categories.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_CAT_DIFF_H
+#define POLDIFF_CAT_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_cat poldiff_cat_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for categories.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_cat_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of user differences from the category difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * category difference summary.
+ *
+ * @return A vector of elements of type poldiff_cat_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_cat_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a category.
+ *
+ * @param diff The policy difference structure associated with the category.
+ * @param cat The category from which to generate the string.
+ *
+ * @return A string representation of category difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_cat_to_string(const poldiff_t * diff, const void *cat);
+
+/**
+ * Get the name of the category from a category diff.
+ *
+ * @param cat The category from which to get the name.
+ *
+ * @return Name of the category on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_cat_get_name(const poldiff_cat_t * cat);
+
+/**
+ * Get the form of difference from a category diff.
+ *
+ * @param cat The category from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_cat_get_form(const void *cat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CAT_DIFF_H */
diff --git a/libpoldiff/include/poldiff/class_diff.h b/libpoldiff/include/poldiff/class_diff.h
new file mode 100644
index 0000000..0d89924
--- /dev/null
+++ b/libpoldiff/include/poldiff/class_diff.h
@@ -0,0 +1,222 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in classes
+ * and commons.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_CLASS_DIFF_H
+#define POLDIFF_CLASS_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+/******************** object classes ********************/
+
+ typedef struct poldiff_class poldiff_class_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for object classes.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_class_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of class differences from the class difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * class difference summary.
+ *
+ * @return A vector of elements of type poldiff_class_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_class_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * an object class.
+ *
+ * @param diff The policy difference structure associated with the class.
+ * @param cls The class from which to generate the string.
+ *
+ * @return A string representation of class difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_class_to_string(const poldiff_t * diff, const void *cls);
+
+/**
+ * Get the name of the class from a class diff.
+ *
+ * @param cls The class from which to get the name.
+ *
+ * @return Name of the class on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_class_get_name(const poldiff_class_t * cls);
+
+/**
+ * Get the form of difference from a class diff.
+ *
+ * @param cls The class from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_class_get_form(const void *cls);
+
+/**
+ * Get a vector of permissions added to the class.
+ *
+ * @param cls The class diff from which to get the permission vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the class in the modified policy. If no permissions
+ * were added the size of the returned vector will be 0. The caller
+ * must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_class_get_added_perms(const poldiff_class_t * cls);
+
+/**
+ * Get a vector of permissions removed from the class.
+ *
+ * @param cls The class diff from which to get the permission vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the class in the original policy. If no permissions
+ * were removed the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_class_get_removed_perms(const poldiff_class_t * cls);
+
+/******************** common classes ********************/
+
+ typedef struct poldiff_common poldiff_common_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for common classes.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_common_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of commons differences from the commons difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * commons difference summary.
+ *
+ * @return A vector of elements of type poldiff_common_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_common_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a common class.
+ *
+ * @param diff The policy difference structure associated with the
+ * common.
+ * @param common The common from which to generate the string.
+ *
+ * @return A string representation of common difference; the caller
+ * is responsible for free()ing this string. On error, return NULL
+ * and set errno.
+ */
+ extern char *poldiff_common_to_string(const poldiff_t * diff, const void *common);
+
+/**
+ * Get the name of the common from a common diff.
+ *
+ * @param common The common from which to get the name.
+ *
+ * @return Name of the common on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_common_get_name(const poldiff_common_t * common);
+
+/**
+ * Get the form of difference from a common diff.
+ *
+ * @param common The common from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_common_get_form(const void *common);
+
+/**
+ * Get a vector of permissions added to the common.
+ *
+ * @param common The common diff from which to get the permission
+ * vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the common in the modified policy. If no permissions
+ * were added the size of the returned vector will be 0. The caller
+ * must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_common_get_added_perms(const poldiff_common_t * common);
+
+/**
+ * Get a vector of permissions removed from the common.
+ *
+ * @param common The common diff from which to get the permission
+ * vector.
+ *
+ * @return A vector of permission names (type char *) that are
+ * assigned to the common in the original policy. If no permissions
+ * were removed the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_common_get_removed_perms(const poldiff_common_t * common);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CLASS_DIFF_H */
diff --git a/libpoldiff/include/poldiff/component_record.h b/libpoldiff/include/poldiff/component_record.h
new file mode 100644
index 0000000..0104845
--- /dev/null
+++ b/libpoldiff/include/poldiff/component_record.h
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Typedefs to aid declaring function pointers for callbacks
+ * extracted from component records.
+ *
+ * This file also declares functions to extract the callbacks for
+ * component records. This implements a form of polymorphism so that
+ * one can operate on component records and not care about the
+ * library dependent implementation.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Mark Goldman mgoldman@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_COMPONENT_RECORD_H
+#define POLDIFF_COMPONENT_RECORD_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Callback function signature for getting an array of statistics for the
+ * number of differences of each form for a given item.
+ * @param diff The policy difference structure from which to get the stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is as follows:
+ * number of items of form POLDIFF_FORM_ADDED, number of POLDIFF_FORM_REMOVED,
+ * number of POLDIFF_FORM_MODIFIED, number of form POLDIFF_FORM_ADD_TYPE, and
+ * number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ typedef void (*poldiff_get_item_stats_fn_t) (const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Callback function signature for getting a vector of all result
+ * items that were created during a call to poldiff_do_item_diff().
+ * @param diff Policy diff structure containing results.
+ * @return A vector of result items, which the caller may not modify
+ * or destroy. Upon error, return null and set errno.
+ */
+ typedef const apol_vector_t *(*poldiff_get_result_items_fn_t) (const poldiff_t * diff);
+
+/**
+ * Callback function signature for getting the form of difference for
+ * a result item.
+ * @param diff The policy difference structure associated with the item.
+ * @param item The item from which to get the form.
+ * @return One of the POLDIFF_FORM_* enumeration.
+ */
+ typedef poldiff_form_e(*poldiff_item_get_form_fn_t) (const void *item);
+
+/**
+ * Callback function signature for obtaining a newly allocated string
+ * representation of a difference item.
+ * @param diff The policy difference structure associated with the item.
+ * @param item The item from which to generate the string.
+ * @return Expected return value from this function is a newly allocated
+ * string representation of the item or null on error; if the call fails,
+ * it is expected to set errno.
+ */
+ typedef char *(*poldiff_item_to_string_fn_t) (const poldiff_t * diff, const void *item);
+
+ typedef struct poldiff_component_record poldiff_component_record_t;
+
+/**
+ * Get the poldiff_component_record_t for a particular policy
+ * component. Consult this record for function pointers, so as to
+ * achieve a limited form of polymorphism.
+ *
+ * @param which Flag (as defined in <poldiff/poldiff.h>) indicating
+ * which component to look up.
+ * @return A poldiff_component_record_t associated with the component
+ * or NULL if not found.
+ */
+ extern const poldiff_component_record_t *poldiff_get_component_record(uint32_t which);
+
+/**
+ * Get the function that will return the form from a
+ * poldiff_component_record_t.
+ *
+ * @param comp Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_item_get_form_fn_t poldiff_component_record_get_form_fn(const poldiff_component_record_t * comp);
+
+/**
+ * Get the function that will return the to_string from a
+ * poldiff_component_record_t.
+ *
+ * @param diff Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_item_to_string_fn_t poldiff_component_record_get_to_string_fn(const poldiff_component_record_t * diff);
+
+/**
+ * Get the function that will return the item_stats from a
+ * poldiff_component_record_t.
+ *
+ * @param diff Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_get_item_stats_fn_t poldiff_component_record_get_stats_fn(const poldiff_component_record_t * diff);
+
+/**
+ * Get the function that will return the results from a
+ * poldiff_component_record_t.
+ *
+ * @param diff Pointer to the component to extract the named virtual
+ * function.
+ *
+ * @return Function pointer relating to the passed in record key, or
+ * NULL upon error.
+ */
+ extern poldiff_get_result_items_fn_t poldiff_component_record_get_results_fn(const poldiff_component_record_t * diff);
+
+/**
+ * Get the function that will return the label from a
+ * poldiff_component_record_t. This label describes the policy
+ * component (e.g., "attribute" or "AVrule dontaudit").
+ *
+ * @param diff Pointer to the component to extract named the label.
+ *
+ * @return Label describing the policy component record. Do not
+ * modify this string.
+ */
+ extern const char *poldiff_component_record_get_label(const poldiff_component_record_t * diff);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpoldiff/include/poldiff/level_diff.h b/libpoldiff/include/poldiff/level_diff.h
new file mode 100644
index 0000000..e9eba23
--- /dev/null
+++ b/libpoldiff/include/poldiff/level_diff.h
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in levels.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_LEVEL_DIFF_H
+#define POLDIFF_LEVEL_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_level poldiff_level_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for levels.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_level_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of level differences from the level difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * level difference summary.
+ *
+ * @return A vector of elements of type poldiff_level_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_level_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a level.
+ *
+ * @param diff The policy difference structure associated with the level.
+ * @param level The level from which to generate the string.
+ *
+ * @return A string representation of level difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_level_to_string(const poldiff_t * diff, const void *level);
+
+/**
+ * Allocate and return a string rendering of a poldiff_level_t,
+ * suitable for embedding within some other component's to_string
+ * function (e.g., a user's default level).
+ *
+ * @param diff Poldiff object, for error handling.
+ * @param level Level diff object to render.
+ *
+ * @return String rendering of level, or NULL upon error. Caller must
+ * free() string afterwards.
+ */
+ char *poldiff_level_to_string_brief(const poldiff_t * diff, const poldiff_level_t * level);
+
+/**
+ * Get the name of the level (i.e., the sensitivity) from a level diff.
+ *
+ * @param level The level from which to get the name.
+ *
+ * @return Name of the level on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_level_get_name(const poldiff_level_t * level);
+
+/**
+ * Get the form of difference from a level diff.
+ *
+ * @param level The level from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_level_get_form(const void *level);
+
+/**
+ * Get a vector of unmodified categories from the level. These will
+ * be sorted in the same order as given by the original policy.
+ *
+ * @param level The level diff from which to get the category vector.
+ *
+ * @return A vector of category names (type char *) that are assigned to
+ * the level in the original policy. If no categories were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_level_get_unmodified_cats(const poldiff_level_t * level);
+
+/**
+ * Get a vector of categories added to the level. These will be
+ * sorted in the same order as given by the modified policy. If the
+ * level was added by the modified policy then this vector will hold
+ * all of the categories.
+ *
+ * @param level The level diff from which to get the categories.
+ *
+ * @return A vector of category names (type char *) that are assigned
+ * to the level in the modified policy. If no categories were added
+ * the size of the returned vector will be 0. The caller must not
+ * modify this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_level_get_added_cats(const poldiff_level_t * level);
+
+/**
+ * Get a vector of categories removed from the level. These will be
+ * sorted in the same order as given by the original policy. If the
+ * level was removed by the modified policy then this vector will
+ * hold all of the categories.
+ *
+ * @param level The level diff from which to get the category vector.
+ *
+ * @return A vector of category names (type char *) that are assigned to
+ * the level in the original policy. If no categories were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_level_get_removed_cats(const poldiff_level_t * level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_LEVEL_DIFF_H */
diff --git a/libpoldiff/include/poldiff/poldiff.h b/libpoldiff/include/poldiff/poldiff.h
new file mode 100644
index 0000000..9047600
--- /dev/null
+++ b/libpoldiff/include/poldiff/poldiff.h
@@ -0,0 +1,218 @@
+/**
+ * @file
+ * Public interface for computing semantic policy differences
+ * between two policies. The user loads two policies, the "original"
+ * and "modified" policies, and then calls poldiff_create() to obtain
+ * a poldiff object. Next call poldiff_run() to actually execute the
+ * differencing algorithm. Results are retrieved via
+ * poldiff_get_type_vector(), poldiff_get_avrule_vector(), and so
+ * forth.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_POLDIFF_H
+#define POLDIFF_POLDIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/policy.h>
+#include <apol/policy-query.h>
+#include <apol/vector.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+ typedef struct poldiff poldiff_t;
+
+/**
+ * Form of a difference. This enumeration describes the kind of change
+ * in a policy component or rule from policy1 to policy2.
+ * Differences can be additions (item present only in policy2),
+ * removals (item present only in policy1) or a modification
+ * (item present in both policies with different semantic meaning).
+ * For rules there are two more options - added or removed due to a
+ * type being added or removed; these forms differentiate these cases
+ * from those of added/removed rules where the types exist in both policies.
+ */
+ typedef enum poldiff_form
+ {
+ /** only for error conditions */
+ POLDIFF_FORM_NONE,
+ /** item was added - only in policy 2 */
+ POLDIFF_FORM_ADDED,
+ /** item was removed - only in policy 1 */
+ POLDIFF_FORM_REMOVED,
+ /** item was modified - in both policies but with different meaning */
+ POLDIFF_FORM_MODIFIED,
+ /** item was added due to an added type - for rules only */
+ POLDIFF_FORM_ADD_TYPE,
+ /** item was removed due to a removed type - for rules only */
+ POLDIFF_FORM_REMOVE_TYPE
+ } poldiff_form_e;
+
+ typedef void (*poldiff_handle_fn_t) (void *arg, const poldiff_t * diff, int level, const char *fmt, va_list va_args);
+
+#include <poldiff/attrib_diff.h>
+#include <poldiff/avrule_diff.h>
+#include <poldiff/cat_diff.h>
+#include <poldiff/bool_diff.h>
+#include <poldiff/class_diff.h>
+#include <poldiff/level_diff.h>
+#include <poldiff/range_diff.h>
+#include <poldiff/range_trans_diff.h>
+#include <poldiff/rbac_diff.h>
+#include <poldiff/role_diff.h>
+#include <poldiff/terule_diff.h>
+#include <poldiff/type_diff.h>
+#include <poldiff/user_diff.h>
+#include <poldiff/type_map.h>
+#include <poldiff/util.h>
+
+/* NOTE: while defined OCONS are not currently supported */
+#define POLDIFF_DIFF_CLASSES 0x00000001U
+#define POLDIFF_DIFF_COMMONS 0x00000002U
+#define POLDIFF_DIFF_TYPES 0x00000004U
+#define POLDIFF_DIFF_ATTRIBS 0x00000008U
+#define POLDIFF_DIFF_ROLES 0x00000010U
+#define POLDIFF_DIFF_USERS 0x00000020U
+#define POLDIFF_DIFF_BOOLS 0x00000040U
+#define POLDIFF_DIFF_LEVELS 0x00000080U
+#define POLDIFF_DIFF_CATS 0x00000100U
+#define POLDIFF_DIFF_ROLE_ALLOWS 0x00000800U
+#define POLDIFF_DIFF_ROLE_TRANS 0x00001000U
+#define POLDIFF_DIFF_RANGE_TRANS 0x00002000U
+#define POLDIFF_DIFF_AVALLOW 0x10000000U
+#define POLDIFF_DIFF_AVAUDITALLOW 0x20000000U
+#define POLDIFF_DIFF_AVDONTAUDIT 0x40000000U
+#define POLDIFF_DIFF_AVNEVERALLOW 0x80000000U
+#define POLDIFF_DIFF_TECHANGE 0x01000000U
+#define POLDIFF_DIFF_TEMEMBER 0x02000000U
+#define POLDIFF_DIFF_TETRANS 0x04000000U
+
+#define POLDIFF_DIFF_TERULES_COMPAT 0x00000400U /**< deprecated */
+#define POLDIFF_DIFF_AVRULES_COMPAT 0x00000200U /**< deprecated */
+
+#define POLDIFF_DIFF_AVRULES (POLDIFF_DIFF_AVALLOW | POLDIFF_DIFF_AVNEVERALLOW | POLDIFF_DIFF_AVAUDITALLOW | POLDIFF_DIFF_AVDONTAUDIT)
+#define POLDIFF_DIFF_TERULES (POLDIFF_DIFF_TEMEMBER | POLDIFF_DIFF_TECHANGE | POLDIFF_DIFF_TETRANS)
+/*
+ * Add ocons here and modify POLDIFF_DIFF_OCONS below
+ * #define POLDIFF_DIFF_ *
+ */
+#define POLDIFF_DIFF_SYMBOLS (POLDIFF_DIFF_CLASSES|POLDIFF_DIFF_COMMONS|POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_USERS|POLDIFF_DIFF_BOOLS)
+#define POLDIFF_DIFF_RULES (POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_RBAC (POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_MLS (POLDIFF_DIFF_LEVELS|POLDIFF_DIFF_CATS|POLDIFF_DIFF_RANGE_TRANS)
+#define POLDIFF_DIFF_OCONS 0
+#define POLDIFF_DIFF_REMAPPED (POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_TRANS|POLDIFF_DIFF_RANGE_TRANS|POLDIFF_DIFF_OCONS)
+#define POLDIFF_DIFF_ALL (POLDIFF_DIFF_SYMBOLS|POLDIFF_DIFF_RULES|POLDIFF_DIFF_MLS|POLDIFF_DIFF_OCONS)
+
+/**
+ * Allocate and initialize a new policy difference structure. This
+ * function takes ownership of the supplied policies and will handle
+ * their destruction upon poldiff_destroy(). The poldiff object will
+ * be responsible for rebuilding the policy (such as if neverallows
+ * are requested). It is still safe to access elements within the
+ * policies, but avoid making changes to the policy while the poldiff
+ * object still exists.
+ * @param orig_policy The original policy.
+ * @param mod_policy The new (modified) policy.
+ * @param fn Function to be called by the error handler. If NULL
+ * then write messages to standard error.
+ * @param callback_arg Argument for the callback.
+ * @return a newly allocated and initialized difference structure or
+ * NULL on error; if the call fails, errno will be set.
+ * The caller is responsible for calling poldiff_destroy() to free
+ * memory used by this structure.
+ */
+ extern poldiff_t *poldiff_create(apol_policy_t * orig_policy,
+ apol_policy_t * mod_policy, poldiff_handle_fn_t fn, void *callback_arg);
+
+/**
+ * Free all memory used by a policy difference structure and set it to NULL.
+ * @param diff Reference pointer to the difference structure to destroy.
+ * This pointer will be set to NULL. (If already NULL, function is a no-op.)
+ */
+ extern void poldiff_destroy(poldiff_t ** diff);
+
+/**
+ * Run the difference algorithm for the selected policy components/rules.
+ * @param diff The policy difference structure for which to compute
+ * the differences.
+ * @param flags Bit-wise or'd set of POLDIFF_DIFF_* from above indicating
+ * the components and rules for which to compute the difference.
+ * If an item has already been computed the flag for that item is ignored.
+ * @return 0 on success or < 0 on error; if the call fails, errno will
+ * be set and the only defined operation on the difference structure is
+ * poldiff_destroy().
+ */
+ extern int poldiff_run(poldiff_t * diff, uint32_t flags);
+
+/**
+ * Determine if a particular policy component/rule diff was actually
+ * run yet or not.
+ * @param diff The policy difference structure for which to compute
+ * the differences.
+ * @param flags Bit-wise or'd set of POLDIFF_DIFF_* from above indicating
+ * which components/rules diffs were run.
+ * @return 1 if all indicated diffs were run, 0 if any were not, < 0
+ * on error.
+ */
+ extern int poldiff_is_run(const poldiff_t * diff, uint32_t flags);
+
+/**
+ * Get a total of the differences of each form for a given item (or set
+ * of items).
+ * @param diff The policy difference structure from which to get the stats.
+ * @param flags Bit-wise or'd set of POLDIFF_DIFF_* from above indicating
+ * the items for which to get the total differences. If more that one bit
+ * is set differences of the same form are totaled for all specified items.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is as follows:
+ * number of items of form POLDIFF_FORM_ADDED, number of POLDIFF_FORM_REMOVED,
+ * number of POLDIFF_FORM_MODIFIED, number of form POLDIFF_FORM_ADD_TYPE, and
+ * number of POLDIFF_FORM_REMOVE_TYPE.
+ * @return 0 on success and < 0 on error; if the call fails, errno will be set.
+ */
+ extern int poldiff_get_stats(const poldiff_t * diff, uint32_t flags, size_t stats[5]);
+
+/**
+ * Enable line numbers for all rule differences. If not called, line
+ * numbers will not be available when displaying differences. This
+ * function is safe to call multiple times and will have no effect
+ * after the first time. It also has no effect if one policy (or
+ * both of them) does not support line numbers. Be aware that line
+ * numbers will need to be re-enabled each time poldiff_run() is
+ * called.
+ *
+ * @param diff The policy difference structure.
+ *
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the difference structure should be destroyed.
+ */
+ extern int poldiff_enable_line_numbers(poldiff_t * diff);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_POLDIFF_H */
diff --git a/libpoldiff/include/poldiff/range_diff.h b/libpoldiff/include/poldiff/range_diff.h
new file mode 100644
index 0000000..fcdd846
--- /dev/null
+++ b/libpoldiff/include/poldiff/range_diff.h
@@ -0,0 +1,129 @@
+/**
+ * @file
+ * Public interface for returning the differences in MLS ranges.
+ * Obtain a range difference object from its respective policy
+ * component (e.g., a user's assigned range). The individual level
+ * difference querying functions are in the level_diff.h header.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_RANGE_DIFF_H
+#define POLDIFF_RANGE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/mls-query.h>
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_range poldiff_range_t;
+
+/**
+ * Allocate and return a string that represents the differences
+ * encoded by the given range. The returned string is suitable for
+ * embedding within another item's to_string() display.
+ *
+ * @param diff Poldiff diff structure containing policies.
+ * @param range Range object to render.
+ *
+ * @return Rendered string, or NULL upon error. Caller must free()
+ * string afterwards.
+ */
+ char *poldiff_range_to_string_brief(const poldiff_t * diff, const poldiff_range_t * range);
+
+/**
+ * Get the vector of level differences from a range diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type poldiff_level_t, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+ extern apol_vector_t *poldiff_range_get_levels(const poldiff_range_t * range);
+
+/**
+ * Get the original item's range. This could represent a user's
+ * original assigned range or the original target range for a
+ * range_transition. If there was no original range (such as for
+ * items that are added) then this returns NULL.
+ *
+ * @param range Range object to query.
+ *
+ * @return Original range, or NULL upon error or no range available.
+ * The caller should <b>not</b> modify the returned object.
+ */
+ extern const apol_mls_range_t *poldiff_range_get_original_range(const poldiff_range_t * range);
+
+/**
+ * Get the modified item's range. This could represent a user's
+ * modified assigned range or the modified target range for a
+ * range_transition. If there was no original range (such as for
+ * items that are removed) then this returns NULL.
+ *
+ * @param range Range object to query.
+ *
+ * @return Modified range, or NULL upon error or no range available.
+ * The caller should <b>not</b> modify the returned object.
+ */
+ extern const apol_mls_range_t *poldiff_range_get_modified_range(const poldiff_range_t * range);
+
+/**
+ * Get the vector of categories added to the minimum set from a range
+ * diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type string, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+
+ extern apol_vector_t *poldiff_range_get_min_added_cats(const poldiff_range_t * range);
+
+/**
+ * Get the vector of categories removed from the minimum set from a
+ * range diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type string, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+ extern apol_vector_t *poldiff_range_get_min_removed_cats(const poldiff_range_t * range);
+
+/**
+ * Get the vector of unmodified categories of the minimum set from a
+ * range diffence object.
+ *
+ * @param range Range object to query.
+ *
+ * @return A vector of elements of type string, or NULL on
+ * error. The caller should <b>not</b> modify the returned vector.
+ */
+ extern apol_vector_t *poldiff_range_get_min_unmodified_cats(const poldiff_range_t * range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/range_trans_diff.h b/libpoldiff/include/poldiff/range_trans_diff.h
new file mode 100644
index 0000000..ee6dc5b
--- /dev/null
+++ b/libpoldiff/include/poldiff/range_trans_diff.h
@@ -0,0 +1,140 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in range
+ * transition rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_RANGETRANS_DIFF_H
+#define POLDIFF_RANGETRANS_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/mls-query.h>
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_range_trans poldiff_range_trans_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for range transition rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_range_trans_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of range transition differences from the policy
+ * difference structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * differences.
+ *
+ * @return A vector of elements of type poldiff_range_trans_t, or
+ * NULL on error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_range_trans_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a range transition rule.
+ *
+ * @param diff The policy difference structure associated with the rule.
+ * @param range_trans The range transition diff from which to
+ * generate the string.
+ *
+ * @return A string representation of the rule difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_range_trans_to_string(const poldiff_t * diff, const void *range_trans);
+
+/**
+ * Get the name of the source type from a range transition diff.
+ *
+ * @param range_trans The rule from which to get the source type.
+ *
+ * @return Name of the source type on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_range_trans_get_source_type(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the name of the target type from a range transition diff.
+ *
+ * @param range_trans The rule from which to get the target type.
+ *
+ * @return Name of the target type on success and NULL on failure; if
+ * the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_range_trans_get_target_type(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the name of the target object class from a range transition
+ * diff.
+ *
+ * @param range_trans The rule from which to get the target class.
+ *
+ * @return Name of the target class on success and NULL on failure;
+ * if the call fails, errno will be set. The caller should not free
+ * the returned string.
+ */
+ extern const char *poldiff_range_trans_get_target_class(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the change in target range from a range transition diff.
+ *
+ * @param range_trans The rule from which to get the target range.
+ *
+ * @return Rule's target range on success, or NULL upon error or if
+ * there is no difference in range. Do not modify the returned value.
+ */
+ extern const poldiff_range_t *poldiff_range_trans_get_range(const poldiff_range_trans_t * range_trans);
+
+/**
+ * Get the form of difference from a range transition diff.
+ *
+ * @param range_trans The range transition rule from which to get the
+ * difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_range_trans_get_form(const void *range_trans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGETRANS_DIFF_H */
diff --git a/libpoldiff/include/poldiff/rbac_diff.h b/libpoldiff/include/poldiff/rbac_diff.h
new file mode 100644
index 0000000..200beb3
--- /dev/null
+++ b/libpoldiff/include/poldiff/rbac_diff.h
@@ -0,0 +1,251 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in role
+ * allow rules and role_transition rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_RBAC_DIFF_H
+#define POLDIFF_RBAC_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_role_allow poldiff_role_allow_t;
+ typedef struct poldiff_role_trans poldiff_role_trans_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for role allow rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_role_allow_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of role allow differences from the policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * differences.
+ *
+ * @return A vector of elements of type poldiff_role_allow_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_role_allow_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a role allow rule.
+ *
+ * @param diff The policy difference structure associated with the rule.
+ * @param role_allow The role from which to generate the string.
+ *
+ * @return A string representation of the rule difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_role_allow_to_string(const poldiff_t * diff, const void *role_allow);
+
+/**
+ * Get the name of the source role from a role allow diff.
+ *
+ * @param role_allow The rule allow from which to get the source role name.
+ *
+ * @return Name of the source role on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_allow_get_name(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get the form of difference from a role allow diff.
+ *
+ * @param role_allow The role allow rule from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_role_allow_get_form(const void *role_allow);
+
+/**
+ * Get a vector of roles unmodified by the role allow rule. The
+ * vector will be non-empty only if the form is
+ * POLDIFF_FORM_MODIFIED.
+ *
+ * @param role_allow The role allow diff from which to get the roles
+ * vector.
+ *
+ * @return A vector of role names (type char *) that are in both
+ * policies. If no roles are common to both policies then the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. The caller must not destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_role_allow_get_unmodified_roles(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get a vector of roles added to the role allow rule. If the role
+ * allow was added by the modified policy then this vector will hold
+ * all of the roles.
+ *
+ * @param role_allow The role allow diff from which to get the roles
+ * vector.
+ *
+ * @return A vector of role names (type char *) that are allowed to
+ * the role in the modified policy. If no roles were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector.
+ */
+ extern const apol_vector_t *poldiff_role_allow_get_added_roles(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get a vector of roles removed from the role allow rule. If the
+ * role allow was removed by the modified policy then this vector
+ * will hold all of the roles.
+ *
+ * @param role_allow The role allow diff from which to get the roles
+ * vector.
+ *
+ * @return A vector of role names (type char *) that are allowed to
+ * the role in the original policy. If no roles were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector.
+ */
+ extern const apol_vector_t *poldiff_role_allow_get_removed_roles(const poldiff_role_allow_t * role_allow);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for role_transition rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_role_trans_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of role_transition differences from the policy difference
+ * structure.
+ *
+ * @param diff The policy difference structure from which to get the
+ * differences.
+ *
+ * @return A vector of elements of type poldiff_role_trans_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_role_trans_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a role_transition rule.
+ *
+ * @param diff The policy difference structure associated with the rule.
+ * @param role_trans The rule from which to generate the string.
+ *
+ * @return A string representation of the rule difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_role_trans_to_string(const poldiff_t * diff, const void *role_trans);
+
+/**
+ * Get the name of the source role from a role_transition difference.
+ *
+ * @param role_trans The rule from which to get the source role.
+ *
+ * @return Name of the source role on success and NULL on failure;
+ * if the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_trans_get_source_role(const poldiff_role_trans_t * role_trans);
+
+/**
+ * Get the name of the target type from a role_transition difference.
+ *
+ * @param role_trans The rule from which to get the target type.
+ *
+ * @return Name of the target type on success and NULL on failure;
+ * if the call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_trans_get_target_type(const poldiff_role_trans_t * role_trans);
+
+/**
+ * Get the form of difference from a role_transition diff.
+ *
+ * @param role_trans The role_transition rule from which to get the
+ * difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_role_trans_get_form(const void *role_trans);
+
+/**
+ * Get the original default type from a role_transition diff. Note that
+ * if this rule was added (form POLDIFF_FORM_ADDED or POLDIFF_FORM_ADD_TYPE)
+ * then the return value will be NULL.
+ *
+ * @param role_trans The role_transition rule from which to get the
+ * original default role.
+ *
+ * @return Name of the original default role. If there was no original role or
+ * upon error then return NULL. The caller should not free the returned
+ * string.
+ */
+ extern const char *poldiff_role_trans_get_original_default(const poldiff_role_trans_t * role_trans);
+
+/**
+ * Get the modified default type from a role_transition diff. Note that if
+ * this rule was removed (form POLDIFF_FORM_REMOVED or
+ * POLDIFF_FORM_REMOVE_TYPE) then the return value will be NULL.
+ *
+ * @param role_trans The role_transition rule from which to get the
+ * modified default role.
+ *
+ * @return Name of the modified default role. If there was no modified role or
+ * upon error then return NULL. The caller should not free the returned
+ * string.
+ */
+ extern const char *poldiff_role_trans_get_modified_default(const poldiff_role_trans_t * role_trans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RBAC_DIFF_H */
diff --git a/libpoldiff/include/poldiff/role_diff.h b/libpoldiff/include/poldiff/role_diff.h
new file mode 100644
index 0000000..9526cb5
--- /dev/null
+++ b/libpoldiff/include/poldiff/role_diff.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in roles.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_ROLE_DIFF_H
+#define POLDIFF_ROLE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_role poldiff_role_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for roles.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_role_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of role differences from the role difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * role difference summary.
+ *
+ * @return A vector of elements of type poldiff_role_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_role_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a role.
+ *
+ * @param diff The policy difference structure associated with the role.
+ * @param role The role from which to generate the string.
+ *
+ * @return A string representation of role difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_role_to_string(const poldiff_t * diff, const void *role);
+
+/**
+ * Get the name of the role from a role diff.
+ *
+ * @param role The role from which to get the name.
+ *
+ * @return Name of the role on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_role_get_name(const poldiff_role_t * role);
+
+/**
+ * Get the form of difference from a role diff.
+ *
+ * @param role The role from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_role_get_form(const void *role);
+
+/**
+ * Get a vector of types added to the role.
+ *
+ * @param role The role diff from which to get the types vector.
+ *
+ * @return a vector of type names (type char *) that are allowed to
+ * the role in the modified policy. If no types were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_role_get_added_types(const poldiff_role_t * role);
+
+/**
+ * Get a vector of types removed from the role.
+ *
+ * @param role The role diff from which to get the types vector.
+ *
+ * @return A vector of type names (type char *) that are allowed to
+ * the role in the original policy. If no types were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_role_get_removed_types(const poldiff_role_t * role);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ROLE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/terule_diff.h b/libpoldiff/include/poldiff/terule_diff.h
new file mode 100644
index 0000000..6d09e9d
--- /dev/null
+++ b/libpoldiff/include/poldiff/terule_diff.h
@@ -0,0 +1,262 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in te rules
+ * (type_transition, type_change, type_member).
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_TERULE_DIFF_H
+#define POLDIFF_TERULE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_terule poldiff_terule_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for TE type_change rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_terule_get_stats_change(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for TE type_member rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_terule_get_stats_member(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for TE type_transition rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_terule_get_stats_trans(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary for just type_change rules.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_terule_vector_change(const poldiff_t * diff);
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary for just type_member rules.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_terule_vector_member(const poldiff_t * diff);
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary for just type_transition rules.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_terule_vector_trans(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a te rule.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule.
+ * @param terule The te rule from which to generate the string.
+ *
+ * @return A string representation of te rule difference; the caller
+ * is responsible for free()ing this string. On error, return NULL
+ * and set errno.
+ */
+ extern char *poldiff_terule_to_string(const poldiff_t * diff, const void *terule);
+
+/**
+ * Get the form of difference from a te rule diff.
+ *
+ * @param terule The te rule from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error.
+ */
+ extern poldiff_form_e poldiff_terule_get_form(const void *terule);
+
+/**
+ * Get the type of rule this is from a te rule diff.
+ *
+ * @param avrule The av rule from which to get the rule type.
+ *
+ * @return One of QPOL_RULE_TYPE_TRANS etc, suitable for printing via
+ * apol_rule_type_to_str().
+ */
+ extern uint32_t poldiff_terule_get_rule_type(const poldiff_terule_t * terule);
+
+/**
+ * Get the source type from a te rule diff.
+ *
+ * @param terule The te rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_source_type(const poldiff_terule_t * terule);
+
+/**
+ * Get the target type from a te rule diff.
+ *
+ * @param terule The te rule from which to get the type.
+ *
+ * @return A string for the type. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_target_type(const poldiff_terule_t * terule);
+
+/**
+ * Get the object class from a te rule diff.
+ *
+ * @param terule The te rule from which to get the class.
+ *
+ * @return A string for the class. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_object_class(const poldiff_terule_t * terule);
+
+/**
+ * Get the conditional expression from a te rule diff. Note that
+ * this really returns a qpol_cond_t and an apol_policy_t, which may
+ * then be used in other routines such as apol_cond_expr_render().
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param terule The te rule from which to get the conditional.
+ * @param cond Reference to the rule's conditional pointer, or NULL
+ * if the rule is not conditional. The caller must not free() this
+ * pointer.
+ * @param which_list Reference to which list the rule belongs, either
+ * 1 if in the true branch, 0 if in false. If the rule is not
+ * conditional then this value will be set to 1.
+ * @param p Reference to the policy from which the conditional
+ * originated, or NULL if the rule is not conditional. The caller
+ * must not destroy this pointer.
+ */
+ extern void poldiff_terule_get_cond(const poldiff_t * diff, const poldiff_terule_t * terule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p);
+
+/**
+ * Get the original default type for this type rule. Note that if
+ * this rule was added (form POLDIFF_FORM_ADDED or
+ * POLDIFF_FORM_ADD_TYPE) then the return value will be NULL.
+ *
+ * @param terule The te rule diff from which to get the original
+ * default type.
+ *
+ * @return Original default type. If there was no original type or
+ * upon error then return NULL. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_original_default(const poldiff_terule_t * terule);
+
+/**
+ * Get the modified default type for this type rule. Note that if
+ * this rule was removed (form POLDIFF_FORM_REMOVED or
+ * POLDIFF_FORM_REMOVE_TYPE) then the return value will be NULL.
+ *
+ * @param terule The te rule diff from which to get the modified
+ * default type.
+ *
+ * @return Modified default type. If there was no modified type or
+ * upon error then return NULL. <b>Do not free() this string.</b>
+ */
+ extern const char *poldiff_terule_get_modified_default(const poldiff_terule_t * terule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this te rule
+ * difference from the original policy. Note that if the form is
+ * POLDIFF_FORM_ADDED or POLDIFF_FORM_ADD_TYPE then this will return NULL.
+ * Also, if the original policy is a binary policy or line numbers are not yet
+ * enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param terule The te rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the original policy, or NULL if no numbers are available.
+ */
+ extern apol_vector_t *poldiff_terule_get_orig_line_numbers(const poldiff_terule_t * terule);
+
+/**
+ * Get a vector of line numbers (of type unsigned long) for this te rule
+ * difference from the modified policy. Note that if the form is
+ * POLDIFF_FORM_REMOVED or POLDIFF_FORM_REMOVE_TYPE then this will return
+ * NULL. Also, if the modified policy is a binary policy or line numbers are
+ * not yet enabled then this returns NULL.
+ * @see poldiff_enable_line_numbers() to enable line numbers.
+ *
+ * @param terule The te rule diff from which to get line numbers.
+ *
+ * @return A vector of line numbers (type unsigned long) for the rule
+ * in the modified policy, or NULL if no numbers are available.
+ */
+ extern apol_vector_t *poldiff_terule_get_mod_line_numbers(const poldiff_terule_t * terule);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TERULE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/type_diff.h b/libpoldiff/include/poldiff/type_diff.h
new file mode 100644
index 0000000..b92795b
--- /dev/null
+++ b/libpoldiff/include/poldiff/type_diff.h
@@ -0,0 +1,132 @@
+/**
+ * @file
+ * Public interface for computing semantic differences of primary
+ * types. Aliases are resolved by the type mapping system
+ * (type_map.h); attributes are found in the attrib_diff.h header
+ * file.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_TYPE_DIFF_H
+#define POLDIFF_TYPE_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_type poldiff_type_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for types.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_type_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of type differences from the type difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * type difference summary.
+ *
+ * @return A vector of elements of type poldiff_type_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_type_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a type.
+ *
+ * @param diff The policy difference structure associated with the type.
+ * @param type The type from which to generate the string.
+ *
+ * @return A string representation of type difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_type_to_string(const poldiff_t * diff, const void *type);
+
+/**
+ * Get the name of the type from a type diff.
+ *
+ * @param type The type from which to get the name.
+ *
+ * @return Name of the type on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_type_get_name(const poldiff_type_t * type);
+
+/**
+ * Get the form of difference from a type diff.
+ *
+ * @param cls The type from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_type_get_form(const void *type);
+
+/**
+ * Get a vector of attributes added to the type.
+ *
+ * @param type The type diff from which to get the attribute
+ * vector.
+ *
+ * @return A vector of attribute names (type char *) that are
+ * assigned to the type in the modified policy. If no attributes
+ * were added the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_type_get_added_attribs(const poldiff_type_t * type);
+
+/**
+ * Get a vector of attributes removed from the type.
+ *
+ * @param type The type diff from which to get the attribute
+ * vector.
+ *
+ * @return A vector of attribute names (type char *) that are
+ * assigned to the type in the original policy. If no attributes
+ * were removed the size of the returned vector will be 0. The
+ * caller must not destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_type_get_removed_attribs(const poldiff_type_t * type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_DIFF_H */
diff --git a/libpoldiff/include/poldiff/type_map.h b/libpoldiff/include/poldiff/type_map.h
new file mode 100644
index 0000000..56eb742
--- /dev/null
+++ b/libpoldiff/include/poldiff/type_map.h
@@ -0,0 +1,153 @@
+/**
+ * @file
+ * Public interface for type equivalence mapping for semantic
+ * difference calculations.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_TYPE_MAP_H
+#define POLDIFF_TYPE_MAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <poldiff/poldiff.h>
+#include <apol/vector.h>
+
+ typedef struct poldiff_type_remap_entry poldiff_type_remap_entry_t;
+
+/**
+ * Note that a type(s) from the original policy should be remapped in
+ * the modified policy. Subsequent diffs will treat type(s) in
+ * orig_names to be equivalent to type(s) in mod_names. The created
+ * remap entry will be marked as enabled.
+ *
+ * It is an error for the size of both vectors to be greater than
+ * one.
+ *
+ * Note that you may only remap primary types, not attributes nor
+ * aliases.
+ *
+ * @param diff The difference structure associated with the types.
+ * Note that renaming a type will reset the status of previously run
+ * difference calculations and they will need to be rerun.
+ * @param orig_names A vector of type names (char *) in the original
+ * policy.
+ * @param mod_name A vector of type names (char *) in the modified
+ * policy to consider equivalent.
+ *
+ * @return 0 on success or < 0 on error; if the call fails, errno
+ * will be set and the poldiff object remains unchanged.
+ */
+ extern int poldiff_type_remap_create(poldiff_t * diff, const apol_vector_t * orig_names, const apol_vector_t * mod_names);
+
+/**
+ * Get a vector of all identified type remap entries. The caller may
+ * then manipulate this list by selectively enabling/disabling
+ * individual entries.
+ *
+ * @param diff The difference structure associated with the types
+ * remaps.
+ *
+ * @return Vector of poldiff_type_remap_entry_t objects. The caller
+ * should not destroy this vector.
+ */
+ extern apol_vector_t *poldiff_type_remap_get_entries(const poldiff_t * diff);
+
+/**
+ * Remove a poldiff_type_remap_entry object. This function will
+ * destroy the entry object afterwards.
+ *
+ * @param diff The difference structure associated with the types
+ * remaps.
+ * @param entry Type remap entry to remove and destroy.
+ */
+ extern void poldiff_type_remap_entry_remove(poldiff_t * diff, poldiff_type_remap_entry_t * entry);
+
+/**
+ * Allocate and return a sorted vector of type names (char *)
+ * corresponding to the original types within a
+ * poldiff_type_remap_entry_t object. The strings themselves are to
+ * be considered immutable; if the caller needs them for future use
+ * it should duplicate them.
+ *
+ * @param diff Difference structure, for error reporting.
+ * @param entry Remap entry from which to get type names.
+ *
+ * @return Vector of type names. The caller is responsible for
+ * calling apol_vector_destroy() afterwards. Upon error return NULL
+ * and set errno.
+ */
+ extern apol_vector_t *poldiff_type_remap_entry_get_original_types(const poldiff_t * diff,
+ const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Allocate and return a sorted vector of type names (char *)
+ * corresponding to the modified types within a
+ * poldiff_type_remap_entry_t object. The strings themselves are to
+ * be considered immutable; if the caller needs them for future use
+ * it should duplicate them.
+ *
+ * @param diff Difference structure, for error reporting.
+ * @param entry Remap entry from which to get type names.
+ *
+ * @return Vector of type names. The caller is responsible for
+ * calling apol_vector_destroy() afterwards. Upon error return NULL
+ * and set errno.
+ */
+ extern apol_vector_t *poldiff_type_remap_entry_get_modified_types(const poldiff_t * diff,
+ const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Given a poldiff_type_remap_entry_t object, determine if was
+ * an inferred mapping or not.
+ *
+ * @param entry Remap entry from which to get its inference status.
+ *
+ * @return 1 if it was inferred, 0 if not, < 0 on error.
+ */
+ extern int poldiff_type_remap_entry_get_is_inferred(const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Given a poldiff_type_remap_entry_t object, determine if it is
+ * enabled or not.
+ *
+ * @param entry Remap entry from which to get its enabled status.
+ *
+ * @return 1 if it is enabled, 0 if not, < 0 on error.
+ */
+ extern int poldiff_type_remap_entry_get_is_enabled(const poldiff_type_remap_entry_t * entry);
+
+/**
+ * Enable or disable a poldiff_type_remap_entry_t object.
+ *
+ * @param entry Remap entry from which to set its enabled status.
+ * @param enabled 0 to disable this entry, non-zero to enable it.
+ */
+ extern void poldiff_type_remap_entry_set_enabled(poldiff_type_remap_entry_t * entry, int enabled);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_MAP_H */
diff --git a/libpoldiff/include/poldiff/user_diff.h b/libpoldiff/include/poldiff/user_diff.h
new file mode 100644
index 0000000..0af50e6
--- /dev/null
+++ b/libpoldiff/include/poldiff/user_diff.h
@@ -0,0 +1,191 @@
+/**
+ * @file
+ * Public interface for computing semantic differences in users.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_USER_DIFF_H
+#define POLDIFF_USER_DIFF_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <poldiff/poldiff.h>
+
+ typedef struct poldiff_user poldiff_user_t;
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for users.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ */
+ extern void poldiff_user_get_stats(const poldiff_t * diff, size_t stats[5]);
+
+/**
+ * Get the vector of user differences from the user difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the
+ * user difference summary.
+ *
+ * @return A vector of elements of type poldiff_user_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector
+ * returned. If the call fails, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_get_user_vector(const poldiff_t * diff);
+
+/**
+ * Obtain a newly allocated string representation of a difference in
+ * a user.
+ *
+ * @param diff The policy difference structure associated with the user.
+ * @param user The user from which to generate the string.
+ *
+ * @return A string representation of user difference; the caller is
+ * responsible for free()ing this string. On error, return NULL and
+ * set errno.
+ */
+ extern char *poldiff_user_to_string(const poldiff_t * diff, const void *user);
+
+/**
+ * Get the name of the user from a user diff.
+ *
+ * @param user The user from which to get the name.
+ *
+ * @return Name of the user on success and NULL on failure; if the
+ * call fails, errno will be set. The caller should not free the
+ * returned string.
+ */
+ extern const char *poldiff_user_get_name(const poldiff_user_t * user);
+
+/**
+ * Get the form of difference from a user diff.
+ *
+ * @param user The user from which to get the difference form.
+ *
+ * @return The form of difference (one of POLDIFF_FORM_*) or
+ * POLDIFF_FORM_NONE on error. If the call fails, errno will be set.
+ */
+ extern poldiff_form_e poldiff_user_get_form(const void *user);
+
+/**
+ * Get a vector of unmodified roles for the user.
+ *
+ * @param user The user diff from which to get the roles vector.
+ *
+ * @return A vector of role names (type char *) that are assigned to
+ * the user in the modified policy. If no roles were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_user_get_unmodified_roles(const poldiff_user_t * user);
+
+/**
+ * Get a vector of roles added to the user. If a user was added by
+ * the modified policy then this vector will hold all of the roles.
+ *
+ * @param user The user diff from which to get the roles vector.
+ *
+ * @return A vector of role names (type char *) that are assigned to
+ * the user in the modified policy. If no roles were added the size
+ * of the returned vector will be 0. The caller must not destroy
+ * this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_user_get_added_roles(const poldiff_user_t * user);
+
+/**
+ * Get a vector of roles removed from the user. If a user was
+ * removed by the modified policy then this vector will hold all of
+ * the roles.
+ *
+ * @param user The user diff from which to get the roles vector.
+ *
+ * @return A vector of role names (type char *) that are assigned to
+ * the user in the original policy. If no roles were removed the
+ * size of the returned vector will be 0. The caller must not
+ * destroy this vector. On error, errno will be set.
+ */
+ extern const apol_vector_t *poldiff_user_get_removed_roles(const poldiff_user_t * user);
+
+/**
+ * Get the original user's default MLS level. That is, this is the
+ * level assigned to the user in the original policy. If the level
+ * has the form POLDIFF_FORM_MODIFIED, then this indiciates that the
+ * user had the same sensitivity between the two policies but
+ * different categories.
+ *
+ * If neither policy is MLS or there are no differences in default
+ * level, then the return value is NULL.
+ *
+ * @param user The user diff from which to get default level.
+ *
+ * @return User's original default MLS level. Returns NULL upon
+ * error or if there is no difference in level.
+ */
+ extern const poldiff_level_t *poldiff_user_get_original_dfltlevel(const poldiff_user_t * user);
+
+/**
+ * Get the modified user's MLS level. That is, this is the level
+ * assigned to the user in the modified policy. If the level had the
+ * same sensitivity but different categories call
+ * poldiff_user_get_original_dfltlevel() to get the difference; this
+ * function will return NULL.
+ *
+ * If neither policy is MLS or there are no differences in
+ * default level, then the return value is NULL.
+ *
+ * @param user The user diff from which to get default level.
+ *
+ * @return User's modified default MLS level. Returns NULL upon
+ * error, if there is no difference in level, or if the sensitivity
+ * was unchanged.
+ */
+ extern const poldiff_level_t *poldiff_user_get_modified_dfltlevel(const poldiff_user_t * user);
+
+/**
+ * Get the change in user's assigned MLS range.
+ *
+ * If neither policy is MLS or there are no differences in range,
+ * then the return value is NULL.
+ *
+ * @param user The user diff from which to get assigned range
+ * differences.
+ *
+ * @return User's MLS range differences. Returns NULL upon error or
+ * if there is no difference in range.
+ */
+ extern const poldiff_range_t *poldiff_user_get_range(const poldiff_user_t * user);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_USER_DIFF_H */
diff --git a/libpoldiff/include/poldiff/util.h b/libpoldiff/include/poldiff/util.h
new file mode 100644
index 0000000..445a92b
--- /dev/null
+++ b/libpoldiff/include/poldiff/util.h
@@ -0,0 +1,45 @@
+/**
+ * @file
+ *
+ * Miscellaneous, uncategorized functions for libpoldiff.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_UTIL_H
+#define POLDIFF_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libpoldiff_get_version(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libpoldiff/src/Makefile.am b/libpoldiff/src/Makefile.am
new file mode 100644
index 0000000..8275309
--- /dev/null
+++ b/libpoldiff/src/Makefile.am
@@ -0,0 +1,57 @@
+lib_LIBRARIES = libpoldiff.a
+
+poldiffso_DATA = libpoldiff.so.@libpoldiff_version@
+poldiffsodir = $(libdir)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ \
+ @APOL_CFLAGS@ @QPOL_CFLAGS@ -I$(srcdir)/../include -fpic
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libpoldiff_a_SOURCES = \
+ poldiff.c \
+ attrib_diff.c attrib_internal.h \
+ avrule_diff.c avrule_internal.h \
+ bool_diff.c bool_internal.h \
+ cat_diff.c cat_internal.h \
+ class_diff.c class_internal.h \
+ level_diff.c level_internal.h \
+ range_diff.c range_internal.h \
+ range_trans_diff.c range_trans_internal.h \
+ rbac_diff.c rbac_internal.h \
+ role_diff.c role_internal.h \
+ terule_diff.c terule_internal.h \
+ type_diff.c type_internal.h \
+ user_diff.c user_internal.h \
+ poldiff_internal.h \
+ type_map.c type_map_internal.h \
+ util.c
+
+libpoldiff_a_DEPENDENCIES = $(top_builddir)/libapol/src/libapol.so
+
+libpoldiff_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libpoldiff_a_SOURCES)))
+LIBPOLDIFF_SONAME = @libpoldiff_soname@
+
+dist_noinst_DATA = libpoldiff.map writing-diffs-HOWTO
+
+$(poldiffso_DATA): $(libpoldiff_so_OBJS) libpoldiff.map
+ $(CC) -shared -o $@ $(libpoldiff_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBPOLDIFF_SONAME),--version-script=$(srcdir)/libpoldiff.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so $(top_builddir)/libapol/src/libapol.so
+ $(LN_S) -f $@ @libpoldiff_soname@
+ $(LN_S) -f $@ libpoldiff.so
+
+libpoldiff.so: $(poldiffso_DATA)
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+install-data-hook:
+ cd $(DESTDIR)$(poldiffsodir) && $(LN_S) -f $(poldiffso_DATA) @libpoldiff_soname@
+ cd $(DESTDIR)$(poldiffsodir) && $(LN_S) -f $(poldiffso_DATA) libpoldiff.so
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libpoldiff.so @libpoldiff_soname@ $(poldiffso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(poldiffsodir)/$(poldiffso_DATA) $(DESTDIR)$(poldiffsodir)/@libpoldiff_soname@ $(DESTDIR)$(poldiffsodir)/libpoldiff.so
diff --git a/libpoldiff/src/attrib_diff.c b/libpoldiff/src/attrib_diff.c
new file mode 100644
index 0000000..a9802df
--- /dev/null
+++ b/libpoldiff/src/attrib_diff.c
@@ -0,0 +1,544 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in attributes.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_attrib_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_attrib
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_types;
+ apol_vector_t *removed_types;
+};
+
+void poldiff_attrib_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->attrib_diffs->num_added;
+ stats[1] = diff->attrib_diffs->num_removed;
+ stats[2] = diff->attrib_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_attrib_to_string(const poldiff_t * diff, const void *attrib)
+{
+ poldiff_attrib_t *at = (poldiff_attrib_t *) attrib;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *type;
+ if (diff == NULL || attrib == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(at->added_types);
+ num_removed = apol_vector_get_size(at->removed_types);
+ switch (at->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", at->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", at->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", at->name) < 0) {
+ break;
+ }
+ if (num_added > 0 && apol_str_appendf(&s, &len, "%zd Added Type%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ if (num_removed > 0
+ && apol_str_appendf(&s, &len, "%s%zd Removed Type%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(at->added_types); i++) {
+ type = (char *)apol_vector_get_element(at->added_types, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(at->removed_types); i++) {
+ type = (char *)apol_vector_get_element(at->removed_types, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_attrib_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->attrib_diffs->diffs;
+}
+
+const char *poldiff_attrib_get_name(const poldiff_attrib_t * attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return attrib->name;
+}
+
+poldiff_form_e poldiff_attrib_get_form(const void *attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_attrib_t *)attrib)->form;
+}
+
+const apol_vector_t *poldiff_attrib_get_added_types(const poldiff_attrib_t * attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return attrib->added_types;
+}
+
+const apol_vector_t *poldiff_attrib_get_removed_types(const poldiff_attrib_t * attrib)
+{
+ if (attrib == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return attrib->removed_types;
+}
+
+/*************** protected functions for attribs ***************/
+
+static void attrib_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_attrib_t *t = (poldiff_attrib_t *) elem;
+ free(t->name);
+ apol_vector_destroy(&t->added_types);
+ apol_vector_destroy(&t->removed_types);
+ free(t);
+ }
+}
+
+poldiff_attrib_summary_t *attrib_summary_create(void)
+{
+ poldiff_attrib_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(attrib_free)) == NULL) {
+ attrib_summary_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void attrib_summary_destroy(poldiff_attrib_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+int attrib_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ attrib_summary_destroy(&diff->attrib_diffs);
+ diff->attrib_diffs = attrib_summary_create();
+ if (diff->attrib_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two attribs from the same policy.
+ */
+static int attrib_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_type_t *r1 = x;
+ const qpol_type_t *r2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_type_get_name(q, r1, &name1) < 0 || qpol_type_get_name(q, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *attrib_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_type_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create(NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ unsigned char isattr;
+ qpol_type_t *type;
+ qpol_iterator_get_item(iter, (void **)&type);
+ qpol_type_get_isattr(q, type, &isattr);
+ if (isattr) {
+ apol_vector_append(v, type);
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, attrib_name_comp, (void *)policy);
+ return v;
+}
+
+int attrib_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_type_t *r1 = x;
+ const qpol_type_t *r2 = y;
+ const char *name1, *name2;
+ if (qpol_type_get_name(diff->orig_qpol, r1, &name1) < 0 || qpol_type_get_name(diff->mod_qpol, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new attrib difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the attrib that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling attrib_free() upon the returned
+ * value.
+ */
+static poldiff_attrib_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_attrib_t *pr;
+ int error;
+ if ((pr = calloc(1, sizeof(*pr))) == NULL ||
+ (pr->name = strdup(name)) == NULL ||
+ (pr->added_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pr->removed_types = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ attrib_free(pr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pr->form = form;
+ return pr;
+}
+
+int attrib_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_type_t *r = item;
+ const char *name = NULL;
+ poldiff_attrib_t *pr;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_type_get_name(diff->mod_qpol, r, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_type_get_name(diff->orig_qpol, r, &name) < 0))
+ {
+ return -1;
+ }
+ pr = make_diff(diff, form, name);
+ if (pr == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->attrib_diffs->diffs, pr) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ attrib_free(pr);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->attrib_diffs->num_added++;
+ } else {
+ diff->attrib_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given a attrib, return an unsorted vector of its allowed types (in
+ * the form of uint32_t corresponding to pseudo-type values).
+ *
+ * @param diff Policy diff error handler.
+ * @param attrib Attrib whose types to get.
+ * @param which Which policy, one of POLDIFF_POLICY_ORIG or
+ * POLDIFF_POLICY_MOD.
+ *
+ * @return Vector of pseudo-type values. The caller is responsible
+ * for calling apol_vector_destroy(). On error, return NULL.
+ */
+static apol_vector_t *attrib_get_types(const poldiff_t * diff, const qpol_type_t * attrib, int which)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_type_t *type;
+ uint32_t new_val;
+ apol_vector_t *v = NULL;
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (which == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_type_iter(diff->orig_qpol, attrib, &iter) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (qpol_type_get_type_iter(diff->mod_qpol, attrib, &iter) < 0) {
+ goto cleanup;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || (new_val = type_map_lookup(diff, type, which)) == 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(v, (void *)((size_t) new_val)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+int attrib_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_type_t *r1 = x;
+ const qpol_type_t *r2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ apol_vector_t *added_types = NULL, *removed_types = NULL;
+ const apol_vector_t *reverse_v;
+ const char *name;
+ char *new_name;
+ uint32_t t1, t2;
+ poldiff_attrib_t *r = NULL;
+ qpol_type_t *t;
+ size_t i, j;
+ int retval = -1, error = 0;
+ if (qpol_type_get_name(diff->orig_qpol, r1, &name) < 0 ||
+ (v1 = attrib_get_types(diff, r1, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = attrib_get_types(diff, r2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort_uniquify(v1, NULL, NULL);
+ apol_vector_sort_uniquify(v2, NULL, NULL);
+ if ((added_types = apol_vector_create(NULL)) == NULL || (removed_types = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (t2 > t1) {
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (t1 > t2) {
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(added_types) > 0 || apol_vector_get_size(removed_types) > 0) {
+ if ((r = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(removed_types); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(removed_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t1, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->removed_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(added_types); i++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(added_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->mod_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->added_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort(r->removed_types, apol_str_strcmp, NULL);
+ apol_vector_sort(r->added_types, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->attrib_diffs->diffs, r) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->attrib_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ apol_vector_destroy(&added_types);
+ apol_vector_destroy(&removed_types);
+ if (retval != 0) {
+ attrib_free(r);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/attrib_internal.h b/libpoldiff/src/attrib_internal.h
new file mode 100644
index 0000000..75d1e1d
--- /dev/null
+++ b/libpoldiff/src/attrib_internal.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Protected interface for attribute differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_ATTRIB_INTERNAL_H
+#define POLDIFF_ATTRIB_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_attrib_summary poldiff_attrib_summary_t;
+
+/**
+ * Allocate and return a new poldiff_attrib_summary_t object.
+ *
+ * @return A new attrib summary. The caller must call attrib_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_attrib_summary_t *attrib_summary_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_attrib_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param us Reference to a attrib summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void attrib_summary_destroy(poldiff_attrib_summary_t ** us);
+
+/**
+ * Reset the state of all attribute differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int attrib_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all attribs from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all attribs. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *attrib_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_attrib_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The attrib from the original policy.
+ * @param y The attrib from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if attrib x is respectively less than, equal
+ * to, or greater than attrib y.
+ */
+ int attrib_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a attrib.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int attrib_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two attribs for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * attrib.
+ *
+ * @param diff The policy difference structure associated with both
+ * attribs and to which to add an entry if needed.
+ * @param x The attrib from the original policy.
+ * @param y The attrib from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int attrib_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ATTRIB_INTERNAL_H */
diff --git a/libpoldiff/src/avrule_diff.c b/libpoldiff/src/avrule_diff.c
new file mode 100644
index 0000000..a1b61b1
--- /dev/null
+++ b/libpoldiff/src/avrule_diff.c
@@ -0,0 +1,1636 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in AV and Type
+ * rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <qpol/policy_extend.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_avrule_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ int diffs_sorted;
+ /** vector of poldiff_avrule_t */
+ apol_vector_t *diffs;
+};
+
+struct poldiff_avrule
+{
+ uint32_t spec;
+ /* pointer into policy's symbol table */
+ const char *source, *target;
+ /** the class string is pointer into the class_bst BST */
+ char *cls;
+ poldiff_form_e form;
+ /** vector of pointers into the perm_bst BST (char *) */
+ apol_vector_t *unmodified_perms;
+ /** vector of pointers into the perm_bst BST (char *) */
+ apol_vector_t *added_perms;
+ /** vector of pointers into the perm_bst BST (char *) */
+ apol_vector_t *removed_perms;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ uint32_t branch;
+ /** vector of unsigned longs of line numbers from original policy */
+ apol_vector_t *orig_linenos;
+ /** vector of unsigned longs of line numbers from modified policy */
+ apol_vector_t *mod_linenos;
+ /** array of pointers for original rules */
+ qpol_avrule_t **orig_rules;
+ size_t num_orig_rules;
+ /** array of pointers for modified rules */
+ qpol_avrule_t **mod_rules;
+ size_t num_mod_rules;
+};
+
+typedef struct pseudo_avrule
+{
+ uint32_t spec;
+ /** pseudo-type values */
+ uint32_t source, target;
+ /** pointer into the class_bst BST */
+ char *cls;
+ /** array of pointers into the perm_bst BST */
+ /* (use an array here to save space) */
+ char **perms;
+ size_t num_perms;
+ /** array of pointers into the bool_bst BST */
+ char *bools[5];
+ uint32_t bool_val;
+ uint32_t branch;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ /** array of qpol_avrule_t pointers, for showing line numbers */
+ const qpol_avrule_t **rules;
+ size_t num_rules;
+} pseudo_avrule_t;
+
+/******************** public avrule functions ********************/
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for av rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ * @param idx Index into the avrule differences specifying which
+ * avrule type to get, one of AVRULE_OFFSET_ALLOW, etc.
+ */
+static void poldiff_avrule_get_stats(const poldiff_t * diff, size_t stats[5], avrule_offset_e idx)
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->avrule_diffs[idx]->num_added;
+ stats[1] = diff->avrule_diffs[idx]->num_removed;
+ stats[2] = diff->avrule_diffs[idx]->num_modified;
+ stats[3] = diff->avrule_diffs[idx]->num_added_type;
+ stats[4] = diff->avrule_diffs[idx]->num_removed_type;
+}
+
+void poldiff_avrule_get_stats_allow(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_ALLOW);
+}
+
+void poldiff_avrule_get_stats_neverallow(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_NEVERALLOW);
+}
+
+void poldiff_avrule_get_stats_dontaudit(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_DONTAUDIT);
+}
+
+void poldiff_avrule_get_stats_auditallow(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_avrule_get_stats(diff, stats, AVRULE_OFFSET_AUDITALLOW);
+}
+
+char *poldiff_avrule_to_string(const poldiff_t * diff, const void *avrule)
+{
+ const poldiff_avrule_t *pa = (const poldiff_avrule_t *)avrule;
+ apol_policy_t *p;
+ const char *rule_type;
+ char *diff_char = "", *s = NULL, *perm_name, *cond_expr = NULL;
+ size_t i, len = 0;
+ int show_perm_sym = 0, error;
+ if (diff == NULL || avrule == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (pa->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff_char = "+";
+ p = diff->mod_pol;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff_char = "-";
+ p = diff->orig_pol;
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ diff_char = "*";
+ p = diff->orig_pol;
+ show_perm_sym = 1;
+ break;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ rule_type = apol_rule_type_to_str(pa->spec);
+ if (apol_str_appendf(&s, &len, "%s %s %s %s : %s {", diff_char, rule_type, pa->source, pa->target, pa->cls) < 0) {
+ error = errno;
+ goto err;
+ }
+ for (i = 0; pa->unmodified_perms != NULL && i < apol_vector_get_size(pa->unmodified_perms); i++) {
+ perm_name = (char *)apol_vector_get_element(pa->unmodified_perms, i);
+ if (apol_str_appendf(&s, &len, " %s", perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (i = 0; pa->added_perms != NULL && i < apol_vector_get_size(pa->added_perms); i++) {
+ perm_name = (char *)apol_vector_get_element(pa->added_perms, i);
+ if (apol_str_appendf(&s, &len, " %s%s", (show_perm_sym ? "+" : ""), perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (i = 0; pa->removed_perms != NULL && i < apol_vector_get_size(pa->removed_perms); i++) {
+ perm_name = (char *)apol_vector_get_element(pa->removed_perms, i);
+ if (apol_str_appendf(&s, &len, " %s%s", (show_perm_sym ? "-" : ""), perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, " };") < 0) {
+ error = errno;
+ goto err;
+ }
+ if (pa->cond != NULL) {
+ if ((cond_expr = apol_cond_expr_render(p, pa->cond)) == NULL) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " [%s]:%s", cond_expr, (pa->branch ? "TRUE" : "FALSE")) < 0) {
+ error = errno;
+ goto err;
+ }
+ free(cond_expr);
+ }
+ return s;
+ err:
+ free(s);
+ free(cond_expr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+}
+
+/**
+ * Sort poldiff_avrule diff results in a mostly alphabetical order.
+ */
+static int poldiff_avrule_cmp(const void *x, const void *y, void *data __attribute__ ((unused)))
+{
+ const poldiff_avrule_t *a = (const poldiff_avrule_t *)x;
+ const poldiff_avrule_t *b = (const poldiff_avrule_t *)y;
+ int compval;
+ if (a->spec != b->spec) {
+ const char *rule_type1 = apol_rule_type_to_str(a->spec);
+ const char *rule_type2 = apol_rule_type_to_str(b->spec);
+ compval = strcmp(rule_type1, rule_type2);
+ if (compval != 0) {
+ return compval;
+ }
+ }
+ if ((compval = strcmp(a->source, b->source)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->target, b->target)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->cls, b->cls)) != 0) {
+ return compval;
+ }
+ if (a->cond != b->cond) {
+ return (char *)a->cond - (char *)b->cond;
+ }
+ /* sort true branch before false branch */
+ return b->branch - a->branch;
+}
+
+/**
+ * Get the vector of av rule differences from the av rule difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the av
+ * rule difference summary.
+ * @param idx Index into the avrule differences specifying which
+ * avrule type to get, one of AVRULE_OFFSET_ALLOW, etc.
+ *
+ * @return A vector of elements of type poldiff_avrule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+static const apol_vector_t *poldiff_get_avrule_vector(const poldiff_t * diff, avrule_offset_e idx)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (diff->avrule_diffs[idx]->diffs_sorted == 0) {
+ apol_vector_sort(diff->avrule_diffs[idx]->diffs, poldiff_avrule_cmp, NULL);
+ diff->avrule_diffs[idx]->diffs_sorted = 1;
+ }
+ return diff->avrule_diffs[idx]->diffs;
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_allow(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_ALLOW);
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_auditallow(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_AUDITALLOW);
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_dontaudit(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_DONTAUDIT);
+}
+
+const apol_vector_t *poldiff_get_avrule_vector_neverallow(const poldiff_t * diff)
+{
+ return poldiff_get_avrule_vector(diff, AVRULE_OFFSET_NEVERALLOW);
+}
+
+poldiff_form_e poldiff_avrule_get_form(const void *avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_avrule_t *)avrule)->form;
+}
+
+uint32_t poldiff_avrule_get_rule_type(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->spec;
+}
+
+const char *poldiff_avrule_get_source_type(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->source;
+}
+
+const char *poldiff_avrule_get_target_type(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->target;
+}
+
+const char *poldiff_avrule_get_object_class(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avrule->cls;
+}
+
+void poldiff_avrule_get_cond(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p)
+{
+ if (diff == NULL || avrule == NULL || cond == NULL || p == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ *cond = avrule->cond;
+ if (*cond == NULL) {
+ *which_list = 1;
+ *p = NULL;
+ } else if (avrule->form == POLDIFF_FORM_ADDED || avrule->form == POLDIFF_FORM_ADD_TYPE) {
+ *which_list = avrule->branch;
+ *p = diff->mod_pol;
+ } else {
+ *which_list = avrule->branch;
+ *p = diff->orig_pol;
+ }
+}
+
+const apol_vector_t *poldiff_avrule_get_unmodified_perms(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->unmodified_perms;
+}
+
+const apol_vector_t *poldiff_avrule_get_added_perms(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->added_perms;
+}
+
+const apol_vector_t *poldiff_avrule_get_removed_perms(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->removed_perms;
+}
+
+const apol_vector_t *poldiff_avrule_get_orig_line_numbers(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->orig_linenos;
+}
+
+const apol_vector_t *poldiff_avrule_get_mod_line_numbers(const poldiff_avrule_t * avrule)
+{
+ if (avrule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avrule->mod_linenos;
+}
+
+/**
+ * Get the line numbers from an array of qpol_avrule_t that contain
+ * the given permission.
+ */
+static apol_vector_t *avrule_get_line_numbers_for_perm(const poldiff_t * diff, const char *perm, const qpol_policy_t * q,
+ qpol_avrule_t ** rules, const size_t num_rules)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *syn_iter = NULL, *perm_iter = NULL;
+ size_t i;
+ int error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; i < num_rules; i++) {
+ if (qpol_avrule_get_syn_avrule_iter(q, rules[i], &syn_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(syn_iter); qpol_iterator_next(syn_iter)) {
+ qpol_syn_avrule_t *syn_rule;
+ qpol_iterator_get_item(syn_iter, (void **)&syn_rule);
+ if (qpol_syn_avrule_get_perm_iter(q, syn_rule, &perm_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ char *syn_perm;
+ qpol_iterator_get_item(perm_iter, (void **)&syn_perm);
+ if (strcmp(perm, syn_perm) == 0) {
+ unsigned long lineno;
+ qpol_syn_avrule_get_lineno(q, syn_rule, &lineno);
+ if (apol_vector_append(v, (void *)lineno) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ }
+ break;
+ }
+ }
+ qpol_iterator_destroy(&perm_iter);
+ }
+ qpol_iterator_destroy(&syn_iter);
+ }
+ apol_vector_sort_uniquify(v, NULL, NULL);
+ cleanup:
+ qpol_iterator_destroy(&syn_iter);
+ qpol_iterator_destroy(&perm_iter);
+ if (error != 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *poldiff_avrule_get_orig_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm)
+{
+ if (diff == NULL || avrule == NULL || perm == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!diff->line_numbers_enabled || avrule->form == POLDIFF_FORM_ADDED || avrule->form == POLDIFF_FORM_ADD_TYPE) {
+ return NULL;
+ }
+ if (avrule->num_orig_rules == 0) {
+ return NULL;
+ }
+ return avrule_get_line_numbers_for_perm(diff, perm, diff->orig_qpol, avrule->orig_rules, avrule->num_orig_rules);
+}
+
+apol_vector_t *poldiff_avrule_get_mod_line_numbers_for_perm(const poldiff_t * diff, const poldiff_avrule_t * avrule,
+ const char *perm)
+{
+ if (diff == NULL || avrule == NULL || perm == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (!diff->line_numbers_enabled || avrule->form == POLDIFF_FORM_REMOVED || avrule->form == POLDIFF_FORM_REMOVE_TYPE) {
+ return NULL;
+ }
+ if (avrule->num_mod_rules == 0) {
+ return NULL;
+ }
+ return avrule_get_line_numbers_for_perm(diff, perm, diff->mod_qpol, avrule->mod_rules, avrule->num_mod_rules);
+}
+
+/******************** protected functions below ********************/
+
+/**
+ * Free all space used by a poldiff_avrule_t, including the pointer
+ * itself. Does nothing if the pointer is already NULL.
+ *
+ * @param elem Pointer to a poldiff_avrule_t.
+ */
+static void poldiff_avrule_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_avrule_t *a = (poldiff_avrule_t *) elem;
+ apol_vector_destroy(&a->unmodified_perms);
+ apol_vector_destroy(&a->added_perms);
+ apol_vector_destroy(&a->removed_perms);
+ apol_vector_destroy(&a->orig_linenos);
+ apol_vector_destroy(&a->mod_linenos);
+ free(a->orig_rules);
+ free(a->mod_rules);
+ free(a);
+ }
+}
+
+poldiff_avrule_summary_t *avrule_create(void)
+{
+ poldiff_avrule_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(poldiff_avrule_free)) == NULL) {
+ avrule_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void avrule_destroy(poldiff_avrule_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+/**
+ * Reset the state of an AV rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @param idx Index into the avrule diffs array indicating which rule
+ * type to reset, one of AVRULE_OFFSET_ALLOW, etc.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+static int avrule_reset(poldiff_t * diff, avrule_offset_e idx)
+{
+ int error = 0;
+
+ avrule_destroy(&diff->avrule_diffs[idx]);
+ diff->avrule_diffs[idx] = avrule_create();
+ if (diff->avrule_diffs[idx] == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+int avrule_reset_allow(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_ALLOW);
+}
+
+int avrule_reset_auditallow(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_AUDITALLOW);
+}
+
+int avrule_reset_dontaudit(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_DONTAUDIT);
+}
+
+int avrule_reset_neverallow(poldiff_t * diff)
+{
+ return avrule_reset(diff, AVRULE_OFFSET_NEVERALLOW);
+}
+
+static void avrule_free_item(void *item)
+{
+ pseudo_avrule_t *a = (pseudo_avrule_t *) item;
+ if (item != NULL) {
+ free(a->perms);
+ free(a->rules);
+ free(a);
+ }
+}
+
+/**
+ * Apply an ordering scheme to two pseudo-av rules.
+ *
+ * <ul>
+ * <li>Sort by target pseudo-type value,
+ * <li>Then by source pseudo-type value,
+ * <li>Then by object class's BST's pointer value,
+ * <li>Then by rule specified (allow, neverallow, etc.),
+ * <li>Then choose unconditional rules over conditional rules,
+ * <li>Then by conditional expression's BST's boolean pointer value.
+ * </ul>
+ *
+ * If this function is being used for sorting (via avrule_get_items())
+ * then sort by truth value, and then by branch (true branch, then
+ * false branch). Otherwise, when comparing rules (via avrule_comp())
+ * then by truth value, inverting rule2's value if in the other
+ * branch.
+ */
+static int pseudo_avrule_comp(const pseudo_avrule_t * rule1, const pseudo_avrule_t * rule2, int is_sorting)
+{
+ size_t i;
+ uint32_t bool_val;
+ if (rule1->target != rule2->target) {
+ return rule1->target - rule2->target;
+ }
+ if (rule1->source != rule2->source) {
+ return rule1->source - rule2->source;
+ }
+ if (rule1->cls != rule2->cls) {
+ return (int)(rule1->cls - rule2->cls);
+ }
+ if (rule1->spec != rule2->spec) {
+ return rule1->spec - rule2->spec;
+ }
+ if (rule1->bools[0] == NULL && rule2->bools[0] == NULL) {
+ /* both rules are unconditional */
+ return 0;
+ } else if (rule1->bools[0] == NULL && rule2->bools[0] != NULL) {
+ /* unconditional rules come before conditional */
+ return -1;
+ } else if (rule1->bools[0] != NULL && rule2->bools[0] == NULL) {
+ /* unconditional rules come before conditional */
+ return 1;
+ }
+ for (i = 0; i < (sizeof(rule1->bools) / sizeof(rule1->bools[0])); i++) {
+ if (rule1->bools[i] != rule2->bools[i]) {
+ return (int)(rule1->bools[i] - rule2->bools[i]);
+ }
+ }
+ if (is_sorting) {
+ if (rule1->branch != rule2->branch) {
+ return rule1->branch - rule2->branch;
+ }
+ return (int)rule1->bool_val - (int)rule2->bool_val;
+ } else {
+ if (rule1->branch == rule2->branch) {
+ bool_val = rule2->bool_val;
+ } else {
+ bool_val = ~rule2->bool_val;
+ }
+ if (rule1->bool_val < bool_val) {
+ return -1;
+ } else if (rule1->bool_val > bool_val) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+static int avrule_bst_comp(const void *x, const void *y, void *data __attribute__ ((unused)))
+{
+ const pseudo_avrule_t *r1 = (const pseudo_avrule_t *)x;
+ const pseudo_avrule_t *r2 = (const pseudo_avrule_t *)y;
+ return pseudo_avrule_comp(r1, r2, 1);
+}
+
+/**
+ * Given a conditional expression, convert its booleans to a sorted
+ * array of pseudo-boolean values, assign that array to the
+ * pseudo-avrule key, and then derive the truth table.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy containing conditional.
+ * @param cond Conditional expression to convert.
+ * @param key Location to write converted expression.
+ */
+static int avrule_build_cond(poldiff_t * diff, const apol_policy_t * p, const qpol_cond_t * cond, pseudo_avrule_t * key)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_cond_expr_node_t *node;
+ uint32_t expr_type, truthiness;
+ qpol_bool_t *bools[5] = { NULL, NULL, NULL, NULL, NULL }, *qbool;
+ size_t i, j;
+ size_t num_bools = 0;
+ const char *bool_name;
+ char *pseudo_bool, *t;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ if (qpol_cond_get_expr_node_iter(q, cond, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&node) < 0 || qpol_cond_expr_node_get_expr_type(q, node, &expr_type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ continue;
+ }
+ if (qpol_cond_expr_node_get_bool(q, node, &qbool) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (bools[i] == qbool) {
+ break;
+ }
+ }
+ if (i >= num_bools) {
+ assert(i < 5);
+ bools[i] = qbool;
+ num_bools++;
+ }
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (qpol_bool_get_name(q, bools[i], &bool_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->bool_bst, (void *)bool_name, NULL, (void **)&pseudo_bool) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ key->bools[i] = pseudo_bool;
+ }
+
+ /* bubble sorth the pseudo bools (not bad because there are at
+ * most five elements */
+ for (i = num_bools; i > 1; i--) {
+ for (j = 1; j < i; j++) {
+ compval = strcmp(key->bools[j - 1], key->bools[j]);
+ if (compval > 0) {
+ t = key->bools[j];
+ key->bools[j] = key->bools[j - 1];
+ key->bools[j - 1] = t;
+ qbool = bools[j];
+ bools[j] = bools[j - 1];
+ bools[j - 1] = qbool;
+ }
+ }
+ }
+
+ /* now compute the truth table for the booleans */
+ key->bool_val = 0;
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < num_bools; j++) {
+ int state = ((i & (1 << j)) ? 1 : 0);
+ if (qpol_bool_set_state_no_eval(q, bools[j], state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (qpol_cond_eval(q, cond, &truthiness) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ key->bool_val = (key->bool_val << 1) | truthiness;
+ }
+
+ key->cond = cond;
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Bubble sort the permissions within a pseudo-avrule, sorted by
+ * pointer value. (Bubble-sort is fine because the number of
+ * permissions will usually be less than 10.) Then uniquify the list.
+ *
+ * @param key Rule whose permissions to sort.
+ */
+static void sort_and_uniquify_perms(pseudo_avrule_t * key)
+{
+ size_t i, j;
+ char *t;
+ for (i = key->num_perms; i > 1; i--) {
+ for (j = 1; j < i; j++) {
+ if (key->perms[j - 1] > key->perms[j]) {
+ t = key->perms[j];
+ key->perms[j] = key->perms[j - 1];
+ key->perms[j - 1] = t;
+ }
+ }
+ }
+ for (i = 1; i < key->num_perms; i++) {
+ if (key->perms[i] == key->perms[i - 1]) {
+ memmove(key->perms + i, key->perms + i + 1, (key->num_perms - i - 1) * sizeof(key->perms[0]));
+ key->num_perms--;
+ }
+ }
+}
+
+/**
+ * Given a rule, construct a new pseudo-avrule and insert it into the
+ * BST if not already there.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule AV rule to insert.
+ * @param source Source pseudo-type value.
+ * @param target Target pseudo-type value.
+ * @param b BST containing pseudo-avrules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int avrule_add_to_bst(poldiff_t * diff, const apol_policy_t * p,
+ const qpol_avrule_t * rule, uint32_t source, uint32_t target, apol_bst_t * b)
+{
+ pseudo_avrule_t *key, *inserted_key;
+ const qpol_class_t *obj_class;
+ qpol_iterator_t *perm_iter = NULL;
+ const char *class_name;
+ char *perm_name, *pseudo_perm, **t;
+ size_t num_perms;
+ const qpol_cond_t *cond;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ if ((key = calloc(1, sizeof(*key))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_avrule_get_rule_type(q, rule, &(key->spec)) < 0 ||
+ qpol_avrule_get_object_class(q, rule, &obj_class) < 0 ||
+ qpol_avrule_get_perm_iter(q, rule, &perm_iter) < 0 || qpol_avrule_get_cond(q, rule, &cond) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (qpol_class_get_name(q, obj_class, &class_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->class_bst, (void *)class_name, NULL, (void **)&key->cls) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ key->source = source;
+ key->target = target;
+ if (cond != NULL && (qpol_avrule_get_which_list(q, rule, &(key->branch)) < 0 || avrule_build_cond(diff, p, cond, key) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* insert this pseudo into the tree if not already there */
+ if ((compval = apol_bst_insert_and_get(b, (void **)&key, NULL)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key = key;
+ key = NULL;
+
+ /* append and uniquify this rule's permissions */
+ if (qpol_iterator_get_size(perm_iter, &num_perms) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((t = realloc(inserted_key->perms, (inserted_key->num_perms + num_perms) * sizeof(*t))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key->perms = t;
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void *)&perm_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->perm_bst, perm_name, NULL, (void **)&pseudo_perm) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ free(perm_name);
+ goto cleanup;
+ }
+ free(perm_name);
+ inserted_key->perms[(inserted_key->num_perms)++] = pseudo_perm;
+ }
+ sort_and_uniquify_perms(inserted_key);
+
+ /* store the rule pointer, to be used for showing line numbers */
+ if (qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS)) {
+ const qpol_avrule_t **a = realloc(inserted_key->rules,
+ (inserted_key->num_rules + 1) * sizeof(*a));
+ if (a == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key->rules = a;
+ inserted_key->rules[inserted_key->num_rules++] = rule;
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ if (retval < 0) {
+ avrule_free_item(key);
+ }
+ errno = error;
+ return retval;
+}
+
+/**
+ * Given a rule, expand its source and target types into individual
+ * pseudo-type values. Then add the expanded rules to the BST. This
+ * is needed for when the source and/or target is an attribute.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule AV rule to insert.
+ * @param b BST containing pseudo-avrules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int avrule_expand(poldiff_t * diff, const apol_policy_t * p, const qpol_avrule_t * rule, apol_bst_t * b)
+{
+ const qpol_type_t *source, *orig_target, *target;
+ unsigned char source_attr, target_attr;
+ qpol_iterator_t *source_iter = NULL, *target_iter = NULL;
+ uint32_t source_val, target_val;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ int retval = -1, error = 0;
+ if (qpol_avrule_get_source_type(q, rule, &source) < 0 ||
+ qpol_avrule_get_target_type(q, rule, &orig_target) < 0 ||
+ qpol_type_get_isattr(q, source, &source_attr) < 0 || qpol_type_get_isattr(q, orig_target, &target_attr)) {
+ error = errno;
+ goto cleanup;
+ }
+#ifdef SETOOLS_DEBUG
+ const char *orig_source_name, *orig_target_name;
+ qpol_type_get_name(q, source, &orig_source_name);
+ qpol_type_get_name(q, orig_target, &orig_target_name);
+#endif
+
+ if (source_attr) {
+ if (qpol_type_get_type_iter(q, source, &source_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ /* handle situation where a rule has as its source an
+ * attribute without any types */
+ if (qpol_iterator_end(source_iter)) {
+ retval = 0;
+ goto cleanup;
+ }
+ }
+ do {
+ if (source_attr) {
+ if (qpol_iterator_get_item(source_iter, (void **)&source) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(source_iter);
+ }
+ if (target_attr) {
+ if (qpol_type_get_type_iter(q, orig_target, &target_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ /* handle situation where a rule has as its
+ * target an attribute without any types */
+ if (qpol_iterator_end(target_iter)) {
+ retval = 0;
+ goto cleanup;
+ }
+ } else {
+ target = orig_target;
+ }
+ do {
+ if (target_attr) {
+ if (qpol_iterator_get_item(target_iter, (void **)&target) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(target_iter);
+ }
+#ifdef SETOOLS_DEBUG
+ const char *n1, *n2;
+ qpol_type_get_name(q, source, &n1);
+ qpol_type_get_name(q, target, &n2);
+#endif
+ if ((source_val = type_map_lookup(diff, source, which)) == 0 ||
+ (target_val = type_map_lookup(diff, target, which)) == 0 ||
+ avrule_add_to_bst(diff, p, rule, source_val, target_val, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } while (target_attr && !qpol_iterator_end(target_iter));
+ qpol_iterator_destroy(&target_iter);
+ } while (source_attr && !qpol_iterator_end(source_iter));
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&source_iter);
+ qpol_iterator_destroy(&target_iter);
+ errno = error;
+ return retval;
+}
+
+/**
+ * Get a vector of avrules from the given policy, sorted. This
+ * function will remap source and target types to their pseudo-type
+ * value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @param which Kind of rule to get, one of QPOL_RULE_ALLOW, etc.
+ *
+ * @return A newly allocated vector of all av rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+static apol_vector_t *avrule_get_items(poldiff_t * diff, const apol_policy_t * policy, const unsigned int which)
+{
+ apol_vector_t *bools = NULL, *bool_states = NULL;
+ size_t i, num_rules, j;
+ apol_bst_t *b = NULL;
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_avrule_t *rule;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int retval = -1, error = 0;
+
+ /* special case: if getting neverallow rules if the policy
+ does not support it, then return an empty vector */
+ if (which == QPOL_RULE_NEVERALLOW && !qpol_policy_has_capability(q, QPOL_CAP_NEVERALLOW)) {
+ v = apol_vector_create_with_capacity(1, avrule_free_item);
+ if (v == NULL) {
+ ERR(diff, "%s", strerror(error));
+ }
+ return v;
+ }
+
+ if (poldiff_build_bsts(diff) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* store original boolean values */
+ if (apol_bool_get_by_query(policy, NULL, &bools) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((bool_states = apol_vector_create_with_capacity(apol_vector_get_size(bools), NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state;
+ if (qpol_bool_get_state(q, qbool, &state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(bool_states, (void *)((size_t) state)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if ((b = apol_bst_create(avrule_bst_comp, avrule_free_item)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_policy_get_avrule_iter(q, which, &iter) < 0) {
+
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_get_size(iter, &num_rules);
+ for (j = 0; !qpol_iterator_end(iter); qpol_iterator_next(iter), j++) {
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0 || avrule_expand(diff, policy, rule, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (!(j % 1024)) {
+ int percent = 50 * j / num_rules + (policy == diff->mod_pol ? 50 : 0);
+ INFO(diff, "Computing AV rule difference: %02d%% complete", percent);
+ }
+ }
+ if ((v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ /* restore boolean states */
+ for (i = 0; i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state = (int)((size_t) apol_vector_get_element(bool_states, i));
+ qpol_bool_set_state_no_eval(q, qbool, state);
+ }
+ qpol_policy_reevaluate_conds(q);
+ apol_vector_destroy(&bools);
+ apol_vector_destroy(&bool_states);
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *avrule_get_items_allow(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_ALLOW);
+}
+
+apol_vector_t *avrule_get_items_auditallow(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_AUDITALLOW);
+}
+
+apol_vector_t *avrule_get_items_dontaudit(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_DONTAUDIT);
+}
+
+apol_vector_t *avrule_get_items_neverallow(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return avrule_get_items(diff, policy, QPOL_RULE_NEVERALLOW);
+}
+
+int avrule_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_avrule_t *r1 = (const pseudo_avrule_t *)x;
+ const pseudo_avrule_t *r2 = (const pseudo_avrule_t *)y;
+ return pseudo_avrule_comp(r1, r2, 0);
+}
+
+/**
+ * Allocate and return a new avrule difference object. If the
+ * pseudo-avrule's source and/or target expands to multiple read
+ * types, then just choose the first one for display.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param rule Pseudo avrule that changed.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling poldiff_avrule_free() upon
+ * the returned value.
+ */
+static poldiff_avrule_t *make_avdiff(poldiff_t * diff, poldiff_form_e form, pseudo_avrule_t * rule)
+{
+ poldiff_avrule_t *pa = NULL;
+ const char *n1, *n2;
+ int error = 0;
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_MOD);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_MOD);
+ } else {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_ORIG);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_ORIG);
+ }
+ assert(n1 != NULL && n2 != NULL);
+ if ((pa = calloc(1, sizeof(*pa))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ pa->spec = rule->spec;
+ pa->source = n1;
+ pa->target = n2;
+ pa->cls = rule->cls;
+ pa->form = form;
+ pa->cond = rule->cond;
+ pa->branch = rule->branch;
+ cleanup:
+ if (error != 0) {
+ poldiff_avrule_free(pa);
+ errno = error;
+ return NULL;
+ }
+ return pa;
+}
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ * @param idx Index into the avrule differences specifying into which
+ * to place the constructed pseudo-av rule.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int avrule_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item, avrule_offset_e idx)
+{
+ pseudo_avrule_t *rule = (pseudo_avrule_t *) item;
+ poldiff_avrule_t *pa = NULL;
+ const apol_vector_t *v1, *v2;
+ apol_vector_t **target;
+ apol_policy_t *p;
+ size_t i;
+ int retval = -1, error = errno;
+
+ /* check if form should really become ADD_TYPE / REMOVE_TYPE,
+ * by seeing if the /other/ policy's reverse lookup is
+ * empty */
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ p = diff->mod_pol;
+ } else {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_MOD)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ p = diff->orig_pol;
+ }
+
+ pa = make_avdiff(diff, form, rule);
+ if (pa == NULL) {
+ return -1;
+ }
+
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ if ((pa->removed_perms = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (pa->unmodified_perms = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ target = &pa->added_perms;
+ } else {
+ if ((pa->added_perms = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (pa->unmodified_perms = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ target = &pa->removed_perms;
+ }
+ if ((*target = apol_vector_create_with_capacity(rule->num_perms, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < rule->num_perms; i++) {
+ if (apol_vector_append(*target, rule->perms[i]) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort(*target, apol_str_strcmp, NULL);
+
+ if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_LINE_NUMBERS)) {
+ /* calculate line numbers */
+ apol_vector_t *vl = NULL;
+ if ((vl = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pa->mod_linenos = vl;
+ } else {
+ pa->orig_linenos = vl;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pa->num_mod_rules = rule->num_rules;
+ pa->mod_rules = calloc(rule->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->mod_rules, rule->rules, rule->num_rules * sizeof(qpol_avrule_t *));
+ } else {
+ pa->num_orig_rules = rule->num_rules;
+ pa->orig_rules = calloc(rule->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->orig_rules, rule->rules, rule->num_rules * sizeof(qpol_avrule_t *));
+ }
+ }
+
+ if (apol_vector_append(diff->avrule_diffs[idx]->diffs, pa) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ diff->avrule_diffs[idx]->num_added++;
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ diff->avrule_diffs[idx]->num_added_type++;
+ break;
+ case POLDIFF_FORM_REMOVED:
+ diff->avrule_diffs[idx]->num_removed++;
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ diff->avrule_diffs[idx]->num_removed_type++;
+ break;
+ default:
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ diff->avrule_diffs[idx]->diffs_sorted = 0;
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ poldiff_avrule_free(pa);
+ }
+ errno = error;
+ return retval;
+}
+
+int avrule_new_diff_allow(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_ALLOW);
+}
+
+int avrule_new_diff_auditallow(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_AUDITALLOW);
+}
+
+int avrule_new_diff_dontaudit(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_DONTAUDIT);
+}
+
+int avrule_new_diff_neverallow(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return avrule_new_diff(diff, form, item, AVRULE_OFFSET_NEVERALLOW);
+}
+
+/**
+ * Compute the semantic difference of two pseudo-av rules for which
+ * the compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ * @param idx Index into the avrule differences specifying into which
+ * to place the constructed pseudo-av rule.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int avrule_deep_diff(poldiff_t * diff, const void *x, const void *y, avrule_offset_e idx)
+{
+ pseudo_avrule_t *r1 = (pseudo_avrule_t *) x;
+ pseudo_avrule_t *r2 = (pseudo_avrule_t *) y;
+ apol_vector_t *unmodified_perms = NULL, *added_perms = NULL, *removed_perms = NULL;
+ size_t i, j;
+ char *perm1, *perm2;
+ poldiff_avrule_t *pa = NULL;
+ int retval = -1, error = 0;
+
+ if ((unmodified_perms = apol_vector_create(NULL)) == NULL ||
+ (added_perms = apol_vector_create(NULL)) == NULL || (removed_perms = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < r1->num_perms;) {
+ if (j >= r2->num_perms)
+ break;
+ perm1 = r1->perms[i];
+ perm2 = r2->perms[j];
+ if (perm2 > perm1) {
+ if (apol_vector_append(removed_perms, perm1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (perm1 > perm2) {
+ if (apol_vector_append(added_perms, perm2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if (apol_vector_append(unmodified_perms, perm1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+
+ for (; i < r1->num_perms; i++) {
+ perm1 = r1->perms[i];
+ if (apol_vector_append(removed_perms, perm1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < r2->num_perms; j++) {
+ perm2 = r2->perms[j];
+ if (apol_vector_append(added_perms, perm2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(added_perms) > 0 || apol_vector_get_size(removed_perms) > 0) {
+ if ((pa = make_avdiff(diff, POLDIFF_FORM_MODIFIED, r1)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ pa->unmodified_perms = unmodified_perms;
+ pa->added_perms = added_perms;
+ pa->removed_perms = removed_perms;
+ unmodified_perms = NULL;
+ added_perms = NULL;
+ removed_perms = NULL;
+ apol_vector_sort(pa->unmodified_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(pa->added_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(pa->removed_perms, apol_str_strcmp, NULL);
+
+ /* calculate line numbers */
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pa->orig_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pa->num_orig_rules = r1->num_rules;
+ pa->orig_rules = calloc(r1->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->orig_rules, r1->rules, r1->num_rules * sizeof(qpol_avrule_t *));
+ }
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pa->mod_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pa->num_mod_rules = r2->num_rules;
+ pa->mod_rules = calloc(r2->num_rules, sizeof(qpol_avrule_t *));
+ if (!pa->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pa->mod_rules, r2->rules, r2->num_rules * sizeof(qpol_avrule_t *));
+ }
+ if (apol_vector_append(diff->avrule_diffs[idx]->diffs, pa) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->avrule_diffs[idx]->num_modified++;
+ diff->avrule_diffs[idx]->diffs_sorted = 0;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&unmodified_perms);
+ apol_vector_destroy(&added_perms);
+ apol_vector_destroy(&removed_perms);
+ if (retval != 0) {
+ poldiff_avrule_free(pa);
+ }
+ errno = error;
+ return retval;
+}
+
+int avrule_deep_diff_allow(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_ALLOW);
+}
+
+int avrule_deep_diff_auditallow(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_AUDITALLOW);
+}
+
+int avrule_deep_diff_dontaudit(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_DONTAUDIT);
+}
+
+int avrule_deep_diff_neverallow(poldiff_t * diff, const void *x, const void *y)
+{
+ return avrule_deep_diff(diff, x, y, AVRULE_OFFSET_NEVERALLOW);
+}
+
+int avrule_enable_line_numbers(poldiff_t * diff, avrule_offset_e idx)
+{
+ const apol_vector_t *av = NULL;
+ poldiff_avrule_t *avrule = NULL;
+ size_t i, j;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_avrule_t *sav = NULL;
+ int error = 0;
+ unsigned long lineno = 0;
+
+ av = poldiff_get_avrule_vector(diff, idx);
+
+ for (i = 0; i < apol_vector_get_size(av); i++) {
+ avrule = apol_vector_get_element(av, i);
+ if (apol_vector_get_size(avrule->mod_linenos) || apol_vector_get_size(avrule->orig_linenos))
+ continue;
+ for (j = 0; j < avrule->num_orig_rules; j++) {
+ if (qpol_avrule_get_syn_avrule_iter(diff->orig_qpol, avrule->orig_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&sav) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_avrule_get_lineno(diff->orig_qpol, sav, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(avrule->orig_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(avrule->orig_linenos, NULL, NULL);
+ for (j = 0; j < avrule->num_mod_rules; j++) {
+ if (qpol_avrule_get_syn_avrule_iter(diff->mod_qpol, avrule->mod_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&sav) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_avrule_get_lineno(diff->mod_qpol, sav, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(avrule->mod_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(avrule->mod_linenos, NULL, NULL);
+ }
+ return 0;
+ err:
+ qpol_iterator_destroy(&iter);
+ return -1;
+}
diff --git a/libpoldiff/src/avrule_internal.h b/libpoldiff/src/avrule_internal.h
new file mode 100644
index 0000000..9bdf517
--- /dev/null
+++ b/libpoldiff/src/avrule_internal.h
@@ -0,0 +1,296 @@
+/**
+ * @file
+ * Protected interface for AV rule differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_AVRULE_INTERNAL_H
+#define POLDIFF_AVRULE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_avrule_summary poldiff_avrule_summary_t;
+
+/**
+ * Allocate and return a new poldiff_terule_summary_t object, used by
+ * AV rule searches.
+ *
+ * @return A new rule summary. The caller must call avrule_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_avrule_summary_t *avrule_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_avrule_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param rs Reference to an rule summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void avrule_destroy(poldiff_avrule_summary_t ** rs);
+
+/**
+ * Reset the state of AV allow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_allow(poldiff_t * diff);
+
+/**
+ * Reset the state of AV auditallow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_auditallow(poldiff_t * diff);
+
+/**
+ * Reset the state of AV dontaudit rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_dontaudit(poldiff_t * diff);
+
+/**
+ * Reset the state of AV neverallow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int avrule_reset_neverallow(poldiff_t * diff);
+
+/**
+ * Get a vector of AV allow rules from the given policy, sorted. This
+ * function will remap source and target types to their pseudo-type
+ * value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av allow rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_allow(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of AV auditallow rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av auditallow rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_auditallow(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of AV dontaudit rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av dontaudit rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_dontaudit(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of AV neverallow rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of av neverallow rules (of type
+ * pseudo_avrule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *avrule_get_items_neverallow(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_avrule_t objects, determining if they have the
+ * same key (specified + source + target + class + conditional
+ * expression).
+ *
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if av rule x is respectively less than,
+ * equal to, or greater than av rule y.
+ */
+ int avrule_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally an allow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_allow(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally an auditallow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_auditallow(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally a dontaudit rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_dontaudit(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-av rule that was originally a neverallow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_new_diff_neverallow(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * allow rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_allow(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * auditallow rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_auditallow(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * dontaudit rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_dontaudit(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-av rules (that were
+ * neverallow rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-av rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-av rules and to which to add an entry if needed.
+ * @param x The pseudo-av rule from the original policy.
+ * @param y The pseudo-av rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int avrule_deep_diff_neverallow(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Iterate through an AV rule difference, filling in its line numbers.
+ *
+ * @param diff Diff structure containing avrule differences.
+ * @param idx Index into the avrule differences specifying which line
+ * number table to enable.
+ *
+ * @return 0 on success, < 0 on errno.
+ */
+ int avrule_enable_line_numbers(poldiff_t * diff, avrule_offset_e idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_AVRULE_INTERNAL_H */
diff --git a/libpoldiff/src/bool_diff.c b/libpoldiff/src/bool_diff.c
new file mode 100644
index 0000000..3db1e46
--- /dev/null
+++ b/libpoldiff/src/bool_diff.c
@@ -0,0 +1,333 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in booleans.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <apol/vector.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+struct poldiff_bool_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_bool
+{
+ char *name;
+ poldiff_form_e form;
+ bool state;
+};
+
+void poldiff_bool_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->bool_diffs->num_added;
+ stats[1] = diff->bool_diffs->num_removed;
+ stats[2] = diff->bool_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_bool_to_string(const poldiff_t * diff, const void *boolean)
+{
+ poldiff_bool_t *b = (poldiff_bool_t *) boolean;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || boolean == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (b->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", b->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", b->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf
+ (&s, &len, "* %s (changed from %s)", b->name, (b->state ? "false to true" : "true to false")) < 0) {
+ break;
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_bool_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->bool_diffs->diffs;
+}
+
+const char *poldiff_bool_get_name(const poldiff_bool_t * boolean)
+{
+ if (boolean == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return boolean->name;
+}
+
+poldiff_form_e poldiff_bool_get_form(const void *boolean)
+{
+ if (boolean == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_bool_t *)boolean)->form;
+}
+
+/******************** protected functions ********************/
+
+static void bool_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_bool_t *b = (poldiff_bool_t *) elem;
+ free(b->name);
+ free(b);
+ }
+}
+
+poldiff_bool_summary_t *bool_create(void)
+{
+ poldiff_bool_summary_t *bs = calloc(1, sizeof(*bs));
+ if (bs == NULL) {
+ return NULL;
+ }
+ if ((bs->diffs = apol_vector_create(bool_free)) == NULL) {
+ bool_destroy(&bs);
+ return NULL;
+ }
+ return bs;
+}
+
+void bool_destroy(poldiff_bool_summary_t ** bs)
+{
+ if (bs != NULL && *bs != NULL) {
+ apol_vector_destroy(&(*bs)->diffs);
+ free(*bs);
+ *bs = NULL;
+ }
+}
+
+int bool_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ bool_destroy(&diff->bool_diffs);
+ diff->bool_diffs = bool_create();
+ if (diff->bool_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two bools from the same policy.
+ */
+static int bool_name_comp(const void *x, const void *y, void *arg)
+{
+ qpol_bool_t *c1 = (qpol_bool_t *) x;
+ qpol_bool_t *c2 = (qpol_bool_t *) y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_bool_get_name(q, c1, &name1) < 0 || qpol_bool_get_name(q, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *bool_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_bool_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, bool_name_comp, (void *)policy);
+ return v;
+}
+
+int bool_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ qpol_bool_t *c1 = (qpol_bool_t *) x;
+ qpol_bool_t *c2 = (qpol_bool_t *) y;
+ const char *name1, *name2;
+ if (qpol_bool_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_bool_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new bool difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the bool that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling bool_free() upon the
+ * returned value.
+ */
+static poldiff_bool_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_bool_t *pb;
+ int error;
+ if ((pb = calloc(1, sizeof(*pb))) == NULL || (pb->name = strdup(name)) == NULL) {
+ error = errno;
+ bool_free(pb);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pb->form = form;
+ return pb;
+}
+
+int bool_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ qpol_bool_t *c = (qpol_bool_t *) item;
+ const char *name = NULL;
+ poldiff_bool_t *pb;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_bool_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_bool_get_name(diff->orig_qpol, c, &name) < 0))
+ {
+ return -1;
+ }
+ pb = make_diff(diff, form, name);
+ if (pb == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->bool_diffs->diffs, pb) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ bool_free(pb);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED)
+ diff->bool_diffs->num_added++;
+ else
+ diff->bool_diffs->num_removed++;
+ return 0;
+}
+
+int bool_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ qpol_bool_t *b1 = (qpol_bool_t *) x;
+ qpol_bool_t *b2 = (qpol_bool_t *) y;
+ const char *name;
+ int s1, s2;
+ poldiff_bool_t *b = NULL;
+ int retval = -1, error = 0;
+
+ if (qpol_bool_get_name(diff->orig_qpol, b1, &name) < 0 ||
+ qpol_bool_get_state(diff->orig_qpol, b1, &s1) < 0 || qpol_bool_get_state(diff->mod_qpol, b2, &s2) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (s1 != s2) {
+ if ((b = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (s2)
+ b->state = true;
+ else
+ b->state = false;
+ }
+ if (b != NULL) {
+ if (apol_vector_append(diff->bool_diffs->diffs, b) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->bool_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/bool_internal.h b/libpoldiff/src/bool_internal.h
new file mode 100644
index 0000000..744419f
--- /dev/null
+++ b/libpoldiff/src/bool_internal.h
@@ -0,0 +1,122 @@
+/**
+ * @file
+ * Protected interface for boolean differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_BOOL_INTERNAL_H
+#define POLDIFF_BOOL_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_bool_summary poldiff_bool_summary_t;
+
+/**
+ * Allocate and return a new poldiff_bool_summary_t object.
+ *
+ * @return A new bool summary. The caller must call bool_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_bool_summary_t *bool_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_bool_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param bs Reference to a bool summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void bool_destroy(poldiff_bool_summary_t ** bs);
+
+/**
+ * Reset the state of all boolean differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int bool_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all bools from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all bools. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *bool_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_bool_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The bool from the original policy.
+ * @param y The bool from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if bool x is respectively less than, equal
+ * to, or greater than bool y.
+ */
+ int bool_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a bool.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int bool_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two bools for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert an new semantic difference entry
+ * for that bool.
+ *
+ * @param diff The policy difference structure associated with both
+ * bools and to which to add an entry if needed.
+ * @param x The bool from the original policy.
+ * @param y The bool from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int bool_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_BOOL_INTERNAL_H */
diff --git a/libpoldiff/src/cat_diff.c b/libpoldiff/src/cat_diff.c
new file mode 100644
index 0000000..2f6ab17
--- /dev/null
+++ b/libpoldiff/src/cat_diff.c
@@ -0,0 +1,289 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in categories.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_cat_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_cat
+{
+ char *name;
+ poldiff_form_e form;
+};
+
+void poldiff_cat_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->cat_diffs->num_added;
+ stats[1] = diff->cat_diffs->num_removed;
+ stats[2] = 0;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+const apol_vector_t *poldiff_get_cat_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->cat_diffs->diffs;
+}
+
+char *poldiff_cat_to_string(const poldiff_t * diff, const void *cat)
+{
+ poldiff_cat_t *c = (poldiff_cat_t *) cat;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || cat == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (c->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+const char *poldiff_cat_get_name(const poldiff_cat_t * cat)
+{
+ if (cat == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return cat->name;
+}
+
+poldiff_form_e poldiff_cat_get_form(const void *cat)
+{
+ if (cat == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+
+ return ((const poldiff_cat_t *)cat)->form;
+}
+
+static void cat_free(void *elem)
+{
+ poldiff_cat_t *s = elem;
+ if (!elem)
+ return;
+ free(s->name);
+ free(s);
+}
+
+poldiff_cat_summary_t *cat_create(void)
+{
+ poldiff_cat_summary_t *cs = calloc(1, sizeof(poldiff_cat_summary_t));
+ if (cs == NULL)
+ return NULL;
+ if ((cs->diffs = apol_vector_create(cat_free)) == NULL) {
+ cat_destroy(&cs);
+ return NULL;
+ }
+ return cs;
+}
+
+void cat_destroy(poldiff_cat_summary_t ** cs)
+{
+ if (cs == NULL || *cs == NULL)
+ return;
+ apol_vector_destroy(&(*cs)->diffs);
+ free(*cs);
+ *cs = NULL;
+}
+
+int cat_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ cat_destroy(&diff->cat_diffs);
+ diff->cat_diffs = cat_create();
+ if (diff->cat_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two categories from the same policy.
+ */
+static int cat_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_cat_t *c1 = x;
+ const qpol_cat_t *c2 = y;
+ apol_policy_t *p = arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+
+ if (qpol_cat_get_name(q, c1, &name1) < 0 || qpol_cat_get_name(q, c2, &name2) < 0)
+ return 0;
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *cat_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_cat_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, cat_name_comp, (void *)policy);
+ return v;
+}
+
+int cat_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_cat_t *c1 = x;
+ const qpol_cat_t *c2 = y;
+ const char *name1, *name2;
+ if (qpol_cat_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_cat_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new category difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the category that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling cat_free() upon the returned
+ * value.
+ */
+static poldiff_cat_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_cat_t *pl;
+ int error;
+ if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL) {
+ error = errno;
+ cat_free(pl);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pl->form = form;
+ return pl;
+}
+
+int cat_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_cat_t *c = item;
+ const char *name = NULL;
+ poldiff_cat_t *pl;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_cat_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_cat_get_name(diff->orig_qpol, c, &name) < 0)) {
+ return -1;
+ }
+ pl = make_diff(diff, form, name);
+ if (pl == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->cat_diffs->diffs, pl) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ cat_free(pl);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->cat_diffs->num_added++;
+ } else {
+ diff->cat_diffs->num_removed++;
+ }
+ return 0;
+}
+
+int cat_deep_diff(poldiff_t * diff __attribute__ ((unused)), const void *x __attribute__ ((unused)), const void *y
+ __attribute__ ((unused)))
+{
+ /* Categories cannot be modified only added or removed.
+ * This call back simply returns 0 to satisfy the generic diff algorithm. */
+ return 0;
+}
diff --git a/libpoldiff/src/cat_internal.h b/libpoldiff/src/cat_internal.h
new file mode 100644
index 0000000..4c5714e
--- /dev/null
+++ b/libpoldiff/src/cat_internal.h
@@ -0,0 +1,120 @@
+/**
+ * @file
+ * Protected interface for computing semantic differences in
+ * categories.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_CAT_INTERNAL_H
+#define POLDIFF_CAT_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_cat_summary poldiff_cat_summary_t;
+
+/**
+ * Allocate and return a new poldiff_cat_summary_t object.
+ *
+ * @return A new category summary. The caller must call cat_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_cat_summary_t *cat_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_cat_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param cs Reference to a category summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void cat_destroy(poldiff_cat_summary_t ** cs);
+
+/**
+ * Reset the state of all category differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int cat_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all categoriess from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all categories. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *cat_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_cat_t objects, determining if they have the same
+ * level name or not.
+ *
+ * @param x The category from the original policy.
+ * @param y The category from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if category x is respectively less than, equal
+ * to, or greater than category y.
+ */
+ int cat_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a category.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int cat_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two categories for which the compare
+ * callback returns 0. Categories cannot be modified only added or removed.
+ * This function always returns 0.
+ *
+ * @param diff The policy difference structure associated with both
+ * categories.
+ * @param x The category from the original policy.
+ * @param y The category from the modified policy.
+ *
+ * @return always 0.
+ */
+ int cat_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CAT_INTERNAL_H */
diff --git a/libpoldiff/src/class_diff.c b/libpoldiff/src/class_diff.c
new file mode 100644
index 0000000..1c4541c
--- /dev/null
+++ b/libpoldiff/src/class_diff.c
@@ -0,0 +1,990 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in classes and
+ * commons.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/******************** object classes ********************/
+
+struct poldiff_class_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_class
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_perms;
+ apol_vector_t *removed_perms;
+};
+
+void poldiff_class_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->class_diffs->num_added;
+ stats[1] = diff->class_diffs->num_removed;
+ stats[2] = diff->class_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_class_to_string(const poldiff_t * diff, const void *cls)
+{
+ poldiff_class_t *c = (poldiff_class_t *) cls;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *perm;
+ if (diff == NULL || cls == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(c->added_perms);
+ num_removed = apol_vector_get_size(c->removed_perms);
+ switch (c->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Permission%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Permission%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(c->added_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->added_perms, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(c->removed_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->removed_perms, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_class_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->class_diffs->diffs;
+}
+
+const char *poldiff_class_get_name(const poldiff_class_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->name;
+}
+
+poldiff_form_e poldiff_class_get_form(const void *cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_class_t *)cls)->form;
+}
+
+const apol_vector_t *poldiff_class_get_added_perms(const poldiff_class_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->added_perms;
+}
+
+const apol_vector_t *poldiff_class_get_removed_perms(const poldiff_class_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->removed_perms;
+}
+
+/*************** protected functions for object classes ***************/
+
+static void class_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_class_t *c = (poldiff_class_t *) elem;
+ free(c->name);
+ apol_vector_destroy(&c->added_perms);
+ apol_vector_destroy(&c->removed_perms);
+ free(c);
+ }
+}
+
+poldiff_class_summary_t *class_create(void)
+{
+ poldiff_class_summary_t *cs = calloc(1, sizeof(*cs));
+ if (cs == NULL) {
+ return NULL;
+ }
+ if ((cs->diffs = apol_vector_create(class_free)) == NULL) {
+ class_destroy(&cs);
+ return NULL;
+ }
+ return cs;
+}
+
+void class_destroy(poldiff_class_summary_t ** cs)
+{
+ if (cs != NULL && *cs != NULL) {
+ apol_vector_destroy(&(*cs)->diffs);
+ free(*cs);
+ *cs = NULL;
+ }
+}
+
+int class_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ class_destroy(&diff->class_diffs);
+ diff->class_diffs = class_create();
+ if (diff->class_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two classes from the same policy.
+ */
+static int class_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_class_t *c1 = x;
+ const qpol_class_t *c2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_class_get_name(q, c1, &name1) < 0 || qpol_class_get_name(q, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *class_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_class_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, class_name_comp, (void *)policy);
+ return v;
+}
+
+int class_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_class_t *c1 = x;
+ const qpol_class_t *c2 = y;
+ const char *name1, *name2;
+ if (qpol_class_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_class_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new class difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the class that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling class_free() upon the
+ * returned value.
+ */
+static poldiff_class_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_class_t *pc;
+ int error;
+ if ((pc = calloc(1, sizeof(*pc))) == NULL ||
+ (pc->name = strdup(name)) == NULL ||
+ (pc->added_perms = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pc->removed_perms = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ class_free(pc);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pc->form = form;
+ return pc;
+}
+
+int class_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_class_t *c = item;
+ const char *name = NULL;
+ poldiff_class_t *pc;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_class_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_class_get_name(diff->orig_qpol, c, &name) < 0))
+ {
+ return -1;
+ }
+ pc = make_diff(diff, form, name);
+ if (pc == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->class_diffs->diffs, pc) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ class_free(pc);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->class_diffs->num_added++;
+ } else {
+ diff->class_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given an object class, return a vector of its permissions (in the
+ * form of strings). These permissions include those inherited from
+ * the class's common, if present.
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the class came.
+ * @param class Class whose permissions to get.
+ *
+ * @return Vector of permissions strings for the class. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *class_get_perms(const poldiff_t * diff, const apol_policy_t * p, const qpol_class_t * class)
+{
+ const qpol_common_t *common;
+ qpol_iterator_t *perm_iter = NULL, *common_iter = NULL;
+ char *perm;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_class_get_common(q, class, &common) < 0 || qpol_class_get_perm_iter(q, class, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, perm) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (common != NULL) {
+ if (qpol_common_get_perm_iter(q, common, &common_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(common_iter); qpol_iterator_next(common_iter)) {
+ if (qpol_iterator_get_item(common_iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, perm) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ qpol_iterator_destroy(&common_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ return v;
+}
+
+int class_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_class_t *c1 = x;
+ const qpol_class_t *c2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *perm1 = NULL, *perm2 = NULL;
+ const char *name;
+ poldiff_class_t *c = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if (qpol_class_get_name(diff->orig_qpol, c1, &name) < 0 ||
+ (v1 = class_get_perms(diff, diff->orig_pol, c1)) == NULL || (v2 = class_get_perms(diff, diff->mod_pol, c2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(perm1, perm2);
+ if (compval != 0 && c == NULL) {
+ if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (compval < 0) {
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ if (c == NULL) {
+ if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ if (c == NULL) {
+ if ((c = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (c != NULL) {
+ apol_vector_sort(c->removed_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(c->added_perms, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->class_diffs->diffs, c) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->class_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ if (retval != 0) {
+ class_free(c);
+ }
+ errno = error;
+ return retval;
+}
+
+/******************** common classes ********************/
+
+struct poldiff_common_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_common
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_perms;
+ apol_vector_t *removed_perms;
+};
+
+void poldiff_common_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->common_diffs->num_added;
+ stats[1] = diff->common_diffs->num_removed;
+ stats[2] = diff->common_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_common_to_string(const poldiff_t * diff, const void *cls)
+{
+ poldiff_common_t *c = (poldiff_common_t *) cls;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *perm;
+ if (diff == NULL || cls == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(c->added_perms);
+ num_removed = apol_vector_get_size(c->removed_perms);
+ switch (c->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", c->name) < 0) {
+ s = NULL;
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Permission%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Permission%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(c->added_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->added_perms, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(c->removed_perms); i++) {
+ perm = (char *)apol_vector_get_element(c->removed_perms, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", perm) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_common_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->common_diffs->diffs;
+}
+
+const char *poldiff_common_get_name(const poldiff_common_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->name;
+}
+
+poldiff_form_e poldiff_common_get_form(const void *cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_common_t *)cls)->form;
+}
+
+const apol_vector_t *poldiff_common_get_added_perms(const poldiff_common_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->added_perms;
+}
+
+const apol_vector_t *poldiff_common_get_removed_perms(const poldiff_common_t * cls)
+{
+ if (cls == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cls->removed_perms;
+}
+
+/*************** protected functions for common classes ***************/
+
+static void common_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_common_t *c = (poldiff_common_t *) elem;
+ free(c->name);
+ apol_vector_destroy(&c->added_perms);
+ apol_vector_destroy(&c->removed_perms);
+ free(c);
+ }
+}
+
+poldiff_common_summary_t *common_create(void)
+{
+ poldiff_common_summary_t *cs = calloc(1, sizeof(*cs));
+ if (cs == NULL) {
+ return NULL;
+ }
+ if ((cs->diffs = apol_vector_create(common_free)) == NULL) {
+ common_destroy(&cs);
+ return NULL;
+ }
+ return cs;
+}
+
+void common_destroy(poldiff_common_summary_t ** cs)
+{
+ if (cs != NULL && *cs != NULL) {
+ apol_vector_destroy(&(*cs)->diffs);
+ free(*cs);
+ *cs = NULL;
+ }
+}
+
+int common_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ common_destroy(&diff->common_diffs);
+ diff->common_diffs = common_create();
+ if (diff->common_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two commons from the same policy.
+ */
+static int common_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_common_t *c1 = x;
+ const qpol_common_t *c2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_common_get_name(q, c1, &name1) < 0 || qpol_common_get_name(q, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *common_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_common_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, common_name_comp, (void *)policy);
+ return v;
+}
+
+int common_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_common_t *c1 = x;
+ const qpol_common_t *c2 = y;
+ const char *name1, *name2;
+ if (qpol_common_get_name(diff->orig_qpol, c1, &name1) < 0 || qpol_common_get_name(diff->mod_qpol, c2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new common difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the common that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling common_free() upon the
+ * returned value.
+ */
+static poldiff_common_t *make_common_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_common_t *pc;
+ int error;
+ if ((pc = calloc(1, sizeof(*pc))) == NULL ||
+ (pc->name = strdup(name)) == NULL ||
+ (pc->added_perms = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pc->removed_perms = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ common_free(pc);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pc->form = form;
+ return pc;
+}
+
+int common_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_common_t *c = item;
+ const char *name = NULL;
+ poldiff_common_t *pc;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_common_get_name(diff->mod_qpol, c, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) &&
+ qpol_common_get_name(diff->orig_qpol, c, &name) < 0)) {
+ return -1;
+ }
+ pc = make_common_diff(diff, form, name);
+ if (pc == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->common_diffs->diffs, pc) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ common_free(pc);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->common_diffs->num_added++;
+ } else {
+ diff->common_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given a common class, return a vector of its permissions (in the
+ * form of strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the common came.
+ * @param common Common whose permissions to get.
+ *
+ * @return Vector of permissions strings for the common. The caller
+ * is responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *common_get_perms(const poldiff_t * diff, const apol_policy_t * p, const qpol_common_t * common)
+{
+ qpol_iterator_t *perm_iter = NULL;
+ char *perm;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_common_get_perm_iter(q, common, &perm_iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(perm_iter); qpol_iterator_next(perm_iter)) {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm) < 0) {
+ goto cleanup;
+ }
+ if (apol_vector_append(v, perm) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&perm_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ return v;
+}
+
+int common_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_common_t *c1 = x;
+ const qpol_common_t *c2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *perm1 = NULL, *perm2 = NULL;
+ const char *name;
+ poldiff_common_t *c = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if (qpol_common_get_name(diff->orig_qpol, c1, &name) < 0 ||
+ (v1 = common_get_perms(diff, diff->orig_pol, c1)) == NULL || (v2 = common_get_perms(diff, diff->mod_pol, c2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(perm1, perm2);
+ if (compval != 0 && c == NULL) {
+ if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (compval < 0) {
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ perm1 = (char *)apol_vector_get_element(v1, i);
+ if (c == NULL) {
+ if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm1 = strdup(perm1)) == NULL || apol_vector_append(c->removed_perms, perm1) < 0) {
+ error = errno;
+ free(perm1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ perm2 = (char *)apol_vector_get_element(v2, j);
+ if (c == NULL) {
+ if ((c = make_common_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((perm2 = strdup(perm2)) == NULL || apol_vector_append(c->added_perms, perm2) < 0) {
+ error = errno;
+ free(perm2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (c != NULL) {
+ apol_vector_sort(c->removed_perms, apol_str_strcmp, NULL);
+ apol_vector_sort(c->added_perms, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->common_diffs->diffs, c) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->common_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ if (retval != 0) {
+ common_free(c);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/class_internal.h b/libpoldiff/src/class_internal.h
new file mode 100644
index 0000000..69f6fc0
--- /dev/null
+++ b/libpoldiff/src/class_internal.h
@@ -0,0 +1,212 @@
+/**
+ * @file
+ * Protected interface for class and common differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_CLASS_INTERNAL_H
+#define POLDIFF_CLASS_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/******************** object classes ********************/
+
+ typedef struct poldiff_class_summary poldiff_class_summary_t;
+
+/**
+ * Allocate and return a new poldiff_class_summary_t object.
+ *
+ * @return A new class summary. The caller must call class_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_class_summary_t *class_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_class_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param cs Reference to a class summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void class_destroy(poldiff_class_summary_t ** cs);
+
+/**
+ * Reset the state of all class differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int class_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all object classes (type qpol_class_t) from the
+ * given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all classes. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *class_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_class_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The class from the original policy.
+ * @param y The class from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if class x is respectively less than, equal
+ * to, or greater than class y.
+ */
+ int class_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a class.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int class_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two classes for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that class.
+ *
+ * @param diff The policy difference structure associated with both
+ * classes and to which to add an entry if needed.
+ * @param x The class from the original policy.
+ * @param y The class from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int class_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+/******************** common classes ********************/
+
+ typedef struct poldiff_common_summary poldiff_common_summary_t;
+
+/**
+ * Allocate and return a new poldiff_common_summary_t object.
+ *
+ * @return A new common summary. The caller must call
+ * common_destroy() afterwards. On error, return NULL and set errno.
+ */
+ poldiff_common_summary_t *common_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_common_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param cs Reference to a common summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void common_destroy(poldiff_common_summary_t ** cs);
+
+/**
+ * Reset the state of all common differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int common_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all common classes (type qpol_common_t) from the
+ * given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all commons. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *common_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_common_t objects, determining if they have the
+ * same name or not.
+ *
+ * @param x The common from the original policy.
+ * @param y The common from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if common x is respectively less than, equal
+ * to, or greater than common y.
+ */
+ int common_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a common.
+ *
+ * @param diff The policy difference structure to which to add the
+ * entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int common_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two commons for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that common.
+ *
+ * @param diff The policy difference structure associated with both
+ * commons and to which to add an entry if needed.
+ * @param x The common from the original policy.
+ * @param y The common from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int common_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_CLASS_INTERNAL_H */
diff --git a/libpoldiff/src/level_diff.c b/libpoldiff/src/level_diff.c
new file mode 100644
index 0000000..b384519
--- /dev/null
+++ b/libpoldiff/src/level_diff.c
@@ -0,0 +1,769 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in levels.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_level_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+void poldiff_level_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->level_diffs->num_added;
+ stats[1] = diff->level_diffs->num_removed;
+ stats[2] = diff->level_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+const apol_vector_t *poldiff_get_level_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->level_diffs->diffs;
+}
+
+char *poldiff_level_to_string(const poldiff_t * diff, const void *level)
+{
+ poldiff_level_t *l = (poldiff_level_t *) level;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *cat;
+ if (diff == NULL || level == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(l->added_cats);
+ num_removed = apol_vector_get_size(l->removed_cats);
+ switch (l->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", l->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", l->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", l->name) < 0) {
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added %s", num_added, (num_added == 1 ? "Category" : "Categories")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed %s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "Category" : "Categories")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(l->added_cats); i++) {
+ cat = (char *)apol_vector_get_element(l->added_cats, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", cat) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(l->removed_cats); i++) {
+ cat = (char *)apol_vector_get_element(l->removed_cats, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", cat) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+char *poldiff_level_to_string_brief(const poldiff_t * diff, const poldiff_level_t * level)
+{
+ char *s = NULL, t, *cat, *sep = "";
+ int show_cat_sym = 0;
+ size_t len = 0, i;
+ switch (level->form) {
+ case POLDIFF_FORM_ADDED:
+ t = '+';
+ break;
+ case POLDIFF_FORM_REMOVED:
+ t = '-';
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ t = '*';
+ show_cat_sym = 1;
+ break;
+ default:
+ /* don't show unmodified levels */
+ if ((s = strdup("")) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ return s;
+ }
+ if (apol_str_appendf(&s, &len, "%c %s", t, level->name) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ if ((level->unmodified_cats != NULL && apol_vector_get_size(level->unmodified_cats) > 0) ||
+ (level->added_cats != NULL && apol_vector_get_size(level->added_cats) > 0) ||
+ (level->removed_cats != NULL && apol_vector_get_size(level->removed_cats) > 0)) {
+ if (apol_str_append(&s, &len, " : ") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ for (i = 0; level->unmodified_cats != NULL && i < apol_vector_get_size(level->unmodified_cats); i++) {
+ cat = apol_vector_get_element(level->unmodified_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; level->added_cats != NULL && i < apol_vector_get_size(level->added_cats); i++) {
+ cat = apol_vector_get_element(level->added_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "+" : ""), cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; level->removed_cats != NULL && i < apol_vector_get_size(level->removed_cats); i++) {
+ cat = apol_vector_get_element(level->removed_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s%s", sep, (show_cat_sym ? "-" : ""), cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ }
+ if (apol_str_append(&s, &len, "\n") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ return s;
+}
+
+const char *poldiff_level_get_name(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return level->name;
+}
+
+poldiff_form_e poldiff_level_get_form(const void *level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+
+ return ((const poldiff_level_t *)level)->form;
+}
+
+const apol_vector_t *poldiff_level_get_added_cats(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return level->added_cats;
+}
+
+const apol_vector_t *poldiff_level_get_removed_cats(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return level->removed_cats;
+}
+
+const apol_vector_t *poldiff_level_get_unmodified_cats(const poldiff_level_t * level)
+{
+ if (level == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return level->unmodified_cats;
+}
+
+poldiff_level_summary_t *level_create(void)
+{
+ poldiff_level_summary_t *ls = calloc(1, sizeof(poldiff_level_summary_t));
+ if (ls == NULL)
+ return NULL;
+ if ((ls->diffs = apol_vector_create(level_free)) == NULL) {
+ level_destroy(&ls);
+ return NULL;
+ }
+ return ls;
+}
+
+void level_destroy(poldiff_level_summary_t ** ls)
+{
+ if (ls == NULL || *ls == NULL)
+ return;
+ apol_vector_destroy(&(*ls)->diffs);
+ free(*ls);
+ *ls = NULL;
+}
+
+int level_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ level_destroy(&diff->level_diffs);
+ diff->level_diffs = level_create();
+ if (diff->level_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two levels from the same policy.
+ */
+static int level_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_level_t *s1 = x;
+ const qpol_level_t *s2 = y;
+ apol_policy_t *p = arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+
+ if (qpol_level_get_name(q, s1, &name1) < 0 || qpol_level_get_name(q, s2, &name2) < 0)
+ return 0;
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_level_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, level_name_comp, (void *)policy);
+ return v;
+}
+
+int level_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_level_t *l1 = x;
+ const qpol_level_t *l2 = y;
+ const char *name1, *name2;
+ if (qpol_level_get_name(diff->orig_qpol, l1, &name1) < 0 || qpol_level_get_name(diff->mod_qpol, l2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new level difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the level that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling level_free() upon the returned
+ * value.
+ */
+static poldiff_level_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_level_t *pl;
+ int error;
+ if ((pl = calloc(1, sizeof(*pl))) == NULL || (pl->name = strdup(name)) == NULL ||
+ (pl->added_cats = apol_vector_create(free)) == NULL ||
+ (pl->removed_cats = apol_vector_create(free)) == NULL || (pl->unmodified_cats = apol_vector_create(free)) == NULL) {
+ error = errno;
+ level_free(pl);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pl->form = form;
+ return pl;
+}
+
+/**
+ * Given a level, return a vector of its allowed categories (in the
+ * form of strings). These will be sorted in policy order.
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the level came.
+ * @param level Level whose categories to get.
+ *
+ * @return Vector of category strings for the level. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *level_get_cats(const poldiff_t * diff, const apol_policy_t * p, const qpol_level_t * level)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_cat_t *cat;
+ const char *cat_name;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_level_get_cat_iter(q, level, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&cat) < 0 || qpol_cat_get_name(q, cat, &cat_name)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(v, (void *)cat_name) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_level_t *l = item;
+ const char *name = NULL;
+ poldiff_level_t *pl = NULL;
+ apol_policy_t *p;
+ qpol_policy_t *q;
+ apol_vector_t *v = NULL;
+ int error = 0, retval = -1;
+ if (form == POLDIFF_FORM_ADDED) {
+ p = diff->mod_pol;
+ q = diff->mod_qpol;
+ } else {
+ p = diff->orig_pol;
+ q = diff->orig_qpol;
+ }
+ if (qpol_level_get_name(q, l, &name) < 0 || (pl = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v = level_get_cats(diff, p, l)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ apol_vector_destroy(&pl->added_cats);
+ if ((pl->added_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else if (form == POLDIFF_FORM_REMOVED) {
+ apol_vector_destroy(&pl->removed_cats);
+ if ((pl->removed_cats = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(diff->level_diffs->diffs, pl) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->level_diffs->num_added++;
+ } else {
+ diff->level_diffs->num_removed++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ if (retval < 0) {
+ level_free(pl);
+ errno = error;
+ }
+ return retval;
+}
+
+/**
+ * Comparison function for two category names from the same policy.
+ *
+ * @param a Name of a category.
+ * @param b Name of another category.
+ * @param data qpol policy from which the categories originate.
+ *
+ * @return Less than zero, zero, or greater than zero based upon the
+ * categories' order within the policy.
+ */
+static int level_cat_comp(const void *a, const void *b, void *data)
+{
+ const char *name1 = (const char *)a;
+ const char *name2 = (const char *)b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const qpol_cat_t *cat1, *cat2;
+ qpol_policy_get_cat_by_name(q, name1, &cat1);
+ qpol_policy_get_cat_by_name(q, name2, &cat2);
+ assert(cat1 != NULL && cat2 != NULL);
+ uint32_t val1, val2;
+ qpol_cat_get_value(q, cat1, &val1);
+ qpol_cat_get_value(q, cat2, &val2);
+ return val1 - val2;
+}
+
+int level_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_level_t *l1 = x;
+ const qpol_level_t *l2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
+ const char *name;
+ poldiff_level_t *l = NULL;
+ int retval = -1, error = 0, compval;
+
+ if (qpol_level_get_name(diff->orig_qpol, l1, &name) < 0 ||
+ (v1 = level_get_cats(diff, diff->orig_pol, l1)) == NULL || (v2 = level_get_cats(diff, diff->mod_pol, l2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ compval = level_deep_diff_cats(diff, v1, v2, &added, &removed, &unmodified);
+ if (compval < 0) {
+ error = errno;
+ goto cleanup;
+ } else if (compval > 0) {
+ if ((l = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_destroy(&l->added_cats);
+ apol_vector_destroy(&l->removed_cats);
+ apol_vector_destroy(&l->unmodified_cats);
+ if ((l->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL ||
+ (l->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL ||
+ (l->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ apol_vector_sort(l->removed_cats, level_cat_comp, diff->orig_qpol);
+ apol_vector_sort(l->added_cats, level_cat_comp, diff->mod_qpol);
+ apol_vector_sort(l->unmodified_cats, level_cat_comp, diff->orig_qpol);
+ if (apol_vector_append(diff->level_diffs->diffs, l) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->level_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ apol_vector_destroy(&added);
+ apol_vector_destroy(&removed);
+ apol_vector_destroy(&unmodified);
+ if (retval != 0) {
+ level_free(l);
+ }
+ errno = error;
+ return retval;
+}
+
+poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form)
+{
+ const char *sens = apol_mls_level_get_sens(level);
+ const apol_vector_t *cats = apol_mls_level_get_cats(level);
+ poldiff_level_t *pl = NULL;
+ apol_vector_t **target;
+ if ((pl = calloc(1, sizeof(*pl))) == NULL ||
+ (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;;
+ }
+ pl->form = form;
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ target = &pl->added_cats;
+ } else if (form == POLDIFF_FORM_REMOVED) {
+ if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ target = &pl->removed_cats;
+ } else {
+ if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ return pl;
+ }
+ if ((*target = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) {
+ level_free(pl);
+ return NULL;
+ }
+ return pl;
+}
+
+void level_free(void *elem)
+{
+ poldiff_level_t *s = elem;
+ if (!elem)
+ return;
+ free(s->name);
+ apol_vector_destroy(&s->added_cats);
+ apol_vector_destroy(&s->removed_cats);
+ apol_vector_destroy(&s->unmodified_cats);
+ free(s);
+}
+
+int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2,
+ poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl)
+{
+ poldiff_level_t *u1 = NULL, *u2 = NULL;
+ apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
+ const char *sens1 = apol_mls_level_get_sens(level1);
+ const apol_vector_t *cats1 = apol_mls_level_get_cats(level1);
+ const char *sens2 = apol_mls_level_get_sens(level2);
+ const apol_vector_t *cats2 = apol_mls_level_get_cats(level2);
+ int retval = -1, compval;
+
+ *orig_pl = *mod_pl = NULL;
+ if (strcmp(sens1, sens2) != 0) {
+ /* sensitivities do not match, so don't check categories */
+ if ((u1 = make_diff(diff, POLDIFF_FORM_REMOVED, sens1)) == NULL ||
+ (u2 = make_diff(diff, POLDIFF_FORM_ADDED, sens2)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ level_free(u1);
+ level_free(u2);
+ return -1;
+ }
+ apol_vector_destroy(&u1->removed_cats);
+ apol_vector_destroy(&u2->added_cats);
+ if ((u1->removed_cats = apol_vector_create_from_vector(cats1, apol_str_strdup, NULL, free)) == NULL ||
+ (u2->added_cats = apol_vector_create_from_vector(cats2, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ level_free(u1);
+ level_free(u2);
+ return -1;
+ }
+ apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol);
+ apol_vector_sort(u2->added_cats, level_cat_comp, diff->mod_qpol);
+ *orig_pl = u1;
+ *mod_pl = u2;
+ return 0;
+ }
+
+ compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval > 0) {
+ if ((u1 = calloc(1, sizeof(*u1))) == NULL || (u1->name = strdup(sens1)) == NULL ||
+ (u1->added_cats = apol_vector_create_from_vector(added, apol_str_strdup, NULL, free)) == NULL ||
+ (u1->removed_cats = apol_vector_create_from_vector(removed, apol_str_strdup, NULL, free)) == NULL ||
+ (u1->unmodified_cats = apol_vector_create_from_vector(unmodified, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ level_free(u1);
+ goto cleanup;
+ }
+ apol_vector_sort(u1->added_cats, level_cat_comp, diff->mod_qpol);
+ apol_vector_sort(u1->removed_cats, level_cat_comp, diff->orig_qpol);
+ apol_vector_sort(u1->unmodified_cats, level_cat_comp, diff->orig_qpol);
+ u1->form = POLDIFF_FORM_MODIFIED;
+ *orig_pl = u1;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&added);
+ apol_vector_destroy(&removed);
+ apol_vector_destroy(&unmodified);
+ return retval;
+}
+
+int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added,
+ apol_vector_t ** removed, apol_vector_t ** unmodified)
+{
+ size_t i, j;
+ char *cat1, *cat2;
+ int compval, retval = -1, error = 0;
+ *added = *removed = *unmodified = NULL;
+ if ((*added = apol_vector_create(free)) == NULL ||
+ (*removed = apol_vector_create(free)) == NULL || (*unmodified = apol_vector_create(free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2)) {
+ break;
+ }
+ cat1 = (char *)apol_vector_get_element(v1, i);
+ cat2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(cat1, cat2);
+ if (compval < 0) {
+ if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat1);
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat2);
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*unmodified, cat1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat1);
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ cat1 = (char *)apol_vector_get_element(v1, i);
+ if ((cat1 = strdup(cat1)) == NULL || apol_vector_append(*removed, cat1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat1);
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ cat2 = (char *)apol_vector_get_element(v2, j);
+ if ((cat2 = strdup(cat2)) == NULL || apol_vector_append(*added, cat2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(cat2);
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(*added) > 0 || apol_vector_get_size(*removed) > 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ if (retval <= 0) {
+ /* if no differences found, then destroy all vectors */
+ apol_vector_destroy(added);
+ apol_vector_destroy(removed);
+ apol_vector_destroy(unmodified);
+ }
+ if (retval < 0) {
+ error = errno;
+ }
+ return retval;
+}
diff --git a/libpoldiff/src/level_internal.h b/libpoldiff/src/level_internal.h
new file mode 100644
index 0000000..4a0f603
--- /dev/null
+++ b/libpoldiff/src/level_internal.h
@@ -0,0 +1,208 @@
+/**
+ * @file
+ * Protected interface for computing semantic differences in levels,
+ * either from level declarations, user's default level, user's
+ * permitted range, or a range_transition's target range.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_LEVEL_INTERNAL_H
+#define POLDIFF_LEVEL_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_level_summary poldiff_level_summary_t;
+
+ struct poldiff_level
+ {
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_cats;
+ apol_vector_t *removed_cats;
+ apol_vector_t *unmodified_cats;
+ };
+
+/**
+ * Allocate and return a new poldiff_level_summary_t object.
+ *
+ * @return A new level summary. The caller must call level_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_level_summary_t *level_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_level_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param ls Reference to a level summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void level_destroy(poldiff_level_summary_t ** ls);
+
+/**
+ * Reset the state of all level differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int level_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all levels from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all levels. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *level_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_level_t objects, determining if they have the same
+ * level name or not.
+ *
+ * @param x The level from the original policy.
+ * @param y The level from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if level x is respectively less than, equal
+ * to, or greater than level y.
+ */
+ int level_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a level.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int level_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two levels for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * level.
+ *
+ * @param diff The policy difference structure associated with both
+ * levels and to which to add an entry if needed.
+ * @param x The level from the original policy.
+ * @param y The level from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int level_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+/*********************
+ * The remainder are protected functions that operate upon a single
+ * poldiff_level_t. These are used by user's default level, user's
+ * assigned range, etc.
+ *********************/
+
+/**
+ * Allocate and return a poldiff_level_t object. If the form is added
+ * or removed, set that respective vector to be all of the categories
+ * from the given level.
+ *
+ * @param level Level object to use as a template.
+ * @param form Form of difference for the level.
+ *
+ * @return Initialized level, or NULL upon error. Caller must call
+ * level_free() upon the returned value.
+ */
+ poldiff_level_t *level_create_from_apol_mls_level(const apol_mls_level_t * level, poldiff_form_e form);
+
+/**
+ * Deallocate all space associated with a poldiff_level_t, including
+ * the pointer itself.
+ *
+ * @param elem Pointer to a poldiff_level_t object. If NULL then do
+ * nothing.
+ */
+ void level_free(void *elem);
+
+/**
+ * Perform a deep diff of two levels. This will first compare the
+ * sensitivity names; if they match then it compares the vectors of
+ * category names. If the sensitivities do not match, then generate
+ * two poldiff_level_ts, one for the original level and one for
+ * modified level. If they do match then create just one
+ * poldiff_level_t and write it to orig_pl.
+ *
+ * @param diff Poldiff object, used for error reporting and for
+ * sorting the categories to policy order.
+ * @param level1 Original level.
+ * @param level2 Modified level.
+ * @param orig_pl Destination to where to write the poldiff_level_t,
+ * if the sensitivites do not match or if the categories do not match.
+ * @param mod_pl Destination to where to write the poldiff_level_t,
+ * if the sensitivities do not match.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int level_deep_diff_apol_mls_levels(poldiff_t * diff, const apol_mls_level_t * level1, const apol_mls_level_t * level2,
+ poldiff_level_t ** orig_pl, poldiff_level_t ** mod_pl);
+
+/**
+ * Calculate the differences between two sorted vectors of category
+ * names. Allocate the vectors added, removed, and unmodified; fill
+ * them with appropriate category names. The returned vectors'
+ * categories will be sorted alphabetically.
+ *
+ * @param diff Error handler.
+ * @param v1 First vector of category names, sorted alphabetically.
+ * @param v2 Other vector of category names, sorted alphabetically.
+ * @param added Reference to where to store added categories. The
+ * caller is responsible for calling apol_vector_destroy() upon the
+ * value. If no differences are found then this will be set to NULL.
+ * @param removed Reference to where to store removed categories. The
+ * caller is responsible for calling apol_vector_destroy() upon the
+ * value. If no differences are found then this will be set to NULL.
+ * @param unmodified Reference to where to store unmodified
+ * categories. The caller is responsible for calling
+ * apol_vector_destroy() upon the value. If no differences are found
+ * then this will be set to NULL.
+ *
+ * @return Greater than zero if a difference was found, zero upon no
+ * differences, less than zero on error.
+ */
+ int level_deep_diff_cats(poldiff_t * diff, const apol_vector_t * v1, const apol_vector_t * v2, apol_vector_t ** added,
+ apol_vector_t ** removed, apol_vector_t ** unmodified);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_LEVEL_INTERNAL_H */
diff --git a/libpoldiff/src/libpoldiff.map b/libpoldiff/src/libpoldiff.map
new file mode 100644
index 0000000..29950c0
--- /dev/null
+++ b/libpoldiff/src/libpoldiff.map
@@ -0,0 +1,50 @@
+VERS_1.2{
+ global:
+ poldiff_create;
+ poldiff_destroy;
+ poldiff_run;
+ poldiff_is_run;
+ poldiff_type_remap_*;
+ poldiff_get_*;
+ poldiff_attrib_*;
+ poldiff_avrule_*;
+ poldiff_bool_*;
+ poldiff_cat_*;
+ poldiff_class_*;
+ poldiff_common_*;
+ poldiff_level_*;
+ poldiff_range_*;
+ poldiff_range_trans_*;
+ poldiff_role_*;
+ poldiff_role_allow_*;
+ poldiff_role_trans_*;
+ poldiff_terule_*;
+ poldiff_type_*;
+ poldiff_user_*;
+ libpoldiff_get_version;
+ poldiff_enable_line_numbers;
+ local: *;
+};
+
+VERS_1.3{
+ global:
+ poldiff_avrule_get_stats_allow;
+ poldiff_avrule_get_stats_auditallow;
+ poldiff_avrule_get_stats_dontaudit;
+ poldiff_avrule_get_stats_neverallow;
+ poldiff_get_avrule_vector_allow;
+ poldiff_get_avrule_vector_auditallow;
+ poldiff_get_avrule_vector_dontaudit;
+ poldiff_get_avrule_vector_neverallow;
+ poldiff_component_record_*;
+ poldiff_range_get_min_added_cats;
+ poldiff_range_get_min_removed_cats;
+ poldiff_range_get_min_unmodified_cats;
+ poldiff_role_allow_get_unmodified_roles;
+ poldiff_terule_get_stats_change;
+ poldiff_terule_get_stats_member;
+ poldiff_terule_get_stats_trans;
+ poldiff_get_terule_vector_change;
+ poldiff_get_terule_vector_member;
+ poldiff_get_terule_vector_trans;
+} VERS_1.2;
diff --git a/libpoldiff/src/poldiff.c b/libpoldiff/src/poldiff.c
new file mode 100644
index 0000000..cce4fc4
--- /dev/null
+++ b/libpoldiff/src/poldiff.c
@@ -0,0 +1,814 @@
+/**
+ * @file
+ * Implementation for computing a semantic policy difference.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+#include <poldiff/component_record.h>
+
+#include <apol/util.h>
+#include <qpol/policy_extend.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * All policy items (object classes, types, rules, etc.) must
+ * implement at least these functions. Next, a record should be
+ * appended to the array 'component_records' below.
+ */
+struct poldiff_component_record
+{
+ const char *item_name;
+ uint32_t flag_bit;
+ poldiff_get_item_stats_fn_t get_stats;
+ poldiff_get_result_items_fn_t get_results;
+ poldiff_item_get_form_fn_t get_form;
+ poldiff_item_to_string_fn_t to_string;
+ poldiff_reset_fn_t reset;
+ poldiff_get_items_fn_t get_items;
+ poldiff_item_comp_fn_t comp;
+ poldiff_new_diff_fn_t new_diff;
+ poldiff_deep_diff_fn_t deep_diff;
+};
+
+static const poldiff_component_record_t component_records[] = {
+ {
+ "attribute",
+ POLDIFF_DIFF_ATTRIBS,
+ poldiff_attrib_get_stats,
+ poldiff_get_attrib_vector,
+ poldiff_attrib_get_form,
+ poldiff_attrib_to_string,
+ attrib_reset,
+ attrib_get_items,
+ attrib_comp,
+ attrib_new_diff,
+ attrib_deep_diff,
+ },
+ {
+ "Allow Rules",
+ POLDIFF_DIFF_AVALLOW,
+ poldiff_avrule_get_stats_allow,
+ poldiff_get_avrule_vector_allow,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_allow,
+ avrule_get_items_allow,
+ avrule_comp,
+ avrule_new_diff_allow,
+ avrule_deep_diff_allow,
+ },
+ {
+ "Audit Allow Rules",
+ POLDIFF_DIFF_AVAUDITALLOW,
+ poldiff_avrule_get_stats_auditallow,
+ poldiff_get_avrule_vector_auditallow,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_auditallow,
+ avrule_get_items_auditallow,
+ avrule_comp,
+ avrule_new_diff_auditallow,
+ avrule_deep_diff_auditallow,
+ },
+ {
+ "Don't Audit Rules",
+ POLDIFF_DIFF_AVDONTAUDIT,
+ poldiff_avrule_get_stats_dontaudit,
+ poldiff_get_avrule_vector_dontaudit,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_dontaudit,
+ avrule_get_items_dontaudit,
+ avrule_comp,
+ avrule_new_diff_dontaudit,
+ avrule_deep_diff_dontaudit,
+ },
+ {
+ "Never Allow Rules",
+ POLDIFF_DIFF_AVNEVERALLOW,
+ poldiff_avrule_get_stats_neverallow,
+ poldiff_get_avrule_vector_neverallow,
+ poldiff_avrule_get_form,
+ poldiff_avrule_to_string,
+ avrule_reset_neverallow,
+ avrule_get_items_neverallow,
+ avrule_comp,
+ avrule_new_diff_neverallow,
+ avrule_deep_diff_neverallow,
+ },
+ {
+ "bool",
+ POLDIFF_DIFF_BOOLS,
+ poldiff_bool_get_stats,
+ poldiff_get_bool_vector,
+ poldiff_bool_get_form,
+ poldiff_bool_to_string,
+ bool_reset,
+ bool_get_items,
+ bool_comp,
+ bool_new_diff,
+ bool_deep_diff,
+ },
+ {
+ "category",
+ POLDIFF_DIFF_CATS,
+ poldiff_cat_get_stats,
+ poldiff_get_cat_vector,
+ poldiff_cat_get_form,
+ poldiff_cat_to_string,
+ cat_reset,
+ cat_get_items,
+ cat_comp,
+ cat_new_diff,
+ cat_deep_diff,
+ },
+ {
+ "class",
+ POLDIFF_DIFF_CLASSES,
+ poldiff_class_get_stats,
+ poldiff_get_class_vector,
+ poldiff_class_get_form,
+ poldiff_class_to_string,
+ class_reset,
+ class_get_items,
+ class_comp,
+ class_new_diff,
+ class_deep_diff,
+ },
+ {
+ "common",
+ POLDIFF_DIFF_COMMONS,
+ poldiff_common_get_stats,
+ poldiff_get_common_vector,
+ poldiff_common_get_form,
+ poldiff_common_to_string,
+ common_reset,
+ common_get_items,
+ common_comp,
+ common_new_diff,
+ common_deep_diff,
+ },
+ {
+ "level",
+ POLDIFF_DIFF_LEVELS,
+ poldiff_level_get_stats,
+ poldiff_get_level_vector,
+ poldiff_level_get_form,
+ poldiff_level_to_string,
+ level_reset,
+ level_get_items,
+ level_comp,
+ level_new_diff,
+ level_deep_diff,
+ },
+ {
+ "range transition",
+ POLDIFF_DIFF_RANGE_TRANS,
+ poldiff_range_trans_get_stats,
+ poldiff_get_range_trans_vector,
+ poldiff_range_trans_get_form,
+ poldiff_range_trans_to_string,
+ range_trans_reset,
+ range_trans_get_items,
+ range_trans_comp,
+ range_trans_new_diff,
+ range_trans_deep_diff,
+ },
+ {
+ "role",
+ POLDIFF_DIFF_ROLES,
+ poldiff_role_get_stats,
+ poldiff_get_role_vector,
+ poldiff_role_get_form,
+ poldiff_role_to_string,
+ role_reset,
+ role_get_items,
+ role_comp,
+ role_new_diff,
+ role_deep_diff,
+ },
+ {
+ "role_allow",
+ POLDIFF_DIFF_ROLE_ALLOWS,
+ poldiff_role_allow_get_stats,
+ poldiff_get_role_allow_vector,
+ poldiff_role_allow_get_form,
+ poldiff_role_allow_to_string,
+ role_allow_reset,
+ role_allow_get_items,
+ role_allow_comp,
+ role_allow_new_diff,
+ role_allow_deep_diff,
+ },
+ {
+ "role_transition",
+ POLDIFF_DIFF_ROLE_TRANS,
+ poldiff_role_trans_get_stats,
+ poldiff_get_role_trans_vector,
+ poldiff_role_trans_get_form,
+ poldiff_role_trans_to_string,
+ role_trans_reset,
+ role_trans_get_items,
+ role_trans_comp,
+ role_trans_new_diff,
+ role_trans_deep_diff,
+ },
+ {
+ "Type Change rules",
+ POLDIFF_DIFF_TECHANGE,
+ poldiff_terule_get_stats_change,
+ poldiff_get_terule_vector_change,
+ poldiff_terule_get_form,
+ poldiff_terule_to_string,
+ terule_reset_change,
+ terule_get_items_change,
+ terule_comp,
+ terule_new_diff_change,
+ terule_deep_diff_change,
+ },
+ {
+ "Type Member Rules",
+ POLDIFF_DIFF_TEMEMBER,
+ poldiff_terule_get_stats_member,
+ poldiff_get_terule_vector_member,
+ poldiff_terule_get_form,
+ poldiff_terule_to_string,
+ terule_reset_member,
+ terule_get_items_member,
+ terule_comp,
+ terule_new_diff_member,
+ terule_deep_diff_member,
+ },
+ {
+ "Type Transition Rules",
+ POLDIFF_DIFF_TETRANS,
+ poldiff_terule_get_stats_trans,
+ poldiff_get_terule_vector_trans,
+ poldiff_terule_get_form,
+ poldiff_terule_to_string,
+ terule_reset_trans,
+ terule_get_items_trans,
+ terule_comp,
+ terule_new_diff_trans,
+ terule_deep_diff_trans,
+ },
+ {
+ "type",
+ POLDIFF_DIFF_TYPES,
+ poldiff_type_get_stats,
+ poldiff_get_type_vector,
+ poldiff_type_get_form,
+ poldiff_type_to_string,
+ type_reset,
+ type_get_items,
+ type_comp,
+ type_new_diff,
+ type_deep_diff,
+ },
+ {
+ "user",
+ POLDIFF_DIFF_USERS,
+ poldiff_user_get_stats,
+ poldiff_get_user_vector,
+ poldiff_user_get_form,
+ poldiff_user_to_string,
+ user_reset,
+ user_get_items,
+ user_comp,
+ user_new_diff,
+ user_deep_diff,
+ },
+};
+
+const poldiff_component_record_t *poldiff_get_component_record(uint32_t which)
+{
+ size_t i = 0;
+ size_t num_items;
+
+ num_items = sizeof(component_records) / sizeof(poldiff_component_record_t);
+ for (i = 0; i < num_items; i++) {
+ if (component_records[i].flag_bit == which)
+ return &component_records[i];
+ }
+ return NULL;
+}
+
+poldiff_t *poldiff_create(apol_policy_t * orig_policy, apol_policy_t * mod_policy, poldiff_handle_fn_t fn, void *callback_arg)
+{
+ poldiff_t *diff = NULL;
+ int error;
+
+ if (!orig_policy || !mod_policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(diff = calloc(1, sizeof(poldiff_t)))) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+ }
+ diff->orig_pol = orig_policy;
+ diff->mod_pol = mod_policy;
+ diff->orig_qpol = apol_policy_get_qpol(diff->orig_pol);
+ diff->mod_qpol = apol_policy_get_qpol(diff->mod_pol);
+ diff->fn = fn;
+ diff->handle_arg = callback_arg;
+ if ((diff->type_map = type_map_create()) == NULL) {
+ ERR(diff, "%s", strerror(ENOMEM));
+ poldiff_destroy(&diff);
+ errno = ENOMEM;
+ return NULL;
+ }
+ if (type_map_infer(diff) < 0) {
+ error = errno;
+ poldiff_destroy(&diff);
+ errno = error;
+ return NULL;
+ }
+
+ if ((diff->attrib_diffs = attrib_summary_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_ALLOW] = avrule_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_AUDITALLOW] = avrule_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_DONTAUDIT] = avrule_create()) == NULL ||
+ (diff->avrule_diffs[AVRULE_OFFSET_NEVERALLOW] = avrule_create()) == NULL ||
+ (diff->bool_diffs = bool_create()) == NULL ||
+ (diff->cat_diffs = cat_create()) == NULL ||
+ (diff->class_diffs = class_create()) == NULL ||
+ (diff->common_diffs = common_create()) == NULL ||
+ (diff->level_diffs = level_create()) == NULL ||
+ (diff->range_trans_diffs = range_trans_create()) == NULL ||
+ (diff->role_diffs = role_create()) == NULL ||
+ (diff->role_allow_diffs = role_allow_create()) == NULL ||
+ (diff->role_trans_diffs = role_trans_create()) == NULL ||
+ (diff->terule_diffs[TERULE_OFFSET_CHANGE] = terule_create()) == NULL ||
+ (diff->terule_diffs[TERULE_OFFSET_MEMBER] = terule_create()) == NULL ||
+ (diff->terule_diffs[TERULE_OFFSET_TRANS] = terule_create()) == NULL ||
+ (diff->type_diffs = type_summary_create()) == NULL || (diff->user_diffs = user_create()) == NULL) {
+ ERR(diff, "%s", strerror(ENOMEM));
+ poldiff_destroy(&diff);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ diff->policy_opts = QPOL_POLICY_OPTION_NO_RULES | QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+ return diff;
+}
+
+void poldiff_destroy(poldiff_t ** diff)
+{
+ if (!diff || !(*diff))
+ return;
+ apol_policy_destroy(&(*diff)->orig_pol);
+ apol_policy_destroy(&(*diff)->mod_pol);
+ apol_bst_destroy(&(*diff)->class_bst);
+ apol_bst_destroy(&(*diff)->perm_bst);
+ apol_bst_destroy(&(*diff)->bool_bst);
+
+ type_map_destroy(&(*diff)->type_map);
+ attrib_summary_destroy(&(*diff)->attrib_diffs);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_ALLOW]);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_AUDITALLOW]);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_DONTAUDIT]);
+ avrule_destroy(&(*diff)->avrule_diffs[AVRULE_OFFSET_NEVERALLOW]);
+ bool_destroy(&(*diff)->bool_diffs);
+ cat_destroy(&(*diff)->cat_diffs);
+ class_destroy(&(*diff)->class_diffs);
+ common_destroy(&(*diff)->common_diffs);
+ level_destroy(&(*diff)->level_diffs);
+ range_trans_destroy(&(*diff)->range_trans_diffs);
+ role_destroy(&(*diff)->role_diffs);
+ role_allow_destroy(&(*diff)->role_allow_diffs);
+ role_trans_destroy(&(*diff)->role_trans_diffs);
+ user_destroy(&(*diff)->user_diffs);
+ terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_CHANGE]);
+ terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_MEMBER]);
+ terule_destroy(&(*diff)->terule_diffs[TERULE_OFFSET_TRANS]);
+ type_summary_destroy(&(*diff)->type_diffs);
+ free(*diff);
+ *diff = NULL;
+}
+
+/**
+ * Given a particular policy item record (e.g., one for object
+ * classes), (re-)perform a diff of them between the two policies
+ * listed in the poldiff_t structure. Upon success, set the status
+ * flag within 'diff' to indicate that this diff is done.
+ *
+ * @param diff The policy difference structure containing the policies
+ * to compare and to populate with the item differences.
+ * @param component_record Item record containg callbacks to perform each
+ * step of the computation for a particular kind of item.
+ *
+ * @return 0 on success and < 0 on error; if the call fails; errno
+ * will be set and the only defined operation on the policy difference
+ * structure will be poldiff_destroy().
+ */
+static int poldiff_do_item_diff(poldiff_t * diff, const poldiff_component_record_t * component_record)
+{
+ apol_vector_t *p1_v = NULL, *p2_v = NULL;
+ int error = 0, retv;
+ size_t x = 0, y = 0;
+ void *item_x = NULL, *item_y = NULL;
+
+ if (!diff || !component_record) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ diff->diff_status &= (~component_record->flag_bit);
+
+ INFO(diff, "Getting %s items from original policy.", component_record->item_name);
+ p1_v = component_record->get_items(diff, diff->orig_pol);
+ if (!p1_v) {
+ error = errno;
+ goto err;
+ }
+
+ INFO(diff, "Getting %s items from modified policy.", component_record->item_name);
+ p2_v = component_record->get_items(diff, diff->mod_pol);
+ if (!p2_v) {
+ error = errno;
+ goto err;
+ }
+
+ INFO(diff, "Finding differences in %s.", component_record->item_name);
+ for (x = 0, y = 0; x < apol_vector_get_size(p1_v);) {
+ if (y >= apol_vector_get_size(p2_v))
+ break;
+ item_x = apol_vector_get_element(p1_v, x);
+ item_y = apol_vector_get_element(p2_v, y);
+ retv = component_record->comp(item_x, item_y, diff);
+ if (retv < 0) {
+ if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) {
+ error = errno;
+ goto err;
+ }
+ x++;
+ } else if (retv > 0) {
+ if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) {
+ error = errno;
+ goto err;
+ }
+ y++;
+ } else {
+ if (component_record->deep_diff(diff, item_x, item_y)) {
+ error = errno;
+ goto err;
+ }
+ x++;
+ y++;
+ }
+ }
+ for (; x < apol_vector_get_size(p1_v); x++) {
+ item_x = apol_vector_get_element(p1_v, x);
+ if (component_record->new_diff(diff, POLDIFF_FORM_REMOVED, item_x)) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (; y < apol_vector_get_size(p2_v); y++) {
+ item_y = apol_vector_get_element(p2_v, y);
+ if (component_record->new_diff(diff, POLDIFF_FORM_ADDED, item_y)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ apol_vector_destroy(&p1_v);
+ apol_vector_destroy(&p2_v);
+ diff->diff_status |= component_record->flag_bit;
+ return 0;
+ err:
+ apol_vector_destroy(&p1_v);
+ apol_vector_destroy(&p2_v);
+ errno = error;
+ return -1;
+}
+
+int poldiff_run(poldiff_t * diff, uint32_t flags)
+{
+ size_t i, num_items;
+
+ if (!flags)
+ return 0; /* nothing to do */
+
+ if (!diff) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ int policy_opts = diff->policy_opts;
+ if (flags & (POLDIFF_DIFF_AVRULES | POLDIFF_DIFF_TERULES)) {
+ policy_opts &= ~(QPOL_POLICY_OPTION_NO_RULES);
+ }
+ if (flags & POLDIFF_DIFF_AVNEVERALLOW) {
+ policy_opts &= ~(QPOL_POLICY_OPTION_NO_NEVERALLOWS);
+ }
+ if (policy_opts != diff->policy_opts) {
+ INFO(diff, "%s", "Loading rules from original policy.");
+ if (qpol_policy_rebuild(diff->orig_qpol, policy_opts)) {
+ return -1;
+ }
+ INFO(diff, "%s", "Loading rules from modified policy.");
+ if (qpol_policy_rebuild(diff->mod_qpol, policy_opts)) {
+ return -1;
+ }
+ // force flushing of existing pointers into policies
+ diff->remapped = 1;
+ diff->policy_opts = policy_opts;
+ }
+
+ num_items = sizeof(component_records) / sizeof(poldiff_component_record_t);
+ if (diff->remapped) {
+ for (i = 0; i < num_items; i++) {
+ if (component_records[i].flag_bit & POLDIFF_DIFF_REMAPPED) {
+ INFO(diff, "Resetting %s diff.", component_records[i].item_name);
+ if (component_records[i].reset(diff))
+ return -1;
+ }
+ }
+ diff->diff_status &= ~(POLDIFF_DIFF_REMAPPED);
+ diff->remapped = 0;
+ }
+
+ INFO(diff, "%s", "Building type map.");
+ if (type_map_build(diff)) {
+ return -1;
+ }
+
+ diff->line_numbers_enabled = 0;
+ for (i = 0; i < num_items; i++) {
+ /* item requested but not yet run */
+ if ((flags & component_records[i].flag_bit) && !(component_records[i].flag_bit & diff->diff_status)) {
+ INFO(diff, "Running %s diff.", component_records[i].item_name);
+ if (poldiff_do_item_diff(diff, &(component_records[i]))) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int poldiff_is_run(const poldiff_t * diff, uint32_t flags)
+{
+ if (!flags)
+ return 1; /* nothing to do */
+
+ if (!diff) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if ((diff->diff_status & flags) == flags) {
+ return 1;
+ }
+ return 0;
+}
+
+int poldiff_get_stats(const poldiff_t * diff, uint32_t flags, size_t stats[5])
+{
+ size_t i, j, num_items, tmp_stats[5] = { 0, 0, 0, 0, 0 };
+
+ if (!diff || !flags) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ stats[0] = stats[1] = stats[2] = stats[3] = stats[4] = 0;
+
+ num_items = sizeof(component_records) / sizeof(poldiff_component_record_t);
+ for (i = 0; i < num_items; i++) {
+ if (flags & component_records[i].flag_bit) {
+ component_records[i].get_stats(diff, tmp_stats);
+ for (j = 0; j < 5; j++)
+ stats[j] += tmp_stats[j];
+ }
+ }
+
+ return 0;
+}
+
+int poldiff_enable_line_numbers(poldiff_t * diff)
+{
+ int retval;
+ if (diff == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!diff->line_numbers_enabled) {
+ if (qpol_policy_build_syn_rule_table(diff->orig_qpol))
+ return -1;
+ if (qpol_policy_build_syn_rule_table(diff->mod_qpol))
+ return -1;
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_ALLOW)) < 0) {
+ return retval;
+ }
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_AUDITALLOW)) < 0) {
+ return retval;
+ }
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_DONTAUDIT)) < 0) {
+ return retval;
+ }
+ if ((retval = avrule_enable_line_numbers(diff, AVRULE_OFFSET_NEVERALLOW)) < 0) {
+ return retval;
+ }
+ if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_CHANGE)) < 0) {
+ return retval;
+ }
+ if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_MEMBER)) < 0) {
+ return retval;
+ }
+ if ((retval = terule_enable_line_numbers(diff, TERULE_OFFSET_TRANS)) < 0) {
+ return retval;
+ }
+ diff->line_numbers_enabled = 1;
+ }
+ return 0;
+}
+
+int poldiff_build_bsts(poldiff_t * diff)
+{
+ apol_vector_t *classes[2] = { NULL, NULL };
+ apol_vector_t *perms[2] = { NULL, NULL };
+ apol_vector_t *bools[2] = { NULL, NULL };
+ size_t i, j;
+ const qpol_class_t *cls;
+ qpol_bool_t *qbool;
+ const char *name;
+ char *new_name;
+ int retval = -1, error = 0;
+ if (diff->class_bst != NULL) {
+ return 0;
+ }
+ if ((diff->class_bst = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (diff->perm_bst = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (diff->bool_bst = apol_bst_create(apol_str_strcmp, free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < 2; i++) {
+ apol_policy_t *p = (i == 0 ? diff->orig_pol : diff->mod_pol);
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ if (apol_class_get_by_query(p, NULL, &classes[i]) < 0 ||
+ apol_perm_get_by_query(p, NULL, &perms[i]) < 0 || apol_bool_get_by_query(p, NULL, &bools[i]) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(classes[i]); j++) {
+ cls = apol_vector_get_element(classes[i], j);
+ if (qpol_class_get_name(q, cls, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL ||
+ apol_bst_insert_and_get(diff->class_bst, (void **)&new_name, NULL) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (j = 0; j < apol_vector_get_size(perms[i]); j++) {
+ name = (char *)apol_vector_get_element(perms[i], j);
+ if ((new_name = strdup(name)) == NULL ||
+ apol_bst_insert_and_get(diff->perm_bst, (void **)&new_name, NULL) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (j = 0; j < apol_vector_get_size(bools[i]); j++) {
+ qbool = (qpol_bool_t *) apol_vector_get_element(bools[i], j);
+ if (qpol_bool_get_name(q, qbool, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL ||
+ apol_bst_insert_and_get(diff->bool_bst, (void **)&new_name, NULL) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&classes[0]);
+ apol_vector_destroy(&classes[1]);
+ apol_vector_destroy(&perms[0]);
+ apol_vector_destroy(&perms[1]);
+ apol_vector_destroy(&bools[0]);
+ apol_vector_destroy(&bools[1]);
+ errno = error;
+ return retval;
+}
+
+static void poldiff_handle_default_callback(void *arg __attribute__ ((unused)),
+ poldiff_t * p __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case POLDIFF_MSG_INFO:
+ {
+ /* by default do not display these messages */
+ return;
+ }
+ case POLDIFF_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case POLDIFF_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (p == NULL || p->fn == NULL) {
+ poldiff_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else {
+ p->fn(p->handle_arg, p, level, fmt, ap);
+ }
+ va_end(ap);
+}
+
+poldiff_item_get_form_fn_t poldiff_component_record_get_form_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->get_form;
+}
+
+poldiff_item_to_string_fn_t poldiff_component_record_get_to_string_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->to_string;
+}
+
+poldiff_get_item_stats_fn_t poldiff_component_record_get_stats_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->get_stats;
+}
+
+poldiff_get_result_items_fn_t poldiff_component_record_get_results_fn(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->get_results;
+}
+
+const char *poldiff_component_record_get_label(const poldiff_component_record_t * diff)
+{
+ if (!diff) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->item_name;
+}
diff --git a/libpoldiff/src/poldiff_internal.h b/libpoldiff/src/poldiff_internal.h
new file mode 100644
index 0000000..e84cdb7
--- /dev/null
+++ b/libpoldiff/src/poldiff_internal.h
@@ -0,0 +1,239 @@
+/**
+ * @file
+ * Protected interface for computing semantic policy difference.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_POLDIFF_INTERNAL_H
+#define POLDIFF_POLDIFF_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <poldiff/poldiff.h>
+#include <apol/bst.h>
+
+ typedef enum
+ {
+ AVRULE_OFFSET_ALLOW = 0, AVRULE_OFFSET_AUDITALLOW,
+ AVRULE_OFFSET_DONTAUDIT, AVRULE_OFFSET_NEVERALLOW,
+ AVRULE_OFFSET_MAX
+ } avrule_offset_e;
+
+ typedef enum
+ {
+ TERULE_OFFSET_CHANGE = 0, TERULE_OFFSET_MEMBER,
+ TERULE_OFFSET_TRANS,
+ TERULE_OFFSET_MAX
+ } terule_offset_e;
+
+#include "attrib_internal.h"
+#include "avrule_internal.h"
+#include "bool_internal.h"
+#include "cat_internal.h"
+#include "class_internal.h"
+#include "level_internal.h"
+#include "range_internal.h"
+#include "range_trans_internal.h"
+#include "rbac_internal.h"
+#include "role_internal.h"
+#include "terule_internal.h"
+#include "user_internal.h"
+#include "type_internal.h"
+
+#include "type_map_internal.h"
+
+/* forward declarations */
+ struct poldiff_attrib_summary;
+ struct poldiff_avrule_summary;
+ struct poldiff_bool_summary;
+ struct poldiff_cat_summary;
+ struct poldiff_class_summary;
+ struct poldiff_common_summary;
+ struct poldiff_level_summary;
+ struct poldiff_range_trans_summary;
+ struct poldiff_role_summary;
+ struct poldiff_role_allow_summary;
+ struct poldiff_role_trans_summary;
+ struct poldiff_terule_summary;
+ struct poldiff_type_summary;
+ struct poldiff_user_summary;
+/* and so forth for ocon_summary structs */
+
+ struct poldiff
+ {
+ /** the "original" policy */
+ apol_policy_t *orig_pol;
+ /** the "modified" policy */
+ apol_policy_t *mod_pol;
+ /** pointer to original's qpol policy within orig_pol */
+ qpol_policy_t *orig_qpol;
+ /** pointer to modified's qpol policy within mod_pol */
+ qpol_policy_t *mod_qpol;
+ /** non-zero if rules' line numbers are accurate */
+ int line_numbers_enabled;
+ /** BST of duplicated strings, used when making pseudo-rules */
+ apol_bst_t *class_bst;
+ /** BST of duplicated strings, used when making pseudo-rules */
+ apol_bst_t *perm_bst;
+ /** BST of duplicated strings, used when making pseudo-rules */
+ apol_bst_t *bool_bst;
+ poldiff_handle_fn_t fn;
+ void *handle_arg;
+ /** set of POLDIF_DIFF_* bits for diffs run */
+ uint32_t diff_status;
+ struct poldiff_attrib_summary *attrib_diffs;
+ struct poldiff_avrule_summary *avrule_diffs[AVRULE_OFFSET_MAX];
+ struct poldiff_bool_summary *bool_diffs;
+ struct poldiff_cat_summary *cat_diffs;
+ struct poldiff_class_summary *class_diffs;
+ struct poldiff_common_summary *common_diffs;
+ struct poldiff_level_summary *level_diffs;
+ struct poldiff_range_trans_summary *range_trans_diffs;
+ struct poldiff_role_summary *role_diffs;
+ struct poldiff_role_allow_summary *role_allow_diffs;
+ struct poldiff_role_trans_summary *role_trans_diffs;
+ struct poldiff_terule_summary *terule_diffs[TERULE_OFFSET_MAX];
+ struct poldiff_type_summary *type_diffs;
+ struct poldiff_user_summary *user_diffs;
+ /* and so forth if we want ocon_diffs */
+ type_map_t *type_map;
+ /** most recently used flags to open the two policies */
+ int policy_opts;
+ /** set if type mapping was changed since last run */
+ int remapped;
+ };
+
+/**
+ * Callback function signature for getting a vector of all unique
+ * items of a given kind in a policy. The vector must be sorted
+ * prior to returning from this function.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @return a newly allocated vector of all unique items of the
+ * appropriate kind on success, or NULL on error; if the call fails,
+ * errno will be set.
+ */
+ typedef apol_vector_t *(*poldiff_get_items_fn_t) (poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Callback function signature for quickly comparing two items to
+ * determine if they are semantically the same item. This operation
+ * should quickly determine if the two are obviously different or
+ * not.
+ *
+ * @param x The item from the original policy.
+ * @param y The item from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * items.
+ *
+ * @return Expected return value from this function is < 0, 0, or > 0
+ * if item x is respectively less than, equal to, or greater than item y.
+ * This must be able to return a defined stable ordering for all items
+ * not semantically equivalent.
+ */
+ typedef int (*poldiff_item_comp_fn_t) (const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Callback function signature for creating, initializing and inserting
+ * a new semantic difference entry for an item.
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference, one of POLDIFF_FORM_ADDED or
+ * POLDIFF_FORM_REMOVED.
+ * @param item Item for which the entry is being created.
+ * @return Expected return value from this function is 0 on success and
+ * < 0 on error; if the call fails, it is expected to set errno and to
+ * leave the policy difference structure unchanged.
+ */
+ typedef int (*poldiff_new_diff_fn_t) (poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Callback function signature for computing the semantic difference of
+ * two items for which the compare callback returns 0. This function should
+ * calculate the difference of any properties of the items and if a difference
+ * is found to allocate, initialize, and insert an new semantic difference
+ * entry for that item.
+ * @param diff The policy difference structure associated with both items and
+ * to which to add an entry if needed.
+ * @param x The item from the original policy.
+ * @param y The item from the modified policy.
+ * @return Expected return value from this function is 0 on success and
+ * < 0 on error; if the call fails, it is expected to set errno and to
+ * leave the policy difference structure unchanged.
+ */
+ typedef int (*poldiff_deep_diff_fn_t) (poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Callback function signature for resetting the diff results for an
+ * item. called when mapping of the symbols used by the diff change.
+ * @param diff The policy difference structure containing the diffs
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * it is expected to set errno.
+ */
+ typedef int (*poldiff_reset_fn_t) (poldiff_t * diff);
+
+/******************** error handling code below ********************/
+
+#define POLDIFF_MSG_ERR 1
+#define POLDIFF_MSG_WARN 2
+#define POLDIFF_MSG_INFO 3
+
+/**
+ * Write a message to the callback stored within a poldiff error
+ * handler. If the msg_callback field is empty then suppress the
+ * message.
+ *
+ * @param p Error reporting handler. If NULL then write message to
+ * stderr.
+ * @param level Severity of message, one of POLDIFF_MSG_ERR,
+ * POLDIFF_MSG_WARN, or POLDIFF_MSG_INFO.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+ __attribute__ ((format(printf, 3, 4))) extern void poldiff_handle_msg(const poldiff_t * p, int level, const char *fmt, ...);
+
+#undef ERR
+#undef WARN
+#undef INFO
+
+#define ERR(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_ERR, format, __VA_ARGS__)
+#define WARN(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_WARN, format, __VA_ARGS__)
+#define INFO(handle, format, ...) poldiff_handle_msg(handle, POLDIFF_MSG_INFO, format, __VA_ARGS__)
+
+/**
+ * Build the BST for classes, permissions, and booleans if the
+ * policies have changed. This effectively provides a partial mapping
+ * of rules from one policy to the other.
+ *
+ * @param diff Policy difference structure containing policies to diff.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ int poldiff_build_bsts(poldiff_t * diff);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_POLDIFF_INTERNAL_H */
diff --git a/libpoldiff/src/range_diff.c b/libpoldiff/src/range_diff.c
new file mode 100644
index 0000000..e6c7c3a
--- /dev/null
+++ b/libpoldiff/src/range_diff.c
@@ -0,0 +1,420 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in ranges.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+
+struct poldiff_range
+{
+ apol_mls_range_t *orig_range;
+ apol_mls_range_t *mod_range;
+ /** a vector of poldiff_level_t */
+ apol_vector_t *levels;
+ apol_vector_t *min_added_cats;
+ apol_vector_t *min_removed_cats;
+ apol_vector_t *min_unmodified_cats;
+};
+
+apol_vector_t *poldiff_range_get_levels(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->levels;
+}
+
+const apol_mls_range_t *poldiff_range_get_original_range(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->orig_range;
+}
+
+const apol_mls_range_t *poldiff_range_get_modified_range(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->mod_range;
+}
+
+apol_vector_t *poldiff_range_get_min_added_cats(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->min_added_cats;
+}
+
+apol_vector_t *poldiff_range_get_min_removed_cats(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->min_removed_cats;
+}
+
+apol_vector_t *poldiff_range_get_min_unmodified_cats(const poldiff_range_t * range)
+{
+ if (range == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range->min_unmodified_cats;
+}
+
+char *poldiff_range_to_string_brief(const poldiff_t * diff, const poldiff_range_t * range)
+{
+ char *r1 = NULL, *r2 = NULL;
+ char *s = NULL, *t = NULL, *sep = "", *cat;
+ size_t len = 0, i;
+ if (range->orig_range != NULL && (r1 = apol_mls_range_render(diff->orig_pol, range->orig_range)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (range->mod_range != NULL && (r2 = apol_mls_range_render(diff->mod_pol, range->mod_range)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ assert(r1 != NULL || r2 != NULL);
+ if (r1 == NULL) {
+ if (apol_str_appendf(&s, &len, " range: %s\n", r2) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ } else if (r2 == NULL) {
+ if (apol_str_appendf(&s, &len, " range: %s\n", r1) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ } else {
+ if (apol_str_appendf(&s, &len, " range: %s --> %s\n", r1, r2) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if ((range->min_added_cats != NULL && apol_vector_get_size(range->min_added_cats) > 0) ||
+ (range->min_removed_cats != NULL && apol_vector_get_size(range->min_removed_cats) > 0) ||
+ (range->min_unmodified_cats != NULL && apol_vector_get_size(range->min_unmodified_cats) > 0)) {
+ if (apol_str_append(&s, &len, " minimum categories: ") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (i = 0; range->min_unmodified_cats != NULL && i < apol_vector_get_size(range->min_unmodified_cats); i++) {
+ cat = apol_vector_get_element(range->min_unmodified_cats, i);
+ if (apol_str_appendf(&s, &len, "%s%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; range->min_added_cats != NULL && i < apol_vector_get_size(range->min_added_cats); i++) {
+ cat = apol_vector_get_element(range->min_added_cats, i);
+ if (apol_str_appendf(&s, &len, "%s+%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ for (i = 0; range->min_removed_cats != NULL && i < apol_vector_get_size(range->min_removed_cats); i++) {
+ cat = apol_vector_get_element(range->min_removed_cats, i);
+ if (apol_str_appendf(&s, &len, "%s-%s", sep, cat) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ sep = ",";
+ }
+ if (apol_str_append(&s, &len, "\n") < 0) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(range->levels); i++) {
+ poldiff_level_t *level = apol_vector_get_element(range->levels, i);
+ if ((t = poldiff_level_to_string_brief(diff, level)) == NULL) {
+ goto cleanup;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ free(t);
+ t = NULL;
+ }
+ cleanup:
+ free(r1);
+ free(r2);
+ free(t);
+ return s;
+}
+
+poldiff_range_t *range_create(const poldiff_t * diff, const qpol_mls_range_t * orig_range, const qpol_mls_range_t * mod_range,
+ poldiff_form_e form)
+{
+ poldiff_range_t *pr = NULL;
+ apol_policy_t *p;
+ apol_mls_range_t *range;
+ apol_vector_t *levels = NULL;
+ poldiff_level_t *pl = NULL;
+ size_t i;
+ int retval = -1;
+ if ((pr = calloc(1, sizeof(*pr))) == NULL || (pr->levels = apol_vector_create(level_free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (orig_range != NULL && (pr->orig_range = apol_mls_range_create_from_qpol_mls_range(diff->orig_pol, orig_range)) == NULL) {
+ goto cleanup;
+ }
+ if (mod_range != NULL && (pr->mod_range = apol_mls_range_create_from_qpol_mls_range(diff->mod_pol, mod_range)) == NULL) {
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ p = diff->mod_pol;
+ range = pr->mod_range;
+ } else if (form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_REMOVE_TYPE) {
+ p = diff->orig_pol;
+ range = pr->orig_range;
+ } else if (form == POLDIFF_FORM_MODIFIED) {
+ /* don't fill in the range's levels here */
+ return pr;
+ } else {
+ /* should never get here */
+ assert(0);
+ return pr;
+ }
+ if ((levels = apol_mls_range_get_levels(p, range)) == NULL) {
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(levels); i++) {
+ apol_mls_level_t *l = apol_vector_get_element(levels, i);
+ const char *sens = apol_mls_level_get_sens(l);
+ const apol_vector_t *cats = apol_mls_level_get_cats(l);
+ if ((pl = calloc(1, sizeof(*pl))) == NULL ||
+ (pl->name = strdup(sens)) == NULL || (pl->unmodified_cats = apol_vector_create_with_capacity(1, free)) == NULL)
+ {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((pl->added_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL ||
+ (pl->removed_cats = apol_vector_create_with_capacity(1, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ } else if (form == POLDIFF_FORM_REMOVED) {
+ if ((pl->added_cats = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pl->removed_cats = apol_vector_create_from_vector(cats, apol_str_strdup, NULL, free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(pr->levels, pl) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ pl = NULL;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&levels);
+ if (retval != 0) {
+ level_free(pl);
+ range_destroy(&pr);
+ return NULL;
+ }
+ return pr;
+}
+
+void range_destroy(poldiff_range_t ** range)
+{
+ if (range != NULL && *range != NULL) {
+ apol_mls_range_destroy(&(*range)->orig_range);
+ apol_mls_range_destroy(&(*range)->mod_range);
+ apol_vector_destroy(&(*range)->levels);
+ apol_vector_destroy(&(*range)->min_added_cats);
+ apol_vector_destroy(&(*range)->min_removed_cats);
+ apol_vector_destroy(&(*range)->min_unmodified_cats);
+ free(*range);
+ *range = NULL;
+ }
+}
+
+/**
+ * Comparison function for two apol_mls_level_t objects from the same
+ * apol_mls_range_t. Sorts the levels in alphabetical order according
+ * to sensitivity.
+ */
+static int range_comp_alphabetize(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const apol_mls_level_t *l1 = a;
+ const apol_mls_level_t *l2 = b;
+ const char *sens1 = apol_mls_level_get_sens(l1);
+ const char *sens2 = apol_mls_level_get_sens(l2);
+ return strcmp(sens1, sens2);
+}
+
+/**
+ * Comparison function for two levels from the same poldiff_range_t.
+ * Sorts the levels by form; within each form sort them by policy
+ * order.
+ */
+static int range_comp(const void *a, const void *b, void *data)
+{
+ const poldiff_level_t *l1 = a;
+ const poldiff_level_t *l2 = b;
+ poldiff_t *diff = data;
+ qpol_policy_t *q;
+ const qpol_level_t *ql1, *ql2;
+ uint32_t v1, v2;
+ if (l1->form != l2->form) {
+ return l1->form - l2->form;
+ }
+ if (l1->form == POLDIFF_FORM_ADDED) {
+ q = diff->mod_qpol;
+ } else {
+ q = diff->orig_qpol;
+ }
+ qpol_policy_get_level_by_name(q, l1->name, &ql1);
+ qpol_policy_get_level_by_name(q, l2->name, &ql2);
+ qpol_level_get_value(q, ql1, &v1);
+ qpol_level_get_value(q, ql2, &v2);
+ assert(v1 != 0 && v2 != 0);
+ return v1 - v2;
+}
+
+int range_deep_diff(poldiff_t * diff, poldiff_range_t * range)
+{
+ apol_vector_t *orig_levels = NULL, *mod_levels = NULL;
+ apol_vector_t *added = NULL, *removed = NULL, *unmodified = NULL;
+ apol_mls_level_t *l1, *l2;
+ poldiff_level_t *pl1, *pl2;
+ size_t i, j;
+ int retval = -1, differences_found = 0, compval;
+ if ((orig_levels = apol_mls_range_get_levels(diff->orig_pol, range->orig_range)) == NULL ||
+ (mod_levels = apol_mls_range_get_levels(diff->mod_pol, range->mod_range)) == NULL) {
+ goto cleanup;
+ }
+ apol_vector_sort(orig_levels, range_comp_alphabetize, NULL);
+ apol_vector_sort(mod_levels, range_comp_alphabetize, NULL);
+ for (i = j = 0; i < apol_vector_get_size(orig_levels);) {
+ if (j >= apol_vector_get_size(mod_levels))
+ break;
+ l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i);
+ l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j);
+ pl1 = pl2 = NULL;
+ const char *sens1 = apol_mls_level_get_sens(l1);
+ const char *sens2 = apol_mls_level_get_sens(l2);
+ compval = strcmp(sens1, sens2);
+ if (compval < 0) {
+ if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL
+ || apol_vector_append(range->levels, pl1) < 0) {
+ level_free(pl1);
+ goto cleanup;
+ }
+ differences_found = 1;
+ i++;
+ } else if (compval > 0) {
+ if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL
+ || apol_vector_append(range->levels, pl2) < 0) {
+ level_free(pl2);
+ goto cleanup;
+ }
+ differences_found = 1;
+ j++;
+ } else {
+ if (level_deep_diff_apol_mls_levels(diff, l1, l2, &pl1, &pl2) < 0) {
+ goto cleanup;
+ }
+ assert(pl2 == NULL);
+ if (pl1 != NULL) {
+ if (apol_vector_append(range->levels, pl1) < 0) {
+ level_free(pl1);
+ goto cleanup;
+ }
+ differences_found = 1;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(orig_levels); i++) {
+ l1 = (apol_mls_level_t *) apol_vector_get_element(orig_levels, i);
+ if ((pl1 = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL
+ || apol_vector_append(range->levels, pl1) < 0) {
+ level_free(pl1);
+ goto cleanup;
+ }
+ differences_found = 1;
+ }
+ for (; j < apol_vector_get_size(mod_levels); j++) {
+ l2 = (apol_mls_level_t *) apol_vector_get_element(mod_levels, j);
+ if ((pl2 = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL
+ || apol_vector_append(range->levels, pl2) < 0) {
+ level_free(pl2);
+ goto cleanup;
+ }
+ differences_found = 1;
+ }
+ /* now check minimum category sets */
+ const apol_mls_level_t *low1 = apol_mls_range_get_low(range->orig_range);
+ const apol_vector_t *cats1 = apol_mls_level_get_cats(low1);
+ const apol_mls_level_t *low2 = apol_mls_range_get_low(range->mod_range);
+ const apol_vector_t *cats2 = apol_mls_level_get_cats(low2);
+ compval = level_deep_diff_cats(diff, cats1, cats2, &added, &removed, &unmodified);
+ if (compval < 0) {
+ goto cleanup;
+ } else if (compval > 0) {
+ differences_found = 1;
+ range->min_added_cats = added;
+ range->min_removed_cats = removed;
+ range->min_unmodified_cats = unmodified;
+ added = NULL;
+ removed = NULL;
+ unmodified = NULL;
+ }
+ if (differences_found) {
+ apol_vector_sort(range->levels, range_comp, diff);
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ apol_vector_destroy(&orig_levels);
+ apol_vector_destroy(&mod_levels);
+ apol_vector_destroy(&added);
+ apol_vector_destroy(&removed);
+ apol_vector_destroy(&unmodified);
+ return retval;
+}
diff --git a/libpoldiff/src/range_internal.h b/libpoldiff/src/range_internal.h
new file mode 100644
index 0000000..bf4d296
--- /dev/null
+++ b/libpoldiff/src/range_internal.h
@@ -0,0 +1,80 @@
+/**
+ * @file
+ * Protected interface for range differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_RANGE_INTERNAL_H
+#define POLDIFF_RANGE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Allocate and return a poldiff_range_t object. This will fill in
+ * the orig_range and mod_range strings. If the form is modified,
+ * then this will allocate the levels vector but leave it empty.
+ * Otherwise the levels vector will be filled with the levels that
+ * were added/removed.
+ *
+ * @param diff Diff object containing policies.
+ * @param orig_range Range from original policy, or NULL if there is
+ * no original range.
+ * @param mod_range Range from modified policy, or NULL if there is no
+ * modified range.
+ * @param form Form of the range.
+ *
+ * @return An initialized range, or NULL upon error. Caller must call
+ * range_destroy() upon the returned value.
+ */
+ poldiff_range_t *range_create(const poldiff_t * diff, const qpol_mls_range_t * orig_range,
+ const qpol_mls_range_t * mod_range, poldiff_form_e form);
+
+/**
+ * Deallocate all space for a range, including the pointer itself.
+ * Afterwards set the pointer to NULL.
+ *
+ * @param range Reference to a range to destroy.
+ */
+ void range_destroy(poldiff_range_t ** range);
+
+/**
+ * Calculate the differences between two ranges (that are stored
+ * within the poldiff_range_t object). This involves two things:
+ * changes in the expanded levels, and changes to minimum category
+ * sets. If differences are found then the range's levels vector will
+ * be filled with those differences.
+ *
+ * @param diff Diff object containing policies.
+ * @param range Range object to diff.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+ int range_deep_diff(poldiff_t * diff, poldiff_range_t * range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGE_INTERNAL_H */
diff --git a/libpoldiff/src/range_trans_diff.c b/libpoldiff/src/range_trans_diff.c
new file mode 100644
index 0000000..7394c89
--- /dev/null
+++ b/libpoldiff/src/range_trans_diff.c
@@ -0,0 +1,520 @@
+/**
+ * @file
+ * Implementation for computing a semantic differences in range
+ * transition rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/mls-query.h>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_range_trans_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_range_trans
+{
+ char *source;
+ char *target;
+ char *target_class;
+ poldiff_form_e form;
+ poldiff_range_t *range;
+};
+
+void poldiff_range_trans_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->range_trans_diffs->num_added;
+ stats[1] = diff->range_trans_diffs->num_removed;
+ stats[2] = diff->range_trans_diffs->num_modified;
+ stats[3] = diff->range_trans_diffs->num_added_type;
+ stats[4] = diff->range_trans_diffs->num_removed_type;
+}
+
+char *poldiff_range_trans_to_string(const poldiff_t * diff, const void *range_trans)
+{
+ const poldiff_range_trans_t *rt = range_trans;
+ const poldiff_range_t *range = poldiff_range_trans_get_range(rt);
+ const apol_mls_range_t *orig_range = poldiff_range_get_original_range(range);
+ const apol_mls_range_t *mod_range = poldiff_range_get_modified_range(range);
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || range_trans == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (rt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ char *t = NULL;
+ if ((t = apol_mls_range_render(diff->mod_pol, mod_range)) == NULL ||
+ apol_str_appendf(&s, &len, "+ range_transition %s %s : %s %s;", rt->source, rt->target,
+ rt->target_class, t) < 0) {
+ free(t);
+ goto cleanup;
+ }
+ free(t);
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ char *t = NULL;
+ if ((t = apol_mls_range_render(diff->orig_pol, orig_range)) == NULL ||
+ apol_str_appendf(&s, &len, "- range_transition %s %s : %s %s;", rt->source, rt->target,
+ rt->target_class, t) < 0) {
+ free(t);
+ goto cleanup;
+ }
+ free(t);
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ char *t;
+ if ((t = poldiff_range_to_string_brief(diff, range)) == NULL ||
+ apol_str_appendf(&s, &len, "* range_transition %s %s : %s\n%s", rt->source, rt->target,
+ rt->target_class, t) < 0) {
+ free(t);
+ goto cleanup;
+ }
+ free(t);
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ cleanup:
+ /* if this is reached then an error occurred */
+ ERR(diff, "%s", strerror(ENOMEM));
+ free(s);
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_range_trans_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->range_trans_diffs->diffs;
+}
+
+const char *poldiff_range_trans_get_source_type(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->source;
+}
+
+const char *poldiff_range_trans_get_target_type(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->target;
+}
+
+const char *poldiff_range_trans_get_target_class(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->target_class;
+}
+
+const poldiff_range_t *poldiff_range_trans_get_range(const poldiff_range_trans_t * range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return range_trans->range;
+}
+
+poldiff_form_e poldiff_range_trans_get_form(const void *range_trans)
+{
+ if (range_trans == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_range_trans_t *)range_trans)->form;
+}
+
+/**
+ * Destroy all space used by a poldiff_range_trans_t, including the
+ * pointer itself.
+ */
+static void range_trans_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_range_trans_t *rt = (poldiff_range_trans_t *) elem;
+ free(rt->source);
+ free(rt->target);
+ free(rt->target_class);
+ range_destroy(&rt->range);
+ free(rt);
+ }
+}
+
+poldiff_range_trans_summary_t *range_trans_create(void)
+{
+ poldiff_range_trans_summary_t *rts = calloc(1, sizeof(*rts));
+ if (rts == NULL) {
+ return NULL;
+ }
+ if ((rts->diffs = apol_vector_create(range_trans_free)) == NULL) {
+ range_trans_destroy(&rts);
+ return NULL;
+ }
+ return rts;
+}
+
+void range_trans_destroy(poldiff_range_trans_summary_t ** rts)
+{
+ if (rts != NULL && *rts != NULL) {
+ apol_vector_destroy(&(*rts)->diffs);
+ free(*rts);
+ *rts = NULL;
+ }
+}
+
+typedef struct pseudo_range_trans
+{
+ uint32_t source_type, target_type;
+ /* pointer into a policy's class's symbol table */
+ const char *target_class;
+ const qpol_mls_range_t *range;
+} pseudo_range_trans_t;
+
+static void range_trans_free_item(void *item)
+{
+ if (item != NULL) {
+ pseudo_range_trans_t *prt = item;
+ free(prt);
+ }
+}
+
+int range_trans_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_range_trans_t *p1 = x;
+ const pseudo_range_trans_t *p2 = y;
+
+ if (p1->source_type != p2->source_type) {
+ return p1->source_type - p2->source_type;
+ }
+ if (p1->target_type != p2->target_type) {
+ return p1->target_type - p2->target_type;
+ }
+ return strcmp(p1->target_class, p2->target_class);
+}
+
+int range_trans_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ range_trans_destroy(&diff->range_trans_diffs);
+ diff->range_trans_diffs = range_trans_create();
+ if (diff->range_trans_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate and return a new range trans difference object. If the
+ * pseudo-range trans's source and/or target expands to multiple read
+ * types, then just choose the first one for display.
+ */
+static poldiff_range_trans_t *make_range_trans_diff(const poldiff_t * diff, poldiff_form_e form, const pseudo_range_trans_t * prt)
+{
+ poldiff_range_trans_t *rt = NULL;
+ const char *n1, *n2;
+ int error;
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ n1 = type_map_get_name(diff, prt->source_type, POLDIFF_POLICY_MOD);
+ n2 = type_map_get_name(diff, prt->target_type, POLDIFF_POLICY_MOD);
+ } else {
+ n1 = type_map_get_name(diff, prt->source_type, POLDIFF_POLICY_ORIG);
+ n2 = type_map_get_name(diff, prt->target_type, POLDIFF_POLICY_ORIG);
+ }
+ assert(n1 != NULL && n2 != NULL);
+ if ((rt = calloc(1, sizeof(*rt))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ if ((rt->source = strdup(n1)) == NULL ||
+ (rt->target = strdup(n2)) == NULL || (rt->target_class = strdup(prt->target_class)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(errno));
+ range_trans_free(rt);
+ errno = error;
+ return NULL;
+ }
+ rt->form = form;
+ return rt;
+}
+
+int range_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const pseudo_range_trans_t *prt = (const pseudo_range_trans_t *)item;
+ const apol_vector_t *v1, *v2;
+ const qpol_mls_range_t *orig_range = NULL, *mod_range = NULL;
+ poldiff_range_trans_t *rt = NULL;
+ int error;
+
+ /* check if form should really become ADD_TYPE / REMOVE_TYPE,
+ * by seeing if the /other/ policy's reverse lookup is
+ * empty */
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((v1 = type_map_lookup_reverse(diff, prt->source_type, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, prt->target_type, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ mod_range = prt->range;
+ } else {
+ if ((v1 = type_map_lookup_reverse(diff, prt->source_type, POLDIFF_POLICY_MOD)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, prt->target_type, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ orig_range = prt->range;
+ }
+ if ((rt = make_range_trans_diff(diff, form, prt)) == NULL ||
+ (rt->range = range_create(diff, orig_range, mod_range, form)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(diff->range_trans_diffs->diffs, rt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ /* increment appropriate counter */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ diff->range_trans_diffs->num_added++;
+ break;
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff->range_trans_diffs->num_added_type++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ diff->range_trans_diffs->num_removed++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff->range_trans_diffs->num_removed_type++;
+ break;
+ }
+ default:
+ {
+ /* not reachable */
+ assert(0);
+ }
+ }
+ return 0;
+ cleanup:
+ range_trans_free(rt);
+ errno = error;
+ return -1;
+}
+
+/**
+ * Compare two pseudo range transition rules from the same policy.
+ * Compares the pseudo source type, pseudo target type, and target
+ * class.
+ *
+ * @param x A pseudo_range_trans_t entry.
+ * @param y A pseudo_range_trans_t entry.
+ * @param arg The policy difference structure.
+ *
+ * @return < 0, 0, or > 0 if the first rule is respectively less than,
+ * equal to, or greater than the second. If the return value would be 0
+ * but the default role is different a warning is issued.
+ */
+static int pseudo_range_trans_comp(const void *x, const void *y, void *arg)
+{
+ const pseudo_range_trans_t *a = x;
+ const pseudo_range_trans_t *b = y;
+ poldiff_t *diff = arg;
+ int retval = range_trans_comp(a, b, diff);
+ return retval;
+}
+
+apol_vector_t *range_trans_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_range_trans_t *qrt = NULL;
+ const qpol_type_t *source_type, *target_type;
+ const qpol_class_t *target_class;
+ const char *class_name;
+ const qpol_mls_range_t *range;
+ pseudo_range_trans_t *prt = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0, which_pol;
+
+ which_pol = (policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ if (qpol_policy_get_range_trans_iter(q, &iter)) {
+ error = errno;
+ goto err;
+ }
+ if ((v = apol_vector_create(range_trans_free_item)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&qrt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_range_trans_get_source_type(q, qrt, &source_type) < 0 ||
+ qpol_range_trans_get_target_type(q, qrt, &target_type) < 0 ||
+ qpol_range_trans_get_target_class(q, qrt, &target_class) < 0 ||
+ qpol_class_get_name(q, target_class, &class_name) < 0 || qpol_range_trans_get_range(q, qrt, &range) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (!(prt = calloc(1, sizeof(*prt)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ prt->source_type = type_map_lookup(diff, source_type, which_pol);
+ prt->target_type = type_map_lookup(diff, target_type, which_pol);
+ prt->target_class = class_name;
+ prt->range = range;
+ if (apol_vector_append(v, prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ prt = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort_uniquify(v, pseudo_range_trans_comp, diff);
+ return v;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&v);
+ free(prt);
+ errno = error;
+ return NULL;
+}
+
+int range_trans_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const pseudo_range_trans_t *prt1 = x;
+ const pseudo_range_trans_t *prt2 = y;
+ poldiff_range_t *range = NULL;
+ poldiff_range_trans_t *rt = NULL;
+ int error = 0, retval = -1;
+
+ if ((range = range_create(diff, prt1->range, prt2->range, POLDIFF_FORM_MODIFIED)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((retval = range_deep_diff(diff, range)) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (retval > 0) {
+ if ((rt = make_range_trans_diff(diff, POLDIFF_FORM_MODIFIED, prt1)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ rt->range = range;
+ range = NULL;
+ if (apol_vector_append(diff->range_trans_diffs->diffs, rt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->range_trans_diffs->num_modified++;
+ rt = NULL;
+ }
+ retval = 0;
+ cleanup:
+ range_destroy(&range);
+ range_trans_free(rt);
+ if (retval != 0) {
+ errno = error;
+ }
+ return retval;
+}
diff --git a/libpoldiff/src/range_trans_internal.h b/libpoldiff/src/range_trans_internal.h
new file mode 100644
index 0000000..e1bfe11
--- /dev/null
+++ b/libpoldiff/src/range_trans_internal.h
@@ -0,0 +1,124 @@
+/**
+ * @file
+ * Protected interface for range transition rule differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_RANGE_TRANS_INTERNAL_H
+#define POLDIFF_RANGE_TRANS_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_range_trans_summary poldiff_range_trans_summary_t;
+
+/**
+ * Allocate and return a new poldiff_range_trans_summary_t object.
+ *
+ * @return A new range transition summary. The caller must call
+ * range_trans_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ poldiff_range_trans_summary_t *range_trans_create(void);
+
+/**
+ * Deallocate all space associated with a
+ * poldiff_range_trans_summary_t object, including the pointer itself.
+ * If the pointer is already NULL then do nothing.
+ *
+ * @param rts Reference to a range transition summary to destroy. The
+ * pointer will be set to NULL afterwards.
+ */
+ void range_trans_destroy(poldiff_range_trans_summary_t ** rts);
+
+/**
+ * Reset the state of all range transition rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int range_trans_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all range transition rules from the given policy,
+ * sorted by source type.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all range transition rules (of
+ * type pseudo_range_trans_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *range_trans_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_range_trans_t objects, determining if they have
+ * the same source type, target type, and target class or not.
+ *
+ * @param x The range transition from the original policy.
+ * @param y The range transition from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if x is respectively less than, equal to, or
+ * greater than source role of y.
+ */
+ int range_trans_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a range transition rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int range_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two range transition rules for
+ * which the compare callback returns 0. If a difference is found
+ * then allocate, initialize, and insert a new semantic difference
+ * entry for that range transition rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * rules and to which to add an entry if needed.
+ * @param x The range transition rule from the original policy.
+ * @param y The range transition rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int range_trans_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RANGE_TRANS_INTERNAL_H */
diff --git a/libpoldiff/src/rbac_diff.c b/libpoldiff/src/rbac_diff.c
new file mode 100644
index 0000000..bc47ba7
--- /dev/null
+++ b/libpoldiff/src/rbac_diff.c
@@ -0,0 +1,1052 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in role allow
+ * rules and role_transition rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/bst.h>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_role_allow_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_role_trans_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_role_allow
+{
+ const char *source_role;
+ poldiff_form_e form;
+ apol_vector_t *orig_roles;
+ apol_vector_t *added_roles;
+ apol_vector_t *removed_roles;
+};
+
+struct poldiff_role_trans
+{
+ const char *source_role;
+ char *target_type;
+ const char *orig_default;
+ const char *mod_default;
+ poldiff_form_e form;
+};
+
+/**************** role allow functions *******************/
+
+void poldiff_role_allow_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->role_allow_diffs->num_added;
+ stats[1] = diff->role_allow_diffs->num_removed;
+ stats[2] = diff->role_allow_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_role_allow_to_string(const poldiff_t * diff, const void *role_allow)
+{
+ const poldiff_role_allow_t *ra = role_allow;
+ size_t len = 0, i;
+ char *s = NULL, *role;
+ if (diff == NULL || role_allow == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (ra->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ allow %s { ", ra->source_role) < 0) {
+ s = NULL;
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(ra->added_roles); i++) {
+ role = apol_vector_get_element(ra->added_roles, i);
+ if (apol_str_appendf(&s, &len, "%s ", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, "};") < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- allow %s { ", ra->source_role) < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(ra->removed_roles); i++) {
+ role = apol_vector_get_element(ra->removed_roles, i);
+ if (apol_str_appendf(&s, &len, "%s ", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, "};") < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* allow %s { ", ra->source_role) < 0) {
+ s = NULL;
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(ra->orig_roles); i++) {
+ role = apol_vector_get_element(ra->orig_roles, i);
+ if (apol_str_appendf(&s, &len, "%s ", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(ra->added_roles); i++) {
+ role = apol_vector_get_element(ra->added_roles, i);
+ if (apol_str_appendf(&s, &len, "+%s ", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(ra->removed_roles); i++) {
+ role = apol_vector_get_element(ra->removed_roles, i);
+ if (apol_str_appendf(&s, &len, "-%s ", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, "};") < 0) {
+ break;
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ /* if this is reached then an error occurred */
+ err:
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_role_allow_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->role_allow_diffs->diffs;
+}
+
+const char *poldiff_role_allow_get_name(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->source_role;
+}
+
+poldiff_form_e poldiff_role_allow_get_form(const void *role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_role_allow_t *)role_allow)->form;
+}
+
+const apol_vector_t *poldiff_role_allow_get_unmodified_roles(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->orig_roles;
+}
+
+const apol_vector_t *poldiff_role_allow_get_added_roles(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->added_roles;
+}
+
+const apol_vector_t *poldiff_role_allow_get_removed_roles(const poldiff_role_allow_t * role_allow)
+{
+ if (role_allow == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_allow->removed_roles;
+}
+
+static void role_allow_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_role_allow_t *r = (poldiff_role_allow_t *) elem;
+ apol_vector_destroy(&r->orig_roles);
+ apol_vector_destroy(&r->added_roles);
+ apol_vector_destroy(&r->removed_roles);
+ free(r);
+ }
+}
+
+poldiff_role_allow_summary_t *role_allow_create(void)
+{
+ poldiff_role_allow_summary_t *ras = calloc(1, sizeof(*ras));
+ if (ras == NULL) {
+ return NULL;
+ }
+ if ((ras->diffs = apol_vector_create(role_allow_free)) == NULL) {
+ role_allow_destroy(&ras);
+ return NULL;
+ }
+ return ras;
+}
+
+void role_allow_destroy(poldiff_role_allow_summary_t ** ras)
+{
+ if (ras != NULL && *ras != NULL) {
+ apol_vector_destroy(&(*ras)->diffs);
+ free(*ras);
+ *ras = NULL;
+ }
+}
+
+typedef struct pseudo_role_allow
+{
+ const char *source_role;
+ apol_vector_t *target_roles;
+} pseudo_role_allow_t;
+
+static void role_allow_free_item(void *item)
+{
+ pseudo_role_allow_t *pra = item;
+
+ if (!item)
+ return;
+
+ /* no need to free source name or target role names */
+ apol_vector_destroy(&pra->target_roles);
+ free(item);
+}
+
+static int role_allow_source_comp(const void *x, const void *y, void *arg __attribute__ ((unused)))
+{
+ const pseudo_role_allow_t *p1 = x;
+ const pseudo_role_allow_t *p2 = y;
+
+ return strcmp(p1->source_role, p2->source_role);
+}
+
+apol_vector_t *role_allow_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *tmp = NULL, *v = NULL;
+ int error = 0, retv;
+ size_t i;
+ apol_bst_t *bst = NULL;
+ pseudo_role_allow_t *pra = NULL;
+ const qpol_role_t *sr = NULL, *tr = NULL;
+ const char *sr_name = NULL, *tr_name = NULL;
+ const qpol_role_allow_t *qra = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (qpol_policy_get_role_allow_iter(q, &iter) < 0) {
+ return NULL;
+ }
+
+ tmp = apol_vector_create_from_iter(iter, NULL);
+ if (tmp == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+
+ bst = apol_bst_create(role_allow_source_comp, role_allow_free_item);
+
+ for (i = 0; i < apol_vector_get_size(tmp); i++) {
+ qra = apol_vector_get_element(tmp, i);
+ if (!(pra = calloc(1, sizeof(*pra))) || (!(pra->target_roles = apol_vector_create_with_capacity(1, NULL)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_role_allow_get_source_role(q, qra, &sr) || qpol_role_get_name(q, sr, &sr_name)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ sr = NULL;
+ if (qpol_role_allow_get_target_role(q, qra, &tr) || qpol_role_get_name(q, tr, &tr_name)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tr = NULL;
+ pra->source_role = sr_name;
+ retv = apol_bst_insert_and_get(bst, (void **)&pra, NULL);
+ if (retv < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ apol_vector_append_unique(pra->target_roles, (void *)tr_name, apol_str_strcmp, NULL);
+ pra = NULL;
+ }
+ apol_vector_destroy(&tmp);
+
+ v = apol_bst_get_vector(bst, 1);
+ if (!v) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ apol_bst_destroy(&bst);
+
+ return v;
+
+ err:
+ role_allow_free_item(pra);
+ apol_bst_destroy(&bst);
+ errno = error;
+ return NULL;
+}
+
+int role_allow_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_role_allow_t *p1 = x;
+ const pseudo_role_allow_t *p2 = y;
+
+ return strcmp(p1->source_role, p2->source_role);
+}
+
+int role_allow_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ role_allow_destroy(&diff->role_allow_diffs);
+ diff->role_allow_diffs = role_allow_create();
+ if (diff->role_allow_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Allocate and return a new role allow rule difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param source_role Name of the source role in the role allow rule.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling role_allow_free() upon the returned
+ * value.
+ */
+static poldiff_role_allow_t *make_ra_diff(const poldiff_t * diff, poldiff_form_e form, const char *source_role)
+{
+ poldiff_role_allow_t *ra = NULL;
+ int error = 0;
+ if ((ra = calloc(1, sizeof(*ra))) == NULL ||
+ (ra->source_role = source_role) == NULL ||
+ (ra->added_roles = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (ra->orig_roles = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (ra->removed_roles = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ role_allow_free(ra);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ ra->form = form;
+ return ra;
+}
+
+int role_allow_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ pseudo_role_allow_t *ra = (pseudo_role_allow_t *) item;
+ poldiff_role_allow_t *pra;
+ int error;
+
+ pra = make_ra_diff(diff, form, ra->source_role);
+ if (pra == NULL) {
+ return -1;
+ }
+ int rt;
+ if (form == POLDIFF_FORM_ADDED) {
+ rt = apol_vector_cat(pra->added_roles, ra->target_roles);
+ } else {
+ rt = apol_vector_cat(pra->removed_roles, ra->target_roles);
+ }
+ if (rt < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_allow_free(pra);
+ errno = error;
+ return -1;
+ }
+ if (apol_vector_append(diff->role_allow_diffs->diffs, pra) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_allow_free(pra);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->role_allow_diffs->num_added++;
+ } else {
+ diff->role_allow_diffs->num_removed++;
+ }
+ return 0;
+}
+
+int role_allow_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const pseudo_role_allow_t *p1 = x;
+ const pseudo_role_allow_t *p2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *role1, *role2;
+ poldiff_role_allow_t *pra = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ v1 = p1->target_roles;
+ v2 = p2->target_roles;
+
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ role1 = (char *)apol_vector_get_element(v1, i);
+ role2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(role1, role2);
+ if (pra == NULL) {
+ if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (compval < 0) {
+ if (apol_vector_append(pra->removed_roles, role1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if (apol_vector_append(pra->added_roles, role2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if (apol_vector_append(pra->orig_roles, role1) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ role1 = (char *)apol_vector_get_element(v1, i);
+ if (pra == NULL) {
+ if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(pra->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ role2 = (char *)apol_vector_get_element(v2, j);
+ if (pra == NULL) {
+ if ((pra = make_ra_diff(diff, POLDIFF_FORM_MODIFIED, p1->source_role)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(pra->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(pra->added_roles) || apol_vector_get_size(pra->removed_roles)) {
+ apol_vector_sort(pra->removed_roles, apol_str_strcmp, NULL);
+ apol_vector_sort(pra->added_roles, apol_str_strcmp, NULL);
+ apol_vector_sort(pra->orig_roles, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->role_allow_diffs->diffs, pra) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->role_allow_diffs->num_modified++;
+ } else {
+ role_allow_free(pra);
+ pra = NULL;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ role_allow_free(pra);
+ }
+ errno = error;
+ return retval;
+}
+
+/**************** role_transition functions *******************/
+
+void poldiff_role_trans_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->role_trans_diffs->num_added;
+ stats[1] = diff->role_trans_diffs->num_removed;
+ stats[2] = diff->role_trans_diffs->num_modified;
+ stats[3] = diff->role_trans_diffs->num_added_type;
+ stats[4] = diff->role_trans_diffs->num_removed_type;
+}
+
+extern char *poldiff_role_trans_to_string(const poldiff_t * diff, const void *role_trans)
+{
+ const poldiff_role_trans_t *rt = role_trans;
+ char *s = NULL;
+
+ if (diff == NULL || role_trans == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (rt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ if (asprintf(&s, "+ role_transition %s %s %s;", rt->source_role, rt->target_type, rt->mod_default) < 0)
+ break;
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ if (asprintf(&s, "- role_transition %s %s %s;", rt->source_role, rt->target_type, rt->orig_default) < 0)
+ break;
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (asprintf
+ (&s, "* role_transition %s %s { +%s -%s };", rt->source_role, rt->target_type, rt->mod_default,
+ rt->orig_default) < 0)
+ break;
+ return s;
+ }
+ case POLDIFF_FORM_NONE:
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_role_trans_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->role_trans_diffs->diffs;
+}
+
+extern const char *poldiff_role_trans_get_source_role(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->source_role;
+}
+
+extern const char *poldiff_role_trans_get_target_type(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->target_type;
+}
+
+extern poldiff_form_e poldiff_role_trans_get_form(const void *role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_role_trans_t *)role_trans)->form;
+}
+
+extern const char *poldiff_role_trans_get_original_default(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->orig_default;
+}
+
+extern const char *poldiff_role_trans_get_modified_default(const poldiff_role_trans_t * role_trans)
+{
+ if (role_trans == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role_trans->mod_default;
+}
+
+static void role_trans_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_role_trans_t *rt = elem;
+ free(rt->target_type);
+ free(rt);
+ }
+}
+
+poldiff_role_trans_summary_t *role_trans_create(void)
+{
+ poldiff_role_trans_summary_t *rts = calloc(1, sizeof(*rts));
+ if (rts == NULL) {
+ return NULL;
+ }
+ if ((rts->diffs = apol_vector_create(role_trans_free)) == NULL) {
+ role_trans_destroy(&rts);
+ return NULL;
+ }
+ return rts;
+}
+
+void role_trans_destroy(poldiff_role_trans_summary_t ** rts)
+{
+ if (rts != NULL && *rts != NULL) {
+ apol_vector_destroy(&(*rts)->diffs);
+ free(*rts);
+ *rts = NULL;
+ }
+}
+
+int role_trans_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ role_trans_destroy(&diff->role_trans_diffs);
+ diff->role_trans_diffs = role_trans_create();
+ if (diff->role_trans_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+typedef struct pseudo_role_trans
+{
+ const char *source_role;
+ uint32_t pseudo_target;
+ const char *default_role;
+} pseudo_role_trans_t;
+
+/**
+ * Compare two pseudo role_transition rules from the same policy.
+ * Compares the source role name and then pseudo type value of the target.
+ *
+ * @param x A pseudo_role_trans_t entry.
+ * @param y A pseudo_role_trans_t entry.
+ * @param arg The policy difference structure.
+ *
+ * @return < 0, 0, or > 0 if the first rule is respectively less than,
+ * equal to, or greater than the second. If the return value would be 0
+ * but the default role is different a warning is issued.
+ */
+static int pseudo_role_trans_comp(const void *x, const void *y, void *arg)
+{
+ int retv = 0;
+ const pseudo_role_trans_t *a = x;
+ const pseudo_role_trans_t *b = y;
+ poldiff_t *diff = arg;
+
+ retv = strcmp(a->source_role, b->source_role);
+ if (!retv)
+ retv = a->pseudo_target - b->pseudo_target;
+ else
+ return retv;
+ if (!retv && strcmp(a->default_role, b->default_role))
+ WARN(diff, "Multiple role_transition rules for %s %s with different default roles.", a->source_role,
+ type_map_get_name(diff, a->pseudo_target, POLDIFF_POLICY_ORIG));
+ return retv;
+}
+
+static void role_trans_free_item(void *item)
+{
+ /* no need to free members of a pseudo role_transition */
+ free(item);
+}
+
+apol_vector_t *role_trans_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL, *attr_types = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_role_trans_t *qrt = NULL;
+ pseudo_role_trans_t *tmp_prt = NULL;
+ const char *tmp_name = NULL;
+ const qpol_role_t *tmp_role = NULL;
+ const qpol_type_t *tmp_type = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0, which_pol;
+ unsigned char isattr = 0;
+
+ which_pol = (policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ if (qpol_policy_get_role_trans_iter(q, &iter)) {
+ error = errno;
+ goto err;
+ }
+ v = apol_vector_create(role_trans_free_item);
+ if (!v) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ isattr = 0;
+ if (qpol_iterator_get_item(iter, (void **)&qrt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_role_trans_get_target_type(q, qrt, &tmp_type) < 0) {
+ error = errno;
+ goto err;
+ }
+ qpol_type_get_isattr(q, tmp_type, &isattr);
+ if (isattr) {
+ qpol_type_get_type_iter(q, tmp_type, &attr_types);
+ for (; !qpol_iterator_end(attr_types); qpol_iterator_next(attr_types)) {
+ qpol_iterator_get_item(attr_types, (void **)&tmp_type);
+ if (!(tmp_prt = calloc(1, sizeof(*tmp_prt)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt->pseudo_target = type_map_lookup(diff, tmp_type, which_pol);
+ qpol_role_trans_get_source_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->source_role = tmp_name;
+ qpol_role_trans_get_default_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->default_role = tmp_name;
+ if (apol_vector_append(v, tmp_prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt = NULL;
+ }
+ qpol_iterator_destroy(&attr_types);
+ } else {
+ if (!(tmp_prt = calloc(1, sizeof(*tmp_prt)))) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt->pseudo_target = type_map_lookup(diff, tmp_type, which_pol);
+ qpol_role_trans_get_source_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->source_role = tmp_name;
+ qpol_role_trans_get_default_role(q, qrt, &tmp_role);
+ qpol_role_get_name(q, tmp_role, &tmp_name);
+ tmp_prt->default_role = tmp_name;
+ if (apol_vector_append(v, tmp_prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ tmp_prt = NULL;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort_uniquify(v, pseudo_role_trans_comp, diff);
+
+ return v;
+
+ err:
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&attr_types);
+ apol_vector_destroy(&v);
+ free(tmp_prt);
+ errno = error;
+ return NULL;
+}
+
+int role_trans_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ int retv = 0;
+ const pseudo_role_trans_t *a = x;
+ const pseudo_role_trans_t *b = y;
+
+ retv = strcmp(a->source_role, b->source_role);
+ if (!retv)
+ return a->pseudo_target - b->pseudo_target;
+ else
+ return retv;
+}
+
+/**
+ * Allocate and return a new role_transition rule difference object.
+ *
+ * @param diff Policy difference error handler.
+ * @param form Form of the difference.
+ * @param src Name of the source role.
+ * @param tgt Name of the target type.
+ *
+ * @return A newly allocated and initialised diff or NULL upon error.
+ * The caller is responsible for calling free() upon the returned
+ * value.
+ */
+static poldiff_role_trans_t *make_rt_diff(const poldiff_t * diff, poldiff_form_e form, const char *src, const char *tgt)
+{
+ poldiff_role_trans_t *rt = NULL;
+ int error = 0;
+ if ((rt = calloc(1, sizeof(*rt))) == NULL || (rt->source_role = src) == NULL || (rt->target_type = strdup(tgt)) == NULL) {
+ error = errno;
+ role_trans_free(rt);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ rt->form = form;
+ return rt;
+}
+
+int role_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const pseudo_role_trans_t *rt = item;
+ poldiff_role_trans_t *prt = NULL;
+ const char *tgt_name = NULL;
+ int error = 0;
+
+ /* get tgt_name from type_map */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ tgt_name = type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_MOD);
+ if (type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_ORIG) == NULL) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ tgt_name = type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_ORIG);
+ if (type_map_get_name(diff, rt->pseudo_target, POLDIFF_POLICY_MOD) == NULL) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED: /* not supported here */
+ case POLDIFF_FORM_NONE:
+ default:
+ {
+ assert(0);
+ return -1;
+ }
+ }
+ assert(tgt_name != NULL);
+
+ /* create a new diff */
+ prt = make_rt_diff(diff, form, rt->source_role, tgt_name);
+ if (!prt)
+ return -1;
+
+ /* set the appropriate default */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ prt->mod_default = rt->default_role;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ prt->orig_default = rt->default_role;
+ break;
+ }
+ default:
+ {
+ /* not reachable */
+ assert(0);
+ }
+ }
+ if (apol_vector_append(diff->role_trans_diffs->diffs, prt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_trans_free(prt);
+ errno = error;
+ return -1;
+ };
+
+ /* increment appropriate counter */
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ diff->role_trans_diffs->num_added++;
+ break;
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff->role_trans_diffs->num_added_type++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ diff->role_trans_diffs->num_removed++;
+ break;
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff->role_trans_diffs->num_removed_type++;
+ break;
+ }
+ default:
+ {
+ /* not reachable */
+ assert(0);
+ }
+ }
+
+ return 0;
+}
+
+int role_trans_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const pseudo_role_trans_t *prt1 = x;
+ const pseudo_role_trans_t *prt2 = y;
+ const char *default1 = NULL, *default2 = NULL;
+ poldiff_role_trans_t *rt = NULL;
+ const char *tgt = NULL;
+ int error = 0;
+
+ default1 = prt1->default_role;
+ default2 = prt2->default_role;
+
+ if (!strcmp(default1, default2))
+ return 0; /* no difference */
+
+ tgt = type_map_get_name(diff, prt1->pseudo_target, POLDIFF_POLICY_ORIG);
+ assert(tgt != NULL);
+ rt = make_rt_diff(diff, POLDIFF_FORM_MODIFIED, prt1->source_role, tgt);
+ if (!rt)
+ return -1; /* errors already reported */
+ rt->orig_default = default1;
+ rt->mod_default = default2;
+ if (apol_vector_append(diff->role_trans_diffs->diffs, rt)) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_trans_free(rt);
+ errno = error;
+ return -1;
+ };
+ diff->role_trans_diffs->num_modified++;
+
+ return 0;
+}
diff --git a/libpoldiff/src/rbac_internal.h b/libpoldiff/src/rbac_internal.h
new file mode 100644
index 0000000..6c89234
--- /dev/null
+++ b/libpoldiff/src/rbac_internal.h
@@ -0,0 +1,209 @@
+/**
+ * @file
+ * Protected interface for role allow rule and role_transition rule
+ * differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_RBAC_INTERNAL_H
+#define POLDIFF_RBAC_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_role_allow_summary poldiff_role_allow_summary_t;
+ typedef struct poldiff_role_trans_summary poldiff_role_trans_summary_t;
+
+/**
+ * Allocate and return a new poldiff_role_allow_summary_t object.
+ *
+ * @return A new role allow summary. The caller must call role_allow_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_role_allow_summary_t *role_allow_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_role_allow_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param ras Reference to a role allow summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void role_allow_destroy(poldiff_role_allow_summary_t ** ras);
+
+/**
+ * Reset the state of all role allow rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int role_allow_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all role allow rules from the given policy,
+ * sorted by source name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all role allow rules (of type
+ * pseudo_role_allow_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *role_allow_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_role_allow_t objects, determining if they have the same
+ * source name or not.
+ *
+ * @param x The role allow from the original policy.
+ * @param y The role allow from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if source role of x is respectively less than, equal
+ * to, or greater than source role of y.
+ */
+ int role_allow_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a role allow rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_allow_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two role allow rules for which the
+ * compare callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that role allow
+ * rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * rules and to which to add an entry if needed.
+ * @param x The role allow rule from the original policy.
+ * @param y The role allow rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_allow_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Allocate and return a new poldiff_role_trans_summary_t object.
+ *
+ * @return A new role transition summary. The caller must call
+ * role_trans_destroy() afterwards. On error, return NULL and set errno.
+ */
+ poldiff_role_trans_summary_t *role_trans_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_role_trans_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param rts Reference to a role transition summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void role_trans_destroy(poldiff_role_trans_summary_t ** rts);
+
+/**
+ * Reset the state of all role_transition rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int role_trans_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all role_transition rules from the given policy,
+ * sorted by source name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of all role_transition rules (of
+ * type pseudo_role_trans_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *role_trans_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_role_trans_t objects, determining if they have the same
+ * source name and target or not.
+ *
+ * @param x The role_transition from the original policy.
+ * @param y The role_transition from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if source role of x is respectively less than, equal
+ * to, or greater than source role of y.
+ */
+ int role_trans_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a role_transition rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_trans_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two role_transition rules for which the
+ * compare callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * role_transition rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * rules and to which to add an entry if needed.
+ * @param x The role_transition rule from the original policy.
+ * @param y The role_transition rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_trans_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_RBAC_INTERNAL_H */
diff --git a/libpoldiff/src/role_diff.c b/libpoldiff/src/role_diff.c
new file mode 100644
index 0000000..bac2062
--- /dev/null
+++ b/libpoldiff/src/role_diff.c
@@ -0,0 +1,543 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in roles.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_role_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_role
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_types;
+ apol_vector_t *removed_types;
+};
+
+void poldiff_role_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->role_diffs->num_added;
+ stats[1] = diff->role_diffs->num_removed;
+ stats[2] = diff->role_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_role_to_string(const poldiff_t * diff, const void *role)
+{
+ const poldiff_role_t *r = role;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *type;
+ if (diff == NULL || role == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(r->added_types);
+ num_removed = apol_vector_get_size(r->removed_types);
+ switch (r->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", r->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", r->name) < 0) {
+ s = NULL;
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", r->name) < 0) {
+ s = NULL;
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Type%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Type%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(r->added_types); i++) {
+ type = (char *)apol_vector_get_element(r->added_types, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(r->removed_types); i++) {
+ type = (char *)apol_vector_get_element(r->removed_types, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", type) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_role_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->role_diffs->diffs;
+}
+
+const char *poldiff_role_get_name(const poldiff_role_t * role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role->name;
+}
+
+poldiff_form_e poldiff_role_get_form(const void *role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_role_t *)role)->form;
+}
+
+const apol_vector_t *poldiff_role_get_added_types(const poldiff_role_t * role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role->added_types;
+}
+
+const apol_vector_t *poldiff_role_get_removed_types(const poldiff_role_t * role)
+{
+ if (role == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return role->removed_types;
+}
+
+/*************** protected functions for roles ***************/
+
+static void role_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_role_t *r = (poldiff_role_t *) elem;
+ free(r->name);
+ apol_vector_destroy(&r->added_types);
+ apol_vector_destroy(&r->removed_types);
+ free(r);
+ }
+}
+
+poldiff_role_summary_t *role_create(void)
+{
+ poldiff_role_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(role_free)) == NULL) {
+ role_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void role_destroy(poldiff_role_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+int role_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ role_destroy(&diff->role_diffs);
+ diff->role_diffs = role_create();
+ if (diff->role_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two roles from the same policy.
+ */
+static int role_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_role_t *r1 = x;
+ const qpol_role_t *r2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_role_get_name(q, r1, &name1) < 0 || qpol_role_get_name(q, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *role_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_role_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, role_name_comp, (void *)policy);
+ return v;
+}
+
+int role_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_role_t *r1 = x;
+ const qpol_role_t *r2 = y;
+ const char *name1, *name2;
+ if (qpol_role_get_name(diff->orig_qpol, r1, &name1) < 0 || qpol_role_get_name(diff->mod_qpol, r2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new role difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the role that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling role_free() upon the returned
+ * value.
+ */
+static poldiff_role_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_role_t *pr;
+ int error;
+ if ((pr = calloc(1, sizeof(*pr))) == NULL ||
+ (pr->name = strdup(name)) == NULL ||
+ (pr->added_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pr->removed_types = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ role_free(pr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pr->form = form;
+ return pr;
+}
+
+int role_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_role_t *r = item;
+ const char *name = NULL;
+ poldiff_role_t *pr;
+ int error;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_role_get_name(diff->mod_qpol, r, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_role_get_name(diff->orig_qpol, r, &name) < 0))
+ {
+ return -1;
+ }
+ pr = make_diff(diff, form, name);
+ if (pr == NULL) {
+ return -1;
+ }
+ if (apol_vector_append(diff->role_diffs->diffs, pr) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ role_free(pr);
+ errno = error;
+ return -1;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->role_diffs->num_added++;
+ } else {
+ diff->role_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given a role, return an unsorted vector of its allowed types (in
+ * the form of uint32_t corresponding to pseudo-type values).
+ *
+ * @param diff Policy diff error handler.
+ * @param role Role whose roles to get.
+ * @param which Which policy, one of POLDIFF_POLICY_ORIG or
+ * POLDIFF_POLICY_MOD.
+ *
+ * @return Vector of pseudo-type values. The caller is responsible
+ * for calling apol_vector_destroy(). On error, return NULL.
+ */
+static apol_vector_t *role_get_types(const poldiff_t * diff, const qpol_role_t * role, int which)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_type_t *type;
+ uint32_t new_val;
+ apol_vector_t *v = NULL;
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (which == POLDIFF_POLICY_ORIG) {
+ if (qpol_role_get_type_iter(diff->orig_qpol, role, &iter) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (qpol_role_get_type_iter(diff->mod_qpol, role, &iter) < 0) {
+ goto cleanup;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || (new_val = type_map_lookup(diff, type, which)) == 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(v, (void *)((size_t) new_val)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+int role_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_role_t *r1 = x;
+ const qpol_role_t *r2 = y;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ apol_vector_t *added_types = NULL, *removed_types = NULL;
+ const apol_vector_t *reverse_v;
+ const char *name;
+ char *new_name;
+ uint32_t t1, t2;
+ poldiff_role_t *r = NULL;
+ qpol_type_t *t;
+ size_t i, j;
+ int retval = -1, error = 0;
+
+ if (qpol_role_get_name(diff->orig_qpol, r1, &name) < 0 ||
+ (v1 = role_get_types(diff, r1, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = role_get_types(diff, r2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort_uniquify(v1, NULL, NULL);
+ apol_vector_sort_uniquify(v2, NULL, NULL);
+ if ((added_types = apol_vector_create(NULL)) == NULL || (removed_types = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (t2 > t1) {
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (t1 > t2) {
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(v1, i));
+ if (apol_vector_append(removed_types, (void *)((size_t) t1)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(v2, j));
+ if (apol_vector_append(added_types, (void *)((size_t) t2)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(added_types) > 0 || apol_vector_get_size(removed_types) > 0) {
+ if ((r = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(removed_types); i++) {
+ t1 = (uint32_t) ((size_t) apol_vector_get_element(removed_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t1, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->removed_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(added_types); i++) {
+ t2 = (uint32_t) ((size_t) apol_vector_get_element(added_types, i));
+ if ((reverse_v = type_map_lookup_reverse(diff, t2, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(reverse_v); j++) {
+ t = (qpol_type_t *) apol_vector_get_element(reverse_v, j);
+ if (qpol_type_get_name(diff->mod_qpol, t, &name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((new_name = strdup(name)) == NULL || apol_vector_append(r->added_types, new_name) < 0) {
+ error = errno;
+ free(new_name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort(r->removed_types, apol_str_strcmp, NULL);
+ apol_vector_sort(r->added_types, apol_str_strcmp, NULL);
+ if (apol_vector_append(diff->role_diffs->diffs, r) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->role_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ apol_vector_destroy(&added_types);
+ apol_vector_destroy(&removed_types);
+ if (retval != 0) {
+ role_free(r);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/role_internal.h b/libpoldiff/src/role_internal.h
new file mode 100644
index 0000000..7c2aa90
--- /dev/null
+++ b/libpoldiff/src/role_internal.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Protected interface for role differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_ROLE_INTERNAL_H
+#define POLDIFF_ROLE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_role_summary poldiff_role_summary_t;
+
+/**
+ * Allocate and return a new poldiff_role_summary_t object.
+ *
+ * @return A new role summary. The caller must call role_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_role_summary_t *role_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_role_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param us Reference to a role summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void role_destroy(poldiff_role_summary_t ** us);
+
+/**
+ * Reset the state of all role differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int role_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all roles from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all roles. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *role_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_role_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The role from the original policy.
+ * @param y The role from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if role x is respectively less than, equal
+ * to, or greater than role y.
+ */
+ int role_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a role.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two roles for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * role.
+ *
+ * @param diff The policy difference structure associated with both
+ * roles and to which to add an entry if needed.
+ * @param x The role from the original policy.
+ * @param y The role from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int role_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_ROLE_INTERNAL_H */
diff --git a/libpoldiff/src/terule_diff.c b/libpoldiff/src/terule_diff.c
new file mode 100644
index 0000000..2857403
--- /dev/null
+++ b/libpoldiff/src/terule_diff.c
@@ -0,0 +1,1329 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in AV and Type
+ * rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <qpol/policy_extend.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_terule_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ size_t num_added_type;
+ size_t num_removed_type;
+ int diffs_sorted;
+ /** vector of poldiff_terule_t */
+ apol_vector_t *diffs;
+};
+
+struct poldiff_terule
+{
+ uint32_t spec;
+ /* pointer into policy's symbol table */
+ const char *source, *target;
+ /** the class string is pointer into the class_bst BST */
+ const char *cls;
+ poldiff_form_e form;
+ /* pointer into policy's symbol table */
+ const char *orig_default, *mod_default;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ uint32_t branch;
+ /** vector of unsigned longs of line numbers from original policy */
+ apol_vector_t *orig_linenos;
+ /** vector of unsigned longs of line numbers from modified policy */
+ apol_vector_t *mod_linenos;
+ /** array of pointers for original rules */
+ qpol_terule_t **orig_rules;
+ size_t num_orig_rules;
+ /** array of pointers for modified rules */
+ qpol_terule_t **mod_rules;
+ size_t num_mod_rules;
+};
+
+typedef struct pseudo_terule
+{
+ uint32_t spec;
+ /** pseudo-type values */
+ uint32_t source, target, default_type;
+ /** pointer into the class_bst BST */
+ const char *cls;
+ /** array of pointers into the bool_bst BST */
+ const char *bools[5];
+ uint32_t bool_val;
+ uint32_t branch;
+ /** pointer into policy's conditional list, needed to render
+ * conditional expressions */
+ const qpol_cond_t *cond;
+ /** array of qpol_terule_t pointers, for showing line numbers */
+ const qpol_terule_t **rules;
+ size_t num_rules;
+} pseudo_terule_t;
+
+/******************** public terule functions ********************/
+
+/**
+ * Get an array of statistics for the number of differences of each
+ * form for te rules.
+ *
+ * @param diff The policy difference structure from which to get the
+ * stats.
+ * @param stats Array into which to write the numbers (array must be
+ * pre-allocated). The order of the values written to the array is
+ * as follows: number of items of form POLDIFF_FORM_ADDED, number of
+ * POLDIFF_FORM_REMOVED, number of POLDIFF_FORM_MODIFIED, number of
+ * POLDIFF_FORM_ADD_TYPE, and number of POLDIFF_FORM_REMOVE_TYPE.
+ * @param idx Index into the terule diffs array indicating which rule
+ * type to reset, one of TERULE_OFFSET_MEMBER, etc.
+ */
+static void poldiff_terule_get_stats(const poldiff_t * diff, size_t stats[5], unsigned int idx)
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->terule_diffs[idx]->num_added;
+ stats[1] = diff->terule_diffs[idx]->num_removed;
+ stats[2] = diff->terule_diffs[idx]->num_modified;
+ stats[3] = diff->terule_diffs[idx]->num_added_type;
+ stats[4] = diff->terule_diffs[idx]->num_removed_type;
+}
+
+void poldiff_terule_get_stats_change(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_CHANGE);
+}
+
+void poldiff_terule_get_stats_member(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_MEMBER);
+}
+
+void poldiff_terule_get_stats_trans(const poldiff_t * diff, size_t stats[5])
+{
+ poldiff_terule_get_stats(diff, stats, TERULE_OFFSET_TRANS);
+}
+
+char *poldiff_terule_to_string(const poldiff_t * diff, const void *terule)
+{
+ const poldiff_terule_t *pt = (const poldiff_terule_t *)terule;
+ apol_policy_t *p;
+ const char *rule_type;
+ char *diff_char = "", *s = NULL, *cond_expr = NULL;
+ size_t len = 0;
+ int error;
+ if (diff == NULL || terule == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (pt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ diff_char = "+";
+ p = diff->mod_pol;
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ diff_char = "-";
+ p = diff->orig_pol;
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ diff_char = "*";
+ p = diff->orig_pol;
+ break;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ rule_type = apol_rule_type_to_str(pt->spec);
+ if (apol_str_appendf(&s, &len, "%s %s %s %s : %s ", diff_char, rule_type, pt->source, pt->target, pt->cls) < 0) {
+ error = errno;
+ s = NULL;
+ goto err;
+ }
+ switch (pt->form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ if (apol_str_append(&s, &len, pt->mod_default) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ if (apol_str_append(&s, &len, pt->orig_default) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "{ -%s +%s }", pt->orig_default, pt->mod_default) < 0) {
+ error = errno;
+ goto err;
+ }
+ break;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ if (apol_str_append(&s, &len, ";") < 0) {
+ error = errno;
+ goto err;
+ }
+ if (pt->cond != NULL) {
+ if ((cond_expr = apol_cond_expr_render(p, pt->cond)) == NULL) {
+ error = errno;
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " [%s]:%s", cond_expr, (pt->branch ? "TRUE" : "FALSE")) < 0) {
+ error = errno;
+ goto err;
+ }
+ free(cond_expr);
+ }
+ return s;
+ err:
+ free(s);
+ free(cond_expr);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+}
+
+/**
+ * Sort poldiff_terule diff results in a mostly alphabetical order.
+ */
+static int poldiff_terule_cmp(const void *x, const void *y, void *data __attribute__ ((unused)))
+{
+ const poldiff_terule_t *a = (const poldiff_terule_t *)x;
+ const poldiff_terule_t *b = (const poldiff_terule_t *)y;
+ int compval;
+ if (a->spec != b->spec) {
+ const char *rule_type1 = apol_rule_type_to_str(a->spec);
+ const char *rule_type2 = apol_rule_type_to_str(b->spec);
+ compval = strcmp(rule_type1, rule_type2);
+ if (compval != 0) {
+ return compval;
+ }
+ }
+ if ((compval = strcmp(a->source, b->source)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->target, b->target)) != 0) {
+ return compval;
+ }
+ if ((compval = strcmp(a->cls, b->cls)) != 0) {
+ return compval;
+ }
+ if (a->cond != b->cond) {
+ return (int)((char *)a->cond - (char *)b->cond);
+ }
+ /* sort true branch before false branch */
+ return b->branch - a->branch;
+}
+
+/**
+ * Get the vector of te rule differences from the te rule difference
+ * summary.
+ *
+ * @param diff The policy difference structure associated with the te
+ * rule difference summary.
+ * @param idx Index into the terule diffs array indicating which rule
+ * type to reset, one of TERULE_OFFSET_MEMBER, etc.
+ *
+ * @return A vector of elements of type poldiff_terule_t, or NULL on
+ * error. The caller should <b>not</b> destroy the vector returned.
+ * If the call fails, errno will be set.
+ */
+static const apol_vector_t *poldiff_get_terule_vector(const poldiff_t * diff, terule_offset_e idx)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (diff->terule_diffs[idx]->diffs_sorted == 0) {
+ apol_vector_sort(diff->terule_diffs[idx]->diffs, poldiff_terule_cmp, NULL);
+ diff->terule_diffs[idx]->diffs_sorted = 1;
+ }
+ return diff->terule_diffs[idx]->diffs;
+}
+
+const apol_vector_t *poldiff_get_terule_vector_member(const poldiff_t * diff)
+{
+ return poldiff_get_terule_vector(diff, TERULE_OFFSET_MEMBER);
+}
+
+const apol_vector_t *poldiff_get_terule_vector_change(const poldiff_t * diff)
+{
+ return poldiff_get_terule_vector(diff, TERULE_OFFSET_CHANGE);
+}
+
+const apol_vector_t *poldiff_get_terule_vector_trans(const poldiff_t * diff)
+{
+ return poldiff_get_terule_vector(diff, TERULE_OFFSET_TRANS);
+}
+
+poldiff_form_e poldiff_terule_get_form(const void *terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_terule_t *)terule)->form;
+}
+
+uint32_t poldiff_terule_get_rule_type(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->spec;
+}
+
+const char *poldiff_terule_get_source_type(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->source;
+}
+
+const char *poldiff_terule_get_target_type(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->target;
+}
+
+const char *poldiff_terule_get_object_class(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->cls;
+}
+
+void poldiff_terule_get_cond(const poldiff_t * diff, const poldiff_terule_t * terule,
+ const qpol_cond_t ** cond, uint32_t * which_list, const apol_policy_t ** p)
+{
+ if (diff == NULL || terule == NULL || cond == NULL || p == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ *cond = terule->cond;
+ if (*cond == NULL) {
+ *which_list = 1;
+ *p = NULL;
+ } else if (terule->form == POLDIFF_FORM_ADDED || terule->form == POLDIFF_FORM_ADD_TYPE) {
+ *which_list = terule->branch;
+ *p = diff->mod_pol;
+ } else {
+ *which_list = terule->branch;
+ *p = diff->orig_pol;
+ }
+}
+
+const char *poldiff_terule_get_original_default(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->orig_default;
+}
+
+const char *poldiff_terule_get_modified_default(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return terule->mod_default;
+}
+
+apol_vector_t *poldiff_terule_get_orig_line_numbers(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return terule->orig_linenos;
+}
+
+apol_vector_t *poldiff_terule_get_mod_line_numbers(const poldiff_terule_t * terule)
+{
+ if (terule == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return terule->mod_linenos;
+}
+
+/*************** protected functions ***************/
+
+/**
+ * Free all space used by a poldiff_terule_t, including the pointer
+ * itself. Does nothing if the pointer is already NULL.
+ *
+ * @param elem Pointer to a poldiff_terule_t.
+ */
+static void poldiff_terule_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_terule_t *t = elem;
+ apol_vector_destroy(&t->orig_linenos);
+ apol_vector_destroy(&t->mod_linenos);
+ free(t->orig_rules);
+ free(t->mod_rules);
+ free(elem);
+ }
+}
+
+poldiff_terule_summary_t *terule_create(void)
+{
+ poldiff_terule_summary_t *rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ return NULL;
+ }
+ if ((rs->diffs = apol_vector_create(poldiff_terule_free)) == NULL) {
+ terule_destroy(&rs);
+ return NULL;
+ }
+ return rs;
+}
+
+void terule_destroy(poldiff_terule_summary_t ** rs)
+{
+ if (rs != NULL && *rs != NULL) {
+ apol_vector_destroy(&(*rs)->diffs);
+ free(*rs);
+ *rs = NULL;
+ }
+}
+
+/**
+ * Reset the state of all TE rule differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @param idx Index into the terule diffs array indicating which rule
+ * type to reset, one of TERULE_OFFSET_CHANGE, etc.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+static int terule_reset(poldiff_t * diff, terule_offset_e idx)
+{
+ int error = 0;
+ terule_destroy(&diff->terule_diffs[idx]);
+ diff->terule_diffs[idx] = terule_create();
+ if (diff->terule_diffs[idx] == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+int terule_reset_change(poldiff_t * diff)
+{
+ return terule_reset(diff, TERULE_OFFSET_CHANGE);
+}
+
+int terule_reset_member(poldiff_t * diff)
+{
+ return terule_reset(diff, TERULE_OFFSET_MEMBER);
+}
+
+int terule_reset_trans(poldiff_t * diff)
+{
+ return terule_reset(diff, TERULE_OFFSET_TRANS);
+}
+
+static void terule_free_item(void *item)
+{
+ pseudo_terule_t *t = (pseudo_terule_t *) item;
+ if (item != NULL) {
+ free(t->rules);
+ free(t);
+ }
+}
+
+/**
+ * Apply an ordering scheme to two pseudo-te rules.
+ *
+ * <ul>
+ * <li>Sort by target pseudo-type value,
+ * <li>Then by source pseudo-type value,
+ * <li>Then by object class's BST's pointer value,
+ * <li>Then by rule specified (allow, neverallow, etc.),
+ * <li>Then choose unconditional rules over conditional rules,
+ * <li>Then by conditional expression's BST's boolean pointer value.
+ * </ul>
+ *
+ * If this function is being used for sorting (via terule_get_items())
+ * then sort by truth value, and then by branch (true branch, then
+ * false branch). Otherwise, when comparing rules (via terule_comp())
+ * then by truth value, inverting rule2's value if in the other
+ * branch.
+ */
+static int pseudo_terule_comp(const pseudo_terule_t * rule1, const pseudo_terule_t * rule2, int is_sorting)
+{
+ size_t i;
+ uint32_t bool_val;
+ if (rule1->target != rule2->target) {
+ return rule1->target - rule2->target;
+ }
+ if (rule1->source != rule2->source) {
+ return rule1->source - rule2->source;
+ }
+ if (rule1->cls != rule2->cls) {
+ return (int)(rule1->cls - rule2->cls);
+ }
+ if (rule1->spec != rule2->spec) {
+ return rule1->spec - rule2->spec;
+ }
+ if (rule1->bools[0] == NULL && rule2->bools[0] == NULL) {
+ /* both rules are unconditional */
+ return 0;
+ } else if (rule1->bools[0] == NULL && rule2->bools[0] != NULL) {
+ /* unconditional rules come before conditional */
+ return -1;
+ } else if (rule1->bools[0] != NULL && rule2->bools[0] == NULL) {
+ /* unconditional rules come before conditional */
+ return 1;
+ }
+ for (i = 0; i < (sizeof(rule1->bools) / sizeof(rule1->bools[0])); i++) {
+ if (rule1->bools[i] != rule2->bools[i]) {
+ return (int)(rule1->bools[i] - rule2->bools[i]);
+ }
+ }
+ if (is_sorting) {
+ if (rule1->branch != rule2->branch) {
+ return rule1->branch - rule2->branch;
+ }
+ return (int)rule1->bool_val - (int)rule2->bool_val;
+ } else {
+ if (rule1->branch == rule2->branch) {
+ bool_val = rule2->bool_val;
+ } else {
+ bool_val = ~rule2->bool_val;
+ }
+ if (rule1->bool_val < bool_val) {
+ return -1;
+ } else if (rule1->bool_val > bool_val) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+static int terule_bst_comp(const void *x, const void *y, void *data)
+{
+ const pseudo_terule_t *r1 = (const pseudo_terule_t *)x;
+ const pseudo_terule_t *r2 = (const pseudo_terule_t *)y;
+ poldiff_t *diff = data;
+ int retv;
+ retv = pseudo_terule_comp(r1, r2, 1);
+ if (!retv && r1->default_type != r2->default_type)
+ WARN(diff, "Multiple %s rules for %s %s %s with different default types", apol_rule_type_to_str(r1->spec),
+ type_map_get_name(diff, r1->source, POLDIFF_POLICY_ORIG), type_map_get_name(diff, r1->target,
+ POLDIFF_POLICY_ORIG), r1->cls);
+ return retv;
+}
+
+/**
+ * Given a conditional expression, convert its booleans to a sorted
+ * array of pseudo-boolean values, assign that array to the
+ * pseudo-terule key, and then derive the truth table.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy containing conditional.
+ * @param cond Conditional expression to convert.
+ * @param key Location to write converted expression.
+ */
+static int terule_build_cond(const poldiff_t * diff, const apol_policy_t * p, const qpol_cond_t * cond, pseudo_terule_t * key)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_cond_expr_node_t *node;
+ uint32_t expr_type, truthiness;
+ qpol_bool_t *bools[5] = { NULL, NULL, NULL, NULL, NULL }, *qbool;
+ size_t i, j;
+ size_t num_bools = 0;
+ const char *bool_name, *pseudo_bool, *t;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ if (qpol_cond_get_expr_node_iter(q, cond, &iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&node) < 0 || qpol_cond_expr_node_get_expr_type(q, node, &expr_type) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (expr_type != QPOL_COND_EXPR_BOOL) {
+ continue;
+ }
+ if (qpol_cond_expr_node_get_bool(q, node, &qbool) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (bools[i] == qbool) {
+ break;
+ }
+ }
+ if (i >= num_bools) {
+ assert(i < 5);
+ bools[i] = qbool;
+ num_bools++;
+ }
+ }
+ for (i = 0; i < num_bools; i++) {
+ if (qpol_bool_get_name(q, bools[i], &bool_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->bool_bst, (void *)bool_name, NULL, (void **)&pseudo_bool) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ key->bools[i] = pseudo_bool;
+ }
+
+ /* bubble sorth the pseudo bools (not bad because there are at
+ * most five elements */
+ for (i = num_bools; i > 1; i--) {
+ for (j = 1; j < i; j++) {
+ compval = strcmp(key->bools[j - 1], key->bools[j]);
+ if (compval > 0) {
+ t = key->bools[j];
+ key->bools[j] = key->bools[j - 1];
+ key->bools[j - 1] = t;
+ qbool = bools[j];
+ bools[j] = bools[j - 1];
+ bools[j - 1] = qbool;
+ }
+ }
+ }
+
+ /* now compute the truth table for the booleans */
+ key->bool_val = 0;
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < num_bools; j++) {
+ int state = ((i & (1 << j)) ? 1 : 0);
+ if (qpol_bool_set_state_no_eval(q, bools[j], state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if (qpol_cond_eval(q, cond, &truthiness) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ key->bool_val = (key->bool_val << 1) | truthiness;
+ }
+
+ key->cond = cond;
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Given a rule, construct a new pseudo-terule and insert it into the
+ * BST if not already there.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule TE rule to insert.
+ * @param source Source pseudo-type value.
+ * @param target Target pseudo-type value.
+ * @param b BST containing pseudo-terules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int terule_add_to_bst(poldiff_t * diff, const apol_policy_t * p,
+ const qpol_terule_t * rule, uint32_t source, uint32_t target, apol_bst_t * b)
+{
+ pseudo_terule_t *key, *inserted_key;
+ const qpol_class_t *obj_class;
+ const qpol_type_t *default_type;
+ const char *class_name;
+ const qpol_cond_t *cond;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0, compval;
+ int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ if ((key = calloc(1, sizeof(*key))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_terule_get_rule_type(q, rule, &(key->spec)) < 0 ||
+ qpol_terule_get_object_class(q, rule, &obj_class) < 0 ||
+ qpol_terule_get_default_type(q, rule, &default_type) < 0 || qpol_terule_get_cond(q, rule, &cond) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (qpol_class_get_name(q, obj_class, &class_name) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_bst_get_element(diff->class_bst, (void *)class_name, NULL, (void **)&key->cls) < 0) {
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ if ((key->default_type = type_map_lookup(diff, default_type, which)) == 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ key->source = source;
+ key->target = target;
+ if (cond != NULL && (qpol_terule_get_which_list(q, rule, &(key->branch)) < 0 || terule_build_cond(diff, p, cond, key) < 0)) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* insert this pseudo into the tree if not already there */
+ if ((compval = apol_bst_insert_and_get(b, (void **)&key, diff)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key = key;
+ key = NULL;
+
+ /* store the rule pointer, to be used for showing line numbers */
+ if (qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS)) {
+ const qpol_terule_t **t = realloc(inserted_key->rules,
+ (inserted_key->num_rules + 1) * sizeof(*t));
+ if (t == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ inserted_key->rules = t;
+ inserted_key->rules[inserted_key->num_rules++] = rule;
+ }
+
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ terule_free_item(key);
+ }
+ errno = error;
+ return retval;
+}
+
+/**
+ * Given a rule, expand its source and target types into individual
+ * pseudo-type values. Then add the expanded rule to the BST. This
+ * is needed for when the source and/or target is an attribute.
+ *
+ * @param diff Policy difference structure.
+ * @param p Policy from which the rule came.
+ * @param rule TE rule to insert.
+ * @param b BST containing pseudo-terules.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int terule_expand(poldiff_t * diff, const apol_policy_t * p, const qpol_terule_t * rule, apol_bst_t * b)
+{
+ const qpol_type_t *source, *orig_target, *target;
+ unsigned char source_attr, target_attr;
+ qpol_iterator_t *source_iter = NULL, *target_iter = NULL;
+ uint32_t source_val, target_val;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int which = (p == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ int retval = -1, error = 0;
+ if (qpol_terule_get_source_type(q, rule, &source) < 0 ||
+ qpol_terule_get_target_type(q, rule, &orig_target) < 0 ||
+ qpol_type_get_isattr(q, source, &source_attr) < 0 || qpol_type_get_isattr(q, orig_target, &target_attr)) {
+ error = errno;
+ goto cleanup;
+ }
+ if (source_attr && qpol_type_get_type_iter(q, source, &source_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ do {
+ if (source_attr) {
+ if (qpol_iterator_get_item(source_iter, (void **)&source) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(source_iter);
+ }
+ if (target_attr) {
+ if (qpol_type_get_type_iter(q, orig_target, &target_iter) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } else {
+ target = orig_target;
+ }
+ do {
+ if (target_attr) {
+ if (qpol_iterator_get_item(target_iter, (void **)&target) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ qpol_iterator_next(target_iter);
+ }
+ const char *n1, *n2;
+ qpol_type_get_name(q, source, &n1);
+ qpol_type_get_name(q, target, &n2);
+ if ((source_val = type_map_lookup(diff, source, which)) == 0 ||
+ (target_val = type_map_lookup(diff, target, which)) == 0 ||
+ terule_add_to_bst(diff, p, rule, source_val, target_val, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } while (target_attr && !qpol_iterator_end(target_iter));
+ qpol_iterator_destroy(&target_iter);
+ } while (source_attr && !qpol_iterator_end(source_iter));
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&source_iter);
+ qpol_iterator_destroy(&target_iter);
+ errno = error;
+ return retval;
+}
+
+/**
+ * Get a vector of terules from the given policy, sorted. This
+ * function will remap source and target types to their pseudo-type
+ * value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @param which Kind of rule to get, one of QPOL_RULE_TYPE_TRANS, etc.
+ *
+ * @return A newly allocated vector of all te rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+static apol_vector_t *terule_get_items(poldiff_t * diff, const apol_policy_t * policy, unsigned int which)
+{
+ apol_vector_t *bools = NULL, *bool_states = NULL;
+ size_t i, num_rules, j;
+ apol_bst_t *b = NULL;
+ apol_vector_t *v = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_terule_t *rule;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int retval = -1, error = 0;
+ if (poldiff_build_bsts(diff) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* store original boolean values */
+ if (apol_bool_get_by_query(policy, NULL, &bools) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((bool_states = apol_vector_create_with_capacity(apol_vector_get_size(bools), NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state;
+ if (qpol_bool_get_state(q, qbool, &state) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_append(bool_states, (void *)((size_t) state)) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if ((b = apol_bst_create(terule_bst_comp, terule_free_item)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_policy_get_terule_iter(q, which, &iter) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ qpol_iterator_get_size(iter, &num_rules);
+ for (j = 0; !qpol_iterator_end(iter); qpol_iterator_next(iter), j++) {
+ if (qpol_iterator_get_item(iter, (void **)&rule) < 0 || terule_expand(diff, policy, rule, b) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (!(j % 1024)) {
+ int percent = 50 * j / num_rules + (policy == diff->mod_pol ? 50 : 0);
+ INFO(diff, "Computing TE rule difference: %02d%% complete", percent);
+ }
+ }
+ if ((v = apol_bst_get_vector(b, 1)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ /* restore boolean states */
+ for (i = 0; bools != NULL && i < apol_vector_get_size(bools); i++) {
+ qpol_bool_t *qbool = apol_vector_get_element(bools, i);
+ int state = (int)((size_t) apol_vector_get_element(bool_states, i));
+ qpol_bool_set_state_no_eval(q, qbool, state);
+ }
+ apol_vector_destroy(&bools);
+ apol_vector_destroy(&bool_states);
+ qpol_policy_reevaluate_conds(q);
+ apol_bst_destroy(&b);
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+apol_vector_t *terule_get_items_change(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return terule_get_items(diff, policy, QPOL_RULE_TYPE_CHANGE);
+}
+
+apol_vector_t *terule_get_items_member(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return terule_get_items(diff, policy, QPOL_RULE_TYPE_MEMBER);
+}
+
+apol_vector_t *terule_get_items_trans(poldiff_t * diff, const apol_policy_t * policy)
+{
+ return terule_get_items(diff, policy, QPOL_RULE_TYPE_TRANS);
+}
+
+int terule_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ const pseudo_terule_t *r1 = (const pseudo_terule_t *)x;
+ const pseudo_terule_t *r2 = (const pseudo_terule_t *)y;
+ return pseudo_terule_comp(r1, r2, 0);
+}
+
+/**
+ * Allocate and return a new terule difference object. If the
+ * pseudo-terule's source and/or target expands to multiple read
+ * types, then just choose the first one for display.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param rule Pseudo terule that changed.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling poldiff_terule_free() upon
+ * the returned value.
+ */
+static poldiff_terule_t *make_tediff(const poldiff_t * diff, poldiff_form_e form, const pseudo_terule_t * rule)
+{
+ poldiff_terule_t *pt;
+ const char *n1, *n2;
+ int error;
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_MOD);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_MOD);
+ } else {
+ n1 = type_map_get_name(diff, rule->source, POLDIFF_POLICY_ORIG);
+ n2 = type_map_get_name(diff, rule->target, POLDIFF_POLICY_ORIG);
+ }
+ assert(n1 != NULL && n2 != NULL);
+ if ((pt = calloc(1, sizeof(*pt))) == NULL) {
+ error = errno;
+ poldiff_terule_free(pt);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pt->spec = rule->spec;
+ pt->source = n1;
+ pt->target = n2;
+ pt->cls = rule->cls;
+ pt->form = form;
+ pt->cond = rule->cond;
+ pt->branch = rule->branch;
+ return pt;
+}
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ * @param idx Index into the terule differences specifying int which
+ * to place the constructed pseudo-te rule.
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int terule_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item, terule_offset_e idx)
+{
+ pseudo_terule_t *rule = (pseudo_terule_t *) item;
+ poldiff_terule_t *pt = NULL;
+ const apol_vector_t *v1, *v2;
+ apol_policy_t *p;
+ const char *orig_default = NULL, *mod_default = NULL;
+ int retval = -1, error = errno;
+
+ /* check if form should really become ADD_TYPE / REMOVE_TYPE,
+ * by seeing if the /other/ policy's reverse lookup is
+ * empty */
+ if (form == POLDIFF_FORM_ADDED) {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_ORIG)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_ORIG)) == NULL ||
+ (mod_default = type_map_get_name(diff, rule->default_type, POLDIFF_POLICY_MOD)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_ADD_TYPE;
+ }
+ p = diff->mod_pol;
+ } else {
+ if ((v1 = type_map_lookup_reverse(diff, rule->source, POLDIFF_POLICY_MOD)) == NULL ||
+ (v2 = type_map_lookup_reverse(diff, rule->target, POLDIFF_POLICY_MOD)) == NULL ||
+ (orig_default = type_map_get_name(diff, rule->default_type, POLDIFF_POLICY_ORIG)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ form = POLDIFF_FORM_REMOVE_TYPE;
+ }
+ p = diff->orig_pol;
+ }
+
+ pt = make_tediff(diff, form, rule);
+ if (pt == NULL) {
+ return -1;
+ }
+ pt->orig_default = orig_default;
+ pt->mod_default = mod_default;
+
+ /* calculate line numbers */
+ if (qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_LINE_NUMBERS)) {
+ apol_vector_t *vl = NULL;
+ if ((vl = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pt->mod_linenos = vl;
+ } else {
+ pt->orig_linenos = vl;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ if (form == POLDIFF_FORM_ADDED || form == POLDIFF_FORM_ADD_TYPE) {
+ pt->num_mod_rules = rule->num_rules;
+ pt->mod_rules = calloc(rule->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->mod_rules, rule->rules, rule->num_rules * sizeof(qpol_terule_t *));
+ } else {
+ pt->num_orig_rules = rule->num_rules;
+ pt->orig_rules = calloc(rule->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->orig_rules, rule->rules, rule->num_rules * sizeof(qpol_terule_t *));
+ }
+ }
+
+ if (apol_vector_append(diff->terule_diffs[idx]->diffs, pt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ diff->terule_diffs[idx]->num_added++;
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ diff->terule_diffs[idx]->num_added_type++;
+ break;
+ case POLDIFF_FORM_REMOVED:
+ diff->terule_diffs[idx]->num_removed++;
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ diff->terule_diffs[idx]->num_removed_type++;
+ break;
+ default:
+ error = EBADRQC; /* should never get here */
+ ERR(diff, "%s", strerror(error));
+ assert(0);
+ goto cleanup;
+ }
+ diff->terule_diffs[idx]->diffs_sorted = 0;
+ retval = 0;
+ cleanup:
+ if (retval < 0) {
+ poldiff_terule_free(pt);
+ }
+ errno = error;
+ return retval;
+}
+
+int terule_new_diff_change(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return terule_new_diff(diff, form, item, TERULE_OFFSET_CHANGE);
+}
+
+int terule_new_diff_member(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return terule_new_diff(diff, form, item, TERULE_OFFSET_MEMBER);
+}
+
+int terule_new_diff_trans(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ return terule_new_diff(diff, form, item, TERULE_OFFSET_TRANS);
+}
+
+/**
+ * Compute the semantic difference of two pseudo-te rules for which
+ * the compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ * @param idx Index into the terule differences specifying into which
+ * to place the constructed pseudo-ate rule.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+static int terule_deep_diff(poldiff_t * diff, const void *x, const void *y, terule_offset_e idx)
+{
+ pseudo_terule_t *r1 = (pseudo_terule_t *) x;
+ pseudo_terule_t *r2 = (pseudo_terule_t *) y;
+ poldiff_terule_t *pt = NULL;
+ int retval = -1, error = 0;
+
+ if (r1->default_type != r2->default_type) {
+ if ((pt = make_tediff(diff, POLDIFF_FORM_MODIFIED, r1)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ pt->orig_default = type_map_get_name(diff, r1->default_type, POLDIFF_POLICY_ORIG);
+ pt->mod_default = type_map_get_name(diff, r2->default_type, POLDIFF_POLICY_MOD);
+
+ /* calculate line numbers */
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pt->orig_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pt->num_orig_rules = r1->num_rules;
+ pt->orig_rules = calloc(r1->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->orig_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->orig_rules, r1->rules, r1->num_rules * sizeof(qpol_terule_t *));
+ }
+ if (qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_LINE_NUMBERS)) {
+ if ((pt->mod_linenos = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* copy rule pointers for delayed line number claculation */
+ pt->num_mod_rules = r2->num_rules;
+ pt->mod_rules = calloc(r2->num_rules, sizeof(qpol_terule_t *));
+ if (!pt->mod_rules) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ memcpy(pt->mod_rules, r2->rules, r2->num_rules * sizeof(qpol_terule_t *));
+ }
+
+ if (apol_vector_append(diff->terule_diffs[idx]->diffs, pt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->terule_diffs[idx]->num_modified++;
+ diff->terule_diffs[idx]->diffs_sorted = 0;
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ poldiff_terule_free(pt);
+ }
+ errno = error;
+ return retval;
+}
+
+int terule_deep_diff_change(poldiff_t * diff, const void *x, const void *y)
+{
+ return terule_deep_diff(diff, x, y, TERULE_OFFSET_CHANGE);
+}
+
+int terule_deep_diff_member(poldiff_t * diff, const void *x, const void *y)
+{
+ return terule_deep_diff(diff, x, y, TERULE_OFFSET_MEMBER);
+}
+
+int terule_deep_diff_trans(poldiff_t * diff, const void *x, const void *y)
+{
+ return terule_deep_diff(diff, x, y, TERULE_OFFSET_TRANS);
+}
+
+int terule_enable_line_numbers(poldiff_t * diff, terule_offset_e idx)
+{
+ const apol_vector_t *te = NULL;
+ poldiff_terule_t *terule = NULL;
+ size_t i, j;
+ qpol_iterator_t *iter = NULL;
+ qpol_syn_terule_t *ste = NULL;
+ int error = 0;
+ unsigned long lineno = 0;
+
+ te = poldiff_get_terule_vector(diff, idx);
+
+ for (i = 0; i < apol_vector_get_size(te); i++) {
+ terule = apol_vector_get_element(te, i);
+ if (apol_vector_get_size(terule->mod_linenos) || apol_vector_get_size(terule->orig_linenos))
+ continue;
+ for (j = 0; j < terule->num_orig_rules; j++) {
+ if (qpol_terule_get_syn_terule_iter(diff->orig_qpol, terule->orig_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&ste) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_terule_get_lineno(diff->orig_qpol, ste, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(terule->orig_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(terule->orig_linenos, NULL, NULL);
+ for (j = 0; j < terule->num_mod_rules; j++) {
+ if (qpol_terule_get_syn_terule_iter(diff->mod_qpol, terule->mod_rules[j], &iter)) {
+ error = errno;
+ goto err;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&ste) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ if (qpol_syn_terule_get_lineno(diff->mod_qpol, ste, &lineno) < 0) {
+ error = errno;
+ goto err;
+ }
+ if (apol_vector_append(terule->mod_linenos, (void *)lineno) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto err;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_sort_uniquify(terule->mod_linenos, NULL, NULL);
+ }
+
+ return 0;
+ err:
+ qpol_iterator_destroy(&iter);
+ return -1;
+}
diff --git a/libpoldiff/src/terule_internal.h b/libpoldiff/src/terule_internal.h
new file mode 100644
index 0000000..79b4441
--- /dev/null
+++ b/libpoldiff/src/terule_internal.h
@@ -0,0 +1,244 @@
+/**
+ * @file
+ * Protected interface for rule differences, both AV and Type rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_TERULE_INTERNAL_H
+#define POLDIFF_TERULE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_terule_summary poldiff_terule_summary_t;
+
+/**
+ * Allocate and return a new poldiff_terule_summary_t object, used by
+ * TE rule searches.
+ *
+ * @return A new rule summary. The caller must call terule_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_terule_summary_t *terule_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_terule_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param rs Reference to an rule summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void terule_destroy(poldiff_terule_summary_t ** rs);
+
+/**
+ * Reset the state of TE rule type_change differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int terule_reset_change(poldiff_t * diff);
+
+/**
+ * Reset the state of TE rule type_member differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int terule_reset_member(poldiff_t * diff);
+
+/**
+ * Reset the state of TE rule type_transition differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int terule_reset_trans(poldiff_t * diff);
+
+/**
+ * Get a vector of type_change rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of type_change rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *terule_get_items_change(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of type_member rules from the given policy, sorted.
+ * This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ * @param flags Kind of rule to get, one of QPOL_RULE_TYPE_TRANS, etc.
+ *
+ * @return A newly allocated vector of type_member rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *terule_get_items_member(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Get a vector of type_transition rules from the given policy,
+ * sorted. This function will remap source and target types to their
+ * pseudo-type value equivalents.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return A newly allocated vector of type_transition rules (of type
+ * pseudo_terule_t). The caller is responsible for calling
+ * apol_vector_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ apol_vector_t *terule_get_items_trans(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two pseudo_terule_t objects, determining if they have the
+ * same key (specified + source + target + class + conditional
+ * expression).
+ *
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if te rule x is respectively less than,
+ * equal to, or greater than te rule y.
+ */
+ int terule_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule that was originally from a type_change rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_new_diff_change(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule that was originally from a type_member rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_new_diff_member(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a pseudo-te rule that was originally from a type_transition rule.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_new_diff_trans(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two pseudo-te rules (that were
+ * type_change rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_deep_diff_change(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-te rules (that were
+ * type_member rules) for which the compare callback returns 0. If a
+ * difference is found then allocate, initialize, and insert a new
+ * semantic difference entry for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_deep_diff_member(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Compute the semantic difference of two pseudo-te rules (that were
+ * type_transition rules) for which the compare callback returns 0.
+ * If a difference is found then allocate, initialize, and insert a
+ * new semantic difference entry for that pseudo-te rule.
+ *
+ * @param diff The policy difference structure associated with both
+ * pseudo-te rules and to which to add an entry if needed.
+ * @param x The pseudo-te rule from the original policy.
+ * @param y The pseudo-te rule from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int terule_deep_diff_trans(poldiff_t * diff, const void *x, const void *y);
+
+/**
+ * Iterate through a TE rule differences, filling in its line numbers.
+ *
+ * @param diff Diff structure containing avrule differences.
+ * @param idx Index into the terule differences specifying which line
+ * number table to enable.
+ *
+ * @return 0 on success, < 0 on errno.
+ */
+ int terule_enable_line_numbers(poldiff_t * diff, unsigned int idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TERULE_INTERNAL_H */
diff --git a/libpoldiff/src/type_diff.c b/libpoldiff/src/type_diff.c
new file mode 100644
index 0000000..32c7a17
--- /dev/null
+++ b/libpoldiff/src/type_diff.c
@@ -0,0 +1,662 @@
+/**
+ * @file
+ * Implementation for computing a semantic differences in types.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+/******************** types ********************/
+
+struct poldiff_type_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ int are_diffs_sorted;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_type
+{
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_attribs;
+ apol_vector_t *removed_attribs;
+};
+
+void poldiff_type_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->type_diffs->num_added;
+ stats[1] = diff->type_diffs->num_removed;
+ stats[2] = diff->type_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+char *poldiff_type_to_string(const poldiff_t * diff, const void *type)
+{
+ poldiff_type_t *t = (poldiff_type_t *) type;
+ size_t num_added, num_removed, len = 0, i;
+ char *s = NULL, *attrib;
+
+ if (diff == NULL || type == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ num_added = apol_vector_get_size(t->added_attribs);
+ num_removed = apol_vector_get_size(t->removed_attribs);
+ switch (t->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", t->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", t->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if (apol_str_appendf(&s, &len, "* %s (", t->name) < 0) {
+ break;
+ }
+ if (num_added > 0) {
+ if (apol_str_appendf(&s, &len, "%zd Added Attribute%s", num_added, (num_added == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (num_removed > 0) {
+ if (apol_str_appendf
+ (&s, &len, "%s%zd Removed Attribute%s", (num_added > 0 ? ", " : ""), num_removed,
+ (num_removed == 1 ? "" : "s")) < 0) {
+ break;
+ }
+ }
+ if (apol_str_append(&s, &len, ")\n") < 0) {
+ break;
+ }
+ for (i = 0; i < apol_vector_get_size(t->added_attribs); i++) {
+ attrib = (char *)apol_vector_get_element(t->added_attribs, i);
+ if (apol_str_appendf(&s, &len, "\t+ %s\n", attrib) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(t->removed_attribs); i++) {
+ attrib = (char *)apol_vector_get_element(t->removed_attribs, i);
+ if (apol_str_appendf(&s, &len, "\t- %s\n", attrib) < 0) {
+ goto err;
+ }
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+static int poldiff_type_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const poldiff_type_t *t1 = a;
+ const poldiff_type_t *t2 = b;
+ return strcmp(t1->name, t2->name);
+}
+
+const apol_vector_t *poldiff_get_type_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* the elements of the results vector are not sorted by name,
+ * but by pseudo-type value. thus sort them by name as
+ * necessary */
+ if (!diff->type_diffs->are_diffs_sorted) {
+ apol_vector_sort(diff->type_diffs->diffs, poldiff_type_comp, NULL);
+ diff->type_diffs->are_diffs_sorted = 1;
+ }
+ return diff->type_diffs->diffs;
+}
+
+const char *poldiff_type_get_name(const poldiff_type_t * type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return type->name;
+}
+
+poldiff_form_e poldiff_type_get_form(const void *type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return POLDIFF_FORM_NONE;
+ }
+ return ((const poldiff_type_t *)type)->form;
+}
+
+const apol_vector_t *poldiff_type_get_added_attribs(const poldiff_type_t * type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return type->added_attribs;
+}
+
+const apol_vector_t *poldiff_type_get_removed_attribs(const poldiff_type_t * type)
+{
+ if (type == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return type->removed_attribs;
+}
+
+/*************** protected functions for types ***************/
+
+/**
+ * Destroy a specified type
+ * @param type The type to destroy (a poldiff_type_t object)
+ */
+static void type_destroy(void *type)
+{
+ poldiff_type_t *t;
+ if (type == NULL)
+ return;
+ t = (poldiff_type_t *) type;
+ free(t->name);
+ apol_vector_destroy(&(t->added_attribs));
+ apol_vector_destroy(&(t->removed_attribs));
+ free(t);
+}
+
+poldiff_type_summary_t *type_summary_create(void)
+{
+ poldiff_type_summary_t *type = calloc(1, sizeof(*type));
+ if (type == NULL) {
+ return NULL;
+ }
+ if ((type->diffs = apol_vector_create(type_destroy)) == NULL) {
+ type_summary_destroy(&type);
+ return NULL;
+ }
+ return type;
+}
+
+void type_summary_destroy(poldiff_type_summary_t ** type)
+{
+ if (type != NULL && *type != NULL) {
+ apol_vector_destroy(&(*type)->diffs);
+ free(*type);
+ *type = NULL;
+ }
+}
+
+apol_vector_t *type_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ int error = 0;
+ qpol_type_t *t;
+ unsigned char isattr, isalias;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ uint32_t val;
+
+ if (diff == NULL || policy == NULL) {
+ error = errno = EINVAL;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ if (qpol_policy_get_type_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create(NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&t);
+ qpol_type_get_isalias(q, t, &isalias);
+ qpol_type_get_isattr(q, t, &isattr);
+ if (isattr || isalias)
+ continue;
+ val = type_map_lookup(diff, t, policy == diff->orig_pol ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD);
+ apol_vector_append(v, (void *)((size_t) val));
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort_uniquify(v, NULL, NULL);
+ return v;
+}
+
+int type_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ type_summary_destroy(&diff->type_diffs);
+ diff->type_diffs = type_summary_create();
+ if (diff->type_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Compare two type map values
+ * @param x The first type to compare, a (uint32_t) value
+ * @param y The second type to compare, a (uint32_t) value
+ * @param diff The policy difference structure
+ *
+ * @return < 0, 0, or > 0, if x is respectively less than
+ * equal to, or greater than y.
+ */
+int type_comp(const void *x, const void *y, const poldiff_t * diff __attribute__ ((unused)))
+{
+ uint32_t p1val = (uint32_t) ((size_t) x);
+ uint32_t p2val = (uint32_t) ((size_t) y);
+
+ /* p1val == p2val means the types are semantically equivalent */
+ return p1val - p2val;
+}
+
+/**
+ * Allocate and return a new type difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the type that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling type_destroy() upon the
+ * returned value.
+ */
+static poldiff_type_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_type_t *pt;
+ int error;
+
+ if ((pt = calloc(1, sizeof(poldiff_type_t))) == NULL ||
+ (pt->name = strdup(name)) == NULL ||
+ (pt->added_attribs = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pt->removed_attribs = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ type_destroy(pt);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pt->form = form;
+ return pt;
+}
+
+/**
+ * Allocate and return a string representing the type that is
+ * different. If the type was not remapped then it is simply that
+ * type's name within the policy. Otherwise the string will be of the
+ * form "orig_name1, orig_name2, ... -> mod_name1, mod_name2, ..."
+ *
+ * @param diff Diff structure containing type remaps.
+ * @param tval Pseudo-type value whose name to get.
+ *
+ * @return Allocated string name for the type, or NULL upon error.
+ * The caller must call free() upon the returned value.
+ */
+static char *type_get_name(const poldiff_t * diff, uint32_t tval)
+{
+ const apol_vector_t *v1, *v2;
+ size_t sv1, sv2;
+ size_t i, len = 0;
+ const qpol_type_t *qtype;
+ const char *name = NULL;
+ char *ret = NULL;
+ int error = 0;
+
+ /* names mapped from the first policy */
+ v1 = type_map_lookup_reverse(diff, tval, POLDIFF_POLICY_ORIG);
+ sv1 = apol_vector_get_size(v1);
+ /* names mapped from the second policy */
+ v2 = type_map_lookup_reverse(diff, tval, POLDIFF_POLICY_MOD);
+ sv2 = apol_vector_get_size(v2);
+
+ if (sv1 == 1 && sv2 == 0) {
+ /* return the name in v1 */
+ qtype = apol_vector_get_element(v1, 0);
+ if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 || (ret = strdup(name)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else if (sv1 == 0 && sv2 == 1) {
+ /* return the name in v2 */
+ qtype = apol_vector_get_element(v2, 0);
+ if (qpol_type_get_name(diff->mod_qpol, qtype, &name) < 0 || (ret = strdup(name)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else {
+ /* if the single name in v1 and v2 is the same return that name */
+ if (sv1 == sv2 && sv2 == 1) {
+ const char *name2;
+ qpol_type_t *qtype2;
+ qtype = apol_vector_get_element(v1, 0);
+ qtype2 = apol_vector_get_element(v2, 0);
+ if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 ||
+ qpol_type_get_name(diff->mod_qpol, qtype2, &name2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (strcmp(name, name2) == 0) {
+ if ((ret = strdup(name)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ }
+ goto cleanup;
+ }
+ }
+ /* build and return the composite name */
+ for (i = 0; i < sv1; i++) {
+ qtype = apol_vector_get_element(v1, i);
+ if (qpol_type_get_name(diff->orig_qpol, qtype, &name) < 0 ||
+ apol_str_appendf(&ret, &len, "%s%s", (i > 0 ? ", " : ""), name) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_str_append(&ret, &len, " -> ");
+ for (i = 0; i < sv2; i++) {
+ qtype = apol_vector_get_element(v2, i);
+ if (qpol_type_get_name(diff->mod_qpol, qtype, &name) < 0 ||
+ apol_str_appendf(&ret, &len, "%s%s", (i > 0 ? ", " : ""), name) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ cleanup:
+ if (error != 0) {
+ free(ret);
+ errno = error;
+ return NULL;
+ }
+ return ret;
+}
+
+int type_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ uint32_t tval = (uint32_t) ((size_t) item);
+ char *name = NULL;
+ poldiff_type_t *pt;
+ int error;
+
+ if ((name = type_get_name(diff, tval)) == NULL || (pt = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ free(name);
+ errno = error;
+ return -1;
+ }
+ free(name);
+ if (apol_vector_append(diff->type_diffs->diffs, pt) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ type_destroy(pt);
+ errno = error;
+ return -1;
+ }
+ diff->type_diffs->are_diffs_sorted = 0;
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->type_diffs->num_added++;
+ } else {
+ diff->type_diffs->num_removed++;
+ }
+ return 0;
+}
+
+/**
+ * Given an type, return a vector of its attributes (in the form of
+ * strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the type came.
+ * @param type Type whose attributes to get.
+ *
+ * @return Vector of attribute strings for the type. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *type_get_attrib_names(const poldiff_t * diff, const apol_policy_t * p, uint32_t type)
+{
+ qpol_iterator_t *attrib_iter = NULL;
+ const char *attrib;
+ char *new_attrib;
+ const apol_vector_t *v = NULL;
+ apol_vector_t *ret = NULL;
+ qpol_type_t *qt = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1;
+ size_t i;
+
+ if ((ret = apol_vector_create(free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* get the qpol_type_t objects for the specified type value
+ * and policy */
+ v = type_map_lookup_reverse(diff, type, (diff->orig_pol == p ? POLDIFF_POLICY_ORIG : POLDIFF_POLICY_MOD));
+ if (apol_vector_get_size(v) == 0) {
+ assert(false);
+ return NULL;
+ }
+ /* append the attributes for each qpol_type_t to the vector we return */
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ qt = apol_vector_get_element(v, i);
+ if (qt == NULL) {
+ assert(false);
+ return NULL;
+ }
+ qpol_type_get_attr_iter(q, qt, &attrib_iter);
+ for (; !qpol_iterator_end(attrib_iter); qpol_iterator_next(attrib_iter)) {
+
+ if (qpol_iterator_get_item(attrib_iter, (void **)&qt) < 0) {
+ goto cleanup;
+ }
+ qpol_type_get_name(q, qt, &attrib);
+ if ((new_attrib = strdup(attrib)) == NULL || apol_vector_append(ret, new_attrib) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ }
+ apol_vector_sort_uniquify(ret, &apol_str_strcmp, NULL);
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&attrib_iter);
+ if (retval < 0) {
+ apol_vector_destroy(&ret);
+ return NULL;
+ }
+ return ret;
+}
+
+int type_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ uint32_t tval1 = (uint32_t) ((size_t) x);
+ uint32_t tval2 = (uint32_t) ((size_t) y);
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *attrib1 = NULL, *attrib2 = NULL, *name = NULL;
+ poldiff_type_t *t = NULL;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ assert(tval1 == tval2);
+ /* can't do a deep diff of type if either policy does not retain attribute
+ * names because the fake attribute names are bogus */
+ if (!(qpol_policy_has_capability(apol_policy_get_qpol(diff->orig_pol), QPOL_CAP_ATTRIB_NAMES))
+ || !(qpol_policy_has_capability(apol_policy_get_qpol(diff->mod_pol), QPOL_CAP_ATTRIB_NAMES))) {
+ return 0;
+ }
+ v1 = type_get_attrib_names(diff, diff->orig_pol, tval1);
+ v2 = type_get_attrib_names(diff, diff->mod_pol, tval2);
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ attrib1 = (char *)apol_vector_get_element(v1, i);
+ attrib2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(attrib1, attrib2);
+ if (compval != 0 && t == NULL) {
+ name = type_get_name(diff, tval1);
+ if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ free(name);
+ name = NULL;
+ }
+ if (compval < 0) {
+ if ((attrib1 = strdup(attrib1)) == NULL || apol_vector_append(t->removed_attribs, attrib1) < 0) {
+ error = errno;
+ free(attrib1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((attrib2 = strdup(attrib2)) == NULL || apol_vector_append(t->added_attribs, attrib2) < 0) {
+ error = errno;
+ free(attrib2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ attrib1 = (char *)apol_vector_get_element(v1, i);
+ if (t == NULL) {
+ name = type_get_name(diff, tval1);
+ if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ free(name);
+ name = NULL;
+ }
+ if ((attrib1 = strdup(attrib1)) == NULL || apol_vector_append(t->removed_attribs, attrib1) < 0) {
+ error = errno;
+ free(attrib1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ attrib2 = (char *)apol_vector_get_element(v2, j);
+ if (t == NULL) {
+ name = type_get_name(diff, tval1);
+ if ((t = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ free(name);
+ name = NULL;
+ }
+ if ((attrib2 = strdup(attrib2)) == NULL || apol_vector_append(t->added_attribs, attrib2) < 0) {
+ error = errno;
+ free(attrib2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (t != NULL) {
+ if (apol_vector_append(diff->type_diffs->diffs, t) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->type_diffs->are_diffs_sorted = 0;
+ diff->type_diffs->num_modified++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ free(name);
+ if (retval != 0) {
+ type_destroy(t);
+ }
+ errno = error;
+ return retval;
+ return 0;
+}
diff --git a/libpoldiff/src/type_internal.h b/libpoldiff/src/type_internal.h
new file mode 100644
index 0000000..8c9cb44
--- /dev/null
+++ b/libpoldiff/src/type_internal.h
@@ -0,0 +1,125 @@
+/**
+ * @file
+ * Protected Interface for type differences.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_TYPE_INTERNAL_H
+#define POLDIFF_TYPE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/******************** types ********************/
+
+ typedef struct poldiff_type_summary poldiff_type_summary_t;
+
+/**
+ * Allocate and return a new poldiff_type_summary_t object.
+ *
+ * @return A new type summary. The caller must call type_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_type_summary_t *type_summary_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_type_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param type Reference to a type summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void type_summary_destroy(poldiff_type_summary_t ** type);
+
+/**
+ * Reset the state of all type differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int type_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all type (type qpol_type_t) from the
+ * given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all typees. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *type_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_type_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The type from the original policy.
+ * @param y The type from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if type x is respectively less than, equal
+ * to, or greater than type y.
+ */
+ int type_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a type.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int type_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two types for which the
+ * compare callback returns 0. If a difference is found then
+ * allocate, initialize, and insert a new semantic difference entry
+ * for that type.
+ *
+ * @param diff The policy difference structure associated with both
+ * types and to which to add an entry if needed.
+ * @param x The type from the original policy.
+ * @param y The type from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int type_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_INTERNAL_H */
diff --git a/libpoldiff/src/type_map.c b/libpoldiff/src/type_map.c
new file mode 100644
index 0000000..ff97c43
--- /dev/null
+++ b/libpoldiff/src/type_map.c
@@ -0,0 +1,985 @@
+/**
+ * @file
+ * Implementation of type equivalence mapping for semantic
+ * difference calculations.
+ * The mapping of types is handled by creating a list of pseudo type
+ * values to represent the set of all semantically unique types in
+ * both the original and modified policies. This mapping takes into
+ * account both inferred and user specified mappings of types and may
+ * contain holes where a type does not exist in one of the policies.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "poldiff_internal.h"
+
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * A poldiff's type map consists of maps between policies' types to a
+ * unified pseudo-type value.
+ */
+struct type_map
+{
+ /** array of size num_orig_types mapping types by (value - 1)
+ to pseudo value */
+ uint32_t *orig_to_pseudo;
+ /** array of size num_mod_types mapping types by (value - 1)
+ to pseudo value */
+ uint32_t *mod_to_pseudo;
+ /** vector of vector of qpol_type_t that reverse map pseudo
+ value to orig_pol value(s) */
+ apol_vector_t *pseudo_to_orig;
+ /** vector of vector of qpol_type_t that reverse map pseudo
+ value to mod_pol value(s) */
+ apol_vector_t *pseudo_to_mod;
+ size_t num_orig_types;
+ size_t num_mod_types;
+ /** vector of poldiff_type_remap_entry_t */
+ apol_vector_t *remap;
+};
+
+/**
+ * Each map entry consists of 2 vectors, each vector being a list of
+ * qpol_type_t.
+ */
+struct poldiff_type_remap_entry
+{
+ /** vector of names of qpol_type_t in original qpolicy */
+ apol_vector_t *orig_types;
+ /** vector of names of qpol_type_t in the modified qpolicy */
+ apol_vector_t *mod_types;
+ int inferred;
+ int enabled;
+};
+
+/**
+ * Free the space associated with a singly type remap entry.
+ *
+ * @param elem Pointer to a type remap entry to free. If NULL then do
+ * nothing.
+ */
+static void type_remap_entry_free(void *elem)
+{
+ poldiff_type_remap_entry_t *entry = (poldiff_type_remap_entry_t *) elem;
+ if (entry != NULL) {
+ apol_vector_destroy(&entry->orig_types);
+ apol_vector_destroy(&entry->mod_types);
+ free(entry);
+ }
+}
+
+/**
+ * Allocate a new poldiff type remap entry, append it to the current
+ * type remap vector, enable it, and return the entry.
+ *
+ * @param diff Policy diff structure containing remap vector.
+ *
+ * @return a new entry, or NULL on error.
+ */
+static poldiff_type_remap_entry_t *poldiff_type_remap_entry_create(poldiff_t * diff)
+{
+ poldiff_type_remap_entry_t *e = NULL;
+ if ((e = calloc(1, sizeof(*e))) == NULL ||
+ (e->orig_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (e->mod_types = apol_vector_create_with_capacity(1, free)) == NULL || apol_vector_append(diff->type_map->remap, e) < 0)
+ {
+ type_remap_entry_free(e);
+ return NULL;
+ }
+ diff->remapped = 1;
+ e->enabled = 1;
+ return e;
+}
+
+int poldiff_type_remap_create(poldiff_t * diff, const apol_vector_t * orig_names, const apol_vector_t * mod_names)
+{
+ poldiff_type_remap_entry_t *entry = NULL;
+ size_t i;
+ char *name;
+ const qpol_type_t *type;
+ unsigned char isalias, isattr;
+ int retval = -1, error = 0;
+ if (diff == NULL || orig_names == NULL || mod_names == NULL) {
+ error = EINVAL;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_size(orig_names) == 0 ||
+ apol_vector_get_size(mod_names) == 0 || (apol_vector_get_size(orig_names) > 1 && apol_vector_get_size(mod_names) > 1)) {
+ error = EINVAL;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if ((entry = calloc(1, sizeof(*entry))) == NULL ||
+ (entry->orig_types = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (entry->mod_types = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = ENOMEM;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(orig_names); i++) {
+ name = (char *)apol_vector_get_element(orig_names, i);
+ if (qpol_policy_get_type_by_name(diff->orig_qpol, name, &type) < 0 ||
+ qpol_type_get_isalias(diff->orig_qpol, type, &isalias) < 0 ||
+ qpol_type_get_isattr(diff->orig_qpol, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias || isattr) {
+ error = EINVAL;
+ ERR(diff, "%s is not a primary type.", name);
+ goto cleanup;
+ }
+ if ((name = strdup(name)) == NULL || apol_vector_append(entry->orig_types, (void *)name) < 0) {
+ error = ENOMEM;
+ free(name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort_uniquify(entry->orig_types, apol_str_strcmp, NULL);
+ for (i = 0; i < apol_vector_get_size(mod_names); i++) {
+ name = (char *)apol_vector_get_element(mod_names, i);
+ if (qpol_policy_get_type_by_name(diff->mod_qpol, name, &type) < 0 ||
+ qpol_type_get_isalias(diff->mod_qpol, type, &isalias) < 0 ||
+ qpol_type_get_isattr(diff->mod_qpol, type, &isattr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (isalias || isattr) {
+ error = EINVAL;
+ ERR(diff, "%s is not a primary type.", name);
+ goto cleanup;
+ }
+ if ((name = strdup(name)) == NULL || apol_vector_append(entry->mod_types, (void *)name) < 0) {
+ error = ENOMEM;
+ free(name);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort_uniquify(entry->mod_types, apol_str_strcmp, NULL);
+ entry->enabled = 1;
+ if (apol_vector_append(diff->type_map->remap, entry) < 0) {
+ error = ENOMEM;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ diff->remapped = 1;
+ cleanup:
+ if (retval < 0) {
+ type_remap_entry_free(entry);
+ }
+ errno = error;
+ return retval;
+}
+
+apol_vector_t *poldiff_type_remap_get_entries(const poldiff_t * diff)
+{
+ if (diff == NULL || diff->type_map == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->type_map->remap;
+}
+
+void poldiff_type_remap_entry_remove(poldiff_t * diff, poldiff_type_remap_entry_t * entry)
+{
+ size_t idx;
+ if (diff == NULL || entry == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ if (apol_vector_get_index(diff->type_map->remap, entry, NULL, NULL, &idx) < 0) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ apol_vector_remove(diff->type_map->remap, idx);
+ diff->remapped = 1;
+}
+
+apol_vector_t *poldiff_type_remap_entry_get_original_types(const poldiff_t * diff, const poldiff_type_remap_entry_t * entry)
+{
+ if (diff == NULL || entry == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_vector_create_from_vector(entry->orig_types, NULL, NULL, NULL);
+}
+
+apol_vector_t *poldiff_type_remap_entry_get_modified_types(const poldiff_t * diff, const poldiff_type_remap_entry_t * entry)
+{
+ if (diff == NULL || entry == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_vector_create_from_vector(entry->mod_types, NULL, NULL, NULL);
+}
+
+int poldiff_type_remap_entry_get_is_inferred(const poldiff_type_remap_entry_t * entry)
+{
+ if (entry == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return entry->inferred;
+}
+
+int poldiff_type_remap_entry_get_is_enabled(const poldiff_type_remap_entry_t * entry)
+{
+ if (entry == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return entry->enabled;
+}
+
+void poldiff_type_remap_entry_set_enabled(poldiff_type_remap_entry_t * entry, int enabled)
+{
+ if (entry == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ if (enabled) {
+ entry->enabled = 1;
+ } else {
+ entry->enabled = 0;
+ }
+}
+
+type_map_t *type_map_create(void)
+{
+ type_map_t *map = calloc(1, sizeof(*map));
+ if (map == NULL) {
+ return NULL;
+ }
+ if ((map->remap = apol_vector_create(type_remap_entry_free)) == NULL) {
+ type_map_destroy(&map);
+ return NULL;
+ }
+ return map;
+}
+
+void type_map_destroy(type_map_t ** map)
+{
+ if (map != NULL && *map != NULL) {
+ free((*map)->orig_to_pseudo);
+ free((*map)->mod_to_pseudo);
+ apol_vector_destroy(&(*map)->pseudo_to_orig);
+ apol_vector_destroy(&(*map)->pseudo_to_mod);
+ apol_vector_destroy(&(*map)->remap);
+ free(*map);
+ *map = NULL;
+ }
+}
+
+/**
+ * If --enable-debug is given, then dump to stdout the type map from
+ * policy's types -> pseudo-types.
+ */
+static void type_map_dump(poldiff_t * diff)
+{
+#ifdef SETOOLS_DEBUG
+ size_t i, j;
+ apol_vector_t *v;
+ const qpol_type_t *t;
+ const char *name;
+ printf("# type map debug dump (qpol_type_t -> pseudo-type):\norig:\n");
+ for (i = 0; i < diff->type_map->num_orig_types; i++) {
+ printf("%3zd:%5d", i, diff->type_map->orig_to_pseudo[i]);
+ if ((i + 1) % 5 == 0) {
+ printf("\n");
+ } else {
+ printf("\t");
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(diff->type_map->pseudo_to_orig); i++) {
+ v = apol_vector_get_element(diff->type_map->pseudo_to_orig, i);
+ printf("\n%3zd->", i);
+ for (j = 0; j < apol_vector_get_size(v); j++) {
+ t = apol_vector_get_element(v, j);
+ qpol_type_get_name(diff->orig_qpol, t, &name);
+ printf(" %s", name);
+ }
+ }
+ printf("\nmod:\n");
+ for (i = 0; i < diff->type_map->num_mod_types; i++) {
+ printf("%3zd:%5d", i, diff->type_map->mod_to_pseudo[i]);
+ if ((i + 1) % 5 == 0) {
+ printf("\n");
+ } else {
+ printf("\t");
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(diff->type_map->pseudo_to_mod); i++) {
+ v = apol_vector_get_element(diff->type_map->pseudo_to_mod, i);
+ printf("\n%3zd->", i);
+ for (j = 0; j < apol_vector_get_size(v); j++) {
+ t = apol_vector_get_element(v, j);
+ qpol_type_get_name(diff->mod_qpol, t, &name);
+ printf(" %s", name);
+ }
+ }
+ printf("\n");
+#endif
+}
+
+/**
+ * Free a vector of qpol_type_t pointers.
+ */
+static void type_map_vector_free(void *elem)
+{
+ apol_vector_t *v = (apol_vector_t *) elem;
+ if (v != NULL) {
+ apol_vector_destroy(&v);
+ }
+}
+
+int type_map_build(poldiff_t * diff)
+{
+ type_map_t *map;
+ apol_vector_t *ov = NULL, *mv = NULL;
+ int retval = -1, error = 0;
+ size_t i, j;
+ const qpol_type_t *t;
+ uint32_t val, max_val, next_val;
+ apol_vector_t *reverse_v = NULL;
+
+ map = diff->type_map;
+ free(map->orig_to_pseudo);
+ map->orig_to_pseudo = NULL;
+ map->num_orig_types = 0;
+ free(map->mod_to_pseudo);
+ map->mod_to_pseudo = NULL;
+ map->num_mod_types = 0;
+ apol_vector_destroy(&map->pseudo_to_orig);
+ apol_vector_destroy(&map->pseudo_to_mod);
+
+ if (apol_type_get_by_query(diff->orig_pol, NULL, &ov) < 0 || apol_type_get_by_query(diff->mod_pol, NULL, &mv) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* there is no guarantee that the number of types is equal to
+ * the highest type value (because a policy could have
+ * attributes), so calculate them here */
+ max_val = 0;
+ for (i = 0; i < apol_vector_get_size(ov); i++) {
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ if (qpol_type_get_value(diff->orig_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (val > max_val) {
+ max_val = val;
+ }
+ }
+ if ((map->orig_to_pseudo = calloc(max_val, sizeof(*(map->orig_to_pseudo)))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ map->num_orig_types = max_val;
+ max_val = 0;
+ for (i = 0; i < apol_vector_get_size(mv); i++) {
+ t = (qpol_type_t *) apol_vector_get_element(mv, i);
+ if (qpol_type_get_value(diff->mod_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (val > max_val) {
+ max_val = val;
+ }
+ }
+ if ((map->mod_to_pseudo = calloc(max_val, sizeof(*(map->mod_to_pseudo)))) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ map->num_mod_types = max_val;
+
+ if ((map->pseudo_to_orig = apol_vector_create(type_map_vector_free)) == NULL
+ || (map->pseudo_to_mod = apol_vector_create(type_map_vector_free)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ next_val = 1;
+ for (i = 0; i < apol_vector_get_size(map->remap); i++) {
+ poldiff_type_remap_entry_t *e;
+ const char *name;
+ e = (poldiff_type_remap_entry_t *) apol_vector_get_element(map->remap, i);
+ if (!e->enabled) {
+ continue;
+ }
+
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(e->orig_types); j++) {
+ name = (const char *)apol_vector_get_element(e->orig_types, j);
+ if (qpol_policy_get_type_by_name(diff->orig_qpol, name, &t) < 0 ||
+ qpol_type_get_value(diff->orig_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->orig_to_pseudo[val - 1] != 0) {
+ error = EINVAL;
+ ERR(diff, "Type %s is already remapped.", name);
+ goto cleanup;
+ }
+ map->orig_to_pseudo[val - 1] = next_val;
+ if (apol_vector_append(reverse_v, (void *)t) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(e->mod_types); j++) {
+ name = (const char *)apol_vector_get_element(e->mod_types, j);
+ if (qpol_policy_get_type_by_name(diff->mod_qpol, name, &t) < 0 ||
+ qpol_type_get_value(diff->mod_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->mod_to_pseudo[val - 1] != 0) {
+ error = EINVAL;
+ ERR(diff, "Type %s is already remapped.", name);
+ goto cleanup;
+ }
+ map->mod_to_pseudo[val - 1] = next_val;
+ if (apol_vector_append(reverse_v, (void *)t) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+
+ next_val++;
+ }
+
+ /* all remaining types (both from orig and mod) get their own
+ * values */
+ for (i = 0; i < apol_vector_get_size(ov); i++) {
+ t = apol_vector_get_element(ov, i);
+ if (qpol_type_get_value(diff->orig_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->orig_to_pseudo[val - 1] == 0) {
+ map->orig_to_pseudo[val - 1] = next_val;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(reverse_v, (void *)t) < 0 ||
+ apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ next_val++;
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(mv); i++) {
+ t = apol_vector_get_element(mv, i);
+ if (qpol_type_get_value(diff->mod_qpol, t, &val) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (map->mod_to_pseudo[val - 1] == 0) {
+ map->mod_to_pseudo[val - 1] = next_val;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(reverse_v, (void *)t) < 0 || apol_vector_append(map->pseudo_to_mod, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ if ((reverse_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(map->pseudo_to_orig, reverse_v) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ reverse_v = NULL;
+ next_val++;
+ }
+ }
+
+ type_map_dump(diff);
+
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&ov);
+ apol_vector_destroy(&mv);
+ apol_vector_destroy(&reverse_v);
+ error = errno;
+ return retval;
+}
+
+void poldiff_type_remap_flush(poldiff_t * diff)
+{
+ if (diff == NULL || diff->type_map == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ apol_vector_destroy(&(diff->type_map->remap));
+ /* no error checking below */
+ diff->type_map->remap = apol_vector_create(type_remap_entry_free);
+ diff->remapped = 1;
+}
+
+/**
+ * Convenience struct for comparing elements within arrays of primary types.
+ */
+struct type_map_comp
+{
+ poldiff_t *diff;
+ /** from which policy the first element came, either
+ * POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD */
+ int dir;
+};
+
+/**
+ * Given two qpol_type_t pointers, both of which are primary types,
+ * compare their names for equivalence.
+ *
+ * @param a Pointer to a qpol_type_t from a policy.
+ * @param b Pointer to a qpol_type_t from a policy.
+ * @param data Pointer to a type_map_comp struct.
+ *
+ * @return 0 if the names match, non-zero if not.
+ */
+static int type_map_primary_comp(const void *a, const void *b, void *data)
+{
+ const qpol_type_t *ta = a;
+ const qpol_type_t *tb = b;
+ struct type_map_comp *c = (struct type_map_comp *)data;
+ poldiff_t *diff = c->diff;
+ int dir = c->dir;
+ const char *na, *nb;
+ if (dir == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_name(diff->orig_qpol, ta, &na) < 0 || qpol_type_get_name(diff->mod_qpol, tb, &nb) < 0) {
+ return -1;
+ }
+ } else {
+ if (qpol_type_get_name(diff->mod_qpol, ta, &na) < 0 || qpol_type_get_name(diff->orig_qpol, tb, &nb) < 0) {
+ return -1;
+ }
+ }
+ return strcmp(na, nb);
+}
+
+/**
+ * Given two qpol_type_t pointers, both of which are primary types,
+ * see if the first type matches any of the other type's aliases.
+ *
+ * @param a Pointer to a qpol_type_t from a policy.
+ * @param b Pointer to a qpol_type_t from a policy.
+ * @param data Pointer to a type_map_comp struct.
+ *
+ * @return 0 if b is a member of a's aliases, non-zero if not.
+ */
+static int type_map_prim_alias_comp(const void *a, const void *b, void *data)
+{
+ const qpol_type_t *ta = a;
+ const qpol_type_t *tb = b;
+ struct type_map_comp *c = (struct type_map_comp *)data;
+ poldiff_t *diff = c->diff;
+ int dir = c->dir;
+ const char *prim, *alias;
+ qpol_iterator_t *iter = NULL;
+ if (dir == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_alias_iter(diff->orig_qpol, ta, &iter) < 0 || qpol_type_get_name(diff->mod_qpol, tb, &prim) < 0) {
+ qpol_iterator_destroy(&iter);
+ return -1;
+ }
+ } else {
+ if (qpol_type_get_alias_iter(diff->mod_qpol, ta, &iter) < 0 || qpol_type_get_name(diff->orig_qpol, tb, &prim) < 0) {
+ qpol_iterator_destroy(&iter);
+ return -1;
+ }
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&alias) < 0) {
+ qpol_iterator_destroy(&iter);
+ return -1;
+ }
+ if (strcmp(prim, alias) == 0) {
+ qpol_iterator_destroy(&iter);
+ return 0;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return -1;
+}
+
+/**
+ * Given two qpol_type_t pointers, both of which are primary types,
+ * see if the first type's aliases matches the second type's aliases.
+ *
+ * @param a Pointer to a qpol_type_t from a policy.
+ * @param b Pointer to a qpol_type_t from a policy.
+ * @param data Pointer to a type_map_comp struct.
+ *
+ * @return 0 if b is a member of a's aliases, non-zero if not.
+ */
+static int type_map_prim_aliases_comp(const void *a, const void *b, void *data)
+{
+ qpol_type_t *ta = (qpol_type_t *) a;
+ qpol_type_t *tb = (qpol_type_t *) b;
+ struct type_map_comp *c = (struct type_map_comp *)data;
+ poldiff_t *diff = c->diff;
+ int dir = c->dir;
+ qpol_policy_t *p1, *p2;
+ qpol_iterator_t *iter1 = NULL, *iter2 = NULL;
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ size_t i;
+ int retval = -1, error = 0;
+ if (dir == POLDIFF_POLICY_ORIG) {
+ p1 = diff->orig_qpol;
+ p2 = diff->mod_qpol;
+ } else {
+ p1 = diff->mod_qpol;
+ p2 = diff->orig_qpol;
+ }
+ if (qpol_type_get_alias_iter(p1, ta, &iter1) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v1 = apol_vector_create_from_iter(iter1, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (qpol_type_get_alias_iter(p2, tb, &iter2) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((v2 = apol_vector_create_from_iter(iter2, NULL)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (apol_vector_get_size(v1) == 0 || apol_vector_get_size(v2) == 0) {
+ retval = 1;
+ goto cleanup;
+ } else {
+ apol_vector_sort_uniquify(v1, apol_str_strcmp, NULL);
+ apol_vector_sort_uniquify(v2, apol_str_strcmp, NULL);
+ retval = apol_vector_compare(v1, v2, apol_str_strcmp, NULL, &i);
+ }
+ cleanup:
+ qpol_iterator_destroy(&iter1);
+ qpol_iterator_destroy(&iter2);
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ errno = error;
+ return retval;
+}
+
+/**
+ * If --enable-debug is given, then dump to stdout the type-map from
+ * pseudo-types to the policy's type(s).
+ */
+static void type_remap_vector_dump(poldiff_t * diff)
+{
+#ifdef SETOOLS_DEBUG
+ apol_vector_t *v, *w;
+ size_t i, j;
+ poldiff_type_remap_entry_t *e;
+ char *name;
+ printf("# type remap vector debug dump (pseudo-type -> qpol_type_t(s):\n");
+ v = poldiff_type_remap_get_entries(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ e = apol_vector_get_element(v, i);
+ printf("%zd\t%s\t", i, poldiff_type_remap_entry_get_is_enabled(e) ? "en" : "dis");
+ w = poldiff_type_remap_entry_get_original_types(diff, e);
+ for (j = 0; j < apol_vector_get_size(w); j++) {
+ name = apol_vector_get_element(w, j);
+ printf("%s ", name);
+ }
+ apol_vector_destroy(&w);
+ printf("-> ");
+ w = poldiff_type_remap_entry_get_modified_types(diff, e);
+ for (j = 0; j < apol_vector_get_size(w); j++) {
+ name = apol_vector_get_element(w, j);
+ printf("%s ", name);
+ }
+ apol_vector_destroy(&w);
+ printf("\n");
+ }
+#endif
+}
+
+static int type_map_entry_append_qtypes(poldiff_t * diff, poldiff_type_remap_entry_t * entry, const qpol_type_t * t,
+ const qpol_type_t * u)
+{
+ const char *name;
+ char *dup_name;
+ if (qpol_type_get_name(diff->orig_qpol, t, &name) < 0) {
+ return -1;
+ }
+ if ((dup_name = strdup(name)) == NULL || apol_vector_append(entry->orig_types, (void *)dup_name) < 0) {
+ free(dup_name);
+ return -1;
+ }
+
+ if (qpol_type_get_name(diff->mod_qpol, u, &name) < 0) {
+ return -1;
+ }
+ if ((dup_name = strdup(name)) == NULL || apol_vector_append(entry->mod_types, (void *)dup_name) < 0) {
+ free(dup_name);
+ return -1;
+ }
+ return 0;
+}
+
+int type_map_infer(poldiff_t * diff)
+{
+ apol_vector_t *ov = NULL, *mv = NULL;
+ char *orig_done = NULL, *mod_done = NULL;
+ size_t num_orig, num_mod, i, j;
+ qpol_type_t *t, *u;
+ struct type_map_comp c = { diff, 0 };
+ poldiff_type_remap_entry_t *entry = NULL;
+ int retval = -1, error = 0;
+
+ INFO(diff, "%s", "Inferring type remap.");
+ if (apol_type_get_by_query(diff->orig_pol, NULL, &ov) < 0 || apol_type_get_by_query(diff->mod_pol, NULL, &mv) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ num_orig = apol_vector_get_size(ov);
+ num_mod = apol_vector_get_size(mv);
+ if ((orig_done = calloc(1, num_orig)) == NULL || (mod_done = calloc(1, num_mod)) == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ /* first map primary <--> primary */
+ c.dir = POLDIFF_POLICY_MOD;
+ for (i = 0; i < num_orig; i++) {
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ if (apol_vector_get_index(mv, t, type_map_primary_comp, &c, &j) < 0) {
+ continue;
+ }
+ assert(!mod_done[j]);
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ /* now map primary -> primary's alias */
+ c.dir = POLDIFF_POLICY_MOD;
+ for (i = 0; i < num_orig; i++) {
+ if (orig_done[i]) {
+ continue;
+ }
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ u = NULL;
+ for (j = 0; j < num_mod; j++) {
+ if (mod_done[j]) {
+ continue;
+ }
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ if (type_map_prim_alias_comp(u, t, &c) == 0) {
+ break;
+ }
+ }
+ if (j >= num_mod) {
+ continue;
+ }
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ /* then map primary's alias <- primary */
+ c.dir = POLDIFF_POLICY_ORIG;
+ for (j = 0; j < num_mod; j++) {
+ if (mod_done[j]) {
+ continue;
+ }
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ t = NULL;
+ for (i = 0; i < num_orig; i++) {
+ if (orig_done[i]) {
+ continue;
+ }
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ if (type_map_prim_alias_comp(t, u, &c) == 0) {
+ break;
+ }
+ }
+ if (i >= num_orig) {
+ continue;
+ }
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ /* map alias <-> alias */
+ c.dir = POLDIFF_POLICY_MOD;
+ for (i = 0; i < num_orig; i++) {
+ if (orig_done[i]) {
+ continue;
+ }
+ t = (qpol_type_t *) apol_vector_get_element(ov, i);
+ u = NULL;
+ for (j = 0; j < num_mod; j++) {
+ if (mod_done[j]) {
+ continue;
+ }
+ u = (qpol_type_t *) apol_vector_get_element(mv, j);
+ if (type_map_prim_aliases_comp(u, t, &c) == 0) {
+ break;
+ }
+ }
+ if (j >= num_mod) {
+ continue;
+ }
+ if ((entry = poldiff_type_remap_entry_create(diff)) == NULL || type_map_entry_append_qtypes(diff, entry, t, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ entry->inferred = 1;
+ orig_done[i] = 1;
+ mod_done[j] = 1;
+ }
+
+ type_remap_vector_dump(diff);
+
+ retval = 0;
+ diff->remapped = 1;
+ cleanup:
+ apol_vector_destroy(&ov);
+ apol_vector_destroy(&mv);
+ free(orig_done);
+ free(mod_done);
+ errno = error;
+ return retval;
+}
+
+uint32_t type_map_lookup(const poldiff_t * diff, const qpol_type_t * type, int which_pol)
+{
+ uint32_t val;
+ if (which_pol == POLDIFF_POLICY_ORIG) {
+ if (qpol_type_get_value(diff->orig_qpol, type, &val) < 0) {
+ return 0;
+ }
+ assert(val <= diff->type_map->num_orig_types);
+ assert(diff->type_map->orig_to_pseudo[val - 1] != 0);
+ return diff->type_map->orig_to_pseudo[val - 1];
+ } else {
+ if (qpol_type_get_value(diff->mod_qpol, type, &val) < 0) {
+ return 0;
+ }
+ assert(val <= diff->type_map->num_mod_types);
+ assert(diff->type_map->mod_to_pseudo[val - 1] != 0);
+ return diff->type_map->mod_to_pseudo[val - 1];
+ }
+}
+
+const apol_vector_t *type_map_lookup_reverse(const poldiff_t * diff, uint32_t val, int which_pol)
+{
+ if (which_pol == POLDIFF_POLICY_ORIG) {
+ return apol_vector_get_element(diff->type_map->pseudo_to_orig, val - 1);
+ } else {
+ return apol_vector_get_element(diff->type_map->pseudo_to_mod, val - 1);
+ }
+}
+
+const char *type_map_get_name(const poldiff_t * diff, const uint32_t pseudo_val, int pol)
+{
+ const apol_vector_t *v = NULL;
+ const char *name = NULL;
+ const qpol_type_t *t;
+
+ v = type_map_lookup_reverse(diff, pseudo_val, pol);
+ if (apol_vector_get_size(v) == 0) {
+ return NULL;
+ }
+ t = apol_vector_get_element(v, 0);
+ if (pol == POLDIFF_POLICY_ORIG)
+ qpol_type_get_name(diff->orig_qpol, t, &name);
+ else
+ qpol_type_get_name(diff->mod_qpol, t, &name);
+ return name;
+}
diff --git a/libpoldiff/src/type_map_internal.h b/libpoldiff/src/type_map_internal.h
new file mode 100644
index 0000000..786f38d
--- /dev/null
+++ b/libpoldiff/src/type_map_internal.h
@@ -0,0 +1,171 @@
+/**
+ * @file
+ * Protected interface for type equivalence mapping for semantic
+ * difference calculations.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_TYPE_MAP_INTERNAL_H
+#define POLDIFF_TYPE_MAP_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+#include <qpol/policy.h>
+
+ typedef struct type_map type_map_t;
+
+#define POLDIFF_POLICY_ORIG 1
+#define POLDIFF_POLICY_MOD 2
+
+/**
+ * Allocate and return a new type_map_t object.
+ *
+ * @return a new type map object. The caller must call
+ * type_map_destroy() afterwards. On error, return NULL and set
+ * errno.
+ */
+ type_map_t *type_map_create(void);
+
+/**
+ * Free all memory used by the type map.
+ *
+ * @param map Reference pointer to the type map to destroy. This
+ * pointer will be set to NULL afterwards.
+ */
+ void type_map_destroy(type_map_t ** map);
+
+/**
+ * Build the type map for a policy difference structure, using all
+ * enabled poldiff_type_remap_entry entries as hints for the
+ * mappings. This function should be called by poldiff_run() before
+ * each run.
+ *
+ * @param diff The policy difference structure containing the
+ * policies from which to construct the type map.
+ * @return 0 on success and < 0 on error, if the call fails, errno will
+ * be set and the policy difference structure will be unchanged.
+ */
+ int type_map_build(poldiff_t * diff);
+
+/**
+ * Clear away all type remap entries within the type map. This
+ * function should be called some time after type_map_create().
+ *
+ * @param diff The policy difference structure containing the
+ * policies from which to construct the type map.
+ */
+ void poldiff_type_remap_flush(poldiff_t * diff);
+
+/**
+ * Infer type remappings and append them to the current type remap
+ * vector. The vector should probably be first flushed via
+ * poldiff_type_remap_flush(). Generated entries will have their
+ * 'inferred' and 'enabled' flags set.
+ *
+ * The heuristic for determining type remaps is as follow.
+ * <ol>
+ *
+ * <li>If any type name exists as a primary in both policies then map
+ * it.
+ *
+ * <li>For all remaining unmapped primary types in the original
+ * policy, if that type name appears as an alias to an unmapped
+ * primary in the modified then map it.
+ *
+ * <li>For all remaining unmapped primary types in the modified
+ * policy, if that type name appears as an alias to an unmapped
+ * primary in the original then map it.
+ *
+ * <li>For all remaining unmapped primary types in both policies, if
+ * all of the aliases of one type are exactly the same as another
+ * type's aliases then map it.
+ *
+ * <li>All remaining types are left as unmapped.
+ *
+ * </ol>
+ *
+ * A side-effect of this heuristic is that it is reversible; the same
+ * inferences are made regardless of the order of policies.
+ *
+ * @param diff The policy difference structure containing the
+ * policies from which to construct the type map.
+ *
+ * @return 0 on success, < 0 on error and errno will be set.
+ */
+ int type_map_infer(poldiff_t * diff);
+
+/**
+ * Given a qpol_type_t and a flag indicating from which the policy
+ * the type originated, return its remapped value. (type_map_build()
+ * must have been first called.)
+ *
+ * @param diff The policy difference structure assocated with the
+ * types.
+ * @param type Type to lookup.
+ * @param which_pol One of POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD.
+ *
+ * @return The type's remapped value. On error this will be 0 and
+ * errno will be set.
+ */
+ uint32_t type_map_lookup(const poldiff_t * diff, const qpol_type_t * type, int which_pol);
+
+/**
+ * Given a pseudo-type's value and a flag indicating for which policy
+ * to look up, return a vector of qpol_type_t pointers to reference
+ * back to the unmapped types. (type_map_build() must have been
+ * first called.) Note that the returned vector could be empty for
+ * the situation where a type was added or removed.
+ *
+ * @param diff The policy difference structure assocated with the
+ * types.
+ * @param val Pseudo-type value to lookup.
+ * @param which_pol One of POLDIFF_POLICY_ORIG or POLDIFF_POLICY_MOD.
+ *
+ * @return A vector of qpol_type_t pointers. The caller should not
+ * free this vector. If the call fails, NULL will be returned and
+ * errno will be set.
+ */
+ const apol_vector_t *type_map_lookup_reverse(const poldiff_t * diff, uint32_t val, int which_pol);
+
+/**
+ * Get the first name that can be found for a pseudo type value.
+ *
+ * @param diff Policy difference structure associated with the value.
+ * @param pseudo_val Value for which to get a name.
+ * @param pol The policy to use, either POLDIFF_POLICY_ORIG or
+ * POLDIFF_POLICY_MOD.
+ *
+ * @return A valid name of a type from either policy that maps to the
+ * specified value. If the type does not exist then return NULL. Do
+ * not modify the string; it is a pointer into the policy's symbol
+ * table.
+ */
+ const char *type_map_get_name(const poldiff_t * diff, const uint32_t pseudo_val, int pol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_TYPE_MAP_INTERNAL_H */
diff --git a/libpoldiff/src/user_diff.c b/libpoldiff/src/user_diff.c
new file mode 100644
index 0000000..e673291
--- /dev/null
+++ b/libpoldiff/src/user_diff.c
@@ -0,0 +1,789 @@
+/**
+ * @file
+ * Implementation for computing semantic differences in users.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "poldiff_internal.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct poldiff_user_summary
+{
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs;
+};
+
+struct poldiff_user
+{
+ char *name;
+ poldiff_form_e form;
+ /* the next three are vector of strings */
+ apol_vector_t *unmodified_roles;
+ apol_vector_t *added_roles;
+ apol_vector_t *removed_roles;
+ /** if not diffing a MLS policy, this will be NULL */
+ poldiff_level_t *orig_default_level;
+ /** if not diffing a MLS policy, this will be NULL; this is
+ also NULL if orig_default_level->form is
+ POLDIFF_FORM_MODIFIED */
+ poldiff_level_t *mod_default_level;
+ /** if not diffing MLS policies then the range is NULL */
+ poldiff_range_t *range;
+};
+
+void poldiff_user_get_stats(const poldiff_t * diff, size_t stats[5])
+{
+ if (diff == NULL || stats == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return;
+ }
+ stats[0] = diff->user_diffs->num_added;
+ stats[1] = diff->user_diffs->num_removed;
+ stats[2] = diff->user_diffs->num_modified;
+ stats[3] = 0;
+ stats[4] = 0;
+}
+
+/**
+ * Generate the to_string for a modified user.
+ */
+static char *user_to_modified_string(const poldiff_t * diff, const poldiff_user_t * u)
+{
+ size_t len = 0, i;
+ char *s = NULL, *t = NULL, *role, *range = NULL;
+ size_t num_added_roles = apol_vector_get_size(u->added_roles);
+ size_t num_removed_roles = apol_vector_get_size(u->removed_roles);
+ if (apol_str_appendf(&s, &len, "* %s\n", u->name) < 0) {
+ goto err;
+ }
+ if (num_added_roles > 0 || num_removed_roles > 0) {
+ if (apol_str_append(&s, &len, " roles {") < 0) {
+ goto err;
+ }
+ for (i = 0; i < apol_vector_get_size(u->unmodified_roles); i++) {
+ role = (char *)apol_vector_get_element(u->unmodified_roles, i);
+ if (apol_str_appendf(&s, &len, " %s", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < num_added_roles; i++) {
+ role = (char *)apol_vector_get_element(u->added_roles, i);
+ if (apol_str_appendf(&s, &len, " +%s", role) < 0) {
+ goto err;
+ }
+ }
+ for (i = 0; i < num_removed_roles; i++) {
+ role = (char *)apol_vector_get_element(u->removed_roles, i);
+ if (apol_str_appendf(&s, &len, " -%s", role) < 0) {
+ goto err;
+ }
+ }
+ if (apol_str_append(&s, &len, " }\n") < 0) {
+ goto err;
+ }
+ }
+ if ((u->mod_default_level != NULL || u->orig_default_level != NULL) && apol_str_append(&s, &len, " level:\n") < 0) {
+ goto err;
+ }
+ if (u->mod_default_level != NULL) {
+ if ((t = poldiff_level_to_string_brief(diff, u->mod_default_level)) == NULL) {
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto err;
+ }
+ free(t);
+ t = NULL;
+ }
+ if (u->orig_default_level != NULL) {
+ if ((t = poldiff_level_to_string_brief(diff, u->orig_default_level)) == NULL) {
+ goto err;
+ }
+ if (apol_str_appendf(&s, &len, " %s", t) < 0) {
+ ERR(diff, "%s", strerror(errno));
+ goto err;
+ }
+ free(t);
+ t = NULL;
+ }
+ if (u->range != NULL) {
+ if ((range = poldiff_range_to_string_brief(diff, u->range)) == NULL
+ || (apol_str_appendf(&s, &len, "%s", range) < 0)) {
+ free(range);
+ goto err;
+ }
+ free(range);
+ }
+ return s;
+ err:
+ free(s);
+ free(t);
+ return NULL;
+}
+
+char *poldiff_user_to_string(const poldiff_t * diff, const void *user)
+{
+ poldiff_user_t *u = (poldiff_user_t *) user;
+ size_t len = 0;
+ char *s = NULL;
+ if (diff == NULL || user == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (u->form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ if (apol_str_appendf(&s, &len, "+ %s", u->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ if (apol_str_appendf(&s, &len, "- %s", u->name) < 0) {
+ break;
+ }
+ return s;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ if ((s = user_to_modified_string(diff, u)) == NULL) {
+ goto err;
+ }
+ return s;
+ }
+ default:
+ {
+ ERR(diff, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+ err:
+ /* if this is reached then an error occurred */
+ free(s);
+ ERR(diff, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return NULL;
+}
+
+const apol_vector_t *poldiff_get_user_vector(const poldiff_t * diff)
+{
+ if (diff == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return diff->user_diffs->diffs;
+}
+
+const char *poldiff_user_get_name(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->name;
+}
+
+poldiff_form_e poldiff_user_get_form(const void *user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return ((const poldiff_user_t *)user)->form;
+}
+
+const apol_vector_t *poldiff_user_get_unmodified_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->unmodified_roles;
+}
+
+const apol_vector_t *poldiff_user_get_added_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->added_roles;
+}
+
+const apol_vector_t *poldiff_user_get_removed_roles(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->removed_roles;
+}
+
+const poldiff_level_t *poldiff_user_get_original_dfltlevel(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->orig_default_level;
+}
+
+const poldiff_level_t *poldiff_user_get_modified_dfltlevel(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->mod_default_level;
+}
+
+const poldiff_range_t *poldiff_user_get_range(const poldiff_user_t * user)
+{
+ if (user == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return user->range;
+}
+
+/*************** protected functions for users ***************/
+
+static void user_free(void *elem)
+{
+ if (elem != NULL) {
+ poldiff_user_t *u = (poldiff_user_t *) elem;
+ free(u->name);
+ apol_vector_destroy(&u->added_roles);
+ apol_vector_destroy(&u->removed_roles);
+ apol_vector_destroy(&u->unmodified_roles);
+ level_free(u->orig_default_level);
+ level_free(u->mod_default_level);
+ range_destroy(&u->range);
+ free(u);
+ }
+}
+
+poldiff_user_summary_t *user_create(void)
+{
+ poldiff_user_summary_t *us = calloc(1, sizeof(*us));
+ if (us == NULL) {
+ return NULL;
+ }
+ if ((us->diffs = apol_vector_create(user_free)) == NULL) {
+ user_destroy(&us);
+ return NULL;
+ }
+ return us;
+}
+
+void user_destroy(poldiff_user_summary_t ** us)
+{
+ if (us != NULL && *us != NULL) {
+ apol_vector_destroy(&(*us)->diffs);
+ free(*us);
+ *us = NULL;
+ }
+}
+
+int user_reset(poldiff_t * diff)
+{
+ int error = 0;
+
+ if (diff == NULL) {
+ ERR(diff, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ user_destroy(&diff->user_diffs);
+ diff->user_diffs = user_create();
+ if (diff->user_diffs == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Comparison function for two users from the same policy.
+ */
+static int user_name_comp(const void *x, const void *y, void *arg)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ apol_policy_t *p = (apol_policy_t *) arg;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ const char *name1, *name2;
+ if (qpol_user_get_name(q, u1, &name1) < 0 || qpol_user_get_name(q, u2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+ if (qpol_policy_get_user_iter(q, &iter) < 0) {
+ return NULL;
+ }
+ v = apol_vector_create_from_iter(iter, NULL);
+ if (v == NULL) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ apol_vector_sort(v, user_name_comp, (void *)policy);
+ return v;
+}
+
+int user_comp(const void *x, const void *y, const poldiff_t * diff)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ const char *name1, *name2;
+ if (qpol_user_get_name(diff->orig_qpol, u1, &name1) < 0 || qpol_user_get_name(diff->mod_qpol, u2, &name2) < 0) {
+ return 0;
+ }
+ return strcmp(name1, name2);
+}
+
+/**
+ * Allocate and return a new user difference object.
+ *
+ * @param diff Policy diff error handler.
+ * @param form Form of the difference.
+ * @param name Name of the user that is different.
+ *
+ * @return A newly allocated and initialized diff, or NULL upon error.
+ * The caller is responsible for calling user_free() upon the returned
+ * value.
+ */
+static poldiff_user_t *make_diff(const poldiff_t * diff, poldiff_form_e form, const char *name)
+{
+ poldiff_user_t *pu;
+ int error;
+ if ((pu = calloc(1, sizeof(*pu))) == NULL ||
+ (pu->name = strdup(name)) == NULL ||
+ (pu->added_roles = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pu->removed_roles = apol_vector_create_with_capacity(1, free)) == NULL ||
+ (pu->unmodified_roles = apol_vector_create_with_capacity(1, free)) == NULL) {
+ error = errno;
+ user_free(pu);
+ ERR(diff, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ pu->form = form;
+ return pu;
+}
+
+/**
+ * Given a user, return a vector of its allowed roles (in the form of
+ * strings).
+ *
+ * @param diff Policy diff error handler.
+ * @param p Policy from which the user came.
+ * @param user User whose roles to get.
+ *
+ * @return Vector of role strings for the user. The caller is
+ * responsible for calling apol_vector_destroy(). On error, return
+ * NULL.
+ */
+static apol_vector_t *user_get_roles(const poldiff_t * diff, const apol_policy_t * p, const qpol_user_t * user)
+{
+ qpol_iterator_t *iter = NULL;
+ const qpol_role_t *role;
+ const char *role_name;
+ char *role_name2;
+ apol_vector_t *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ int retval = -1, error = 0;
+
+ if ((v = apol_vector_create(free)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (qpol_user_get_role_iter(q, user, &iter) < 0) {
+ goto cleanup;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&role) < 0 || qpol_role_get_name(q, role, &role_name)) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((role_name2 = strdup(role_name)) == NULL || apol_vector_append(v, (void *)role_name2) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ free(role_name2);
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ if (retval < 0) {
+ apol_vector_destroy(&v);
+ errno = error;
+ return NULL;
+ }
+ return v;
+}
+
+/**
+ * Perform a deep diff of the default MLS levels assigned to the two
+ * users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine, or NULL if the user
+ * was added.
+ * @param u2 User from modified policy to examine, or NULL if the user
+ * was removed.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_default_levels(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ const qpol_mls_level_t *ql1 = NULL, *ql2 = NULL;
+ poldiff_level_t *pl = NULL;
+ apol_mls_level_t *l1 = NULL, *l2 = NULL;
+ int retval = -1;
+ if (u1 != NULL && qpol_user_get_dfltlevel(diff->orig_qpol, u1, &ql1) < 0) {
+ return -1;
+ }
+ if (u2 != NULL && qpol_user_get_dfltlevel(diff->mod_qpol, u2, &ql2) < 0) {
+ return -1;
+ }
+ if (ql1 == NULL && ql2 == NULL) {
+ /* neither policy is MLS */
+ return 0;
+ }
+ if (ql1 == NULL) {
+ if ((l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL ||
+ (pl = level_create_from_apol_mls_level(l2, POLDIFF_FORM_ADDED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->mod_default_level = pl;
+ retval = 1;
+ } else if (ql2 == NULL) {
+ if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL ||
+ (pl = level_create_from_apol_mls_level(l1, POLDIFF_FORM_REMOVED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->orig_default_level = pl;
+ retval = 1;
+ } else {
+ if ((l1 = apol_mls_level_create_from_qpol_mls_level(diff->orig_pol, ql1)) == NULL ||
+ (l2 = apol_mls_level_create_from_qpol_mls_level(diff->mod_pol, ql2)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (level_deep_diff_apol_mls_levels(diff, l1, l2, &u->orig_default_level, &u->mod_default_level) < 0) {
+ goto cleanup;
+ }
+ if (u->orig_default_level != NULL) {
+ retval = 1;
+ }
+ }
+ if (retval == -1) {
+ /* if reach this point, then no differences were found */
+ retval = 0;
+ }
+ cleanup:
+ apol_mls_level_destroy(&l1);
+ apol_mls_level_destroy(&l2);
+ if (retval < 0) {
+ level_free(pl);
+ }
+ return retval;
+}
+
+/**
+ * Perform a deep diff of the MLS ranges assigned to the two users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine, or NULL if the user
+ * was added.
+ * @param u2 User from modified policy to examine, or NULL if the user
+ * was removed.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_ranges(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ const qpol_mls_range_t *r1 = NULL, *r2 = NULL;
+ poldiff_range_t *pr = NULL;
+ int retval = -1;
+ if (u1 != NULL && qpol_user_get_range(diff->orig_qpol, u1, &r1) < 0) {
+ return -1;
+ }
+ if (u2 != NULL && qpol_user_get_range(diff->mod_qpol, u2, &r2) < 0) {
+ return -1;
+ }
+ if (r1 == NULL && r2 == NULL) {
+ /* neither policy is MLS */
+ return 0;
+ }
+ if (r1 == NULL) {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_ADDED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->range = pr;
+ pr = NULL;
+ retval = 1;
+ } else if (r2 == NULL) {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_REMOVED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ u->range = pr;
+ pr = NULL;
+ retval = 1;
+ } else {
+ if ((pr = range_create(diff, r1, r2, POLDIFF_FORM_MODIFIED)) == NULL) {
+ ERR(diff, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if ((retval = range_deep_diff(diff, pr)) < 0) {
+ goto cleanup;
+ }
+ if (retval > 0) {
+ u->range = pr;
+ pr = NULL;
+ }
+ }
+ cleanup:
+ range_destroy(&pr);
+ return retval;
+}
+
+int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item)
+{
+ const qpol_user_t *u = item;
+ const char *name = NULL;
+ apol_vector_t *v = NULL;
+ poldiff_user_t *pu = NULL;
+ int error = 0, retval = -1;
+ if ((form == POLDIFF_FORM_ADDED &&
+ qpol_user_get_name(diff->mod_qpol, u, &name) < 0) ||
+ ((form == POLDIFF_FORM_REMOVED || form == POLDIFF_FORM_MODIFIED) && qpol_user_get_name(diff->orig_qpol, u, &name) < 0))
+ {
+ error = errno;
+ goto cleanup;
+ }
+ if ((pu = make_diff(diff, form, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ apol_vector_destroy(&pu->added_roles);
+ if ((v = user_get_roles(diff, diff->mod_pol, u)) == NULL ||
+ (pu->added_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL ||
+ user_deep_diff_default_levels(diff, NULL, u, pu) < 0 || user_deep_diff_ranges(diff, NULL, u, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ } else {
+ apol_vector_destroy(&pu->removed_roles);
+ if ((v = user_get_roles(diff, diff->orig_pol, u)) == NULL ||
+ (pu->removed_roles = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL ||
+ user_deep_diff_default_levels(diff, u, NULL, pu) < 0 || user_deep_diff_ranges(diff, u, NULL, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_append(diff->user_diffs->diffs, pu) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (form == POLDIFF_FORM_ADDED) {
+ diff->user_diffs->num_added++;
+ } else {
+ diff->user_diffs->num_removed++;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ if (retval < 0) {
+ user_free(pu);
+ errno = error;
+ }
+ return retval;
+}
+
+/**
+ * Perform a deep diff of the roles assigned to the two users.
+ *
+ * @param diff Diff structure containing the original and modified
+ * policies.
+ * @param u1 User from original policy to examine.
+ * @param u2 User from modified policy to examine.
+ * @param u Result structure where differences are to be recorded.
+ *
+ * @return Greater than zero if a diff was found, zero if none found,
+ * less than zero for errors.
+ */
+static int user_deep_diff_roles(poldiff_t * diff, const qpol_user_t * u1, const qpol_user_t * u2, poldiff_user_t * u)
+{
+ apol_vector_t *v1 = NULL, *v2 = NULL;
+ char *role1, *role2;
+ size_t i, j;
+ int retval = -1, error = 0, compval;
+
+ if ((v1 = user_get_roles(diff, diff->orig_pol, u1)) == NULL || (v2 = user_get_roles(diff, diff->mod_pol, u2)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v1, apol_str_strcmp, NULL);
+ apol_vector_sort(v2, apol_str_strcmp, NULL);
+ for (i = j = 0; i < apol_vector_get_size(v1);) {
+ if (j >= apol_vector_get_size(v2))
+ break;
+ role1 = (char *)apol_vector_get_element(v1, i);
+ role2 = (char *)apol_vector_get_element(v2, j);
+ compval = strcmp(role1, role2);
+ if (compval < 0) {
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ } else if (compval > 0) {
+ if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ j++;
+ } else {
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->unmodified_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ i++;
+ j++;
+ }
+ }
+ for (; i < apol_vector_get_size(v1); i++) {
+ role1 = (char *)apol_vector_get_element(v1, i);
+ if ((role1 = strdup(role1)) == NULL || apol_vector_append(u->removed_roles, role1) < 0) {
+ error = errno;
+ free(role1);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ for (; j < apol_vector_get_size(v2); j++) {
+ role2 = (char *)apol_vector_get_element(v2, j);
+ if ((role2 = strdup(role2)) == NULL || apol_vector_append(u->added_roles, role2) < 0) {
+ error = errno;
+ free(role2);
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if (apol_vector_get_size(u->removed_roles) > 0 || apol_vector_get_size(u->added_roles) > 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ apol_vector_destroy(&v1);
+ apol_vector_destroy(&v2);
+ errno = error;
+ return retval;
+}
+
+int user_deep_diff(poldiff_t * diff, const void *x, const void *y)
+{
+ const qpol_user_t *u1 = x;
+ const qpol_user_t *u2 = y;
+ const char *name;
+ poldiff_user_t *u = NULL;
+ int retval = -1, r1 = 0, r2 = 0, r3 = 0, error = 0;
+ if (qpol_user_get_name(diff->orig_qpol, u1, &name) < 0 || (u = make_diff(diff, POLDIFF_FORM_MODIFIED, name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((r1 = user_deep_diff_roles(diff, u1, u2, u)) < 0 || (r2 = user_deep_diff_default_levels(diff, u1, u2, u)) < 0 ||
+ (r3 = user_deep_diff_ranges(diff, u1, u2, u)) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (r1 > 0 || r2 > 0 || r3 > 0) {
+ if (apol_vector_append(diff->user_diffs->diffs, u) < 0) {
+ error = errno;
+ ERR(diff, "%s", strerror(error));
+ goto cleanup;
+ }
+ diff->user_diffs->num_modified++;
+ } else {
+ /* no differences found */
+ user_free(u);
+ }
+ retval = 0;
+ cleanup:
+ if (retval != 0) {
+ user_free(u);
+ }
+ errno = error;
+ return retval;
+}
diff --git a/libpoldiff/src/user_internal.h b/libpoldiff/src/user_internal.h
new file mode 100644
index 0000000..f94d46b
--- /dev/null
+++ b/libpoldiff/src/user_internal.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Protected interface for user differences.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLDIFF_USER_INTERNAL_H
+#define POLDIFF_USER_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct poldiff_user_summary poldiff_user_summary_t;
+
+/**
+ * Allocate and return a new poldiff_user_summary_t object.
+ *
+ * @return A new user summary. The caller must call user_destroy()
+ * afterwards. On error, return NULL and set errno.
+ */
+ poldiff_user_summary_t *user_create(void);
+
+/**
+ * Deallocate all space associated with a poldiff_user_summary_t
+ * object, including the pointer itself. If the pointer is already
+ * NULL then do nothing.
+ *
+ * @param us Reference to a user summary to destroy. The pointer
+ * will be set to NULL afterwards.
+ */
+ void user_destroy(poldiff_user_summary_t ** us);
+
+/**
+ * Reset the state of all user differences.
+ * @param diff The policy difference structure containing the differences
+ * to reset.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set and the user should call poldiff_destroy() on diff.
+ */
+ int user_reset(poldiff_t * diff);
+
+/**
+ * Get a vector of all users from the given policy, sorted by name.
+ *
+ * @param diff Policy diff error handler.
+ * @param policy The policy from which to get the items.
+ *
+ * @return a newly allocated vector of all users. The caller is
+ * responsible for calling apol_vector_destroy() afterwards. On
+ * error, return NULL and set errno.
+ */
+ apol_vector_t *user_get_items(poldiff_t * diff, const apol_policy_t * policy);
+
+/**
+ * Compare two qpol_user_t objects, determining if they have the same
+ * name or not.
+ *
+ * @param x The user from the original policy.
+ * @param y The user from the modified policy.
+ * @param diff The policy difference structure associated with both
+ * policies.
+ *
+ * @return < 0, 0, or > 0 if user x is respectively less than, equal
+ * to, or greater than user y.
+ */
+ int user_comp(const void *x, const void *y, const poldiff_t * diff);
+
+/**
+ * Create, initialize, and insert a new semantic difference entry for
+ * a user.
+ *
+ * @param diff The policy difference structure to which to add the entry.
+ * @param form The form of the difference.
+ * @param item Item for which the entry is being created.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int user_new_diff(poldiff_t * diff, poldiff_form_e form, const void *item);
+
+/**
+ * Compute the semantic difference of two users for which the compare
+ * callback returns 0. If a difference is found then allocate,
+ * initialize, and insert a new semantic difference entry for that
+ * user.
+ *
+ * @param diff The policy difference structure associated with both
+ * users and to which to add an entry if needed.
+ * @param x The user from the original policy.
+ * @param y The user from the modified policy.
+ *
+ * @return 0 on success and < 0 on error; if the call fails, set errno
+ * and leave the policy difference structure unchanged.
+ */
+ int user_deep_diff(poldiff_t * diff, const void *x, const void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLDIFF_USER_INTERNAL_H */
diff --git a/libpoldiff/src/util.c b/libpoldiff/src/util.c
new file mode 100644
index 0000000..059dadf
--- /dev/null
+++ b/libpoldiff/src/util.c
@@ -0,0 +1,32 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions for libpoldiff.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+#include <poldiff/util.h>
+
+const char *libpoldiff_get_version(void)
+{
+ return LIBPOLDIFF_VERSION_STRING;
+}
diff --git a/libpoldiff/src/writing-diffs-HOWTO b/libpoldiff/src/writing-diffs-HOWTO
new file mode 100644
index 0000000..4980492
--- /dev/null
+++ b/libpoldiff/src/writing-diffs-HOWTO
@@ -0,0 +1,351 @@
+Writing New Diff Modules for libpoldiff HOWTO
+August 1, 2007
+
+
+0. Introduction
+
+libpoldiff is a library to be used in conjunction with libapol to find
+"semantic" differences between policies. For the purposes of this
+HOWTO, the term "semantic" refers to how the SELinux kernel would
+enforce accesses. If two policies could ever be enforced differently
+then they are defined to be semantically different.
+
+libpoldiff operates by breaking a policy into various 'policy items'.
+Examples of items are users, object classes, and type enforcement
+rules. These items correspond to flags passed into the sediff
+program. So as to be extensible, libpoldiff was designed to allow one
+to add new diff modules (and hence additional flags to sediff). These
+modules should operate independent of each other and without regard to
+ordering of modules.
+
+
+1. Library Overview
+
+libpoldiff implements what we term "the generic diffing algorithm",
+akin to a merge sort. The algorithm takes two ordered lists of items
+and successively picks the first item from the lists. If the two
+match, according to the items' comparison function, then they are
+deemed the same. Otherwise the same ordering used to generate the
+lists may also be used to determine if one was added or removed from
+the policy.
+
+As an example, consider a diff of users for two hypothetical SELinux
+policies, "orig.policy" and "updated.policy". Within orig.policy are
+users adam_u, charlie_u, and dave_u. updated.policy has the users adam_u,
+bob_u, and charlie_u. The first step of the generic diffing algorithm
+is to get the ordered list of items. Let the ordering algorithm be
+alphabetical order; thus the lists would be:
+
+ orig.policy -> {adam_u, charlie_u, dave_u}
+ updated.policy -> {adam_u, bob_u, charlie_u}
+
+The algorithm picks the first item from each list and compares them.
+As that "adam_u" is the same as "adam_u", the algorithm accepts them
+as the same and continues. The next two items are "charlie_u" and
+"bob_u". The ordering functions finds "bob_u" to be "earlier" than
+"charlie_u" (because it appears earlier alphabetically), "bob_u" is
+marked as item added to updated.policy. The algorithm keeps
+"charlie_u" from the first policy but advances the pointer for the
+second list. These "charlie_u" are the same. The remaining type,
+"dave_u", has no complement in the second list and is thus marked as
+removed.
+
+Of course, finding differences for users is not as simple as comparing
+their names. In addition one must also examine the roles assigned to
+them as well. Comparing names was a "shallow diff"; checking roles is
+a "deep diff". The deep diff must look at all aspects of the two
+items to determine if they are the same, modified,
+modified-by-adding-a-type, or modified-by-removing-a-type. (At this
+time the users' allowed MLS ranges and default range would also be
+checked.)
+
+To complicate things, consider the aspect of type remapping. Type
+names may be changed between the policies; they could also be joined
+into a new type and conversely split. Thus one must be careful how
+the ordered lists of types are generated. The functions
+type_map_lookup() and type_map_lookup_reverse() will prove essential.
+
+
+2. Reporting Differences
+
+If a difference was found, either via a shallow diff or a deep diff,
+then an "item diff" struct must be created. If it the difference was
+'added' or 'removed' then libpoldiff's poldiff_do_item_diff() will
+call the item diff creation function. If instead the difference was
+found within the deep diff comparison function then that function is
+responsible for creating the item diff struct. The item diff struct
+is used by each item's to_string() function to create a human-readable
+report.
+
+
+3. Complete Walkthrough
+
+The following walkthrough describes the process for writing a diff for
+items between the original and modified policies. The shallow
+diff is to see if a type in the original policy exists in the modified
+policy, with respect to the type map. The deep diff determines if the
+types have the same set of attributes.
+
+
+3a. Public Header
+
+Create a new file, libpoldiff/include/poldiff/type_diff.h, to declare
+publicly accessible functions. At least three functions must exist
+here:
+
+ extern void poldiff_type_get_stats(poldiff_t *diff, size_t stats[5]);
+ extern apol_vector_t *poldiff_get_type_vector(poldiff_t *diff);
+ extern poldiff_form_e *poldiff_type_get_form(const void *type);
+ extern char *poldiff_type_to_string(poldiff_t *diff, const void *type);
+
+(The reason for using a void * in poldiff_type_to_string() will become
+apparant in section 3d.)
+
+Also in this file, declare an opaque object to hold a single type
+difference:
+
+ typedef struct poldiff_type poldiff_type_t;
+
+Once a user gets a vector of poldiff_type_t objects, via
+poldiff_get_type_vector(), he may want to do a number of things with
+it. He could print it via poldiff_type_to_string() or get its form;
+or just get the type's name, list of added attributes, or list of
+removed attributes. Therefore add these three functions to
+type_diff.h:
+
+ extern const char *poldiff_type_get_name(poldiff_type_t *type);
+ extern apol_vector_t *poldiff_type_get_added_attribs(poldiff_type_t *type);
+ extern apol_vector_t *poldiff_type_get_removed_attribs(poldiff_type_t *type);
+
+As a convenience to developers, one should only need to #include the
+public poldiff.h to pick up all diff modules. Modify
+libpoldiff/include/poldiff/poldiff.h in the vicinity of line 74:
+
+ #include <poldiff/type_diff.h>
+
+Finally, modify libpoldiff/include/poldiff/Makefile.am by adding an
+entry for type_diff.h.
+
+3b. Protected Header
+
+There will be functions accessible only between library files (i.e.,
+protected functions). To distinguish public functions from those that
+are protected, do not prefix these with 'poldiff_'. Create a new
+file, libpoldiff/src/type_internal.h, that declares protected
+routines. libpoldiff requires these four functions to exist:
+
+ apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy);
+ int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item);
+ int type_comp(const void *x, const void *y, poldiff_t *diff);
+ int type_deep_diff(poldiff_t *diff, const void *x, const void *y);
+
+Associated with the computed list of poldiff_type_t objects is a
+summary structure. Check that libpoldiff/src/poldiff_internal.h has
+declare a 'struct poldiff_type_summary', then add the following line
+to the protected header:
+
+ typedef struct poldiff_type_summary poldiff_type_summary_t;
+
+As with all other poldiff objects, you will need a constructor and a
+destructor:
+
+ poldiff_type_summary_t *type_create(void);
+ void type_destroy(poldiff_type_summary_t **ts);
+
+As a convenience to developers, one should only need to #include the
+protected poldiff_internal.h to pick up all diff modules. Modify
+libpoldiff/src/poldiff_internal.h in the vicinity of line 50:
+
+ #include "type_internal.h"
+
+Finally, modify libpoldiff/src/Makefile.am by adding an entry for
+type_internal.h.
+
+3c. Implementing Functions
+
+Create a new file, libpoldiff/src/type_diff.c, to implement all
+public, protected, and any necessary private functions. First declare
+the contents of the structures:
+
+ struct poldiff_type_summary {
+ size_t num_added;
+ size_t num_removed;
+ size_t num_modified;
+ apol_vector_t *diffs; /* vector of poldiff_type_t */
+ };
+ struct poldiff_type {
+ char *name;
+ poldiff_form_e form;
+ apol_vector_t *added_attribs; /* vector of char* */
+ apol_vector_t *removed_attribs; /* vector of char* */
+ };
+
+The public functions are easy to write.
+
+ * poldiff_type_get_stats() fetches the fields
+ diff->type_diffs->num_added, diff->type_diffs->num_removed, and
+ diff->type_diffs->num_modified.
+
+ * poldiff_type_get_form() fetches an individual result's form. Note
+ that you must first cast the second parameter from void* to a
+ poldiff_type_t*, because this function operates upon items
+ returned by poldiff_get_type_vector().
+
+ * poldiff_type_to_string() returns an allocated string akin to
+ poldiff_user_to_string(). Note that you must first cast the
+ second parameter from void* to a poldiff_type_t*, because this
+ function operates upon items returned by
+ poldiff_get_type_vector().
+
+ * poldiff_get_type_vector() returns diff->type_diffs->diffs.
+
+The rest of the public functions are accessors into a poldiff_type_t
+object.
+
+The protected functions are more difficult.
+
+ * type_create() and type_destroy() affect a poldiff_type_summary_t
+ object.
+
+ * The other protected functions are described in section 3e.
+
+Finally, modify libpoldiff/src/Makefile.am by adding an entry for
+type_diff.c.
+
+3d. Adding Hooks to Diff Module
+
+The main library now needs to create and destroy the
+poldiff_type_summary_t object and to actually diff types. Make these
+changes to libpoldiff/src/poldiff.c:
+
+ * Create a poldiff_type_summary_t object by calling type_create()
+ within poldiff_create().
+
+ * Destroy the summary by calling type_destroy() within
+ poldiff_destroy().
+
+ * To enforce that all diff modules have the requisite public and
+ protected functions, one must fulfill the requirements as given by
+ a poldiff_item_record, as defined on line 41. The first four
+ callbacks are satisfied by the first four public functions, the
+ remainder are met by protected functions. Thus add a new record
+ to the item_records[] array like so:
+
+ /* ... */
+ {
+ "type",
+ POLDIFF_DIFF_TYPES,
+ poldiff_type_get_stats,
+ poldiff_get_type_vector,
+ poldiff_type_get_form,
+ poldiff_type_to_string,
+ type_reset,
+ type_get_items,
+ type_comp,
+ type_new_diff,
+ type_deep_diff
+ },
+ /* ... */
+
+Finally, for the public functions to be accessible through
+libpoldiff.so, add this line to libpoldiff/src/libpoldiff.map under
+the 'global' category:
+
+ poldiff_type_*;
+
+3e. General Idea for Diffing Types
+
+Rather than comparing qpol_type_t pointers from one policy to another,
+it is more convenient to convert those pointers to "pseudo-type
+values", which are represented as uint32_ts. These new values handle
+the type mappings between policies. Whenever a difference is found,
+convert those pseudo-type values back to the component qpol_type_t
+pointers. With type splits and type joins, a single pseudo-type value
+may map to multiple qpol_type_t pointers.
+
+First look at type_get_items(). Its job is to return a sorted list of
+unique items. Write it like this:
+
+ apol_vector_t *type_get_items(poldiff_t *diff, apol_policy_t *policy)
+ {
+ get an iterator of types from the policy
+ allocate a new vector v
+ for each item in the iterator,
+ convert qpol_type_t* to uint32_t via type_map_lookup()
+ append that uint32_t to v
+ sort and unquify v
+ return v
+ }
+
+type_map_lookup() needs a third parameter that says from which policy
+the type originated. Use these lines to calculate the parameter:
+
+ if (policy == diff->orig_pol)
+ which_pol = POLDIFF_POLICY_ORIG;
+ else
+ which_pol = POLDIFF_POLICY_MOD;
+
+Now that you have a vector of pseudo-type values, all further
+functions will need to be in terms of these values. libpoldiff will
+pass elements from the type_get_items() vector into your protected
+functions. In this walkthrough you will thus need to cast all values
+from void* to uint32_t because type_get_items() returned a vector of
+uint32_ts. For example:
+
+ int type_comp(const void *x, const void *y,
+ poldiff_t *diff __attribute__((unused)))
+ {
+ uint32_t t1 = (uint32_t) x;
+ uint32_t t2 = (uint32_t) y;
+ return t1 - t2;
+ }
+
+ int type_deep_diff(poldiff_t *diff, const void *x, const void *y)
+ {
+ uint32_t t1 = (uint32_t) x;
+ uint32_t t2 = (uint32_t) y;
+ apol_vector_t *v1 = type_map_lookup_reverse(diff, t1,
+ POLDIFF_POLICY_ORIG);
+ apol_vector_t *v2 = type_map_lookup_reverse(diff, t2,
+ POLDIFF_POLICY_ORIG);
+ apol_vector_t *added_attribs, *removed_attribs;
+ let vector a1 = union of all attributes for all types in vector v1
+ let vector a2 = union of all attributes for all types in vector v2
+ sort and uniquify a1
+ sort and uniquify a2
+ for all attributes in a1 not in a2,
+ append to removed_attribs those attributes
+ for all attributes in a2 not in a1,
+ append to added_attribs those attributes
+ if removed_attribs is not empty or added_attribs is not empty,
+ then foreach type in v1,
+ create a new poldiff_type_t
+ set the poldiff_type_t's name to the type's name
+ set the form to POLDIFF_FORM_MODIFIED
+ clone added_attribs
+ clone removed_attribs
+ append the poldiff_type_t to diff->type_diffs->diffs
+ if no poldiff_type_t was created
+ return 0
+ else
+ return non-zero
+ }
+
+ int type_new_diff(poldiff_t *diff, poldiff_form_e form, const void *item)
+ {
+ uint32_t t = (uint32_t) item;
+ apol_vector_t *v;
+ if (form == POLDIFF_FORM_REMOVED)
+ v = type_map_lookup(diff, t, POLDIFF_POLICY_ORIG);
+ else
+ v = type_map_lookup(diff, t, POLDIFF_POLICY_MOD);
+ foreach type in v,
+ create a new poldiff_type_t
+ set the poldiff_type_t's name to the type's name
+ set the form to form
+ append the poldiff_type_t to diff->type_diffs->diffs
+ }
+
+One implementation of creating temporary vectors similar to
+added_attribs and removed_attribs may be found at
+libpoldiff/src/role_diff.c:role_deep_diff().
diff --git a/libpoldiff/swig/Makefile.am b/libpoldiff/swig/Makefile.am
new file mode 100644
index 0000000..3412858
--- /dev/null
+++ b/libpoldiff/swig/Makefile.am
@@ -0,0 +1,15 @@
+if DO_SWIGIFY_PYTHON
+ MAYBE_PYSWIG = python
+endif
+
+if DO_SWIGIFY_JAVA
+ MAYBE_JSWIG = java
+endif
+
+if DO_SWIGIFY_TCL
+ MAYBE_TCLSWIG = tcl
+endif
+
+SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG)
+
+dist_noinst_DATA = poldiff.i
diff --git a/libpoldiff/swig/java/MANIFEST.MF.in b/libpoldiff/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..dd680b0
--- /dev/null
+++ b/libpoldiff/swig/java/MANIFEST.MF.in
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+
+Name: com/tresys/setools/
+Specification-Title: "SETools Java Libraries"
+Specification-Version: "@VERSION@"
+Specification-Vendor: "Tresys Technology"
+Implementation-Title: "com.tresys.setools.poldiff"
+Implementation-Version: "@libpoldiff_version@"
+Implementation-Vendor: "Tresys Technology"
+Extension-List: qpol apol
+qpol-Extension-Name: com.tresys.setools.qpol
+qpol-Implementation-Version: @libqpol_version@
+apol-Extension-Name: com.tresys.setools.apol
+apol-Implementation-Version: @libapol_version@
diff --git a/libpoldiff/swig/java/Makefile.am b/libpoldiff/swig/java/Makefile.am
new file mode 100644
index 0000000..6be72d3
--- /dev/null
+++ b/libpoldiff/swig/java/Makefile.am
@@ -0,0 +1,93 @@
+wrappedso_DATA = libjpoldiff.so.@libpoldiff_version@
+wrappedso_SONAME = @libpoldiff_jswig_soname@
+short_name = libjpoldiff.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.poldiff
+package_dir = $(dir $(subst .,/,$(package_name)))poldiff
+
+wrappedjar_DATA = poldiff.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../poldiff.i
+BUILT_SOURCES = poldiff_wrap.c \
+ poldiff_attrib_t.java \
+ poldiff_avrule_t.java \
+ poldiff_bool_t.java \
+ poldiff_cat_t.java \
+ poldiff_class_t.java \
+ poldiff_common_t.java \
+ poldiffConstants.java \
+ poldiff_form_e.java \
+ poldiff.java \
+ poldiffJNI.java \
+ poldiff_level_t.java \
+ poldiff_range_t.java \
+ poldiff_range_trans_t.java \
+ poldiff_role_allow_t.java \
+ poldiff_role_t.java \
+ poldiff_role_trans_t.java \
+ poldiff_stats_t.java \
+ poldiff_terule_t.java \
+ poldiff_t.java \
+ poldiff_type_remap_entry_t.java \
+ poldiff_type_t.java \
+ poldiff_user_t.java \
+ SWIGTYPE_p_void.java
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libpoldiff/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \
+ -classpath $(top_builddir)/libqpol/swig/java/qpol.jar:$(top_builddir)/libapol/swig/java/apol.jar
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @POLDIFF_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libpoldiff/src/libpoldiff.so
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_JAVA_OPT) -package $(package_name) -o $@ \
+ -I$(top_srcdir)/libpoldiff/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES))
+
+$(wrappedso_DATA): $(filter %.c, $(BUILT_SOURCES))
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_JAVA_CFLAGS) -DSWIGJAVA=1 $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+# Intentionally do not include SWIGTYPE_p_void.java below so that the
+# Java compiler uses the one created in package
+# com.tresys.setools.qpol instead of the one from package
+# com.tresys.setools.poldiff.
+java_files = $(filter-out SWIGTYPE_p_void.java, $(filter %.java, $(BUILT_SOURCES)))
+
+classes = $(patsubst %.java, $(package_dir)/%.class, $(java_files))
+
+# Because the Java compiler can generate multiple class files from the
+# same source .java file, putting all of the classes below will result
+# in repeated invocations of javac. Therefore, an alternative is to
+# just depend upon the first class file, and let the Java compiler
+# create the rest of them.
+$(firstword $(classes)): $(java_files)
+ $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^
+
+$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes))
+
+$(wrappedjar_DATA): MANIFEST.MF
+
+$(wrappedjar_DATA): $(classes)
+ $(JAR) cfm $@ MANIFEST.MF $^
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name)
+ $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name)
+ -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA)
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name)
diff --git a/libpoldiff/swig/poldiff.i b/libpoldiff/swig/poldiff.i
new file mode 100644
index 0000000..29767a8
--- /dev/null
+++ b/libpoldiff/swig/poldiff.i
@@ -0,0 +1,1335 @@
+/**
+ * @file
+ * SWIG declarations for libpoldiff.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%module poldiff
+
+%{
+#include <poldiff/attrib_diff.h>
+#include <poldiff/avrule_diff.h>
+#include <poldiff/bool_diff.h>
+#include <poldiff/cat_diff.h>
+#include <poldiff/class_diff.h>
+#include <poldiff/level_diff.h>
+#include <poldiff/poldiff.h>
+#include <poldiff/range_diff.h>
+#include <poldiff/range_trans_diff.h>
+#include <poldiff/rbac_diff.h>
+#include <poldiff/role_diff.h>
+#include <poldiff/terule_diff.h>
+#include <poldiff/type_diff.h>
+#include <poldiff/type_map.h>
+#include <poldiff/user_diff.h>
+#include <poldiff/util.h>
+
+/* Provide hooks so that language-specific modules can define the
+ * callback function, used by the handler in poldiff_create().
+ */
+SWIGEXPORT poldiff_handle_fn_t poldiff_swig_message_callback = NULL;
+SWIGEXPORT void * poldiff_swig_message_callback_arg = NULL;
+
+%}
+
+#ifdef SWIGJAVA
+%javaconst(1);
+/* get the java environment so we can throw exceptions */
+%{
+ static JNIEnv *poldiff_global_jenv;
+ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ (*vm)->AttachCurrentThread(vm, (void **)&poldiff_global_jenv, NULL);
+ return JNI_VERSION_1_2;
+ }
+%}
+#endif
+
+%include exception.i
+%include stdint.i
+%import apol.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+#ifdef SWIGJAVA
+
+%exception {
+ poldiff_global_jenv = jenv;
+ $action
+}
+
+%{
+#define BEGIN_EXCEPTION JNIEnv *local_jenv = poldiff_global_jenv; {
+#define END_EXCEPTION }
+%}
+
+/* handle size_t correctly in java as architecture independent */
+%typemap(jni) size_t "jlong"
+%typemap(jtype) size_t "long"
+%typemap(jstype) size_t "long"
+%typemap("javaimports") SWIGTYPE %{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%typemap(javabody) SWIGTYPE %{
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ public $javaclassname(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ public static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+%}
+/* the following handles the dependencies on qpol and apol */
+%pragma(java) jniclassimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libpoldiff_get_version ();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("jpoldiff");
+ }
+ }
+%}
+%pragma(java) moduleimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+#else
+/* not in java so handle size_t as architecture dependent */
+#ifdef SWIGWORDSIZE64
+typedef uint64_t size_t;
+#else
+typedef uint32_t size_t;
+#endif
+%{
+#define BEGIN_EXCEPTION
+#define END_EXCEPTION
+%}
+#endif
+
+#ifdef SWIGJAVA
+/* if java, pass the new exception macro to C not just SWIG */
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+#define SWIG_exception_typemap(code, msg) {SWIG_JavaException(jenv, code, msg);}
+%inline %{
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%}
+#endif
+
+#ifdef SWIGTCL
+/* implement a custom non thread-safe error handler */
+%{
+static char *message = NULL;
+static void tcl_clear_error(void)
+{
+ free(message);
+ message = NULL;
+}
+static void tcl_throw_error(const char *s)
+{
+ free(message);
+ message = strdup(s);
+}
+static char *tcl_get_error(void)
+{
+ return message;
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+%}
+
+%wrapper %{
+/* Tcl module's initialization routine is expected to be named
+ * Poldiff_Init(), but the output file will be called libtpoldiff.so instead
+ * of libpoldiff.so. Therefore add an alias from Tpoldiff_Init() to the
+ * real Poldiff_Init().
+ */
+SWIGEXPORT int Tpoldiff_Init(Tcl_Interp *interp) {
+ return SWIG_init(interp);
+}
+%}
+
+%exception {
+ char *err;
+ tcl_clear_error();
+ $action
+ if ((err = tcl_get_error()) != NULL) {
+ Tcl_Obj *obj = Tcl_NewStringObj(message, -1);
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, obj);
+ goto fail;
+ }
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+#endif
+
+%inline %{
+ typedef struct apol_string_vector apol_string_vector_t;
+%}
+
+const char *libpoldiff_get_version (void);
+
+/* diff element flags */
+#define POLDIFF_DIFF_CLASSES 0x00000001
+#define POLDIFF_DIFF_COMMONS 0x00000002
+#define POLDIFF_DIFF_TYPES 0x00000004
+#define POLDIFF_DIFF_ATTRIBS 0x00000008
+#define POLDIFF_DIFF_ROLES 0x00000010
+#define POLDIFF_DIFF_USERS 0x00000020
+#define POLDIFF_DIFF_BOOLS 0x00000040
+#define POLDIFF_DIFF_LEVELS 0x00000080
+#define POLDIFF_DIFF_CATS 0x00000100
+#define POLDIFF_DIFF_AVRULES 0x00000200
+#define POLDIFF_DIFF_TERULES 0x00000400
+#define POLDIFF_DIFF_ROLE_ALLOWS 0x00000800
+#define POLDIFF_DIFF_ROLE_TRANS 0x00001000
+#define POLDIFF_DIFF_RANGE_TRANS 0x00002000
+#define POLDIFF_DIFF_SYMBOLS (POLDIFF_DIFF_CLASSES|POLDIFF_DIFF_COMMONS|POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_USERS|POLDIFF_DIFF_BOOLS)
+#define POLDIFF_DIFF_RULES (POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_RBAC (POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_ALLOWS|POLDIFF_DIFF_ROLE_TRANS)
+#define POLDIFF_DIFF_MLS (POLDIFF_DIFF_LEVELS|POLDIFF_DIFF_CATS|POLDIFF_DIFF_RANGE_TRANS)
+/* NOTE: while defined OCONS are not currently supported */
+#define POLDIFF_DIFF_OCONS 0
+#define POLDIFF_DIFF_REMAPPED (POLDIFF_DIFF_TYPES|POLDIFF_DIFF_ATTRIBS|POLDIFF_DIFF_AVRULES|POLDIFF_DIFF_TERULES|POLDIFF_DIFF_ROLES|POLDIFF_DIFF_ROLE_TRANS|POLDIFF_DIFF_RANGE_TRANS|POLDIFF_DIFF_OCONS)
+#define POLDIFF_DIFF_ALL (POLDIFF_DIFF_SYMBOLS|POLDIFF_DIFF_RULES|POLDIFF_DIFF_MLS|POLDIFF_DIFF_OCONS)
+
+%typemap(check) uint32_t flags {
+ if ($1 & ~(POLDIFF_DIFF_ALL)) {
+#ifdef SWIGJAVA
+ SWIG_exception_typemap(SWIG_ValueError, "Invalid diff flag specified");
+#else
+ SWIG_exception(SWIG_ValueError, "Invalid diff flag specified");
+#endif
+ }
+}
+
+%inline %{
+/* if java, pass the new exception macro to C not just SWIG */
+#ifdef SWIGJAVA
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(jenv, code, msg); goto fail;}
+#endif
+%}
+
+/* poldiff form */
+typedef enum poldiff_form
+{
+ POLDIFF_FORM_NONE,
+ POLDIFF_FORM_ADDED,
+ POLDIFF_FORM_REMOVED,
+ POLDIFF_FORM_MODIFIED,
+ POLDIFF_FORM_ADD_TYPE,
+ POLDIFF_FORM_REMOVE_TYPE
+} poldiff_form_e;
+
+/* for handling the get_stats function */
+%{
+ typedef struct poldiff_stats {
+ size_t stats[5];
+ } poldiff_stats_t;
+ poldiff_stats_t *poldiff_stats_create() {
+ return calloc(1, sizeof(poldiff_stats_t));
+ }
+ void poldiff_stats_destroy(poldiff_stats_t **x) {
+ if (!x || !(*x))
+ return;
+ free(*x);
+ *x = NULL;
+ }
+%}
+typedef struct poldiff_stats {} poldiff_stats_t;
+%extend poldiff_stats_t {
+ poldiff_stats_t() {
+ poldiff_stats_t *s;
+ BEGIN_EXCEPTION
+ s = poldiff_stats_create();
+ if (!s) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return s;
+ };
+ ~poldiff_stats_t() {
+ poldiff_stats_destroy(&self);
+ };
+ size_t get_stat(poldiff_form_e form) {
+ BEGIN_EXCEPTION
+ switch(form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ return self->stats[0];
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ return self->stats[1];
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ return self->stats[2];
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ return self->stats[3];
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ return self->stats[4];
+ }
+ case POLDIFF_FORM_NONE:
+ default:
+ {
+ SWIG_exception(SWIG_ValueError, "Invalid poldiff form");
+ }
+ }
+ END_EXCEPTION
+ fail:
+ return 0;
+ };
+};
+
+/* for handling vector of line numbers stored as unsigned long but returned as void* */
+%{
+ unsigned long to_ulong(void *x) {
+ return (unsigned long)x;
+ }
+%}
+unsigned long to_ulong(void *x);
+
+/* poldiff object */
+#ifdef SWIGPYTHON
+/* the following type maps handle the poldiff object taking ownership of the policies */
+%typemap(in) apol_policy_t *op {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_policy, 0 | 0 );
+ $1 = (apol_policy_t*)x;
+}
+%typemap(in) apol_policy_t *mp {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_apol_policy, 0 | 0 );
+ $1 = (apol_policy_t*)x;
+}
+#endif
+typedef struct poldiff {} poldiff_t;
+%extend poldiff_t {
+ poldiff_t(apol_policy_t *op, apol_policy_t *mp) {
+ poldiff_t *p;
+ BEGIN_EXCEPTION
+ p = poldiff_create(op, mp, poldiff_swig_message_callback, poldiff_swig_message_callback_arg);
+ if (!p) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ return p;
+ fail:
+ return NULL;
+ };
+ ~poldiff_t() {
+ poldiff_destroy(&self);
+ };
+ void run(uint32_t flags) {
+ BEGIN_EXCEPTION
+ if (poldiff_run(self, flags)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not run diff");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ int is_run(uint32_t flags) {
+ return poldiff_is_run(self, flags);
+ };
+ %newobject get_stats(uint32_t);
+ poldiff_stats_t *get_stats(uint32_t flags) {
+ poldiff_stats_t *s = NULL;
+ BEGIN_EXCEPTION
+ s = poldiff_stats_create();
+ if (!s) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ if (poldiff_get_stats(self, flags, s->stats)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get stats");
+ }
+ END_EXCEPTION
+ return s;
+ fail:
+ poldiff_stats_destroy(&s);
+ return NULL;
+ };
+ void enable_line_numbers() {
+ BEGIN_EXCEPTION
+ if (poldiff_enable_line_numbers(self)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not enable line numbers");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_vector_t *get_attrib_vector() {
+ return poldiff_get_attrib_vector(self);
+ };
+ const apol_vector_t *get_avrule_vector_allow() {
+ return poldiff_get_avrule_vector_allow(self);
+ };
+ const apol_vector_t *get_avrule_vector_auditallow() {
+ return poldiff_get_avrule_vector_auditallow(self);
+ };
+ const apol_vector_t *get_avrule_vector_dontaudit() {
+ return poldiff_get_avrule_vector_dontaudit(self);
+ };
+ const apol_vector_t *get_avrule_vector_neverallow() {
+ return poldiff_get_avrule_vector_neverallow(self);
+ };
+ const apol_vector_t *get_bool_vector() {
+ return poldiff_get_bool_vector(self);
+ };
+ const apol_vector_t *get_cat_vector() {
+ return poldiff_get_cat_vector(self);
+ };
+ const apol_vector_t *get_class_vector() {
+ return poldiff_get_class_vector(self);
+ };
+ const apol_vector_t *get_common_vector() {
+ return poldiff_get_common_vector(self);
+ };
+ const apol_vector_t *get_level_vector() {
+ return poldiff_get_level_vector(self);
+ };
+ const apol_vector_t *get_range_trans_vector() {
+ return poldiff_get_range_trans_vector(self);
+ };
+ const apol_vector_t *get_role_allow_vector() {
+ return poldiff_get_role_allow_vector(self);
+ };
+ const apol_vector_t *get_role_trans_vector() {
+ return poldiff_get_role_trans_vector(self);
+ };
+ const apol_vector_t *get_role_vector() {
+ return poldiff_get_role_vector(self);
+ };
+ const apol_vector_t *get_terule_vector_change() {
+ return poldiff_get_terule_vector_change(self);
+ };
+ const apol_vector_t *get_terule_vector_member() {
+ return poldiff_get_terule_vector_member(self);
+ };
+ const apol_vector_t *get_terule_vector_trans() {
+ return poldiff_get_terule_vector_trans(self);
+ };
+ const apol_vector_t *get_type_vector() {
+ return poldiff_get_type_vector(self);
+ };
+ const apol_vector_t *get_user_vector() {
+ return poldiff_get_user_vector(self);
+ };
+ const apol_vector_t *get_type_remap_entries() {
+ return poldiff_type_remap_get_entries(self);
+ };
+ void type_remap_create(apol_string_vector_t *orig_types, apol_string_vector_t *mod_types) {
+ BEGIN_EXCEPTION
+ if (poldiff_type_remap_create(self, (apol_vector_t*)orig_types, (apol_vector_t*)mod_types)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not remap types");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void type_remap_remove(poldiff_type_remap_entry_t *ent) {
+ poldiff_type_remap_entry_remove(self, ent);
+ };
+};
+
+/* attribute diff */
+typedef struct poldiff_attrib {} poldiff_attrib_t;
+%extend poldiff_attrib_t {
+ poldiff_attrib_t () {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_attrib_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_attrib_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_attrib_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_attrib_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_attrib_get_form(self);
+ };
+ const apol_string_vector_t *get_added_types() {
+ return (apol_string_vector_t*)poldiff_attrib_get_added_types(self);
+ };
+ const apol_string_vector_t *get_removed_types() {
+ return (apol_string_vector_t*)poldiff_attrib_get_removed_types(self);
+ };
+};
+%inline %{
+ poldiff_attrib_t *poldiff_attrib_from_void(void *x) {
+ return (poldiff_attrib_t*)x;
+ };
+%}
+
+/* av rule diff */
+typedef struct poldiff_avrule {} poldiff_avrule_t;
+%extend poldiff_avrule_t {
+ poldiff_avrule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_avrule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_avrule_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_avrule_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_avrule_get_form(self);
+ };
+ uint32_t get_rule_type() {
+ return poldiff_avrule_get_rule_type(self);
+ };
+ const char *get_source_type() {
+ return poldiff_avrule_get_source_type(self);
+ };
+ const char *get_target_type() {
+ return poldiff_avrule_get_target_type(self);
+ };
+ const char *get_object_class() {
+ return poldiff_avrule_get_object_class(self);
+ };
+ const qpol_cond_t *get_cond(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_avrule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return cond;
+ };
+ uint32_t get_cond_list(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_avrule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_list;
+ };
+ const apol_policy_t *get_cond_policy(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_avrule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_pol;
+ };
+ const apol_string_vector_t *get_unmodified_perms() {
+ return (apol_string_vector_t*)poldiff_avrule_get_unmodified_perms(self);
+ };
+ const apol_string_vector_t *get_added_perms() {
+ return (apol_string_vector_t*)poldiff_avrule_get_added_perms(self);
+ };
+ const apol_string_vector_t *get_removed_perms() {
+ return (apol_string_vector_t*)poldiff_avrule_get_removed_perms(self);
+ };
+ const apol_vector_t *get_orig_line_numbers() {
+ return poldiff_avrule_get_orig_line_numbers(self);
+ };
+ %newobject get_orig_line_numbers_for_perm(poldiff_t*, char*);
+ apol_vector_t *get_orig_line_numbers_for_perm(poldiff_t *p, char *perm) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_avrule_get_orig_line_numbers_for_perm(p, self, perm);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ const apol_vector_t *get_mod_line_numbers() {
+ return poldiff_avrule_get_mod_line_numbers(self);
+ };
+ %newobject get_mod_line_numbers_for_perm(poldiff_t*, char*);
+ apol_vector_t *get_mod_line_numbers_for_perm(poldiff_t *p, char *perm) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_avrule_get_mod_line_numbers_for_perm(p, self, perm);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+};
+%inline %{
+ poldiff_avrule_t *poldiff_avrule_from_void(void *x) {
+ return (poldiff_avrule_t*)x;
+ };
+%}
+
+/* boolean diff */
+typedef struct poldiff_bool {} poldiff_bool_t;
+%extend poldiff_bool_t {
+ poldiff_bool_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_bool_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_bool_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_bool_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_bool_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_bool_get_form(self);
+ };
+};
+%inline %{
+ poldiff_bool_t *poldiff_bool_from_void(void *x) {
+ return (poldiff_bool_t*)x;
+ };
+%}
+
+/* category diff */
+typedef struct poldiff_cat {} poldiff_cat_t;
+%extend poldiff_cat_t {
+ poldiff_cat_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_cat_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_cat_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_cat_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_cat_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_cat_get_form(self);
+ };
+};
+%inline %{
+ poldiff_cat_t *poldiff_cat_from_void(void *x) {
+ return (poldiff_cat_t*)x;
+ };
+%}
+
+/* class diff */
+typedef struct poldiff_class {} poldiff_class_t;
+%extend poldiff_class_t {
+ poldiff_class_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_class_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_class_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_class_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_class_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_class_get_form(self);
+ };
+ const apol_string_vector_t *get_added_perms() {
+ return (apol_string_vector_t*)poldiff_class_get_added_perms(self);
+ };
+ const apol_string_vector_t *get_removed_perms() {
+ return (apol_string_vector_t*)poldiff_class_get_removed_perms(self);
+ };
+};
+%inline %{
+ poldiff_class_t *poldiff_class_from_void(void *x) {
+ return (poldiff_class_t*)x;
+ };
+%}
+
+/* common diff */
+typedef struct poldiff_common {} poldiff_common_t;
+%extend poldiff_common_t {
+ poldiff_common_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_common_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_common_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_common_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_common_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_common_get_form(self);
+ };
+ const apol_string_vector_t *get_added_perms() {
+ return (apol_string_vector_t*)poldiff_common_get_added_perms(self);
+ };
+ const apol_string_vector_t *get_removed_perms() {
+ return (apol_string_vector_t*)poldiff_common_get_removed_perms(self);
+ };
+};
+%inline %{
+ poldiff_common_t *poldiff_common_from_void(void *x) {
+ return (poldiff_common_t*)x;
+ };
+%}
+
+/* level diff */
+typedef struct poldiff_level {} poldiff_level_t;
+%extend poldiff_level_t {
+ poldiff_level_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_level_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_level_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_level_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ %newobject to_string_brief(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_level_to_string_brief(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_level_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_level_get_form(self);
+ };
+ const apol_string_vector_t *get_unmodified_cats() {
+ return (apol_string_vector_t*)poldiff_level_get_unmodified_cats(self);
+ };
+ const apol_string_vector_t *get_added_cats() {
+ return (apol_string_vector_t*)poldiff_level_get_added_cats(self);
+ };
+ const apol_string_vector_t *get_removed_cats() {
+ return (apol_string_vector_t*)poldiff_level_get_removed_cats(self);
+ };
+};
+%inline %{
+ poldiff_level_t *poldiff_level_from_void(void *x) {
+ return (poldiff_level_t*)x;
+ };
+%}
+
+/* range diff */
+typedef struct poldiff_range {} poldiff_range_t;
+%extend poldiff_range_t {
+ poldiff_range_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_range_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_range_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string_brief(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_range_to_string_brief(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const apol_vector_t *get_levels() {
+ return poldiff_range_get_levels(self);
+ };
+ const apol_mls_range_t *get_original_range() {
+ return poldiff_range_get_original_range(self);
+ };
+ const apol_mls_range_t *get_modified_range() {
+ return poldiff_range_get_modified_range(self);
+ };
+ const apol_string_vector_t *get_min_added_cats() {
+ return (apol_string_vector_t*)poldiff_range_get_min_added_cats(self);
+ };
+ const apol_string_vector_t *get_min_removed_cats() {
+ return (apol_string_vector_t*)poldiff_range_get_min_removed_cats(self);
+ };
+ const apol_string_vector_t *get_min_unmodified_cats() {
+ return (apol_string_vector_t*)poldiff_range_get_min_unmodified_cats(self);
+ };
+};
+%inline %{
+ poldiff_range_t *poldiff_range_from_void(void *x) {
+ return (poldiff_range_t*)x;
+ };
+%}
+
+/* range_transition rule diff */
+typedef struct poldiff_range_trans {} poldiff_range_trans_t;
+%extend poldiff_range_trans_t {
+ poldiff_range_trans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_range_trans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_range_trans_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_range_trans_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_range_trans_get_form(self);
+ };
+ const char *get_source_type() {
+ return poldiff_range_trans_get_source_type(self);
+ };
+ const char *get_target_type() {
+ return poldiff_range_trans_get_target_type(self);
+ };
+ const char *get_target_class() {
+ return poldiff_range_trans_get_target_class(self);
+ };
+ const poldiff_range_t *get_range() {
+ return poldiff_range_trans_get_range(self);
+ };
+};
+%inline %{
+ poldiff_range_trans_t *poldiff_range_trans_from_void(void *x) {
+ return (poldiff_range_trans_t *)x;
+ };
+%}
+
+/* role allow rule diff */
+typedef struct poldiff_role_allow {} poldiff_role_allow_t;
+%extend poldiff_role_allow_t {
+ poldiff_role_allow_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_role_allow_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_role_allow_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_role_allow_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_role_allow_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_role_allow_get_form(self);
+ };
+ const apol_string_vector_t *get_unmodified_roles() {
+ return (apol_string_vector_t*)poldiff_role_allow_get_unmodified_roles(self);
+ };
+ const apol_string_vector_t *get_added_roles() {
+ return (apol_string_vector_t*)poldiff_role_allow_get_added_roles(self);
+ };
+ const apol_string_vector_t *get_removed_roles() {
+ return (apol_string_vector_t*)poldiff_role_allow_get_removed_roles(self);
+ };
+};
+%inline %{
+ poldiff_role_allow_t *poldiff_role_allow_from_void(void *x) {
+ return (poldiff_role_allow_t *)x;
+ };
+%}
+
+/* role_transition rule diff */
+typedef struct poldiff_role_trans {} poldiff_role_trans_t;
+%extend poldiff_role_trans_t {
+ poldiff_role_trans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_role_trans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_role_trans_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_role_trans_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_role_trans_get_form(self);
+ };
+ const char *get_source_role() {
+ return poldiff_role_trans_get_source_role(self);
+ };
+ const char *get_target_type() {
+ return poldiff_role_trans_get_target_type(self);
+ };
+ const char *get_original_default() {
+ return poldiff_role_trans_get_original_default(self);
+ };
+ const char *get_modified_default() {
+ return poldiff_role_trans_get_modified_default(self);
+ };
+};
+%inline %{
+ poldiff_role_trans_t *poldiff_role_trans_from_void(void *x) {
+ return (poldiff_role_trans_t *)x;
+ };
+%}
+
+/* role diff */
+typedef struct poldiff_role {} poldiff_role_t;
+%extend poldiff_role_t {
+ poldiff_role_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_role_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_role_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_role_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_role_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_role_get_form(self);
+ };
+ const apol_string_vector_t *get_added_types() {
+ return (apol_string_vector_t*)poldiff_role_get_added_types(self);
+ };
+ const apol_string_vector_t *get_removed_types() {
+ return (apol_string_vector_t*)poldiff_role_get_removed_types(self);
+ };
+};
+%inline %{
+ poldiff_role_t *poldiff_role_from_void(void *x) {
+ return (poldiff_role_t*)x;
+ };
+%}
+
+/* te rule diff */
+typedef struct poldiff_terule {} poldiff_terule_t;
+%extend poldiff_terule_t {
+ poldiff_terule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_terule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_terule_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_terule_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ poldiff_form_e get_form() {
+ return poldiff_terule_get_form(self);
+ };
+ uint32_t get_rule_type() {
+ return poldiff_terule_get_rule_type(self);
+ };
+ const char *get_source_type() {
+ return poldiff_terule_get_source_type(self);
+ };
+ const char *get_target_type() {
+ return poldiff_terule_get_target_type(self);
+ };
+ const char *get_object_class() {
+ return poldiff_terule_get_object_class(self);
+ };
+ const qpol_cond_t *get_cond(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_terule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return cond;
+ };
+ uint32_t get_cond_list(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_terule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_list;
+ };
+ const apol_policy_t *get_cond_policy(poldiff_t *p) {
+ const qpol_cond_t *cond;
+ uint32_t which_list;
+ const apol_policy_t *which_pol;
+ poldiff_terule_get_cond(p, self, &cond, &which_list, &which_pol);
+ return which_pol;
+ };
+ const char *get_original_default() {
+ return poldiff_terule_get_original_default(self);
+ };
+ const char *get_modified_default() {
+ return poldiff_terule_get_modified_default(self);
+ };
+ const apol_vector_t *get_orig_line_numbers() {
+ return poldiff_terule_get_orig_line_numbers(self);
+ };
+ const apol_vector_t *get_mod_line_numbers() {
+ return poldiff_terule_get_mod_line_numbers(self);
+ };
+};
+%inline %{
+ poldiff_terule_t *poldiff_terule_from_void(void *x) {
+ return (poldiff_terule_t*)x;
+ };
+%}
+
+/* type diff */
+typedef struct poldiff_type {} poldiff_type_t;
+%extend poldiff_type_t {
+ poldiff_type_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_type_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_type_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_type_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_type_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_type_get_form(self);
+ };
+ const apol_string_vector_t *get_added_attribs() {
+ return (apol_string_vector_t*)poldiff_type_get_added_attribs(self);
+ };
+ const apol_string_vector_t *get_removed_attribs() {
+ return (apol_string_vector_t*)poldiff_type_get_removed_attribs(self);
+ };
+};
+%inline %{
+ poldiff_type_t *poldiff_type_from_void(void *x) {
+ return (poldiff_type_t*)x;
+ };
+%}
+
+/* user diff */
+typedef struct poldiff_user {} poldiff_user_t;
+%extend poldiff_user_t {
+ poldiff_user_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_user_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_user_t() {
+ /* no op */
+ return;
+ };
+ %newobject to_string(poldiff_t*);
+ char *to_string(poldiff_t *p) {
+ char *str;
+ BEGIN_EXCEPTION
+ str = poldiff_user_to_string(p, self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ const char *get_name() {
+ return poldiff_user_get_name(self);
+ };
+ poldiff_form_e get_form() {
+ return poldiff_user_get_form(self);
+ };
+ const apol_string_vector_t *get_unmodified_roles() {
+ return (apol_string_vector_t*)poldiff_user_get_unmodified_roles(self);
+ };
+ const apol_string_vector_t *get_added_roles() {
+ return (apol_string_vector_t*)poldiff_user_get_added_roles(self);
+ };
+ const apol_string_vector_t *get_removed_roles() {
+ return (apol_string_vector_t*)poldiff_user_get_removed_roles(self);
+ };
+ const poldiff_level_t *get_original_dfltlevel() {
+ return poldiff_user_get_original_dfltlevel(self);
+ };
+ const poldiff_level_t *get_modified_dfltlevel() {
+ return poldiff_user_get_modified_dfltlevel(self);
+ };
+ const poldiff_range_t *get_range() {
+ return poldiff_user_get_range(self);
+ };
+};
+%inline %{
+ poldiff_user_t *poldiff_user_from_void(void *x) {
+ return (poldiff_user_t*)x;
+ };
+%}
+
+/* type remap */
+typedef struct poldiff_type_remap_entry {} poldiff_type_remap_entry_t;
+%extend poldiff_type_remap_entry_t {
+ poldiff_type_remap_entry_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create poldiff_type_remap_entry_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~poldiff_type_remap_entry_t() {
+ /* no op */
+ return;
+ };
+ %newobject get_original_types(poldiff_t*);
+ apol_string_vector_t *get_original_types(poldiff_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_type_remap_entry_get_original_types(p, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ %newobject get_modified_types(poldiff_t*);
+ apol_string_vector_t *get_modified_types(poldiff_t *p) {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = poldiff_type_remap_entry_get_modified_types(p, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ int get_is_inferred() {
+ return poldiff_type_remap_entry_get_is_inferred(self);
+ };
+ int get_is_enabled() {
+ return poldiff_type_remap_entry_get_is_enabled(self);
+ };
+ void set_enabled(int enable) {
+ poldiff_type_remap_entry_set_enabled(self, enable);
+ };
+};
+%inline %{
+ poldiff_type_remap_entry_t *poldiff_type_remap_entry_from_void(void *x) {
+ return (poldiff_type_remap_entry_t*)x;
+ };
+%}
+
diff --git a/libpoldiff/swig/python/Makefile.am b/libpoldiff/swig/python/Makefile.am
new file mode 100644
index 0000000..ea3ea85
--- /dev/null
+++ b/libpoldiff/swig/python/Makefile.am
@@ -0,0 +1,39 @@
+wrappedso_DATA = _poldiff.so.@libpoldiff_version@
+wrappedso_SONAME = @libpoldiff_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+wrappedpy_DATA = poldiff.py
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../poldiff.i
+BUILT_SOURCES = poldiff_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libpoldiff/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @POLDIFF_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libpoldiff/src/libpoldiff.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_PYTHON_OPT) -o $@ \
+ -I$(top_srcdir)/libpoldiff/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _poldiff.so
+
+$(wrappedpy_DATA): $(BUILT_SOURCES)
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _poldiff.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_poldiff.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _poldiff.so poldiff.pyc
diff --git a/libpoldiff/swig/tcl/Makefile.am b/libpoldiff/swig/tcl/Makefile.am
new file mode 100644
index 0000000..d4bcdc9
--- /dev/null
+++ b/libpoldiff/swig/tcl/Makefile.am
@@ -0,0 +1,37 @@
+wrappedso_DATA = libtpoldiff.so.@libpoldiff_version@
+wrappedso_SONAME = @libpoldiff_tswig_soname@
+short_name = libtpoldiff.so
+wrappedsodir = $(libdir)/setools/poldiff
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../poldiff.i
+BUILT_SOURCES = poldiff_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libpoldiff/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @POLDIFF_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libpoldiff/src/libpoldiff.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_TCL_OPT) -pkgversion @libpoldiff_version@ -o $@ -I$(top_srcdir)/libpoldiff/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libapol/swig -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+$(package_SCRIPTS): $(wrappedso_DATA)
+ echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src:$(top_builddir)/libpoldiff/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) poldiff
+ cp $(wrappedso_DATA) $@ poldiff
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) poldiff/$(wrappedso_DATA) poldiff/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libpoldiff/tests/Makefile.am b/libpoldiff/tests/Makefile.am
new file mode 100644
index 0000000..f46f739
--- /dev/null
+++ b/libpoldiff/tests/Makefile.am
@@ -0,0 +1,19 @@
+TESTS = libpoldiff-tests
+check_PROGRAMS = libpoldiff-tests
+
+libpoldiff_tests_SOURCES = \
+ components-tests.c components-tests.h \
+ libpoldiff-tests.c libpoldiff-tests.h \
+ mls-tests.c mls-tests.h \
+ nomls-tests.c nomls-tests.h \
+ policy-defs.h \
+ rules-tests.c rules-tests.h
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @POLDIFF_CFLAGS@
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @POLDIFF_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libpoldiff_tests_DEPENDENCIES = ../src/libpoldiff.so
diff --git a/libpoldiff/tests/components-tests.c b/libpoldiff/tests/components-tests.c
new file mode 100644
index 0000000..4d70e80
--- /dev/null
+++ b/libpoldiff/tests/components-tests.c
@@ -0,0 +1,544 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for components.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "libpoldiff-tests.h"
+#include "components-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <apol/util.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *unchanged_attributes[] = {
+/* 00.0 */
+ "data",
+ NULL
+};
+char *added_attributes[] = {
+/* 00.1 */
+ "mineral",
+ NULL
+};
+char *removed_attributes[] = {
+/* 00.2 */
+ "other",
+ NULL
+};
+char *modified_attributes[] = {
+/* 00.3.0 */
+ "tree +holly_t",
+/* 00.3.1 */
+ "fish -bass_t",
+ "plant -daikon_t",
+/* 00.3.2 */
+ "animal +hippo_t",
+ "animal -bass_t",
+ "animal -koala_t",
+ "mammal +hippo_t",
+ "mammal -bear_t",
+ NULL
+};
+char *unchanged_bools[] = {
+/* 02.0 */
+ "frog",
+ NULL
+};
+char *added_bools[] = {
+/* 02.1 */
+ "shark",
+ NULL
+};
+char *removed_bools[] = {
+/* 02.2 */
+ "dog",
+ NULL
+};
+char *modified_bools[] = {
+/* 02.3 */
+ "wark",
+ NULL
+};
+char *unchanged_classes[] = {
+/* 04.0 */
+ "filesystem", "dir", "blk_file", "sock_file", "fifo_file", "netif",
+ "process", "msg", "security", "system", "capability", "passwd",
+ "window", "font", "colormap", "property", "cursor", "xclient",
+ "xinput", "xserver", "xextension", "pax", "dbus", "ncsd",
+ "association", "context", NULL
+};
+char *added_classes[] = {
+/* 04.1 */
+ "thing",
+ NULL
+};
+char *removed_classes[] = {
+/* 04.2 */
+ "key",
+ NULL
+};
+char *modified_classes[] = {
+/* 04.3.00 */
+ "fd +be",
+/* 04.3.01 */
+ "chr_file -execmod",
+/* 04.3.02*/
+ "file +newperm",
+ "file -execmod",
+/* 04.3.03 */
+ "ipc +unix_exec",
+ "sem +unix_exec",
+/* 04.3.04 */
+ "socket -name_bind",
+ "tcp_socket -name_bind",
+ "udp_socket -name_bind",
+ "netlink_socket -name_bind",
+ "packet_socket -name_bind",
+ "key_socket -name_bind",
+ "unix_dgram_socket -name_bind",
+ "dccp_socket -name_bind",
+ "netlink_route_socket -name_bind",
+ "netlink_firewall_socket -name_bind",
+ "netlink_tcpdiag_socket -name_bind",
+ "netlink_nflog_socket -name_bind",
+ "netlink_xfrm_socket -name_bind",
+ "netlink_selinux_socket -name_bind",
+ "netlink_audit_socket -name_bind",
+ "netlink_ip6fw_socket -name_bind",
+ "netlink_dnrt_socket -name_bind",
+ "appletalk_socket -name_bind",
+ "netlink_kobject_uevent_socket -name_bind",
+/* 04.3.05 */
+ "drawable +bar",
+ "drawable -blah",
+/* 04.3.06 */
+ "msgq +unix_exec",
+ "msgq +dequeue",
+/* 04.3.07 */
+ "rawip_socket -name_bind",
+ "rawip_socket +ip_bind",
+/* 04.3.08 */
+ "shm +unix_exec",
+ "shm -lock",
+/* 04.3.09 */
+ "unix_stream_socket -newconn",
+ "unix_stream_socket -name_bind",
+/* 04.3.10 */
+ "gc +bar",
+ "gc +remove",
+ "gc -blah",
+ "gc -free",
+ NULL
+};
+
+char *unchanged_commons[] = {
+/* 05.0 */
+ "file",
+ NULL
+};
+char *added_commons[] = {
+/* 05.1 */
+ "new",
+ NULL
+};
+char *removed_commons[] = {
+/* 05.2 */
+ "old",
+ NULL
+};
+char *modified_commons[] = {
+/* 05.3.0 */
+ "ipc +unix_exec",
+/* 05.3.1 */
+ "socket -name_bind",
+/* 05.3.2 */
+ "bob -blah",
+ "bob +bar",
+ NULL
+};
+
+char *unchanged_roles[] = {
+/* 08.0 */
+ "placeholder_r", "admin_r", "intern_r",
+ NULL
+};
+char *added_roles[] = {
+/* 08.1 */
+ "strange_r",
+ NULL
+};
+char *removed_roles[] = {
+/* 08.2 */
+ "guest_r",
+ NULL
+};
+char *modified_roles[] = {
+/* 08.3.0 */
+ "user_r +hippo_t",
+/* 08.3.1 */
+ "lumberjack_r +holly_t",
+/* 08.3.2 */
+ "staff_r -bass_t",
+/* 08.3.3 */
+ "aquarium_r -bass_t",
+ "garden_r -daikon_t",
+/* 08.3.4 */
+ "object_r +hippo_t",
+ "object_r +acorn_t",
+ "object_r -bass_t",
+ "object_r -koala_t",
+ "deity_r +acorn_t",
+ "deity_r +hippo_t",
+ "deity_r -bass_t",
+ "deity_r -dirt_t",
+ "deity_r -koala_t",
+/* 08.3.5 */
+ "zoo_r +hippo_t",
+ "zoo_r -bass_t",
+ "zoo_r -koala_t",
+ "mammal_r +hippo_t",
+ "mammal_r -bear_t",
+ NULL
+};
+
+char *unchanged_types[] = {
+/* 12.0.0 */
+ "placeholder_t", "finch_t", "trout_t",
+ "birch_t", "oak_t", "potato_t", "tiger_t",
+ "lion_t", "pine_t", "log_t", "file_t",
+/* 12.0.1 */
+ "firefly_t", "lightningbug_t",
+/* 12.0.2 */
+ "rock_t", "big_stone_t",
+ NULL
+};
+
+char *added_types[] = {
+/* 12.1.0 */
+ "hippo_t",
+ "acorn_t",
+ NULL
+};
+
+/* 12.1.1 */
+char *removed_types[] = {
+/* 12.2.0 */
+ "bass_t",
+/* 12.2.1 */
+ "koala_t",
+ NULL
+};
+
+char *modified_types[] = {
+/* 12.3.0 */
+ "holly_t +tree",
+/* 12.3.1 */
+ "bear_t -mammal",
+/* 12.3.2 */
+ "daikon_t -plant",
+ "daikon_t +mineral",
+/* 12.3.3 */
+ "glass_t -> crystal_t +mineral",
+/* 12.3.4 */
+ "dirt_t -> soil_t +mineral",
+/* NEED TO BE ADDED */
+ "stone_t -other",
+ "system_t -other",
+ NULL
+};
+char *aliased_types[] = {
+ /* 12.2.1 */
+ "bear_t -> koala_t",
+ NULL
+};
+
+char *unchanged_users[] = {
+/* 13.0 */
+ "placeholder_u", "su_u", "cyn_u", "danika_u",
+ NULL
+};
+char *added_users[] = {
+/* 13.1 */
+ "gai_u",
+ NULL
+};
+char *removed_users[] = {
+/* 13.2 */
+ "mehnlo_u",
+ NULL
+};
+char *modified_users[] = {
+/* 13.3.0 */
+ "devona_u +aquarium_r",
+ "eve_u +strange_r",
+/* 13.3.1 */
+ "nika_u -user_r",
+/* 13.3.2 */
+ "meloni_u +garden_r",
+ "meloni_u -user_r",
+ NULL
+};
+
+/* This #define is kind of like a template since all of the "get_name" classes
+ * follow the same pattern. The wrapped function name comes out the same as the
+ * original, but with a _w at the end (for example: poldiff_attribute_get_name_w
+ * see definition in components-tests.h */
+WRAP_NAME_FUNC(attrib)
+ WRAP_NAME_FUNC(bool)
+ WRAP_NAME_FUNC(class)
+ WRAP_NAME_FUNC(common)
+ WRAP_NAME_FUNC(role)
+ WRAP_NAME_FUNC(type)
+ WRAP_NAME_FUNC(user)
+ WRAP_NAME_FUNC(cat)
+/* This is the same idea except for with "get_added" and "get_removed" */
+ WRAP_MOD_FUNC(class, perms, added)
+ WRAP_MOD_FUNC(class, perms, removed)
+ WRAP_MOD_FUNC(attrib, types, added)
+ WRAP_MOD_FUNC(attrib, types, removed)
+ WRAP_MOD_FUNC(common, perms, added)
+ WRAP_MOD_FUNC(common, perms, removed)
+ WRAP_MOD_FUNC(role, types, added)
+ WRAP_MOD_FUNC(role, types, removed)
+ WRAP_MOD_FUNC(user, roles, added)
+ WRAP_MOD_FUNC(user, roles, removed)
+ WRAP_MOD_FUNC(type, attribs, added)
+ WRAP_MOD_FUNC(type, attribs, removed)
+
+void build_component_vecs(component_funcs_t * component_funcs)
+{
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = component_funcs->get_diff_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item = apol_vector_get_element(v, i);
+ const char *name_only = NULL;
+ name_only = component_funcs->get_name(item);
+ if (component_funcs->get_form(item) == POLDIFF_FORM_ADDED) {
+ apol_vector_append(added_v, strdup(name_only));
+ } else if (component_funcs->get_form(item) == POLDIFF_FORM_REMOVED) {
+ apol_vector_append(removed_v, strdup(name_only));
+ } else if (component_funcs->get_form(item) == POLDIFF_FORM_MODIFIED) {
+ apol_vector_append(modified_name_only_v, strdup(name_only));
+ size_t j;
+ if (component_funcs->get_added) {
+ const apol_vector_t *added_elements = component_funcs->get_added(item);
+ for (j = 0; j < apol_vector_get_size(added_elements); ++j) {
+ char *added_element;
+ added_element = apol_vector_get_element(added_elements, j);
+ char *modification_str = NULL;
+ size_t modification_str_len = 0;
+ apol_str_appendf(&modification_str, &modification_str_len, "%s %s%s", name_only, "+",
+ added_element);
+ apol_vector_append(modified_v, modification_str);
+ }
+ }
+ if (component_funcs->get_removed) {
+ const apol_vector_t *removed_elements = component_funcs->get_removed(item);
+ for (j = 0; j < apol_vector_get_size(removed_elements); ++j) {
+ char *removed_element;
+ removed_element = apol_vector_get_element(removed_elements, j);
+ char *modification_str = NULL;
+ size_t modification_str_len = 0;
+ apol_str_appendf(&modification_str, &modification_str_len, "%s %s%s", name_only, "-",
+ removed_element);
+ apol_vector_append(modified_v, modification_str);
+ }
+ }
+ if (!(component_funcs->get_added && component_funcs)) {
+ apol_vector_append(modified_v, strdup(name_only));
+ }
+ }
+ }
+}
+
+void components_types_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_types, removed_types, unchanged_types, modified_types);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_type_vector, poldiff_type_get_name_w,
+ poldiff_type_get_form, poldiff_type_get_added_attribs_w,
+ poldiff_type_get_removed_attribs_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ /* this is for the alias tests */
+ size_t i;
+ apol_vector_t *orig_aliases_v = apol_vector_create(free);
+ apol_vector_t *mod_aliases_v = apol_vector_create(free);
+ apol_vector_t *final_aliases_v = apol_vector_create(free);
+ apol_vector_t *correct_final_aliases_v = string_array_to_vector(aliased_types);
+ apol_vector_t *changed_aliases_v;
+
+ qpol_policy_t *orig_qpolicy = apol_policy_get_qpol(orig_policy);
+ qpol_policy_t *mod_qpolicy = apol_policy_get_qpol(mod_policy);
+
+ qpol_iterator_t *orig_types;
+ qpol_iterator_t *mod_types;
+
+ qpol_policy_get_type_iter(mod_qpolicy, &orig_types);
+ for (; !qpol_iterator_end(orig_types); qpol_iterator_next(orig_types)) {
+ unsigned char isalias = 0;
+ qpol_type_t *qpol_type;
+ const char *name;
+ qpol_iterator_get_item(orig_types, (void **)&qpol_type);
+ qpol_type_get_name(orig_qpolicy, qpol_type, &name);
+ qpol_type_get_isalias(orig_qpolicy, qpol_type, &isalias);
+ if (!isalias) {
+ apol_vector_append(orig_aliases_v, strdup(name));
+ }
+ }
+ qpol_policy_get_type_iter(mod_qpolicy, &mod_types);
+ for (; !qpol_iterator_end(mod_types); qpol_iterator_next(mod_types)) {
+ unsigned char isalias = 0;
+ const qpol_type_t *qpol_type;
+ const char *name;
+ qpol_iterator_get_item(mod_types, (void **)&qpol_type);
+ qpol_type_get_name(mod_qpolicy, qpol_type, &name);
+ qpol_type_get_isalias(mod_qpolicy, qpol_type, &isalias);
+ if (isalias) {
+ apol_vector_append(mod_aliases_v, strdup(name));
+ }
+ }
+
+ changed_aliases_v = apol_vector_create_from_intersection(orig_aliases_v, mod_aliases_v, apol_str_strcmp, NULL);
+ char *alias_str = NULL, *str = NULL;
+ size_t alias_str_len = 0, str_len = 0;
+ for (i = 0; i < apol_vector_get_size(changed_aliases_v); ++i) {
+ char *name = apol_vector_get_element(changed_aliases_v, i);
+ qpol_iterator_t *aliased_to;
+ const qpol_type_t *qtype;
+ qpol_policy_get_type_by_name(mod_qpolicy, name, &qtype);
+ qpol_type_get_alias_iter(mod_qpolicy, qtype, &aliased_to);
+ for (; !qpol_iterator_end(aliased_to); qpol_iterator_next(aliased_to)) {
+ const char *name;
+ qpol_iterator_get_item(aliased_to, (void **)&name);
+ apol_str_append(&alias_str, &alias_str_len, name);
+ }
+ apol_str_appendf(&str, &str_len, "%s -> %s", name, alias_str);
+ free(alias_str);
+ apol_vector_append(final_aliases_v, str);
+ qpol_iterator_destroy(&aliased_to);
+ }
+ apol_vector_sort(final_aliases_v, compare_str, NULL);
+ apol_vector_sort(correct_final_aliases_v, compare_str, NULL);
+ size_t first_diff = 0;
+ int test_result;
+
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(final_aliases_v, correct_final_aliases_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(final_aliases_v, correct_final_aliases_v, first_diff, "Aliases");
+ }
+ apol_vector_destroy(&orig_aliases_v);
+ apol_vector_destroy(&mod_aliases_v);
+ apol_vector_destroy(&final_aliases_v);
+ apol_vector_destroy(&correct_final_aliases_v);
+ apol_vector_destroy(&changed_aliases_v);
+ qpol_iterator_destroy(&mod_types);
+ qpol_iterator_destroy(&orig_types);
+
+ cleanup_test(answers);
+}
+
+void components_bools_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_bools, removed_bools, unchanged_bools, modified_bools);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_bool_vector, poldiff_bool_get_name_w,
+ poldiff_bool_get_form, NULL, NULL);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_users_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_users, removed_users, unchanged_users, modified_users);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_user_vector, poldiff_user_get_name_w,
+ poldiff_user_get_form, poldiff_user_get_added_roles_w,
+ poldiff_user_get_removed_roles_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_roles_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_roles, removed_roles, unchanged_roles, modified_roles);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_role_vector, poldiff_role_get_name_w, poldiff_role_get_form,
+ poldiff_role_get_added_types_w, poldiff_role_get_removed_types_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_commons_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_commons, removed_commons, unchanged_commons, modified_commons);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_common_vector, poldiff_common_get_name_w, poldiff_common_get_form,
+ poldiff_common_get_added_perms_w, poldiff_common_get_removed_perms_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_attributes_tests()
+{
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_attributes, removed_attributes, unchanged_attributes, modified_attributes);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_attrib_vector, poldiff_attrib_get_name_w,
+ poldiff_attrib_get_form, poldiff_attrib_get_added_types_w,
+ poldiff_attrib_get_removed_types_w);
+
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+void components_class_tests()
+{
+ poldiff_test_answers_t *answers = init_answer_vectors(added_classes, removed_classes, unchanged_classes, modified_classes);
+ component_funcs_t *funcs = init_test_funcs(poldiff_get_class_vector, poldiff_class_get_name_w,
+ poldiff_class_get_form, poldiff_class_get_added_perms_w,
+ poldiff_class_get_removed_perms_w);
+ run_test(funcs, answers, COMPONENT);
+ free(funcs);
+ cleanup_test(answers);
+}
+
+int components_test_init()
+{
+ if (!(diff = init_poldiff(COMPONENTS_ORIG_POLICY, COMPONENTS_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/libpoldiff/tests/components-tests.h b/libpoldiff/tests/components-tests.h
new file mode 100644
index 0000000..8d8ed6f
--- /dev/null
+++ b/libpoldiff/tests/components-tests.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of components.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef COMPONENTS_TEST
+#define COMPONENTS_TEST
+
+#define WRAP_NAME_FUNC(component) const char *poldiff_##component##_get_name_w(const void *arg) { \
+ const poldiff_##component##_t *cls = (const poldiff_##component##_t *)arg; \
+ return poldiff_##component##_get_name(cls); }
+
+#define WRAP_MOD_FUNC(component,mod_component,mod_type) const apol_vector_t* poldiff_##component##_get_##mod_type##_##mod_component##_w(const void* arg) { \
+ const poldiff_##component##_t *cls = (const poldiff_##component##_t *)arg; \
+ return poldiff_##component##_get_##mod_type##_##mod_component(cls); }
+
+void build_component_vecs(component_funcs_t *);
+
+int components_test_init();
+int components_test_cleanup();
+
+void components_attributes_tests();
+void components_bools_tests();
+void components_commons_tests();
+void components_roles_tests();
+void components_users_tests();
+void components_class_tests();
+void components_types_tests();
+
+#endif
diff --git a/libpoldiff/tests/libpoldiff-tests.c b/libpoldiff/tests/libpoldiff-tests.c
new file mode 100644
index 0000000..c665c1b
--- /dev/null
+++ b/libpoldiff/tests/libpoldiff-tests.c
@@ -0,0 +1,364 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for libpoldiff's correctness.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "libpoldiff-tests.h"
+
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <apol/util.h>
+#include <apol/vector.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "components-tests.h"
+#include "rules-tests.h"
+#include "mls-tests.h"
+#include "nomls-tests.h"
+
+apol_vector_t *string_array_to_vector(char *arr[])
+{
+ apol_vector_t *v = apol_vector_create(free);
+ int i;
+ for (i = 0; arr[i] != NULL; ++i) {
+ apol_vector_append(v, strdup(arr[i]));
+ }
+ return v;
+}
+
+char *vector_to_string(const apol_vector_t * v, const char *pre, const char *sep)
+{
+ char *item = NULL, *str = NULL, *tmp = NULL;
+ size_t i = 0, str_len = 0, tmp_len = 0;
+ size_t num_elements = apol_vector_get_size(v);
+ for (i = 0; v && i < num_elements; i++) {
+ item = apol_vector_get_element(v, i);
+ if (apol_str_appendf(&tmp, &tmp_len, "%s%s", sep, item) < 0) {
+ return NULL;
+ }
+ }
+ apol_str_trim(tmp);
+ if (tmp) {
+ apol_str_appendf(&str, &str_len, "%s%s", pre, tmp);
+ } else {
+ str = strdup("");
+ }
+ free(tmp);
+ return str;
+}
+
+apol_vector_t *shallow_copy_str_vec_and_sort(const apol_vector_t * v)
+{
+ apol_vector_t *copy = apol_vector_create_from_vector(v, NULL, NULL, NULL);
+ apol_vector_sort(copy, apol_str_strcmp, NULL);
+ return copy;
+}
+
+void run_test(component_funcs_t * component_funcs, poldiff_test_answers_t * poldiff_test_answers, test_numbers_e test_num)
+{
+ added_v = apol_vector_create(free);
+ removed_v = apol_vector_create(free);
+ modified_v = apol_vector_create(free);
+ modified_name_only_v = apol_vector_create(free);
+ switch (test_num) {
+ case COMPONENT:
+ build_component_vecs(component_funcs);
+ break;
+ case RULES_AVRULE:
+ build_avrule_vecs();
+ break;
+ case RULES_TERULE:
+ build_terule_vecs();
+ break;
+ case RULES_ROLEALLOW:
+ build_roleallow_vecs();
+ break;
+ case RULES_ROLETRANS:
+ build_roletrans_vecs();
+ break;
+ case MLS_CATEGORY:
+ build_category_vecs();
+ break;
+ case MLS_LEVEL:
+ build_level_vecs();
+ break;
+ case MLS_RANGETRANS:
+ build_rangetrans_vecs();
+ break;
+ case MLS_USER:
+ build_user_vecs();
+ break;
+ }
+ size_t first_diff;
+ apol_vector_t *intersect = NULL, *all_changes = NULL;
+ if (!(all_changes = apol_vector_create(NULL))) {
+ goto err;
+ }
+ apol_vector_cat(all_changes, added_v);
+ apol_vector_cat(all_changes, removed_v);
+ apol_vector_cat(all_changes, modified_name_only_v);
+ if (!
+ (intersect =
+ apol_vector_create_from_intersection(all_changes, poldiff_test_answers->correct_unchanged_v, compare_str, NULL))) {
+ goto err;
+ }
+ /* unchanged */
+ CU_ASSERT_EQUAL(apol_vector_get_size(intersect), 0);
+ /* added */
+ apol_vector_sort(added_v, compare_str, NULL);
+ apol_vector_sort(poldiff_test_answers->correct_added_v, compare_str, NULL);
+ int test_result;
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_v, poldiff_test_answers->correct_added_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(added_v, poldiff_test_answers->correct_added_v, first_diff, "Added");
+ }
+ /* removed */
+ apol_vector_sort(removed_v, compare_str, NULL);
+ apol_vector_sort(poldiff_test_answers->correct_removed_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_v, poldiff_test_answers->correct_removed_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(removed_v, poldiff_test_answers->correct_removed_v, first_diff, "Removed");
+ }
+ /* modified */
+ apol_vector_sort(modified_v, compare_str, NULL);
+ apol_vector_sort(poldiff_test_answers->correct_modified_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(modified_v, poldiff_test_answers->correct_modified_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(modified_v, poldiff_test_answers->correct_modified_v, first_diff, "Modified");
+ }
+
+ apol_vector_destroy(&intersect);
+ apol_vector_destroy(&added_v);
+ apol_vector_destroy(&removed_v);
+ apol_vector_destroy(&modified_name_only_v);
+ apol_vector_destroy(&modified_v);
+ apol_vector_destroy(&all_changes);
+ return;
+ err:
+ apol_vector_destroy(&intersect);
+ apol_vector_destroy(&added_v);
+ apol_vector_destroy(&removed_v);
+ apol_vector_destroy(&modified_name_only_v);
+ apol_vector_destroy(&modified_v);
+ apol_vector_destroy(&all_changes);
+ CU_FAIL_FATAL("Could not initialize vectors for test");
+}
+
+void print_test_failure(apol_vector_t * actual, apol_vector_t * expected, size_t first_diff, const char *test_name)
+{
+ printf("\nTEST FAILED\n");
+ size_t i;
+ printf("--- ACTUAL RESULT (%s) -----\n", test_name);
+ for (i = first_diff; i < apol_vector_get_size(actual); ++i) {
+ char *item = (char *)apol_vector_get_element(actual, i);
+ printf("\t%3d. %s\n", (int)i, item);
+ }
+ printf("--- EXPECTED RESULT (%s) ---\n", test_name);
+ for (i = first_diff; i < apol_vector_get_size(expected); ++i) {
+ char *item = (char *)apol_vector_get_element(expected, i);
+ printf("\t%3d. %s\n", (int)i, item);
+ }
+}
+
+int compare_str(const void *s1, const void *s2, void *debug)
+{
+ char *str1 = strdup((char *)s1);
+ char *str2 = strdup((char *)s2);
+ apol_str_trim(str1);
+ apol_str_trim(str2);
+ int result = strcmp(str1, str2);
+ free(str1);
+ free(str2);
+ return result;
+}
+
+poldiff_test_answers_t *init_answer_vectors(char *added_arr[], char *removed_arr[], char *unchanged_arr[], char *modified_arr[])
+{
+ poldiff_test_answers_t *answers = (poldiff_test_answers_t *) malloc(sizeof(poldiff_test_answers_t));
+ answers->correct_added_v = string_array_to_vector(added_arr);
+ answers->correct_removed_v = string_array_to_vector(removed_arr);
+ answers->correct_unchanged_v = string_array_to_vector(unchanged_arr);
+ answers->correct_modified_v = string_array_to_vector(modified_arr);
+ return answers;
+}
+
+component_funcs_t *init_test_funcs(poldiff_get_diff_vector get_diff_vector, poldiff_get_name get_name, poldiff_get_form get_form,
+ poldiff_get_added get_added, poldiff_get_removed get_removed)
+{
+ component_funcs_t *funcs = (component_funcs_t *) malloc(sizeof(component_funcs_t));
+ funcs->get_diff_vector = get_diff_vector;
+ funcs->get_name = get_name;
+ funcs->get_form = get_form;
+ funcs->get_added = get_added;
+ funcs->get_removed = get_removed;
+ return funcs;
+}
+
+poldiff_t *init_poldiff(char *orig_base_path, char *mod_base_path)
+{
+ poldiff_t *return_diff = NULL;
+ uint32_t flags = POLDIFF_DIFF_ALL;
+ apol_policy_path_t *mod_pol_path = NULL;
+ apol_policy_path_t *orig_pol_path = NULL;
+
+ orig_pol_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, orig_base_path, NULL);
+ if (!orig_pol_path) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ mod_pol_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, mod_base_path, NULL);
+ if (!mod_pol_path) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ orig_policy = apol_policy_create_from_policy_path(orig_pol_path, 0, NULL, NULL);
+ if (!orig_policy) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ mod_policy = apol_policy_create_from_policy_path(mod_pol_path, 0, NULL, NULL);
+ if (!mod_policy) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ if (!(return_diff = poldiff_create(orig_policy, mod_policy, NULL, NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ if (poldiff_run(return_diff, flags)) {
+ goto err;
+ }
+ apol_policy_path_destroy(&orig_pol_path);
+ apol_policy_path_destroy(&mod_pol_path);
+ return return_diff;
+ err:
+ apol_policy_destroy(&orig_policy);
+ apol_policy_destroy(&mod_policy);
+ apol_policy_path_destroy(&orig_pol_path);
+ apol_policy_path_destroy(&mod_pol_path);
+ poldiff_destroy(&return_diff);
+ return NULL;
+}
+
+void cleanup_test(poldiff_test_answers_t * answers)
+{
+ if (answers != NULL) {
+ apol_vector_destroy(&answers->correct_added_v);
+ apol_vector_destroy(&answers->correct_unchanged_v);
+ apol_vector_destroy(&answers->correct_removed_v);
+ apol_vector_destroy(&answers->correct_modified_v);
+ free(answers);
+ }
+}
+
+int poldiff_cleanup()
+{
+ poldiff_destroy(&diff);
+ return 0;
+}
+
+int main()
+{
+ if (CU_initialize_registry() != CUE_SUCCESS) {
+ return CU_get_error();
+ }
+
+ CU_TestInfo components_tests_arr[] = {
+ {"Attributes", components_attributes_tests}
+ ,
+ {"Classes", components_class_tests}
+ ,
+ {"Commons", components_commons_tests}
+ ,
+ {"Roles", components_roles_tests}
+ ,
+ {"Users", components_users_tests}
+ ,
+ {"Bools", components_bools_tests}
+ ,
+ {"Types", components_types_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+
+ CU_TestInfo rules_tests_arr[] = {
+ {"AV Rules", rules_avrules_tests}
+ ,
+ {"TE Rules", rules_terules_tests}
+ ,
+ {"Role Allow Rules", rules_roleallow_tests}
+ ,
+ {"Role Transition Rules", rules_roletrans_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+
+ CU_TestInfo mls_tests_arr[] = {
+ {"Categories", mls_category_tests}
+ ,
+ {"Levels", mls_level_tests}
+ ,
+ {"Range Transitions", mls_rangetrans_tests}
+ ,
+ {"Users (MLS)", mls_user_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+ CU_TestInfo nomls_tests_arr[] = {
+ {"Changed & Unchanged Users", nomls_tests}
+ ,
+ CU_TEST_INFO_NULL
+ };
+
+ CU_SuiteInfo suites[] = {
+ {"Components", components_test_init, poldiff_cleanup, components_tests_arr}
+ ,
+ {"Rules", rules_test_init, poldiff_cleanup, rules_tests_arr}
+ ,
+ {"MLS", mls_test_init, poldiff_cleanup, mls_tests_arr}
+ ,
+ {"Non-MLS vs. MLS Users", nomls_test_init, poldiff_cleanup, nomls_tests_arr}
+ ,
+ CU_SUITE_INFO_NULL
+ };
+
+ CU_register_suites(suites);
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ unsigned int num_failures = CU_get_number_of_failure_records();
+ CU_cleanup_registry();
+ return (int)num_failures;
+}
diff --git a/libpoldiff/tests/libpoldiff-tests.h b/libpoldiff/tests/libpoldiff-tests.h
new file mode 100644
index 0000000..d34c847
--- /dev/null
+++ b/libpoldiff/tests/libpoldiff-tests.h
@@ -0,0 +1,85 @@
+/**
+ * @file
+ *
+ * Header for for CUnit testing framework of libpoldiff's correctness.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBPOLDIFF_TESTS
+#define LIBPOLDIFF_TESTS
+
+#include <poldiff/poldiff.h>
+#include <apol/vector.h>
+
+typedef const apol_vector_t *(*poldiff_get_diff_vector) (const poldiff_t *);
+typedef const char *(*poldiff_get_name) (const void *);
+typedef poldiff_form_e(*poldiff_get_form) (const void *);
+typedef const apol_vector_t *(*poldiff_get_added) (const void *);
+typedef const apol_vector_t *(*poldiff_get_removed) (const void *);
+
+typedef struct _test_answers
+{
+ apol_vector_t *correct_added_v;
+ apol_vector_t *correct_removed_v;
+ apol_vector_t *correct_unchanged_v;
+ apol_vector_t *correct_modified_v;
+} poldiff_test_answers_t;
+
+typedef struct _component_funcs
+{
+ poldiff_get_diff_vector get_diff_vector;
+ poldiff_get_name get_name;
+ poldiff_get_form get_form;
+ poldiff_get_added get_added;
+ poldiff_get_removed get_removed;
+} component_funcs_t;
+
+typedef enum _test_numbers
+{
+ COMPONENT = 0, RULES_AVRULE, RULES_TERULE, RULES_ROLEALLOW, RULES_ROLETRANS,
+ MLS_CATEGORY, MLS_LEVEL, MLS_RANGETRANS, MLS_USER
+} test_numbers_e;
+
+poldiff_t *init_poldiff(char *orig_base_path, char *mod_base_path);
+component_funcs_t *init_test_funcs(poldiff_get_diff_vector, poldiff_get_name, poldiff_get_form, poldiff_get_added,
+ poldiff_get_removed);
+void run_test(component_funcs_t *, poldiff_test_answers_t *, test_numbers_e);
+
+apol_vector_t *string_array_to_vector(char *[]);
+void cleanup_test(poldiff_test_answers_t *);
+char *vector_to_string(const apol_vector_t *, const char *, const char *);
+
+int compare_str(const void *s1, const void *s2, void *debug);
+poldiff_test_answers_t *init_answer_vectors(char *[], char *[], char *[], char *[]);
+void print_test_failure(apol_vector_t *, apol_vector_t *, size_t, const char *);
+
+apol_vector_t *shallow_copy_str_vec_and_sort(const apol_vector_t * v);
+
+poldiff_t *diff;
+
+apol_policy_t *orig_policy;
+apol_policy_t *mod_policy;
+
+apol_vector_t *added_v;
+apol_vector_t *removed_v;
+apol_vector_t *modified_v;
+apol_vector_t *modified_name_only_v;
+
+#endif
diff --git a/libpoldiff/tests/mls-tests.c b/libpoldiff/tests/mls-tests.c
new file mode 100644
index 0000000..ad03feb
--- /dev/null
+++ b/libpoldiff/tests/mls-tests.c
@@ -0,0 +1,635 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for MLS.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "libpoldiff-tests.h"
+#include "mls-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <poldiff/poldiff.h>
+#include <apol/policy.h>
+#include <apol/vector.h>
+#include <apol/util.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *unchanged_users_mls[] = {
+ /* 13.0 */
+ "placeholder_u",
+ "reyna_u",
+ NULL
+};
+
+/* these aren't real tests, but the arrays must be declared anyways */
+char *added_users_mls[] = { NULL };
+char *removed_users_mls[] = { NULL };
+
+/* These strings are always in the same order: added, removed, modified
+ *
+ * Modified User fields are always in this order:
+ * d[...] represents a change in the default level
+ * range[...]
+ * roles[...]
+ */
+char *modified_users_mls[] = {
+ /* 13.3.03 */
+ "su_u: d[+s2 -s1]",
+ /* 13.3.04 */
+ "cyn_u: d[s1 +c2] range[*{s1:c1 c2 +c3}]",
+ /* 13.3.05 */
+ "devona_u: d[s1 -c1]",
+ /* 13.3.06 */
+ "danika_u: d[s1 +c3 -c1] range[*{s1:c1 c2 +c3}]",
+ /* 13.3.07 */
+ "mehnlo_u: range[+{s4:c4}]",
+ /* 13.3.08 */
+ "meloni_u: range[-{s4:c4 c5}]",
+ /* 13.3.09 */
+ "eve_u: range[+{s6} -{s0}]",
+ /* 13.3.10 */
+ "nika_u: range[*{s1:c1 c2 +c3} *{s2:c1 c2 c3 +c4}]",
+ /* 13.3.11 */
+ "koss_u: range[*{s3:c4 -c5} *{s4:c4 -c5} *{s5:c4 -c5}]",
+ /* 13.3.12 */
+ "kihm_u: range[+{s6:c1 c2 c3 c4 c5 c6} -{s0:c0 c1 c2} *{s1:c1 c2 +c3} *{s2:c1 c2 c3 c4 c5 -c0} *{s3:c1 c4 c5 +c6} *{s5:c1 c2 c3 c4 c5 +c6 -c0}]",
+ /* 13.3.13 */
+ "aidan_u: range[*{s1:c1 +c3 -c2} *{s2:c1 c3 -c2}]",
+ /* 13.3.14 */
+ "timera_u: d[+s2 -s1] roles[-admin_r]",
+ /* 13.3.15 */
+ "sheena_u: range[+{s4:c5} *{s2:+c5} *{s3:+c5}] roles[+user_r]",
+ /* 13.3.16 */
+ "chiyo_u: d[+s2 -s1] range[+{s4:c5} *{s2:+c5} *{s3:+c5}]",
+ /* 13.3.17 -- separate test -- see nomls-tests.c */
+ /* 13.3.18 -- separate test -- see nomls-tests.c */
+
+ /* 13.3.19 */
+ "jamei_u: d[+s2 -s1] range[+{s4:c5} *{s2:+c5} *{s3:+c5}] roles[-aquarium_r]",
+ NULL
+};
+
+char *unchanged_rangetrans[] = {
+/* 07.0 */
+ "range_transition placeholder_t oak_t : file s2",
+ NULL
+};
+
+char *added_rangetrans[] = {
+/* 07.1 */
+ "range_transition bear_t stone_t : gc s1",
+ "range_transition log_t bear_t : ipc s2",
+ "range_transition log_t file_t : fd s1",
+ "range_transition rock_t stone_t : dir s3",
+ NULL
+};
+char *removed_rangetrans[] = {
+
+/* 07.2 */
+ "range_transition potato_t daikon_t : dir s0:c2",
+ "range_transition rock_t stone_t : file s3",
+ "range_transition bear_t file_t : msg s1 - s5",
+ "range_transition bear_t log_t : msg s1 - s5",
+ "range_transition trout_t bear_t : pax s1",
+ NULL
+};
+
+/* m{...} represents a change in the minimum set of the transition and is always first,
+ * the rest of string is in the same order: added, removed, modified*/
+char *modified_rangetrans[] = {
+/* 07.3.0 */
+ "range_transition file_t system_t : process +{s2:c1} ",
+/* 07.3.1 */
+ "range_transition tiger_t trout_t : node m{+c1 +c2} -{s0:c1 c2}",
+/* 07.3.2 */
+ "range_transition glass_t log_t : netif +{s6:c1 c2 c3 c4 c5} -{s0:c1 c2} *{s1:c1 c2 +c3}",
+/* 07.3.3 */
+ "range_transition pine_t holly_t : lnk_file m{+c5} *{s3:c4 +c5}",
+/* 07.3.4 */
+ "range_transition rock_t finch_t : chr_file m{-c5} *{s3:c4 -c5}",
+/* 07.3.5 */
+ "range_transition trout_t dirt_t : blk_file m{-c0} *{s2:c1 c2 c3 c4 c5 -c0} *{s3:c1 c4 c5 +c6} *{s5:c1 c2 c3 c4 c5 +c6 -c0}",
+/* 07.3.6 */
+ "range_transition tiger_t stone_t : sock_file m{+c3} *{s1:c1 c2 +c3}",
+/* 07.3.7 */
+ "range_transition firefly_t log_t : fd m{+c5}",
+/* 07.3.8 */
+ "range_transition file_t trout_t : process m{-c2} *{s1:c1 c2 +c3}",
+/* 07.3.9 */
+ "range_transition pine_t oak_t : lnk_file m{+c5 -c0} *{s2:c1 c2 c3 c4 c5 -c0} *{s5:c1 c2 c3 c4 c5 -c0}",
+ NULL
+};
+char *added_rangetrans_type[] = {
+/* 07.4.0 */
+ "range_transition pipe_t rock_t : file s3",
+/* 07.4.1 */
+ "range_transition glass_t pipe_t : process s1",
+/* 07.4.2 */
+ "range_transition hippo_t file_t : msg s1 - s5",
+ "range_transition hippo_t log_t : msg s1 - s5",
+ "range_transition pipe_t oak_t : fifo_file s2 - s3:c5",
+/* 07.4.3 */
+ "range_transition lion_t pipe_t : msg s1 - s5",
+ "range_transition pine_t pipe_t : sem s1 - s4:c4.c5",
+ "range_transition tiger_t pipe_t : msg s1 - s5",
+/* 07.4.4 */
+ "range_transition pipe_t acorn_t : file s2",
+/* 07.4.5 */
+ "range_transition hippo_t pipe_t : msg s1 - s5",
+/* needs to be added */
+ "range_transition trout_t hippo_t : pax s1",
+ NULL
+};
+char *removed_rangetrans_type[] = {
+/* 07.5.0 */
+ "range_transition koala_t stone_t : gc s1",
+/* 07.5.1 */
+ "range_transition log_t koala_t : ipc s2",
+/* 07.5.2 */
+ "range_transition bass_t bear_t : pax s1",
+ "range_transition bass_t lion_t : pax s1",
+ "range_transition bass_t log_t : dir s3:c1",
+ "range_transition bass_t tiger_t : pax s1",
+/* 07.5.3 */
+ "range_transition firefly_t bass_t : passwd s2:c1.c5 - s5:c1.c5",
+/* "range_transition trout_t bear_t : pax s1", this rule is "simply removed" so its in the
+ normal removed array */
+/* 07.5.4 */
+ "range_transition bass_t koala_t : shm s0",
+/* 07.5.5 is a duplicate of the first rule in 07.5.2 */
+
+ NULL
+};
+
+char *unchanged_levels[] = {
+/* 06.0 */
+ "s4",
+ NULL
+};
+char *added_levels[] = {
+/* 06.1 */
+ "s6",
+ NULL
+};
+char *removed_levels[] = {
+/* 06.2 */
+ "s0",
+ NULL
+};
+char *modified_levels[] = {
+/* 06.3.0 */
+ "s3 +c6",
+/* 06.3.1 */
+ "s2 -c0",
+/* 06.3.2 */
+ "s5 +c6 -c0",
+/* 06.3.3 */
+ "s1 +c3",
+ NULL
+};
+
+char *unchanged_categories[] = {
+ /* 03.0 */
+ "c1", "c2", "c3", "c4", "c5",
+ NULL
+};
+char *added_categories[] = {
+ /* 03.1 */
+ "c6",
+ NULL
+};
+char *removed_categories[] = {
+ /* 03.2 */
+ "c0",
+ NULL
+};
+
+char *modified_categories[] = { NULL };
+
+int mls_test_init()
+{
+ if (!(diff = init_poldiff(MLS_ORIG_POLICY, MLS_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void build_category_vecs()
+{
+ char *str = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_cat_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ const char *name = poldiff_cat_get_name(item);
+ str = strdup(name);
+ poldiff_form_e form = poldiff_cat_get_form(item);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ default:
+ // can never get here
+ assert(0);
+ }
+ str = NULL;
+ }
+}
+
+char *level_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_level_t *level = (poldiff_level_t *) arg;
+ char *str = NULL, *cat = NULL;
+ size_t i, str_len = 0;
+ const char *name = poldiff_level_get_name(level);
+ if (name) {
+ apol_str_appendf(&str, &str_len, "%s", name);
+ if (show_changes) {
+ if (form == POLDIFF_FORM_MODIFIED) {
+ const apol_vector_t *added_cats = poldiff_level_get_added_cats(level);
+ for (i = 0; i < apol_vector_get_size(added_cats); ++i) {
+ cat = apol_vector_get_element(added_cats, i);
+ apol_str_appendf(&str, &str_len, " +%s", cat);
+ }
+ const apol_vector_t *removed_cats = poldiff_level_get_removed_cats(level);
+ for (i = 0; i < apol_vector_get_size(removed_cats); ++i) {
+ cat = apol_vector_get_element(removed_cats, i);
+ apol_str_appendf(&str, &str_len, " -%s", cat);
+ }
+ }
+ }
+ }
+ if (str)
+ apol_str_trim(str);
+ return str;
+}
+
+char *modified_mls_range_to_string(const poldiff_range_t * range)
+{
+ char *str = NULL;
+ apol_vector_t *levels = NULL;
+ if (!(levels = poldiff_range_get_levels(range)))
+ goto err;
+ size_t i, str_len = 0;
+ char *min_set_str = NULL;
+ size_t min_set_str_len = 0;
+
+ apol_vector_t *min_set_added = poldiff_range_get_min_added_cats(range);
+ apol_vector_t *min_set_removed = poldiff_range_get_min_removed_cats(range);
+ size_t num_min_added = apol_vector_get_size(min_set_added);
+ size_t num_min_removed = apol_vector_get_size(min_set_removed);
+ if (min_set_added && num_min_added > 0) {
+ char *min_set_added_str = vector_to_string(min_set_added, "", " +");
+ apol_str_appendf(&min_set_str, &min_set_str_len, "%s", min_set_added_str);
+ free(min_set_added_str);
+ }
+ if (min_set_removed && num_min_removed > 0) {
+ char *min_set_removed_str = vector_to_string(min_set_removed, "", " -");
+ apol_str_appendf(&min_set_str, &min_set_str_len, "%s%s", num_min_added > 0 ? " " : "", min_set_removed_str);
+ free(min_set_removed_str);
+ }
+ if (num_min_added || num_min_removed) {
+ char *tmp = strdup(min_set_str);
+ free(min_set_str);
+ min_set_str = NULL;
+ min_set_str_len = 0;
+ apol_str_appendf(&min_set_str, &min_set_str_len, "m{%s} ", tmp);
+ free(tmp);
+ }
+ if (min_set_str) {
+ apol_str_appendf(&str, &str_len, "%s", min_set_str);
+ free(min_set_str);
+ }
+
+ for (i = 0; i < apol_vector_get_size(levels); ++i) {
+ poldiff_level_t *level = apol_vector_get_element(levels, i);
+ poldiff_form_e form = poldiff_level_get_form(level);
+ const char *level_str = poldiff_level_get_name(level);
+ char *sep = NULL, *add_sep = " +", *remove_sep = " -";
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ sep = "+";
+ add_sep = " ";
+ break;
+ case POLDIFF_FORM_REMOVED:
+ sep = "-";
+ remove_sep = " ";
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ sep = "*";
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ const apol_vector_t *unmod_cats = poldiff_level_get_unmodified_cats(level);
+ const apol_vector_t *added_cats = poldiff_level_get_added_cats(level);
+ const apol_vector_t *removed_cats = poldiff_level_get_removed_cats(level);
+ size_t num_unmod_cats = apol_vector_get_size(unmod_cats);
+ size_t num_added_cats = apol_vector_get_size(added_cats);
+ size_t num_removed_cats = apol_vector_get_size(removed_cats);
+ size_t num_cats = num_unmod_cats + num_added_cats + num_removed_cats;
+ char *unmod_cats_str = vector_to_string(unmod_cats, "", " ");
+ char *added_cats_str = vector_to_string(added_cats, num_unmod_cats > 0 ? " " : "", add_sep);
+ char *removed_cats_str = vector_to_string(removed_cats, num_added_cats > 0 ||
+ num_unmod_cats > 0 ? " " : "", remove_sep);
+ apol_str_appendf(&str, &str_len, "%s{%s%s%s%s%s} ", sep, level_str, num_cats > 0 ? ":" : "", unmod_cats_str,
+ added_cats_str, removed_cats_str);
+ free(unmod_cats_str);
+ free(added_cats_str);
+ free(removed_cats_str);
+ }
+ apol_str_trim(str);
+ return str;
+ err:
+ return NULL;
+}
+
+char *rangetrans_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ char *str = NULL;
+ size_t str_len = 0;
+ poldiff_range_trans_t *rt = (poldiff_range_trans_t *) arg;
+ const poldiff_range_t *range = poldiff_range_trans_get_range(rt);
+ const apol_mls_range_t *mod_range = poldiff_range_get_modified_range(range);
+ const apol_mls_range_t *orig_range = poldiff_range_get_original_range(range);
+ char *range_str = NULL;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ range_str = apol_mls_range_render(mod_policy, mod_range);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ range_str = apol_mls_range_render(orig_policy, orig_range);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ range_str = modified_mls_range_to_string(range);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ const char *source_type = poldiff_range_trans_get_source_type(rt);
+ const char *target_type = poldiff_range_trans_get_target_type(rt);
+ const char *target_class = poldiff_range_trans_get_target_class(rt);
+ if (show_changes) {
+ apol_str_appendf(&str, &str_len, "range_transition %s %s : %s %s", source_type, target_type, target_class,
+ range_str);
+ } else {
+ apol_str_appendf(&str, &str_len, "range_transition %s %s : %s", source_type, target_type, target_class);
+ }
+ free(range_str);
+ return str;
+}
+
+void build_rangetrans_vecs()
+{
+ apol_vector_t *added_rangetrans_type_v = apol_vector_create(free);
+ apol_vector_t *removed_rangetrans_type_v = apol_vector_create(free);
+ apol_vector_t *correct_added_rangetrans_type_v = string_array_to_vector(added_rangetrans_type);
+ apol_vector_t *correct_removed_rangetrans_type_v = string_array_to_vector(removed_rangetrans_type);
+
+ char *str = NULL, *name_only = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_range_trans_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_form_e form = poldiff_range_trans_get_form(item);
+ str = rangetrans_to_string(item, form, 1);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_rangetrans_type_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_rangetrans_type_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = rangetrans_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ int test_result;
+ size_t first_diff = 0;
+ apol_vector_sort(added_rangetrans_type_v, compare_str, NULL);
+ apol_vector_sort(correct_added_rangetrans_type_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_rangetrans_type_v, correct_added_rangetrans_type_v, compare_str, NULL,
+ &first_diff));
+ if (test_result) {
+ print_test_failure(added_rangetrans_type_v, correct_added_rangetrans_type_v, first_diff, "Added Due to Types");
+ }
+ apol_vector_sort(removed_rangetrans_type_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_rangetrans_type_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_rangetrans_type_v, correct_removed_rangetrans_type_v, compare_str, NULL,
+ &first_diff));
+ if (test_result) {
+ print_test_failure(removed_rangetrans_type_v, correct_removed_rangetrans_type_v, first_diff,
+ "Removed Due to Types");
+ }
+ apol_vector_destroy(&added_rangetrans_type_v);
+ apol_vector_destroy(&correct_added_rangetrans_type_v);
+ apol_vector_destroy(&removed_rangetrans_type_v);
+ apol_vector_destroy(&correct_removed_rangetrans_type_v);
+
+}
+
+void build_level_vecs()
+{
+ char *str = NULL, *name_only = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_level_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_form_e form = poldiff_cat_get_form(item);
+ str = level_to_string(item, form, 1);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = level_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+}
+
+char *mls_user_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_user_t *u = (poldiff_user_t *) arg;
+ char *str = NULL, *dlevel_str = NULL, *range_str = NULL, *roles_str = NULL;
+ size_t str_len = 0, dlevel_str_len = 0, range_str_len = 0, roles_str_len = 0;
+ const poldiff_range_t *range = poldiff_user_get_range(u);
+ const poldiff_level_t *orig_level = poldiff_user_get_original_dfltlevel(u);
+ const poldiff_level_t *mod_level = poldiff_user_get_modified_dfltlevel(u);
+ poldiff_form_e orig_form = poldiff_level_get_form(orig_level);
+ poldiff_form_e mod_form = poldiff_level_get_form(mod_level);
+ char *orig_level_str = level_to_string(orig_level, orig_form, 1);
+ char *mod_level_str = level_to_string(mod_level, mod_form, 1);
+ //change of default sensitivity
+ if (mod_level_str && orig_level_str) {
+ apol_str_appendf(&dlevel_str, &dlevel_str_len, "d[+%s -%s] ", mod_level_str, orig_level_str);
+ }
+ //change of default category within a sensitivity
+ else if (!mod_level_str && orig_level_str) {
+ apol_str_appendf(&dlevel_str, &dlevel_str_len, "d[%s] ", orig_level_str);
+ } else if (!orig_level_str && mod_level_str) {
+ //this should never happen
+ CU_ASSERT_FALSE(1);
+ }
+ if ((range_str = modified_mls_range_to_string(range)) != NULL) {
+ char *tmp = strdup(range_str);
+ free(range_str);
+ range_str = NULL;
+ range_str_len = 0;
+ apol_str_appendf(&range_str, &range_str_len, "range[%s] ", tmp);
+ free(tmp);
+ }
+ char *added_roles_str = vector_to_string(poldiff_user_get_added_roles(u), "", " +");
+ char *removed_roles_str = vector_to_string(poldiff_user_get_removed_roles(u), "", " -");
+ if (strlen(added_roles_str) > 0 || strlen(removed_roles_str) > 0) {
+ apol_str_appendf(&roles_str, &roles_str_len, "roles[%s%s] ", added_roles_str ? added_roles_str : "",
+ removed_roles_str ? removed_roles_str : "");
+ }
+ const char *user_name = poldiff_user_get_name(u);
+ if (show_changes) {
+ apol_str_appendf(&str, &str_len, "%s: %s%s%s", user_name, dlevel_str ? dlevel_str : "", range_str ? range_str : "",
+ roles_str ? roles_str : "");
+ } else {
+ apol_str_appendf(&str, &str_len, "%s", user_name);
+ }
+
+ free(range_str);
+ free(roles_str);
+ free(dlevel_str);
+ free(mod_level_str);
+ free(orig_level_str);
+ free(added_roles_str);
+ free(removed_roles_str);
+ return str;
+}
+
+void build_user_vecs()
+{
+ char *str = NULL, *name_only;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_user_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_form_e form = poldiff_cat_get_form(item);
+ str = mls_user_to_string(item, form, 1);
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = mls_user_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ str = NULL;
+ }
+}
+
+void mls_category_tests()
+{
+ test_numbers_e test_num = MLS_CATEGORY;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_categories, removed_categories, unchanged_categories, modified_categories);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void mls_rangetrans_tests()
+{
+ test_numbers_e test_num = MLS_RANGETRANS;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_rangetrans, removed_rangetrans, unchanged_rangetrans, modified_rangetrans);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void mls_level_tests()
+{
+ test_numbers_e test_num = MLS_LEVEL;
+ poldiff_test_answers_t *answers = init_answer_vectors(added_levels, removed_levels, unchanged_levels, modified_levels);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void mls_user_tests()
+{
+ test_numbers_e test_num = MLS_USER;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_users_mls, removed_users_mls, unchanged_users_mls, modified_users_mls);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
diff --git a/libpoldiff/tests/mls-tests.h b/libpoldiff/tests/mls-tests.h
new file mode 100644
index 0000000..8922ebb
--- /dev/null
+++ b/libpoldiff/tests/mls-tests.h
@@ -0,0 +1,39 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of MLS.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MLS_TEST
+#define MLS_TEST
+int mls_test_init();
+int mls_test_cleanup();
+
+void mls_category_tests();
+void mls_user_tests();
+void mls_rangetrans_tests();
+void mls_level_tests();
+void build_category_vecs();
+void build_rangetrans_vecs();
+void build_level_vecs();
+void build_user_vecs();
+
+#endif
diff --git a/libpoldiff/tests/nomls-tests.c b/libpoldiff/tests/nomls-tests.c
new file mode 100644
index 0000000..da7dad2
--- /dev/null
+++ b/libpoldiff/tests/nomls-tests.c
@@ -0,0 +1,139 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for MLS versus non-MLS policies.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "libpoldiff-tests.h"
+#include "nomls-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <poldiff/poldiff.h>
+#include <apol/util.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *nomls_unchanged_users[] = {
+/* 13.3.17 */
+ "placeholder_u",
+ "su_u",
+ "cyn_u",
+ "devona_u",
+ "danika_u",
+ "mehnlo_u",
+ "meloni_u",
+ "eve_u",
+ "nika_u",
+ "koss_u",
+ "kihm_u",
+ "aidan_u",
+ "chiyo_u",
+ "reyna_u",
+ NULL
+};
+apol_vector_t *unchanged_users_v;
+apol_vector_t *changed_users_v;
+
+char *nomls_changed_users[] = {
+/* 13.3.18 */
+ "timera_u -admin_r",
+ "sheena_u +user_r",
+ "jamei_u -aquarium_r",
+ NULL
+};
+
+int nomls_test_init()
+{
+ if (!(diff = init_poldiff(NOMLS_ORIG_POLICY, NOMLS_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void build_nomls_vecs()
+{
+ const void *item;
+ const apol_vector_t *v = NULL;
+ size_t i, str_len = 0;
+ char *str = NULL;
+ v = poldiff_get_user_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); ++i) {
+ item = apol_vector_get_element(v, i);
+ poldiff_user_t *u = (poldiff_user_t *) item;
+ const char *name = poldiff_user_get_name(u);
+ const apol_vector_t *added_roles = poldiff_user_get_added_roles(u);
+ const apol_vector_t *removed_roles = poldiff_user_get_removed_roles(u);
+ if (apol_vector_get_size(added_roles) == 0 && apol_vector_get_size(removed_roles) == 0) {
+ apol_vector_append(unchanged_users_v, strdup(name));
+ } else {
+ char *added_roles_str = vector_to_string(added_roles, "", " +");
+ char *removed_roles_str = vector_to_string(removed_roles, "-", " ");
+ apol_str_appendf(&str, &str_len, "%s %s%s", name, added_roles_str, removed_roles_str);
+ free(added_roles_str);
+ free(removed_roles_str);
+ apol_str_trim(str);
+ apol_vector_append(changed_users_v, str);
+ str = NULL;
+ str_len = 0;
+ }
+ }
+}
+void nomls_tests()
+{
+ size_t first_diff = 0;
+ int test_result;
+ unchanged_users_v = apol_vector_create(free);
+ changed_users_v = apol_vector_create(free);
+
+ apol_vector_t *correct_unchanged_users_v = string_array_to_vector(nomls_unchanged_users);
+ apol_vector_t *correct_changed_users_v = string_array_to_vector(nomls_changed_users);
+
+ build_nomls_vecs();
+ apol_vector_sort(unchanged_users_v, compare_str, NULL);
+ apol_vector_sort(correct_unchanged_users_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(unchanged_users_v, correct_unchanged_users_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(unchanged_users_v, correct_unchanged_users_v, first_diff, "Unchanged MLS Users");
+ }
+ apol_vector_sort(changed_users_v, compare_str, NULL);
+ apol_vector_sort(correct_changed_users_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(changed_users_v, correct_changed_users_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(changed_users_v, correct_changed_users_v, first_diff, "Changed MLS Users");
+ }
+ apol_vector_destroy(&unchanged_users_v);
+ apol_vector_destroy(&changed_users_v);
+ apol_vector_destroy(&correct_unchanged_users_v);
+ apol_vector_destroy(&correct_changed_users_v);
+
+}
diff --git a/libpoldiff/tests/nomls-tests.h b/libpoldiff/tests/nomls-tests.h
new file mode 100644
index 0000000..3232fd0
--- /dev/null
+++ b/libpoldiff/tests/nomls-tests.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of MLS versus non-MLS policies.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef NOMLS_TEST
+#define NOMLS_TEST
+int nomls_test_init();
+int nomls_test_cleanup();
+
+void nomls_tests();
+void nomls_unchanged_test();
+
+#endif
diff --git a/libpoldiff/tests/policy-defs.h b/libpoldiff/tests/policy-defs.h
new file mode 100644
index 0000000..37a925b
--- /dev/null
+++ b/libpoldiff/tests/policy-defs.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * Header file defining location of test policies.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLICY_DEFS
+#define POLICY_DEFS
+
+#include <config.h>
+
+#define POLICY_ROOT TEST_POLICIES "/setools-3.2/sediff"
+
+#define COMPONENTS_ORIG_POLICY (POLICY_ROOT "/testing-component-orig.conf")
+#define COMPONENTS_MOD_POLICY (POLICY_ROOT "/testing-component-mod.conf")
+
+#define RULES_ORIG_POLICY (POLICY_ROOT "/testing-rules-orig.conf")
+#define RULES_MOD_POLICY (POLICY_ROOT "/testing-rules-mod.conf")
+
+#define MLS_ORIG_POLICY (POLICY_ROOT "/testing-mls-orig.conf")
+#define MLS_MOD_POLICY (POLICY_ROOT "/testing-mls-mod.conf")
+
+#define NOMLS_ORIG_POLICY (POLICY_ROOT "/testing-mls-orig.conf")
+#define NOMLS_MOD_POLICY (POLICY_ROOT "/testing-mls-mod-nomls.conf")
+
+#endif
diff --git a/libpoldiff/tests/rules-tests.c b/libpoldiff/tests/rules-tests.c
new file mode 100644
index 0000000..c7b7ef1
--- /dev/null
+++ b/libpoldiff/tests/rules-tests.c
@@ -0,0 +1,914 @@
+/**
+ * @file
+ *
+ * Test the libpoldiff's correctness for rules.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "libpoldiff-tests.h"
+#include "rules-tests.h"
+#include "policy-defs.h"
+#include <CUnit/Basic.h>
+#include <CUnit/TestDB.h>
+
+#include <poldiff/poldiff.h>
+#include <apol/policy.h>
+#include <apol/vector.h>
+#include <apol/util.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static apol_vector_t *added_type_rules_v;
+static apol_vector_t *removed_type_rules_v;
+static apol_vector_t *correct_added_type_rules_v;
+static apol_vector_t *correct_removed_type_rules_v;
+
+char *unchanged_avrules[] = {
+/* 01.0 */
+ "allow placeholder_t placeholder_t : file read",
+ "auditallow potato_t pine_t : dir setattr",
+ NULL
+};
+char *added_avrules[] = {
+/* 01.1 */
+ "allow bear_t oak_t : fifo_file write",
+ "allow rock_t log_t : file getattr",
+ "allow tiger_t bear_t : file execute",
+ "auditallow system_t log_t : netif udp_recv",
+ "neverallow lion_t bear_t : file execute",
+ NULL
+};
+char *removed_avrules[] = {
+/* 01.2 */
+ "allow rock_t log_t : dir search",
+ "auditallow system_t log_t : node udp_recv",
+ "allow bear_t bear_t : dir search",
+ "allow bear_t birch_t : fd use",
+ "allow bear_t daikon_t : fd use",
+ "allow bear_t glass_t : file getattr",
+ "allow bear_t holly_t : fd use",
+ "allow bear_t oak_t : fd use",
+ "allow bear_t pine_t : fd use",
+ "allow bear_t potato_t : fd use",
+ NULL
+};
+
+char *modified_avrules[] = {
+/*01.3.0*/
+ "allow firefly_t file_t : file execute +lock",
+/*01.3.1*/
+ "dontaudit bass_t stone_t : dir read search -getattr",
+ "dontaudit trout_t stone_t : dir read search -getattr",
+/*01.3.2*/
+ "allow potato_t daikon_t : file getattr ioctl setattr +write -read",
+ NULL
+};
+
+char *added_type_avrules[] = {
+/* 01.4.00 */
+ "auditallow pipe_t bear_t : blk_file ioctl",
+/* 01.4.01 */
+ "auditallow dirt_t hippo_t : sock_file read",
+/* 01.4.02 */
+ "allow hippo_t birch_t : fd use",
+ "allow hippo_t daikon_t : fd use",
+ "allow hippo_t glass_t : file getattr",
+ "allow hippo_t holly_t : fd use",
+ "allow hippo_t oak_t : fd use",
+ "allow hippo_t pine_t : fd use",
+ "allow hippo_t potato_t : fd use",
+/* 01.4.03 */
+ "allow system_t pipe_t : file getattr ioctl read",
+ "neverallow bear_t pipe_t : process transition",
+ "neverallow lion_t pipe_t : process transition",
+ "neverallow tiger_t pipe_t : process transition",
+/* 01.4.04 */
+ "allow hippo_t pipe_t : lnk_file write",
+/* 01.4.05 */
+ "neverallow hippo_t pipe_t : process transition",
+/* 01.4.06 */
+ "allow hippo_t hippo_t : file getattr",
+/* 01.4.07 */
+ "allow hippo_t hippo_t : dir search",
+/* 01.4.08 */
+ "neverallow hippo_t finch_t : dir add_name",
+/*"neverallow pipe_t finch_t : dir add_name",*/
+ "neverallow pipe_t potato_t : lnk_file write",
+ "neverallow pipe_t system_t : lnk_file write",
+ "neverallow pipe_t bass_t : lnk_file write",
+ "neverallow pipe_t bear_t : lnk_file write",
+ "neverallow pipe_t birch_t : lnk_file write",
+ "neverallow pipe_t daikon_t : lnk_file write",
+ "neverallow pipe_t dirt_t : lnk_file write",
+ "neverallow pipe_t finch_t : lnk_file write",
+ "neverallow pipe_t firefly_t : lnk_file write",
+ "neverallow pipe_t glass_t : lnk_file write",
+ "neverallow pipe_t holly_t : lnk_file write",
+ "neverallow pipe_t lion_t : lnk_file write",
+ "neverallow pipe_t oak_t : lnk_file write",
+ "neverallow pipe_t pine_t : lnk_file write",
+ "neverallow pipe_t placeholder_t : lnk_file write",
+ "neverallow pipe_t rock_t : lnk_file write",
+ "neverallow pipe_t stone_t : lnk_file write",
+ "neverallow pipe_t tiger_t : lnk_file write",
+ "neverallow pipe_t trout_t : lnk_file write",
+/*01.4.09*/
+ "neverallow birch_t hippo_t : lnk_file write",
+ "neverallow daikon_t hippo_t : lnk_file write",
+ "neverallow dirt_t hippo_t : lnk_file write",
+ "neverallow file_t hippo_t : lnk_file write",
+ "neverallow glass_t hippo_t : lnk_file write",
+ "neverallow holly_t hippo_t : lnk_file write",
+ "neverallow lion_t pipe_t : file execute",
+ "neverallow log_t hippo_t : lnk_file write",
+ "neverallow oak_t hippo_t : lnk_file write",
+ "neverallow pine_t hippo_t : lnk_file write",
+ "neverallow potato_t hippo_t : lnk_file write",
+ "neverallow placeholder_t hippo_t : lnk_file write",
+ "neverallow rock_t hippo_t : lnk_file write",
+ "neverallow stone_t hippo_t : lnk_file write",
+ "neverallow system_t hippo_t : lnk_file write",
+/* 01.4.10 */
+ "neverallow pipe_t hippo_t : lnk_file write",
+/* 01.4.11 */
+ "neverallow hippo_t log_t : file execute",
+ "neverallow pipe_t log_t : file execute",
+/* 01.4.12 */
+ "neverallow placeholder_t hippo_t : fd use",
+ "neverallow placeholder_t pipe_t : fd use",
+/*********** NEED TO BE ADDED TO DOCUMENT *******/
+ "neverallow bass_t pipe_t : process transition",
+ "neverallow finch_t pipe_t : process transition",
+ "neverallow firefly_t pipe_t : process transition",
+ "neverallow hippo_t file_t : process transition",
+ "neverallow hippo_t log_t : process transition",
+ "neverallow trout_t pipe_t : process transition",
+ NULL
+};
+char *removed_type_avrules[] = {
+/* 01.5.00 */
+ "allow koala_t oak_t : fifo_file write",
+/* 01.5.01 */
+ "allow tiger_t koala_t : file execute",
+/* 01.5.02 */
+/*"allow bear_t glass_t : file getattr",
+BEAR_T IS NO LONGER MAMMAL, THIS RULES DOESN'T APPLY*/
+ "allow turnip_t dirt_t : dir search",
+ "neverallow koala_t file_t : process transition",
+ "neverallow koala_t log_t : process transition",
+/* 01.5.03 */
+ "allow bear_t turnip_t : fd use",
+ "allow lion_t turnip_t : fd use",
+ "allow stone_t turnip_t : blk_file write",
+ "allow tiger_t turnip_t : fd use",
+/* 01.5.04 */
+ "allow koala_t turnip_t : lnk_file read",
+/* 01.5.05
+"allow bear_t turnip_t : fd use",
+WRONG
+*/
+/* 01.5.06 */
+ "allow turnip_t turnip_t : fd use",
+/* 01.5.07 */
+/*"allow bear_t bear_t : dir search",
+BEAR_T IS NO LONGER MAMMAL, THIS RULE DOESNT APPLY*/
+/* 01.5.08 */
+ "neverallow koala_t finch_t : dir add_name",
+ "neverallow turnip_t finch_t : dir add_name",
+ "neverallow turnip_t potato_t : lnk_file write",
+ "neverallow turnip_t system_t : lnk_file write",
+ "neverallow turnip_t bass_t : lnk_file write",
+ "neverallow turnip_t bear_t : lnk_file write",
+ "neverallow turnip_t birch_t : lnk_file write",
+ "neverallow turnip_t daikon_t : lnk_file write",
+ "neverallow turnip_t dirt_t : lnk_file write",
+ "neverallow turnip_t finch_t : lnk_file write",
+ "neverallow turnip_t firefly_t : lnk_file write",
+ "neverallow turnip_t glass_t : lnk_file write",
+ "neverallow turnip_t holly_t : lnk_file write",
+ "neverallow turnip_t lion_t : lnk_file write",
+ "neverallow turnip_t oak_t : lnk_file write",
+ "neverallow turnip_t pine_t : lnk_file write",
+ "neverallow turnip_t placeholder_t : lnk_file write",
+ "neverallow turnip_t rock_t : lnk_file write",
+ "neverallow turnip_t stone_t : lnk_file write",
+ "neverallow turnip_t tiger_t : lnk_file write",
+ "neverallow turnip_t trout_t : lnk_file write",
+/* 01.5.09 */
+ "neverallow birch_t koala_t : lnk_file write",
+ "neverallow birch_t turnip_t : lnk_file write",
+ "neverallow daikon_t koala_t : lnk_file write",
+ "neverallow daikon_t turnip_t : lnk_file write",
+ "neverallow dirt_t koala_t : lnk_file write",
+ "neverallow dirt_t turnip_t : lnk_file write",
+ "neverallow file_t koala_t : lnk_file write",
+ "neverallow file_t turnip_t : lnk_file write",
+ "neverallow glass_t koala_t : lnk_file write",
+ "neverallow glass_t turnip_t : lnk_file write",
+ "neverallow holly_t koala_t : lnk_file write",
+ "neverallow holly_t turnip_t : lnk_file write",
+ "neverallow lion_t koala_t : file execute",
+ "neverallow log_t koala_t : lnk_file write",
+ "neverallow log_t turnip_t : lnk_file write",
+ "neverallow oak_t koala_t : lnk_file write",
+ "neverallow oak_t turnip_t : lnk_file write",
+ "neverallow placeholder_t koala_t : lnk_file write",
+ "neverallow placeholder_t turnip_t : lnk_file write",
+ "neverallow pine_t koala_t : lnk_file write",
+ "neverallow pine_t turnip_t : lnk_file write",
+ "neverallow potato_t koala_t : lnk_file write",
+ "neverallow potato_t turnip_t : lnk_file write",
+ "neverallow rock_t koala_t : lnk_file write",
+ "neverallow rock_t turnip_t : lnk_file write",
+ "neverallow stone_t koala_t : lnk_file write",
+ "neverallow stone_t turnip_t : lnk_file write",
+ "neverallow system_t koala_t : lnk_file write",
+ "neverallow system_t turnip_t : lnk_file write",
+/* 01.5.10 */
+ "neverallow turnip_t koala_t : lnk_file write",
+ "neverallow turnip_t turnip_t : lnk_file write",
+/* 01.5.11 */
+ "neverallow koala_t log_t : file execute",
+ "neverallow turnip_t log_t : file execute",
+/* 01.5.12 */
+ "neverallow placeholder_t koala_t : fd use",
+ "neverallow placeholder_t turnip_t : fd use",
+ NULL
+};
+
+char *unchanged_roleallowrules[] = {
+/* 09.0*/
+ "allow admin_r staff_r user_r",
+ "allow deity_r { admin_r aquarium_r garden_r guest_r intern_r lumberjack_r mammal_r placeholder_r staff_r user_r zoo_r }",
+ "allow mammal_r intern_r user_r",
+ "allow placeholder_r staff_r",
+ NULL
+};
+char *added_roleallowrules[] = {
+/* 09.1 */
+ "allow intern_r user_r",
+ NULL
+};
+char *removed_roleallowrules[] = {
+/* 09.2 */
+ "allow guest_r user_r",
+ NULL
+};
+char *modified_roleallowrules[] = {
+/* 09.3.0 */
+ "allow aquarium_r { guest_r staff_r +admin_r }",
+ "allow user_r { placeholder_r +guest_r }",
+/* 09.3.1 */
+ "allow garden_r { guest_r -user_r -zoo_r }",
+ "allow lumberjack_r { garden_r -staff_r }",
+ "allow zoo_r { aquarium_r garden_r mammal_r -admin_r }",
+/* 09.3.2 */
+ "allow staff_r { guest_r user_r +mammal_r -intern_r }",
+ NULL
+};
+
+char *unchanged_roletrans_rules[] = {
+/* 10.0*/
+ "role_transition garden_r birch_t lumberjack_r",
+ "role_transition garden_r oak_t lumberjack_r",
+ "role_transition garden_r pine_t lumberjack_r",
+ "role_transition staff_r holly_t garden_r",
+ NULL
+};
+char *added_roletrans_rules[] = {
+/* 10.1 */
+ "role_transition guest_r bear_t staff_r",
+ "role_transition intern_r file_t staff_r",
+ NULL
+};
+char *removed_roletrans_rules[] = {
+/* 10.2 */
+ "role_transition zoo_r bass_t aquarium_r",
+ "role_transition zoo_r bear_t mammal_r",
+ "role_transition zoo_r trout_t aquarium_r",
+ NULL
+};
+char *modified_roletrans_rules[] = {
+/* 10.3.0 */
+ "role_transition guest_r dirt_t { +admin_r -intern_r }",
+ NULL
+};
+char *added_roletrans_type[] = {
+/* 10.4.0 */
+ "role_transition guest_r pipe_t staff_r",
+/* 10.4.1 */
+ "role_transition admin_r pipe_t staff_r",
+ "role_transition staff_r hippo_t zoo_r",
+ "role_transition zoo_r hippo_t mammal_r",
+ NULL
+};
+
+char *removed_roletrans_type[] = {
+/* 10.5.0 */
+ "role_transition guest_r koala_t staff_r",
+/* 10.5.1 */
+ "role_transition staff_r koala_t zoo_r",
+ NULL
+};
+
+char *unchanged_terules[] = {
+/* 11.0 */
+ "type_transition system_t dirt_t : process daikon_t",
+ NULL
+};
+char *added_terules[] = {
+/* 11.1 */
+ "type_member log_t file_t : netif rock_t",
+ "type_transition holly_t bear_t : dir oak_t",
+ NULL
+};
+char *removed_terules[] = {
+/* 11.2 */
+ "type_transition potato_t pine_t : fd log_t",
+ "type_change file_t bear_t : passwd daikon_t",
+ "type_member log_t file_t : node rock_t",
+ "type_change log_t bear_t : passwd daikon_t",
+ NULL
+};
+char *added_type_terules[] = {
+/*11.4.0 */
+ "type_transition hippo_t log_t : file system_t",
+/*11.4.1 */
+ "type_transition bear_t pipe_t : chr_file birch_t",
+/*11.4.2 */
+ "type_transition hippo_t stone_t : netif potato_t",
+/*11.4.3 */
+ "type_change glass_t hippo_t : socket bass_t",
+/*11.4.4 */
+ "type_change hippo_t pipe_t : gc log_t",
+/*11.4.5 */
+ "type_change file_t hippo_t : passwd daikon_t",
+ "type_change log_t hippo_t : passwd daikon_t",
+ "type_change pipe_t hippo_t : passwd daikon_t",
+ "type_change pipe_t lion_t : passwd daikon_t",
+ "type_change pipe_t tiger_t : passwd daikon_t",
+ "type_member hippo_t birch_t : chr_file file_t",
+ "type_member hippo_t daikon_t : chr_file file_t",
+ "type_member hippo_t holly_t : chr_file file_t",
+ "type_member hippo_t oak_t : chr_file file_t",
+ "type_member hippo_t pine_t : chr_file file_t",
+ "type_member hippo_t potato_t : chr_file file_t",
+ NULL
+};
+char *removed_type_terules[] = {
+/* 11.5.0 */
+ "type_change turnip_t glass_t : dir stone_t",
+/* 11.5.1 */
+ "type_change tiger_t turnip_t : file file_t",
+/* 11.5.2 */
+ "type_member turnip_t dirt_t : dir glass_t",
+/* 11.5.3 */
+ "type_member firefly_t turnip_t : file pine_t",
+/* 11.5.4 */
+ "type_member turnip_t turnip_t : fd lion_t",
+/* 11.5.5 */
+ "type_member bass_t turnip_t : chr_file file_t",
+ "type_member bear_t turnip_t : chr_file file_t",
+ "type_member finch_t turnip_t : chr_file file_t",
+ "type_member firefly_t turnip_t : chr_file file_t",
+/* these rules are incorrect because there was no hippo_t in the original policy, so it cannot be removed
+"type_member hippo_t birch_t : chr_file file_t",
+"type_member hippo_t daikon_t : chr_file file_t",
+"type_member hippo_t holly_t : chr_file file_t",
+"type_member hippo_t oak_t : chr_file file_t",
+"type_member hippo_t pine_t : chr_file file_t",
+"type_member hippo_t potato_t : chr_file file_t",
+*/
+ "type_member koala_t birch_t : chr_file file_t",
+ "type_member koala_t daikon_t : chr_file file_t",
+ "type_member koala_t holly_t : chr_file file_t",
+ "type_member koala_t oak_t : chr_file file_t",
+ "type_member koala_t pine_t : chr_file file_t",
+ "type_member koala_t potato_t : chr_file file_t",
+ "type_member koala_t turnip_t : chr_file file_t",
+ "type_member lion_t turnip_t : chr_file file_t",
+ "type_member tiger_t turnip_t : chr_file file_t",
+ "type_member trout_t turnip_t : chr_file file_t",
+/* koala_t is now an alias of animal, thus this rule now applies:
+ type_change glass_t animal : socket bass_t;
+ */
+ "type_change glass_t koala_t : socket bass_t",
+/* also this rule applies:
+ type_transition animal stone_t : netif potato_t;
+*/
+ "type_transition koala_t stone_t : netif potato_t",
+ NULL
+};
+
+char *modified_terules[] = {
+ "type_transition lion_t tiger_t : file +bear_t -koala_t",
+ NULL
+};
+
+static char *get_rule_modification_str(const apol_vector_t * unmodified, const apol_vector_t * added, const apol_vector_t * removed,
+ poldiff_form_e form, int show_changes)
+{
+ char *perm_add_char = "+", *perm_remove_char = "-";
+ apol_vector_t *added_copy = shallow_copy_str_vec_and_sort(added);
+ apol_vector_t *removed_copy = shallow_copy_str_vec_and_sort(removed);
+ apol_vector_t *unmodified_copy = shallow_copy_str_vec_and_sort(unmodified);
+ int error = 0;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ perm_add_char = "";
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ case POLDIFF_FORM_REMOVED:
+ perm_remove_char = "";
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ // do nothing
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ size_t i, str_len;
+ char *perm_name = NULL, *str = NULL;
+ for (i = 0; unmodified_copy != NULL && i < apol_vector_get_size(unmodified_copy); ++i) {
+ char *unmod_perm = apol_vector_get_element(unmodified_copy, i);
+ apol_str_appendf(&str, &str_len, " %s", unmod_perm);
+ }
+ if (show_changes) {
+ for (i = 0; added != NULL && i < apol_vector_get_size(added); i++) {
+ perm_name = (char *)apol_vector_get_element(added_copy, i);
+ if (apol_str_appendf(&str, &str_len, " %s%s", perm_add_char, perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (i = 0; removed != NULL && i < apol_vector_get_size(removed_copy); i++) {
+ perm_name = (char *)apol_vector_get_element(removed_copy, i);
+ if (apol_str_appendf(&str, &str_len, " %s%s", perm_remove_char, perm_name) < 0) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ apol_vector_destroy(&added_copy);
+ apol_vector_destroy(&removed_copy);
+ apol_vector_destroy(&unmodified_copy);
+ return str;
+ err:
+ free(str);
+ return NULL;
+}
+
+static char *avrule_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ const poldiff_avrule_t *avr = (const poldiff_avrule_t *)arg;
+ char *str = NULL;
+ size_t str_len = 0;
+ uint32_t rule_type = poldiff_avrule_get_rule_type(avr);
+ const char *rule_type_str = apol_rule_type_to_str(rule_type);
+ const char *target_type = poldiff_avrule_get_target_type(avr);
+ const char *source_type = poldiff_avrule_get_source_type(avr);
+ const char *object_class = poldiff_avrule_get_object_class(avr);
+ apol_str_appendf(&str, &str_len, "%s %s %s : %s", rule_type_str, source_type, target_type, object_class);
+ if (show_changes) {
+ const apol_vector_t *unmodified_perms = poldiff_avrule_get_unmodified_perms(avr);
+ const apol_vector_t *removed_perms = poldiff_avrule_get_removed_perms(avr);
+ const apol_vector_t *added_perms = poldiff_avrule_get_added_perms(avr);
+ char *perm_str = get_rule_modification_str(unmodified_perms, added_perms, removed_perms, form, show_changes);
+ apol_str_appendf(&str, &str_len, "%s", perm_str);
+ free(perm_str);
+ }
+ return str;
+}
+
+static char *terule_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_terule_t *ter = (poldiff_terule_t *) arg;
+ char *str = NULL;
+ size_t str_len = 0;
+ uint32_t rule_type = poldiff_terule_get_rule_type(ter);
+ const char *rule_type_str = apol_rule_type_to_str(rule_type);
+ const char *target_type = poldiff_terule_get_target_type(ter);
+ const char *source_type = poldiff_terule_get_source_type(ter);
+ const char *object_class = poldiff_terule_get_object_class(ter);
+ const char *default_type;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ default_type = poldiff_terule_get_modified_default(ter);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ case POLDIFF_FORM_MODIFIED:
+ default_type = poldiff_terule_get_original_default(ter);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ if (form == POLDIFF_FORM_MODIFIED && show_changes) {
+ const char *orig_default = poldiff_terule_get_original_default(ter);
+ const char *mod_default = poldiff_terule_get_modified_default(ter);
+ apol_str_appendf(&str, &str_len, "%s %s %s : %s +%s -%s", rule_type_str, source_type, target_type, object_class,
+ mod_default, orig_default);
+ } else
+ apol_str_appendf(&str, &str_len, "%s %s %s : %s %s", rule_type_str, source_type, target_type, object_class,
+ default_type);
+ return str;
+}
+
+static char *roletrans_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_role_trans_t *rt = (poldiff_role_trans_t *) arg;
+ char *str = NULL;
+ size_t str_len = 0;
+ const char *source_role = poldiff_role_trans_get_source_role(rt);
+ const char *target_type = poldiff_role_trans_get_target_type(rt);
+ apol_str_appendf(&str, &str_len, "role_transition %s %s", source_role, target_type);
+ if (show_changes) {
+ const char *orig_default = poldiff_role_trans_get_original_default(rt);
+ const char *mod_default = poldiff_role_trans_get_modified_default(rt);
+
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_str_appendf(&str, &str_len, " %s", mod_default);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_str_appendf(&str, &str_len, " %s", orig_default);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ apol_str_appendf(&str, &str_len, " { +%s -%s }", mod_default, orig_default);
+ break;
+ default:
+ // should never get here:
+ assert(0);
+ }
+ }
+ return str;
+}
+
+static char *roleallow_to_string(const void *arg, poldiff_form_e form, int show_changes)
+{
+ poldiff_role_allow_t *rat = (poldiff_role_allow_t *) arg;
+ char *str = NULL, *orig_roles_str = NULL;
+ size_t str_len = 0, orig_roles_str_len = 0;
+ const char *name = poldiff_role_allow_get_name(rat);
+ const apol_vector_t *orig_roles;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ orig_roles = poldiff_role_allow_get_added_roles(rat);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ orig_roles = poldiff_role_allow_get_removed_roles(rat);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ orig_roles = poldiff_role_allow_get_unmodified_roles(rat);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ size_t i;
+ size_t num_orig_roles = apol_vector_get_size(orig_roles);
+ const char *fmt;
+ if (num_orig_roles > 1 || (show_changes && form == POLDIFF_FORM_MODIFIED))
+ fmt = "allow %s {%s }";
+ else
+ fmt = "allow %s%s";
+ for (i = 0; i < num_orig_roles; ++i) {
+ char *role = apol_vector_get_element(orig_roles, i);
+ apol_str_appendf(&orig_roles_str, &orig_roles_str_len, " %s", role);
+ }
+ if (show_changes && form == POLDIFF_FORM_MODIFIED) {
+ const apol_vector_t *added_role_v = poldiff_role_allow_get_added_roles(rat);
+ for (i = 0; i < apol_vector_get_size(added_role_v); ++i) {
+ char *added_role = apol_vector_get_element(added_role_v, i);
+ apol_str_appendf(&orig_roles_str, &orig_roles_str_len, " +%s", added_role);
+ }
+ const apol_vector_t *removed_role_v = poldiff_role_allow_get_removed_roles(rat);
+ for (i = 0; i < apol_vector_get_size(removed_role_v); ++i) {
+ char *removed_role = apol_vector_get_element(removed_role_v, i);
+ apol_str_appendf(&orig_roles_str, &orig_roles_str_len, " -%s", removed_role);
+ }
+ }
+ apol_str_appendf(&str, &str_len, fmt, name, orig_roles_str);
+ free(orig_roles_str);
+ return str;
+}
+
+void build_roleallow_vecs()
+{
+ char *str = NULL, *name_only = NULL;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_role_allow_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item = apol_vector_get_element(v, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_role_allow_get_form(item);
+ str = roleallow_to_string(item, form, 1);
+ if (!str)
+ break;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = roleallow_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+}
+
+void build_roletrans_vecs()
+{
+ added_type_rules_v = apol_vector_create(free);
+ removed_type_rules_v = apol_vector_create(free);
+ correct_added_type_rules_v = string_array_to_vector(added_roletrans_type);
+ correct_removed_type_rules_v = string_array_to_vector(removed_roletrans_type);
+
+ char *str = NULL, *name_only;
+ size_t i;
+ const void *item = NULL;
+ const apol_vector_t *v = NULL;
+ v = poldiff_get_role_trans_vector(diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item = apol_vector_get_element(v, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_role_trans_get_form(item);
+ str = roletrans_to_string(item, form, 1);
+ if (!str)
+ break;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = roletrans_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ int test_result;
+ size_t first_diff = 0;
+ apol_vector_sort(added_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_added_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_type_rules_v, correct_added_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(added_type_rules_v, correct_added_type_rules_v, first_diff, "Added Rule (due to Type)");
+ }
+ apol_vector_sort(removed_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_type_rules_v, correct_removed_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(removed_type_rules_v, correct_removed_type_rules_v, first_diff, "Removed Rule (due to Type)");
+ }
+ apol_vector_destroy(&added_type_rules_v);
+ apol_vector_destroy(&correct_added_type_rules_v);
+ apol_vector_destroy(&removed_type_rules_v);
+ apol_vector_destroy(&correct_removed_type_rules_v);
+}
+
+void build_terule_vecs()
+{
+ added_type_rules_v = apol_vector_create(free);
+ removed_type_rules_v = apol_vector_create(free);
+ correct_added_type_rules_v = string_array_to_vector(added_type_terules);
+ correct_removed_type_rules_v = string_array_to_vector(removed_type_terules);
+
+ size_t i;
+ char *str = NULL;
+ const void *item = NULL;
+ const apol_vector_t *member_v = NULL, *change_v = NULL, *trans_v = NULL;
+ member_v = poldiff_get_terule_vector_member(diff);
+ change_v = poldiff_get_terule_vector_change(diff);
+ trans_v = poldiff_get_terule_vector_trans(diff);
+ apol_vector_t *all_terules = apol_vector_create(NULL);
+ apol_vector_cat(all_terules, member_v);
+ apol_vector_cat(all_terules, change_v);
+ apol_vector_cat(all_terules, trans_v);
+
+ for (i = 0; i < apol_vector_get_size(all_terules); i++) {
+ item = apol_vector_get_element(all_terules, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_terule_get_form(item);
+ str = terule_to_string(item, form, 1);
+ if (!str)
+ break;
+ char *name_only = NULL;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = terule_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ size_t first_diff = 0;
+ int test_result = 0;
+ apol_vector_sort(added_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_added_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(added_type_rules_v, correct_added_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(added_type_rules_v, correct_added_type_rules_v, first_diff, "Added Rules (due to types)");
+ }
+
+ apol_vector_sort(removed_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(test_result =
+ apol_vector_compare(removed_type_rules_v, correct_removed_type_rules_v, compare_str, NULL, &first_diff));
+ if (test_result) {
+ print_test_failure(removed_type_rules_v, correct_removed_type_rules_v, first_diff, "Removed Rules (due to types)");
+ }
+ apol_vector_destroy(&all_terules);
+ apol_vector_destroy(&added_type_rules_v);
+ apol_vector_destroy(&correct_added_type_rules_v);
+ apol_vector_destroy(&removed_type_rules_v);
+ apol_vector_destroy(&correct_removed_type_rules_v);
+}
+
+void build_avrule_vecs()
+{
+ added_type_rules_v = apol_vector_create(free);
+ removed_type_rules_v = apol_vector_create(free);
+ correct_added_type_rules_v = string_array_to_vector(added_type_avrules);
+ correct_removed_type_rules_v = string_array_to_vector(removed_type_avrules);
+
+ size_t i;
+ char *str = NULL, *name_only = NULL;
+ const void *item = NULL;
+ const apol_vector_t *allow_v = NULL, *neverallow_v = NULL, *auditallow_v = NULL, *dontaudit_v = NULL;
+ apol_vector_t *all_avrules_v = apol_vector_create(NULL);
+
+ allow_v = poldiff_get_avrule_vector_allow(diff);
+ neverallow_v = poldiff_get_avrule_vector_neverallow(diff);
+ auditallow_v = poldiff_get_avrule_vector_auditallow(diff);
+ dontaudit_v = poldiff_get_avrule_vector_dontaudit(diff);
+
+ apol_vector_cat(all_avrules_v, allow_v);
+ apol_vector_cat(all_avrules_v, neverallow_v);
+ apol_vector_cat(all_avrules_v, auditallow_v);
+ apol_vector_cat(all_avrules_v, dontaudit_v);
+
+ for (i = 0; i < apol_vector_get_size(all_avrules_v); i++) {
+ item = apol_vector_get_element(all_avrules_v, i);
+ if (!item)
+ return;
+ poldiff_form_e form = poldiff_avrule_get_form(item);
+ str = avrule_to_string(item, form, 1);
+ if (!str)
+ break;
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ apol_vector_append(added_v, str);
+ break;
+ case POLDIFF_FORM_REMOVED:
+ apol_vector_append(removed_v, str);
+ break;
+ case POLDIFF_FORM_ADD_TYPE:
+ apol_vector_append(added_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_REMOVE_TYPE:
+ apol_vector_append(removed_type_rules_v, str);
+ break;
+ case POLDIFF_FORM_MODIFIED:
+ name_only = avrule_to_string(item, form, 0);
+ apol_vector_append(modified_name_only_v, name_only);
+ apol_vector_append(modified_v, str);
+ break;
+ default:
+ // should never get here
+ assert(0);
+ }
+ }
+ size_t first_diff = 0;
+ apol_vector_sort(added_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_added_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(apol_vector_compare(added_type_rules_v, correct_added_type_rules_v, compare_str, NULL, &first_diff));
+
+ apol_vector_sort(removed_type_rules_v, compare_str, NULL);
+ apol_vector_sort(correct_removed_type_rules_v, compare_str, NULL);
+ CU_ASSERT_FALSE(apol_vector_compare(removed_type_rules_v, correct_removed_type_rules_v, compare_str, NULL, &first_diff));
+
+ apol_vector_destroy(&removed_type_rules_v);
+ apol_vector_destroy(&correct_removed_type_rules_v);
+ apol_vector_destroy(&added_type_rules_v);
+ apol_vector_destroy(&correct_added_type_rules_v);
+ apol_vector_destroy(&all_avrules_v);
+}
+
+void rules_avrules_tests()
+{
+ test_numbers_e test_num = RULES_AVRULE;
+ poldiff_test_answers_t *answers = init_answer_vectors(added_avrules, removed_avrules, unchanged_avrules, modified_avrules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void rules_terules_tests()
+{
+ test_numbers_e test_num = RULES_TERULE;
+ poldiff_test_answers_t *answers = init_answer_vectors(added_terules, removed_terules, unchanged_terules, modified_terules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void rules_roleallow_tests()
+{
+ test_numbers_e test_num = RULES_ROLEALLOW;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_roleallowrules, removed_roleallowrules, unchanged_roleallowrules,
+ modified_roleallowrules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+void rules_roletrans_tests()
+{
+ test_numbers_e test_num = RULES_ROLETRANS;
+ poldiff_test_answers_t *answers =
+ init_answer_vectors(added_roletrans_rules, removed_roletrans_rules, unchanged_roletrans_rules,
+ modified_roletrans_rules);
+ run_test(NULL, answers, test_num);
+ cleanup_test(answers);
+}
+
+int rules_test_init()
+{
+ if (!(diff = init_poldiff(RULES_ORIG_POLICY, RULES_MOD_POLICY))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/libpoldiff/tests/rules-tests.h b/libpoldiff/tests/rules-tests.h
new file mode 100644
index 0000000..34a30eb
--- /dev/null
+++ b/libpoldiff/tests/rules-tests.h
@@ -0,0 +1,40 @@
+/**
+ * @file
+ *
+ * Header file for libpoldiff's correctness of rules.
+ *
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RULES_TEST
+#define RULES_TEST
+int rules_test_init();
+int rules_test_cleanup();
+
+void rules_avrules_tests();
+void rules_roleallow_tests();
+void rules_roletrans_tests();
+void rules_terules_tests();
+
+void build_avrule_vecs();
+void build_terule_vecs();
+void build_roletrans_vecs();
+void build_roleallow_vecs();
+
+#endif
diff --git a/libqpol/Makefile.am b/libqpol/Makefile.am
new file mode 100644
index 0000000..49afe65
--- /dev/null
+++ b/libqpol/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libqpol.a libqpol.so:
+ $(MAKE) -C src $@
diff --git a/libqpol/include/Makefile.am b/libqpol/include/Makefile.am
new file mode 100644
index 0000000..e90a0d4
--- /dev/null
+++ b/libqpol/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = qpol
diff --git a/libqpol/include/qpol/Makefile.am b/libqpol/include/qpol/Makefile.am
new file mode 100644
index 0000000..b55acb7
--- /dev/null
+++ b/libqpol/include/qpol/Makefile.am
@@ -0,0 +1,30 @@
+qpoldir = $(includedir)/qpol
+
+qpol_HEADERS = \
+ avrule_query.h \
+ bool_query.h \
+ class_perm_query.h \
+ cond_query.h \
+ constraint_query.h \
+ context_query.h \
+ fs_use_query.h \
+ genfscon_query.h \
+ isid_query.h \
+ iterator.h \
+ mls_query.h \
+ mlsrule_query.h \
+ module.h \
+ netifcon_query.h \
+ nodecon_query.h \
+ permissive_query.h \
+ polcap_query.h \
+ policy.h \
+ policy_extend.h \
+ portcon_query.h \
+ rbacrule_query.h \
+ role_query.h \
+ syn_rule_query.h \
+ terule_query.h \
+ type_query.h \
+ user_query.h \
+ util.h
diff --git a/libqpol/include/qpol/avrule_query.h b/libqpol/include/qpol/avrule_query.h
new file mode 100644
index 0000000..d9050d2
--- /dev/null
+++ b/libqpol/include/qpol/avrule_query.h
@@ -0,0 +1,167 @@
+ /**
+ * @file
+ * Defines the public interface for searching and iterating over avrules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_AVRULE_QUERY_H
+#define QPOL_AVRULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/class_perm_query.h>
+#include <qpol/cond_query.h>
+#include <qpol/type_query.h>
+
+ typedef struct qpol_avrule qpol_avrule_t;
+
+/* rule type defines (values copied from "sepol/policydb/policydb.h") */
+#define QPOL_RULE_ALLOW 1
+#define QPOL_RULE_NEVERALLOW 128
+#define QPOL_RULE_AUDITALLOW 2
+/* dontaudit is actually stored as auditdeny so that value is used here */
+#define QPOL_RULE_DONTAUDIT 4
+
+/**
+ * Get an iterator over all av rules in a policy of a rule type in
+ * rule_type_mask. It is an error to call this function if rules are
+ * not loaded. Likewise, it is an error if neverallows are requested
+ * but they were not loaded.
+ * @param policy Policy from which to get the av rules.
+ * @param rule_type_mask Bitwise or'ed set of QPOL_RULE_* values.
+ * It is an error to specify any of QPOL_RULE_TYPE_* in the mask.
+ * @param iter Iterator over items of type qpol_avrule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_avrule_iter(const qpol_policy_t * policy, uint32_t rule_type_mask, qpol_iterator_t ** iter);
+
+/**
+ * Get the source type from an av rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the source type.
+ * @param source Pointer in which to store the source type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source will be NULL.
+ */
+ extern int qpol_avrule_get_source_type(const qpol_policy_t * policy, const qpol_avrule_t * rule,
+ const qpol_type_t ** source);
+
+/**
+ * Get the target type from an av rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the target type.
+ * @param target Pointer in which to store the target type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target will be NULL.
+ */
+ extern int qpol_avrule_get_target_type(const qpol_policy_t * policy, const qpol_avrule_t * rule,
+ const qpol_type_t ** target);
+
+/**
+ * Get the object class from an av rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the object class.
+ * @param obj_class Pointer in which to store the object class.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *obj_class will be NULL.
+ */
+ extern int qpol_avrule_get_object_class(const qpol_policy_t * policy, const qpol_avrule_t * rule,
+ const qpol_class_t ** obj_class);
+
+/**
+ * Get an iterator over the permissions in an av rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the permissions.
+ * @param perms Iterator over items of type char* returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. The caller <b>should call</b>
+ * <b>free() on the strings returned by qpol_iterator_get_item().</b>
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *perms will be NULL.
+ */
+ extern int qpol_avrule_get_perm_iter(const qpol_policy_t * policy, const qpol_avrule_t * rule, qpol_iterator_t ** perms);
+
+/**
+ * Get the rule type value for an av rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the rule type.
+ * @param rule_type Integer in which to store the rule type value.
+ * The value will be one of the QPOL_RULE_* values above.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *rule_type will be 0.
+ */
+ extern int qpol_avrule_get_rule_type(const qpol_policy_t * policy, const qpol_avrule_t * rule, uint32_t * rule_type);
+
+/**
+ * Get the conditional from which an av rule comes. If the rule
+ * is not a conditional rule *cond is set to NULL.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the conditional.
+ * @param cond The conditional returned. (NULL if rule is not conditional)
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *cond will be NULL. If the rule is not conditional
+ * *cond is set to NULL and the function is considered successful.
+ */
+ extern int qpol_avrule_get_cond(const qpol_policy_t * policy, const qpol_avrule_t * rule, const qpol_cond_t ** cond);
+
+/**
+ * Determine if a rule is enabled. Unconditional rules are always enabled.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule to check.
+ * @param is_enabled Integer in which to store the result: set to 1 if enabled
+ * and 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *is_enabled will be 0.
+ */
+ extern int qpol_avrule_get_is_enabled(const qpol_policy_t * policy, const qpol_avrule_t * rule, uint32_t * is_enabled);
+
+/**
+ * Get the list (true or false) in which a conditional rule is. It is
+ * an error to call this function for an unconditional rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule to check.
+ * @param which_list Integer in which to store the result: set to 1 if
+ * rule is in the true list or 0 if in the false list.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *which_list will be 0.
+ */
+ extern int qpol_avrule_get_which_list(const qpol_policy_t * policy, const qpol_avrule_t * rule, uint32_t * which_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/include/qpol/bool_query.h b/libqpol/include/qpol/bool_query.h
new file mode 100644
index 0000000..41c2d26
--- /dev/null
+++ b/libqpol/include/qpol/bool_query.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over booleans.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_BOOL_QUERY_H
+#define QPOL_BOOL_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/policy.h>
+#include <qpol/iterator.h>
+
+ typedef struct qpol_bool qpol_bool_t;
+
+/**
+ * Get the datum for a conditional boolean by name.
+ * @param policy The policy database from which to retrieve the boolean.
+ * @param name The name of the boolean; searching is case sensitive.
+ * @param datum Pointer to set to the boolean's datum entry in the policy.
+ * This memory should not be freed by the user.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *datum will be NULL.
+ */
+ extern int qpol_policy_get_bool_by_name(const qpol_policy_t * policy, const char *name, qpol_bool_t ** datum);
+
+/**
+ * Get an iterator for conditional booleans in the policy.
+ * @param policy The policy database from which to create the iterator.
+ * @param iter Iterator of type qpol_bool_t* returned;
+ * the user is responsible for calling qpol_iterator_destroy to
+ * free memory used. It is also important to note that an iterator
+ * is only valid as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_bool_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a boolean. Values range from 1 to
+ * the number of conditional booleans declared in the policy.
+ * @param policy The policy with which the boolean is associated.
+ * @param datum Boolean datum from which to get the value. Must be non-NULL.
+ * @param value Pointer to integer be set to value. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *value will be 0.
+ */
+ extern int qpol_bool_get_value(const qpol_policy_t * policy, const qpol_bool_t * datum, uint32_t * value);
+
+/**
+ * Get the state of a boolean.
+ * @param policy The policy with which the boolean is associated.
+ * @param datum Boolean datum from which to get the state. Must be non-NULL.
+ * @param state Pointer to the integer to be set to the boolean's state.
+ * Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *state will set to 0 (false).
+ */
+ extern int qpol_bool_get_state(const qpol_policy_t * policy, const qpol_bool_t * datum, int *state);
+
+/**
+ * Set the state of a boolean and update the state of all conditionals
+ * using the boolean.
+ * @param policy The policy with which the boolean is associated.
+ * The state of the policy is changed by this function.
+ * @param datum Boolean datum for which to set the state. Must be non-NULL.
+ * @param state Value to which to set the state of the boolean.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set.
+ */
+ extern int qpol_bool_set_state(qpol_policy_t * policy, qpol_bool_t * datum, int state);
+
+/**
+ * Set the state of a boolean but do not update the state of all conditionals
+ * using the boolean. The caller is responsible for calling
+ * qpol_policy_reevaluate_conds() at a later time to maintain a consistent
+ * state of conditional expressions.
+ * @param policy The policy with which the boolean is associated.
+ * The state of the policy is changed by this function.
+ * @param datum Boolean datum for which to set the state. Must be non-NULL.
+ * @param state Value to which to set the state of the boolean.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set.
+ */
+ extern int qpol_bool_set_state_no_eval(qpol_policy_t * policy, qpol_bool_t * datum, int state);
+
+/**
+ * Get the name which identifies a boolean from its datum.
+ * @param policy The policy with which the boolean is associated.
+ * @param datum Boolean datum for which to get the name. Must be non-NULL.
+ * @param name Pointer to the string in which to store the name.
+ * Must be non-NULL. The caller should not free the string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_bool_get_name(const qpol_policy_t * policy, const qpol_bool_t * datum, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_BOOL_QUERY_H */
diff --git a/libqpol/include/qpol/class_perm_query.h b/libqpol/include/qpol/class_perm_query.h
new file mode 100644
index 0000000..a74357a
--- /dev/null
+++ b/libqpol/include/qpol/class_perm_query.h
@@ -0,0 +1,209 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over
+ * classes, commons, and permissions.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_CLASS_PERM_QUERY_H
+#define QPOL_CLASS_PERM_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_class qpol_class_t;
+ typedef struct qpol_common qpol_common_t;
+
+/* perms */
+/**
+ * Get an iterator over the set of classes which contain a permission
+ * with the name perm. This function does not search for the permission
+ * in the class's inherited common.
+ * @param policy The policy from which to query the classes.
+ * @param perm The name of the permission to be matched. Must be non-NULL.
+ * @param classes The iterator of type qpol_class_t returned;
+ * the user is responsible for calling qpol_iterator_destroy
+ * to free memory used. It is also important to note
+ * that an iterator is only valid as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *classes will be NULL;
+ */
+ extern int qpol_perm_get_class_iter(const qpol_policy_t * policy, const char *perm, qpol_iterator_t ** classes);
+
+/**
+ * Get an iterator over the set of commons which contain a permission
+ * with the name perm.
+ * @param policy The policy from which to query the commons.
+ * @param perm The name of the permission to be matched. Must be non-NULL.
+ * @param commons The iterator of type qpol_common_t returned;
+ * the user is responsible for calling qpol_iterator_destroy
+ * to free memory used. It is also important to note
+ * that an iterator is only valid as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *commons will be NULL;
+ */
+ extern int qpol_perm_get_common_iter(const qpol_policy_t * policy, const char *perm, qpol_iterator_t ** commons);
+
+/* classes */
+/**
+ * Get an object class by name.
+ * @param policy The policy from which to get the class.
+ * @param name The name of the class; searching is case sensitive.
+ * @param obj_class Pointer in which to store the class.
+ * Caller should not free this pointer.
+ * @return Returns 0 for success and < 0 for failure; if the call fails,
+ * errno will be set and *obj_class will be NULL;
+ */
+ extern int qpol_policy_get_class_by_name(const qpol_policy_t * policy, const char *name, const qpol_class_t ** obj_class);
+
+/**
+ * Get an iterator for object classes in the policy.
+ * @param policy The policy database from which to create the iterator.
+ * @param iter Iterator of type qpol_class_t* returned; the user
+ * is responsible for calling qpol_iterator_destroy to free memory used.
+ * It is also important to note that an iterator is only valid as long
+ * as the policy is unchanged.
+ * @return Returns 0 for success and < 0 for failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+*/
+ extern int qpol_policy_get_class_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a class. Values range from 1 to
+ * the number of object classes declared in the policy.
+ * @param policy The policy with which the class is associated.
+ * @param obj_class Class from which to get the value. Must be non-NULL.
+ * @param value Pointer to the integer to be set to value. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *value will be 0.
+ */
+ extern int qpol_class_get_value(const qpol_policy_t * policy, const qpol_class_t * obj_class, uint32_t * value);
+
+/**
+ * Get the common used by a class.
+ * @param policy The policy with which the class is associated.
+ * @param obj_class Class from which to get the value. Must be non-NULL.
+ * @param common Pointer to the common associated with this
+ * class; the caller should not free this pointer. Not all classes have an
+ * associated common so it is possible for *common to be NULL on success.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *common will be NULL.
+ */
+ extern int qpol_class_get_common(const qpol_policy_t * policy, const qpol_class_t * obj_class,
+ const qpol_common_t ** common);
+
+/**
+ * Get an iterator for the set of (unique) permissions for a class.
+ * @param policy The policy with which the class is associated.
+ * @param obj_class The class from which to get the permissions.
+ * @param perms Iterator of type char* returned for the list of
+ * permissions for this class. The list only contains permissions unique
+ * to the class not those included from a common. The iterator is only
+ * valid as long as the policy is unchanged; the caller is responsible
+ * for calling qpol_iterator_destroy to free memory used.
+ * @return Returns 0 for success and < 0 for failure; if the call fails,
+ * errno will be set and *perms will be NULL.
+ */
+ extern int qpol_class_get_perm_iter(const qpol_policy_t * policy, const qpol_class_t * obj_class, qpol_iterator_t ** perms);
+
+/**
+ * Get the name which identifies a class.
+ * @param policy The policy with which the class is associated.
+ * @param datum Class for which to get the name. Must be non-NULL.
+ * @param name Pointer to the string in which to store the name.
+ * Must be non-NULL. Caller should not free the string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_class_get_name(const qpol_policy_t * policy, const qpol_class_t * obj_class, const char **name);
+
+/* commons */
+/**
+ * Get a common by name.
+ * @param policy from which to get the common.
+ * @param name The name of the common; searching is case sensitive.
+ * @param common Pointer in which to store the common.
+ * Caller should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *common will be NULL.
+ */
+ extern int qpol_policy_get_common_by_name(const qpol_policy_t * policy, const char *name, const qpol_common_t ** common);
+
+/**
+ * Get an iterator for commons in the policy
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator of type qpol_common_t* returned;
+ * the user is responsible for calling qpol_iterator_destroy to
+ * free memory used. It is also important to note that an iterator is
+ * only valid as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_common_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a common. Values range from 1 to
+ * the number of commons declared in the policy.
+ * @param policy The policy associated with the common.
+ * @param common The common from which to get the value.
+ * @param value Pointer to the integer to be set to value. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *value will be 0.
+ */
+ extern int qpol_common_get_value(const qpol_policy_t * policy, const qpol_common_t * common, uint32_t * value);
+
+/**
+ * Get an iterator for the permissions included in a common.
+ * @param policy The policy associated with the common.
+ * @param common The common from which to get permissions.
+ * @param perms Iterator of type char* returned for the list of
+ * permissions for this common. The iterator is only valid as long
+ * as the policy is unchanged; the caller is responsible for calling
+ * qpol_iterator_destroy to free memory used.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *perms will be NULL.
+ */
+ extern int qpol_common_get_perm_iter(const qpol_policy_t * policy, const qpol_common_t * common, qpol_iterator_t ** perms);
+
+/**
+ * Get the name which identifies a common.
+ * @param policy associated with the common.
+ * @param common The common from which to get the name.
+ * @param name Pointer in which to store the name. Must be non-NULL;
+ * the caller should not free the string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_common_get_name(const qpol_policy_t * policy, const qpol_common_t * common, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_CLASS_PERM_QUERY_H */
diff --git a/libqpol/include/qpol/cond_query.h b/libqpol/include/qpol/cond_query.h
new file mode 100644
index 0000000..e2722ef
--- /dev/null
+++ b/libqpol/include/qpol/cond_query.h
@@ -0,0 +1,198 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over
+ * conditionals
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_COND_QUERY_H
+#define QPOL_COND_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/bool_query.h>
+#include <qpol/iterator.h>
+
+ typedef struct qpol_cond qpol_cond_t;
+ typedef struct qpol_cond_expr_node qpol_cond_expr_node_t;
+
+/**
+ * Get an iterator over all conditionals in a policy.
+ * It is an error to call this function if rules are not loaded.
+ * @param policy Policy from which to get the conditionals.
+ * @param iter Iterator over items of type qpol_cond_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used in this iterator.
+ * It is important to node that this iterator is only valid as long as
+ * the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_cond_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator over the nodes in a conditional expression.
+ * Each node represents a single token of the expression in RPN.
+ * @param policy The policy associated with the conditional.
+ * @param cond The conditional from which to get the expression.
+ * @param iter Iterator over items of type qpol_cond_expr_node_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used in this iterator.
+ * It is important to node that this iterator is only valid as long as
+ * the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_cond_get_expr_node_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, qpol_iterator_t ** iter);
+
+/* flags for conditional rules */
+#define QPOL_COND_RULE_LIST 0x00000001
+#define QPOL_COND_RULE_ENABLED 0x00000002
+
+/**
+ * Get an iterator over all av rules in a conditional's true list
+ * of a rule type in rule_type_mask.
+ * @param policy The policy associated with the conditional.
+ * @param cond The conditional from which to get the rules.
+ * @param rule_type_mask Bitwise or'ed set of QPOL_RULE_* values
+ * (see avrule_query.h) to include.
+ * @param iter Iterator over items of type qpol_avrule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_cond_get_av_true_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator over all type rules in a conditional's true list
+ * of a rule type in rule_type_mask.
+ * @param policy The policy associated with the conditional.
+ * @param cond The conditional from which to get the rules.
+ * @param rule_type_mask Bitwise or'ed set of QPOL_RULE_TYPE_* values
+ * (see terule_query.h) to include.
+ * @param iter Iterator over items of type qpol_terule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_cond_get_te_true_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator over all av rules in a conditional's false list
+ * of a rule type in rule_type_mask.
+ * @param policy The policy associated with the conditional.
+ * @param cond The conditional from which to get the rules.
+ * @param rule_type_mask Bitwise or'ed set of QPOL_RULE_* values
+ * (see avrule_query.h) to include.
+ * @param iter Iterator over items of type qpol_avrule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_cond_get_av_false_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator over all type rules in a conditional's false list
+ * of a rule type in rule_type_mask.
+ * @param policy The policy associated with the conditional.
+ * @param cond The conditional from which to get the rules.
+ * @param rule_type_mask Bitwise or'ed set of QPOL_RULE_TYPE_* values
+ * (see terule_query.h) to include.
+ * @param iter Iterator over items of type qpol_avrule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_cond_get_te_false_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter);
+
+/**
+ * Evaluate the expression of a conditional using current boolean values
+ * in the policy.
+ * @param policy The policy associated with the conditional.
+ * @param cond The conditional to evaluate.
+ * @param is_true Integer in which to store the result of evaluating the
+ * the expression, will be 1 if true and 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *is_true will be 0.
+ */
+ extern int qpol_cond_eval(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t * is_true);
+
+/* values identical to conditional.h in sepol */
+#define QPOL_COND_EXPR_BOOL 1 /* plain bool */
+#define QPOL_COND_EXPR_NOT 2 /* !bool */
+#define QPOL_COND_EXPR_OR 3 /* bool || bool */
+#define QPOL_COND_EXPR_AND 4 /* bool && bool */
+#define QPOL_COND_EXPR_XOR 5 /* bool ^ bool */
+#define QPOL_COND_EXPR_EQ 6 /* bool == bool */
+#define QPOL_COND_EXPR_NEQ 7 /* bool != bool */
+
+/**
+ * Get the type of an expression node.
+ * @param policy The policy associated with the conditional expression.
+ * @param node The node from which to get the expression type.
+ * @param expr_type Integer in which to store the expression type;
+ * the value will be one of QPOL_COND_EXPR_* above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *expr_type will be 0.
+ */
+ extern int qpol_cond_expr_node_get_expr_type(const qpol_policy_t * policy, const qpol_cond_expr_node_t * node,
+ uint32_t * expr_type);
+
+/**
+ * Get the boolean used in an expression node. This is only valid
+ * when the node's expression type is QPOL_COND_EXPR_BOOL.
+ * @param policy The policy associated with the conditional experssion.
+ * @param node The node from which to get the boolean. It is an error
+ * to call this function if the node is not of type QPOL_COND_EXPR_BOOL.
+ * @param cond_bool Pointer in which to store the boolean.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *cond_bool will be NULL.
+ */
+ extern int qpol_cond_expr_node_get_bool(const qpol_policy_t * policy, const qpol_cond_expr_node_t * node,
+ qpol_bool_t ** cond_bool);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_COND_QUERY_H */
diff --git a/libqpol/include/qpol/constraint_query.h b/libqpol/include/qpol/constraint_query.h
new file mode 100644
index 0000000..f5cd7bf
--- /dev/null
+++ b/libqpol/include/qpol/constraint_query.h
@@ -0,0 +1,262 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over
+ * constraints
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_CONSTRAINT_QUERY_H
+#define QPOL_CONSTRAINT_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/iterator.h>
+#include <qpol/class_perm_query.h>
+
+ typedef struct qpol_constraint qpol_constraint_t;
+ typedef struct qpol_validatetrans qpol_validatetrans_t;
+ typedef struct qpol_constraint_expr_node qpol_constraint_expr_node_t;
+
+/**
+ * Get an iterator for the constraints in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_constraint_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. <b>The caller must also call free()
+ * on items returned by qpol_iterator_get_item() when using this iterator.</b>
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_constraint_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the object class from a constraint.
+ * @param policy The policy associated with the constraint.
+ * @param constr The constraint from which to get the class.
+ * @param obj_class Pointer in which to store the object class.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *obj_class will be NULL.
+ */
+ extern int qpol_constraint_get_class(const qpol_policy_t * policy, const qpol_constraint_t * constr,
+ const qpol_class_t ** obj_class);
+
+/**
+ * Get an iterator over the permissions in a constraint.
+ * @param policy The policy from which the constraint comes.
+ * @param constr The constraint from which to get the permissions.
+ * @param iter Iterator over items of type char*.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. The caller <b>should call</b>
+ * <b>free() on the strings returned by qpol_iterator_get_item().</b>
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_constraint_get_perm_iter(const qpol_policy_t * policy, const qpol_constraint_t * constr,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator over the nodes in a constraint expression.
+ * @param policy The policy from which the constraint comes.
+ * @param constr The constraint from which to get the expression.
+ * @param iter Iterator over items of type qpol_constraint_expr_node_t.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. The caller should not
+ * free the items returned by qpol_iterator_get_item().
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_constraint_get_expr_iter(const qpol_policy_t * policy, const qpol_constraint_t * constr,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator for the validatetrans statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_validatetrans_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. <b>The caller must also call free()
+ * on items returned by qpol_iterator_get_item() when using this iterator.</b>
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_validatetrans_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the object class from a validatetrans statement.
+ * @param policy The policy associated with the validatetrans statement.
+ * @param vtrans The validatetrans statement from which to get the class.
+ * @param obj_class Pointer in which to store the object class.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *obj_class will be NULL.
+ */
+ extern int qpol_validatetrans_get_class(const qpol_policy_t * policy, const qpol_validatetrans_t * vtrans,
+ const qpol_class_t ** obj_class);
+
+/**
+ * Get an iterator over the nodes in a validatetrans expression.
+ * @param policy The policy from which the validatetrans statement comes.
+ * @param vtrans The validatetrans statement from which to get the expression.
+ * @param iter Iterator over items of type qpol_constraint_expr_node_t.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. The caller should not
+ * free the items returned by qpol_iterator_get_item().
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_validatetrans_get_expr_iter(const qpol_policy_t * policy, const qpol_validatetrans_t * vtrans,
+ qpol_iterator_t ** iter);
+
+/* expr_type values */
+#define QPOL_CEXPR_TYPE_NOT 1
+#define QPOL_CEXPR_TYPE_AND 2
+#define QPOL_CEXPR_TYPE_OR 3
+#define QPOL_CEXPR_TYPE_ATTR 4
+#define QPOL_CEXPR_TYPE_NAMES 5
+
+/**
+ * Get the code for the expression type of by an expression node.
+ * @patam policy The policy from which the expression comes.
+ * @param expr The expression node from which to get the expression type.
+ * @param expr_type Integer in which to store the expression type; the value
+ * will be one of QPOL_CEXPR_TYPE_* above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *op will be 0.
+ */
+ extern int qpol_constraint_expr_node_get_expr_type(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ uint32_t * expr_type);
+
+/* attr values */
+#define QPOL_CEXPR_SYM_USER 1
+#define QPOL_CEXPR_SYM_ROLE 2
+#define QPOL_CEXPR_SYM_TYPE 4
+#define QPOL_CEXPR_SYM_TARGET 8
+#define QPOL_CEXPR_SYM_XTARGET 16
+#define QPOL_CEXPR_SYM_L1L2 32
+#define QPOL_CEXPR_SYM_L1H2 64
+#define QPOL_CEXPR_SYM_H1L2 128
+#define QPOL_CEXPR_SYM_H1H2 256
+#define QPOL_CEXPR_SYM_L1H1 512
+#define QPOL_CEXPR_SYM_L2H2 1024
+
+/**
+ * Get the code for the symbol type used by an expression node.
+ * @param policy The policy from which the expression comes.
+ * @param expr The expression node from which to get the symbol type.
+ * Must be of expression type QPOL_CEXPR_TYPE_ATTR or QPOL_CEXPR_TYPE_NAMES.
+ * @param sym_type Integer in which to store the symbol type; the value
+ * will be a bitwise or'ed set of QPOL_CEXPR_SYM_* above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *sym_type will be 0.
+ */
+ extern int qpol_constraint_expr_node_get_sym_type(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ uint32_t * sym_type);
+
+/* op values */
+#define QPOL_CEXPR_OP_EQ 1
+#define QPOL_CEXPR_OP_NEQ 2
+#define QPOL_CEXPR_OP_DOM 3
+#define QPOL_CEXPR_OP_DOMBY 4
+#define QPOL_CEXPR_OP_INCOMP 5
+
+/**
+ * Get the operator used by an expression node.
+ * @param policy The policy from which the expression comes.
+ * @param expr The expression node from which to get the operator.
+ * Must be of expression type QPOL_CEXPR_TYPE_ATTR or QPOL_CEXPR_TYPE_NAMES.
+ * @param op Integer in which to store the operator; the value
+ * will be one of QPOL_CEXPR_OP_* above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *op will be 0.
+ */
+ extern int qpol_constraint_expr_node_get_op(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ uint32_t * op);
+
+/**
+ * Get an iterator of the names in an expression node.
+ * @param policy The policy from which the expression comes.
+ * @param expr The expression node from which to create the iterator.
+ * Must be of expression type QPOL_CEXPR_TYPE_NAMES.
+ * @param iter Iterator over items of type char* returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. <b>The caller should call
+ * free() on the strings returned by qpol_iterator_get_item().</b>
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * In the case where the symbol names are types, the name of a subtracted
+ * type will be prepended with a '-' character.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_constraint_expr_node_get_names_iter(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator for the constraints on a class.
+ * @param policy The policy associated with the class.
+ * @param obj_class The class from which to create the iterator.
+ * @param constr Iterator over items of type qpol_constraint_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. <b>The caller must also call free()
+ * on items returned by qpol_iterator_get_item() when using this iterator.</b>
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *constr will be NULL.
+ */
+ extern int qpol_class_get_constraint_iter(const qpol_policy_t * policy, const qpol_class_t * obj_class,
+ qpol_iterator_t ** constr);
+
+/**
+ * Get an iterator for the validatetrans statements for a class.
+ * @param policy The policy associated with the class.
+ * @param obj_class The class from which to create the iterator.
+ * @param vtrans Iterator over items of type qpol_validatetrans_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. <b>The caller must also call free()
+ * on items returned by qpol_iterator_get_item() when using this iterator.</b>
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *vtrans will be NULL.
+ */
+ extern int qpol_class_get_validatetrans_iter(const qpol_policy_t * policy, const qpol_class_t * obj_class,
+ qpol_iterator_t ** vtrans);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_CONSTRAINT_QUERY_H */
diff --git a/libqpol/include/qpol/context_query.h b/libqpol/include/qpol/context_query.h
new file mode 100644
index 0000000..d152d79
--- /dev/null
+++ b/libqpol/include/qpol/context_query.h
@@ -0,0 +1,93 @@
+/**
+ * @file
+ * Defines the public interface accessing contexts.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_CONTEXT_QUERY_H
+#define QPOL_CONTEXT_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/policy.h>
+#include <qpol/user_query.h>
+#include <qpol/role_query.h>
+#include <qpol/type_query.h>
+#include <qpol/mls_query.h>
+
+ typedef struct qpol_context qpol_context_t;
+
+/**
+ * Get the datum for the user field of a context.
+ * @param policy The policy associated with the context.
+ * @param context The context from which to get the user.
+ * @param user Pointer in which to store the user datum.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *user will be NULL.
+ */
+ extern int qpol_context_get_user(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_user_t ** user);
+
+/**
+ * Get the datum for the role field of a context.
+ * @param policy The policy associated with the context.
+ * @param context The context from which to get the role.
+ * @param role Pointer in which to store the role datum.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *role will be NULL.
+ */
+ extern int qpol_context_get_role(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_role_t ** role);
+
+/**
+ * Get the datum for the type field of a context.
+ * @param policy The policy associated with the context.
+ * @param context The context from which to get the type.
+ * @param type Pointer in which to store the type datum.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *type will be NULL.
+ */
+ extern int qpol_context_get_type(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_type_t ** type);
+
+/**
+ * Get the datum for the MLS range field of a context.
+ * @param policy The policy associated with the context.
+ * @param context The context from which to get the MLS range.
+ * @param range Pointer in which to store the MLS range.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *range will be NULL.
+ */
+ extern int qpol_context_get_range(const qpol_policy_t * policy, const qpol_context_t * context,
+ const qpol_mls_range_t ** range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_CONTEXT_QUERY_H */
diff --git a/libqpol/include/qpol/fs_use_query.h b/libqpol/include/qpol/fs_use_query.h
new file mode 100644
index 0000000..c45ae44
--- /dev/null
+++ b/libqpol/include/qpol/fs_use_query.h
@@ -0,0 +1,115 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over fs_use statements.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_FS_USE_QUERY_H
+#define QPOL_FS_USE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+
+ typedef struct qpol_fs_use qpol_fs_use_t;
+
+/**
+ * Get a fs_use statement by file system name.
+ * @param policy The policy from which to get the fs_use statement.
+ * @param name The name of the file system.
+ * @param ocon Pointer in which to store the fs_use statement.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *ocon will be NULL.
+ */
+ extern int qpol_policy_get_fs_use_by_name(const qpol_policy_t * policy, const char *name, const qpol_fs_use_t ** ocon);
+
+/**
+ * Get an iterator for the fs_use statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_fs_use_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_fs_use_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the file system name from a fs_use statement.
+ * @param policy The policy associated with the fs_use statement.
+ * @param ocon The fs_use statement from which to get the name.
+ * @param name Pointer to the string in which to store the name.
+ * The caller should not free this string.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_fs_use_get_name(const qpol_policy_t * policy, const qpol_fs_use_t * ocon, const char **name);
+
+/* The defines QPOL_FS_USE_XATTR through QPOL_FS_USE_NONE are
+ * copied from sepol/policydb/services.h.
+ * QPOL_FS_USE_PSID is an extension to support v12 policies. */
+#define QPOL_FS_USE_XATTR 1U
+#define QPOL_FS_USE_TRANS 2U
+#define QPOL_FS_USE_TASK 3U
+#define QPOL_FS_USE_GENFS 4U
+#define QPOL_FS_USE_NONE 5U
+#define QPOL_FS_USE_PSID 6U
+
+/**
+ * Get the labeling behavior from a fs_use statement.
+ * @param policy The policy associated with the fs_use statement.
+ * @param ocon The fs_use statement from which to get the behavior.
+ * @param behavior Pointer to be set to the value of the labeling behavior.
+ * The value will be one of the QPOL_FS_USE_* values defined above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *behavior will be 0.
+ */
+ extern int qpol_fs_use_get_behavior(const qpol_policy_t * policy, const qpol_fs_use_t * ocon, uint32_t * behavior);
+
+/**
+ * Get the context from a fs_use statement.
+ * @param policy The policy associated with the fs_use statement.
+ * @param ocon The fs_use statement from which to get the context.
+ * @param context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL. It is considered an
+ * error to call this function if behavior is QPOL_FS_USE_PSID.
+ */
+ extern int qpol_fs_use_get_context(const qpol_policy_t * policy, const qpol_fs_use_t * ocon,
+ const qpol_context_t ** context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_FS_USE_QUERY_H */
diff --git a/libqpol/include/qpol/genfscon_query.h b/libqpol/include/qpol/genfscon_query.h
new file mode 100644
index 0000000..f89795a
--- /dev/null
+++ b/libqpol/include/qpol/genfscon_query.h
@@ -0,0 +1,128 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over genfscon statements.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 20062007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_OCON_QUERY_H
+#define QPOL_OCON_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_genfscon qpol_genfscon_t;
+
+/**
+ * Get a genfscon statement by file system name and path.
+ * @param policy The policy from which to get the genfscon statement.
+ * @param name The name of the file system.
+ * @param path The path relative to the filesystem mount point.
+ * @param genfscon Pointer in which to store the genfscon statement.
+ * The caller should call free() on this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *genfscon will be NULL.
+ */
+ extern int qpol_policy_get_genfscon_by_name(const qpol_policy_t * policy, const char *name, const char *path,
+ qpol_genfscon_t ** genfscon);
+
+/**
+ * Get an iterator for the genfscon statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_genfscon_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator. The caller must also call free()
+ * on items returned by qpol_iterator_get_item() when using this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_genfscon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the file system name from a gefscon statement.
+ * @param policy The policy associated with the genfscon statement.
+ * @param genfs The genfscon statement from which to get the name.
+ * @param name Pointer to th string in which to store the name.
+ * The caller should not free this string.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_genfscon_get_name(const qpol_policy_t * policy, const qpol_genfscon_t * genfs, const char **name);
+
+/**
+ * Get the relative path from a gefscon statement.
+ * @param policy The policy associated with the genfscon statement.
+ * @param genfs The genfscon statement from which to get the path.
+ * @param path Pointer to the string in which to store the path.
+ * The caller should not free this string.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *path will be NULL.
+ */
+ extern int qpol_genfscon_get_path(const qpol_policy_t * policy, const qpol_genfscon_t * genfs, const char **path);
+
+/* values from flask do not change */
+#define QPOL_CLASS_ALL 0U
+#define QPOL_CLASS_BLK_FILE 11U
+#define QPOL_CLASS_CHR_FILE 10U
+#define QPOL_CLASS_DIR 7U
+#define QPOL_CLASS_FIFO_FILE 13U
+#define QPOL_CLASS_FILE 6U
+#define QPOL_CLASS_LNK_FILE 9U
+#define QPOL_CLASS_SOCK_FILE 12U
+
+/**
+ * Get the object class from a genfscon statement.
+ * @param policy The policy associated with the genfscon statement.
+ * @param genfs The genfscon statement from which to get the path.
+ * @param obj_class Pointer in which to store the integer code for the
+ * object class. See QPOL_CLASS_* defines above for values.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *class will be 0.
+ */
+ extern int qpol_genfscon_get_class(const qpol_policy_t * policy, const qpol_genfscon_t * genfs, uint32_t * obj_class);
+
+/**
+ * Get the context from a path component of a genfscon statement.
+ * @param policy The policy associated with the genfscon statement.
+ * @param genfscon The genfscon statement from which to get
+ * the context.
+ * @param context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL.
+ */
+ extern int qpol_genfscon_get_context(const qpol_policy_t * policy, const qpol_genfscon_t * genfscon,
+ const qpol_context_t ** context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_OCON_QUERY_H */
diff --git a/libqpol/include/qpol/isid_query.h b/libqpol/include/qpol/isid_query.h
new file mode 100644
index 0000000..56ff4d2
--- /dev/null
+++ b/libqpol/include/qpol/isid_query.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over initial SIDs.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_ISID_QUERY_H
+#define QPOL_ISID_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_isid qpol_isid_t;
+
+/**
+ * Get an initial SID statement by name.
+ * @param policy The policy from which to get the initial SID statement.
+ * @param name The name of the initial SID.
+ * @param ocon Pointer in which to store the initial SID.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *ocon will be NULL.
+ */
+ extern int qpol_policy_get_isid_by_name(const qpol_policy_t * policy, const char *name, const qpol_isid_t ** ocon);
+
+/**
+ * Get an iterator for the initial SID statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_isid_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_isid_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the name from an initial SID statement.
+ * @param policy The policy associated with the initial SID.
+ * @param ocon The initial SID from which to get the name.
+ * @param name Pointer to the string in which to store the name.
+ * The caller should not free this string.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_isid_get_name(const qpol_policy_t * policy, const qpol_isid_t * ocon, const char **name);
+
+/**
+ * Get the context from an initial SID statement.
+ * @param policy The policy associated with the inital SID.
+ * @param ocon The initial SID from which to get the context.
+ * @param context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL.
+ */
+ extern int qpol_isid_get_context(const qpol_policy_t * policy, const qpol_isid_t * ocon, const qpol_context_t ** context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_ISID_QUERY_H */
diff --git a/libqpol/include/qpol/iterator.h b/libqpol/include/qpol/iterator.h
new file mode 100644
index 0000000..e7bb8be
--- /dev/null
+++ b/libqpol/include/qpol/iterator.h
@@ -0,0 +1,95 @@
+/**
+ * @file
+ * Defines the public API for qpol_iterator; this structure
+ * is used when requesting lists of components from the policy
+ * database.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_ITERATOR_H
+#define QPOL_ITERATOR_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+
+ struct qpol_iterator;
+ typedef struct qpol_iterator qpol_iterator_t;
+
+/**
+ * Free memory used by the iterator.
+ * @param iter Pointer to the iterator to be freed; frees all
+ * memory used by the iterator and the iterator itself. On returning
+ * *iter will be NULL.
+ */
+ extern void qpol_iterator_destroy(qpol_iterator_t ** iter);
+
+/**
+ * Get the item at the current position of the iterator.
+ * @param iter The iterator from which to get the item.
+ * @param item Pointer in which to store the current item; the caller is
+ * responsible for safely casting this pointer. Unless specifically
+ * noted by the function creating the iterator, the item set
+ * by this function should not be freed. If the iterator is at
+ * the end (i.e. all items have been traversed) *item will be NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *item will be NULL.
+ */
+ extern int qpol_iterator_get_item(const qpol_iterator_t * iter, void **item);
+
+/**
+ * Advance the iterator to the next item.
+ * @param iter The iterator to advance; internal state data will change.
+ * @return Returns 0 on success and < 0 on failure; advancing an
+ * iterator that is at the end fails (and returns < 0). If the call fails,
+ * errno will be set.
+ */
+ extern int qpol_iterator_next(qpol_iterator_t * iter);
+
+/**
+ * Determine if an iterator is at the end.
+ * @param iter The iterator to check.
+ * @return Returns non-zero if the current position of the iterator
+ * is at the end of the list (i.e. past the last valid item) and
+ * zero in any other case. If there is an error determining if
+ * the iterator is at the end then non-zero will be returned.
+ */
+ extern int qpol_iterator_end(const qpol_iterator_t * iter);
+
+/**
+ * Get the total number of items in the list traversed by the iterator.
+ * @param iter The iterator from which to get the number of items.
+ * @param size Pointer in which to store the number of items.
+ * Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *size will be 0.
+ */
+ extern int qpol_iterator_get_size(const qpol_iterator_t * iter, size_t * size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_ITERATOR */
diff --git a/libqpol/include/qpol/mls_query.h b/libqpol/include/qpol/mls_query.h
new file mode 100644
index 0000000..c5fadc5
--- /dev/null
+++ b/libqpol/include/qpol/mls_query.h
@@ -0,0 +1,260 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over
+ * policy MLS components.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_MLS_QUERY_H
+#define QPOL_MLS_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+ typedef struct qpol_level qpol_level_t;
+ typedef struct qpol_cat qpol_cat_t;
+ typedef struct qpol_mls_range qpol_mls_range_t;
+ typedef struct qpol_mls_level qpol_mls_level_t;
+
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+/* level */
+/**
+ * Get datum for a security level by (sensitivity) name.
+ * @param policy The policy from which to get the level datum.
+ * @param name The sensitivity name; searching is case sensitive.
+ * @param datum Pointer in which to store the level datum. Must be non-NULL.
+ * The caller should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *datum will be NULL.
+ */
+ extern int qpol_policy_get_level_by_name(const qpol_policy_t * policy, const char *name, const qpol_level_t ** datum);
+
+/**
+ * Get an iterator for the levels in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator of type qpol_level_t* returned;
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used; it is important to note that the iterator
+ * is valid only as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_level_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Determine if a level is an alias for another level.
+ * @param policy The policy associated with the level datum.
+ * @param datum The level to check.
+ * @param isalias Pointer in which to store the alias state of
+ * the level. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *isalias will be 0 (false).
+ */
+ extern int qpol_level_get_isalias(const qpol_policy_t * policy, const qpol_level_t * datum, unsigned char *isalias);
+
+/**
+ * Get the integer value associated with the sensitivity of a level.
+ * Values range from 1 to the number of declared levels in the policy.
+ * @param policy The policy associated with the level.
+ * @param datum The level datum from which to get the value.
+ * @param value Pointer to the integer to set to value. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *value will be 0.
+ */
+ extern int qpol_level_get_value(const qpol_policy_t * policy, const qpol_level_t * datum, uint32_t * value);
+
+/**
+ * Get an iterator for the categories associated with a level.
+ * @param policy The policy associated with the level.
+ * @param datum The level from which to get the categories.
+ * @param cats Iterator of type qpol_cat_t* returned;
+ * the categories are in policy order. The caller is responsible
+ * for calling qpol_iterator_destroy to free memory used;
+ * it is important to note that the iterator is valid only as long
+ * as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *cats will be NULL.
+ */
+ extern int qpol_level_get_cat_iter(const qpol_policy_t * policy, const qpol_level_t * datum, qpol_iterator_t ** cats);
+
+/**
+ * Get the name which identifies a level from its datum.
+ * @param policy The policy associated with the level.
+ * @param datum The level from which to get the name.
+ * @param name Pointer in which to store the name. Must be non-NULL;
+ * the caller should not free this string. If the sensitivity is an
+ * alias, the primary name will be returned.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_level_get_name(const qpol_policy_t * policy, const qpol_level_t * datum, const char **name);
+
+/**
+ * Get an iterator for the list of aliases for a level.
+ * @param policy The policy associated with the level.
+ * @param datum The level for which to get aliases.
+ * @param aliases Iterator of type char* returned; the caller is
+ * responsible for calling qpol_iterator_destroy to free
+ * memory used; it is important to note that the iterator is valid
+ * only as long as the policy is unchanged. If a level has no aliases,
+ * the iterator will be at end and have size 0.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *aliases will be NULL.
+ */
+ extern int qpol_level_get_alias_iter(const qpol_policy_t * policy, const qpol_level_t * datum, qpol_iterator_t ** aliases);
+
+/* cat */
+/**
+ * Get the datum for a category by name.
+ * @param policy The policy from which to get the category.
+ * @param name The name of the category; searching is case sensitive.
+ * @param datum Pointer in which to store the datum; the caller should
+ * not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *datum will be NULL.
+ */
+ extern int qpol_policy_get_cat_by_name(const qpol_policy_t * policy, const char *name, const qpol_cat_t ** datum);
+
+/**
+ * Get an iterator for the categories declared in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator of type qpol_cat_t* returned;
+ * the categories are in policy order. The caller is responsible
+ * for calling qpol_iterator_destroy to free the memory used;
+ * it is important to note that the iterator is only valid as
+ * long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_cat_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a category. Values range
+ * from 1 to the number of categories declared in the policy.
+ * @param policy The policy associated with the category.
+ * @param datum The category for which to get the value.
+ * @param value Pointer to the integer to set to value. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *value will be 0.
+ */
+ extern int qpol_cat_get_value(const qpol_policy_t * policy, const qpol_cat_t * datum, uint32_t * value);
+
+/**
+ * Determine if a category is an alias for another category.
+ * @param policy The policy associated with the category.
+ * @param datum The category to check.
+ * @param isalias Pointer in which to store the alias state of the
+ * category; must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *isalias will be 0 (false).
+ */
+ extern int qpol_cat_get_isalias(const qpol_policy_t * policy, const qpol_cat_t * datum, unsigned char *isalias);
+
+/**
+ * Get the name which identifies a category from its datum.
+ * @param policy The policy associated with the category.
+ * @param datum The category from which to get the name.
+ * @param name Pointer in which to store the name. Must be non-NULL;
+ * the caller should not free the string. If the category is an alias
+ * the primary name will be returned.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_cat_get_name(const qpol_policy_t * policy, const qpol_cat_t * datum, const char **name);
+
+/**
+ * Get an iterator for the list of aliases for a category.
+ * @param policy The policy associated with the category.
+ * @param datum The category for which to get aliases.
+ * @param aliases Iterator of type char* returned; the caller is
+ * responsible for calling qpol_iterator_destroy to free
+ * memory used; it is important to note that the iterator is valid
+ * only as long as the policy is unchanged. If a category has no aliases,
+ * the iterator will be at end and have size 0.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *aliases will be NULL.
+ */
+ extern int qpol_cat_get_alias_iter(const qpol_policy_t * policy, const qpol_cat_t * datum, qpol_iterator_t ** aliases);
+
+/* mls range */
+/**
+ * Get the low level from a MLS range.
+ * @param policy The policy associated with the MLS components of range.
+ * @param range The range from which to get the low level.
+ * @param level Pointer in which to store the level; the caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *level will be NULL.
+ */
+ extern int qpol_mls_range_get_low_level(const qpol_policy_t * policy, const qpol_mls_range_t * range,
+ const qpol_mls_level_t ** level);
+
+/**
+ * Get the high level from a MLS range.
+ * @param policy The policy associated with the MLS components of range.
+ * @param range The range from which to get the high level.
+ * @param level Pointer in which to store the level; the caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *level will be NULL.
+ */
+ extern int qpol_mls_range_get_high_level(const qpol_policy_t * policy, const qpol_mls_range_t * range,
+ const qpol_mls_level_t ** level);
+
+/* mls_level */
+/**
+ * Get the name of the sensitivity from a MLS level.
+ * @param policy The policy associated with the MLS components of level.
+ * @param level The level from which to get the sensitivity name.
+ * @param name Pointer in which to store the name; the caller
+ * should not free this string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_mls_level_get_sens_name(const qpol_policy_t * policy, const qpol_mls_level_t * level, const char **name);
+
+/**
+ * Get an iterator for the categories in a MLS level. The list will be
+ * in policy order.
+ * @param policy The policy associated with the MLS components of level.
+ * @param level The level from which to get the categories.
+ * @param cats Iterator of type qpol_cat_t* returned; the list is in
+ * policy order. The caller is responsible for calling
+ * qpol_iterator_destroy to free memory used; it is important to note
+ * that an iterator is only valid as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *cats will be NULL.
+ */
+ extern int qpol_mls_level_get_cat_iter(const qpol_policy_t * policy, const qpol_mls_level_t * level,
+ qpol_iterator_t ** cats);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_MLS_QUERY_H */
diff --git a/libqpol/include/qpol/mlsrule_query.h b/libqpol/include/qpol/mlsrule_query.h
new file mode 100644
index 0000000..5c4b832
--- /dev/null
+++ b/libqpol/include/qpol/mlsrule_query.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over
+ * range transition rules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_MLSRULE_QUERY_H
+#define QPOL_MLSRULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+
+ typedef struct qpol_range_trans qpol_range_trans_t;
+
+/**
+ * Get an iterator over all range transition rules in a policy.
+ * @param policy Policy from which to get the range transitions.
+ * @param iter Iterator over items of type qpol_range_trans_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_range_trans_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the source type from a range transition rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the source type.
+ * @param source Pointer in which to store the source type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source will be NULL.
+ */
+ extern int qpol_range_trans_get_source_type(const qpol_policy_t * policy, const qpol_range_trans_t * rule,
+ const qpol_type_t ** source);
+
+/**
+ * Get the target type from a range transition rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the target type.
+ * @param target Pointer in which to store the target type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target will be NULL.
+ */
+ extern int qpol_range_trans_get_target_type(const qpol_policy_t * policy, const qpol_range_trans_t * rule,
+ const qpol_type_t ** target);
+
+/**
+ * Get the target class from a range transition rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the target class.
+ * @param target Pointer in which to store the target class.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target will be NULL.
+ */
+ extern int qpol_range_trans_get_target_class(const qpol_policy_t * policy, const qpol_range_trans_t * rule,
+ const qpol_class_t ** target);
+
+/**
+ * Get the range from a range transition rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the range.
+ * @param range Pointer in which to store the range.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *range will be NULL.
+ */
+ extern int qpol_range_trans_get_range(const qpol_policy_t * policy, const qpol_range_trans_t * rule,
+ const qpol_mls_range_t ** range);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_MLSRULE_QUERY_H */
diff --git a/libqpol/include/qpol/module.h b/libqpol/include/qpol/module.h
new file mode 100644
index 0000000..d684c40
--- /dev/null
+++ b/libqpol/include/qpol/module.h
@@ -0,0 +1,125 @@
+/**
+ * @file
+ * Defines the public interface the policy modules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_MODULE_H
+#define QPOL_MODULE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdint.h>
+
+ typedef struct qpol_module qpol_module_t;
+
+#define QPOL_MODULE_UNKNOWN 0
+#define QPOL_MODULE_BASE 1
+#define QPOL_MODULE_OTHER 2
+
+/**
+ * Create a qpol module from a policy package file. Newly created
+ * modules are enabled by default.
+ * @param path The file from which to read the module. This string
+ * will be duplicated.
+ * @param module Pointer in which to store the newly allocated
+ * module. The caller is responsible for calling qpol_module_destroy()
+ * to free memory used by this module.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *module will be NULL.
+ */
+ extern int qpol_module_create_from_file(const char *path, qpol_module_t ** module);
+
+/**
+ * Free all memory used by a qpol module and set it to NULL. Does
+ * nothing if the pointer is already NULL.
+ * @param module Reference pointer to the module to destroy.
+ */
+ extern void qpol_module_destroy(qpol_module_t ** module);
+
+/**
+ * Get the path of the policy package file used to create this module.
+ * @param module The module from which to get the path.
+ * @param path Pointer to the string in which to store the path. <b>The
+ * caller should not free this string.</b>
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *path will be NULL.
+ */
+ extern int qpol_module_get_path(const qpol_module_t * module, const char **path);
+
+/**
+ * Get the name of a module.
+ * @param module The module from which to get the name.
+ * @param name Pointer to the string in which to store the name. <b>The
+ * caller should not free this string.</b> If the module is a base
+ * module the name will be NULL.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_module_get_name(const qpol_module_t * module, const char **name);
+
+/**
+ * Get the version of a module.
+ * @param module The module from which to get the version.
+ * @param version Pointer to string in which to store the version. <b>The
+ * caller should not free this string.</b>
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *version will be NULL.
+ */
+ extern int qpol_module_get_version(const qpol_module_t * module, const char **version);
+
+/**
+ * Get the type of module (base or other).
+ * @param module The module from which to get the type.
+ * @param type Pointer to integer in which to store the type. Value
+ * will be one of QPOL_MODULE_BASE or QPOL_MODULE_OTHER.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *type will be QPOL_MODULE_UNKNOWN.
+ */
+ extern int qpol_module_get_type(const qpol_module_t * module, int *type);
+
+/**
+ * Determine if a module is enabled.
+ * @param module The module from which to get the enabled state.
+ * @param enabled Pointer to integer in which to store the state.
+ * Value will be 0 if module is disabled and non-zero if enabled.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *enabled will be 0.
+ */
+ extern int qpol_module_get_enabled(const qpol_module_t * module, int *enabled);
+
+/**
+ * Enable or disable a module. Note that the caller must still
+ * invoke qpol_policy_rebuild() to update the policy.
+ * @param module The module to enable or disable.
+ * @param enabled Non-zero to enable the module, zero to disable.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the module will remain unchanged.
+ */
+ extern int qpol_module_set_enabled(qpol_module_t * module, int enabled);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/include/qpol/netifcon_query.h b/libqpol/include/qpol/netifcon_query.h
new file mode 100644
index 0000000..b35c914
--- /dev/null
+++ b/libqpol/include/qpol/netifcon_query.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over netifcon statements.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_NETIFCON_QUERY_H
+#define QPOL_NETIFCON_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_netifcon qpol_netifcon_t;
+
+/**
+ * Get a netifcon statement by interface name.
+ * @param policy The policy from which to get the netifcon statement.
+ * @param name The name of the interface.
+ * @param ocon Pointer in which to store the statement returned.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *ocon will be NULL.
+ */
+ extern int qpol_policy_get_netifcon_by_name(const qpol_policy_t * policy, const char *name, const qpol_netifcon_t ** ocon);
+
+/**
+ * Get an iterator for the netifcon statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_netifcon_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_netifcon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the name of the interface from a netifcon statement.
+ * @param policy The policy associated wiht the netifcon statement.
+ * @param ocon The netifcon statement from which to get the name.
+ * @param name Pointer in which to store the interface name. The caller
+ * should not free this string.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_netifcon_get_name(const qpol_policy_t * policy, const qpol_netifcon_t * ocon, const char **name);
+
+/**
+ * Get the message context from a netifcon statement.
+ * @param policy The policy associated with the netifcon statement.
+ * @param ocon The netifcon statement from which to get the message context.
+ * @parma context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL.
+ */
+ extern int qpol_netifcon_get_msg_con(const qpol_policy_t * policy, const qpol_netifcon_t * ocon,
+ const qpol_context_t ** context);
+
+/**
+ * Get the interface context from a netifcon statement.
+ * @param policy The policy associated with the netifcon statement.
+ * @param ocon The netifcon statement from which to get the interface context.
+ * @parma context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL.
+ */
+ extern int qpol_netifcon_get_if_con(const qpol_policy_t * policy, const qpol_netifcon_t * ocon,
+ const qpol_context_t ** context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_NETIFCON_QUERY_H */
diff --git a/libqpol/include/qpol/nodecon_query.h b/libqpol/include/qpol/nodecon_query.h
new file mode 100644
index 0000000..bd889f9
--- /dev/null
+++ b/libqpol/include/qpol/nodecon_query.h
@@ -0,0 +1,132 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over nodecon statements.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_NODECON_QUERY_H
+#define QPOL_NODECON_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+#define QPOL_IPV4 0
+#define QPOL_IPV6 1
+
+ typedef struct qpol_nodecon qpol_nodecon_t;
+
+/**
+ * Get a single nodecon statement by address, mask and protocol.
+ * @param policy The policy from which to get the nodecon statement.
+ * @param addr The IP address of the node, if IPv4 only addr[0] is used.
+ * @param mask The net mask of the node, if IPv4 only mask[0] is used.
+ * @param protocol The protocol used in the address and mask;
+ * set to QPOL_IPV4 for IPv4 and QPOL_IPV6 for IPv6.
+ * @param ocon Pointer in which to store the statement returned.
+ * The caller should call free() to free memory used by this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *ocon will be NULL.
+ */
+ extern int qpol_policy_get_nodecon_by_node(const qpol_policy_t * policy, uint32_t addr[4], uint32_t mask[4],
+ unsigned char protocol, qpol_nodecon_t ** ocon);
+
+/**
+ * Get an iterator for the nodecon statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_nodecon_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator. The caller must also call free()
+ * on items returned by qpol_iterator_get_item() when using this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_nodecon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the IP address from a nodecon statement. Sets protocol to indicate
+ * the number of integers used by the array.
+ * @param policy The policy associated with the nodecon statement.
+ * @param ocon The nodecon statement from which to get the IP address.
+ * @param addr Pointer to the byte array of the IP addressto be set ;
+ * the caller should not free this pointer. The number of integers
+ * in this array is 1 if IPv4 and 4 if IPv6.
+ * @param protocol Pointer to be set to the protocol value; this
+ * will be set to QPOL_IPV4 for IPv4 and QPOL_IPV6 for IPv6.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set, addr will be NULL, and protocol will be 0.
+ */
+ extern int qpol_nodecon_get_addr(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, uint32_t ** addr,
+ unsigned char *protocol);
+
+/**
+ * Get the net mask from a nodecon statement. Sets protocol to indicate
+ * the number of integers used by the array.
+ * @param policy The policy associated with the nodecon statement.
+ * @param ocon The nodecon statement from which to get the net mask.
+ * @param mask Pointer to the byte array of the net mask to be set;
+ * the caller should not free this pointer. The number of integers
+ * in this array is 1 if IPv4 and 4 if IPv6.
+ * @param protocol Pointer to be set to the protocol value; this
+ * will be set to QPOL_IPV4 for IPv4 and QPOL_IPV6 for IPv6.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set, mask will be NULL, and protocol will be 0.
+ */
+ extern int qpol_nodecon_get_mask(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, uint32_t ** mask,
+ unsigned char *protocol);
+
+/**
+ * Get the protocol from a nodecon statement.
+ * @param policy The policy associated with the nodecon statement.
+ * @param ocon The nodecon statement from which to get the protocol.
+ * @param protocol Pointer to be set to the protocol value; this
+ * will be set to QPOL_IPV4 for IPv4 and QPOL_IPV6 for IPv6.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and protocol will be 0.
+ */
+ extern int qpol_nodecon_get_protocol(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, unsigned char *protocol);
+
+/**
+ * Get the context from a nodecon statement.
+ * @param policy The policy associated with the nodecon statement.
+ * @param ocon The nodecon statement from which to get the context.
+ * @param context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL.
+ */
+ extern int qpol_nodecon_get_context(const qpol_policy_t * policy, const qpol_nodecon_t * ocon,
+ const qpol_context_t ** context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_NODECON_QUERY_H */
diff --git a/libqpol/include/qpol/permissive_query.h b/libqpol/include/qpol/permissive_query.h
new file mode 100644
index 0000000..e49d508
--- /dev/null
+++ b/libqpol/include/qpol/permissive_query.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over the permissive types.
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-2009 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_PERMISSIVE_QUERY_H
+#define QPOL_PERMISSIVE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_permissive qpol_permissive_t;
+
+/**
+ * Get an iterator for the permissive types in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_isid_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_permissive_iter(const qpol_policy_t *policy, qpol_iterator_t **iter);
+
+
+/**
+ * Get the name which identifies a permissive type from its datum.
+ * @param policy The policy with which the permissive type is associated.
+ * @param datum Permissive datum for which to get the name. Must be non-NULL.
+ * @param name Pointer to the string in which to store the name.
+ * Must be non-NULL. The caller should not free the string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_permissive_get_name(const qpol_policy_t *policy, const qpol_permissive_t *datum, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_PERMISSIVE_QUERY_H */
diff --git a/libqpol/include/qpol/polcap_query.h b/libqpol/include/qpol/polcap_query.h
new file mode 100644
index 0000000..d8de4a0
--- /dev/null
+++ b/libqpol/include/qpol/polcap_query.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over the policy capabilities
+ *
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2006-2009 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_POLCAP_QUERY_H
+#define QPOL_POLCAP_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_polcap qpol_polcap_t;
+
+/**
+ * Get an iterator for the policay capabilities in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_isid_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_polcap_iter(const qpol_policy_t *policy, qpol_iterator_t **iter);
+
+
+/**
+ * Get the name which identifies a policy capability from its datum.
+ * @param policy The policy with which the policy capability is associated.
+ * @param datum Polcap datum for which to get the name. Must be non-NULL.
+ * @param name Pointer to the string in which to store the name.
+ * Must be non-NULL. The caller should not free the string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_polcap_get_name(const qpol_policy_t *policy, const qpol_polcap_t *datum, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_POLCAP_QUERY_H */
diff --git a/libqpol/include/qpol/policy.h b/libqpol/include/qpol/policy.h
new file mode 100644
index 0000000..ae4ea08
--- /dev/null
+++ b/libqpol/include/qpol/policy.h
@@ -0,0 +1,261 @@
+/**
+ * @file
+ * Defines the public interface the QPol policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_POLICY_H
+#define QPOL_POLICY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdarg.h>
+#include <stdint.h>
+
+ typedef struct qpol_policy qpol_policy_t;
+
+#include <qpol/avrule_query.h>
+#include <qpol/bool_query.h>
+#include <qpol/class_perm_query.h>
+#include <qpol/cond_query.h>
+#include <qpol/constraint_query.h>
+#include <qpol/context_query.h>
+#include <qpol/fs_use_query.h>
+#include <qpol/isid_query.h>
+#include <qpol/iterator.h>
+#include <qpol/genfscon_query.h>
+#include <qpol/mls_query.h>
+#include <qpol/mlsrule_query.h>
+#include <qpol/module.h>
+#include <qpol/netifcon_query.h>
+#include <qpol/nodecon_query.h>
+#include <qpol/permissive_query.h>
+#include <qpol/polcap_query.h>
+#include <qpol/portcon_query.h>
+#include <qpol/rbacrule_query.h>
+#include <qpol/role_query.h>
+#include <qpol/syn_rule_query.h>
+#include <qpol/terule_query.h>
+#include <qpol/type_query.h>
+#include <qpol/user_query.h>
+
+ typedef void (*qpol_callback_fn_t) (void *varg, const struct qpol_policy * policy, int level, const char *fmt,
+ va_list va_args);
+
+#define QPOL_POLICY_UNKNOWN -1
+#define QPOL_POLICY_KERNEL_SOURCE 0
+#define QPOL_POLICY_KERNEL_BINARY 1
+#define QPOL_POLICY_MODULE_BINARY 2
+
+/**
+ * When loading the policy, do not load neverallow rules.
+ */
+#define QPOL_POLICY_OPTION_NO_NEVERALLOWS 0x00000001
+
+/**
+ * When loading the policy, do not load any rules;
+ * this option implies QPOL_POLICY_OPTION_NO_NEVERALLOWS.
+ */
+#define QPOL_POLICY_OPTION_NO_RULES 0x00000002
+
+/**
+ * When loading the policy, attempt to interpret it as the way the
+ * running system would. If the policy is of a version higher than
+ * one supported by the system, then the policy will be downgraded to
+ * the system's maximum value.
+ */
+#define QPOL_POLICY_OPTION_MATCH_SYSTEM 0x00000004
+
+/**
+ * List of capabilities a policy may have. This list represents
+ * features of policy that may differ from version to version or
+ * based upon the format of the policy file. Note that "polcaps" in
+ * this case refers to "policy capabilities" that were introduced
+ * with version 22 policies.
+ */
+ typedef enum qpol_capability
+ {
+ /** The policy format stores the names of attributes. */
+ QPOL_CAP_ATTRIB_NAMES,
+ /** The policy format stores the syntactic rule type sets. */
+ QPOL_CAP_SYN_RULES,
+ /** The policy format stores rule line numbers (implies QPOL_CAP_SYN_RULES). */
+ QPOL_CAP_LINE_NUMBERS,
+ /** The policy version supports booleans and conditional statements. */
+ QPOL_CAP_CONDITIONALS,
+ /** The policy version supports MLS components and statements. */
+ QPOL_CAP_MLS,
+ /** The policy version has policy capabilities (polcaps). */
+ QPOL_CAP_POLCAPS,
+ /** The policy format supports linking loadable modules. */
+ QPOL_CAP_MODULES,
+ /** The policy was loaded with av/te rules. */
+ QPOL_CAP_RULES_LOADED,
+ /** The policy source may be displayed. */
+ QPOL_CAP_SOURCE,
+ /** The policy supports and was loaded with neverallow rules. */
+ QPOL_CAP_NEVERALLOW
+ } qpol_capability_e;
+
+/**
+ * Open a policy from a passed in file path.
+ * @param filename The name of the file to open.
+ * @param policy The policy to populate. The caller should not free
+ * this pointer.
+ * @param fn (Optional) If non-NULL, the callback to be used by the handle.
+ * @param varg (Optional) The argument needed by the handle callback.
+ * @param options Options to control loading only portions of a policy;
+ * must be a bitwise-or'd set of QPOL_POLICY_OPTION_* from above.
+ * @return Returns one of QPOL_POLICY_KERNEL_SOURCE,
+ * QPOL_POLICY_KERNEL_BINARY, or QPOL_POLICY_MODULE_BINARY on success
+ * and < 0 on failure; if the call fails, errno will be set and
+ * *policy will be NULL.
+ */
+ extern int qpol_policy_open_from_file(const char *filename, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg,
+ const int options);
+
+/**
+ * Open a policy from a passed in file path but do not load any rules.
+ * @param filename The name of the file to open.
+ * @param policy The policy to populate. The caller should not free
+ * this pointer.
+ * @param fn (Optional) If non-NULL, the callback to be used by the handle.
+ * @param varg (Optional) The argument needed by the handle callback.
+ * @return Returns one of QPOL_POLICY_* above on success and < 0 on failure;
+ * if the call fails, errno will be set and *policy will be NULL.
+ * @deprecated use qpol_policy_open_from_file() with the option QPOL_POLICY_OPTION_NO_RULES instead.
+ */
+ extern int qpol_policy_open_from_file_no_rules(const char *filename, qpol_policy_t ** policy, qpol_callback_fn_t fn,
+ void *varg) __attribute__ ((deprecated));
+
+/**
+ * Open a policy from a passed in buffer.
+ * @param policy The policy to populate. The caller should not free
+ * this pointer.
+ * @param filedata The policy file stored in memory .
+ * @param size The size of filedata
+ * @param fn (Optional) If non-NULL, the callback to be used by the handle.
+ * @param varg (Optional) The argument needed by the handle callback.
+ * @param options Options to control loading only portions of a policy;
+ * must be a bitwise-or'd set of QPOL_POLICY_OPTION_* from above.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *policy will be NULL.
+ */
+ extern int qpol_policy_open_from_memory(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn,
+ void *varg, const int options);
+
+/**
+ * Close a policy and deallocate its memory. Does nothing if it is
+ * already NULL.
+ * @param policy Reference to the policy to close. The pointer will
+ * be set to NULL afterwards.
+ */
+ extern void qpol_policy_destroy(qpol_policy_t ** policy);
+
+/**
+ * Re-evaluate all conditionals in the policy updating the state
+ * and setting the appropriate rule list as emabled for each.
+ * This call modifies the policy.
+ * @param policy The policy for which to re-evaluate the conditionals.
+ * This policy will be modified by this function.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set. On failure, the policy state may be inconsistent.
+ */
+ extern int qpol_policy_reevaluate_conds(qpol_policy_t * policy);
+
+/**
+ * Append a module to a policy. The policy now owns the module.
+ * Note that the caller must still invoke qpol_policy_rebuild()
+ * to update the policy.
+ * @param policy The policy to which to add the module.
+ * @param module The module to append. <b>The caller should not
+ * destroy this module if this function succeeds.</b>
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and both the policy and the module will
+ * remain unchanged. If the call fails, the caller is still
+ * responsible for calling qpol_module_destroy().
+ */
+ extern int qpol_policy_append_module(qpol_policy_t * policy, qpol_module_t * module);
+
+/**
+ * Rebuild the policy. If the options provided are the same as those
+ * provied to the last call to rebuild or open and the modules were not
+ * changed, this function does nothing; otherwise, re-link all enabled
+ * modules with the base and then call expand. If the syntactic rule
+ * table was previously built, the caller should call
+ * qpol_policy_build_syn_rule_table() after calling this function.
+ * @param policy The policy to rebuild.
+ * This policy will be altered by this function.
+ * @param options Options to control loading only portions of a policy;
+ * must be a bitwise-or'd set of QPOL_POLICY_OPTION_* from above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the policy will be reverted to its previous state.
+ */
+ extern int qpol_policy_rebuild(qpol_policy_t * policy, const int options);
+
+/**
+ * Get an iterator of all modules in a policy.
+ * @param policy The policy from which to get the iterator.
+ * @param iter Iteraror of modules (of type qpol_module_t) returned.
+ * The caller should not destroy the modules returned by
+ * qpol_iterator_get_item().
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_module_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the version number of the policy.
+ * @param policy The policy for which to get the version.
+ * @param version Pointer to the integer to set to the version number.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *version will be 0.
+ */
+ extern int qpol_policy_get_policy_version(const qpol_policy_t * policy, unsigned int *version);
+
+/**
+ * Get the type of policy (source, binary, or module).
+ * @param policy The policy from which to get the type.
+ * @param type Pointer to the integer in which to store the type.
+ * Value will be one of QPOL_POLICY_* from above.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *type will be QPOL_POLICY_UNKNOWN.
+ */
+ extern int qpol_policy_get_type(const qpol_policy_t * policy, int *type);
+
+/**
+ * Determine if a policy has support for a specific capability.
+ * @param policy The policy to check.
+ * @param cap The capability for which to check. Must be one of QPOL_CAP_*
+ * defined above.
+ * @return Non-zero if the policy has the specified capability, and zero otherwise.
+ */
+ extern int qpol_policy_has_capability(const qpol_policy_t * policy, qpol_capability_e cap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/include/qpol/policy_extend.h b/libqpol/include/qpol/policy_extend.h
new file mode 100644
index 0000000..1a06a93
--- /dev/null
+++ b/libqpol/include/qpol/policy_extend.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ * Public interface for loading and using an extended
+ * policy image.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_POLICY_EXTEND_H
+#define QPOL_POLICY_EXTEND_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/iterator.h>
+
+/**
+ * Build the table of syntactic rules for a policy.
+ * Subsequent calls to this function have no effect.
+ * @param policy The policy for which to build the table.
+ * This policy will be modified by this call.
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set.
+ */
+ extern int qpol_policy_build_syn_rule_table(qpol_policy_t * policy);
+
+/* forward declarations: see avrule_query.h and terule_query.h */
+ struct qpol_avrule;
+ struct qpol_terule;
+
+/**
+ * Get an iterator over the syntactic rules contributing to an av rule.
+ * @param policy Policy associated with the rule.
+ * @param rule Rule from which to get the syntactic rules.
+ * @param iter Iterator over items of type qpol_syn_avrule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_avrule_get_syn_avrule_iter(const qpol_policy_t * policy, const struct qpol_avrule *rule,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator over the syntactic rules contributing to a type rule.
+ * @param policy Policy associated with the rule.
+ * @param rule Rule from which to get the syntactic rules.
+ * @param iter Iterator over items of type qpol_syn_terule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_terule_get_syn_terule_iter(const qpol_policy_t * policy, const struct qpol_terule *rule,
+ qpol_iterator_t ** iter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_POLICY_EXTEND_H */
diff --git a/libqpol/include/qpol/portcon_query.h b/libqpol/include/qpol/portcon_query.h
new file mode 100644
index 0000000..63210fe
--- /dev/null
+++ b/libqpol/include/qpol/portcon_query.h
@@ -0,0 +1,118 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over portcon statements.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_PORTCON_QUERY_H
+#define QPOL_PORTCON_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <netinet/in.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_portcon qpol_portcon_t;
+
+/**
+ * Get a single portcon statement by port range and protocol.
+ * @param policy The policy from which to get the portcon statement.
+ * @param low The low port of the range of ports (or single port).
+ * @param high The high port of the range of ports; if searching for a
+ * single port, set high equal to low.
+ * @param protocol The protocol used in the portcon statement.
+ * Value should be one of IPPROTO_TCP or IPPROTO_UDP from netinet/in.h
+ * @param ocon Pointer in which to store the statement returned.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *ocon will be NULL.
+ */
+ extern int qpol_policy_get_portcon_by_port(const qpol_policy_t * policy, uint16_t low, uint16_t high, uint8_t protocol,
+ const qpol_portcon_t ** ocon);
+
+/**
+ * Get an iterator for the portcon statements in a policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_portcon_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long
+ * as the policy is unmodified.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_portcon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the protocol from a portcon statement.
+ * @param policy The policy associated with the portcon statement.
+ * @param ocon The portcon statement from which to get the protocol.
+ * @param protocol Pointer to set to the value of protocol.
+ * Value will be one of IPPROTO_TCP or IPPROTO_UDP from netinet/in.h
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *protocol will be 0;
+ */
+ extern int qpol_portcon_get_protocol(const qpol_policy_t * policy, const qpol_portcon_t * ocon, uint8_t * protocol);
+
+/**
+ * Get the low port from a portcon statement.
+ * @param policy the policy associated with the portcon statement.
+ * @param ocon The portcon statement from which to get the low port.
+ * @param port Pointer to set to the port number.
+ * @return 0 on success < 0 on failure; if the call fails,
+ * errno will be set and *port will be 0.
+ */
+ extern int qpol_portcon_get_low_port(const qpol_policy_t * policy, const qpol_portcon_t * ocon, uint16_t * port);
+
+/**
+ * Get the high port from a portcon statement.
+ * @param policy the policy associated with the portcon statement.
+ * @param ocon The portcon statement from which to get the high port.
+ * @param port Pointer to set to the port number.
+ * @return 0 on success < 0 on failure; if the call fails,
+ * errno will be set and *port will be 0.
+ */
+ extern int qpol_portcon_get_high_port(const qpol_policy_t * policy, const qpol_portcon_t * ocon, uint16_t * port);
+
+/**
+ * Get the context from a portcon statement.
+ * @param policy the policy associated with the portcon statement.
+ * @param ocon The portcon statement from which to get the context.
+ * @param context Pointer in which to store the context.
+ * The caller should not free this pointer.
+ * @return 0 on success < 0 on failure; if the call fails,
+ * errno will be set and *context will be NULL.
+ */
+ extern int qpol_portcon_get_context(const qpol_policy_t * policy, const qpol_portcon_t * ocon,
+ const qpol_context_t ** context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_PORTCON_QUERY_H */
diff --git a/libqpol/include/qpol/rbacrule_query.h b/libqpol/include/qpol/rbacrule_query.h
new file mode 100644
index 0000000..342e597
--- /dev/null
+++ b/libqpol/include/qpol/rbacrule_query.h
@@ -0,0 +1,130 @@
+/**
+ * @file
+ * Defines public interface for iterating over RBAC rules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_RBACRULE_QUERY
+#define QPOL_RBACRULE_QUERY
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/iterator.h>
+
+ typedef struct qpol_role_allow qpol_role_allow_t;
+ typedef struct qpol_role_trans qpol_role_trans_t;
+
+/**
+ * Get an iterator over all role allow rules in the policy.
+ * @param policy Policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_role_allow_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_role_allow_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the source role from a role allow rule.
+ * @param policy The policy from which the rule comes.
+ * @param rule The rule from which to get the source role.
+ * @param source Pointer in which to store the source role.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source will be NULL.
+ */
+ extern int qpol_role_allow_get_source_role(const qpol_policy_t * policy, const qpol_role_allow_t * rule,
+ const qpol_role_t ** source);
+
+/**
+ * Get the target role from a role allow rule.
+ * @param policy The policy from which the rule comes.
+ * @param rule The rule from which to get the target role.
+ * @param target Pointer in which to store the target role.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target will be NULL.
+ */
+ extern int qpol_role_allow_get_target_role(const qpol_policy_t * policy, const qpol_role_allow_t * rule,
+ const qpol_role_t ** target);
+
+/**
+ * Get an iterator over all role transition rules in the policy.
+ * @param policy Policy from which to create the iterator.
+ * @param iter Iterator over items of type qpol_role_trans_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_role_trans_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the source role from a role transition rule.
+ * @param policy The policy from which the rule comes.
+ * @param rule The rule from which to get the source role.
+ * @param source Pointer in which to store the source role.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source will be NULL.
+ */
+ extern int qpol_role_trans_get_source_role(const qpol_policy_t * policy, const qpol_role_trans_t * rule,
+ const qpol_role_t ** source);
+
+/**
+ * Get the target type from a role transition rule.
+ * @param policy The policy from which the rule comes.
+ * @param rule The rule from which to get the target type.
+ * @param target Pointer in which to store the target type.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target will be NULL.
+ */
+ extern int qpol_role_trans_get_target_type(const qpol_policy_t * policy, const qpol_role_trans_t * rule,
+ const qpol_type_t ** target);
+
+/**
+ * Get the default role from a role transition rule.
+ * @param policy The policy from which the rule comes.
+ * @param rule The rule from which to get the default role.
+ * @param dflt Pointer in which to store the default role.
+ * The caller should not free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *dflt will be NULL.
+ */
+ extern int qpol_role_trans_get_default_role(const qpol_policy_t * policy, const qpol_role_trans_t * rule,
+ const qpol_role_t ** dflt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_RBACRULE_QUERY */
diff --git a/libqpol/include/qpol/role_query.h b/libqpol/include/qpol/role_query.h
new file mode 100644
index 0000000..c5739ab
--- /dev/null
+++ b/libqpol/include/qpol/role_query.h
@@ -0,0 +1,119 @@
+ /**
+ * @file
+ * Defines the public interface for searching and iterating over roles.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_ROLE_QUERY_H
+#define QPOL_ROLE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_role qpol_role_t;
+
+/**
+ * Get the datum for a role by name.
+ * @param policy The policy from which to get the role.
+ * @param name The name of the role; searching is case sensitive.
+ * @param datum Pointer in which to store the role datum; the caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *datum will be NULL.
+ */
+ extern int qpol_policy_get_role_by_name(const qpol_policy_t * policy, const char *name, const qpol_role_t ** datum);
+
+/**
+ * Get an iterator for roles declared in the policy.
+ * @param policy The policy with which to create the iterator.
+ * @param iter Iterator of type qpol_role_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator
+ * is valid only as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_role_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a role; values range from
+ * 1 to the number of declared roles.
+ * @param policy The policy associated with the role.
+ * @param datum The role from which to get the value.
+ * @param value Pointer to the integer to set to value. Must be non-NULL.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and value will be 0.
+ */
+ extern int qpol_role_get_value(const qpol_policy_t * policy, const qpol_role_t * datum, uint32_t * value);
+
+/**
+ * Get an iterator for the set of roles dominated by a role.
+ * @param policy The policy associated with the role.
+ * @param datum The role from which to get the dominated roles.
+ * @param dominates Iterator of type qpol_role_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator is
+ * valid only as long as the policy is unchanged. Note: By
+ * convention a role always dominates itself, so the user of this
+ * iterator should always check for this case.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *dominates will be NULL.
+ */
+ extern int qpol_role_get_dominate_iter(const qpol_policy_t * policy, const qpol_role_t * datum,
+ qpol_iterator_t ** dominates);
+
+/**
+ * Get an iterator for the set of types assigned to a role.
+ * @param policy The policy associated with the role.
+ * @param datum The role from which to get the types.
+ * @param types Iterator of type qpol_type_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator
+ * is valid only as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and types will be NULL.
+ */
+ extern int qpol_role_get_type_iter(const qpol_policy_t * policy, const qpol_role_t * datum, qpol_iterator_t ** types);
+
+/**
+ * Get the name by which a role is identified from its datum.
+ * @param policy The policy associated with the role.
+ * @param datum The role for which to get the name.
+ * @param name Pointer in which to store the name; the caller
+ * should not free this string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_role_get_name(const qpol_policy_t * policy, const qpol_role_t * datum, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_ROLE_QUERY_H */
diff --git a/libqpol/include/qpol/syn_rule_query.h b/libqpol/include/qpol/syn_rule_query.h
new file mode 100644
index 0000000..446efbb
--- /dev/null
+++ b/libqpol/include/qpol/syn_rule_query.h
@@ -0,0 +1,314 @@
+/**
+ * @file
+ * Public interface for querying syntactic rules from the extended
+ * policy image.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_SYN_RULE_QUERY_H
+#define QPOL_SYN_RULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/cond_query.h>
+#include <qpol/iterator.h>
+#include <stdint.h>
+
+ typedef struct qpol_type_set qpol_type_set_t;
+ typedef struct qpol_syn_avrule qpol_syn_avrule_t;
+ typedef struct qpol_syn_terule qpol_syn_terule_t;
+
+/**
+ * Get an iterator of the included types in a type set.
+ * @param policy Policy associated with the type set.
+ * @param ts Type set from which to get the included types.
+ * @param iter Iterator over items of type qpol_type_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_type_set_get_included_types_iter(const qpol_policy_t * policy, const qpol_type_set_t * ts,
+ qpol_iterator_t ** iter);
+
+/**
+ * Get an iterator of the subtracted types in a type set.
+ * @param policy Policy associated with the type set.
+ * @param ts Type set from which to get the subtracted types.
+ * @param iter Iterator over items of type qpol_type_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_type_set_get_subtracted_types_iter(const qpol_policy_t * policy, const qpol_type_set_t * ts,
+ qpol_iterator_t ** iter);
+
+/**
+ * Determine if a type set includes '*'.
+ * @param policy Policy associated with the type set.
+ * @param ts Type set to check for '*'.
+ * @param is_star Pointer to integer to set.
+ * Will be set to 1 if ts contains '*' or 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *is_star will be 0.
+ */
+ extern int qpol_type_set_get_is_star(const qpol_policy_t * policy, const qpol_type_set_t * ts, uint32_t * is_star);
+
+/**
+ * Determine if a type set is complemented (contains '~').
+ * @param policy Policy associated with the type set.
+ * @param ts Type set to check for complement.
+ * @param is_comp Pointer to integer to set.
+ * Will be set to 1 if ts is complemented or 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *is_comp will be 0.
+ */
+ extern int qpol_type_set_get_is_comp(const qpol_policy_t * policy, const qpol_type_set_t * ts, uint32_t * is_comp);
+
+/**
+ * Get the rule type of a syntactic avrule.
+ * @param policy Policy associated with the rule.
+ * @param rule Avrule from which to get the type.
+ * @param rule_type Pointer to integer to set.
+ * Will be one of QPOL_RULE_* (see qpol/avrule_query.h).
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *rule_type will be 0.
+ */
+ extern int qpol_syn_avrule_get_rule_type(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ uint32_t * rule_type);
+
+/**
+ * Get the set of types specified for a syntatic rule's source field.
+ * @param policy Policy associated with the rule.
+ * @param rule Avrule from which to get the source type set.
+ * @param source_set Type set returned; the caller <b>should not</b>
+ * free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source_set will be NULL.
+ */
+ extern int qpol_syn_avrule_get_source_type_set(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ const qpol_type_set_t ** source_set);
+
+/**
+ * Get the set of types specified for a syntactic rule's target field.
+ * @param policy Policy associated with the rule.
+ * @param rule Avrule from which to get the target type set.
+ * @param target_set Type set returned; the caller <b>should not</b>
+ * free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target_set will be NULL.
+ */
+ extern int qpol_syn_avrule_get_target_type_set(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ const qpol_type_set_t ** target_set);
+
+/**
+ * Determine if a syntactic rule includes the self flag in the target set.
+ * @param policy Policy associated with the rule.
+ * @param rule Avrule to check for the self flag.
+ * @param is_self Pointer to the integer to set; if the rule includes self,
+ * this will be set to 1, otherwise it will be set to 0.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *is_self will be 0.
+ */
+ extern int qpol_syn_avrule_get_is_target_self(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ uint32_t * is_self);
+
+/**
+ * Get an iterator over all classes specified in a syntactic rule.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule from which to get the classes.
+ * @param classes Iterator over items of type qpol_class_t* returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *classes will be NULL.
+ */
+ extern int qpol_syn_avrule_get_class_iter(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ qpol_iterator_t ** classes);
+
+/**
+ * Get an iterator over all permissions specified in a syntactic rule.
+ * @param policy Policy associated with the
+ * @param rule The rule from which to get the permissions.
+ * @param perms Iterator over items of type char* returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *perms will be NULL.
+ */
+ extern int qpol_syn_avrule_get_perm_iter(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ qpol_iterator_t ** perms);
+
+/**
+ * Get the line number of a syntactic rule.
+ * @param policy Policy associated with the rule
+ * @param rule The rule for which to get the line number.
+ * @param lineno Pointer to set to the line number.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *lineno will be 0.
+ */
+ extern int qpol_syn_avrule_get_lineno(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, unsigned long *lineno);
+
+/**
+ * If the syntactic rule is within a conditional, then get that
+ * conditional and assign it to cond. Otherwise assign to cond NULL.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule for which to get the conditional.
+ * @param cond Reference pointer to this rule's conditional
+ * expression, or NULL if the rule is unconditional.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *lineno will be 0.
+ */
+ extern int qpol_syn_avrule_get_cond(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ const qpol_cond_t ** cond);
+
+/**
+ * Determine if the syntactic rule is enabled. Unconditional rules
+ * are always enabled.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule for which to get the conditional.
+ * @param is_enabled Integer in which to store the result: set to 1
+ * if enabled and 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *lineno will be 0.
+ */
+ extern int qpol_syn_avrule_get_is_enabled(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ uint32_t * is_enabled);
+
+/**
+ * Get the rule type of a syntactic terule.
+ * @param policy Policy associated with the rule.
+ * @param rule Terule from which to get the type.
+ * @param rule_type Pointer to integer to set.
+ * Will be one of QPOL_RULE_TYPE_* (see qpol/terule_query.h).
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *rule_type will be 0.
+ */
+ extern int qpol_syn_terule_get_rule_type(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ uint32_t * rule_type);
+
+/**
+ * Bet the set of types specified for a syntactic rule's source field.
+ * @param policy Policy associated with the rule.
+ * @param rule Terule from which to get the source type set.
+ * @param source_set Type set returned; the caller <b>shoule not</b>
+ * free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source_set will be NULL.
+ */
+ extern int qpol_syn_terule_get_source_type_set(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ const qpol_type_set_t ** source_set);
+
+/**
+ * Get the set of types specified for a syntactic rule's target field.
+ * @param policy Policy associated with the rule.
+ * @param rule Terule from which to get the target types et.
+ * @param target_set Type set returned; ther caller <b>should not</b>
+ * free this pointer.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target_set will be NULL.
+ */
+ extern int qpol_syn_terule_get_target_type_set(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ const qpol_type_set_t ** target_set);
+
+/**
+ * Get an iterator over all classes specified in a syntactic rule.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule from which to get the classes.
+ * @param classes Iterator over items of type qpol_class_t* returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *classes will be NULL.
+ */
+ extern int qpol_syn_terule_get_class_iter(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ qpol_iterator_t ** classes);
+
+/* forward declaration */
+ struct qpol_type;
+
+/**
+ * Get the default type of a syntactic terule.
+ * @param policy Policy associated with the rule.
+ * @param rule Terule from which to et the default type.
+ * @param dflt Reference pointer to the type to return.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *dflt will be NULL.
+ */
+ extern int qpol_syn_terule_get_default_type(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ const struct qpol_type **dflt);
+
+/**
+ * Get the line number of a syntactic rule.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule for which to get the line number.
+ * @param lineno Pointer to set to the line number.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *lineno will be 0.
+ */
+ extern int qpol_syn_terule_get_lineno(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, unsigned long *lineno);
+
+/**
+ * If the syntactic rule is within a conditional, then get that
+ * conditional and assign it to cond. Otherwise assign to cond NULL.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule for which to get the conditional.
+ * @param cond Reference pointer to this rule's conditional
+ * expression, or NULL if the rule is unconditional.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *lineno will be 0.
+ */
+ extern int qpol_syn_terule_get_cond(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ const qpol_cond_t ** cond);
+
+/**
+ * Determine if the syntactic rule is enabled. Unconditional rules
+ * are always enabled.
+ * @param policy Policy associated with the rule.
+ * @param rule The rule for which to get the conditional.
+ * @param is_enabled Integer in which to store the result: set to 1
+ * if enabled and 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *lineno will be 0.
+ */
+ extern int qpol_syn_terule_get_is_enabled(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ uint32_t * is_enabled);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_SYN_RULE_QUERY_H */
diff --git a/libqpol/include/qpol/terule_query.h b/libqpol/include/qpol/terule_query.h
new file mode 100644
index 0000000..0006d1a
--- /dev/null
+++ b/libqpol/include/qpol/terule_query.h
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over type rules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_TERULE_QUERY_H
+#define QPOL_TERULE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/cond_query.h>
+
+ typedef struct qpol_terule qpol_terule_t;
+
+/* rule type defines (values copied from "sepol/policydb/policydb.h") */
+#define QPOL_RULE_TYPE_TRANS 16
+#define QPOL_RULE_TYPE_CHANGE 64
+#define QPOL_RULE_TYPE_MEMBER 32
+
+/**
+ * Get an iterator over all type rules in a policy of a rule type in
+ * rule_type_mask. It is an error to call this function if rules are not
+ * loaded.
+ * @param policy Policy from which to get the av rules.
+ * @param rule_type_mask Bitwise or'ed set of QPOL_RULE_TYPE_* values.
+ * It is an error to specify any other values of QPOL_RULE_* in the mask.
+ * @param iter Iterator over items of type qpol_terule_t returned.
+ * The caller is responsible for calling qpol_iterator_destroy()
+ * to free memory used by this iterator.
+ * It is important to note that this iterator is only valid as long as
+ * the policy is unmodifed.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_terule_iter(const qpol_policy_t * policy, uint32_t rule_type_mask, qpol_iterator_t ** iter);
+
+/**
+ * Get the source type from a type rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the source type.
+ * @param source Pointer in which to store the source type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *source will be NULL.
+ */
+ extern int qpol_terule_get_source_type(const qpol_policy_t * policy, const qpol_terule_t * rule,
+ const qpol_type_t ** source);
+
+/**
+ * Get the target type from a type rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the target type.
+ * @param target Pointer in which to store the target type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *target will be NULL.
+ */
+ extern int qpol_terule_get_target_type(const qpol_policy_t * policy, const qpol_terule_t * rule,
+ const qpol_type_t ** target);
+
+/**
+ * Get the object class from a type rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the object class.
+ * @param obj_class Pointer in which to store the object class.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *obj_class will be NULL.
+ */
+ extern int qpol_terule_get_object_class(const qpol_policy_t * policy, const qpol_terule_t * rule,
+ const qpol_class_t ** obj_class);
+
+/**
+ * Get the default type from a type rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the default type.
+ * @param dflt Pointer in which to store the default type.
+ * The caller should not free this pointer.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *dflt will be NULL.
+ */
+ extern int qpol_terule_get_default_type(const qpol_policy_t * policy, const qpol_terule_t * rule,
+ const qpol_type_t ** dflt);
+
+/**
+ * Get the rule type value for a type rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the rule type.
+ * @param rule_type Integer in which to store the rule type value.
+ * The value will be one of the QPOL_RULE_* values above.
+ * @returm 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *rule_type will be 0.
+ */
+ extern int qpol_terule_get_rule_type(const qpol_policy_t * policy, const qpol_terule_t * rule, uint32_t * rule_type);
+
+/**
+ * Get the conditional from which a type rule comes. If the rule
+ * is not a conditional rule *cond is set to NULL.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule from which to get the conditional.
+ * @param cond The conditional returned. (NULL if rule is not conditional)
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *cond will be NULL. If the rule is not conditional
+ * *cond is set to NULL and the function is considered successful.
+ */
+ extern int qpol_terule_get_cond(const qpol_policy_t * policy, const qpol_terule_t * rule, const qpol_cond_t ** cond);
+
+/**
+ * Determine if a rule is enabled. Unconditional rules are always enabled.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule to check.
+ * @param is_enabled Integer in which to store the result: set to 1 if enabled
+ * and 0 otherwise.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *is_enabled will be 0.
+ */
+ extern int qpol_terule_get_is_enabled(const qpol_policy_t * policy, const qpol_terule_t * rule, uint32_t * is_enabled);
+
+/**
+ * Get the list (true or false) in which a conditional rule is. It is
+ * an error to call this function for an unconditional rule.
+ * @param policy Policy from which the rule comes.
+ * @param rule The rule to check.
+ * @param which_list Integer in which to store the result: set to 1 if
+ * rule is in the true list or 0 if in the false list.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *which_list will be 0.
+ */
+ extern int qpol_terule_get_which_list(const qpol_policy_t * policy, const qpol_terule_t * rule, uint32_t * which_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/include/qpol/type_query.h b/libqpol/include/qpol/type_query.h
new file mode 100644
index 0000000..9537f25
--- /dev/null
+++ b/libqpol/include/qpol/type_query.h
@@ -0,0 +1,175 @@
+ /**
+ * @file
+ * Defines the public interface for searching and iterating over types.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_TYPE_QUERY_H
+#define QPOL_TYPE_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+
+ typedef struct qpol_type qpol_type_t;
+
+/**
+ * Get the datum for a type by name.
+ * @param policy The policy from which to get the type.
+ * @param name The name of the type; searching is case sensitive.
+ * @param datum Pointer in which to store the type datum; the caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *datum will be NULL.
+ */
+ extern int qpol_policy_get_type_by_name(const qpol_policy_t * policy, const char *name, const qpol_type_t ** datum);
+
+/**
+ * Get an iterator for types (including attributes and aliases)
+ * declared in the policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator of type qpol_type_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator is
+ * valid only as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_type_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a type. Values range from 1
+ * to the number of types declared in the policy.
+ * @param policy The policy associated with the type.
+ * @param datum The type from which to get the value.
+ * @param value Pointer to the integer in which to store value.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and value will be 0.
+ */
+ extern int qpol_type_get_value(const qpol_policy_t * policy, const qpol_type_t * datum, uint32_t * value);
+
+/**
+ * Determine whether a given type is an alias for another type.
+ * @param policy The policy associated with the type.
+ * @param datum The type to check.
+ * @param isalias Pointer to be set to 1 (true) if the type is an alias
+ * and 0 (false) otherwise.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *isalias will be 0 (false).
+ */
+ extern int qpol_type_get_isalias(const qpol_policy_t * policy, const qpol_type_t * datum, unsigned char *isalias);
+
+/**
+ * Determine whether a given type is an attribute.
+ * @param policy The policy associated with the type.
+ * @param datum The type to check.
+ * @param isattr Pointer to be set to 1 (true) if the type is an
+ * attribute and 0 (false) otherwise.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *isattr will be 0 (false).
+ */
+ extern int qpol_type_get_isattr(const qpol_policy_t * policy, const qpol_type_t * datum, unsigned char *isattr);
+
+/**
+ * Determine whether a given type has been marked as enforcing
+ * (default) or as permissive. If the policy does not support
+ * permissive types, then all types are enforcing. Attributes are
+ * always enforcing.
+ *
+ * @param policy The policy associated with the type.
+ * @param datum The type to check.
+ * @param ispermissive Pointer to be set to 1 (true) if the type is
+ * permissive and 0 (false) otherwise.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *ispermissive will be 0 (false).
+ */
+ extern int qpol_type_get_ispermissive(const qpol_policy_t * policy, const qpol_type_t * datum, unsigned char *ispermissive);
+
+/**
+ * Get an iterator for the list of types in an attribute.
+ * @param policy The policy associated with the attribute.
+ * @param datum The attribute from which to get the types.
+ * @param types Iterator of type qpol_type_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator is
+ * valid only as long as the policy is unchanged.
+ * @return Returns 0 on success, > 0 if the type is not an attribute
+ * and < 0 on failure; if the call fails, errno will be set and
+ * *types will be NULL. If the type is not an attribute *types will
+ * be NULL.
+ */
+ extern int qpol_type_get_type_iter(const qpol_policy_t * policy, const qpol_type_t * datum, qpol_iterator_t ** types);
+
+/**
+ * Get an iterator for the list of attributes given to a type.
+ * @param policy The policy associated with the type.
+ * @param datum The type for which to get the attributes.
+ * @param attrs Iterator of type qpol_type_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator is
+ * valid only as long as the policy is unchanged.
+ * @return Returns 0 on success, > 0 if the type is an attribute
+ * and < 0 on failure; if the call fails, errno will be set and
+ * *types will be NULL. If the type is an attribute *types will
+ * be NULL.
+ */
+ extern int qpol_type_get_attr_iter(const qpol_policy_t * policy, const qpol_type_t * datum, qpol_iterator_t ** attrs);
+
+/**
+ * Get the name by which a type is identified from its datum.
+ * @param policy The policy associated with the type.
+ * @param datum The type for which to get the name.
+ * @param name Pointer in which to store the name; the caller
+ * should not free the string. If the type is an alias then the
+ * primary name will be returned.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_type_get_name(const qpol_policy_t * policy, const qpol_type_t * datum, const char **name);
+
+/**
+ * Get an iterator for the list of aliases for a type. If the given
+ * type is an alias, this returns an iterator of its primary type's
+ * aliases.
+ * @param policy The policy associated with the type.
+ * @param datum The type for which to get aliases.
+ * @param aliases Iterator of type char* returned; the caller is
+ * responsible for calling qpol_iterator_destroy to free
+ * memory used; it is important to note that the iterator is valid
+ * only as long as the policy is unchanged. If a type has no aliases,
+ * the iterator will be at end and have size 0.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *aliases will be NULL.
+ */
+ extern int qpol_type_get_alias_iter(const qpol_policy_t * policy, const qpol_type_t * datum, qpol_iterator_t ** aliases);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_TYPE_QUERY_H */
diff --git a/libqpol/include/qpol/user_query.h b/libqpol/include/qpol/user_query.h
new file mode 100644
index 0000000..be6322d
--- /dev/null
+++ b/libqpol/include/qpol/user_query.h
@@ -0,0 +1,130 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over users.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_USER_QUERY_H
+#define QPOL_USER_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/mls_query.h>
+
+ typedef struct qpol_user qpol_user_t;
+
+/**
+ * Get the datum for a user by name.
+ * @param policy The policy from which to get the user.
+ * @param name The name of the user; searching is case sensitive.
+ * @param datum Pointer in which to store the user datum; the caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and and *datum will be NULL.
+ */
+ extern int qpol_policy_get_user_by_name(const qpol_policy_t * policy, const char *name, const qpol_user_t ** datum);
+
+/**
+ * Get an iterator for users declared in the policy.
+ * @param policy The policy from which to create the iterator.
+ * @param iter Iterator of type qpol_user_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator is
+ * valid only as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *iter will be NULL.
+ */
+ extern int qpol_policy_get_user_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter);
+
+/**
+ * Get the integer value associated with a user. Values range from 1 to
+ * the number of users declared in the policy.
+ * @param policy The policy associate with the user.
+ * @param datum The user from which to get the value.
+ * @param value Pointer to the integer to set to value.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and value will be 0.
+ */
+ extern int qpol_user_get_value(const qpol_policy_t * policy, const qpol_user_t * datum, uint32_t * value);
+
+/**
+ * Get an iterator for the set of roles assigned to a user.
+ * @param policy The policy associated with the user.
+ * @param datum The user from which to get the roles.
+ * @param roles Iterator of type qpol_role_t* returned;
+ * the caller is responsible for calling qpol_iterator_destroy to
+ * free memory used; it is important to note that the iterator is
+ * valid only as long as the policy is unchanged.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *roles will be NULL.
+ */
+ extern int qpol_user_get_role_iter(const qpol_policy_t * policy, const qpol_user_t * datum, qpol_iterator_t ** roles);
+
+/**
+ * Get the allowed MLS range of a user. If the policy is not MLS
+ * then the returned level will be NULL.
+ * @param policy The policy associated with the user.
+ * @param datum The user from which to get the range.
+ * @param range Pointer in which to store the range. If the policy
+ * is not MLS then NULL will be assigned to the pointer. The caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *range will be NULL.
+ */
+ extern int qpol_user_get_range(const qpol_policy_t * policy, const qpol_user_t * datum, const qpol_mls_range_t ** range);
+
+/**
+ * Get the default level for a user. If the policy is not MLS then
+ * the returned level will be NULL.
+ * @param policy The policy associated with the user.
+ * @param datum The user from which to get the level.
+ * @param level Pointer in which to store the level. If the policy
+ * is not MLS then NULL will be assigned to the pointer. The caller
+ * should not free this pointer.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *level will be NULL.
+ */
+ extern int qpol_user_get_dfltlevel(const qpol_policy_t * policy, const qpol_user_t * datum,
+ const qpol_mls_level_t ** level);
+
+/**
+ * Get the name which identifies a user from its datum.
+ * @param policy The policy associated with the user.
+ * @param datum The user for which to get the name.
+ * @param name Pointer in which to store the name; the caller
+ * should not free this string.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and *name will be NULL.
+ */
+ extern int qpol_user_get_name(const qpol_policy_t * policy, const qpol_user_t * datum, const char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_USER_QUERY_H */
diff --git a/libqpol/include/qpol/util.h b/libqpol/include/qpol/util.h
new file mode 100644
index 0000000..f779c8e
--- /dev/null
+++ b/libqpol/include/qpol/util.h
@@ -0,0 +1,61 @@
+/**
+ * @file
+ *
+ * Miscellaneous, uncategorized functions for libqpol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_UTIL_H
+#define QPOL_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libqpol_get_version(void);
+
+/**
+ * Find the "default" policy file on the currently running system.
+ * First try looking for a monolithic source policy; if that does not
+ * exist then try a monolithic binary policy.
+ *
+ * @param path Buffer to store the policy's path. The caller is
+ * responsible for free()ing this string.
+ *
+ * @return 0 if a policy was found, > 0 if not, < 0 upon error.
+ */
+ extern int qpol_default_policy_find(char **path);
+
+/* bunzip() a file to '*data', returning the total number of uncompressed bytes
+ * in the file. Returns -1 if file could not be decompressed. */
+ extern ssize_t qpol_bunzip(FILE *f, char **data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/src/Makefile.am b/libqpol/src/Makefile.am
new file mode 100644
index 0000000..34d87a6
--- /dev/null
+++ b/libqpol/src/Makefile.am
@@ -0,0 +1,89 @@
+sepol_srcdir = @sepol_srcdir@
+
+lib_LIBRARIES = libqpol.a
+qpolso_DATA = libqpol.so.@libqpol_version@
+qpolsodir = $(libdir)
+
+tmp_sepol = ./tmp_sepol
+
+AM_YFLAGS = -d
+BUILT_SOURCES = policy_parse.h
+
+# search in sepol_srcdir/include before system's sepol directory
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ \
+ -I$(srcdir)/../include -I$(sepol_srcdir)/../include @SELINUX_CFLAGS@ -fpic
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libqpol_a_SOURCES = \
+ avrule_query.c \
+ bool_query.c \
+ class_perm_query.c \
+ cond_query.c \
+ constraint_query.c \
+ context_query.c \
+ expand.c \
+ expand.h \
+ fs_use_query.c \
+ genfscon_query.c \
+ isid_query.c \
+ iterator.c \
+ iterator_internal.h \
+ mls_query.c \
+ mlsrule_query.c \
+ module.c \
+ module_compiler.c module_compiler.h \
+ netifcon_query.c \
+ nodecon_query.c \
+ permissive_query.c \
+ polcap_query.c \
+ policy.c \
+ policy_define.c policy_define.h \
+ policy_extend.c \
+ policy_parse.h \
+ portcon_query.c \
+ qpol_internal.h \
+ queue.c queue.h \
+ rbacrule_query.c \
+ role_query.c \
+ syn_rule_internal.h \
+ syn_rule_query.c \
+ terule_query.c \
+ type_query.c \
+ user_query.c \
+ util.c \
+ policy_parse.y policy_scan.l
+libqpol_a_DEPENDENCIES = $(tmp_sepol)
+libqpol_a_LIBADD = $(tmp_sepol)/*.o
+
+libqpol_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libqpol_a_SOURCES))) policy_parse.o policy_scan.o
+LIBQPOL_SONAME = @libqpol_soname@
+
+dist_noinst_DATA = libqpol.map
+
+$(tmp_sepol): $(sepol_srcdir)/libsepol.a
+ mkdir -p $@
+ rm -f $@/*
+ cp $< $@
+ (cd $@; ar x libsepol.a)
+
+$(qpolso_DATA): $(tmp_sepol) $(libqpol_so_OBJS) libqpol.map
+ $(CC) -shared -o $@ $(libqpol_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBQPOL_SONAME),--version-script=$(srcdir)/libqpol.map,-z,defs -Wl,--whole-archive $(sepol_srcdir)/libsepol.a -Wl,--no-whole-archive @SELINUX_LIB_FLAG@ -lselinux -lsepol -lbz2
+ $(LN_S) -f $@ @libqpol_soname@
+ $(LN_S) -f $@ libqpol.so
+
+libqpol.so: $(qpolso_DATA)
+
+install-data-hook:
+ cd $(DESTDIR)$(qpolsodir) && $(LN_S) -f $(qpolso_DATA) @libqpol_soname@
+ cd $(DESTDIR)$(qpolsodir) && $(LN_S) -f $(qpolso_DATA) libqpol.so
+
+CLEANFILES = policy_parse.h policy_scan.c policy_parse.c
+
+clean-local:
+ -rm -rf $(tmp_sepol)
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libqpol.so @libqpol_soname@ $(qpolso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(qpolsodir)/$(qpolso_DATA) $(DESTDIR)$(qpolsodir)/@libqpol_soname@ $(DESTDIR)$(qpolsodir)/libqpol.so
diff --git a/libqpol/src/avrule_query.c b/libqpol/src/avrule_query.c
new file mode 100644
index 0000000..749565b
--- /dev/null
+++ b/libqpol/src/avrule_query.c
@@ -0,0 +1,288 @@
+ /**
+ * @file
+ * Implementation for the public interface for searching and iterating over avrules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "iterator_internal.h"
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/avrule_query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/avtab.h>
+#include <sepol/policydb/util.h>
+#include <stdlib.h>
+#include "qpol_internal.h"
+
+int qpol_policy_get_avrule_iter(const qpol_policy_t * policy, uint32_t rule_type_mask, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ avtab_state_t *state;
+
+ if (iter) {
+ *iter = NULL;
+ }
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+#if 1 // Seems to make sediff/sediffx work better without breaking things
+ if (!qpol_policy_has_capability(policy, QPOL_CAP_RULES_LOADED)) {
+ ERR(policy, "%s", "Cannot get avrules: Rules not loaded");
+ errno = ENOTSUP;
+ return STATUS_ERR;
+ }
+#endif
+
+ if ((rule_type_mask & QPOL_RULE_NEVERALLOW) && !qpol_policy_has_capability(policy, QPOL_CAP_NEVERALLOW)) {
+ ERR(policy, "%s", "Cannot get avrules: Neverallow rules requested but not available");
+ errno = ENOTSUP;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ state = calloc(1, sizeof(avtab_state_t));
+ if (state == NULL) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ state->ucond_tab = &db->te_avtab;
+ state->cond_tab = &db->te_cond_avtab;
+ state->rule_type_mask = rule_type_mask;
+ state->node = db->te_avtab.htable[0];
+
+ if (qpol_iterator_create
+ (policy, state, avtab_state_get_cur, avtab_state_next, avtab_state_end, avtab_state_size, free, iter)) {
+ free(state);
+ return STATUS_ERR;
+ }
+ if (state->node == NULL || !(state->node->key.specified & state->rule_type_mask)) {
+ avtab_state_next(*iter);
+ }
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_source_type(const qpol_policy_t * policy, const qpol_avrule_t * rule, const qpol_type_t ** source)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t avrule = NULL;
+
+ if (source) {
+ *source = NULL;
+ }
+
+ if (!policy || !rule || !source) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ avrule = (avtab_ptr_t) rule;
+
+ *source = (qpol_type_t *) db->type_val_to_struct[avrule->key.source_type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_target_type(const qpol_policy_t * policy, const qpol_avrule_t * rule, const qpol_type_t ** target)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t avrule = NULL;
+
+ if (target) {
+ *target = NULL;
+ }
+
+ if (!policy || !rule || !target) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ avrule = (avtab_ptr_t) rule;
+
+ *target = (qpol_type_t *) db->type_val_to_struct[avrule->key.target_type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_object_class(const qpol_policy_t * policy, const qpol_avrule_t * rule, const qpol_class_t ** obj_class)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t avrule = NULL;
+
+ if (obj_class) {
+ *obj_class = NULL;
+ }
+
+ if (!policy || !rule || !obj_class) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ avrule = (avtab_ptr_t) rule;
+
+ *obj_class = (qpol_class_t *) db->class_val_to_struct[avrule->key.target_class - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_perm_iter(const qpol_policy_t * policy, const qpol_avrule_t * rule, qpol_iterator_t ** perms)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t avrule = NULL;
+ perm_state_t *ps = NULL;
+
+ if (perms) {
+ *perms = NULL;
+ }
+
+ if (!policy || !rule || !perms) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ avrule = (avtab_ptr_t) rule;
+ ps = calloc(1, sizeof(perm_state_t));
+ if (!ps) {
+ return STATUS_ERR;
+ }
+ if (avrule->key.specified & QPOL_RULE_DONTAUDIT) {
+ ps->perm_set = ~(avrule->datum.data); /* stored as auditdeny flip the bits */
+ } else {
+ ps->perm_set = avrule->datum.data;
+ }
+ ps->obj_class_val = avrule->key.target_class;
+
+ if (qpol_iterator_create(policy, (void *)ps, perm_state_get_cur,
+ perm_state_next, perm_state_end, perm_state_size, free, perms)) {
+ return STATUS_ERR;
+ }
+
+ if (!(ps->perm_set & 1)) /* defaults to bit 0, if off: advance */
+ perm_state_next(*perms);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_rule_type(const qpol_policy_t * policy, const qpol_avrule_t * rule, uint32_t * rule_type)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t avrule = NULL;
+
+ if (rule_type) {
+ *rule_type = 0;
+ }
+
+ if (!policy || !rule || !rule_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ avrule = (avtab_ptr_t) rule;
+
+ *rule_type =
+ (avrule->key.specified & (QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT));
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_cond(const qpol_policy_t * policy, const qpol_avrule_t * rule, const qpol_cond_t ** cond)
+{
+ avtab_ptr_t avrule = NULL;
+
+ if (cond) {
+ *cond = NULL;
+ }
+
+ if (!policy || !rule || !cond) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ avrule = (avtab_ptr_t) rule;
+
+ *cond = (qpol_cond_t *) avrule->parse_context;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_is_enabled(const qpol_policy_t * policy, const qpol_avrule_t * rule, uint32_t * is_enabled)
+{
+ avtab_ptr_t avrule = NULL;
+
+ if (is_enabled) {
+ *is_enabled = 0;
+ }
+
+ if (!policy || !rule || !is_enabled) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ avrule = (avtab_ptr_t) rule;
+
+ *is_enabled = ((avrule->merged & QPOL_COND_RULE_ENABLED) ? 1 : 0);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_avrule_get_which_list(const qpol_policy_t * policy, const qpol_avrule_t * rule, uint32_t * which_list)
+{
+ avtab_ptr_t avrule = NULL;
+
+ if (which_list) {
+ *which_list = 0;
+ }
+
+ if (!policy || !rule || !which_list) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ avrule = (avtab_ptr_t) rule;
+
+ if (!avrule->parse_context) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *which_list = ((avrule->merged & QPOL_COND_RULE_LIST) ? 1 : 0);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/bool_query.c b/libqpol/src/bool_query.c
new file mode 100644
index 0000000..8814a59
--- /dev/null
+++ b/libqpol/src/bool_query.c
@@ -0,0 +1,193 @@
+/**
+ * @file
+ * Implementation of the interface for searching and iterating over booleans.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <sepol/policydb.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/expand.h>
+#include "iterator_internal.h"
+#include <qpol/bool_query.h>
+#include "qpol_internal.h"
+
+int qpol_policy_get_bool_by_name(const qpol_policy_t * policy, const char *name, qpol_bool_t ** datum)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || datum == NULL) {
+ if (datum != NULL)
+ *datum = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_bools.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ ERR(policy, "could not find datum for bool %s", name);
+ *datum = NULL;
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *datum = (qpol_bool_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_bool_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ hash_state_t *hs = NULL;
+ int error = 0;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_bools.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_bool_get_value(const qpol_policy_t * policy, const qpol_bool_t * datum, uint32_t * value)
+{
+ cond_bool_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (cond_bool_datum_t *) datum;
+ *value = internal_datum->s.value;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_bool_get_state(const qpol_policy_t * policy, const qpol_bool_t * datum, int *state)
+{
+ cond_bool_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || state == NULL) {
+ if (state != NULL)
+ *state = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (cond_bool_datum_t *) datum;
+ *state = internal_datum->state;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_bool_set_state(qpol_policy_t * policy, qpol_bool_t * datum, int state)
+{
+ cond_bool_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (cond_bool_datum_t *) datum;
+ internal_datum->state = state;
+
+ /* re-evaluate conditionals to update the state of their rules */
+ if (qpol_policy_reevaluate_conds(policy)) {
+ return STATUS_ERR; /* errno already set */
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_bool_set_state_no_eval(qpol_policy_t * policy, qpol_bool_t * datum, int state)
+{
+ cond_bool_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (cond_bool_datum_t *) datum;
+ internal_datum->state = state;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_bool_get_name(const qpol_policy_t * policy, const qpol_bool_t * datum, const char **name)
+{
+ cond_bool_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (cond_bool_datum_t *) datum;
+
+ *name = db->p_bool_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/class_perm_query.c b/libqpol/src/class_perm_query.c
new file mode 100644
index 0000000..f63e1d4
--- /dev/null
+++ b/libqpol/src/class_perm_query.c
@@ -0,0 +1,653 @@
+/**
+ * @file
+ * Implementation of the interface for searching and iterating over
+ * classes, commons, and permissions.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <qpol/iterator.h>
+#include <sepol/policydb.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/expand.h>
+#include "iterator_internal.h"
+#include <qpol/class_perm_query.h>
+#include "qpol_internal.h"
+
+/* perms */
+typedef struct perm_hash_state
+{
+ unsigned int bucket;
+ hashtab_node_t *node;
+ hashtab_t *table;
+ const char *perm_name;
+} perm_hash_state_t;
+
+static int hash_state_next_class_w_perm(qpol_iterator_t * iter)
+{
+ class_datum_t *internal_class = NULL;
+ qpol_iterator_t *internal_perms = NULL;
+ unsigned char has_perm = 0;
+ perm_hash_state_t *hs = NULL;
+ sepol_policydb_t sp;
+ qpol_policy_t qp;
+ char *tmp = NULL;
+
+ hs = (perm_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ /* shallow copy ok here as only internal values are used */
+ sp.p = *qpol_iterator_policy(iter);
+ qp.p = &sp;
+ qp.fn = NULL;
+
+ do {
+ hash_state_next(iter);
+ if (hash_state_end(iter))
+ break;
+ internal_class = hs->node ? (class_datum_t *) hs->node->datum : NULL;
+ qpol_class_get_perm_iter(&qp, (qpol_class_t *) internal_class, &internal_perms);
+ for (; !qpol_iterator_end(internal_perms); qpol_iterator_next(internal_perms)) {
+ qpol_iterator_get_item(internal_perms, (void **)&tmp);
+ if (!strcmp(tmp, hs->perm_name)) {
+ has_perm = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&internal_perms);
+ } while (!has_perm && !hash_state_end(iter));
+
+ return STATUS_SUCCESS;
+}
+
+static size_t hash_perm_state_size_common(const qpol_iterator_t * iter)
+{
+ perm_hash_state_t *hs = NULL;
+ uint32_t tmp_bucket = 0;
+ size_t count = 0;
+ hashtab_node_t *tmp_node;
+ sepol_policydb_t sp;
+ qpol_policy_t qp;
+ qpol_iterator_t *internal_perms;
+ common_datum_t *internal_common;
+ char *tmp = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ hs = (perm_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ /* shallow copy ok here as only internal values are used */
+ sp.p = *qpol_iterator_policy(iter);
+ if (&sp.p == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ qp.p = &sp;
+ qp.fn = NULL;
+ for (tmp_bucket = 0; tmp_bucket < (*(hs->table))->size; tmp_bucket++) {
+ for (tmp_node = (*(hs->table))->htable[tmp_bucket]; tmp_node; tmp_node = tmp_node->next) {
+ internal_common = tmp_node ? ((common_datum_t *) tmp_node->datum) : NULL;
+ qpol_common_get_perm_iter(&qp, (qpol_common_t *) internal_common, &internal_perms);
+ for (; !qpol_iterator_end(internal_perms); qpol_iterator_next(internal_perms)) {
+ qpol_iterator_get_item(internal_perms, (void **)&tmp);
+ if (!strcmp(tmp, hs->perm_name)) {
+ count++;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&internal_perms);
+ }
+ }
+
+ return count;
+}
+
+static size_t hash_perm_state_size_class(const qpol_iterator_t * iter)
+{
+ perm_hash_state_t *hs = NULL;
+ uint32_t tmp_bucket = 0;
+ size_t count = 0;
+ hashtab_node_t *tmp_node;
+ sepol_policydb_t sp;
+ qpol_policy_t qp;
+ qpol_iterator_t *internal_perms;
+ class_datum_t *internal_class;
+ char *tmp = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ hs = (perm_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ /* shallow copy ok here as only internal values are used */
+ sp.p = *qpol_iterator_policy(iter);
+ qp.p = &sp;
+ qp.fn = NULL;
+ if (&sp.p == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ for (tmp_bucket = 0; tmp_bucket < (*(hs->table))->size; tmp_bucket++) {
+ for (tmp_node = (*(hs->table))->htable[tmp_bucket]; tmp_node; tmp_node = tmp_node->next) {
+ internal_class = tmp_node ? ((class_datum_t *) tmp_node->datum) : NULL;
+ qpol_class_get_perm_iter(&qp, (qpol_class_t *) internal_class, &internal_perms);
+ for (; !qpol_iterator_end(internal_perms); qpol_iterator_next(internal_perms)) {
+ qpol_iterator_get_item(internal_perms, (void **)&tmp);
+ if (!strcmp(tmp, hs->perm_name)) {
+ count++;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&internal_perms);
+ }
+ }
+
+ return count;
+}
+
+static int hash_state_next_common_w_perm(qpol_iterator_t * iter)
+{
+ common_datum_t *internal_common = NULL;
+ qpol_iterator_t *internal_perms = NULL;
+ unsigned char has_perm = 0;
+ perm_hash_state_t *hs = NULL;
+ sepol_policydb_t sp;
+ qpol_policy_t qp;
+ char *tmp = NULL;
+
+ hs = (perm_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ /* shallow copy ok here as only internal values are used */
+ sp.p = *qpol_iterator_policy(iter);
+ qp.p = &sp;
+ qp.fn = NULL;
+
+ do {
+ hash_state_next(iter);
+ if (hash_state_end(iter))
+ break;
+ internal_common = hs->node ? (common_datum_t *) hs->node->datum : NULL;
+ qpol_common_get_perm_iter(&qp, (qpol_common_t *) internal_common, &internal_perms);
+ for (; !qpol_iterator_end(internal_perms); qpol_iterator_next(internal_perms)) {
+ qpol_iterator_get_item(internal_perms, (void **)&tmp);
+ if (!strcmp(tmp, hs->perm_name)) {
+ has_perm = 1;
+ break;
+ }
+ }
+ qpol_iterator_destroy(&internal_perms);
+ } while (!has_perm && !hash_state_end(iter));
+
+ return STATUS_SUCCESS;
+}
+
+static int qpol_class_has_perm(const qpol_policy_t * p, const qpol_class_t * class, const char *perm)
+{
+ qpol_iterator_t *iter = NULL;
+ char *tmp;
+
+ qpol_class_get_perm_iter(p, class, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&tmp);
+ if (!strcmp(perm, tmp)) {
+ qpol_iterator_destroy(&iter);
+ return 1;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return 0;
+}
+
+static int qpol_common_has_perm(const qpol_policy_t * p, const qpol_common_t * common, const char *perm)
+{
+ qpol_iterator_t *iter = NULL;
+ char *tmp;
+
+ qpol_common_get_perm_iter(p, common, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&tmp);
+ if (!strcmp(perm, tmp)) {
+ qpol_iterator_destroy(&iter);
+ return 1;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return 0;
+}
+
+int qpol_perm_get_class_iter(const qpol_policy_t * policy, const char *perm, qpol_iterator_t ** classes)
+{
+ policydb_t *db;
+ int error = 0;
+ perm_hash_state_t *hs = NULL;
+
+ if (policy == NULL || classes == NULL) {
+ if (classes != NULL)
+ *classes = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(perm_hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_classes.table;
+ hs->node = (*(hs->table))->htable[0];
+ hs->perm_name = perm;
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next_class_w_perm, hash_state_end, hash_perm_state_size_class, free, classes)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL || !qpol_class_has_perm(policy, (qpol_class_t *) hs->node->datum, perm))
+ hash_state_next_class_w_perm(*classes);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_perm_get_common_iter(const qpol_policy_t * policy, const char *perm, qpol_iterator_t ** commons)
+{
+ policydb_t *db;
+ int error = 0;
+ perm_hash_state_t *hs = NULL;
+
+ if (policy == NULL || commons == NULL) {
+ if (commons != NULL)
+ *commons = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(perm_hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_commons.table;
+ hs->node = (*(hs->table))->htable[0];
+ hs->perm_name = perm;
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next_common_w_perm, hash_state_end, hash_perm_state_size_common, free, commons)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL || !qpol_common_has_perm(policy, (qpol_common_t *) hs->node->datum, perm))
+ hash_state_next_common_w_perm(*commons);
+
+ return STATUS_SUCCESS;
+}
+
+/* classes */
+int qpol_policy_get_class_by_name(const qpol_policy_t * policy, const char *name, const qpol_class_t ** obj_class)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || obj_class == NULL) {
+ if (obj_class != NULL)
+ *obj_class = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_classes.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ *obj_class = NULL;
+ ERR(policy, "could not find class %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ *obj_class = (qpol_class_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_class_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_classes.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_class_get_value(const qpol_policy_t * policy, const qpol_class_t * obj_class, uint32_t * value)
+{
+ class_datum_t *internal_datum;
+
+ if (policy == NULL || obj_class == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (class_datum_t *) obj_class;
+ *value = internal_datum->s.value;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_class_get_common(const qpol_policy_t * policy, const qpol_class_t * obj_class, const qpol_common_t ** common)
+{
+ class_datum_t *internal_datum = NULL;
+
+ if (policy == NULL || obj_class == NULL || common == NULL) {
+ if (common != NULL)
+ *common = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (class_datum_t *) obj_class;
+ *common = (qpol_common_t *) internal_datum->comdatum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_class_get_perm_iter(const qpol_policy_t * policy, const qpol_class_t * obj_class, qpol_iterator_t ** perms)
+{
+ class_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || obj_class == NULL || perms == NULL) {
+ if (perms != NULL)
+ *perms = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (class_datum_t *) obj_class;
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &internal_datum->permissions.table;
+ if (hs->table && *(hs->table)) {
+ hs->node = (*(hs->table))->htable[0];
+ } else { /* object class has no permissions */
+ hs->node = NULL;
+ }
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur_key,
+ hash_state_next, hash_state_end, hash_state_size, free, perms)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*perms);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_class_get_name(const qpol_policy_t * policy, const qpol_class_t * obj_class, const char **name)
+{
+ class_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || obj_class == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (class_datum_t *) obj_class;
+
+ *name = db->p_class_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
+
+/* commons */
+int qpol_policy_get_common_by_name(const qpol_policy_t * policy, const char *name, const qpol_common_t ** common)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || common == NULL) {
+ if (common != NULL)
+ *common = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_commons.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ *common = NULL;
+ ERR(policy, "could not find common %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *common = (qpol_common_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_common_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_commons.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_common_get_value(const qpol_policy_t * policy, const qpol_common_t * common, uint32_t * value)
+{
+ common_datum_t *internal_datum;
+
+ if (policy == NULL || common == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (common_datum_t *) common;
+ *value = internal_datum->s.value;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_common_get_perm_iter(const qpol_policy_t * policy, const qpol_common_t * common, qpol_iterator_t ** perms)
+{
+ common_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || common == NULL || perms == NULL) {
+ if (perms != NULL)
+ *perms = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (common_datum_t *) common;
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &internal_datum->permissions.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur_key,
+ hash_state_next, hash_state_end, hash_state_size, free, perms)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*perms);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_common_get_name(const qpol_policy_t * policy, const qpol_common_t * common, const char **name)
+{
+ common_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || common == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (common_datum_t *) common;
+
+ *name = db->p_common_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/cond_query.c b/libqpol/src/cond_query.c
new file mode 100644
index 0000000..4bd3adf
--- /dev/null
+++ b/libqpol/src/cond_query.c
@@ -0,0 +1,620 @@
+/**
+ * @file
+ * Implememtation for the public interface for searching and iterating
+ * conditionals
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <qpol/policy.h>
+#include <qpol/cond_query.h>
+#include <qpol/bool_query.h>
+#include <qpol/avrule_query.h>
+#include <qpol/terule_query.h>
+#include <qpol/iterator.h>
+#include "iterator_internal.h"
+#include "qpol_internal.h"
+
+#include <sepol/policydb/conditional.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+typedef struct cond_state
+{
+ cond_node_t *head;
+ cond_node_t *cur;
+} cond_state_t;
+
+static int cond_state_end(const qpol_iterator_t * iter)
+{
+ cond_state_t *cs = NULL;
+
+ if (!iter || !(cs = (cond_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return cs->cur ? 0 : 1;
+}
+
+static void *cond_state_get_cur(const qpol_iterator_t * iter)
+{
+ cond_state_t *cs = NULL;
+
+ if (!iter || !(cs = (cond_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return cs->cur;
+}
+
+static int cond_state_next(qpol_iterator_t * iter)
+{
+ cond_state_t *cs = NULL;
+
+ if (!iter || !(cs = (cond_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ cs->cur = cs->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t cond_state_size(const qpol_iterator_t * iter)
+{
+ cond_state_t *cs = NULL;
+ cond_node_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(cs = (cond_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = cs->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_policy_get_cond_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ int error = 0;
+ cond_state_t *cs = NULL;
+ policydb_t *db = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!qpol_policy_has_capability(policy, QPOL_CAP_RULES_LOADED)) {
+ ERR(policy, "%s", "Cannot get conditionals: Rules not loaded");
+ errno = ENOTSUP;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ if (!(cs = calloc(1, sizeof(cond_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ cs->head = cs->cur = db->cond_list;
+
+ if (qpol_iterator_create(policy, (void *)cs,
+ cond_state_get_cur, cond_state_next, cond_state_end, cond_state_size, free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(cs);
+ errno = error;
+ return STATUS_ERR;
+}
+
+typedef struct cond_expr_state
+{
+ cond_expr_t *head;
+ cond_expr_t *cur;
+} cond_expr_state_t;
+
+static int cond_expr_state_end(const qpol_iterator_t * iter)
+{
+ cond_expr_state_t *ces = NULL;
+
+ if (!iter || !(ces = (cond_expr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return ces->cur ? 0 : 1;
+}
+
+static void *cond_expr_state_get_cur(const qpol_iterator_t * iter)
+{
+ cond_expr_state_t *ces = NULL;
+
+ if (!iter || !(ces = (cond_expr_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return ces->cur;
+}
+
+static int cond_expr_state_next(qpol_iterator_t * iter)
+{
+ cond_expr_state_t *ces = NULL;
+
+ if (!iter || !(ces = (cond_expr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ ces->cur = ces->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t cond_expr_state_size(const qpol_iterator_t * iter)
+{
+ cond_expr_state_t *ces = NULL;
+ cond_expr_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(ces = (cond_expr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = ces->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_cond_get_expr_node_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, qpol_iterator_t ** iter)
+{
+ int error = 0;
+ cond_expr_state_t *ces = NULL;
+ cond_node_t *internal_cond = NULL;
+ policydb_t *db = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !cond || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_cond = (cond_node_t *) cond;
+
+ if (!(ces = calloc(1, sizeof(cond_expr_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ ces->head = ces->cur = internal_cond->expr;
+
+ if (qpol_iterator_create(policy, (void *)ces,
+ cond_expr_state_get_cur, cond_expr_state_next, cond_expr_state_end,
+ cond_expr_state_size, free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(ces);
+ errno = error;
+ return STATUS_ERR;
+}
+
+typedef struct cond_rule_state
+{
+ cond_av_list_t *head;
+ cond_av_list_t *cur;
+ uint32_t rule_type_mask;
+} cond_rule_state_t;
+
+static int cond_rule_state_end(const qpol_iterator_t * iter)
+{
+ cond_rule_state_t *crs = NULL;
+
+ if (!iter || !(crs = (cond_rule_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return crs->cur ? 0 : 1;
+}
+
+static void *cond_rule_state_get_cur(const qpol_iterator_t * iter)
+{
+ cond_rule_state_t *crs = NULL;
+
+ if (!iter || !(crs = (cond_rule_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return crs->cur->node;
+}
+
+static int cond_rule_state_next(qpol_iterator_t * iter)
+{
+ cond_rule_state_t *crs = NULL;
+
+ if (!iter || !(crs = (cond_rule_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ crs->cur = crs->cur->next;
+ } while (crs->cur && !(crs->cur->node->key.specified & crs->rule_type_mask));
+
+ return STATUS_SUCCESS;
+}
+
+static size_t cond_rule_state_size(const qpol_iterator_t * iter)
+{
+ cond_rule_state_t *crs = NULL;
+ cond_av_list_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(crs = (cond_rule_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = crs->head; tmp; tmp = tmp->next) {
+ if (tmp->node->key.specified & crs->rule_type_mask)
+ count++;
+ }
+
+ return count;
+}
+
+int qpol_cond_get_av_true_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter)
+{
+ int error = 0;
+ cond_rule_state_t *crs = NULL;
+ cond_node_t *internal_cond = NULL;
+ policydb_t *db = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !cond || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (rule_type_mask & ~(QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT)) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_cond = (cond_node_t *) cond;
+
+ if (!(crs = calloc(1, sizeof(cond_rule_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ crs->head = crs->cur = internal_cond->true_list;
+ crs->rule_type_mask = rule_type_mask;
+
+ if (qpol_iterator_create(policy, (void *)crs,
+ cond_rule_state_get_cur, cond_rule_state_next, cond_rule_state_end,
+ cond_rule_state_size, free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (crs->cur && !(crs->cur->node->key.specified & crs->rule_type_mask))
+ qpol_iterator_next(*iter);
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(crs);
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_cond_get_te_true_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter)
+{
+ int error = 0;
+ cond_rule_state_t *crs = NULL;
+ cond_node_t *internal_cond = NULL;
+ policydb_t *db = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !cond || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (rule_type_mask & ~(QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER)) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_cond = (cond_node_t *) cond;
+
+ if (!(crs = calloc(1, sizeof(cond_rule_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ crs->head = crs->cur = internal_cond->true_list;
+ crs->rule_type_mask = rule_type_mask;
+
+ if (qpol_iterator_create(policy, (void *)crs,
+ cond_rule_state_get_cur, cond_rule_state_next, cond_rule_state_end,
+ cond_rule_state_size, free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (crs->cur && !(crs->cur->node->key.specified & crs->rule_type_mask))
+ qpol_iterator_next(*iter);
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(crs);
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_cond_get_av_false_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter)
+{
+ int error = 0;
+ cond_rule_state_t *crs = NULL;
+ cond_node_t *internal_cond = NULL;
+ policydb_t *db = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !cond || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (rule_type_mask & ~(QPOL_RULE_ALLOW | QPOL_RULE_NEVERALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT)) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_cond = (cond_node_t *) cond;
+
+ if (!(crs = calloc(1, sizeof(cond_rule_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ crs->head = crs->cur = internal_cond->false_list;
+ crs->rule_type_mask = rule_type_mask;
+
+ if (qpol_iterator_create(policy, (void *)crs,
+ cond_rule_state_get_cur, cond_rule_state_next, cond_rule_state_end,
+ cond_rule_state_size, free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (crs->cur && !(crs->cur->node->key.specified & crs->rule_type_mask))
+ qpol_iterator_next(*iter);
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(crs);
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_cond_get_te_false_iter(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t rule_type_mask,
+ qpol_iterator_t ** iter)
+{
+ int error = 0;
+ cond_rule_state_t *crs = NULL;
+ cond_node_t *internal_cond = NULL;
+ policydb_t *db = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !cond || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (rule_type_mask & ~(QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER)) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_cond = (cond_node_t *) cond;
+
+ if (!(crs = calloc(1, sizeof(cond_rule_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ crs->head = crs->cur = internal_cond->false_list;
+ crs->rule_type_mask = rule_type_mask;
+
+ if (qpol_iterator_create(policy, (void *)crs,
+ cond_rule_state_get_cur, cond_rule_state_next, cond_rule_state_end,
+ cond_rule_state_size, free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (crs->cur && !(crs->cur->node->key.specified & crs->rule_type_mask))
+ qpol_iterator_next(*iter);
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(crs);
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_cond_eval(const qpol_policy_t * policy, const qpol_cond_t * cond, uint32_t * is_true)
+{
+ int error = 0;
+ cond_node_t *internal_cond = NULL;
+
+ if (is_true)
+ *is_true = 0;
+
+ if (!policy || !cond || !is_true) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_cond = (cond_node_t *) cond;
+
+ if ((*is_true = (uint32_t) cond_evaluate_expr(&policy->p->p, internal_cond->expr)) > 1) {
+ error = ERANGE;
+ goto err;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_cond_expr_node_get_expr_type(const qpol_policy_t * policy, const qpol_cond_expr_node_t * node, uint32_t * expr_type)
+{
+ cond_expr_t *internal_cond = NULL;
+
+ if (expr_type)
+ *expr_type = 0;
+
+ if (!policy || !node || !expr_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_cond = (cond_expr_t *) node;
+
+ *expr_type = internal_cond->expr_type;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_cond_expr_node_get_bool(const qpol_policy_t * policy, const qpol_cond_expr_node_t * node, qpol_bool_t ** cond_bool)
+{
+ int error = 0;
+ cond_expr_t *internal_cond = NULL;
+ policydb_t *db = NULL;
+
+ if (cond_bool)
+ *cond_bool = NULL;
+
+ if (!policy || !node || !cond_bool) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_cond = (cond_expr_t *) node;
+
+ if (internal_cond->expr_type != QPOL_COND_EXPR_BOOL) {
+ error = EINVAL;
+ goto err;
+ }
+
+ if (!(*cond_bool = (qpol_bool_t *) db->bool_val_to_struct[internal_cond->bool - 1])) {
+ error = EINVAL;
+ goto err;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+}
diff --git a/libqpol/src/constraint_query.c b/libqpol/src/constraint_query.c
new file mode 100644
index 0000000..1545ad7
--- /dev/null
+++ b/libqpol/src/constraint_query.c
@@ -0,0 +1,983 @@
+/**
+ * @file
+ * Implementation of the public interface for searching and iterating over
+ * constraints
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <qpol/policy.h>
+#include <qpol/constraint_query.h>
+#include <qpol/iterator.h>
+#include <qpol/class_perm_query.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/constraint.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+struct qpol_constraint
+{
+ const qpol_class_t *obj_class;
+ constraint_node_t *constr;
+};
+
+typedef struct policy_constr_state
+{
+ qpol_iterator_t *class_iter;
+ qpol_iterator_t *constr_iter;
+ const qpol_policy_t *policy; /* needed to get sub iterators */
+} policy_constr_state_t;
+
+static int policy_constr_state_end(const qpol_iterator_t * iter)
+{
+ policy_constr_state_t *pcs = NULL;
+
+ if (!iter || !(pcs = (policy_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return (qpol_iterator_end(pcs->class_iter) && qpol_iterator_end(pcs->constr_iter)) ? 1 : 0;
+}
+
+static void *policy_constr_state_get_cur(const qpol_iterator_t * iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ void *tmp = NULL;
+
+ if (!iter || !(pcs = (policy_constr_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (qpol_iterator_get_item(pcs->constr_iter, &tmp)) {
+ return NULL;
+ }
+
+ return tmp;
+}
+
+static int policy_constr_state_next(qpol_iterator_t * iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ qpol_class_t *obj_class = NULL;
+
+ if (!iter || !(pcs = (policy_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ qpol_iterator_next(pcs->constr_iter);
+ while (qpol_iterator_end(pcs->constr_iter)) {
+ qpol_iterator_destroy(&pcs->constr_iter);
+ qpol_iterator_next(pcs->class_iter);
+ if (qpol_iterator_end(pcs->class_iter))
+ return STATUS_SUCCESS;
+ if (qpol_iterator_get_item(pcs->class_iter, (void **)&obj_class))
+ return STATUS_ERR;
+ if (qpol_class_get_constraint_iter(pcs->policy, obj_class, &pcs->constr_iter))
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static size_t policy_constr_state_size(const qpol_iterator_t * iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *internal_iter = NULL, *constr_iter = NULL;
+ size_t count = 0, tmp = 0;
+
+ if (!iter || !(pcs = (policy_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (qpol_policy_get_class_iter(pcs->policy, &internal_iter))
+ return 0;
+
+ for (; !qpol_iterator_end(internal_iter); qpol_iterator_next(internal_iter)) {
+ if (qpol_iterator_get_item(internal_iter, (void **)&obj_class))
+ goto err;
+ if (qpol_class_get_constraint_iter(pcs->policy, obj_class, &constr_iter))
+ goto err;
+ if (qpol_iterator_get_size(constr_iter, &tmp))
+ goto err;
+ count += tmp;
+ tmp = 0;
+ qpol_iterator_destroy(&constr_iter);
+ }
+
+ qpol_iterator_destroy(&internal_iter);
+ return count;
+
+ err:
+ qpol_iterator_destroy(&internal_iter);
+ qpol_iterator_destroy(&constr_iter);
+ return 0;
+}
+
+static void policy_constr_state_free(void *x)
+{
+ policy_constr_state_t *pcs = (policy_constr_state_t *) x;
+
+ if (!pcs)
+ return;
+
+ qpol_iterator_destroy(&pcs->class_iter);
+ qpol_iterator_destroy(&pcs->constr_iter);
+ free(pcs);
+}
+
+int qpol_policy_get_constraint_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ int error = 0;
+ qpol_class_t *tmp = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(pcs = calloc(1, sizeof(policy_constr_state_t)))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ pcs->policy = policy;
+ if (qpol_policy_get_class_iter(policy, &pcs->class_iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_item(pcs->class_iter, (void **)&tmp)) {
+ error = errno;
+ ERR(policy, "Error getting first class: %s", strerror(error));
+ goto err;
+ }
+ if (qpol_class_get_constraint_iter(policy, tmp, &pcs->constr_iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (qpol_iterator_create(policy, (void *)pcs,
+ policy_constr_state_get_cur, policy_constr_state_next,
+ policy_constr_state_end, policy_constr_state_size, policy_constr_state_free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (qpol_iterator_end(pcs->constr_iter)) {
+ if (qpol_iterator_next(*iter)) {
+ error = errno;
+ pcs = NULL; /* avoid double free, iterator will destroy this */
+ ERR(policy, "Error finding first constraint: %s", strerror(error));
+ goto err;
+ }
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ policy_constr_state_free(pcs);
+ qpol_iterator_destroy(iter);
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_constraint_get_class(const qpol_policy_t * policy, const qpol_constraint_t * constr, const qpol_class_t ** obj_class)
+{
+ if (obj_class)
+ *obj_class = NULL;
+
+ if (!policy || !constr || !obj_class) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *obj_class = constr->obj_class;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_constraint_get_perm_iter(const qpol_policy_t * policy, const qpol_constraint_t * constr, qpol_iterator_t ** iter)
+{
+ perm_state_t *ps = NULL;
+ constraint_node_t *internal_constr = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !constr || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_constr = (constraint_node_t *) constr->constr;
+
+ if (!(ps = calloc(1, sizeof(perm_state_t)))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ }
+ ps->perm_set = internal_constr->permissions;
+ qpol_class_get_value(policy, constr->obj_class, &ps->obj_class_val);
+
+ if (qpol_iterator_create(policy, (void *)ps, perm_state_get_cur,
+ perm_state_next, perm_state_end, perm_state_size, free, iter)) {
+ free(ps);
+ return STATUS_ERR;
+ }
+
+ if (!(ps->perm_set & 1)) /* defaults to bit 0 */
+ qpol_iterator_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct constr_expr_state
+{
+ constraint_expr_t *head;
+ constraint_expr_t *cur;
+} constr_expr_state_t;
+
+static int constr_expr_state_end(const qpol_iterator_t * iter)
+{
+ constr_expr_state_t *ces = NULL;
+
+ if (!iter || !(ces = (constr_expr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return ces->cur ? 0 : 1;
+}
+
+static void *constr_expr_state_get_cur(const qpol_iterator_t * iter)
+{
+ constr_expr_state_t *ces = NULL;
+
+ if (!iter || !(ces = (constr_expr_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return ces->cur;
+}
+
+static int constr_expr_state_next(qpol_iterator_t * iter)
+{
+ constr_expr_state_t *ces = NULL;
+
+ if (!iter || !(ces = (constr_expr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ ces->cur = ces->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t constr_expr_state_size(const qpol_iterator_t * iter)
+{
+ constr_expr_state_t *ces = NULL;
+ constraint_expr_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(ces = (constr_expr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = ces->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_constraint_get_expr_iter(const qpol_policy_t * policy, const qpol_constraint_t * constr, qpol_iterator_t ** iter)
+{
+ constr_expr_state_t *ces = NULL;
+ constraint_node_t *internal_constr = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !constr || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_constr = (constraint_node_t *) constr->constr;
+
+ if (!(ces = calloc(1, sizeof(constr_expr_state_t)))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ ces->head = ces->cur = internal_constr->expr;
+
+ if (qpol_iterator_create(policy, (void *)ces,
+ constr_expr_state_get_cur, constr_expr_state_next,
+ constr_expr_state_end, constr_expr_state_size, free, iter)) {
+ free(ces);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int policy_constr_state_next_vtrans(qpol_iterator_t * iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ qpol_class_t *obj_class = NULL;
+
+ if (!iter || !(pcs = (policy_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ qpol_iterator_next(pcs->constr_iter);
+ while (qpol_iterator_end(pcs->constr_iter)) {
+ qpol_iterator_destroy(&pcs->constr_iter);
+ qpol_iterator_next(pcs->class_iter);
+ if (qpol_iterator_end(pcs->class_iter))
+ return STATUS_SUCCESS;
+ if (qpol_iterator_get_item(pcs->class_iter, (void **)&obj_class))
+ return STATUS_ERR;
+ if (qpol_class_get_validatetrans_iter(pcs->policy, obj_class, &pcs->constr_iter))
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static size_t policy_constr_state_size_vtrans(const qpol_iterator_t * iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *internal_iter = NULL, *constr_iter = NULL;
+ size_t count = 0, tmp = 0;
+
+ if (!iter || !(pcs = (policy_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (qpol_policy_get_class_iter(pcs->policy, &internal_iter))
+ return 0;
+
+ for (; !qpol_iterator_end(internal_iter); qpol_iterator_next(internal_iter)) {
+ if (qpol_iterator_get_item(internal_iter, (void **)&obj_class))
+ goto err;
+ if (qpol_class_get_validatetrans_iter(pcs->policy, obj_class, &constr_iter))
+ goto err;
+ if (qpol_iterator_get_size(constr_iter, &tmp))
+ goto err;
+ count += tmp;
+ tmp = 0;
+ qpol_iterator_destroy(&constr_iter);
+ }
+
+ qpol_iterator_destroy(&internal_iter);
+ return count;
+
+ err:
+ qpol_iterator_destroy(&internal_iter);
+ qpol_iterator_destroy(&constr_iter);
+ return 0;
+}
+
+int qpol_policy_get_validatetrans_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policy_constr_state_t *pcs = NULL;
+ int error = 0;
+ qpol_class_t *tmp = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(pcs = calloc(1, sizeof(policy_constr_state_t)))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ pcs->policy = policy;
+ if (qpol_policy_get_class_iter(policy, &pcs->class_iter)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_iterator_get_item(pcs->class_iter, (void **)&tmp)) {
+ error = errno;
+ ERR(policy, "Error getting first class: %s", strerror(error));
+ goto err;
+ }
+ if (qpol_class_get_validatetrans_iter(policy, tmp, &pcs->constr_iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (qpol_iterator_create(policy, (void *)pcs,
+ policy_constr_state_get_cur, policy_constr_state_next_vtrans,
+ policy_constr_state_end, policy_constr_state_size_vtrans, policy_constr_state_free, iter)) {
+ error = errno;
+ goto err;
+ }
+
+ if (qpol_iterator_end(pcs->constr_iter)) {
+ if (qpol_iterator_next(*iter)) {
+ error = errno;
+ pcs = NULL; /* avoid double free, iterator will destroy this */
+ ERR(policy, "Error finding first validatetrans: %s", strerror(error));
+ goto err;
+ }
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ policy_constr_state_free(pcs);
+ qpol_iterator_destroy(iter);
+ errno = error;
+ return STATUS_ERR;
+
+}
+
+int qpol_validatetrans_get_class(const qpol_policy_t * policy, const qpol_validatetrans_t * vtrans, const qpol_class_t ** obj_class)
+{
+ if (obj_class)
+ *obj_class = NULL;
+
+ if (!policy || !vtrans || !obj_class) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *obj_class = ((qpol_constraint_t *) vtrans)->obj_class;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_validatetrans_get_expr_iter(const qpol_policy_t * policy, const qpol_validatetrans_t * vtrans, qpol_iterator_t ** iter)
+{
+ constr_expr_state_t *ces = NULL;
+ constraint_node_t *internal_constr = NULL;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !vtrans || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_constr = (constraint_node_t *) ((qpol_constraint_t *) vtrans)->constr;
+
+ if (!(ces = calloc(1, sizeof(constr_expr_state_t)))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ ces->head = ces->cur = internal_constr->expr;
+
+ if (qpol_iterator_create(policy, (void *)ces,
+ constr_expr_state_get_cur, constr_expr_state_next,
+ constr_expr_state_end, constr_expr_state_size, free, iter)) {
+ free(ces);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_constraint_expr_node_get_expr_type(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ uint32_t * expr_type)
+{
+ constraint_expr_t *internal_expr = NULL;
+
+ if (!policy || !expr || !expr_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_expr = (constraint_expr_t *) expr;
+
+ *expr_type = internal_expr->expr_type;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_constraint_expr_node_get_sym_type(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ uint32_t * sym_type)
+{
+ constraint_expr_t *internal_expr = NULL;
+
+ if (!policy || !expr || !sym_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_expr = (constraint_expr_t *) expr;
+
+ *sym_type = internal_expr->attr;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_constraint_expr_node_get_op(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr, uint32_t * op)
+{
+ constraint_expr_t *internal_expr = NULL;
+
+ if (!policy || !expr || !op) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_expr = (constraint_expr_t *) expr;
+
+ *op = internal_expr->op;
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct cexpr_name_state
+{
+ ebitmap_t *inc;
+ ebitmap_t *sub;
+ size_t cur;
+#define QPOL_CEXPR_NAME_STATE_INC_LIST 0
+#define QPOL_CEXPR_NAME_STATE_SUB_LIST 1
+ unsigned char list;
+} cexpr_name_state_t;
+
+static int cexpr_name_state_end(const qpol_iterator_t * iter)
+{
+ cexpr_name_state_t *cns = NULL;
+
+ if (!iter || !(cns = (cexpr_name_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (cns->list == QPOL_CEXPR_NAME_STATE_SUB_LIST && (cns->sub ? cns->cur >= cns->sub->highbit : 1))
+ return 1;
+
+ return 0;
+}
+
+static int cexpr_name_state_next(qpol_iterator_t * iter)
+{
+ cexpr_name_state_t *cns = NULL;
+ ebitmap_t *bmap = NULL;
+
+ if (!iter || !(cns = (cexpr_name_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ bmap = (cns->list == QPOL_CEXPR_NAME_STATE_INC_LIST ? cns->inc : cns->sub);
+
+ do {
+ cns->cur++;
+ if (cns->cur >= bmap->highbit) {
+ if (cns->list == QPOL_CEXPR_NAME_STATE_INC_LIST) {
+ cns->list = QPOL_CEXPR_NAME_STATE_SUB_LIST;
+ cns->cur = 0;
+ bmap = cns->sub;
+ if (!bmap)
+ break;
+ } else {
+ break;
+ }
+ }
+ } while (!ebitmap_get_bit(bmap, cns->cur));
+
+ return STATUS_SUCCESS;
+}
+
+static size_t cexpr_name_state_size(const qpol_iterator_t * iter)
+{
+ cexpr_name_state_t *cns = NULL;
+ size_t count = 0, bit = 0;
+ ebitmap_node_t *node = NULL;
+
+ if (!iter || !(cns = (cexpr_name_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ ebitmap_for_each_bit(cns->inc, node, bit) {
+ count += ebitmap_get_bit(cns->inc, bit);
+ }
+
+ if (!(cns->sub))
+ return count;
+
+ bit = 0;
+ ebitmap_for_each_bit(cns->sub, node, bit) {
+ count += ebitmap_get_bit(cns->sub, bit);
+ }
+
+ return count;
+}
+
+static void *cexpr_name_state_get_cur_user(const qpol_iterator_t * iter)
+{
+ cexpr_name_state_t *cns = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(cns = (cexpr_name_state_t *) qpol_iterator_state(iter)) ||
+ !(db = qpol_iterator_policy(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return strdup(db->p_user_val_to_name[cns->cur]);
+}
+
+static void *cexpr_name_state_get_cur_role(const qpol_iterator_t * iter)
+{
+ cexpr_name_state_t *cns = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(cns = (cexpr_name_state_t *) qpol_iterator_state(iter)) ||
+ !(db = qpol_iterator_policy(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return strdup(db->p_role_val_to_name[cns->cur]);
+}
+
+static void *cexpr_name_state_get_cur_type(const qpol_iterator_t * iter)
+{
+ cexpr_name_state_t *cns = NULL;
+ const policydb_t *db = NULL;
+ char *tmp = NULL, *name = NULL;
+ size_t len = 0;
+
+ if (!iter || !(cns = (cexpr_name_state_t *) qpol_iterator_state(iter)) ||
+ !(db = qpol_iterator_policy(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ tmp = strdup(db->p_type_val_to_name[cns->cur]);
+
+ if (cns->list == QPOL_CEXPR_NAME_STATE_INC_LIST)
+ return tmp;
+
+ len = strlen(tmp);
+ name = calloc(len + 2, sizeof(char));
+ if (!name) {
+ free(tmp);
+ errno = ENOMEM;
+ return NULL;
+ }
+ len++;
+
+ snprintf(name, len + 1, "-%s", tmp);
+ free(tmp);
+
+ return name;
+}
+
+int qpol_constraint_expr_node_get_names_iter(const qpol_policy_t * policy, const qpol_constraint_expr_node_t * expr,
+ qpol_iterator_t ** iter)
+{
+ constraint_expr_t *internal_expr = NULL;
+ cexpr_name_state_t *cns = NULL;
+ int policy_type = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !expr || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_policy_get_type(policy, &policy_type))
+ return STATUS_ERR;
+
+ internal_expr = (constraint_expr_t *) expr;
+
+ if (internal_expr->expr_type != QPOL_CEXPR_TYPE_NAMES) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(cns = calloc(1, sizeof(cexpr_name_state_t)))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ if (internal_expr->attr & QPOL_CEXPR_SYM_TYPE) {
+ if (policy_type == QPOL_POLICY_KERNEL_BINARY) {
+ cns->inc = &(internal_expr->names);
+ } else {
+ cns->inc = &(internal_expr->type_names->types);
+ cns->sub = &(internal_expr->type_names->negset);
+ }
+ } else {
+ cns->inc = &(internal_expr->names);
+ }
+ cns->list = QPOL_CEXPR_NAME_STATE_INC_LIST;
+ cns->cur = cns->inc->node ? cns->inc->node->startbit : 0;
+
+ switch (internal_expr->attr & ~(QPOL_CEXPR_SYM_TARGET | QPOL_CEXPR_SYM_XTARGET)) {
+ case QPOL_CEXPR_SYM_USER:
+ {
+ if (qpol_iterator_create(policy, (void *)cns,
+ cexpr_name_state_get_cur_user, cexpr_name_state_next,
+ cexpr_name_state_end, cexpr_name_state_size, free, iter)) {
+ return STATUS_ERR;
+ }
+ break;
+ }
+ case QPOL_CEXPR_SYM_ROLE:
+ {
+ if (qpol_iterator_create(policy, (void *)cns,
+ cexpr_name_state_get_cur_role, cexpr_name_state_next,
+ cexpr_name_state_end, cexpr_name_state_size, free, iter)) {
+ return STATUS_ERR;
+ }
+ break;
+ }
+ case QPOL_CEXPR_SYM_TYPE:
+ {
+ if (qpol_iterator_create(policy, (void *)cns,
+ cexpr_name_state_get_cur_type, cexpr_name_state_next,
+ cexpr_name_state_end, cexpr_name_state_size, free, iter)) {
+ return STATUS_ERR;
+ }
+ break;
+ }
+ default:
+ {
+ ERR(policy, "%s", strerror(EINVAL));
+ free(cns);
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ }
+
+ if (cns->inc->node && !ebitmap_get_bit(cns->inc, cns->cur))
+ qpol_iterator_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct class_constr_state
+{
+ constraint_node_t *head;
+ constraint_node_t *cur;
+ const qpol_class_t *obj_class;
+} class_constr_state_t;
+
+static int class_constr_state_end(const qpol_iterator_t * iter)
+{
+ class_constr_state_t *ccs = NULL;
+
+ if (!iter || !(ccs = (class_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return ccs->cur ? 0 : 1;
+}
+
+static void *class_constr_state_get_cur(const qpol_iterator_t * iter)
+{
+ class_constr_state_t *ccs = NULL;
+ qpol_constraint_t *qc = NULL;
+
+ if (!iter || !(ccs = (class_constr_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(qc = calloc(1, sizeof(qpol_constraint_t)))) {
+ return NULL; /* errno set by calloc */
+ }
+ qc->obj_class = ccs->obj_class;
+ qc->constr = ccs->cur;
+
+ return qc;
+}
+
+static int class_constr_state_next(qpol_iterator_t * iter)
+{
+ class_constr_state_t *ccs = NULL;
+
+ if (!iter || !(ccs = (class_constr_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ ccs->cur = ccs->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t class_constr_state_size(const qpol_iterator_t * iter)
+{
+ class_constr_state_t *ccs = NULL;
+ constraint_node_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(ccs = (class_constr_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = ccs->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_class_get_constraint_iter(const qpol_policy_t * policy, const qpol_class_t * obj_class, qpol_iterator_t ** constr)
+{
+ const policydb_t *db = NULL;
+ class_constr_state_t *ccs = NULL;
+ class_datum_t *internal_class = NULL;
+ int error = 0;
+
+ if (constr)
+ *constr = NULL;
+
+ if (!policy || !obj_class || !constr) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_class = (class_datum_t *) obj_class;
+
+ ccs = calloc(1, sizeof(class_constr_state_t));
+ if (!ccs) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+ ccs->obj_class = obj_class;
+ ccs->head = ccs->cur = internal_class->constraints;
+
+ if (qpol_iterator_create(policy, (void *)ccs, class_constr_state_get_cur,
+ class_constr_state_next, class_constr_state_end, class_constr_state_size, free, constr)) {
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_class_get_validatetrans_iter(const qpol_policy_t * policy, const qpol_class_t * obj_class, qpol_iterator_t ** vtrans)
+{
+ const policydb_t *db = NULL;
+ class_constr_state_t *ccs = NULL;
+ class_datum_t *internal_class = NULL;
+ int error = 0;
+
+ if (vtrans)
+ *vtrans = NULL;
+
+ if (!policy || !obj_class || !vtrans) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_class = (class_datum_t *) obj_class;
+
+ ccs = calloc(1, sizeof(class_constr_state_t));
+ if (!ccs) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+ ccs->obj_class = obj_class;
+ ccs->head = ccs->cur = internal_class->validatetrans;
+
+ if (qpol_iterator_create(policy, (void *)ccs, class_constr_state_get_cur,
+ class_constr_state_next, class_constr_state_end, class_constr_state_size, free, vtrans)) {
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/context_query.c b/libqpol/src/context_query.c
new file mode 100644
index 0000000..cd17a21
--- /dev/null
+++ b/libqpol/src/context_query.c
@@ -0,0 +1,123 @@
+/**
+* @file
+* Defines the public interface for accessing contexts.
+*
+* @author Kevin Carr kcarr@tresys.com
+* @author Jeremy A. Mowery jmowery@tresys.com
+* @author Jason Tang jtang@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+#include <qpol/user_query.h>
+#include <qpol/role_query.h>
+#include <qpol/type_query.h>
+#include <qpol/mls_query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/context.h>
+#include "qpol_internal.h"
+
+int qpol_context_get_user(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_user_t ** user)
+{
+ policydb_t *db = NULL;
+ context_struct_t *internal_context = NULL;
+
+ if (user != NULL)
+ *user = NULL;
+
+ if (policy == NULL || context == NULL || user == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_context = (context_struct_t *) context;
+ db = &policy->p->p;
+
+ *user = (qpol_user_t *) db->user_val_to_struct[internal_context->user - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_context_get_role(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_role_t ** role)
+{
+ policydb_t *db = NULL;
+ context_struct_t *internal_context = NULL;
+
+ if (role != NULL)
+ *role = NULL;
+
+ if (policy == NULL || context == NULL || role == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_context = (context_struct_t *) context;
+ db = &policy->p->p;
+
+ *role = (qpol_role_t *) db->role_val_to_struct[internal_context->role - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_context_get_type(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_type_t ** type)
+{
+ policydb_t *db = NULL;
+ context_struct_t *internal_context = NULL;
+
+ if (type != NULL)
+ *type = NULL;
+
+ if (policy == NULL || context == NULL || type == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_context = (context_struct_t *) context;
+ db = &policy->p->p;
+
+ *type = (qpol_type_t *) db->type_val_to_struct[internal_context->type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_context_get_range(const qpol_policy_t * policy, const qpol_context_t * context, const qpol_mls_range_t ** range)
+{
+ context_struct_t *internal_context = NULL;
+
+ if (range != NULL)
+ *range = NULL;
+
+ if (policy == NULL || context == NULL || range == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_context = (context_struct_t *) context;
+
+ *range = (qpol_mls_range_t *) & internal_context->range;
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/expand.c b/libqpol/src/expand.c
new file mode 100644
index 0000000..7aa5bdb
--- /dev/null
+++ b/libqpol/src/expand.c
@@ -0,0 +1,190 @@
+/**
+ * @file
+ *
+ * Provides a way for setools to expand policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <sepol/policydb/expand.h>
+#include <sepol/policydb.h>
+#include <stdlib.h>
+#include "qpol_internal.h"
+#include "expand.h"
+
+static int expand_type_attr_map(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *ptr)
+{
+ type_datum_t *type = NULL, *orig_type;
+ policydb_t *db = (policydb_t *) ptr;
+ ebitmap_node_t *node = NULL;
+ uint32_t bit = 0;
+
+ type = (type_datum_t *) datum;
+ /* if this is an attribute go through its list
+ * of types and put in reverse mappings */
+ if (type->flavor == TYPE_ATTRIB) {
+ ebitmap_for_each_bit(&type->types, node, bit) {
+ if (ebitmap_node_get_bit(node, bit)) {
+ orig_type = db->type_val_to_struct[bit];
+ if (ebitmap_set_bit(&orig_type->types, type->s.value - 1, 1)) {
+ return -1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int expand_type_permissive_map(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *ptr)
+{
+#ifdef HAVE_SEPOL_PERMISSIVE_TYPES
+ type_datum_t *type = (type_datum_t *) datum;
+ policydb_t *db = (policydb_t *) ptr;
+
+ type = (type_datum_t *) datum;
+ /* if this type is marked as permissive, then set its
+ corresponding bit in the permissive map. note that unlike
+ other bitmaps, this one does not subtract 1 in the
+ bitmap. */
+ if (type->flags & TYPE_FLAGS_PERMISSIVE) {
+ uint32_t value;
+ if (type->flavor == TYPE_ALIAS) {
+ /* aliases that came from modules should use the value
+ * referenced to by that alias */
+ value = type->primary;
+ } else {
+ value = type->s.value;
+ }
+ if (ebitmap_set_bit(&db->permissive_map, value, 1)) {
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+int qpol_expand_module(qpol_policy_t * base, int neverallows)
+{
+ unsigned int i;
+ uint32_t *typemap = NULL, *boolmap = NULL, *rolemap = NULL, *usermap = NULL;
+ policydb_t *db;
+ int rt, error = 0;
+
+ INFO(base, "%s", "Expanding policy. (Step 3 of 5)");
+ if (base == NULL) {
+ ERR(base, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ db = &base->p->p;
+
+ /* activate the global branch before expansion */
+ db->global->branch_list->enabled = 1;
+ db->global->enabled = db->global->branch_list;
+
+ /* expand out the types to include all the attributes */
+ if (hashtab_map(db->p_types.table, expand_type_attr_map, (db))) {
+ error = errno;
+ ERR(base, "%s", "Error expanding attributes for types.");
+ goto err;
+ }
+#ifdef HAVE_SEPOL_PERMISSIVE_TYPES
+ /* fill in the permissive types bitmap. this is normally done
+ * in type_copy_callback(), but types are not copied in
+ * expand_module_avrules() */
+ if (hashtab_map(db->p_types.table, expand_type_permissive_map, (db))) {
+ error = errno;
+ ERR(base, "%s", "Error expanding attributes for types.");
+ goto err;
+ }
+#endif
+
+ /* Build the typemap such that we can expand into the same policy */
+ typemap = (uint32_t *) calloc(db->p_types.nprim, sizeof(uint32_t));
+ if (typemap == NULL) {
+ error = errno;
+ ERR(base, "%s", strerror(errno));
+ goto err;
+ }
+ for (i = 0; i < db->p_types.nprim; i++) {
+ typemap[i] = i + 1;
+ }
+
+#ifdef HAVE_SEPOL_BOOLMAP
+ boolmap = (uint32_t *) calloc(db->p_bools.nprim, sizeof(uint32_t));
+ if (boolmap == NULL) {
+ error = errno;
+ ERR(base, "%s", strerror(errno));
+ goto err;
+ }
+ for (i = 0; i < db->p_bools.nprim; i++) {
+ boolmap[i] = i + 1;
+ }
+
+#ifdef HAVE_SEPOL_USER_ROLE_MAPPING
+ rolemap = (uint32_t *) calloc(db->p_roles.nprim, sizeof(uint32_t));
+ if (rolemap == NULL) {
+ error = errno;
+ ERR(base, "%s", strerror(errno));
+ goto err;
+ }
+ for (i = 0; i < db->p_roles.nprim; i++) {
+ rolemap[i] = i + 1;
+ }
+ usermap = (uint32_t *) calloc(db->p_users.nprim, sizeof(uint32_t));
+ if (usermap == NULL) {
+ error = errno;
+ ERR(base, "%s", strerror(errno));
+ goto err;
+ }
+ for (i = 0; i < db->p_users.nprim; i++) {
+ usermap[i] = i + 1;
+ }
+ rt = expand_module_avrules(base->sh, db, db, typemap, boolmap, rolemap, usermap, 0, neverallows);
+#else
+ rt = expand_module_avrules(base->sh, db, db, typemap, boolmap, 0, neverallows);
+#endif // end of user/role mapping
+
+#else
+ rt = expand_module_avrules(base->sh, db, db, typemap, 0, neverallows);
+#endif // end of boolean mapping
+ if (rt < 0) {
+ error = errno;
+ goto err;
+ }
+ rt = 0;
+
+ exit:
+ free(typemap);
+ free(boolmap);
+ free(rolemap);
+ free(usermap);
+ errno = error;
+ return rt;
+ err:
+ rt = -1;
+ /* libsepol does not always set errno correctly, so have a
+ default errno here */
+ if (!error)
+ error = EIO;
+ goto exit;
+}
diff --git a/libqpol/src/expand.h b/libqpol/src/expand.h
new file mode 100644
index 0000000..7f400d4
--- /dev/null
+++ b/libqpol/src/expand.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ *
+ * Public interface for expanding a modular policy.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_EXPAND_H
+#define QPOL_EXPAND_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <qpol/policy.h>
+
+/**
+ * Expand a policy. Linking should always be done prior to calling
+ * this function.
+ *
+ * @param base the module to expand.
+ * @param neverallows if non-zero expand neverallows.
+ * @return 0 on success, -1 on error.
+ */
+ int qpol_expand_module(qpol_policy_t * base, int neverallows);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/src/fs_use_query.c b/libqpol/src/fs_use_query.c
new file mode 100644
index 0000000..c6fca82
--- /dev/null
+++ b/libqpol/src/fs_use_query.c
@@ -0,0 +1,167 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over fs_use statements.
+*
+* @author Kevin Carr kcarr@tresys.com
+* @author Jeremy A. Mowery jmowery@tresys.com
+* @author Jason Tang jtang@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/fs_use_query.h>
+#include <qpol/context_query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/context.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+int qpol_policy_get_fs_use_by_name(const qpol_policy_t * policy, const char *name, const qpol_fs_use_t ** ocon)
+{
+ ocontext_t *tmp = NULL;
+ policydb_t *db = NULL;
+
+ if (ocon != NULL)
+ *ocon = NULL;
+
+ if (policy == NULL || name == NULL || ocon == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ for (tmp = db->ocontexts[OCON_FSUSE]; tmp; tmp = tmp->next) {
+ if (!strcmp(name, tmp->u.name))
+ break;
+ }
+
+ *ocon = (qpol_fs_use_t *) tmp;
+
+ if (*ocon == NULL) {
+ ERR(policy, "could not find fs_use statement for %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_fs_use_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ int error = 0;
+ ocon_state_t *os = NULL;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ os = calloc(1, sizeof(ocon_state_t));
+ if (os == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ os->head = os->cur = db->ocontexts[OCON_FSUSE];
+
+ if (qpol_iterator_create(policy, (void *)os, ocon_state_get_cur,
+ ocon_state_next, ocon_state_end, ocon_state_size, free, iter)) {
+ free(os);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_fs_use_get_name(const qpol_policy_t * policy, const qpol_fs_use_t * ocon, const char **name)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (name != NULL)
+ *name = NULL;
+
+ if (policy == NULL || ocon == NULL || name == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *name = internal_ocon->u.name;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_fs_use_get_behavior(const qpol_policy_t * policy, const qpol_fs_use_t * ocon, uint32_t * behavior)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (behavior != NULL)
+ *behavior = 0;
+
+ if (policy == NULL || ocon == NULL || behavior == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *behavior = internal_ocon->v.behavior;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_fs_use_get_context(const qpol_policy_t * policy, const qpol_fs_use_t * ocon, const qpol_context_t ** context)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || ocon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+
+ if (internal_ocon->v.behavior == QPOL_FS_USE_PSID) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *context = (qpol_context_t *) & (internal_ocon->context[0]);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/genfscon_query.c b/libqpol/src/genfscon_query.c
new file mode 100644
index 0000000..e226f2a
--- /dev/null
+++ b/libqpol/src/genfscon_query.c
@@ -0,0 +1,294 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over genfscon statements.
+*
+* @author Kevin Carr kcarr@tresys.com
+* @author Jeremy A. Mowery jmowery@tresys.com
+* @author Jason Tang jtang@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+#include <qpol/genfscon_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+struct qpol_genfscon
+{
+ const char *fs_name;
+ const char *path;
+ const context_struct_t *context;
+ uint32_t sclass;
+};
+
+int qpol_policy_get_genfscon_by_name(const qpol_policy_t * policy, const char *name, const char *path, qpol_genfscon_t ** genfscon)
+{
+ genfs_t *tmp = NULL;
+ ocontext_t *tmp2 = NULL;
+ policydb_t *db = NULL;
+ int error = 0;
+
+ if (genfscon != NULL)
+ *genfscon = NULL;
+
+ if (policy == NULL || name == NULL || path == NULL || genfscon == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ for (tmp = db->genfs; tmp; tmp = tmp->next) {
+ if (!strcmp(name, tmp->fstype))
+ break;
+ }
+
+ if (tmp) {
+ for (tmp2 = tmp->head; tmp2; tmp2 = tmp2->next) {
+ if (!strcmp(path, tmp2->u.name))
+ break;
+ }
+ }
+
+ if (tmp && tmp2) {
+ *genfscon = calloc(1, sizeof(qpol_genfscon_t));
+ if (!(*genfscon)) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = errno;
+ return STATUS_ERR;
+ }
+ /* shallow copy only the struct pointer (genfscon) should be free()'ed */
+ (*genfscon)->fs_name = tmp->fstype;
+ (*genfscon)->path = tmp2->u.name;
+ (*genfscon)->context = &(tmp2->context[0]);
+ (*genfscon)->sclass = tmp2->v.sclass;
+ }
+
+ if (*genfscon == NULL) {
+ ERR(policy, "could not find genfscon statement for %s %s", name, path);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct genfs_state
+{
+ genfs_t *head;
+ genfs_t *cur;
+ ocontext_t *cur_path;
+} genfs_state_t;
+
+static int genfs_state_end(const qpol_iterator_t * iter)
+{
+ genfs_state_t *gs = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ gs = (genfs_state_t *) qpol_iterator_state(iter);
+
+ if (gs->cur == NULL && gs->cur_path == NULL)
+ return 1;
+
+ return 0;
+}
+
+static void *genfs_state_get_cur(const qpol_iterator_t * iter)
+{
+ genfs_state_t *gs = NULL;
+ qpol_genfscon_t *genfscon = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL || genfs_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ gs = (genfs_state_t *) qpol_iterator_state(iter);
+
+ genfscon = calloc(1, sizeof(qpol_genfscon_t));
+ if (!genfscon) {
+ return NULL;
+ }
+
+ genfscon->fs_name = gs->cur->fstype;
+ genfscon->path = gs->cur_path->u.name;
+ genfscon->context = &(gs->cur_path->context[0]);
+ genfscon->sclass = gs->cur_path->v.sclass;
+
+ return genfscon;
+}
+
+static size_t genfs_state_size(const qpol_iterator_t * iter)
+{
+ genfs_state_t *gs = NULL;
+ size_t count = 0;
+ genfs_t *genfs = NULL;
+ ocontext_t *path = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ gs = (genfs_state_t *) qpol_iterator_state(iter);
+
+ for (genfs = gs->head; genfs; genfs = genfs->next)
+ for (path = genfs->head; path; path = path->next)
+ count++;
+
+ return count;
+}
+
+static int genfs_state_next(qpol_iterator_t * iter)
+{
+ genfs_state_t *gs = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ gs = (genfs_state_t *) qpol_iterator_state(iter);
+
+ if (gs->cur == NULL) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ if (gs->cur_path->next != NULL) {
+ gs->cur_path = gs->cur_path->next;
+ } else {
+ gs->cur = gs->cur->next;
+ gs->cur_path = gs->cur ? gs->cur->head : NULL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_genfscon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ genfs_state_t *gs = NULL;
+ int error = 0;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ gs = calloc(1, sizeof(genfs_state_t));
+ if (gs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ gs->head = gs->cur = db->genfs;
+ if (gs->head)
+ gs->cur_path = gs->head->head;
+
+ if (qpol_iterator_create(policy, (void *)gs, genfs_state_get_cur,
+ genfs_state_next, genfs_state_end, genfs_state_size, free, iter)) {
+ free(gs);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_genfscon_get_name(const qpol_policy_t * policy, const qpol_genfscon_t * genfs, const char **name)
+{
+ if (name != NULL)
+ *name = NULL;
+
+ if (policy == NULL || genfs == NULL || name == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *name = genfs->fs_name;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_genfscon_get_path(const qpol_policy_t * policy, const qpol_genfscon_t * genfs, const char **path)
+{
+ if (path != NULL)
+ *path = NULL;
+
+ if (policy == NULL || genfs == NULL || path == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *path = genfs->path;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_genfscon_get_class(const qpol_policy_t * policy, const qpol_genfscon_t * genfs, uint32_t * obj_class)
+{
+ if (obj_class != NULL)
+ *obj_class = 0;
+
+ if (policy == NULL || genfs == NULL || obj_class == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *obj_class = genfs->sclass;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_genfscon_get_context(const qpol_policy_t * policy, const qpol_genfscon_t * genfscon, const qpol_context_t ** context)
+{
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || genfscon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *context = (qpol_context_t *) genfscon->context;
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/isid_query.c b/libqpol/src/isid_query.c
new file mode 100644
index 0000000..8d7546e
--- /dev/null
+++ b/libqpol/src/isid_query.c
@@ -0,0 +1,139 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over initial SIDs.
+*
+* @author Kevin Carr kcarr@tresys.com
+* @author Jeremy A. Mowery jmowery@tresys.com
+* @author Jason Tang jtang@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+#include <qpol/isid_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+int qpol_policy_get_isid_by_name(const qpol_policy_t * policy, const char *name, const qpol_isid_t ** ocon)
+{
+ ocontext_t *tmp = NULL;
+ policydb_t *db = NULL;
+
+ if (ocon != NULL)
+ *ocon = NULL;
+
+ if (policy == NULL || name == NULL || ocon == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ for (tmp = db->ocontexts[OCON_ISID]; tmp; tmp = tmp->next) {
+ if (!strcmp(name, tmp->u.name))
+ break;
+ }
+
+ *ocon = (qpol_isid_t *) tmp;
+
+ if (*ocon == NULL) {
+ ERR(policy, "could not find initial SID statement for %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_isid_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ ocon_state_t *os = NULL;
+ int error = 0;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ os = calloc(1, sizeof(ocon_state_t));
+ if (os == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ os->head = os->cur = db->ocontexts[OCON_ISID];
+
+ if (qpol_iterator_create(policy, (void *)os, ocon_state_get_cur,
+ ocon_state_next, ocon_state_end, ocon_state_size, free, iter)) {
+ free(os);
+ return STATUS_ERR;
+ }
+ return STATUS_SUCCESS;
+}
+
+int qpol_isid_get_name(const qpol_policy_t * policy, const qpol_isid_t * ocon, const char **name)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (name != NULL)
+ *name = NULL;
+
+ if (policy == NULL || ocon == NULL || name == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *name = internal_ocon->u.name;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_isid_get_context(const qpol_policy_t * policy, const qpol_isid_t * ocon, const qpol_context_t ** context)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || ocon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *context = (qpol_context_t *) & (internal_ocon->context[0]);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/iterator.c b/libqpol/src/iterator.c
new file mode 100644
index 0000000..ffc5f7a
--- /dev/null
+++ b/libqpol/src/iterator.c
@@ -0,0 +1,797 @@
+/**
+ * @file
+ * Contains the implementation of the qpol_iterator API, both
+ * public and private, for returning lists of components and rules
+ * from the policy database.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/mls_query.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/polcaps.h>
+#include <sepol/policydb/util.h>
+#include <sepol/policydb.h>
+
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+/**
+ * Declaration of qpol_iterator, an arbitrary valued policy component
+ * iterator used to return lists of components.
+ *
+ */
+struct qpol_iterator
+{
+ policydb_t *policy;
+ void *state;
+ void *(*get_cur) (const qpol_iterator_t * iter);
+ int (*next) (qpol_iterator_t * iter);
+ int (*end) (const qpol_iterator_t * iter);
+ size_t(*size) (const qpol_iterator_t * iter);
+ void (*free_fn) (void *x);
+};
+
+/**
+ * The number of buckets in sepol's av tables was statically set in
+ * libsepol < 2.0.20. With libsepol 2.0.20, this size was dynamically
+ * calculated based upon the number of rules.
+ */
+static uint32_t iterator_get_avtab_size(const avtab_t * avtab)
+{
+#ifdef SEPOL_DYNAMIC_AVTAB
+ return avtab->nslot;
+#else
+ return AVTAB_SIZE;
+#endif
+}
+
+int qpol_iterator_create(const qpol_policy_t * policy, void *state,
+ void *(*get_cur) (const qpol_iterator_t * iter),
+ int (*next) (qpol_iterator_t * iter),
+ int (*end) (const qpol_iterator_t * iter),
+ size_t(*size) (const qpol_iterator_t * iter), void (*free_fn) (void *x), qpol_iterator_t ** iter)
+{
+ int error = 0;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || state == NULL || iter == NULL || get_cur == NULL || next == NULL || end == NULL || size == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *iter = calloc(1, sizeof(struct qpol_iterator));
+ if (*iter == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ (*iter)->policy = &policy->p->p;
+ (*iter)->state = state;
+ (*iter)->get_cur = get_cur;
+ (*iter)->next = next;
+ (*iter)->end = end;
+ (*iter)->size = size;
+ (*iter)->free_fn = free_fn;
+
+ return STATUS_SUCCESS;
+}
+
+void *qpol_iterator_state(const qpol_iterator_t * iter)
+{
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return iter->state;
+}
+
+const policydb_t *qpol_iterator_policy(const qpol_iterator_t * iter)
+{
+ if (iter == NULL || iter->policy == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return iter->policy;
+}
+
+void *hash_state_get_cur(const qpol_iterator_t * iter)
+{
+ hash_state_t *hs = NULL;
+
+ if (iter == NULL || iter->state == NULL || hash_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ hs = (hash_state_t *) iter->state;
+
+ return hs->node->datum;
+}
+
+void *hash_state_get_cur_key(const qpol_iterator_t * iter)
+{
+ hash_state_t *hs = NULL;
+
+ if (iter == NULL || iter->state == NULL || hash_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ hs = (hash_state_t *) iter->state;
+
+ return hs->node->key;
+}
+
+void *ocon_state_get_cur(const qpol_iterator_t * iter)
+{
+ ocon_state_t *os = NULL;
+
+ if (iter == NULL || iter->state == NULL || ocon_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ os = (ocon_state_t *) iter->state;
+
+ return os->cur;
+}
+
+void *avtab_state_get_cur(const qpol_iterator_t * iter)
+{
+ avtab_state_t *state;
+
+ if (iter == NULL || iter->state == NULL || avtab_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ state = (avtab_state_t *) iter->state;
+ return state->node;
+}
+
+int hash_state_next(qpol_iterator_t * iter)
+{
+ hash_state_t *hs = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ hs = (hash_state_t *) iter->state;
+
+ if (hs->table == NULL || *(hs->table) == NULL || hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ if (hs->node != NULL && hs->node->next != NULL) {
+ hs->node = hs->node->next;
+ } else {
+ do {
+ hs->bucket++;
+ if (hs->bucket < (*(hs->table))->size) {
+ hs->node = (*(hs->table))->htable[hs->bucket];
+ } else {
+ hs->node = NULL;
+ }
+ } while (hs->bucket < (*(hs->table))->size && hs->node == NULL);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int ebitmap_state_next(qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ es = (ebitmap_state_t *) iter->state;
+
+ if (es->cur >= es->bmap->highbit) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ es->cur++;
+ } while (es->cur < es->bmap->highbit && !ebitmap_get_bit(es->bmap, es->cur));
+
+ return STATUS_SUCCESS;
+}
+
+int ocon_state_next(qpol_iterator_t * iter)
+{
+ ocon_state_t *os = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ os = (ocon_state_t *) iter->state;
+
+ if (os->cur == NULL) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ os->cur = os->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+int avtab_state_next(qpol_iterator_t * iter)
+{
+ avtab_t *avtab;
+ avtab_state_t *state;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ state = iter->state;
+ avtab = (state->which == QPOL_AVTAB_STATE_AV ? state->ucond_tab : state->cond_tab);
+
+ if ((!avtab->htable || state->bucket >= iterator_get_avtab_size(avtab)) && state->which == QPOL_AVTAB_STATE_COND) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ if (state->node != NULL && state->node->next != NULL) {
+ state->node = state->node->next;
+ } else {
+ /* find the next bucket */
+ do {
+ state->bucket++;
+ if (!avtab->htable || state->bucket >= iterator_get_avtab_size(avtab)) {
+ if (state->which == QPOL_AVTAB_STATE_AV) {
+ state->bucket = 0;
+ avtab = state->cond_tab;
+ state->which = QPOL_AVTAB_STATE_COND;
+ } else {
+ state->node = NULL;
+ break;
+ }
+ }
+ if (avtab->htable && avtab->htable[state->bucket] != NULL) {
+ state->node = avtab->htable[state->bucket];
+ break;
+ }
+ } while (avtab->htable && state->bucket < iterator_get_avtab_size(avtab));
+ }
+ } while (avtab->htable && state->bucket < iterator_get_avtab_size(avtab) &&
+ state->node ? !(state->rule_type_mask & state->node->key.specified) : 0);
+
+ return STATUS_SUCCESS;
+}
+
+int hash_state_end(const qpol_iterator_t * iter)
+{
+ hash_state_t *hs = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ hs = (hash_state_t *) iter->state;
+
+ if (hs->table == NULL || *(hs->table) == NULL || (*(hs->table))->nel == 0 || hs->bucket >= (*(hs->table))->size)
+ return 1;
+
+ return 0;
+}
+
+int ebitmap_state_end(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ es = (ebitmap_state_t *) iter->state;
+
+ if (es->cur >= es->bmap->highbit)
+ return 1;
+
+ return 0;
+}
+
+int ocon_state_end(const qpol_iterator_t * iter)
+{
+ ocon_state_t *os = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ os = (ocon_state_t *) iter->state;
+
+ if (os->cur == NULL)
+ return 1;
+
+ return 0;
+}
+
+int avtab_state_end(const qpol_iterator_t * iter)
+{
+ avtab_state_t *state;
+ avtab_t *avtab;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ state = iter->state;
+ avtab = (state->which == QPOL_AVTAB_STATE_AV ? state->ucond_tab : state->cond_tab);
+ if ((!avtab->htable || state->bucket >= iterator_get_avtab_size(avtab)) && state->which == QPOL_AVTAB_STATE_COND)
+ return 1;
+ return 0;
+}
+
+size_t hash_state_size(const qpol_iterator_t * iter)
+{
+ hash_state_t *hs = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ hs = (hash_state_t *) iter->state;
+
+ return (*(hs->table))->nel;
+}
+
+size_t ebitmap_state_size(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+ size_t count = 0, bit = 0;
+ ebitmap_node_t *node = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ es = (ebitmap_state_t *) iter->state;
+
+ ebitmap_for_each_bit(es->bmap, node, bit) {
+ count += ebitmap_get_bit(es->bmap, bit);
+ }
+
+ return count;
+}
+
+size_t ocon_state_size(const qpol_iterator_t * iter)
+{
+ ocon_state_t *os = NULL;
+ size_t count = 0;
+ ocontext_t *ocon = NULL;
+
+ if (iter == NULL || iter->state == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ os = (ocon_state_t *) iter->state;
+
+ for (ocon = os->head; ocon; ocon = ocon->next)
+ count++;
+
+ return count;
+}
+
+size_t avtab_state_size(const qpol_iterator_t * iter)
+{
+ avtab_state_t *state;
+ avtab_t *avtab;
+ size_t count = 0;
+ avtab_ptr_t node = NULL;
+ uint32_t bucket = 0;
+
+ if (iter == NULL || iter->state == NULL || iter->policy == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ state = iter->state;
+ avtab = state->ucond_tab;
+
+ for (bucket = 0; avtab->htable && bucket < iterator_get_avtab_size(avtab); bucket++) {
+ for (node = avtab->htable[bucket]; node; node = node->next) {
+ if (node->key.specified & state->rule_type_mask)
+ count++;
+ }
+ }
+
+ avtab = state->cond_tab;
+
+ for (bucket = 0; avtab->htable && bucket < iterator_get_avtab_size(avtab); bucket++) {
+ for (node = avtab->htable[bucket]; node; node = node->next) {
+ if (node->key.specified & state->rule_type_mask)
+ count++;
+ }
+ }
+
+ return count;
+}
+
+void qpol_iterator_destroy(qpol_iterator_t ** iter)
+{
+ if (iter == NULL || *iter == NULL)
+ return;
+
+ if ((*iter)->free_fn)
+ (*iter)->free_fn((*iter)->state);
+
+ free(*iter);
+ *iter = NULL;
+}
+
+int qpol_iterator_get_item(const qpol_iterator_t * iter, void **item)
+{
+ if (item != NULL)
+ *item = NULL;
+
+ if (iter == NULL || iter->get_cur == NULL || item == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *item = iter->get_cur(iter);
+ if (*item == NULL)
+ return STATUS_ERR;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_iterator_next(qpol_iterator_t * iter)
+{
+ if (iter == NULL || iter->next == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return iter->next(iter);
+}
+
+int qpol_iterator_end(const qpol_iterator_t * iter)
+{
+ if (iter == NULL || iter->end == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return iter->end(iter);
+}
+
+int qpol_iterator_get_size(const qpol_iterator_t * iter, size_t * size)
+{
+ if (size != NULL)
+ *size = 0;
+
+ if (iter == NULL || size == NULL || iter->size == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *size = iter->size(iter);
+
+ return STATUS_SUCCESS;
+}
+
+void *ebitmap_state_get_cur_type(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+ const policydb_t *db = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ es = qpol_iterator_state(iter);
+ if (es == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ db = qpol_iterator_policy(iter);
+ if (db == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return db->type_val_to_struct[es->cur];
+}
+
+void *ebitmap_state_get_cur_role(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+ const policydb_t *db = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ es = qpol_iterator_state(iter);
+ if (es == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ db = qpol_iterator_policy(iter);
+ if (db == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return db->role_val_to_struct[es->cur];
+}
+
+void *ebitmap_state_get_cur_cat(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+ const policydb_t *db = NULL;
+ const qpol_cat_t *cat = NULL;
+ sepol_policydb_t sp;
+ qpol_policy_t qp;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ es = qpol_iterator_state(iter);
+ if (es == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ db = qpol_iterator_policy(iter);
+ if (db == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* shallow copy is safe here */
+ sp.p = *db;
+ qp.p = &sp;
+ qp.fn = NULL;
+
+ qpol_policy_get_cat_by_name(&qp, db->p_cat_val_to_name[es->cur], &cat);
+
+ /* There is no val_to_struct for categories; this requires that qpol
+ * search for the struct, but it can't be returned as const here so
+ * cast it to void* explicitly. */
+ return (void *)cat;
+}
+
+void *ebitmap_state_get_cur_permissive(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+ const policydb_t *db = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ es = qpol_iterator_state(iter);
+ if (es == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ db = qpol_iterator_policy(iter);
+ if (db == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return db->type_val_to_struct[es->cur - 1];
+}
+
+void *ebitmap_state_get_cur_polcap(const qpol_iterator_t * iter)
+{
+ ebitmap_state_t *es = NULL;
+ const policydb_t *db = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ es = qpol_iterator_state(iter);
+ if (es == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ db = qpol_iterator_policy(iter);
+ if (db == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return sepol_polcap_getname(es->cur);
+}
+
+void ebitmap_state_destroy(void *es)
+{
+ ebitmap_state_t *ies = (ebitmap_state_t *) es;
+
+ if (!es)
+ return;
+
+ ebitmap_destroy(ies->bmap);
+ free(ies->bmap);
+ free(ies);
+}
+
+int perm_state_end(const qpol_iterator_t * iter)
+{
+ perm_state_t *ps = NULL;
+ const policydb_t *db = NULL;
+ unsigned int perm_max = 0;
+
+ if (iter == NULL || (ps = qpol_iterator_state(iter)) == NULL || (db = qpol_iterator_policy(iter)) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ /* permission max is number of permissions in the class which includes
+ * the number of permissions in its common if it inherits one */
+ perm_max = db->class_val_to_struct[ps->obj_class_val - 1]->permissions.nprim;
+ if (perm_max > 32) {
+ errno = EDOM; /* perms set mask is a uint32_t cannot use more than 32 bits */
+ return STATUS_ERR;
+ }
+
+ if (!(ps->perm_set) || ps->cur >= perm_max)
+ return 1;
+
+ return 0;
+}
+
+void *perm_state_get_cur(const qpol_iterator_t * iter)
+{
+ const policydb_t *db = NULL;
+ class_datum_t *obj_class = NULL;
+ common_datum_t *comm = NULL;
+ perm_state_t *ps = NULL;
+ unsigned int perm_max = 0;
+ char *tmp = NULL;
+
+ if (iter == NULL || (db = qpol_iterator_policy(iter)) == NULL ||
+ (ps = (perm_state_t *) qpol_iterator_state(iter)) == NULL || perm_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ obj_class = db->class_val_to_struct[ps->obj_class_val - 1];
+ comm = obj_class->comdatum;
+
+ /* permission max is number of permissions in the class which includes
+ * the number of permissions in its common if it inherits one */
+ perm_max = obj_class->permissions.nprim;
+ if (perm_max > 32) {
+ errno = EDOM; /* perms set mask is a uint32_t cannot use more than 32 bits */
+ return NULL;
+ }
+ if (ps->cur >= perm_max) {
+ errno = ERANGE;
+ return NULL;
+ }
+ if (!(ps->perm_set & 1 << (ps->cur))) { /* perm bit not set? */
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* explicit const_cast for sepol */
+ tmp = sepol_av_to_string((policydb_t *) db, ps->obj_class_val, (sepol_access_vector_t) 1 << (ps->cur));
+ if (tmp) {
+ tmp++; /*sepol_av_to_string prepends a ' ' to the name */
+ return strdup(tmp);
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+int perm_state_next(qpol_iterator_t * iter)
+{
+ perm_state_t *ps = NULL;
+ const policydb_t *db = NULL;
+ unsigned int perm_max = 0;
+
+ if (iter == NULL || (ps = qpol_iterator_state(iter)) == NULL ||
+ (db = qpol_iterator_policy(iter)) == NULL || perm_state_end(iter)) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ /* permission max is number of permissions in the class which includes
+ * the number of permissions in its common if it inherits one */
+ perm_max = db->class_val_to_struct[ps->obj_class_val - 1]->permissions.nprim;
+ if (perm_max > 32) {
+ errno = EDOM; /* perms set mask is a uint32_t cannot use more than 32 bits */
+ return STATUS_ERR;
+ }
+
+ if (ps->cur >= perm_max) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ ps->cur++;
+ } while (ps->cur < perm_max && !(ps->perm_set & 1 << (ps->cur)));
+
+ return STATUS_SUCCESS;
+}
+
+size_t perm_state_size(const qpol_iterator_t * iter)
+{
+ perm_state_t *ps = NULL;
+ const policydb_t *db = NULL;
+ unsigned int perm_max = 0;
+ size_t i, count = 0;
+
+ if (iter == NULL || (ps = qpol_iterator_state(iter)) == NULL ||
+ (db = qpol_iterator_policy(iter)) == NULL || perm_state_end(iter)) {
+ errno = EINVAL;
+ return 0; /* as a size_t 0 is error */
+ }
+
+ /* permission max is number of permissions in the class which includes
+ * the number of permissions in its common if it inherits one */
+ perm_max = db->class_val_to_struct[ps->obj_class_val - 1]->permissions.nprim;
+ if (perm_max > 32) {
+ errno = EDOM; /* perms set mask is a uint32_t cannot use more than 32 bits */
+ return 0; /* as a size_t 0 is error */
+ }
+
+ for (i = 0; i < perm_max; i++) {
+ if (ps->perm_set & 1 << i)
+ count++;
+ }
+
+ return count;
+}
diff --git a/libqpol/src/iterator_internal.h b/libqpol/src/iterator_internal.h
new file mode 100644
index 0000000..183724c
--- /dev/null
+++ b/libqpol/src/iterator_internal.h
@@ -0,0 +1,123 @@
+/**
+ * @file
+ * Declaration of the internal interface for
+ * qpol_iterator, an arbitrary valued policy component
+ * iterator used to return lists of components.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_ITERATOR_INTERNAL_H
+#define QPOL_ITERATOR_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/avtab.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <stddef.h>
+
+ typedef struct hash_state
+ {
+ unsigned int bucket;
+ hashtab_node_t *node;
+ hashtab_t *table;
+ } hash_state_t;
+
+ typedef struct ebitmap_state
+ {
+ ebitmap_t *bmap;
+ size_t cur;
+ } ebitmap_state_t;
+
+ typedef struct ocon_state
+ {
+ ocontext_t *head;
+ ocontext_t *cur;
+ } ocon_state_t;
+
+ typedef struct perm_state
+ {
+ uint32_t perm_set;
+ uint32_t obj_class_val;
+ uint8_t cur;
+ } perm_state_t;
+
+ typedef struct avtab_state
+ {
+ uint32_t rule_type_mask;
+ avtab_t *ucond_tab;
+ avtab_t *cond_tab;
+ uint32_t bucket;
+ avtab_ptr_t node;
+#define QPOL_AVTAB_STATE_AV 0
+#define QPOL_AVTAB_STATE_COND 1
+ unsigned which;
+ } avtab_state_t;
+
+ int qpol_iterator_create(const qpol_policy_t * policy, void *state,
+ void *(*get_cur) (const qpol_iterator_t * iter),
+ int (*next) (qpol_iterator_t * iter),
+ int (*end) (const qpol_iterator_t * iter),
+ size_t(*size) (const qpol_iterator_t * iter), void (*free_fn) (void *x), qpol_iterator_t ** iter);
+
+ void *qpol_iterator_state(const qpol_iterator_t * iter);
+ const policydb_t *qpol_iterator_policy(const qpol_iterator_t * iter);
+
+ void *hash_state_get_cur(const qpol_iterator_t * iter);
+ void *hash_state_get_cur_key(const qpol_iterator_t * iter);
+ void *ebitmap_state_get_cur_type(const qpol_iterator_t * iter);
+ void *ebitmap_state_get_cur_role(const qpol_iterator_t * iter);
+ void *ebitmap_state_get_cur_cat(const qpol_iterator_t * iter);
+ void *ebitmap_state_get_cur_permissive(const qpol_iterator_t * iter);
+ void *ebitmap_state_get_cur_polcap(const qpol_iterator_t * iter);
+ void *ocon_state_get_cur(const qpol_iterator_t * iter);
+ void *perm_state_get_cur(const qpol_iterator_t * iter);
+ void *avtab_state_get_cur(const qpol_iterator_t * iter);
+
+ int hash_state_next(qpol_iterator_t * iter);
+ int ebitmap_state_next(qpol_iterator_t * iter);
+ int ocon_state_next(qpol_iterator_t * iter);
+ int perm_state_next(qpol_iterator_t * iter);
+ int avtab_state_next(qpol_iterator_t * iter);
+
+ int hash_state_end(const qpol_iterator_t * iter);
+ int ebitmap_state_end(const qpol_iterator_t * iter);
+ int ocon_state_end(const qpol_iterator_t * iter);
+ int perm_state_end(const qpol_iterator_t * iter);
+ int avtab_state_end(const qpol_iterator_t * iter);
+
+ size_t hash_state_size(const qpol_iterator_t * iter);
+ size_t ebitmap_state_size(const qpol_iterator_t * iter);
+ size_t ocon_state_size(const qpol_iterator_t * iter);
+ size_t perm_state_size(const qpol_iterator_t * iter);
+ size_t avtab_state_size(const qpol_iterator_t * iter);
+
+ void ebitmap_state_destroy(void *es);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_ITERATOR_INTERNAL_H */
diff --git a/libqpol/src/libqpol.map b/libqpol/src/libqpol.map
new file mode 100644
index 0000000..dd293bc
--- /dev/null
+++ b/libqpol/src/libqpol.map
@@ -0,0 +1,73 @@
+VERS_1.2 {
+ global:
+ qpol_avrule_*;
+ qpol_bool_*;
+ qpol_cat_*;
+ qpol_class_*;
+ qpol_common_*;
+ qpol_cond_*;
+ qpol_constraint_*;
+ qpol_context_*;
+ qpol_default_policy_find;
+ qpol_fs_use_*;
+ qpol_genfscon_*;
+ qpol_isid_*;
+ qpol_iterator_end;
+ qpol_iterator_next;
+ qpol_iterator_get_*;
+ qpol_iterator_destroy;
+ qpol_level_*;
+ qpol_mls_*;
+ qpol_module_*;
+ qpol_netifcon_*;
+ qpol_nodecon_*;
+ qpol_perm_*;
+ qpol_policy_append_module;
+ qpol_policy_build_syn_rule_table;
+ qpol_policy_destroy;
+ qpol_policy_get_*;
+ qpol_policy_has_capability;
+ qpol_policy_open_from_file;
+ qpol_policy_open_from_file_no_rules;
+ qpol_policy_open_from_memory;
+ qpol_policy_rebuild;
+ qpol_policy_reevaluate_conds;
+ qpol_portcon_*;
+ qpol_range_trans_*;
+ qpol_role_*;
+ qpol_syn_avrule_*;
+ qpol_syn_terule_*;
+ qpol_terule_*;
+ qpol_type_get_alias_iter;
+ qpol_type_get_attr_iter;
+ qpol_type_get_isalias;
+ qpol_type_get_isattr;
+ qpol_type_get_name;
+ qpol_type_get_type_iter;
+ qpol_type_get_value;
+ qpol_type_set_*;
+ qpol_user_*;
+ qpol_validatetrans_*;
+ libqpol_get_version;
+ local: *;
+};
+
+VERS_1.3 {
+ global:
+ qpol_policy_open_from_file;
+ qpol_policy_open_from_memory;
+ qpol_policy_rebuild;
+} VERS_1.2;
+
+VERS_1.4 {
+ global:
+ qpol_type_get_ispermissive;
+} VERS_1.3;
+
+VERS_1.5 {
+ global:
+ qpol_policy_permissive_*;
+ qpol_permissive_*;
+ qpol_policy_polcap_*;
+ qpol_polcap_*;
+} VERS_1.4;
diff --git a/libqpol/src/mls_query.c b/libqpol/src/mls_query.c
new file mode 100644
index 0000000..8c46acd
--- /dev/null
+++ b/libqpol/src/mls_query.c
@@ -0,0 +1,639 @@
+/**
+ * @file
+ * Implementation of the interface for searching and iterating over
+ * policy MLS components.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <qpol/iterator.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/expand.h>
+#include "iterator_internal.h"
+#include <qpol/mls_query.h>
+#include "qpol_internal.h"
+
+/* level */
+int qpol_policy_get_level_by_name(const qpol_policy_t * policy, const char *name, const qpol_level_t ** datum)
+{
+ policydb_t *db = NULL;
+ hashtab_datum_t internal_datum = NULL;
+
+ if (policy == NULL || name == NULL || datum == NULL) {
+ if (datum != NULL)
+ *datum = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_levels.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ ERR(policy, "could not find datum for level %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *datum = (qpol_level_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_level_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_levels.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_level_get_isalias(const qpol_policy_t * policy, const qpol_level_t * datum, unsigned char *isalias)
+{
+ level_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || isalias == NULL) {
+ if (isalias != NULL)
+ *isalias = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (level_datum_t *) datum;
+ *isalias = internal_datum->isalias;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_level_get_value(const qpol_policy_t * policy, const qpol_level_t * datum, uint32_t * value)
+{
+ level_datum_t *internal_datum = NULL;
+
+ if (policy == NULL || datum == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (level_datum_t *) datum;
+ *value = internal_datum->level->sens;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_level_get_cat_iter(const qpol_policy_t * policy, const qpol_level_t * datum, qpol_iterator_t ** cats)
+{
+ level_datum_t *internal_datum = NULL;
+ ebitmap_state_t *es = NULL;
+ int error = 0;
+
+ if (policy == NULL || datum == NULL || cats == NULL) {
+ if (cats != NULL)
+ *cats = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (level_datum_t *) datum;
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (es == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_datum->level->cat);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_cat,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, cats)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*cats);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_level_get_name(const qpol_policy_t * policy, const qpol_level_t * datum, const char **name)
+{
+ level_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (level_datum_t *) datum;
+
+ *name = db->p_sens_val_to_name[internal_datum->level->sens - 1];
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct level_alias_hash_state
+{
+ unsigned int bucket;
+ hashtab_node_t *node;
+ hashtab_t *table;
+ uint32_t val;
+} level_alias_hash_state_t;
+
+static int hash_state_next_level_alias(qpol_iterator_t * iter)
+{
+ level_alias_hash_state_t *hs = NULL;
+ level_datum_t *datum = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ hs = (level_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ hash_state_next(iter);
+ datum = hs->node ? (level_datum_t *) hs->node->datum : NULL;
+ } while (datum != NULL && (datum->level->sens != hs->val || !datum->isalias));
+
+ return STATUS_SUCCESS;
+}
+
+static void *hash_state_get_cur_alias(const qpol_iterator_t * iter)
+{
+ level_alias_hash_state_t *hs = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ hs = (level_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ return hs->node->key;
+}
+
+static size_t hash_state_level_alias_size(const qpol_iterator_t * iter)
+{
+ level_alias_hash_state_t *hs = NULL;
+ hashtab_node_t *tmp_node;
+ level_datum_t *tmp_lvl_datum;
+ uint32_t tmp_bucket = 0;
+ size_t count = 0;
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ hs = (level_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (!hs) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ for (tmp_bucket = 0; tmp_bucket < (*(hs->table))->size; tmp_bucket++) {
+ for (tmp_node = (*(hs->table))->htable[tmp_bucket]; tmp_node; tmp_node = tmp_node->next) {
+ tmp_lvl_datum = tmp_node ? tmp_node->datum : NULL;
+ if (tmp_lvl_datum) {
+ if (tmp_lvl_datum->isalias && tmp_lvl_datum->level->sens == hs->val)
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+int qpol_level_get_alias_iter(const qpol_policy_t * policy, const qpol_level_t * datum, qpol_iterator_t ** aliases)
+{
+ level_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ int error;
+ level_alias_hash_state_t *hs = NULL;
+
+ if (policy == NULL || datum == NULL || aliases == NULL) {
+ if (aliases != NULL)
+ *aliases = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (level_datum_t *) datum;
+
+ hs = calloc(1, sizeof(level_alias_hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_levels.table;
+ hs->node = (*(hs->table))->htable[0];
+ hs->val = internal_datum->level->sens;
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur_alias,
+ hash_state_next_level_alias, hash_state_end, hash_state_level_alias_size, free, aliases)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL || !((level_datum_t *) hs->node->datum)->isalias
+ || ((level_datum_t *) (hs->node->datum))->level->sens != hs->val)
+ hash_state_next_level_alias(*aliases);
+
+ return STATUS_SUCCESS;
+}
+
+/* cat */
+int qpol_policy_get_cat_by_name(const qpol_policy_t * policy, const char *name, const qpol_cat_t ** datum)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || datum == NULL) {
+ if (datum != NULL)
+ *datum = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_cats.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ *datum = NULL;
+ ERR(policy, "could not find datum for cat %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *datum = (qpol_cat_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_cat_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_cats.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_cat_get_value(const qpol_policy_t * policy, const qpol_cat_t * datum, uint32_t * value)
+{
+ cat_datum_t *internal_datum = NULL;
+
+ if (policy == NULL || datum == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (cat_datum_t *) datum;
+ *value = internal_datum->s.value;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_cat_get_isalias(const qpol_policy_t * policy, const qpol_cat_t * datum, unsigned char *isalias)
+{
+ cat_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || isalias == NULL) {
+ if (isalias != NULL)
+ *isalias = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (cat_datum_t *) datum;
+ *isalias = internal_datum->isalias;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_cat_get_name(const qpol_policy_t * policy, const qpol_cat_t * datum, const char **name)
+{
+ cat_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (cat_datum_t *) datum;
+
+ *name = db->p_cat_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
+
+static int hash_state_next_cat_alias(qpol_iterator_t * iter)
+{
+ /* using level alias state datum since data needed is identical */
+ level_alias_hash_state_t *hs = NULL;
+ cat_datum_t *datum = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ hs = (level_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ hash_state_next(iter);
+ datum = hs->node ? (cat_datum_t *) hs->node->datum : NULL;
+ } while (datum != NULL && (datum->s.value != hs->val || !datum->isalias));
+
+ return STATUS_SUCCESS;
+}
+
+static size_t hash_state_cat_alias_size(const qpol_iterator_t * iter)
+{
+ level_alias_hash_state_t *hs = NULL;
+ hashtab_node_t *tmp_node;
+ cat_datum_t *tmp_cat_datum;
+ uint32_t tmp_bucket = 0;
+ size_t count = 0;
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ hs = (level_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (!hs) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ for (tmp_bucket = 0; tmp_bucket < (*(hs->table))->size; tmp_bucket++) {
+ for (tmp_node = (*(hs->table))->htable[tmp_bucket]; tmp_node; tmp_node = tmp_node->next) {
+ tmp_cat_datum = tmp_node ? tmp_node->datum : NULL;
+ if (tmp_cat_datum) {
+ if (tmp_cat_datum->isalias && tmp_cat_datum->s.value == hs->val)
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+int qpol_cat_get_alias_iter(const qpol_policy_t * policy, const qpol_cat_t * datum, qpol_iterator_t ** aliases)
+{
+ cat_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ int error;
+ level_alias_hash_state_t *hs = NULL;
+
+ if (policy == NULL || datum == NULL || aliases == NULL) {
+ if (aliases != NULL)
+ *aliases = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (cat_datum_t *) datum;
+
+ hs = calloc(1, sizeof(level_alias_hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_cats.table;
+ hs->node = (*(hs->table))->htable[0];
+ hs->val = internal_datum->s.value;
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur_alias,
+ hash_state_next_cat_alias, hash_state_end, hash_state_cat_alias_size, free, aliases)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL || ((cat_datum_t *) (hs->node->datum))->s.value != hs->val)
+ hash_state_next_cat_alias(*aliases);
+
+ return STATUS_SUCCESS;
+}
+
+/* mls range */
+int qpol_mls_range_get_low_level(const qpol_policy_t * policy, const qpol_mls_range_t * range, const qpol_mls_level_t ** level)
+{
+ mls_range_t *internal_range = NULL;
+
+ if (policy == NULL || range == NULL || level == NULL) {
+ if (level != NULL)
+ *level = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_range = (mls_range_t *) range;
+ *level = (qpol_mls_level_t *) & (internal_range->level[0]);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_mls_range_get_high_level(const qpol_policy_t * policy, const qpol_mls_range_t * range, const qpol_mls_level_t ** level)
+{
+ mls_range_t *internal_range = NULL;
+
+ if (policy == NULL || range == NULL || level == NULL) {
+ if (level != NULL)
+ *level = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_range = (mls_range_t *) range;
+ *level = (qpol_mls_level_t *) & (internal_range->level[1]);
+
+ return STATUS_SUCCESS;
+}
+
+/* mls_level */
+int qpol_mls_level_get_sens_name(const qpol_policy_t * policy, const qpol_mls_level_t * level, const char **name)
+{
+ policydb_t *db = NULL;
+ mls_level_t *internal_level = NULL;
+
+ if (policy == NULL || level == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_level = (mls_level_t *) level;
+ db = &policy->p->p;
+
+ *name = db->p_sens_val_to_name[internal_level->sens - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_mls_level_get_cat_iter(const qpol_policy_t * policy, const qpol_mls_level_t * level, qpol_iterator_t ** cats)
+{
+ mls_level_t *internal_level = NULL;
+ ebitmap_state_t *es = NULL;
+ int error = 0;
+
+ if (policy == NULL || level == NULL || cats == NULL) {
+ if (cats != NULL)
+ *cats = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_level = (mls_level_t *) level;
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (es == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_level->cat);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_cat,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, cats)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*cats);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/mlsrule_query.c b/libqpol/src/mlsrule_query.c
new file mode 100644
index 0000000..cc7e7a8
--- /dev/null
+++ b/libqpol/src/mlsrule_query.c
@@ -0,0 +1,230 @@
+/**
+ * @file
+ * Implementation for the public interface for searching and iterating over
+ * range transition rules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "iterator_internal.h"
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/mlsrule_query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/avtab.h>
+#include <sepol/policydb/util.h>
+#include <stdlib.h>
+#include "qpol_internal.h"
+
+typedef struct range_trans_state
+{
+ range_trans_t *head;
+ range_trans_t *cur;
+} range_trans_state_t;
+
+static int range_trans_state_end(const qpol_iterator_t * iter)
+{
+ range_trans_state_t *rs = NULL;
+
+ if (!iter || !(rs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return rs->cur ? 0 : 1;
+}
+
+static void *range_trans_state_get_cur(const qpol_iterator_t * iter)
+{
+ range_trans_state_t *rs = NULL;
+
+ if (!iter || !(rs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return rs->cur;
+}
+
+static int range_trans_state_next(qpol_iterator_t * iter)
+{
+ range_trans_state_t *rs = NULL;
+
+ if (!iter || !(rs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (range_trans_state_end(iter)) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ rs->cur = rs->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t range_trans_state_size(const qpol_iterator_t * iter)
+{
+ range_trans_state_t *rs = NULL;
+ size_t count = 0;
+ range_trans_t *tmp = NULL;
+
+ if (!iter || !(rs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = rs->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_policy_get_range_trans_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ range_trans_state_t *rs = NULL;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ rs = calloc(1, sizeof(range_trans_state_t));
+ if (!rs) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_create(policy, (void *)rs, range_trans_state_get_cur,
+ range_trans_state_next, range_trans_state_end, range_trans_state_size, free, iter)) {
+ error = errno;
+ free(rs);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ rs->head = rs->cur = db->range_tr;
+ return STATUS_SUCCESS;
+}
+
+int qpol_range_trans_get_source_type(const qpol_policy_t * policy, const qpol_range_trans_t * rule, const qpol_type_t ** source)
+{
+ policydb_t *db = NULL;
+ range_trans_t *rt = NULL;
+
+ if (source) {
+ *source = NULL;
+ }
+
+ if (!policy || !rule || !source) {
+ errno = EINVAL;
+ ERR(policy, "%s", strerror(EINVAL));
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (range_trans_t *) rule;
+
+ *source = (qpol_type_t *) db->type_val_to_struct[rt->source_type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_range_trans_get_target_type(const qpol_policy_t * policy, const qpol_range_trans_t * rule, const qpol_type_t ** target)
+{
+ policydb_t *db = NULL;
+ range_trans_t *rt = NULL;
+
+ if (target) {
+ *target = NULL;
+ }
+
+ if (!policy || !rule || !target) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (range_trans_t *) rule;
+
+ *target = (qpol_type_t *) db->type_val_to_struct[rt->target_type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_range_trans_get_target_class(const qpol_policy_t * policy, const qpol_range_trans_t * rule, const qpol_class_t ** target)
+{
+ policydb_t *db = NULL;
+ range_trans_t *rt = NULL;
+
+ if (target) {
+ *target = NULL;
+ }
+
+ if (!policy || !rule || !target) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (range_trans_t *) rule;
+
+ *target = (qpol_class_t *) db->class_val_to_struct[rt->target_class - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_range_trans_get_range(const qpol_policy_t * policy, const qpol_range_trans_t * rule, const qpol_mls_range_t ** range)
+{
+ policydb_t *db = NULL;
+ range_trans_t *rt = NULL;
+
+ if (range) {
+ *range = NULL;
+ }
+
+ if (!policy || !rule || !range) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (range_trans_t *) rule;
+
+ *range = (qpol_mls_range_t *) & rt->target_range;
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/module.c b/libqpol/src/module.c
new file mode 100644
index 0000000..35136d1
--- /dev/null
+++ b/libqpol/src/module.c
@@ -0,0 +1,236 @@
+/**
+ * @file
+ * Defines the public interface the QPol policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <qpol/module.h>
+#include <qpol/util.h>
+#include "qpol_internal.h"
+
+#include <sepol/policydb.h>
+#include <sepol/policydb/module.h>
+
+int qpol_module_create_from_file(const char *path, qpol_module_t ** module)
+{
+ sepol_module_package_t *smp = NULL;
+ sepol_policy_file_t *spf = NULL;
+ FILE *infile = NULL;
+ int error = 0;
+ char *tmp = NULL;
+ char *data = NULL;
+ ssize_t size;
+
+ if (module)
+ *module = NULL;
+
+ if (!path || !module) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(*module = calloc(1, sizeof(qpol_module_t)))) {
+ return STATUS_ERR;
+ }
+
+ if (!((*module)->path = strdup(path))) {
+ error = errno;
+ goto err;
+ }
+
+ if (sepol_policy_file_create(&spf)) {
+ error = errno;
+ goto err;
+ }
+
+ infile = fopen(path, "rb");
+ if (!infile) {
+ error = errno;
+ goto err;
+ }
+ size = qpol_bunzip(infile, &data);
+
+ if (size > 0) {
+ if (!qpol_is_data_mod_pkg(data)) {
+ error = ENOTSUP;
+ goto err;
+ }
+ sepol_policy_file_set_mem(spf, data, size);
+ } else {
+ if (!qpol_is_file_mod_pkg(infile)) {
+ error = ENOTSUP;
+ goto err;
+ }
+ rewind(infile);
+ sepol_policy_file_set_fp(spf, infile);
+ }
+
+ if (sepol_module_package_create(&smp)) {
+ error = EIO;
+ goto err;
+ }
+
+ if (sepol_module_package_info(spf, &((*module)->type), &((*module)->name), &tmp)) {
+ error = EIO;
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ if (size > 0) {
+ // Re setting the memory location has the effect of rewind
+ // API is not accessible from here to explicitly "rewind" the
+ // in-memory file.
+ sepol_policy_file_set_mem(spf, data, size);
+ } else {
+ rewind(infile);
+ }
+
+ if (sepol_module_package_read(smp, spf, 0)) {
+ error = EIO;
+ goto err;
+ }
+
+ if (!((*module)->p = sepol_module_package_get_policy(smp))) {
+ error = EIO;
+ goto err;
+ }
+ /* set the module package's policy to NULL as the qpol module owns it now */
+ smp->policy = NULL;
+
+ (*module)->version = (*module)->p->p.version;
+ (*module)->enabled = 1;
+
+ sepol_module_package_free(smp);
+ fclose(infile);
+ if (data != NULL)
+ free (data);
+ sepol_policy_file_free(spf);
+
+ return STATUS_SUCCESS;
+
+ err:
+ qpol_module_destroy(module);
+ sepol_policy_file_free(spf);
+ sepol_module_package_free(smp);
+ if (infile)
+ fclose(infile);
+ if (data != NULL)
+ free (data);
+ if (tmp != NULL)
+ free(tmp);
+ errno = error;
+ return STATUS_ERR;
+}
+
+void qpol_module_destroy(qpol_module_t ** module)
+{
+ if (!module || !(*module))
+ return;
+
+ free((*module)->path);
+ free((*module)->name);
+ sepol_policydb_free((*module)->p);
+ free(*module);
+ *module = NULL;
+}
+
+int qpol_module_get_path(const qpol_module_t * module, const char **path)
+{
+ if (!module || !path) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *path = module->path;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_module_get_name(const qpol_module_t * module, const char **name)
+{
+ if (!module || !name) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *name = module->name;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_module_get_version(const qpol_module_t * module, const char **version)
+{
+ if (!module || !version) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *version = module->version;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_module_get_type(const qpol_module_t * module, int *type)
+{
+ if (!module || !type) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *type = module->type;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_module_get_enabled(const qpol_module_t * module, int *enabled)
+{
+ if (!module || !enabled) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *enabled = module->enabled;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_module_set_enabled(qpol_module_t * module, int enabled)
+{
+ if (!module) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (enabled != module->enabled && module->parent) {
+ module->parent->modified = 1;
+ }
+ module->enabled = enabled;
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/module_compiler.c b/libqpol/src/module_compiler.c
new file mode 100644
index 0000000..dc19798
--- /dev/null
+++ b/libqpol/src/module_compiler.c
@@ -0,0 +1,1440 @@
+/**
+ * @file
+ *
+ * This file is a copy of module_compiler.c from NSA's CVS repository.
+ *
+ * Author : Joshua Brindle <jbrindle@tresys.com>
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Jason Tang <jtang@tresys.com>
+ * Added support for binary policy modules
+ *
+ * Copyright (C) 2004 - 2005 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/avrule_block.h>
+#include <sepol/policydb/conditional.h>
+
+#include "queue.h"
+#include "module_compiler.h"
+
+union stack_item_u
+{
+ avrule_block_t *avrule;
+ cond_list_t *cond_list;
+};
+
+typedef struct scope_stack
+{
+ union stack_item_u u;
+ int type; /* for above union: 1 = avrule block, 2 = conditional */
+ avrule_decl_t *decl; /* if in an avrule block, which
+ * declaration is current */
+ avrule_t *last_avrule;
+ int in_else; /* if in an avrule block, within ELSE branch */
+ int require_given; /* 1 if this block had at least one require */
+ struct scope_stack *parent, *child;
+} scope_stack_t;
+
+extern policydb_t *policydbp;
+extern queue_t id_queue;
+extern int yyerror(char *msg);
+extern void yyerror2(char *fmt, ...);
+
+static int push_stack(int stack_type, ...);
+static void pop_stack(void);
+
+/* keep track of the last item added to the stack */
+static scope_stack_t *stack_top = NULL;
+static avrule_block_t *last_block;
+static uint32_t next_decl_id = 1;
+
+int define_policy(int pass, int module_header_given)
+{
+ char *id;
+
+ if (module_header_given) {
+ if (policydbp->policy_type != POLICY_MOD) {
+ yyerror("Module specification found while not building a policy module.\n");
+ return -1;
+ }
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)) != NULL)
+ free(id);
+ } else {
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no module name");
+ return -1;
+ }
+ policydbp->name = id;
+ if ((policydbp->version = queue_remove(id_queue)) == NULL) {
+ yyerror("Expected a module version but none was found.");
+ return -1;
+ }
+ }
+ } else {
+ if (policydbp->policy_type == POLICY_MOD) {
+ yyerror("Building a policy module, but no module specification found.\n");
+ return -1;
+ }
+ }
+ /* the first declaration within the global avrule
+ block will always have an id of 1 */
+ next_decl_id = 2;
+
+ /* reset the scoping stack */
+ while (stack_top != NULL) {
+ pop_stack();
+ }
+ if (push_stack(1, policydbp->global, policydbp->global->branch_list) == -1) {
+ return -1;
+ }
+ last_block = policydbp->global;
+ return 0;
+}
+
+/* Given the current parse stack, returns 1 if a declaration would be
+ * allowed here or 0 if not. For example, declarations are not
+ * allowed in conditionals, so if there are any conditionals in the
+ * current scope stack then this would return a 0.
+ */
+static int is_declaration_allowed(void)
+{
+ if (stack_top->type != 1 || stack_top->in_else) {
+ return 0;
+ }
+ return 1;
+}
+
+/* Attempt to declare a symbol within the current declaration. If
+ * currently within a non-conditional and in a non-else branch then
+ * insert the symbol, return 0 on success if symbol was undeclared.
+ * For roles and users, it is legal to have multiple declarations; as
+ * such return 1 to indicate that caller must free() the datum because
+ * it was not added. If symbols may not be declared here return -1.
+ * For duplicate declarations return -2. For all else, including out
+ * of memory, return -3. Note that dest_value and datum_value might
+ * not be restricted pointers. */
+int declare_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t * datum_value)
+{
+ avrule_decl_t *decl = stack_top->decl;
+ int retval;
+
+ /* first check that symbols may be declared here */
+ if (!is_declaration_allowed()) {
+ return -1;
+ }
+ retval = symtab_insert(policydbp, symbol_type, key, datum, SCOPE_DECL, decl->decl_id, dest_value);
+ if (retval == 1 && dest_value) {
+ symtab_datum_t *s = (symtab_datum_t *) hashtab_search(policydbp->symtab[symbol_type].table,
+ key);
+ assert(s != NULL);
+
+ if (symbol_type == SYM_LEVELS) {
+ *dest_value = ((level_datum_t *) s)->level->sens;
+ } else {
+ *dest_value = s->value;
+ }
+ } else if (retval == -2) {
+ return -2;
+ } else if (retval < 0) {
+ return -3;
+ } else { /* fall through possible if retval is 0 */
+ }
+ if (datum_value != NULL) {
+ if (ebitmap_set_bit(decl->declared.scope + symbol_type, *datum_value - 1, 1)) {
+ return -3;
+ }
+ }
+ return retval;
+}
+
+static int role_implicit_bounds(hashtab_t roles_tab, char *role_id, role_datum_t * role)
+{
+ role_datum_t *bounds;
+ char *bounds_id, *delim;
+
+ delim = strrchr(role_id, '.');
+ if (!delim)
+ return 0; /* no implicit boundary */
+
+ bounds_id = strdup(role_id);
+ if (!bounds_id) {
+ yyerror("out of memory");
+ return -1;
+ }
+ bounds_id[(size_t) (delim - role_id)] = '\0';
+
+ bounds = hashtab_search(roles_tab, bounds_id);
+ if (!bounds) {
+ yyerror2("role %s doesn't exist, is implicit bounds of %s", bounds_id, role_id);
+ return -1;
+ }
+
+ if (!role->bounds)
+ role->bounds = bounds->s.value;
+ else if (role->bounds != bounds->s.value) {
+ yyerror2("role %s has inconsistent bounds %s/%s",
+ role_id, bounds_id, policydbp->p_role_val_to_name[role->bounds - 1]);
+ return -1;
+ }
+ free(bounds_id);
+
+ return 0;
+}
+
+role_datum_t *declare_role(void)
+{
+ char *id = queue_remove(id_queue), *dest_id = NULL;
+ role_datum_t *role = NULL, *dest_role = NULL;
+ int retval;
+ uint32_t value;
+
+ if (id == NULL) {
+ yyerror("no role name");
+ return NULL;
+ }
+ if ((role = (role_datum_t *) malloc(sizeof(*role))) == NULL) {
+ yyerror("Out of memory!");
+ free(id);
+ return NULL;
+ }
+ role_datum_init(role);
+
+ retval = declare_symbol(SYM_ROLES, id, (hashtab_datum_t *) role, &value, &value);
+ if (retval == 0) {
+ role->s.value = value;
+ if ((dest_id = strdup(id)) == NULL) {
+ yyerror("Out of memory!");
+ return NULL;
+ }
+ } else {
+ /* this role was already declared in this module, or error */
+ dest_id = id;
+ role_datum_destroy(role);
+ free(role);
+ }
+ if (retval == 0 || retval == 1) {
+ /* create a new role_datum_t for this decl, if necessary */
+ hashtab_t roles_tab;
+ assert(stack_top->type == 1);
+ if (stack_top->parent == NULL) {
+ /* in parent, so use global symbol table */
+ roles_tab = policydbp->p_roles.table;
+ } else {
+ roles_tab = stack_top->decl->p_roles.table;
+ }
+ dest_role = (role_datum_t *) hashtab_search(roles_tab, dest_id);
+ if (dest_role == NULL) {
+ if ((dest_role = (role_datum_t *) malloc(sizeof(*dest_role))) == NULL) {
+ yyerror("Out of memory!");
+ free(dest_id);
+ return NULL;
+ }
+ role_datum_init(dest_role);
+ dest_role->s.value = value;
+ if (role_implicit_bounds(roles_tab, dest_id, dest_role)) {
+ free(dest_id);
+ role_datum_destroy(dest_role);
+ free(dest_role);
+ return NULL;
+ }
+ if (hashtab_insert(roles_tab, dest_id, dest_role)) {
+ yyerror("Out of memory!");
+ free(dest_id);
+ role_datum_destroy(dest_role);
+ free(dest_role);
+ return NULL;
+ }
+ } else {
+ free(dest_id);
+ }
+ } else {
+ free(dest_id);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return NULL;
+ }
+ case -2:{
+ yyerror("duplicate declaration of role");
+ return NULL;
+ }
+ case -1:{
+ yyerror("could not declare role here");
+ return NULL;
+ }
+ case 0:{
+ if (ebitmap_set_bit(&dest_role->dominates, role->s.value - 1, 1)) {
+ yyerror("out of memory");
+ return NULL;
+ }
+ return dest_role;
+ }
+ case 1:{
+ return dest_role; /* role already declared for this block */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+type_datum_t *declare_type(unsigned char primary, unsigned char isattr)
+{
+ char *id;
+ type_datum_t *typdatum;
+ int retval;
+ uint32_t value = 0;
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no type/attribute name?");
+ return NULL;
+ }
+ if (strcmp(id, "self") == 0) {
+ yyerror("'self' is a reserved type name and may not be declared.");
+ free(id);
+ return NULL;
+ }
+
+ typdatum = (type_datum_t *) malloc(sizeof(type_datum_t));
+ if (!typdatum) {
+ yyerror("Out of memory!");
+ free(id);
+ return NULL;
+ }
+ type_datum_init(typdatum);
+ typdatum->primary = primary;
+ typdatum->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE;
+
+ retval = declare_symbol(SYM_TYPES, id, typdatum, &value, &value);
+ if (retval == 0 || retval == 1) {
+ if (typdatum->primary) {
+ typdatum->s.value = value;
+ }
+ } else {
+ /* error occurred (can't have duplicate type declarations) */
+ free(id);
+ type_datum_destroy(typdatum);
+ free(typdatum);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return NULL;
+ }
+ case -2:{
+ yyerror2("duplicate declaration of type/attribute");
+ return NULL;
+ }
+ case -1:{
+ yyerror("could not declare type/attribute here");
+ return NULL;
+ }
+ case 0:
+ case 1:{
+ return typdatum;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+static int user_implicit_bounds(hashtab_t users_tab, char *user_id, user_datum_t * user)
+{
+ user_datum_t *bounds;
+ char *bounds_id, *delim;
+
+ delim = strrchr(user_id, '.');
+ if (!delim)
+ return 0; /* no implicit boundary */
+
+ bounds_id = strdup(user_id);
+ if (!bounds_id) {
+ yyerror("out of memory");
+ return -1;
+ }
+ bounds_id[(size_t) (delim - user_id)] = '\0';
+
+ bounds = hashtab_search(users_tab, bounds_id);
+ if (!bounds) {
+ yyerror2("user %s doesn't exist, is implicit bounds of %s", bounds_id, user_id);
+ return -1;
+ }
+
+ if (!user->bounds)
+ user->bounds = bounds->s.value;
+ else if (user->bounds != bounds->s.value) {
+ yyerror2("user %s has inconsistent bounds %s/%s",
+ user_id, bounds_id, policydbp->p_role_val_to_name[user->bounds - 1]);
+ return -1;
+ }
+ free(bounds_id);
+
+ return 0;
+}
+
+user_datum_t *declare_user(void)
+{
+ char *id = queue_remove(id_queue), *dest_id = NULL;
+ user_datum_t *user = NULL, *dest_user = NULL;
+ int retval;
+ uint32_t value = 0;
+
+ if (id == NULL) {
+ yyerror("no user name");
+ return NULL;
+ }
+ if ((user = (user_datum_t *) malloc(sizeof(*user))) == NULL) {
+ yyerror("Out of memory!");
+ free(id);
+ return NULL;
+ }
+ user_datum_init(user);
+
+ retval = declare_symbol(SYM_USERS, id, (hashtab_datum_t *) user, &value, &value);
+
+ if (retval == 0) {
+ user->s.value = value;
+ if ((dest_id = strdup(id)) == NULL) {
+ yyerror("Out of memory!");
+ return NULL;
+ }
+ } else {
+ /* this user was already declared in this module, or error */
+ dest_id = id;
+ user_datum_destroy(user);
+ free(user);
+ }
+ if (retval == 0 || retval == 1) {
+ /* create a new user_datum_t for this decl, if necessary */
+ hashtab_t users_tab;
+ assert(stack_top->type == 1);
+ if (stack_top->parent == NULL) {
+ /* in parent, so use global symbol table */
+ users_tab = policydbp->p_users.table;
+ } else {
+ users_tab = stack_top->decl->p_users.table;
+ }
+ dest_user = (user_datum_t *) hashtab_search(users_tab, dest_id);
+ if (dest_user == NULL) {
+ if ((dest_user = (user_datum_t *) malloc(sizeof(*dest_user))) == NULL) {
+ yyerror("Out of memory!");
+ free(dest_id);
+ return NULL;
+ }
+ user_datum_init(dest_user);
+ dest_user->s.value = value;
+ if (user_implicit_bounds(users_tab, dest_id, dest_user)) {
+ free(dest_id);
+ user_datum_destroy(dest_user);
+ free(dest_user);
+ return NULL;
+ }
+ if (hashtab_insert(users_tab, dest_id, dest_user)) {
+ yyerror("Out of memory!");
+ free(dest_id);
+ user_datum_destroy(dest_user);
+ free(dest_user);
+ return NULL;
+ }
+ } else {
+ free(dest_id);
+ }
+ } else {
+ free(dest_id);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return NULL;
+ }
+ case -2:{
+ yyerror("duplicate declaration of user");
+ return NULL;
+ }
+ case -1:{
+ yyerror("could not declare user here");
+ return NULL;
+ }
+ case 0:{
+ return dest_user;
+ }
+ case 1:{
+ return dest_user; /* user already declared for this block */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+/* Return a type_datum_t for the local avrule_decl with the given ID.
+ * If it does not exist, create one with the same value as 'value'.
+ * This function assumes that the ID is within scope. c.f.,
+ * is_id_in_scope().
+ *
+ * NOTE: this function usurps ownership of id afterwards. The caller
+ * shall not reference it nor free() it afterwards.
+ */
+type_datum_t *get_local_type(char *id, uint32_t value, unsigned char isattr)
+{
+ type_datum_t *dest_typdatum;
+ hashtab_t types_tab;
+ assert(stack_top->type == 1);
+ if (stack_top->parent == NULL) {
+ /* in global, so use global symbol table */
+ types_tab = policydbp->p_types.table;
+ } else {
+ types_tab = stack_top->decl->p_types.table;
+ }
+ dest_typdatum = hashtab_search(types_tab, id);
+ if (!dest_typdatum) {
+ dest_typdatum = (type_datum_t *) malloc(sizeof(type_datum_t));
+ if (dest_typdatum == NULL) {
+ free(id);
+ return NULL;
+ }
+ type_datum_init(dest_typdatum);
+ dest_typdatum->s.value = value;
+ dest_typdatum->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE;
+ dest_typdatum->primary = 1;
+ if (hashtab_insert(types_tab, id, dest_typdatum)) {
+ free(id);
+ type_datum_destroy(dest_typdatum);
+ free(dest_typdatum);
+ return NULL;
+ }
+
+ } else {
+ free(id);
+ if (dest_typdatum->flavor != isattr ? TYPE_ATTRIB : TYPE_TYPE) {
+ return NULL;
+ }
+ }
+ return dest_typdatum;
+}
+
+/* Given the current parse stack, returns 1 if a requirement would be
+ * allowed here or 0 if not. For example, the ELSE branch may never
+ * have its own requirements.
+ */
+static int is_require_allowed(void)
+{
+ if (stack_top->type == 1 && !stack_top->in_else) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Attempt to require a symbol within the current scope. If currently
+ * within an optional (and not its else branch), add the symbol to the
+ * required list. Return 0 on success, 1 if caller needs to free()
+ * datum. If symbols may not be declared here return -1. For duplicate
+ * declarations return -2. For all else, including out of memory,
+ * return -3.. Note that dest_value and datum_value might not be
+ * restricted pointers.
+ */
+int require_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t * datum_value)
+{
+ avrule_decl_t *decl = stack_top->decl;
+ int retval;
+
+ /* first check that symbols may be required here */
+ if (!is_require_allowed()) {
+ return -1;
+ }
+ retval = symtab_insert(policydbp, symbol_type, key, datum, SCOPE_REQ, decl->decl_id, dest_value);
+ if (retval == 1) {
+ symtab_datum_t *s = (symtab_datum_t *) hashtab_search(policydbp->symtab[symbol_type].table,
+ key);
+ assert(s != NULL);
+
+ if (symbol_type == SYM_LEVELS) {
+ *dest_value = ((level_datum_t *) s)->level->sens;
+ } else {
+ *dest_value = s->value;
+ }
+ } else if (retval == -2) {
+ /* ignore require statements if that symbol was
+ * previously declared and is in current scope */
+ int prev_declaration_ok = 0;
+ if (is_id_in_scope(symbol_type, key)) {
+ if (symbol_type == SYM_TYPES) {
+ /* check that previous symbol has same
+ * type/attribute-ness */
+ unsigned char new_isattr = ((type_datum_t *) datum)->flavor;
+ type_datum_t *old_datum = (type_datum_t *) hashtab_search(policydbp->symtab[SYM_TYPES].table, key);
+ assert(old_datum != NULL);
+ unsigned char old_isattr = old_datum->flavor;
+ prev_declaration_ok = (old_isattr == new_isattr ? 1 : 0);
+ } else {
+ prev_declaration_ok = 1;
+ }
+ }
+ if (prev_declaration_ok) {
+ /* ignore this require statement because it
+ * was already declared within my scope */
+ stack_top->require_given = 1;
+ return 1;
+ } else {
+ /* previous declaration was not in scope or
+ * had a mismatched type/attribute, so
+ * generate an error */
+ return -2;
+ }
+ } else if (retval < 0) {
+ return -3;
+ } else { /* fall through possible if retval is 0 or 1 */
+ }
+ if (datum_value != NULL) {
+ if (ebitmap_set_bit(decl->required.scope + symbol_type, *datum_value - 1, 1)) {
+ return -3;
+ }
+ }
+ stack_top->require_given = 1;
+ return retval;
+}
+
+int add_perm_to_class(uint32_t perm_value, uint32_t class_value)
+{
+ avrule_decl_t *decl = stack_top->decl;
+ scope_index_t *scope;
+
+ assert(perm_value >= 1);
+ assert(class_value >= 1);
+ scope = &decl->required;
+ if (class_value > scope->class_perms_len) {
+ int i;
+ ebitmap_t *new_map = realloc(scope->class_perms_map,
+ class_value * sizeof(*new_map));
+ if (new_map == NULL) {
+ return -1;
+ }
+ scope->class_perms_map = new_map;
+ for (i = scope->class_perms_len; i < class_value; i++) {
+ ebitmap_init(scope->class_perms_map + i);
+ }
+ scope->class_perms_len = class_value;
+ }
+ if (ebitmap_set_bit(scope->class_perms_map + class_value - 1, perm_value - 1, 1)) {
+ return -1;
+ }
+ return 0;
+}
+
+static int perm_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p __attribute__ ((unused)))
+{
+ if (key)
+ free(key);
+ free(datum);
+ return 0;
+}
+
+static void class_datum_destroy(class_datum_t * cladatum)
+{
+ if (cladatum != NULL) {
+ hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(cladatum->permissions.table);
+ free(cladatum);
+ }
+}
+
+int require_class(int pass)
+{
+ char *class_id = queue_remove(id_queue);
+ char *perm_id = NULL;
+ class_datum_t *datum = NULL;
+ perm_datum_t *perm = NULL;
+ int ret;
+
+ if (pass == 2) {
+ free(class_id);
+ while ((perm_id = queue_remove(id_queue)) != NULL)
+ free(perm_id);
+ return 0;
+ }
+
+ /* first add the class if it is not already there */
+ if (class_id == NULL) {
+ yyerror("no class name for class definition?");
+ return -1;
+ }
+
+ if ((datum = calloc(1, sizeof(*datum))) == NULL || symtab_init(&datum->permissions, PERM_SYMTAB_SIZE)) {
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+ ret = require_symbol(SYM_CLASSES, class_id, datum, &datum->s.value, &datum->s.value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ free(class_id);
+ class_datum_destroy(datum);
+ goto cleanup;
+ }
+ case -2:{
+ yyerror("duplicate declaration of class");
+ free(class_id);
+ class_datum_destroy(datum);
+ goto cleanup;
+ }
+ case -1:{
+ yyerror("could not require class here");
+ free(class_id);
+ class_datum_destroy(datum);
+ goto cleanup;
+ }
+ case 0:{
+ /* a new class was added; reindex everything */
+ if (policydb_index_classes(policydbp)) {
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+ break;
+ }
+ case 1:{
+ class_datum_destroy(datum);
+ datum = hashtab_search(policydbp->p_classes.table, class_id);
+ assert(datum); /* the class datum should have existed */
+ free(class_id);
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+
+ /* now add each of the permissions to this class's requirements */
+ while ((perm_id = queue_remove(id_queue)) != NULL) {
+ int allocated = 0;
+
+ /* Is the permission already in the table? */
+ perm = hashtab_search(datum->permissions.table, perm_id);
+ if (!perm && datum->comdatum)
+ perm = hashtab_search(datum->comdatum->permissions.table, perm_id);
+ if (perm) {
+ /* Yes, drop the name. */
+ free(perm_id);
+ } else {
+ /* No - allocate and insert an entry for it. */
+ if (policydbp->policy_type == POLICY_BASE) {
+ yyerror2("Base policy - require of permission %s without prior declaration.", perm_id);
+ free(perm_id);
+ goto cleanup;
+ }
+ allocated = 1;
+ if ((perm = malloc(sizeof(*perm))) == NULL) {
+ yyerror("Out of memory!");
+ free(perm_id);
+ goto cleanup;
+ }
+ memset(perm, 0, sizeof(*perm));
+ ret = hashtab_insert(datum->permissions.table, perm_id, perm);
+ if (ret) {
+ yyerror("Out of memory!");
+ free(perm_id);
+ free(perm);
+ goto cleanup;
+ }
+ perm->s.value = datum->permissions.nprim + 1;
+ }
+
+ if (add_perm_to_class(perm->s.value, datum->s.value) == -1) {
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+
+ /* Update number of primitives if we allocated one. */
+ if (allocated)
+ datum->permissions.nprim++;
+ }
+ return 0;
+ cleanup:
+ return -1;
+}
+
+int require_role(int pass)
+{
+ char *id = queue_remove(id_queue);
+ role_datum_t *role = NULL;
+ int retval;
+ if (pass == 2) {
+ free(id);
+ return 0;
+ }
+ if (id == NULL) {
+ yyerror("no role name");
+ return -1;
+ }
+ if ((role = malloc(sizeof(*role))) == NULL) {
+ free(id);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ role_datum_init(role);
+ retval = require_symbol(SYM_ROLES, id, (hashtab_datum_t *) role, &role->s.value, &role->s.value);
+ if (retval != 0) {
+ free(id);
+ role_datum_destroy(role);
+ free(role);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return -1;
+ }
+ case -2:{
+ yyerror("duplicate declaration of role");
+ return -1;
+ }
+ case -1:{
+ yyerror("could not require role here");
+ return -1;
+ }
+ case 0:{
+ /* all roles dominate themselves */
+ if (ebitmap_set_bit(&role->dominates, role->s.value - 1, 1)) {
+ yyerror("Out of memory");
+ return -1;
+ }
+ return 0;
+ }
+ case 1:{
+ return 0; /* role already required */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+static int require_type_or_attribute(int pass, unsigned char isattr)
+{
+ char *id = queue_remove(id_queue);
+ type_datum_t *type = NULL;
+ int retval;
+ if (pass == 2) {
+ free(id);
+ return 0;
+ }
+ if (id == NULL) {
+ yyerror("no type name");
+ return -1;
+ }
+ if ((type = malloc(sizeof(*type))) == NULL) {
+ free(id);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ type_datum_init(type);
+ type->primary = 1;
+ type->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE;
+ retval = require_symbol(SYM_TYPES, id, (hashtab_datum_t *) type, &type->s.value, &type->s.value);
+ if (retval != 0) {
+ free(id);
+ free(type);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return -1;
+ }
+ case -2:{
+ yyerror("duplicate declaration of type/attribute");
+ return -1;
+ }
+ case -1:{
+ yyerror("could not require type/attribute here");
+ return -1;
+ }
+ case 0:{
+ return 0;
+ }
+ case 1:{
+ return 0; /* type already required */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+int require_type(int pass)
+{
+ return require_type_or_attribute(pass, 0);
+}
+
+int require_attribute(int pass)
+{
+ return require_type_or_attribute(pass, 1);
+}
+
+int require_user(int pass)
+{
+ char *id = queue_remove(id_queue);
+ user_datum_t *user = NULL;
+ int retval;
+ if (pass == 1) {
+ free(id);
+ return 0;
+ }
+ if (id == NULL) {
+ yyerror("no user name");
+ return -1;
+ }
+ if ((user = malloc(sizeof(*user))) == NULL) {
+ free(id);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ user_datum_init(user);
+ retval = require_symbol(SYM_USERS, id, (hashtab_datum_t *) user, &user->s.value, &user->s.value);
+ if (retval != 0) {
+ free(id);
+ user_datum_destroy(user);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return -1;
+ }
+ case -2:{
+ yyerror("duplicate declaration of user");
+ return -1;
+ }
+ case -1:{
+ yyerror("could not require user here");
+ return -1;
+ }
+ case 0:{
+ return 0;
+ }
+ case 1:{
+ return 0; /* user already required */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+int require_bool(int pass)
+{
+ char *id = queue_remove(id_queue);
+ cond_bool_datum_t *booldatum = NULL;
+ int retval;
+ if (pass == 2) {
+ free(id);
+ return 0;
+ }
+ if (id == NULL) {
+ yyerror("no boolean name");
+ return -1;
+ }
+ if ((booldatum = calloc(1, sizeof(*booldatum))) == NULL) {
+ cond_destroy_bool(id, booldatum, NULL);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ retval = require_symbol(SYM_BOOLS, id, (hashtab_datum_t *) booldatum, &booldatum->s.value, &booldatum->s.value);
+ if (retval != 0) {
+ cond_destroy_bool(id, booldatum, NULL);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return -1;
+ }
+ case -2:{
+ yyerror("duplicate declaration of boolean");
+ return -1;
+ }
+ case -1:{
+ yyerror("could not require boolean here");
+ return -1;
+ }
+ case 0:{
+ return 0;
+ }
+ case 1:{
+ return 0; /* boolean already required */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+int require_sens(int pass)
+{
+ char *id = queue_remove(id_queue);
+ level_datum_t *level = NULL;
+ int retval;
+ if (pass == 2) {
+ free(id);
+ return 0;
+ }
+ if (!id) {
+ yyerror("no sensitivity name");
+ return -1;
+ }
+ level = malloc(sizeof(level_datum_t));
+ if (!level) {
+ free(id);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ level_datum_init(level);
+ level->level = malloc(sizeof(mls_level_t));
+ if (!level->level) {
+ free(id);
+ level_datum_destroy(level);
+ free(level);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ mls_level_init(level->level);
+ retval = require_symbol(SYM_LEVELS, id, (hashtab_datum_t *) level, &level->level->sens, &level->level->sens);
+ if (retval != 0) {
+ free(id);
+ mls_level_destroy(level->level);
+ free(level->level);
+ level_datum_destroy(level);
+ free(level);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return -1;
+ }
+ case -2:{
+ yyerror("duplicate declaration of sensitivity");
+ return -1;
+ }
+ case -1:{
+ yyerror("could not require sensitivity here");
+ return -1;
+ }
+ case 0:{
+ return 0;
+ }
+ case 1:{
+ return 0; /* sensitivity already required */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+int require_cat(int pass)
+{
+ char *id = queue_remove(id_queue);
+ cat_datum_t *cat = NULL;
+ int retval;
+ if (pass == 2) {
+ free(id);
+ return 0;
+ }
+ if (!id) {
+ yyerror("no category name");
+ return -1;
+ }
+ cat = malloc(sizeof(cat_datum_t));
+ if (!cat) {
+ free(id);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ cat_datum_init(cat);
+
+ retval = require_symbol(SYM_CATS, id, (hashtab_datum_t *) cat, &cat->s.value, &cat->s.value);
+ if (retval != 0) {
+ free(id);
+ cat_datum_destroy(cat);
+ free(cat);
+ }
+ switch (retval) {
+ case -3:{
+ yyerror("Out of memory!");
+ return -1;
+ }
+ case -2:{
+ yyerror("duplicate declaration of category");
+ return -1;
+ }
+ case -1:{
+ yyerror("could not require category here");
+ return -1;
+ }
+ case 0:{
+ return 0;
+ }
+ case 1:{
+ return 0; /* category already required */
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+static int is_scope_in_stack(scope_datum_t * scope, scope_stack_t * stack)
+{
+ int i;
+ if (stack == NULL) {
+ return 0; /* no matching scope found */
+ }
+ if (stack->type == 1) {
+ avrule_decl_t *decl = stack->decl;
+ for (i = 0; i < scope->decl_ids_len; i++) {
+ if (scope->decl_ids[i] == decl->decl_id) {
+ return 1;
+ }
+ }
+ } else {
+ /* note that conditionals can't declare or require
+ * symbols, so skip this level */
+ }
+
+ /* not within scope of this stack, so try its parent */
+ return is_scope_in_stack(scope, stack->parent);
+}
+
+int is_id_in_scope(uint32_t symbol_type, hashtab_key_t id)
+{
+ scope_datum_t *scope = (scope_datum_t *) hashtab_search(policydbp->scope[symbol_type].table, id);
+ if (scope == NULL) {
+ return 1; /* id is not known, so return success */
+ }
+ return is_scope_in_stack(scope, stack_top);
+}
+
+static int is_perm_in_scope_index(uint32_t perm_value, uint32_t class_value, scope_index_t * scope)
+{
+ if (class_value > scope->class_perms_len) {
+ return 1;
+ }
+ if (ebitmap_get_bit(scope->class_perms_map + class_value - 1, perm_value - 1)) {
+ return 1;
+ }
+ return 0;
+}
+
+static int is_perm_in_stack(uint32_t perm_value, uint32_t class_value, scope_stack_t * stack)
+{
+ if (stack == NULL) {
+ return 0; /* no matching scope found */
+ }
+ if (stack->type == 1) {
+ avrule_decl_t *decl = stack->decl;
+ if (is_perm_in_scope_index(perm_value, class_value, &decl->required)
+ || is_perm_in_scope_index(perm_value, class_value, &decl->declared)) {
+ return 1;
+ }
+ } else {
+ /* note that conditionals can't declare or require
+ * symbols, so skip this level */
+ }
+
+ /* not within scope of this stack, so try its parent */
+ return is_perm_in_stack(perm_value, class_value, stack->parent);
+}
+
+int is_perm_in_scope(hashtab_key_t perm_id, hashtab_key_t class_id)
+{
+ class_datum_t *cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table,
+ class_id);
+ perm_datum_t *perdatum;
+ if (cladatum == NULL) {
+ return 1;
+ }
+ perdatum = (perm_datum_t *) hashtab_search(cladatum->permissions.table, perm_id);
+ if (perdatum == NULL) {
+ return 1;
+ }
+ return is_perm_in_stack(perdatum->s.value, cladatum->s.value, stack_top);
+}
+
+cond_list_t *get_current_cond_list(cond_list_t * cond)
+{
+ /* FIX ME: do something different here if in a nested
+ * conditional? */
+ avrule_decl_t *decl = stack_top->decl;
+ return get_decl_cond_list(policydbp, decl, cond);
+}
+
+/* Append the new conditional node to the existing ones. During
+ * expansion the list will be reversed -- i.e., the last AV rule will
+ * be the first one listed in the policy. This matches the behavior
+ * of the upstream compiler. */
+void append_cond_list(cond_list_t * cond)
+{
+ cond_list_t *old_cond = get_current_cond_list(cond);
+ avrule_t *tmp;
+ assert(old_cond != NULL); /* probably out of memory */
+ if (old_cond->avtrue_list == NULL) {
+ old_cond->avtrue_list = cond->avtrue_list;
+ } else {
+ for (tmp = old_cond->avtrue_list; tmp->next != NULL; tmp = tmp->next) ;
+ tmp->next = cond->avtrue_list;
+ }
+ if (old_cond->avfalse_list == NULL) {
+ old_cond->avfalse_list = cond->avfalse_list;
+ } else {
+ for (tmp = old_cond->avfalse_list; tmp->next != NULL; tmp = tmp->next) ;
+ tmp->next = cond->avfalse_list;
+ }
+}
+
+void append_avrule(avrule_t * avrule)
+{
+ avrule_decl_t *decl = stack_top->decl;
+
+ /* currently avrules follow a completely different code path
+ * for handling avrules and compute types
+ * (define_cond_avrule_te_avtab, define_cond_compute_type);
+ * therefore there ought never be a conditional on top of the
+ * scope stack */
+ assert(stack_top->type == 1);
+
+ if (stack_top->last_avrule == NULL) {
+ decl->avrules = avrule;
+ } else {
+ stack_top->last_avrule->next = avrule;
+ }
+ stack_top->last_avrule = avrule;
+}
+
+/* this doesn't actually append, but really prepends it */
+void append_role_trans(role_trans_rule_t * role_tr_rules)
+{
+ avrule_decl_t *decl = stack_top->decl;
+
+ /* role transitions are not allowed within conditionals */
+ assert(stack_top->type == 1);
+
+ role_tr_rules->next = decl->role_tr_rules;
+ decl->role_tr_rules = role_tr_rules;
+}
+
+/* this doesn't actually append, but really prepends it */
+void append_role_allow(role_allow_rule_t * role_allow_rules)
+{
+ avrule_decl_t *decl = stack_top->decl;
+
+ /* role allows are not allowed within conditionals */
+ assert(stack_top->type == 1);
+
+ role_allow_rules->next = decl->role_allow_rules;
+ decl->role_allow_rules = role_allow_rules;
+}
+
+/* this doesn't actually append, but really prepends it */
+void append_range_trans(range_trans_rule_t * range_tr_rules)
+{
+ avrule_decl_t *decl = stack_top->decl;
+
+ /* range transitions are not allowed within conditionals */
+ assert(stack_top->type == 1);
+
+ range_tr_rules->next = decl->range_tr_rules;
+ decl->range_tr_rules = range_tr_rules;
+}
+
+int begin_optional(int pass)
+{
+ avrule_block_t *block = NULL;
+ avrule_decl_t *decl;
+ if (pass == 1) {
+ /* allocate a new avrule block for this optional block */
+ if ((block = avrule_block_create()) == NULL || (decl = avrule_decl_create(next_decl_id)) == NULL) {
+ goto cleanup;
+ }
+ block->flags |= AVRULE_OPTIONAL;
+ block->branch_list = decl;
+ last_block->next = block;
+ } else {
+ /* select the next block from the chain built during pass 1 */
+ block = last_block->next;
+ assert(block != NULL && block->branch_list != NULL && block->branch_list->decl_id == next_decl_id);
+ decl = block->branch_list;
+ }
+ if (push_stack(1, block, decl) == -1) {
+ goto cleanup;
+ }
+ stack_top->last_avrule = NULL;
+ last_block = block;
+ next_decl_id++;
+ return 0;
+ cleanup:
+ yyerror("Out of memory!");
+ avrule_block_destroy(block);
+ return -1;
+}
+
+int end_optional(int pass)
+{
+ /* once nested conditionals are allowed, do the stack unfolding here */
+ pop_stack();
+ return 0;
+}
+
+int begin_optional_else(int pass)
+{
+ avrule_decl_t *decl;
+ assert(stack_top->type == 1 && stack_top->in_else == 0);
+ if (pass == 1) {
+ /* allocate a new declaration and add it to the
+ * current chain */
+ if ((decl = avrule_decl_create(next_decl_id)) == NULL) {
+ yyerror("Out of memory!");
+ return -1;
+ }
+ stack_top->decl->next = decl;
+ } else {
+ /* pick the (hopefully last) declaration of this
+ avrule block, built from pass 1 */
+ decl = stack_top->decl->next;
+ assert(decl != NULL && decl->next == NULL && decl->decl_id == next_decl_id);
+ }
+ stack_top->in_else = 1;
+ stack_top->decl = decl;
+ stack_top->last_avrule = NULL;
+ stack_top->require_given = 0;
+ next_decl_id++;
+ return 0;
+}
+
+static int copy_requirements(avrule_decl_t * dest, scope_stack_t * stack)
+{
+ int i;
+ if (stack == NULL) {
+ return 0;
+ }
+ if (stack->type == 1) {
+ scope_index_t *src_scope = &stack->decl->required;
+ scope_index_t *dest_scope = &dest->required;
+ for (i = 0; i < SYM_NUM; i++) {
+ ebitmap_t *src_bitmap = &src_scope->scope[i];
+ ebitmap_t *dest_bitmap = &dest_scope->scope[i];
+ if (ebitmap_union(dest_bitmap, src_bitmap)) {
+ yyerror("Out of memory!");
+ return -1;
+ }
+ }
+ /* now copy class permissions */
+ if (src_scope->class_perms_len > dest_scope->class_perms_len) {
+ ebitmap_t *new_map = realloc(dest_scope->class_perms_map,
+ src_scope->class_perms_len * sizeof(*new_map));
+ if (new_map == NULL) {
+ yyerror("Out of memory!");
+ return -1;
+ }
+ dest_scope->class_perms_map = new_map;
+ for (i = dest_scope->class_perms_len; i < src_scope->class_perms_len; i++) {
+ ebitmap_init(dest_scope->class_perms_map + i);
+ }
+ dest_scope->class_perms_len = src_scope->class_perms_len;
+ }
+ for (i = 0; i < src_scope->class_perms_len; i++) {
+ ebitmap_t *src_bitmap = &src_scope->class_perms_map[i];
+ ebitmap_t *dest_bitmap = &dest_scope->class_perms_map[i];
+ if (ebitmap_union(dest_bitmap, src_bitmap)) {
+ yyerror("Out of memory!");
+ return -1;
+ }
+ }
+ }
+ return copy_requirements(dest, stack->parent);
+}
+
+/* During pass 1, check that at least one thing was required within
+ * this block, for those places where a REQUIRED is necessary. During
+ * pass 2, have this block inherit its parents' requirements. Return
+ * 0 on success, -1 on failure. */
+int end_avrule_block(int pass)
+{
+ avrule_decl_t *decl = stack_top->decl;
+ assert(stack_top->type == 1);
+ if (pass == 2) {
+ /* this avrule_decl inherits all of its parents'
+ * requirements */
+ if (copy_requirements(decl, stack_top->parent) == -1) {
+ return -1;
+ }
+ return 0;
+ }
+ if (!stack_top->in_else && !stack_top->require_given) {
+ if (policydbp->policy_type == POLICY_BASE && stack_top->parent != NULL) {
+ /* if this is base no require should be in the global block */
+ return 0;
+ } else {
+ /* non-ELSE branches must have at least one thing required */
+ yyerror("This block has no require section.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Push a new scope on to the stack and update the 'last' pointer.
+ * Return 0 on success, -1 if out * of memory. */
+static int push_stack(int stack_type, ...)
+{
+ scope_stack_t *s = calloc(1, sizeof(*s));
+ va_list ap;
+ if (s == NULL) {
+ return -1;
+ }
+ va_start(ap, stack_type);
+ switch (s->type = stack_type) {
+ case 1:{
+ s->u.avrule = va_arg(ap, avrule_block_t *);
+ s->decl = va_arg(ap, avrule_decl_t *);
+ break;
+ }
+ case 2:{
+ s->u.cond_list = va_arg(ap, cond_list_t *);
+ break;
+ }
+ default:
+ /* invalid stack type given */
+ assert(0);
+ }
+ va_end(ap);
+ s->parent = stack_top;
+ s->child = NULL;
+ stack_top = s;
+ return 0;
+}
+
+/* Pop off the most recently added from the stack. Update the 'last'
+ * pointer. */
+static void pop_stack(void)
+{
+ scope_stack_t *parent;
+ assert(stack_top != NULL);
+ parent = stack_top->parent;
+ if (parent != NULL) {
+ parent->child = NULL;
+ }
+ free(stack_top);
+ stack_top = parent;
+}
diff --git a/libqpol/src/module_compiler.h b/libqpol/src/module_compiler.h
new file mode 100644
index 0000000..489086d
--- /dev/null
+++ b/libqpol/src/module_compiler.h
@@ -0,0 +1,115 @@
+/**
+ * @file
+ *
+ * This file is a copy of module_compiler.h from NSA's CVS repository.
+ *
+ * Author : Joshua Brindle <jbrindle@tresys.com>
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Jason Tang <jtang@tresys.com>
+ * Added support for binary policy modules
+ *
+ * Copyright (C) 2004 - 2005 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef MODULE_COMPILER_H
+#define MODULE_COMPILER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sepol/policydb/hashtab.h>
+
+/* Called when checkpolicy begins to parse a policy -- either at the
+ * very beginning for a kernel/base policy, or after the module header
+ * for policy modules. Initialize the memory structures within.
+ * Return 0 on success, -1 on error. */
+int define_policy(int pass, int module_header_given);
+
+/* Declare a symbol declaration to the current avrule_decl. Check
+ * that insertion is allowed here and that the symbol does not already
+ * exist. Returns 0 on success, 1 if symbol was already there (caller
+ * needs to free() the datum), -1 if declarations not allowed, -2 for
+ * duplicate declarations, -3 for all else.
+ */
+int declare_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t * datum_value);
+
+role_datum_t *declare_role(void);
+type_datum_t *declare_type(unsigned char primary, unsigned char isattr);
+user_datum_t *declare_user(void);
+
+type_datum_t *get_local_type(char *id, uint32_t value, unsigned char isattr);
+
+/* Add a symbol to the current avrule_block's require section. Note
+ * that a module may not both declare and require the same symbol.
+ * Returns 0 on success, -1 on error. */
+int require_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t * datum_value);
+
+/* Enable a permission for a class within the current avrule_decl.
+ * Return 0 on success, -1 if out of memory. */
+int add_perm_to_class(uint32_t perm_value, uint32_t class_value);
+
+/* Functions called from REQUIRE blocks. Add the first symbol on the
+ * id_queue to this avrule_decl's scope if not already there.
+ * c.f. require_symbol(). */
+int require_class(int pass);
+int require_role(int pass);
+int require_type(int pass);
+int require_attribute(int pass);
+int require_user(int pass);
+int require_bool(int pass);
+int require_sens(int pass);
+int require_cat(int pass);
+
+/* Check if an identifier is within the scope of the current
+ * declaration or any of its parents. Return 1 if it is, 0 if not.
+ * If the identifier is not known at all then return 1 (truth). */
+int is_id_in_scope(uint32_t symbol_type, hashtab_key_t id);
+
+/* Check if a particular permission is within the scope of the current
+ * declaration or any of its parents. Return 1 if it is, 0 if not.
+ * If the identifier is not known at all then return 1 (truth). */
+int is_perm_in_scope(hashtab_key_t perm_id, hashtab_key_t class_id);
+
+/* Search the current avrules block for a conditional with the same
+ * expression as 'cond'. If the conditional does not exist then
+ * create one. Either way, return the conditional. */
+cond_list_t *get_current_cond_list(cond_list_t * cond);
+
+/* Append rule to the current avrule_block. */
+void append_cond_list(cond_list_t * cond);
+void append_avrule(avrule_t * avrule);
+void append_role_trans(role_trans_rule_t * role_tr_rules);
+void append_role_allow(role_allow_rule_t * role_allow_rules);
+void append_range_trans(range_trans_rule_t * range_tr_rules);
+
+/* Create a new optional block and add it to the global policy.
+ * During the second pass resolve the block's requirements. Return 0
+ * on success, -1 on error.
+ */
+int begin_optional(int pass);
+int end_optional(int pass);
+
+/* ELSE blocks are similar to normal blocks with the following two
+ * limitations:
+ * - no declarations are allowed within else branches
+ * - no REQUIRES are allowed; the else branch inherits the parent's
+ * requirements
+ */
+int begin_optional_else(int pass);
+
+/* Called whenever existing an avrule block. Check that the block had
+ * a non-empty REQUIRE section. If so pop the block off of the scop
+ * stack and return 0. If not then send an error to yyerror and
+ * return -1. */
+int end_avrule_block(int pass);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libqpol/src/netifcon_query.c b/libqpol/src/netifcon_query.c
new file mode 100644
index 0000000..2f39757
--- /dev/null
+++ b/libqpol/src/netifcon_query.c
@@ -0,0 +1,159 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over netifcon statements.
+*
+* @author Kevin Carr kcarr@tresys.com
+* @author Jeremy A. Mowery jmowery@tresys.com
+* @author Jason Tang jtang@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+#include <qpol/netifcon_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+int qpol_policy_get_netifcon_by_name(const qpol_policy_t * policy, const char *name, const qpol_netifcon_t ** ocon)
+{
+ ocontext_t *tmp = NULL;
+ policydb_t *db = NULL;
+
+ if (ocon != NULL)
+ *ocon = NULL;
+
+ if (policy == NULL || name == NULL || ocon == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ for (tmp = db->ocontexts[OCON_NETIF]; tmp; tmp = tmp->next) {
+ if (!strcmp(name, tmp->u.name))
+ break;
+ }
+
+ *ocon = (qpol_netifcon_t *) tmp;
+
+ if (*ocon == NULL) {
+ ERR(policy, "could not find netifcon statement for %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_netifcon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ int error = 0;
+ ocon_state_t *os = NULL;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ os = calloc(1, sizeof(ocon_state_t));
+ if (os == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ os->head = os->cur = db->ocontexts[OCON_NETIF];
+
+ if (qpol_iterator_create(policy, (void *)os, ocon_state_get_cur,
+ ocon_state_next, ocon_state_end, ocon_state_size, free, iter)) {
+ free(os);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_netifcon_get_name(const qpol_policy_t * policy, const qpol_netifcon_t * ocon, const char **name)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (name != NULL)
+ *name = NULL;
+
+ if (policy == NULL || ocon == NULL || name == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *name = internal_ocon->u.name;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_netifcon_get_msg_con(const qpol_policy_t * policy, const qpol_netifcon_t * ocon, const qpol_context_t ** context)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || ocon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *context = (qpol_context_t *) & (internal_ocon->context[1]);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_netifcon_get_if_con(const qpol_policy_t * policy, const qpol_netifcon_t * ocon, const qpol_context_t ** context)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || ocon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *context = (qpol_context_t *) & (internal_ocon->context[0]);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/nodecon_query.c b/libqpol/src/nodecon_query.c
new file mode 100644
index 0000000..4a91a1f
--- /dev/null
+++ b/libqpol/src/nodecon_query.c
@@ -0,0 +1,329 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over nodecon statements.
+*
+* @author Kevin Carr kcarr@tresys.com
+* @author Jeremy A. Mowery jmowery@tresys.com
+* @author Jason Tang jtang@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+#include <qpol/nodecon_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+struct qpol_nodecon
+{
+ ocontext_t *ocon;
+ unsigned char protocol;
+};
+
+int qpol_policy_get_nodecon_by_node(const qpol_policy_t * policy, uint32_t addr[4], uint32_t mask[4], unsigned char protocol,
+ qpol_nodecon_t ** ocon)
+{
+ policydb_t *db = NULL;
+ ocontext_t *tmp = NULL;
+ int error = 0;
+
+ if (ocon != NULL)
+ *ocon = NULL;
+
+ if (policy == NULL || ocon == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ for (tmp = db->ocontexts[(protocol == QPOL_IPV4 ? OCON_NODE : OCON_NODE6)]; tmp; tmp = tmp->next) {
+ if (protocol == QPOL_IPV4) {
+ if (addr[0] != tmp->u.node.addr || mask[0] != tmp->u.node.mask)
+ continue;
+ } else {
+ if (memcmp(addr, tmp->u.node6.addr, 16) || memcmp(mask, tmp->u.node6.mask, 16))
+ continue;
+ }
+ *ocon = calloc(1, sizeof(qpol_nodecon_t));
+ if (*ocon == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ (*ocon)->protocol = protocol == QPOL_IPV4 ? QPOL_IPV4 : QPOL_IPV6;
+ (*ocon)->ocon = tmp;
+ }
+
+ if (*ocon == NULL) {
+ ERR(policy, "%s", "could not find nodecon statement for node");
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct node_state
+{
+ ocon_state_t *v4state;
+ ocon_state_t *v6state;
+} node_state_t;
+
+static void node_state_free(void *ns)
+{
+ node_state_t *ins = (node_state_t *) ns;
+
+ if (!ns)
+ return;
+
+ free(ins->v4state);
+ free(ins->v6state);
+ free(ns);
+}
+
+static int node_state_end(const qpol_iterator_t * iter)
+{
+ node_state_t *ns = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ ns = (node_state_t *) qpol_iterator_state(iter);
+
+ return (ns->v4state->cur == NULL && ns->v6state->cur == NULL);
+}
+
+static void *node_state_get_cur(const qpol_iterator_t * iter)
+{
+ node_state_t *ns = NULL;
+ qpol_nodecon_t *node = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL || node_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ns = (node_state_t *) qpol_iterator_state(iter);
+
+ node = calloc(1, sizeof(qpol_nodecon_t));
+ if (!node) {
+ return NULL;
+ }
+
+ node->ocon = ns->v4state->cur ? ns->v4state->cur : ns->v6state->cur;
+ node->protocol = ns->v4state->cur ? QPOL_IPV4 : QPOL_IPV6;
+
+ return node;
+}
+
+static size_t node_state_size(const qpol_iterator_t * iter)
+{
+ node_state_t *ns = NULL;
+ size_t count = 0;
+ ocontext_t *ocon = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ ns = (node_state_t *) qpol_iterator_state(iter);
+
+ if (ns->v4state)
+ for (ocon = ns->v4state->head; ocon; ocon = ocon->next)
+ count++;
+
+ if (ns->v6state)
+ for (ocon = ns->v6state->head; ocon; ocon = ocon->next)
+ count++;
+
+ return count;
+}
+
+static int node_state_next(qpol_iterator_t * iter)
+{
+ node_state_t *ns = NULL;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ ns = (node_state_t *) qpol_iterator_state(iter);
+
+ if (ns->v4state->cur == NULL && ns->v6state->cur == NULL) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ if (ns->v4state->cur)
+ ns->v4state->cur = ns->v4state->cur->next;
+ else
+ ns->v6state->cur = ns->v6state->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_nodecon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ int error = 0;
+ ocon_state_t *v4os = NULL, *v6os = NULL;
+ node_state_t *ns = NULL;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ v4os = calloc(1, sizeof(ocon_state_t));
+ if (v4os == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ v4os->head = v4os->cur = db->ocontexts[OCON_NODE];
+
+ v6os = calloc(1, sizeof(ocon_state_t));
+ if (v6os == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ free(v4os);
+ errno = error;
+ return STATUS_ERR;
+ }
+ v6os->head = v6os->cur = db->ocontexts[OCON_NODE6];
+
+ ns = calloc(1, sizeof(node_state_t));
+ if (ns == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ free(v4os);
+ free(v6os);
+ errno = error;
+ return STATUS_ERR;
+ }
+ ns->v4state = v4os;
+ ns->v6state = v6os;
+
+ if (qpol_iterator_create(policy, (void *)ns, node_state_get_cur,
+ node_state_next, node_state_end, node_state_size, node_state_free, iter)) {
+ node_state_free(ns);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_nodecon_get_addr(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, uint32_t ** addr, unsigned char *protocol)
+{
+ if (addr != NULL)
+ *addr = NULL;
+ if (protocol != NULL)
+ *protocol = 0;
+
+ if (policy == NULL || ocon == NULL || addr == NULL || protocol == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *protocol = ocon->protocol;
+
+ if (ocon->protocol == QPOL_IPV4) {
+ *addr = &(ocon->ocon->u.node.addr);
+ } else {
+ *addr = ocon->ocon->u.node6.addr;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_nodecon_get_mask(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, uint32_t ** mask, unsigned char *protocol)
+{
+ if (mask != NULL)
+ *mask = NULL;
+ if (protocol != NULL)
+ *protocol = 0;
+
+ if (policy == NULL || ocon == NULL || mask == NULL || protocol == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *protocol = ocon->protocol;
+
+ if (ocon->protocol == QPOL_IPV4) {
+ *mask = &(ocon->ocon->u.node.mask);
+ } else {
+ *mask = ocon->ocon->u.node6.mask;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_nodecon_get_protocol(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, unsigned char *protocol)
+{
+ if (protocol != NULL)
+ *protocol = 0;
+
+ if (policy == NULL || ocon == NULL || protocol == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *protocol = ocon->protocol;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_nodecon_get_context(const qpol_policy_t * policy, const qpol_nodecon_t * ocon, const qpol_context_t ** context)
+{
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || ocon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *context = (qpol_context_t *) & (ocon->ocon->context[0]);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/permissive_query.c b/libqpol/src/permissive_query.c
new file mode 100644
index 0000000..df601c2
--- /dev/null
+++ b/libqpol/src/permissive_query.c
@@ -0,0 +1,95 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over the permissive types.
+*
+* @author Steve Lawrence slawrence@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/permissive_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+
+int qpol_permissive_get_name(const qpol_policy_t *policy, const qpol_permissive_t * datum, const char **name)
+{
+ type_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (type_datum_t *)datum;
+
+ *name = db->p_type_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_permissive_iter(const qpol_policy_t *policy, qpol_iterator_t **iter)
+{
+ int error = 0;
+ policydb_t *db;
+ ebitmap_state_t *state = NULL;
+
+ if (iter) {
+ *iter = NULL;
+ }
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ state = calloc(1, sizeof(ebitmap_state_t));
+ if (state == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ state->bmap = &(db->permissive_map);
+ state->cur = state->bmap->node ? state->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, state, ebitmap_state_get_cur_permissive,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, iter)) {
+ free(state);
+ return STATUS_ERR;
+ }
+
+ if (state->bmap->node && !ebitmap_get_bit(state->bmap, state->cur))
+ ebitmap_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/polcap_query.c b/libqpol/src/polcap_query.c
new file mode 100644
index 0000000..019536f
--- /dev/null
+++ b/libqpol/src/polcap_query.c
@@ -0,0 +1,92 @@
+/**
+* @file
+* Defines the public interface for searching and iterating over the policy capabilities.
+*
+* @author Steve Lawrence slawrence@tresys.com
+*
+* Copyright (C) 2006-2007 Tresys Technology, LLC
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/polcap_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+
+int qpol_polcap_get_name(const qpol_policy_t *policy, const qpol_polcap_t * datum, const char **name)
+{
+ char *internal_datum = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (char *) datum;
+ *name = internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_polcap_iter(const qpol_policy_t *policy, qpol_iterator_t **iter)
+{
+ int error = 0;
+ policydb_t *db;
+ ebitmap_state_t *state = NULL;
+
+ if (iter) {
+ *iter = NULL;
+ }
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ state = calloc(1, sizeof(ebitmap_state_t));
+ if (state == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ state->bmap = &(db->policycaps);
+ state->cur = state->bmap->node ? state->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, state, ebitmap_state_get_cur_polcap,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, iter)) {
+ free(state);
+ return STATUS_ERR;
+ }
+
+ if (state->bmap->node && !ebitmap_get_bit(state->bmap, state->cur))
+ ebitmap_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/policy.c b/libqpol/src/policy.c
new file mode 100644
index 0000000..7180556
--- /dev/null
+++ b/libqpol/src/policy.c
@@ -0,0 +1,1563 @@
+/**
+ * @file
+ * Defines the public interface the QPol policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "qpol_internal.h"
+#include <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <asm/types.h>
+
+#include <sepol/debug.h>
+#include <sepol/handle.h>
+#include <sepol/policydb/flask_types.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/conditional.h>
+#include <sepol/policydb.h>
+#include <sepol/module.h>
+#include <sepol/policydb/module.h>
+#include <sepol/policydb/avrule_block.h>
+
+#include <stdbool.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include "expand.h"
+#include "queue.h"
+#include "iterator_internal.h"
+
+/* redefine input so we can read from a string */
+/* borrowed from O'Reilly lex and yacc pg 157 */
+char *qpol_src_originalinput;
+char *qpol_src_input;
+char *qpol_src_inputptr; /* current position in qpol_src_input */
+char *qpol_src_inputlim; /* end of data */
+
+extern void init_scanner(void);
+extern int yyparse(void);
+extern void init_parser(int, int);
+extern queue_t id_queue;
+extern unsigned int policydb_errors;
+extern unsigned long policydb_lineno;
+extern char source_file[];
+extern policydb_t *policydbp;
+extern int mlspol;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#define le16_to_cpu(x) (x)
+#define cpu_to_le32(x) (x)
+#define le32_to_cpu(x) (x)
+#define cpu_to_le64(x) (x)
+#define le64_to_cpu(x) (x)
+#else
+#define cpu_to_le16(x) bswap_16(x)
+#define le16_to_cpu(x) bswap_16(x)
+#define cpu_to_le32(x) bswap_32(x)
+#define le32_to_cpu(x) bswap_32(x)
+#define cpu_to_le64(x) bswap_64(x)
+#define le64_to_cpu(x) bswap_64(x)
+#endif
+
+/* buffer for reading from file */
+typedef struct fbuf
+{
+ char *buf;
+ size_t sz;
+ int err;
+} qpol_fbuf_t;
+
+static void qpol_handle_route_to_callback(void *varg
+ __attribute__ ((unused)), const qpol_policy_t * p, int level, const char *fmt,
+ va_list va_args)
+{
+ if (!p || !(p->fn)) {
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ p->fn(p->varg, p, level, fmt, va_args);
+}
+
+static void sepol_handle_route_to_callback(void *varg, sepol_handle_t * sh, const char *fmt, ...)
+{
+ va_list ap;
+ qpol_policy_t *p = varg;
+
+ if (!sh) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ va_start(ap, fmt);
+ qpol_handle_route_to_callback(NULL, p, sepol_msg_get_level(sh), fmt, ap);
+ va_end(ap);
+}
+
+void qpol_handle_msg(const qpol_policy_t * p, int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!p) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ va_start(ap, fmt);
+ /* explicit cast here to remove const for sepol handle */
+ qpol_handle_route_to_callback((void *)p->varg, p, level, fmt, ap);
+ va_end(ap);
+}
+
+static void qpol_handle_default_callback(void *varg __attribute__ ((unused)), const qpol_policy_t * p
+ __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case QPOL_MSG_INFO:
+ {
+ /* by default ignore info messages */
+ return;
+ }
+ case QPOL_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case QPOL_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+static int read_source_policy(qpol_policy_t * qpolicy, char *progname, int options)
+{
+ int load_rules = 1;
+ if (options & QPOL_POLICY_OPTION_NO_RULES)
+ load_rules = 0;
+ if ((id_queue = queue_create()) == NULL) {
+ ERR(qpolicy, "%s", strerror(ENOMEM));
+ return -1;
+ }
+
+ policydbp = &qpolicy->p->p;
+ mlspol = policydbp->mls;
+
+ INFO(qpolicy, "%s", "Parsing policy. (Step 1 of 5)");
+ init_scanner();
+ init_parser(1, load_rules);
+ errno = 0;
+ if (yyparse() || policydb_errors) {
+ ERR(qpolicy, "%s: error(s) encountered while parsing configuration\n", progname);
+ queue_destroy(id_queue);
+ id_queue = NULL;
+// errno = EIO;
+ return -1;
+ }
+ /* rewind the pointer */
+ qpol_src_inputptr = qpol_src_originalinput;
+ init_parser(2, load_rules);
+ source_file[0] = '\0';
+ if (yyparse() || policydb_errors) {
+ ERR(qpolicy, "%s: error(s) encountered while parsing configuration\n", progname);
+ queue_destroy(id_queue);
+ id_queue = NULL;
+// errno = EIO;
+ return -1;
+ }
+ queue_destroy(id_queue);
+ id_queue = NULL;
+ if (policydb_errors) {
+// errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+static int qpol_init_fbuf(qpol_fbuf_t ** fb)
+{
+ if (fb == NULL)
+ return -1;
+ *fb = (qpol_fbuf_t *) malloc(sizeof(qpol_fbuf_t));
+ if (*fb == NULL)
+ return -1;
+ (*fb)->buf = NULL;
+ (*fb)->sz = 0;
+ (*fb)->err = 0;
+ return 0;
+}
+
+static void qpol_free_fbuf(qpol_fbuf_t ** fb)
+{
+ if (*fb == NULL)
+ return;
+ if ((*fb)->sz > 0 && (*fb)->buf != NULL)
+ free((*fb)->buf);
+ free(*fb);
+ return;
+}
+
+static void *qpol_read_fbuf(qpol_fbuf_t * fb, size_t bytes, FILE * fp)
+{
+ size_t sz;
+
+ assert(fb != NULL && fp != NULL);
+ assert(!(fb->sz > 0 && fb->buf == NULL));
+
+ if (fb->sz == 0) {
+ fb->buf = (char *)malloc(bytes + 1);
+ fb->sz = bytes + 1;
+ } else if (bytes + 1 > fb->sz) {
+ fb->buf = (char *)realloc(fb->buf, bytes + 1);
+ fb->sz = bytes + 1;
+ }
+
+ if (fb->buf == NULL) {
+ fb->err = -1;
+ return NULL;
+ }
+
+ sz = fread(fb->buf, bytes, 1, fp);
+ if (sz != 1) {
+ fb->err = -3;
+ return NULL;
+ }
+ fb->err = 0;
+ return fb->buf;
+}
+
+int qpol_binpol_version(FILE * fp)
+{
+ __u32 *buf;
+ int rt, len;
+ qpol_fbuf_t *fb;
+
+ if (fp == NULL)
+ return -1;
+
+ if (qpol_init_fbuf(&fb) != 0)
+ return -1;
+
+ /* magic # and sz of policy string */
+ buf = qpol_read_fbuf(fb, sizeof(__u32) * 2, fp);
+ if (buf == NULL) {
+ rt = fb->err;
+ goto err_return;
+ }
+ buf[0] = le32_to_cpu(buf[0]);
+ if (buf[0] != SELINUX_MAGIC) {
+ rt = -2;
+ goto err_return;
+ }
+
+ len = le32_to_cpu(buf[1]);
+ if (len < 0) {
+ rt = -3;
+ goto err_return;
+ }
+ /* skip over the policy string */
+ if (fseek(fp, sizeof(char) * len, SEEK_CUR) != 0) {
+ rt = -3;
+ goto err_return;
+ }
+
+ /* Read the version, config, and table sizes. */
+ buf = qpol_read_fbuf(fb, sizeof(__u32) * 1, fp);
+ if (buf == NULL) {
+ rt = fb->err;
+ goto err_return;
+ }
+ buf[0] = le32_to_cpu(buf[0]);
+
+ rt = buf[0];
+ err_return:
+ rewind(fp);
+ qpol_free_fbuf(&fb);
+ return rt;
+}
+
+int qpol_is_file_binpol(FILE * fp)
+{
+ int rt;
+ size_t sz;
+ __u32 ubuf;
+
+ sz = fread(&ubuf, sizeof(__u32), 1, fp);
+ if (sz != 1)
+ rt = 0;
+
+ ubuf = le32_to_cpu(ubuf);
+ if (ubuf == SELINUX_MAGIC)
+ rt = 1;
+ else
+ rt = 0;
+ rewind(fp);
+ return rt;
+}
+
+int qpol_is_data_mod_pkg(char * data)
+{
+ size_t sz;
+ __u32 ubuf;
+
+ memcpy(&ubuf, data, sizeof(__u32));
+
+ ubuf = le32_to_cpu(ubuf);
+ if (ubuf == SEPOL_MODULE_PACKAGE_MAGIC)
+ return 1;
+
+ return 0;
+}
+
+int qpol_is_file_mod_pkg(FILE * fp)
+{
+ size_t sz;
+ __u32 ubuf;
+ int rt;
+
+ sz = fread(&ubuf, sizeof(__u32), 1, fp);
+
+ if (sz != 1)
+ rt = 0; /* problem reading file */
+
+ ubuf = le32_to_cpu(ubuf);
+ if (ubuf == SEPOL_MODULE_PACKAGE_MAGIC)
+ rt = 1;
+ else
+ rt = 0;
+ rewind(fp);
+ return rt;
+}
+
+static int infer_policy_version(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ const qpol_class_t *obj_class = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_fs_use_t *fsuse = NULL;
+ qpol_range_trans_t *rangetrans = NULL;
+ uint32_t behavior = 0;
+ size_t nvtrans = 0, fsusexattr = 0;
+ const char *obj_name = NULL;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ if (db->policyvers) {
+ /* version already set */
+ return STATUS_SUCCESS;
+ }
+
+ /* check fs_use for xattr and psid */
+ qpol_policy_get_fs_use_iter(policy, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&fsuse);
+ qpol_fs_use_get_behavior(policy, fsuse, &behavior);
+ /* not possible to have xattr and psid in same policy */
+ if (behavior == QPOL_FS_USE_XATTR) {
+ fsusexattr = 1;
+ break;
+ } else if (behavior == QPOL_FS_USE_PSID) {
+ qpol_iterator_destroy(&iter);
+ db->policyvers = 12;
+ return STATUS_SUCCESS;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+#if defined(HAVE_SEPOL_PERMISSIVE_TYPES) || defined(HAVE_SEPOL_POLICYCAPS)
+ ebitmap_node_t *node = NULL;
+ unsigned int i = 0;
+#endif
+
+ /* 23 : there exists at least one type that is permissive */
+#ifdef HAVE_SEPOL_PERMISSIVE_TYPES
+ ebitmap_for_each_bit(&db->permissive_map, node, i) {
+ if (ebitmap_get_bit(&db->permissive_map, i)) {
+ db->policyvers = 23;
+ return STATUS_SUCCESS;
+ }
+ }
+#endif
+
+ /* 22 : there exists at least one policy capability */
+#ifdef HAVE_SEPOL_POLICYCAPS
+ ebitmap_for_each_bit(&db->policycaps, node, i) {
+ if (ebitmap_get_bit(&db->policycaps, i)) {
+ db->policyvers = 22;
+ return STATUS_SUCCESS;
+ }
+ }
+#endif
+
+ /* 21 : object classes other than process for range_transitions */
+ qpol_policy_get_range_trans_iter(policy, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&rangetrans);
+ qpol_range_trans_get_target_class(policy, rangetrans, &obj_class);
+ qpol_class_get_name(policy, obj_class, &obj_name);
+ if (strcmp(obj_name, "process")) {
+ db->policyvers = 21;
+ qpol_iterator_destroy(&iter);
+ return STATUS_SUCCESS;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ /* 19 & 20 : mls and validatetrans statements added */
+ qpol_policy_get_validatetrans_iter(policy, &iter);
+ qpol_iterator_get_size(iter, &nvtrans);
+ qpol_iterator_destroy(&iter);
+ if (db->mls || nvtrans) {
+ db->policyvers = 19;
+ }
+
+ /* 18 : the netlink_audit_socket class added */
+ else if (hashtab_search(db->p_classes.table, (const hashtab_key_t)"netlink_audit_socket")) {
+ db->policyvers = 18;
+ }
+
+ /* 17 : IPv6 nodecon statements added */
+ else if (db->ocontexts[OCON_NODE6]) {
+ db->policyvers = 17;
+ }
+
+ /* 16 : conditional policy added */
+ else if (db->p_bool_val_to_name && db->p_bool_val_to_name[0]) {
+ db->policyvers = 16;
+
+ }
+ /* 15 */
+ else if (fsusexattr) {
+ db->policyvers = 15;
+ }
+
+ /* 12 */
+ else {
+ db->policyvers = 12;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/** State tracking struct used in the functions check_disabled, remove_symbol, and prune_disabled_symbols to handle disabled symbols */
+struct symbol_pruning_state
+{
+ qpol_policy_t *p; /**< The policy */
+ int symbol_type; /**< The current symbol type being processed */
+};
+
+/** Apply callback for hashtab_map_remove_on_error.
+ * This function tests whether a symbol referenced by the policy is declared or only ever required.
+ * Symbols without a declaration are disabled and must be removed.
+ * @param key Symbol key to check.
+ * @param datum Symbol datum to check.
+ * @param args State object (of type struct symbol_pruning_state)
+ * @return 0 if symbol is enabled, 1 if not enabled.
+ */
+static int check_disabled(hashtab_key_t key, hashtab_datum_t datum, void *args)
+{
+ struct symbol_pruning_state *s = args;
+ if (!is_id_enabled((char *)key, &(s->p->p->p), s->symbol_type))
+ return 1;
+ return 0;
+}
+
+/** Remove callback for hashtab_map_remove_on_error.
+ * Frees all memory associated with a disabled symbol that has been removed from the symbol table.
+ * @param key Symbol key to remove
+ * @param datum Symbol datum to remove
+ * @param args State object (of type struct symbol_pruning_state)
+ * @post All memory associated with the symbol is freed.
+ */
+static void remove_symbol(hashtab_key_t key, hashtab_datum_t datum, void *args)
+{
+ struct symbol_pruning_state *s = args;
+ switch (s->symbol_type) {
+ case SYM_ROLES:
+ {
+ role_datum_destroy((role_datum_t *) datum);
+ break;
+ }
+ case SYM_TYPES:
+ {
+ type_datum_destroy((type_datum_t *) datum);
+ break;
+ }
+ case SYM_USERS:
+ {
+ user_datum_destroy((user_datum_t *) datum);
+ break;
+ }
+ case SYM_BOOLS:
+ {
+ /* no-op */
+ break;
+ }
+ case SYM_LEVELS:
+ {
+ level_datum_destroy((level_datum_t *) datum);
+ break;
+ }
+ case SYM_CATS:
+ {
+ cat_datum_destroy((cat_datum_t *) datum);
+ break;
+ }
+ default:
+ return; /* invalid type of datum to free; do nothing */
+ }
+ free(key);
+ free(datum);
+}
+
+/** Remove symbols that are only required but never declared from the policy.
+ * Removes each disabled symbol freeing all memory associated with it.
+ * @param policy The policy from which disabled symbols should be removed.
+ * @return always 0.
+ * @note Since hashtab_map_remove_on_error does not return any error status,
+ * it is impossible to tell if it has failed; if it fails, the policy will
+ * be in an inconsistent state.
+ */
+static int prune_disabled_symbols(qpol_policy_t * policy)
+{
+ if (policy->type == QPOL_POLICY_KERNEL_BINARY)
+ return 0; /* checkpolicy already prunes disabled symbols */
+ struct symbol_pruning_state state;
+ state.p = policy;
+ for (state.symbol_type = SYM_ROLES; state.symbol_type < SYM_NUM; state.symbol_type++) {
+ hashtab_map_remove_on_error(policy->p->p.symtab[state.symbol_type].table, check_disabled, remove_symbol, &state);
+ }
+ return 0;
+}
+
+/** For all symbols that are multiply defined (such as attributes, roles, and users),
+ * union the relevant sets of types and roles from each declaration.
+ * @param policy The policy containig the symbols to union.
+ * @return 0 on success, non-zero on error; if the call fails,
+ * errno will be set, and the policy should be considered invalid.
+ */
+static int union_multiply_declared_symbols(qpol_policy_t * policy) {
+ /* general structure of this function:
+ walk role and user symbol tables for each role/user/attribute
+ get datum from symtab, get key from array
+ look up symbol in scope table
+ foreach decl_id in scope entry
+ union types/roles bitmap with datum's copy
+ */
+ qpol_iterator_t * iter = NULL;
+ int error = 0;
+ if (qpol_policy_get_type_iter(policy, &iter)) {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ type_datum_t *attr;
+ if (qpol_iterator_get_item(iter, (void**)&attr)) {
+ error = errno;
+ goto err;
+ }
+ unsigned char isattr = 0;
+ if (qpol_type_get_isattr(policy, attr, &isattr)) {
+ error = errno;
+ goto err;
+ }
+ if (!isattr)
+ continue;
+ const char *name;
+ if (qpol_type_get_name(policy, (qpol_type_t*)attr, &name)) {
+ error = errno;
+ goto err;
+ }
+ policydb_t *db = &policy->p->p;
+ avrule_block_t *blk = db->global;
+ for (; blk; blk = blk->next) {
+ avrule_decl_t *decl = blk->enabled;
+ if (!decl)
+ continue; /* disabled */
+ type_datum_t *internal_datum = hashtab_search(decl->symtab[SYM_TYPES].table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ continue; /* not declared here */
+ }
+ if (ebitmap_union(&attr->types, &internal_datum->types))
+ {
+ error = errno;
+ ERR(policy, "could not merge declarations for attribute %s", name);
+ goto err;
+ }
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ /* repeat for roles */
+ if (qpol_policy_get_role_iter(policy, &iter)) {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ role_datum_t *role;
+ if (qpol_iterator_get_item(iter, (void**)&role)) {
+ error = errno;
+ goto err;
+ }
+ const char *name;
+ if (qpol_role_get_name(policy, (qpol_role_t*)role, &name)) {
+ error = errno;
+ goto err;
+ }
+ policydb_t *db = &policy->p->p;
+ scope_datum_t* scope_datum = hashtab_search(db->scope[SYM_ROLES].table, (const hashtab_key_t)name);
+ if (scope_datum == NULL) {
+ ERR(policy, "could not find scope datum for role %s", name);
+ error = ENOENT;
+ goto err;
+ }
+ for (uint32_t i = 0; i < scope_datum->decl_ids_len; i++)
+ {
+ if (db->decl_val_to_struct[scope_datum->decl_ids[i] - 1]->enabled == 0)
+ continue; /* block is disabled */
+ role_datum_t *internal_datum = hashtab_search(db->decl_val_to_struct[scope_datum->decl_ids[i] - 1]->symtab[SYM_ROLES].table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ continue; /* not declared here */
+ }
+ if (ebitmap_union(&role->types.types, &internal_datum->types.types) || ebitmap_union(&role->dominates, &internal_datum->dominates))
+ {
+ error = errno;
+ ERR(policy, "could not merge declarations for role %s", name);
+ goto err;
+ }
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ /* repeat for users */
+ if (qpol_policy_get_user_iter(policy, &iter)) {
+ return 1;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ user_datum_t *user;
+ if (qpol_iterator_get_item(iter, (void**)&user)) {
+ error = errno;
+ goto err;
+ }
+ const char *name;
+ if (qpol_user_get_name(policy, (qpol_user_t*)user, &name)) {
+ error = errno;
+ goto err;
+ }
+ policydb_t *db = &policy->p->p;
+ scope_datum_t* scope_datum = hashtab_search(db->scope[SYM_USERS].table, (const hashtab_key_t)name);
+ if (scope_datum == NULL) {
+ ERR(policy, "could not find scope datum for user %s", name);
+ error = ENOENT;
+ goto err;
+ }
+ for (uint32_t i = 0; i < scope_datum->decl_ids_len; i++)
+ {
+ if (db->decl_val_to_struct[scope_datum->decl_ids[i] - 1]->enabled == 0)
+ continue; /* block is disabled */
+ user_datum_t *internal_datum = hashtab_search(db->decl_val_to_struct[scope_datum->decl_ids[i] -1 ]->symtab[SYM_USERS].table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ continue; /* not declared here */
+ }
+ if (ebitmap_union(&user->roles.roles, &internal_datum->roles.roles))
+ {
+ error = errno;
+ ERR(policy, "could not merge declarations for user %s", name);
+ goto err;
+ }
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ return 0;
+err:
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return 1;
+}
+
+/* forward declarations see policy_extend.c */
+struct qpol_extended_image;
+extern void qpol_extended_image_destroy(struct qpol_extended_image **ext);
+
+#if LINK_SHARED == 1
+__asm__(".symver qpol_policy_open_from_file_old,qpol_policy_open_from_file@");
+__asm__(".symver qpol_policy_open_from_file_opt,qpol_policy_open_from_file@@VERS_1.3");
+__asm__(".symver qpol_policy_open_from_memory_old,qpol_policy_open_from_memory@");
+__asm__(".symver qpol_policy_open_from_memory_opt,qpol_policy_open_from_memory@VERS_1.3");
+__asm__(".symver qpol_policy_rebuild_old,qpol_policy_rebuild@");
+__asm__(".symver qpol_policy_rebuild_opt,qpol_policy_rebuild@@VERS_1.3");
+#endif
+
+/**
+ * @brief Internal version of qpol_policy_rebuild() version 1.3
+ *
+ * Implementation of the exported function qpol_policy_rebuild()
+ * for version 1.3; this symbol name is not exported.
+ * @see qpol_policy_rebuild()
+ */
+int qpol_policy_rebuild_opt(qpol_policy_t * policy, const int options)
+{
+ sepol_policydb_t *old_p = NULL;
+ sepol_policydb_t **modules = NULL;
+ qpol_module_t *base = NULL;
+ size_t num_modules = 0, i;
+ int error = 0, old_options;
+
+ if (!policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ /* if kernel binary do nothing */
+ if (policy->type == QPOL_POLICY_KERNEL_BINARY)
+ return STATUS_SUCCESS;
+
+ /* if options are the same and the modules were not modified, do nothing */
+ if (options == policy->options && policy->modified == 0)
+ return STATUS_SUCCESS;
+
+ /* cache old policy in case of failure */
+ old_p = policy->p;
+ policy->p = NULL;
+ struct qpol_extended_image *ext = policy->ext;
+ policy->ext = NULL;
+ old_options = policy->options;
+ policy->options = options;
+
+ /* QPOL_POLICY_OPTION_NO_RULES implies QPOL_POLICY_OPTION_NO_NEVERALLOWS */
+ if (policy->options & QPOL_POLICY_OPTION_NO_RULES)
+ policy->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ if (policy->type == QPOL_POLICY_MODULE_BINARY) {
+ /* allocate enough space for all modules then fill with list of enabled ones only */
+ if (!(modules = calloc(policy->num_modules, sizeof(sepol_policydb_t *)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ /* first module is base and cannot be disabled */
+ for (i = 1; i < policy->num_modules; i++) {
+ if ((policy->modules[i])->enabled) {
+ modules[num_modules++] = (policy->modules[i])->p;
+ }
+ }
+ /* have to reopen the base since link alters it */
+ if (qpol_module_create_from_file((policy->modules[0])->path, &base)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ /* take the policy from base and use as new base into which to link */
+ policy->p = base->p;
+ base->p = NULL;
+ qpol_module_destroy(&base);
+ if (sepol_link_modules(policy->sh, policy->p, modules, num_modules, 0)) {
+ error = EIO;
+ goto err;
+ }
+ free(modules);
+ } else {
+ /* repeat open process as if qpol_policy_open_from_memory() */
+ if (sepol_policydb_create(&(policy->p))) {
+ error = errno;
+ goto err;
+ }
+
+ qpol_src_input = policy->file_data;
+ qpol_src_inputptr = qpol_src_input;
+ qpol_src_inputlim = qpol_src_inputptr + policy->file_data_sz - 1;
+ qpol_src_originalinput = qpol_src_input;
+
+ /* read in source */
+ policy->p->p.policy_type = POLICY_BASE;
+ if (read_source_policy(policy, "parse", policy->options) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ /* link the source */
+ INFO(policy, "%s", "Linking source policy. (Step 2 of 5)");
+ if (sepol_link_modules(policy->sh, policy->p, NULL, 0, 0)) {
+ error = EIO;
+ goto err;
+ }
+ avtab_destroy(&(policy->p->p.te_avtab));
+ avtab_destroy(&(policy->p->p.te_cond_avtab));
+ avtab_init(&(policy->p->p.te_avtab));
+ avtab_init(&(policy->p->p.te_cond_avtab));
+ }
+
+ if (prune_disabled_symbols(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (union_multiply_declared_symbols(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (qpol_expand_module(policy, !(policy->options & (QPOL_POLICY_OPTION_NO_NEVERALLOWS)))) {
+ error = errno;
+ goto err;
+ }
+
+ if (infer_policy_version(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (policy_extend(policy)) {
+ error = errno;
+ goto err;
+ }
+ qpol_extended_image_destroy(&ext);
+
+ sepol_policydb_free(old_p);
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(modules);
+
+ policy->p = old_p;
+ policy->ext = ext;
+ policy->options = old_options;
+ errno = error;
+ return STATUS_ERR;
+}
+
+#if LINK_SHARED == 0
+int qpol_policy_rebuild(qpol_policy_t * policy, int options)
+{
+ return qpol_policy_rebuild_opt(policy, options);
+}
+#endif
+
+/**
+ * @brief Internal version of qpol_policy_rebuild() version 1.2 or earlier
+ * @deprecated use the 1.3 version.
+ * @see qpol_policy_rebuild()
+ */
+int qpol_policy_rebuild_old(qpol_policy_t * policy)
+{
+ if (!policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ /* fail if not a modular policy */
+ if (policy->type != QPOL_POLICY_MODULE_BINARY) {
+ ERR(policy, "%s", strerror(ENOTSUP));
+ errno = ENOTSUP;
+ return STATUS_ERR;
+ }
+
+ if (!policy->modified)
+ return STATUS_SUCCESS;
+
+ return qpol_policy_rebuild_opt(policy, policy->options);
+}
+
+/**
+ * @brief Internal version of qpol_policy_open_from_file() version 1.3
+ *
+ * Implementation of the exported function qpol_policy_open_from_file()
+ * for version 1.3; this symbol name is not exported.
+ * @see qpol_policy_open_from_file()
+ */
+int qpol_policy_open_from_file_opt(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg, const int options)
+{
+ int error = 0, retv = -1;
+ FILE *infile = NULL;
+ sepol_policy_file_t *pfile = NULL;
+ qpol_module_t *mod = NULL;
+ int fd = 0;
+ struct stat sb;
+
+ if (policy != NULL)
+ *policy = NULL;
+
+ if (path == NULL || policy == NULL) {
+ /* handle passed as NULL here as it has yet to be created */
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ errno = 0;
+ if (!(*policy = calloc(1, sizeof(qpol_policy_t)))) {
+ error = errno;
+ ERR(NULL, "%s", strerror(error));
+ goto err;
+ }
+ (*policy)->options = options;
+
+ /* QPOL_POLICY_OPTION_NO_RULES implies QPOL_POLICY_OPTION_NO_NEVERALLOWS */
+ if ((*policy)->options & QPOL_POLICY_OPTION_NO_RULES)
+ (*policy)->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ (*policy)->sh = sepol_handle_create();
+ if ((*policy)->sh == NULL) {
+ error = errno;
+ ERR(*policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ if (fn) {
+ (*policy)->fn = fn;
+ (*policy)->varg = varg;
+ } else {
+ (*policy)->fn = qpol_handle_default_callback;
+ }
+ sepol_msg_set_callback((*policy)->sh, sepol_handle_route_to_callback, (*policy));
+
+ if (sepol_policydb_create(&((*policy)->p))) {
+ error = errno;
+ goto err;
+ }
+
+ if (sepol_policy_file_create(&pfile)) {
+ error = errno;
+ goto err;
+ }
+
+ infile = fopen(path, "rb");
+ if (infile == NULL) {
+ error = errno;
+ goto err;
+ }
+
+ sepol_policy_file_set_handle(pfile, (*policy)->sh);
+
+ errno=0;
+ if (qpol_is_file_binpol(infile)) {
+ (*policy)->type = retv = QPOL_POLICY_KERNEL_BINARY;
+ sepol_policy_file_set_fp(pfile, infile);
+ if (sepol_policydb_read((*policy)->p, pfile)) {
+// error = EIO;
+ goto err;
+ }
+ /* By definition, binary policy cannot have neverallow rules and all other rules are always loaded. */
+ (*policy)->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+ (*policy)->options &= ~(QPOL_POLICY_OPTION_NO_RULES);
+ if (policy_extend(*policy)) {
+ error = errno;
+ goto err;
+ }
+ } else if (qpol_module_create_from_file(path, &mod) == STATUS_SUCCESS) {
+ (*policy)->type = retv = QPOL_POLICY_MODULE_BINARY;
+
+ if (qpol_policy_append_module(*policy, mod)) {
+ error = errno;
+ goto err;
+ }
+ /* *policy now owns mod */
+ mod = NULL;
+ if (qpol_policy_rebuild_opt(*policy, options)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ (*policy)->type = retv = QPOL_POLICY_KERNEL_SOURCE;
+ fd = fileno(infile);
+ if (fd < 0) {
+ error = errno;
+ goto err;
+ }
+ if (fstat(fd, &sb) < 0) {
+ error = errno;
+ ERR(*policy, "Can't stat '%s': %s\n", path, strerror(errno));
+ goto err;
+ }
+ qpol_src_input = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (qpol_src_input == MAP_FAILED) {
+ error = errno;
+ ERR(*policy, "Can't map '%s': %s\n", path, strerror(errno));
+
+ goto err;
+ }
+ qpol_src_inputptr = qpol_src_input;
+ qpol_src_inputlim = &qpol_src_inputptr[sb.st_size - 1];
+ qpol_src_originalinput = qpol_src_input;
+
+ /* store mmaped version for rebuild() */
+ (*policy)->file_data = qpol_src_originalinput;
+ (*policy)->file_data_sz = sb.st_size;
+ (*policy)->file_data_type = QPOL_POLICY_FILE_DATA_TYPE_MMAP;
+
+ (*policy)->p->p.policy_type = POLICY_BASE;
+ if (read_source_policy(*policy, "libqpol", (*policy)->options) < 0) {
+ error = errno;
+ goto err;
+ }
+
+ /* link the source */
+ INFO(*policy, "%s", "Linking source policy. (Step 2 of 5)");
+ if (sepol_link_modules((*policy)->sh, (*policy)->p, NULL, 0, 0)) {
+ error = EIO;
+ goto err;
+ }
+ avtab_destroy(&((*policy)->p->p.te_avtab));
+ avtab_destroy(&((*policy)->p->p.te_cond_avtab));
+ avtab_init(&((*policy)->p->p.te_avtab));
+ avtab_init(&((*policy)->p->p.te_cond_avtab));
+
+ if (prune_disabled_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (union_multiply_declared_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ /* expand */
+ if (qpol_expand_module(*policy, !(options & (QPOL_POLICY_OPTION_NO_NEVERALLOWS)))) {
+ error = errno;
+ goto err;
+ }
+
+ if (infer_policy_version(*policy)) {
+ error = errno;
+ goto err;
+ }
+ if (policy_extend(*policy)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ fclose(infile);
+ sepol_policy_file_free(pfile);
+ return retv;
+
+ err:
+ qpol_policy_destroy(policy);
+ qpol_module_destroy(&mod);
+ sepol_policy_file_free(pfile);
+ if (infile)
+ fclose(infile);
+ errno = error;
+ return -1;
+}
+
+#if LINK_SHARED == 0
+int qpol_policy_open_from_file(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg, const int options)
+{
+ return qpol_policy_open_from_file_opt(path, policy, fn, varg, options);
+}
+#endif
+
+int qpol_policy_open_from_file_no_rules(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg)
+{
+ return qpol_policy_open_from_file_opt(path, policy, fn, varg, QPOL_POLICY_OPTION_NO_RULES);
+}
+
+/**
+ * @brief Internal version of qpol_policy_open_from_memory() version 1.3
+ *
+ * Implementation of the exported function qpol_policy_open_from_memory()
+ * for version 1.3; this symbol name is not exported.
+ * @see qpol_policy_open_from_memory()
+ */
+int qpol_policy_open_from_memory_opt(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn, void *varg,
+ const int options)
+{
+ int error = 0;
+ if (policy == NULL || filedata == NULL)
+ return -1;
+ *policy = NULL;
+
+ if (!(*policy = calloc(1, sizeof(qpol_policy_t)))) {
+ error = errno;
+ goto err;
+ }
+ (*policy)->options = options;
+
+ /* QPOL_POLICY_OPTION_NO_RULES implies QPOL_POLICY_OPTION_NO_NEVERALLOWS */
+ if ((*policy)->options & QPOL_POLICY_OPTION_NO_RULES)
+ (*policy)->options |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ (*policy)->sh = sepol_handle_create();
+ if ((*policy)->sh == NULL) {
+ error = errno;
+ ERR(*policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ sepol_msg_set_callback((*policy)->sh, sepol_handle_route_to_callback, (*policy));
+ if (fn) {
+ (*policy)->fn = fn;
+ (*policy)->varg = varg;
+ } else {
+ (*policy)->fn = qpol_handle_default_callback;
+ }
+
+ if (sepol_policydb_create(&((*policy)->p))) {
+ error = errno;
+ goto err;
+ }
+
+ qpol_src_input = (char *)filedata;
+ qpol_src_inputptr = qpol_src_input;
+ qpol_src_inputlim = qpol_src_inputptr + size - 1;
+ qpol_src_originalinput = qpol_src_input;
+
+ /* store filedata for rebuild() */
+ if (!((*policy)->file_data = malloc(size))) {
+ error = errno;
+ goto err;
+ }
+ memcpy((*policy)->file_data, filedata, size);
+ (*policy)->file_data_sz = size;
+ (*policy)->file_data_type = QPOL_POLICY_FILE_DATA_TYPE_MEM;
+
+ /* read in source */
+ (*policy)->p->p.policy_type = POLICY_BASE;
+ if (read_source_policy(*policy, "parse", (*policy)->options) < 0)
+ exit(1);
+
+ /* link the source */
+ INFO(*policy, "%s", "Linking source policy. (Step 2 of 5)");
+ if (sepol_link_modules((*policy)->sh, (*policy)->p, NULL, 0, 0)) {
+ error = EIO;
+ goto err;
+ }
+ avtab_destroy(&((*policy)->p->p.te_avtab));
+ avtab_destroy(&((*policy)->p->p.te_cond_avtab));
+ avtab_init(&((*policy)->p->p.te_avtab));
+ avtab_init(&((*policy)->p->p.te_cond_avtab));
+
+ if (prune_disabled_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (union_multiply_declared_symbols(*policy)) {
+ error = errno;
+ goto err;
+ }
+
+ /* expand */
+ if (qpol_expand_module(*policy, !(options & (QPOL_POLICY_OPTION_NO_NEVERALLOWS)))) {
+ error = errno;
+ goto err;
+ }
+
+ return 0;
+ err:
+ qpol_policy_destroy(policy);
+ errno = error;
+ return -1;
+
+}
+
+#if LINK_SHARED == 0
+int qpol_policy_open_from_memory(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn, void *varg,
+ const int options)
+{
+ return qpol_policy_open_from_memory_opt(policy, filedata, size, fn, varg, options);
+}
+#endif
+
+/**
+ * @brief Internal version of qpol_policy_open_from_file() version 1.2 or earlier
+ * @deprecated use the 1.3 version.
+ * @see qpol_policy_open_from_file()
+ */
+int qpol_policy_open_from_file_old(const char *path, qpol_policy_t ** policy, qpol_callback_fn_t fn, void *varg)
+{
+ return qpol_policy_open_from_file(path, policy, fn, varg, 0);
+}
+
+/**
+ * @brief Internal version of qpol_policy_open_from_memory() version 1.2 or earlier
+ * @deprecated use the 1.3 version.
+ * @see qpol_policy_open_from_memory()
+ */
+int qpol_policy_open_from_memory_old(qpol_policy_t ** policy, const char *filedata, size_t size, qpol_callback_fn_t fn, void *varg)
+{
+ return qpol_policy_open_from_memory_opt(policy, filedata, size, fn, varg, 0);
+}
+
+void qpol_policy_destroy(qpol_policy_t ** policy)
+{
+ if (policy != NULL && *policy != NULL) {
+ sepol_policydb_free((*policy)->p);
+ sepol_handle_destroy((*policy)->sh);
+ qpol_extended_image_destroy(&((*policy)->ext));
+ if ((*policy)->modules) {
+ size_t i = 0;
+ for (i = 0; i < (*policy)->num_modules; i++) {
+ qpol_module_destroy(&((*policy)->modules[i]));
+ }
+ free((*policy)->modules);
+ }
+ if ((*policy)->file_data_type == QPOL_POLICY_FILE_DATA_TYPE_MEM) {
+ free((*policy)->file_data);
+ } else if ((*policy)->file_data_type == QPOL_POLICY_FILE_DATA_TYPE_MMAP) {
+ munmap((*policy)->file_data, (*policy)->file_data_sz);
+ }
+ free(*policy);
+ *policy = NULL;
+ }
+}
+
+int qpol_policy_reevaluate_conds(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ cond_node_t *cond = NULL;
+ cond_av_list_t *list_ptr = NULL;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ for (cond = db->cond_list; cond; cond = cond->next) {
+ /* evaluate cond */
+ cond->cur_state = cond_evaluate_expr(db, cond->expr);
+ if (cond->cur_state < 0) {
+ ERR(policy, "Error evaluating conditional: %s", strerror(EILSEQ));
+ errno = EILSEQ;
+ return STATUS_ERR;
+ }
+
+ /* walk true list */
+ for (list_ptr = cond->true_list; list_ptr; list_ptr = list_ptr->next) {
+ /* field not used (except by write),
+ * now storing list and enabled flags */
+ if (cond->cur_state)
+ list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
+ else
+ list_ptr->node->merged &= ~(QPOL_COND_RULE_ENABLED);
+ }
+
+ /* walk false list */
+ for (list_ptr = cond->false_list; list_ptr; list_ptr = list_ptr->next) {
+ /* field not used (except by write),
+ * now storing list and enabled flags */
+ if (!cond->cur_state)
+ list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
+ else
+ list_ptr->node->merged &= ~(QPOL_COND_RULE_ENABLED);
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_append_module(qpol_policy_t * policy, qpol_module_t * module)
+{
+ qpol_module_t **tmp = NULL;
+ int error = 0;
+
+ if (!policy || !module) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(tmp = realloc(policy->modules, (1 + policy->num_modules) * sizeof(qpol_module_t *)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ policy->modules = tmp;
+ policy->modules[policy->num_modules] = module;
+ policy->num_modules++;
+ policy->modified = 1;
+ module->parent = policy;
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct mod_state
+{
+ qpol_module_t **list;
+ size_t cur;
+ size_t end;
+} mod_state_t;
+
+static int mod_state_end(const qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ return (ms->cur >= ms->end);
+}
+
+static void *mod_state_get_cur(const qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return ms->list[ms->cur];
+}
+
+static int mod_state_next(qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ ms->cur++;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t mod_state_size(const qpol_iterator_t * iter)
+{
+ mod_state_t *ms;
+
+ if (!iter || !(ms = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ return ms->end;
+}
+
+int qpol_policy_get_module_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ mod_state_t *ms = NULL;
+ int error = 0;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(ms = calloc(1, sizeof(mod_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ if (qpol_iterator_create(policy, (void *)ms, mod_state_get_cur, mod_state_next, mod_state_end, mod_state_size, free, iter)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ free(ms);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ ms->end = policy->num_modules;
+ ms->list = policy->modules;
+
+ return STATUS_SUCCESS;
+}
+
+static int is_mls_policy(const qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ if (db->mls != 0)
+ return 1;
+ else
+ return 0;
+}
+
+int qpol_policy_is_mls_enabled(qpol_policy_t * policy)
+{
+ return is_mls_policy(policy);
+}
+
+int qpol_policy_get_policy_version(const qpol_policy_t * policy, unsigned int *version)
+{
+ policydb_t *db;
+
+ if (version != NULL)
+ *version = 0;
+
+ if (policy == NULL || version == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ *version = db->policyvers;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_type(const qpol_policy_t * policy, int *type)
+{
+ if (!policy || !type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *type = policy->type;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_has_capability(const qpol_policy_t * policy, qpol_capability_e cap)
+{
+ unsigned int version = 0;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+
+ qpol_policy_get_policy_version(policy, &version);
+
+ switch (cap) {
+ case QPOL_CAP_ATTRIB_NAMES:
+ {
+ if ((policy->type == QPOL_POLICY_KERNEL_SOURCE || policy->type == QPOL_POLICY_MODULE_BINARY) || (version >= 24))
+ return 1;
+ break;
+ }
+ case QPOL_CAP_SYN_RULES:
+ {
+ if (policy->type == QPOL_POLICY_KERNEL_SOURCE || policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_LINE_NUMBERS:
+ {
+ if (policy->type == QPOL_POLICY_KERNEL_SOURCE)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_CONDITIONALS:
+ {
+ if (version >= 16 || policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_MLS:
+ {
+ return is_mls_policy(policy);
+ }
+ case QPOL_CAP_MODULES:
+ {
+ if (policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_POLCAPS:
+ {
+ if (version >= 22 && policy->type != QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ if (version >= 7 && policy->type == QPOL_POLICY_MODULE_BINARY)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_RULES_LOADED:
+ {
+ if (!(policy->options & QPOL_POLICY_OPTION_NO_RULES))
+ return 1;
+ break;
+ }
+ case QPOL_CAP_SOURCE:
+ {
+ if (policy->type == QPOL_POLICY_KERNEL_SOURCE)
+ return 1;
+ break;
+ }
+ case QPOL_CAP_NEVERALLOW:
+ {
+ if (!(policy->options & QPOL_POLICY_OPTION_NO_NEVERALLOWS) && policy->type != QPOL_POLICY_KERNEL_BINARY)
+ return 1;
+ break;
+ }
+ default:
+ {
+ ERR(policy, "%s", "Unknown capability");
+ errno = EDOM;
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/libqpol/src/policy_define.c b/libqpol/src/policy_define.c
new file mode 100644
index 0000000..c94f7aa
--- /dev/null
+++ b/libqpol/src/policy_define.c
@@ -0,0 +1,4319 @@
+/**
+ * @file policy_define.c
+ *
+ * This file is based upon checkpolicy/policy_define.c from NSA's SVN
+ * repository. It has been modified to support older policy formats.
+ */
+
+/*
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Updated: David Caplan, <dac@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Updated: Joshua Brindle <jbrindle@tresys.com>
+ * Karl MacMillan <kmacmillan@mentalrootkit.com>
+ * Jason Tang <jtang@tresys.com>
+ *
+ * Added support for binary policy modules
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2008 Tresys Technology, LLC
+ * Copyright (C) 2007 Red Hat Inc.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+/* FLASK */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include <sepol/policydb/expand.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/services.h>
+#include <sepol/policydb/conditional.h>
+#include <sepol/policydb/flask.h>
+#include <sepol/policydb/hierarchy.h>
+#ifdef HAVE_SEPOL_POLICYCAPS
+#include <sepol/policydb/polcaps.h>
+#endif
+#ifdef HAVE_SEPOL_ERRCODES
+#include <sepol/errcodes.h>
+#endif
+
+#include "queue.h"
+#include <qpol/policy.h>
+#include "module_compiler.h"
+#include "policy_define.h"
+
+policydb_t *policydbp;
+queue_t id_queue = 0;
+unsigned int pass;
+static int load_rules;
+static unsigned int num_rules = 0;
+char *curfile = 0;
+int mlspol = 0;
+
+extern unsigned long policydb_lineno;
+extern unsigned long source_lineno;
+extern unsigned int policydb_errors;
+
+extern int yywarn(char *msg);
+extern int yyerror(char *msg);
+
+#define ERRORMSG_LEN 255
+static char errormsg[ERRORMSG_LEN + 1] = { 0 };
+
+static int id_has_dot(char *id);
+static int parse_security_context(context_struct_t * c);
+
+/* initialize all of the state variables for the scanner/parser */
+void init_parser(int pass_number, int do_rules)
+{
+ policydb_lineno = 1;
+ source_lineno = 1;
+ policydb_errors = 0;
+ pass = pass_number;
+ load_rules = do_rules;
+ num_rules = 0;
+}
+
+void yyerror2(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(errormsg, ERRORMSG_LEN, fmt, ap);
+ yyerror(errormsg);
+ va_end(ap);
+}
+
+int define_mls(void)
+{
+ mlspol = 1;
+ policydbp->mls = 1;
+
+ return 0;
+}
+
+/* Add a rule onto an avtab hash table only if it does not already
+ * exist. (Note that the avtab is discarded afterwards; it will be
+ * regenerated during expansion.) Return 1 if rule was added (or
+ * otherwise handled successfully), 0 if it conflicted with something,
+ * 2 if the rule is not to be added, or -1 on error.
+ */
+static int insert_check_type_rule(avrule_t * rule, avtab_t * avtab, cond_av_list_t ** list, cond_av_list_t ** other)
+{
+ int ret;
+
+ if (num_rules && !load_rules)
+ return 2;
+
+#ifdef SEPOL_DYNAMIC_AVTAB
+ if (!avtab->htable)
+ if (avtab_alloc(avtab, MAX_AVTAB_SIZE))
+ return -1;
+#endif
+
+ ret = expand_rule(NULL, policydbp, rule, avtab, list, other, 0);
+ if (ret < 0) {
+ yyerror("Failed on expanding rule");
+ }
+ return ret;
+}
+int insert_separator(int push)
+{
+ int error;
+
+ if (push)
+ error = queue_push(id_queue, 0);
+ else
+ error = queue_insert(id_queue, 0);
+
+ if (error) {
+ yyerror("queue overflow");
+ return -1;
+ }
+ return 0;
+}
+
+int insert_id(char *id, int push)
+{
+ char *newid = 0;
+ int error;
+
+ newid = (char *)malloc(strlen(id) + 1);
+ if (!newid) {
+ yyerror("out of memory");
+ return -1;
+ }
+ strcpy(newid, id);
+ if (push)
+ error = queue_push(id_queue, (queue_element_t) newid);
+ else
+ error = queue_insert(id_queue, (queue_element_t) newid);
+
+ if (error) {
+ yyerror("queue overflow");
+ free(newid);
+ return -1;
+ }
+ return 0;
+}
+
+/* If the identifier has a dot within it and that its first character
+ is not a dot then return 1, else return 0. */
+static int id_has_dot(char *id)
+{
+ if (strchr(id, '.') >= id + 1) {
+ return 1;
+ }
+ return 0;
+}
+
+int define_class(void)
+{
+ char *id = 0;
+ class_datum_t *datum = 0;
+ int ret;
+ uint32_t value;
+
+ if (pass == 2) {
+ id = queue_remove(id_queue);
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no class name for class definition?");
+ return -1;
+ }
+ datum = (class_datum_t *) malloc(sizeof(class_datum_t));
+ if (!datum) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ memset(datum, 0, sizeof(class_datum_t));
+ ret = declare_symbol(SYM_CLASSES, id, datum, &value, &value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto bad;
+ }
+ case -2:{
+ yyerror2("duplicate declaration of class %s", id);
+ goto bad;
+ }
+ case -1:{
+ yyerror("could not declare class here");
+ goto bad;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ datum->s.value = value;
+ return 0;
+
+ bad:
+ if (id)
+ free(id);
+ if (datum)
+ free(datum);
+ return -1;
+}
+
+int define_permissive(void)
+{
+ char *type = NULL;
+ struct type_datum *t;
+ int rc = 0;
+
+ type = queue_remove(id_queue);
+
+ if (!type) {
+ yyerror2("forgot to include type in permissive definition?");
+ rc = -1;
+ goto out;
+ }
+
+ if (pass == 1)
+ goto out;
+
+ if (!is_id_in_scope(SYM_TYPES, type)) {
+ yyerror2("type %s is not within scope", type);
+ rc = -1;
+ goto out;
+ }
+
+ t = hashtab_search(policydbp->p_types.table, type);
+ if (!t) {
+ yyerror2("type is not defined: %s", type);
+ rc = -1;
+ goto out;
+ }
+
+ if (t->flavor == TYPE_ATTRIB) {
+ yyerror2("attributes may not be permissive: %s\n", type);
+ rc = -1;
+ goto out;
+ }
+#ifdef HAVE_SEPOL_PERMISSIVE_TYPES
+ t->flags |= TYPE_FLAGS_PERMISSIVE;
+#else
+ yyerror("This version of SETools does not have permissive types enabled.");
+#endif
+ out:
+ free(type);
+ return rc;
+}
+
+int define_polcap(void)
+{
+ char *id = 0;
+ int capnum;
+
+ if (pass == 2) {
+ id = queue_remove(id_queue);
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no capability name for policycap definition?");
+ goto bad;
+ }
+#ifdef HAVE_SEPOL_POLICYCAPS
+ /* Check for valid cap name -> number mapping */
+ capnum = sepol_polcap_getnum(id);
+ if (capnum < 0) {
+ yyerror2("invalid policy capability name %s", id);
+ goto bad;
+ }
+
+ /* Store it */
+ if (ebitmap_set_bit(&policydbp->policycaps, capnum, TRUE)) {
+ yyerror("out of memory");
+ goto bad;
+ }
+#else
+ yyerror("This version of SETools does not have policycap enabled.");
+#endif
+
+ free(id);
+ return 0;
+
+ bad:
+ free(id);
+ return -1;
+}
+
+int define_initial_sid(void)
+{
+ char *id = 0;
+ ocontext_t *newc = 0, *c, *head;
+
+ if (pass == 2) {
+ id = queue_remove(id_queue);
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no sid name for SID definition?");
+ return -1;
+ }
+ newc = (ocontext_t *) malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+ newc->u.name = id;
+ context_init(&newc->context[0]);
+ head = policydbp->ocontexts[OCON_ISID];
+
+ for (c = head; c; c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name)) {
+ yyerror2("duplicate initial SID %s", id);
+ goto bad;
+ }
+ }
+
+ if (head) {
+ newc->sid[0] = head->sid[0] + 1;
+ } else {
+ newc->sid[0] = 1;
+ }
+ newc->next = head;
+ policydbp->ocontexts[OCON_ISID] = newc;
+
+ return 0;
+
+ bad:
+ if (id)
+ free(id);
+ if (newc)
+ free(newc);
+ return -1;
+}
+
+int define_common_perms(void)
+{
+ char *id = 0, *perm = 0;
+ common_datum_t *comdatum = 0;
+ perm_datum_t *perdatum = 0;
+ int ret;
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no common name for common perm definition?");
+ return -1;
+ }
+ comdatum = hashtab_search(policydbp->p_commons.table, id);
+ if (comdatum) {
+ yyerror2("duplicate declaration for common %s\n", id);
+ return -1;
+ }
+ comdatum = (common_datum_t *) malloc(sizeof(common_datum_t));
+ if (!comdatum) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ memset(comdatum, 0, sizeof(common_datum_t));
+ ret = hashtab_insert(policydbp->p_commons.table, (hashtab_key_t) id, (hashtab_datum_t) comdatum);
+
+ if (ret == SEPOL_EEXIST) {
+ yyerror("duplicate common definition");
+ goto bad;
+ }
+ if (ret == SEPOL_ENOMEM) {
+ yyerror("hash table overflow");
+ goto bad;
+ }
+ comdatum->s.value = policydbp->p_commons.nprim + 1;
+ if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE)) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ policydbp->p_commons.nprim++;
+ while ((perm = queue_remove(id_queue))) {
+ perdatum = (perm_datum_t *) malloc(sizeof(perm_datum_t));
+ if (!perdatum) {
+ yyerror("out of memory");
+ goto bad_perm;
+ }
+ memset(perdatum, 0, sizeof(perm_datum_t));
+ perdatum->s.value = comdatum->permissions.nprim + 1;
+
+ if (perdatum->s.value > (sizeof(sepol_access_vector_t) * 8)) {
+ yyerror("too many permissions to fit in an access vector");
+ goto bad_perm;
+ }
+ ret = hashtab_insert(comdatum->permissions.table, (hashtab_key_t) perm, (hashtab_datum_t) perdatum);
+
+ if (ret == SEPOL_EEXIST) {
+ yyerror2("duplicate permission %s in common %s", perm, id);
+ goto bad_perm;
+ }
+ if (ret == SEPOL_ENOMEM) {
+ yyerror("hash table overflow");
+ goto bad_perm;
+ }
+ comdatum->permissions.nprim++;
+ }
+
+ return 0;
+
+ bad:
+ if (id)
+ free(id);
+ if (comdatum)
+ free(comdatum);
+ return -1;
+
+ bad_perm:
+ if (perm)
+ free(perm);
+ if (perdatum)
+ free(perdatum);
+ return -1;
+}
+
+int define_av_perms(int inherits)
+{
+ char *id;
+ class_datum_t *cladatum;
+ common_datum_t *comdatum;
+ perm_datum_t *perdatum = 0, *perdatum2 = 0;
+ int ret;
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no tclass name for av perm definition?");
+ return -1;
+ }
+ cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id);
+ if (!cladatum) {
+ yyerror2("class %s is not defined", id);
+ goto bad;
+ }
+ free(id);
+
+ if (cladatum->comdatum || cladatum->permissions.nprim) {
+ yyerror("duplicate access vector definition");
+ return -1;
+ }
+ if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE)) {
+ yyerror("out of memory");
+ return -1;
+ }
+ if (inherits) {
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no inherits name for access vector definition?");
+ return -1;
+ }
+ comdatum = (common_datum_t *) hashtab_search(policydbp->p_commons.table, (hashtab_key_t) id);
+
+ if (!comdatum) {
+ yyerror2("common %s is not defined", id);
+ goto bad;
+ }
+ cladatum->comkey = id;
+ cladatum->comdatum = comdatum;
+
+ /*
+ * Class-specific permissions start with values
+ * after the last common permission.
+ */
+ cladatum->permissions.nprim += comdatum->permissions.nprim;
+ }
+ while ((id = queue_remove(id_queue))) {
+ perdatum = (perm_datum_t *) malloc(sizeof(perm_datum_t));
+ if (!perdatum) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ memset(perdatum, 0, sizeof(perm_datum_t));
+ perdatum->s.value = ++cladatum->permissions.nprim;
+
+ if (perdatum->s.value > (sizeof(sepol_access_vector_t) * 8)) {
+ yyerror("too many permissions to fit in an access vector");
+ goto bad;
+ }
+ if (inherits) {
+ /*
+ * Class-specific permissions and
+ * common permissions exist in the same
+ * name space.
+ */
+ perdatum2 = (perm_datum_t *) hashtab_search(cladatum->comdatum->permissions.table, (hashtab_key_t) id);
+ if (perdatum2) {
+ yyerror2("permission %s conflicts with an " "inherited permission", id);
+ goto bad;
+ }
+ }
+ ret = hashtab_insert(cladatum->permissions.table, (hashtab_key_t) id, (hashtab_datum_t) perdatum);
+
+ if (ret == SEPOL_EEXIST) {
+ yyerror2("duplicate permission %s", id);
+ goto bad;
+ }
+ if (ret == SEPOL_ENOMEM) {
+ yyerror("hash table overflow");
+ goto bad;
+ }
+ if (add_perm_to_class(perdatum->s.value, cladatum->s.value)) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ }
+
+ return 0;
+
+ bad:
+ if (id)
+ free(id);
+ if (perdatum)
+ free(perdatum);
+ return -1;
+}
+
+int define_sens(void)
+{
+ char *id;
+ mls_level_t *level = 0;
+ level_datum_t *datum = 0, *aliasdatum = 0;
+ int ret;
+ uint32_t value; /* dummy variable -- its value is never used */
+
+ if (!mlspol) {
+ yyerror("sensitivity definition in non-MLS configuration");
+ return -1;
+ }
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no sensitivity name for sensitivity definition?");
+ return -1;
+ }
+ if (id_has_dot(id)) {
+ yyerror("sensitivity identifiers may not contain periods");
+ goto bad;
+ }
+ level = (mls_level_t *) malloc(sizeof(mls_level_t));
+ if (!level) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ mls_level_init(level);
+ level->sens = 0; /* actual value set in define_dominance */
+ ebitmap_init(&level->cat); /* actual value set in define_level */
+
+ datum = (level_datum_t *) malloc(sizeof(level_datum_t));
+ if (!datum) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ level_datum_init(datum);
+ datum->isalias = FALSE;
+ datum->level = level;
+
+ ret = declare_symbol(SYM_LEVELS, id, datum, &value, &value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto bad;
+ }
+ case -2:{
+ yyerror("duplicate declaration of sensitivity level");
+ goto bad;
+ }
+ case -1:{
+ yyerror("could not declare sensitivity level here");
+ goto bad;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ if (id_has_dot(id)) {
+ yyerror("sensitivity aliases may not contain periods");
+ goto bad_alias;
+ }
+ aliasdatum = (level_datum_t *) malloc(sizeof(level_datum_t));
+ if (!aliasdatum) {
+ yyerror("out of memory");
+ goto bad_alias;
+ }
+ level_datum_init(aliasdatum);
+ aliasdatum->isalias = TRUE;
+ aliasdatum->level = level;
+
+ ret = declare_symbol(SYM_LEVELS, id, aliasdatum, NULL, &value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto bad_alias;
+ }
+ case -2:{
+ yyerror("duplicate declaration of sensitivity alias");
+ goto bad_alias;
+ }
+ case -1:{
+ yyerror("could not declare sensitivity alias here");
+ goto bad_alias;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ }
+
+ return 0;
+
+ bad:
+ if (id)
+ free(id);
+ if (level)
+ free(level);
+ if (datum) {
+ level_datum_destroy(datum);
+ free(datum);
+ }
+ return -1;
+
+ bad_alias:
+ if (id)
+ free(id);
+ if (aliasdatum) {
+ level_datum_destroy(aliasdatum);
+ free(aliasdatum);
+ }
+ return -1;
+}
+
+int define_dominance(void)
+{
+ level_datum_t *datum;
+ int order;
+ char *id;
+
+ if (!mlspol) {
+ yyerror("dominance definition in non-MLS configuration");
+ return -1;
+ }
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ order = 0;
+ while ((id = (char *)queue_remove(id_queue))) {
+ datum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id);
+ if (!datum) {
+ yyerror2("unknown sensitivity %s used in dominance " "definition", id);
+ free(id);
+ return -1;
+ }
+ if (datum->level->sens != 0) {
+ yyerror2("sensitivity %s occurs multiply in dominance " "definition", id);
+ free(id);
+ return -1;
+ }
+ datum->level->sens = ++order;
+
+ /* no need to keep sensitivity name */
+ free(id);
+ }
+
+ if (order != policydbp->p_levels.nprim) {
+ yyerror("all sensitivities must be specified in dominance definition");
+ return -1;
+ }
+ return 0;
+}
+
+int define_category(void)
+{
+ char *id;
+ cat_datum_t *datum = 0, *aliasdatum = 0;
+ int ret;
+ uint32_t value;
+
+ if (!mlspol) {
+ yyerror("category definition in non-MLS configuration");
+ return -1;
+ }
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no category name for category definition?");
+ return -1;
+ }
+ if (id_has_dot(id)) {
+ yyerror("category identifiers may not contain periods");
+ goto bad;
+ }
+ datum = (cat_datum_t *) malloc(sizeof(cat_datum_t));
+ if (!datum) {
+ yyerror("out of memory");
+ goto bad;
+ }
+ cat_datum_init(datum);
+ datum->isalias = FALSE;
+
+ ret = declare_symbol(SYM_CATS, id, datum, &value, &value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto bad;
+ }
+ case -2:{
+ yyerror("duplicate declaration of category");
+ goto bad;
+ }
+ case -1:{
+ yyerror("could not declare category here");
+ goto bad;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ datum->s.value = value;
+
+ while ((id = queue_remove(id_queue))) {
+ if (id_has_dot(id)) {
+ yyerror("category aliases may not contain periods");
+ goto bad_alias;
+ }
+ aliasdatum = (cat_datum_t *) malloc(sizeof(cat_datum_t));
+ if (!aliasdatum) {
+ yyerror("out of memory");
+ goto bad_alias;
+ }
+ cat_datum_init(aliasdatum);
+ aliasdatum->isalias = TRUE;
+ aliasdatum->s.value = datum->s.value;
+
+ ret = declare_symbol(SYM_CATS, id, aliasdatum, NULL, &datum->s.value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto bad_alias;
+ }
+ case -2:{
+ yyerror("duplicate declaration of category aliases");
+ goto bad_alias;
+ }
+ case -1:{
+ yyerror("could not declare category aliases here");
+ goto bad_alias;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ }
+
+ return 0;
+
+ bad:
+ if (id)
+ free(id);
+ if (datum) {
+ cat_datum_destroy(datum);
+ free(datum);
+ }
+ return -1;
+
+ bad_alias:
+ if (id)
+ free(id);
+ if (aliasdatum) {
+ cat_datum_destroy(aliasdatum);
+ free(aliasdatum);
+ }
+ return -1;
+}
+
+static int clone_level(hashtab_key_t key, hashtab_datum_t datum, void *arg)
+{
+ level_datum_t *levdatum = (level_datum_t *) datum;
+ mls_level_t *level = (mls_level_t *) arg, *newlevel;
+
+ if (levdatum->level == level) {
+ levdatum->defined = 1;
+ if (!levdatum->isalias)
+ return 0;
+ newlevel = (mls_level_t *) malloc(sizeof(mls_level_t));
+ if (!newlevel)
+ return -1;
+ if (mls_level_cpy(newlevel, level)) {
+ free(newlevel);
+ return -1;
+ }
+ levdatum->level = newlevel;
+ }
+ return 0;
+}
+
+int define_level(void)
+{
+ char *id;
+ level_datum_t *levdatum;
+
+ if (!mlspol) {
+ yyerror("level definition in non-MLS configuration");
+ return -1;
+ }
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no level name for level definition?");
+ return -1;
+ }
+ levdatum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id);
+ if (!levdatum) {
+ yyerror2("unknown sensitivity %s used in level definition", id);
+ free(id);
+ return -1;
+ }
+ if (ebitmap_length(&levdatum->level->cat)) {
+ yyerror2("sensitivity %s used in multiple level definitions", id);
+ free(id);
+ return -1;
+ }
+ free(id);
+
+ levdatum->defined = 1;
+
+ while ((id = queue_remove(id_queue))) {
+ cat_datum_t *cdatum;
+ int range_start, range_end, i;
+
+ if (id_has_dot(id)) {
+ char *id_start = id;
+ char *id_end = strchr(id, '.');
+
+ *(id_end++) = '\0';
+
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t)
+ id_start);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id_start);
+ free(id);
+ return -1;
+ }
+ range_start = cdatum->s.value - 1;
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t)
+ id_end);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id_end);
+ free(id);
+ return -1;
+ }
+ range_end = cdatum->s.value - 1;
+
+ if (range_end < range_start) {
+ yyerror2("category range is invalid");
+ free(id);
+ return -1;
+ }
+ } else {
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id);
+ range_start = range_end = cdatum->s.value - 1;
+ }
+
+ for (i = range_start; i <= range_end; i++) {
+ if (ebitmap_set_bit(&levdatum->level->cat, i, TRUE)) {
+ yyerror("out of memory");
+ free(id);
+ return -1;
+ }
+ }
+
+ free(id);
+ }
+
+ if (hashtab_map(policydbp->p_levels.table, clone_level, levdatum->level)) {
+ yyerror("out of memory");
+ return -1;
+ }
+
+ return 0;
+}
+
+int define_attrib(void)
+{
+ if (pass == 2) {
+ free(queue_remove(id_queue));
+ return 0;
+ }
+
+ if (declare_type(TRUE, TRUE) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+static int add_aliases_to_type(type_datum_t * type)
+{
+ char *id;
+ type_datum_t *aliasdatum = NULL;
+ int ret;
+ while ((id = queue_remove(id_queue))) {
+ if (id_has_dot(id)) {
+ free(id);
+ yyerror("type alias identifiers may not contain periods");
+ return -1;
+ }
+ aliasdatum = (type_datum_t *) malloc(sizeof(type_datum_t));
+ if (!aliasdatum) {
+ free(id);
+ yyerror("Out of memory!");
+ return -1;
+ }
+ memset(aliasdatum, 0, sizeof(type_datum_t));
+ aliasdatum->s.value = type->s.value;
+
+ ret = declare_symbol(SYM_TYPES, id, aliasdatum, NULL, &aliasdatum->s.value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+ case -2:{
+ yyerror2("duplicate declaration of alias %s", id);
+ goto cleanup;
+ }
+ case -1:{
+ yyerror("could not declare alias here");
+ goto cleanup;
+ }
+ case 0:
+ break;
+ case 1:{
+ /* ret == 1 means the alias was required and therefore already
+ * has a value. Set it up as an alias with a different primary. */
+ type_datum_destroy(aliasdatum);
+ free(aliasdatum);
+
+ aliasdatum = hashtab_search(policydbp->symtab[SYM_TYPES].table, id);
+ assert(aliasdatum);
+
+ aliasdatum->primary = type->s.value;
+ aliasdatum->flavor = TYPE_ALIAS;
+
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ }
+ return 0;
+ cleanup:
+ free(id);
+ type_datum_destroy(aliasdatum);
+ free(aliasdatum);
+ return -1;
+}
+
+int define_typealias(void)
+{
+ char *id;
+ type_datum_t *t;
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no type name for typealias definition?");
+ return -1;
+ }
+
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("type %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ t = hashtab_search(policydbp->p_types.table, id);
+ if (!t || t->flavor == TYPE_ATTRIB) {
+ yyerror2("unknown type %s, or it was already declared as an " "attribute", id);
+ free(id);
+ return -1;
+ }
+ free(id);
+ return add_aliases_to_type(t);
+}
+
+int define_typeattribute(void)
+{
+ char *id;
+ type_datum_t *t, *attr;
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no type name for typeattribute definition?");
+ return -1;
+ }
+
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("type %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ t = hashtab_search(policydbp->p_types.table, id);
+ if (!t || t->flavor == TYPE_ATTRIB) {
+ yyerror2("unknown type %s", id);
+ free(id);
+ return -1;
+ }
+ free(id);
+
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("attribute %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ attr = hashtab_search(policydbp->p_types.table, id);
+ if (!attr) {
+ /* treat it as a fatal error */
+ yyerror2("attribute %s is not declared", id);
+ free(id);
+ return -1;
+ }
+
+ if (attr->flavor != TYPE_ATTRIB) {
+ yyerror2("%s is a type, not an attribute", id);
+ free(id);
+ return -1;
+ }
+
+ if ((attr = get_local_type(id, attr->s.value, 1)) == NULL) {
+ yyerror("Out of memory!");
+ return -1;
+ }
+
+ if (ebitmap_set_bit(&attr->types, (t->s.value - 1), TRUE)) {
+ yyerror("out of memory");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int define_typebounds_helper(char *bounds_id, char *type_id)
+{
+ type_datum_t *bounds, *type;
+
+ if (!is_id_in_scope(SYM_TYPES, bounds_id)) {
+ yyerror2("type %s is not within scope", bounds_id);
+ return -1;
+ }
+
+ bounds = hashtab_search(policydbp->p_types.table, bounds_id);
+ if (!bounds || bounds->flavor == TYPE_ATTRIB) {
+ yyerror2("hoge unknown type %s", bounds_id);
+ return -1;
+ }
+
+ if (!is_id_in_scope(SYM_TYPES, type_id)) {
+ yyerror2("type %s is not within scope", type_id);
+ return -1;
+ }
+
+ type = hashtab_search(policydbp->p_types.table, type_id);
+ if (!type || type->flavor == TYPE_ATTRIB) {
+ yyerror2("type %s is not declared", type_id);
+ return -1;
+ }
+
+ if (type->flavor == TYPE_TYPE && !type->primary) {
+ type = policydbp->type_val_to_struct[type->s.value - 1];
+ } else if (type->flavor == TYPE_ALIAS) {
+ type = policydbp->type_val_to_struct[type->primary - 1];
+ }
+
+ if (!type->bounds)
+ type->bounds = bounds->s.value;
+ else if (type->bounds != bounds->s.value) {
+ yyerror2("type %s has inconsistent master {%s,%s}",
+ type_id,
+ policydbp->p_type_val_to_name[type->bounds - 1], policydbp->p_type_val_to_name[bounds->s.value - 1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+int define_typebounds(void)
+{
+ char *bounds, *id;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ bounds = (char *)queue_remove(id_queue);
+ if (!bounds) {
+ yyerror("no type name for typebounds definition?");
+ return -1;
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ if (define_typebounds_helper(bounds, id))
+ return -1;
+ free(id);
+ }
+ free(bounds);
+
+ return 0;
+}
+
+int define_type(int alias)
+{
+ char *id;
+ type_datum_t *datum, *attr;
+ int newattr = 0;
+
+ if (pass == 2) {
+ /*
+ * If type name contains ".", we have to define boundary
+ * relationship implicitly to keep compatibility with
+ * old name based hierarchy.
+ */
+ if ((id = queue_remove(id_queue))) {
+ char *bounds, *delim;
+
+ if ((delim = strrchr(id, '.'))
+ && (bounds = strdup(id))) {
+ bounds[(size_t) (delim - id)] = '\0';
+
+ if (define_typebounds_helper(bounds, id))
+ return -1;
+ free(bounds);
+ }
+ free(id);
+ }
+
+ if (alias) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ }
+
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ if ((datum = declare_type(TRUE, FALSE)) == NULL) {
+ return -1;
+ }
+
+ if (alias) {
+ if (add_aliases_to_type(datum) == -1) {
+ return -1;
+ }
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("attribute %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ attr = hashtab_search(policydbp->p_types.table, id);
+ if (!attr) {
+ /* treat it as a fatal error */
+ yyerror2("attribute %s is not declared", id);
+ return -1;
+ } else {
+ newattr = 0;
+ }
+
+ if (attr->flavor != TYPE_ATTRIB) {
+ yyerror2("%s is a type, not an attribute", id);
+ return -1;
+ }
+
+ if ((attr = get_local_type(id, attr->s.value, 1)) == NULL) {
+ yyerror("Out of memory!");
+ return -1;
+ }
+
+ if (ebitmap_set_bit(&attr->types, datum->s.value - 1, TRUE)) {
+ yyerror("Out of memory");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct val_to_name
+{
+ unsigned int val;
+ char *name;
+};
+
+/* Adds a type, given by its textual name, to a typeset. If *add is
+ 0, then add the type to the negative set; otherwise if *add is 1
+ then add it to the positive side. */
+static int set_types(type_set_t * set, char *id, int *add, char starallowed)
+{
+ type_datum_t *t;
+
+ if (strcmp(id, "*") == 0) {
+ if (!starallowed) {
+ yyerror("* not allowed in this type of rule");
+ return -1;
+ }
+ /* set TYPE_STAR flag */
+ set->flags = TYPE_STAR;
+ free(id);
+ *add = 1;
+ return 0;
+ }
+
+ if (strcmp(id, "~") == 0) {
+ if (!starallowed) {
+ yyerror("~ not allowed in this type of rule");
+ return -1;
+ }
+ /* complement the set */
+ set->flags = TYPE_COMP;
+ free(id);
+ *add = 1;
+ return 0;
+ }
+
+ if (strcmp(id, "-") == 0) {
+ *add = 0;
+ free(id);
+ return 0;
+ }
+
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("type %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ t = hashtab_search(policydbp->p_types.table, id);
+ if (!t) {
+ yyerror2("unknown type %s", id);
+ free(id);
+ return -1;
+ }
+
+ if (*add == 0) {
+ if (ebitmap_set_bit(&set->negset, t->s.value - 1, TRUE))
+ goto oom;
+ } else {
+ if (ebitmap_set_bit(&set->types, t->s.value - 1, TRUE))
+ goto oom;
+ }
+ free(id);
+ *add = 1;
+ return 0;
+ oom:
+ yyerror("Out of memory");
+ free(id);
+ return -1;
+}
+
+int define_compute_type_helper(int which, avrule_t ** rule)
+{
+ char *id;
+ type_datum_t *datum;
+ class_datum_t *cladatum;
+ ebitmap_t tclasses;
+ ebitmap_node_t *node;
+ avrule_t *avrule;
+ class_perm_node_t *perm;
+ int i, add = 1;
+
+ avrule = malloc(sizeof(avrule_t));
+ if (!avrule) {
+ yyerror("out of memory");
+ return -1;
+ }
+ avrule_init(avrule);
+ avrule->specified = which;
+ avrule->line = policydb_lineno;
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&avrule->stypes, id, &add, 0))
+ return -1;
+ }
+ add = 1;
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&avrule->ttypes, id, &add, 0))
+ return -1;
+ }
+
+ ebitmap_init(&tclasses);
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_CLASSES, id)) {
+ yyerror2("class %s is not within scope", id);
+ free(id);
+ goto bad;
+ }
+ cladatum = hashtab_search(policydbp->p_classes.table, id);
+ if (!cladatum) {
+ yyerror2("unknown class %s", id);
+ goto bad;
+ }
+ if (ebitmap_set_bit(&tclasses, cladatum->s.value - 1, TRUE)) {
+ yyerror("Out of memory");
+ goto bad;
+ }
+ free(id);
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no newtype?");
+ goto bad;
+ }
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("type %s is not within scope", id);
+ free(id);
+ goto bad;
+ }
+ datum = (type_datum_t *) hashtab_search(policydbp->p_types.table, (hashtab_key_t) id);
+ if (!datum || datum->flavor == TYPE_ATTRIB) {
+ yyerror2("unknown type %s", id);
+ goto bad;
+ }
+ free(id);
+
+ ebitmap_for_each_bit(&tclasses, node, i) {
+ if (ebitmap_node_get_bit(node, i)) {
+ perm = malloc(sizeof(class_perm_node_t));
+ if (!perm) {
+ yyerror("out of memory");
+ return -1;
+ }
+ class_perm_node_init(perm);
+ perm->class = i + 1;
+ perm->data = datum->s.value;
+ perm->next = avrule->perms;
+ avrule->perms = perm;
+ }
+ }
+ ebitmap_destroy(&tclasses);
+
+ *rule = avrule;
+ return 0;
+
+ bad:
+ avrule_destroy(avrule);
+ free(avrule);
+ return -1;
+}
+
+int define_compute_type(int which)
+{
+ char *id;
+ avrule_t *avrule;
+ int retval;
+
+ if (pass == 1 || (num_rules && !load_rules)) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ id = queue_remove(id_queue);
+ free(id);
+ return 0;
+ }
+
+ num_rules++;
+
+ if (define_compute_type_helper(which, &avrule))
+ return -1;
+
+ retval = insert_check_type_rule(avrule, &policydbp->te_avtab, NULL, NULL);
+ switch (retval) {
+ case 1:{
+ /* append this avrule to the end of the current rules list */
+ append_avrule(avrule);
+ return 0;
+ }
+ case 2: /* FALLTHROUGH */
+ case 0:{
+ /* rule conflicted, so don't actually add this rule */
+ avrule_destroy(avrule);
+ free(avrule);
+ return 0;
+ }
+ case -1:{
+ avrule_destroy(avrule);
+ free(avrule);
+ return -1;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+}
+
+avrule_t *define_cond_compute_type(int which)
+{
+ char *id;
+ avrule_t *avrule;
+
+ if (pass == 1 || (num_rules && !load_rules)) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ id = queue_remove(id_queue);
+ free(id);
+ return (avrule_t *) 1;
+ }
+
+ num_rules++;
+
+ if (define_compute_type_helper(which, &avrule))
+ return COND_ERR;
+
+ return avrule;
+}
+
+int define_bool(void)
+{
+ char *id, *bool_value;
+ cond_bool_datum_t *datum;
+ int ret;
+ uint32_t value;
+
+ if (pass == 2) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no identifier for bool definition?");
+ return -1;
+ }
+ if (id_has_dot(id)) {
+ free(id);
+ yyerror("boolean identifiers may not contain periods");
+ return -1;
+ }
+ datum = (cond_bool_datum_t *) malloc(sizeof(cond_bool_datum_t));
+ if (!datum) {
+ yyerror("out of memory");
+ free(id);
+ return -1;
+ }
+ memset(datum, 0, sizeof(cond_bool_datum_t));
+ ret = declare_symbol(SYM_BOOLS, id, datum, &value, &value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+ case -2:{
+ yyerror2("duplicate declaration of boolean %s", id);
+ goto cleanup;
+ }
+ case -1:{
+ yyerror("could not declare boolean here");
+ goto cleanup;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ datum->s.value = value;
+
+ bool_value = (char *)queue_remove(id_queue);
+ if (!bool_value) {
+ yyerror("no default value for bool definition?");
+ free(id);
+ return -1;
+ }
+
+ datum->state = (int)(bool_value[0] == 'T') ? 1 : 0;
+ free(bool_value);
+ return 0;
+ cleanup:
+ cond_destroy_bool(id, datum, NULL);
+ return -1;
+}
+
+avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl)
+{
+ if (pass == 1 || (num_rules && !load_rules)) {
+ /* return something so we get through pass 1 */
+ return (avrule_t *) 1;
+ }
+
+ if (sl == NULL) {
+ /* This is a require block, return previous list */
+ return avlist;
+ }
+
+ /* prepend the new avlist to the pre-existing one */
+ sl->next = avlist;
+ return sl;
+}
+
+int define_te_avtab_helper(int which, avrule_t ** rule)
+{
+ char *id;
+ class_datum_t *cladatum;
+ perm_datum_t *perdatum = NULL;
+ class_perm_node_t *perms, *tail = NULL, *cur_perms = NULL;
+ ebitmap_t tclasses;
+ ebitmap_node_t *node;
+ avrule_t *avrule;
+ unsigned int i;
+ int add = 1, ret = 0;
+ int suppress = 0;
+
+ avrule = (avrule_t *) malloc(sizeof(avrule_t));
+ if (!avrule) {
+ yyerror("memory error");
+ ret = -1;
+ goto out;
+ }
+ avrule_init(avrule);
+ avrule->specified = which;
+ avrule->line = policydb_lineno;
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&avrule->stypes, id, &add, which == AVRULE_NEVERALLOW ? 1 : 0)) {
+ ret = -1;
+ goto out;
+ }
+ }
+ add = 1;
+ while ((id = queue_remove(id_queue))) {
+ if (strcmp(id, "self") == 0) {
+ free(id);
+ avrule->flags |= RULE_SELF;
+ continue;
+ }
+ if (set_types(&avrule->ttypes, id, &add, which == AVRULE_NEVERALLOW ? 1 : 0)) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ ebitmap_init(&tclasses);
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_CLASSES, id)) {
+ yyerror2("class %s is not within scope", id);
+ ret = -1;
+ goto out;
+ }
+ cladatum = hashtab_search(policydbp->p_classes.table, id);
+ if (!cladatum) {
+ yyerror2("unknown class %s used in rule", id);
+ ret = -1;
+ goto out;
+ }
+ if (ebitmap_set_bit(&tclasses, cladatum->s.value - 1, TRUE)) {
+ yyerror("Out of memory");
+ ret = -1;
+ goto out;
+ }
+ free(id);
+ }
+
+ perms = NULL;
+ ebitmap_for_each_bit(&tclasses, node, i) {
+ if (!ebitmap_node_get_bit(node, i))
+ continue;
+ cur_perms = (class_perm_node_t *) malloc(sizeof(class_perm_node_t));
+ if (!cur_perms) {
+ yyerror("out of memory");
+ ret = -1;
+ goto out;
+ }
+ class_perm_node_init(cur_perms);
+ cur_perms->class = i + 1;
+ if (!perms)
+ perms = cur_perms;
+ if (tail)
+ tail->next = cur_perms;
+ tail = cur_perms;
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ cur_perms = perms;
+ ebitmap_for_each_bit(&tclasses, node, i) {
+ if (!ebitmap_node_get_bit(node, i))
+ continue;
+ cladatum = policydbp->class_val_to_struct[i];
+
+ if (strcmp(id, "*") == 0) {
+ /* set all permissions in the class */
+ cur_perms->data = ~0U;
+ goto next;
+ }
+
+ if (strcmp(id, "~") == 0) {
+ /* complement the set */
+ if (which == AVRULE_DONTAUDIT)
+ yywarn("dontaudit rule with a ~?");
+ cur_perms->data = ~cur_perms->data;
+ goto next;
+ }
+
+ perdatum = hashtab_search(cladatum->permissions.table, id);
+ if (!perdatum) {
+ if (cladatum->comdatum) {
+ perdatum = hashtab_search(cladatum->comdatum->permissions.table, id);
+ }
+ }
+ if (!perdatum) {
+ if (!suppress)
+ yyerror2("permission %s is not defined"
+ " for class %s", id, policydbp->p_class_val_to_name[i]);
+ continue;
+ } else if (!is_perm_in_scope(id, policydbp->p_class_val_to_name[i])) {
+ if (!suppress) {
+ yyerror2("permission %s of class %s is"
+ " not within scope", id, policydbp->p_class_val_to_name[i]);
+ }
+ continue;
+ } else {
+ cur_perms->data |= 1U << (perdatum->s.value - 1);
+ }
+ next:
+ cur_perms = cur_perms->next;
+ }
+
+ free(id);
+ }
+
+ ebitmap_destroy(&tclasses);
+
+ avrule->perms = perms;
+ *rule = avrule;
+
+ out:
+ return ret;
+
+}
+
+avrule_t *define_cond_te_avtab(int which)
+{
+ char *id;
+ avrule_t *avrule;
+ int i;
+
+ if (pass == 1 || (num_rules && !load_rules)) {
+ for (i = 0; i < 4; i++) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ }
+ return (avrule_t *) 1; /* any non-NULL value */
+ }
+
+ num_rules++;
+
+ if (define_te_avtab_helper(which, &avrule))
+ return COND_ERR;
+
+ return avrule;
+}
+
+int define_te_avtab(int which)
+{
+ char *id;
+ avrule_t *avrule;
+ int i;
+
+ if (pass == 1 || (num_rules && !load_rules)) {
+ for (i = 0; i < 4; i++) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ }
+ return 0;
+ }
+
+ num_rules++;
+
+ if (define_te_avtab_helper(which, &avrule))
+ return -1;
+
+ /* append this avrule to the end of the current rules list */
+ append_avrule(avrule);
+ return 0;
+}
+
+int define_role_types(void)
+{
+ role_datum_t *role;
+ char *id;
+ int add = 1;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ if ((role = declare_role()) == NULL) {
+ return -1;
+ }
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&role->types, id, &add, 0))
+ return -1;
+ }
+
+ return 0;
+}
+
+role_datum_t *merge_roles_dom(role_datum_t * r1, role_datum_t * r2)
+{
+ role_datum_t *new;
+
+ if (pass == 1) {
+ return (role_datum_t *) 1; /* any non-NULL value */
+ }
+
+ new = malloc(sizeof(role_datum_t));
+ if (!new) {
+ yyerror("out of memory");
+ return NULL;
+ }
+ memset(new, 0, sizeof(role_datum_t));
+ new->s.value = 0; /* temporary role */
+ if (ebitmap_or(&new->dominates, &r1->dominates, &r2->dominates)) {
+ yyerror("out of memory");
+ return NULL;
+ }
+ if (ebitmap_or(&new->types.types, &r1->types.types, &r2->types.types)) {
+ yyerror("out of memory");
+ return NULL;
+ }
+ if (!r1->s.value) {
+ /* free intermediate result */
+ type_set_destroy(&r1->types);
+ ebitmap_destroy(&r1->dominates);
+ free(r1);
+ }
+ if (!r2->s.value) {
+ /* free intermediate result */
+ yyerror("right hand role is temporary?");
+ type_set_destroy(&r2->types);
+ ebitmap_destroy(&r2->dominates);
+ free(r2);
+ }
+ return new;
+}
+
+/* This function eliminates the ordering dependency of role dominance rule */
+static int dominate_role_recheck(hashtab_key_t key, hashtab_datum_t datum, void *arg)
+{
+ role_datum_t *rdp = (role_datum_t *) arg;
+ role_datum_t *rdatum = (role_datum_t *) datum;
+ ebitmap_node_t *node;
+ int i;
+
+ /* Don't bother to process against self role */
+ if (rdatum->s.value == rdp->s.value)
+ return 0;
+
+ /* If a dominating role found */
+ if (ebitmap_get_bit(&(rdatum->dominates), rdp->s.value - 1)) {
+ ebitmap_t types;
+ ebitmap_init(&types);
+ if (type_set_expand(&rdp->types, &types, policydbp, 1)) {
+ ebitmap_destroy(&types);
+ return -1;
+ }
+ /* raise types and dominates from dominated role */
+ ebitmap_for_each_bit(&rdp->dominates, node, i) {
+ if (ebitmap_node_get_bit(node, i))
+ if (ebitmap_set_bit(&rdatum->dominates, i, TRUE))
+ goto oom;
+ }
+ ebitmap_for_each_bit(&types, node, i) {
+ if (ebitmap_node_get_bit(node, i))
+ if (ebitmap_set_bit(&rdatum->types.types, i, TRUE))
+ goto oom;
+ }
+ ebitmap_destroy(&types);
+ }
+
+ /* go through all the roles */
+ return 0;
+ oom:
+ yyerror("Out of memory");
+ return -1;
+}
+
+role_datum_t *define_role_dom(role_datum_t * r)
+{
+ role_datum_t *role;
+ char *role_id;
+ ebitmap_node_t *node;
+ unsigned int i;
+ int ret;
+
+ if (pass == 1) {
+ role_id = queue_remove(id_queue);
+ free(role_id);
+ return (role_datum_t *) 1; /* any non-NULL value */
+ }
+
+ yywarn("Role dominance has been deprecated");
+
+ role_id = queue_remove(id_queue);
+ if (!is_id_in_scope(SYM_ROLES, role_id)) {
+ yyerror2("role %s is not within scope", role_id);
+ free(role_id);
+ return NULL;
+ }
+ role = (role_datum_t *) hashtab_search(policydbp->p_roles.table, role_id);
+ if (!role) {
+ role = (role_datum_t *) malloc(sizeof(role_datum_t));
+ if (!role) {
+ yyerror("out of memory");
+ free(role_id);
+ return NULL;
+ }
+ memset(role, 0, sizeof(role_datum_t));
+ ret = declare_symbol(SYM_ROLES, (hashtab_key_t) role_id, (hashtab_datum_t) role, &role->s.value, &role->s.value);
+ switch (ret) {
+ case -3:{
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+ case -2:{
+ yyerror2("duplicate declaration of role %s", role_id);
+ goto cleanup;
+ }
+ case -1:{
+ yyerror("could not declare role here");
+ goto cleanup;
+ }
+ case 0:
+ case 1:{
+ break;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ if (ebitmap_set_bit(&role->dominates, role->s.value - 1, TRUE)) {
+ yyerror("Out of memory!");
+ goto cleanup;
+ }
+ }
+ if (r) {
+ ebitmap_t types;
+ ebitmap_init(&types);
+ ebitmap_for_each_bit(&r->dominates, node, i) {
+ if (ebitmap_node_get_bit(node, i))
+ if (ebitmap_set_bit(&role->dominates, i, TRUE))
+ goto oom;
+ }
+ if (type_set_expand(&r->types, &types, policydbp, 1)) {
+ ebitmap_destroy(&types);
+ return NULL;
+ }
+ ebitmap_for_each_bit(&types, node, i) {
+ if (ebitmap_node_get_bit(node, i))
+ if (ebitmap_set_bit(&role->types.types, i, TRUE))
+ goto oom;
+ }
+ ebitmap_destroy(&types);
+ if (!r->s.value) {
+ /* free intermediate result */
+ type_set_destroy(&r->types);
+ ebitmap_destroy(&r->dominates);
+ free(r);
+ }
+ /*
+ * Now go through all the roles and escalate this role's
+ * dominates and types if a role dominates this role.
+ */
+ hashtab_map(policydbp->p_roles.table, dominate_role_recheck, role);
+ }
+ return role;
+ cleanup:
+ free(role_id);
+ role_datum_destroy(role);
+ free(role);
+ return NULL;
+ oom:
+ yyerror("Out of memory");
+ goto cleanup;
+}
+
+static int role_val_to_name_helper(hashtab_key_t key, hashtab_datum_t datum, void *p)
+{
+ struct val_to_name *v = p;
+ role_datum_t *roldatum;
+
+ roldatum = (role_datum_t *) datum;
+
+ if (v->val == roldatum->s.value) {
+ v->name = key;
+ return 1;
+ }
+
+ return 0;
+}
+
+static char *role_val_to_name(unsigned int val)
+{
+ struct val_to_name v;
+ int rc;
+
+ v.val = val;
+ rc = hashtab_map(policydbp->p_roles.table, role_val_to_name_helper, &v);
+ if (rc)
+ return v.name;
+ return NULL;
+}
+
+static int set_roles(role_set_t * set, char *id)
+{
+ role_datum_t *r;
+
+ if (strcmp(id, "*") == 0) {
+ free(id);
+ yyerror("* is not allowed for role sets");
+ return -1;
+ }
+
+ if (strcmp(id, "~") == 0) {
+ free(id);
+ yyerror("~ is not allowed for role sets");
+ return -1;
+ }
+ if (!is_id_in_scope(SYM_ROLES, id)) {
+ yyerror2("role %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ r = hashtab_search(policydbp->p_roles.table, id);
+ if (!r) {
+ yyerror2("unknown role %s", id);
+ free(id);
+ return -1;
+ }
+
+ if (ebitmap_set_bit(&set->roles, r->s.value - 1, TRUE)) {
+ yyerror("out of memory");
+ free(id);
+ return -1;
+ }
+ free(id);
+ return 0;
+}
+
+int define_role_trans(void)
+{
+ char *id;
+ role_datum_t *role;
+ role_set_t roles;
+ type_set_t types;
+ ebitmap_t e_types, e_roles;
+ ebitmap_node_t *tnode, *rnode;
+ struct role_trans *tr = NULL;
+ struct role_trans_rule *rule = NULL;
+ unsigned int i, j;
+ int add = 1;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ id = queue_remove(id_queue);
+ free(id);
+ return 0;
+ }
+
+ role_set_init(&roles);
+ ebitmap_init(&e_roles);
+ type_set_init(&types);
+ ebitmap_init(&e_types);
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_roles(&roles, id))
+ return -1;
+ }
+ add = 1;
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&types, id, &add, 0))
+ return -1;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no new role in transition definition?");
+ goto bad;
+ }
+ if (!is_id_in_scope(SYM_ROLES, id)) {
+ yyerror2("role %s is not within scope", id);
+ free(id);
+ goto bad;
+ }
+ role = hashtab_search(policydbp->p_roles.table, id);
+ if (!role) {
+ yyerror2("unknown role %s used in transition definition", id);
+ goto bad;
+ }
+ free(id);
+
+ /* This ebitmap business is just to ensure that there are not conflicting role_trans rules */
+#ifdef HAVE_SEPOL_USER_ROLE_MAPPING
+ if (role_set_expand(&roles, &e_roles, policydbp, NULL))
+#else
+ if (role_set_expand(&roles, &e_roles, policydbp))
+#endif
+ goto bad;
+
+ if (type_set_expand(&types, &e_types, policydbp, 1))
+ goto bad;
+
+ ebitmap_for_each_bit(&e_roles, rnode, i) {
+ if (!ebitmap_node_get_bit(rnode, i))
+ continue;
+ ebitmap_for_each_bit(&e_types, tnode, j) {
+ if (!ebitmap_node_get_bit(tnode, j))
+ continue;
+
+ for (tr = policydbp->role_tr; tr; tr = tr->next) {
+ if (tr->role == (i + 1) && tr->type == (j + 1)) {
+ yyerror2("duplicate role transition for (%s,%s)",
+ role_val_to_name(i + 1), policydbp->p_type_val_to_name[j]);
+ goto bad;
+ }
+ }
+
+ tr = malloc(sizeof(struct role_trans));
+ if (!tr) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(tr, 0, sizeof(struct role_trans));
+ tr->role = i + 1;
+ tr->type = j + 1;
+ tr->new_role = role->s.value;
+ tr->next = policydbp->role_tr;
+ policydbp->role_tr = tr;
+ }
+ }
+ /* Now add the real rule */
+ rule = malloc(sizeof(struct role_trans_rule));
+ if (!rule) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(rule, 0, sizeof(struct role_trans_rule));
+ rule->roles = roles;
+ rule->types = types;
+ rule->new_role = role->s.value;
+
+ append_role_trans(rule);
+
+ ebitmap_destroy(&e_roles);
+ ebitmap_destroy(&e_types);
+
+ return 0;
+
+ bad:
+ return -1;
+}
+
+int define_role_allow(void)
+{
+ char *id;
+ struct role_allow_rule *ra = 0;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ ra = malloc(sizeof(role_allow_rule_t));
+ if (!ra) {
+ yyerror("out of memory");
+ return -1;
+ }
+ role_allow_rule_init(ra);
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_roles(&ra->roles, id))
+ return -1;
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_roles(&ra->new_roles, id))
+ return -1;
+ }
+
+ append_role_allow(ra);
+ return 0;
+}
+
+static constraint_expr_t *constraint_expr_clone(constraint_expr_t * expr)
+{
+ constraint_expr_t *h = NULL, *l = NULL, *e, *newe;
+ for (e = expr; e; e = e->next) {
+ newe = malloc(sizeof(*newe));
+ if (!newe)
+ goto oom;
+ if (constraint_expr_init(newe) == -1) {
+ free(newe);
+ goto oom;
+ }
+ if (l)
+ l->next = newe;
+ else
+ h = newe;
+ l = newe;
+ newe->expr_type = e->expr_type;
+ newe->attr = e->attr;
+ newe->op = e->op;
+ if (newe->expr_type == CEXPR_NAMES) {
+ if (newe->attr & CEXPR_TYPE) {
+ if (type_set_cpy(newe->type_names, e->type_names))
+ goto oom;
+ } else {
+ if (ebitmap_cpy(&newe->names, &e->names))
+ goto oom;
+ }
+ }
+ }
+
+ return h;
+ oom:
+ e = h;
+ while (e) {
+ l = e;
+ e = e->next;
+ constraint_expr_destroy(l);
+ }
+ return NULL;
+}
+
+int define_constraint(constraint_expr_t * expr)
+{
+ struct constraint_node *node;
+ char *id;
+ class_datum_t *cladatum;
+ perm_datum_t *perdatum;
+ ebitmap_t classmap;
+ ebitmap_node_t *enode;
+ constraint_expr_t *e;
+ unsigned int i;
+ int depth;
+ unsigned char useexpr = 1;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ depth = -1;
+ for (e = expr; e; e = e->next) {
+ switch (e->expr_type) {
+ case CEXPR_NOT:
+ if (depth < 0) {
+ yyerror("illegal constraint expression");
+ return -1;
+ }
+ break;
+ case CEXPR_AND:
+ case CEXPR_OR:
+ if (depth < 1) {
+ yyerror("illegal constraint expression");
+ return -1;
+ }
+ depth--;
+ break;
+ case CEXPR_ATTR:
+ case CEXPR_NAMES:
+ if (e->attr & CEXPR_XTARGET) {
+ yyerror("illegal constraint expression");
+ return -1; /* only for validatetrans rules */
+ }
+ if (depth == (CEXPR_MAXDEPTH - 1)) {
+ yyerror("constraint expression is too deep");
+ return -1;
+ }
+ depth++;
+ break;
+ default:
+ yyerror("illegal constraint expression");
+ return -1;
+ }
+ }
+ if (depth != 0) {
+ yyerror("illegal constraint expression");
+ return -1;
+ }
+
+ ebitmap_init(&classmap);
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_CLASSES, id)) {
+ yyerror2("class %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id);
+ if (!cladatum) {
+ yyerror2("class %s is not defined", id);
+ ebitmap_destroy(&classmap);
+ free(id);
+ return -1;
+ }
+ if (ebitmap_set_bit(&classmap, cladatum->s.value - 1, TRUE)) {
+ yyerror("out of memory");
+ ebitmap_destroy(&classmap);
+ free(id);
+ return -1;
+ }
+ node = malloc(sizeof(struct constraint_node));
+ if (!node) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(node, 0, sizeof(constraint_node_t));
+ if (useexpr) {
+ node->expr = expr;
+ useexpr = 0;
+ } else {
+ node->expr = constraint_expr_clone(expr);
+ }
+ if (!node->expr) {
+ yyerror("out of memory");
+ return -1;
+ }
+ node->permissions = 0;
+
+ node->next = cladatum->constraints;
+ cladatum->constraints = node;
+
+ free(id);
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ ebitmap_for_each_bit(&classmap, enode, i) {
+ if (ebitmap_node_get_bit(enode, i)) {
+ cladatum = policydbp->class_val_to_struct[i];
+ node = cladatum->constraints;
+
+ perdatum = (perm_datum_t *) hashtab_search(cladatum->permissions.table, (hashtab_key_t)
+ id);
+ if (!perdatum) {
+ if (cladatum->comdatum) {
+ perdatum = (perm_datum_t *)
+ hashtab_search(cladatum->comdatum->permissions.table, (hashtab_key_t)
+ id);
+ }
+ if (!perdatum) {
+ yyerror2("permission %s is not" " defined", id);
+ free(id);
+ ebitmap_destroy(&classmap);
+ return -1;
+ }
+ }
+ node->permissions |= (1 << (perdatum->s.value - 1));
+ }
+ }
+ free(id);
+ }
+
+ ebitmap_destroy(&classmap);
+
+ return 0;
+}
+
+int define_validatetrans(constraint_expr_t * expr)
+{
+ struct constraint_node *node;
+ char *id;
+ class_datum_t *cladatum;
+ ebitmap_t classmap;
+ constraint_expr_t *e;
+ int depth;
+ unsigned char useexpr = 1;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ return 0;
+ }
+
+ depth = -1;
+ for (e = expr; e; e = e->next) {
+ switch (e->expr_type) {
+ case CEXPR_NOT:
+ if (depth < 0) {
+ yyerror("illegal validatetrans expression");
+ return -1;
+ }
+ break;
+ case CEXPR_AND:
+ case CEXPR_OR:
+ if (depth < 1) {
+ yyerror("illegal validatetrans expression");
+ return -1;
+ }
+ depth--;
+ break;
+ case CEXPR_ATTR:
+ case CEXPR_NAMES:
+ if (depth == (CEXPR_MAXDEPTH - 1)) {
+ yyerror("validatetrans expression is too deep");
+ return -1;
+ }
+ depth++;
+ break;
+ default:
+ yyerror("illegal validatetrans expression");
+ return -1;
+ }
+ }
+ if (depth != 0) {
+ yyerror("illegal validatetrans expression");
+ return -1;
+ }
+
+ ebitmap_init(&classmap);
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_CLASSES, id)) {
+ yyerror2("class %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id);
+ if (!cladatum) {
+ yyerror2("class %s is not defined", id);
+ ebitmap_destroy(&classmap);
+ free(id);
+ return -1;
+ }
+ if (ebitmap_set_bit(&classmap, (cladatum->s.value - 1), TRUE)) {
+ yyerror("out of memory");
+ ebitmap_destroy(&classmap);
+ free(id);
+ return -1;
+ }
+
+ node = malloc(sizeof(struct constraint_node));
+ if (!node) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(node, 0, sizeof(constraint_node_t));
+ if (useexpr) {
+ node->expr = expr;
+ useexpr = 0;
+ } else {
+ node->expr = constraint_expr_clone(expr);
+ }
+ node->permissions = 0;
+
+ node->next = cladatum->validatetrans;
+ cladatum->validatetrans = node;
+
+ free(id);
+ }
+
+ ebitmap_destroy(&classmap);
+
+ return 0;
+}
+
+uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2)
+{
+ struct constraint_expr *expr, *e1 = NULL, *e2;
+ user_datum_t *user;
+ role_datum_t *role;
+ ebitmap_t negset;
+ char *id;
+ uint32_t val;
+ int add = 1;
+
+ if (pass == 1) {
+ if (expr_type == CEXPR_NAMES) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ }
+ return 1; /* any non-NULL value */
+ }
+
+ if ((expr = malloc(sizeof(*expr))) == NULL || constraint_expr_init(expr) == -1) {
+ yyerror("out of memory");
+ free(expr);
+ return 0;
+ }
+ expr->expr_type = expr_type;
+
+ switch (expr_type) {
+ case CEXPR_NOT:
+ e1 = NULL;
+ e2 = (struct constraint_expr *)arg1;
+ while (e2) {
+ e1 = e2;
+ e2 = e2->next;
+ }
+ if (!e1 || e1->next) {
+ yyerror("illegal constraint expression");
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ e1->next = expr;
+ return arg1;
+ case CEXPR_AND:
+ case CEXPR_OR:
+ e1 = NULL;
+ e2 = (struct constraint_expr *)arg1;
+ while (e2) {
+ e1 = e2;
+ e2 = e2->next;
+ }
+ if (!e1 || e1->next) {
+ yyerror("illegal constraint expression");
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ e1->next = (struct constraint_expr *)arg2;
+
+ e1 = NULL;
+ e2 = (struct constraint_expr *)arg2;
+ while (e2) {
+ e1 = e2;
+ e2 = e2->next;
+ }
+ if (!e1 || e1->next) {
+ yyerror("illegal constraint expression");
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ e1->next = expr;
+ return arg1;
+ case CEXPR_ATTR:
+ expr->attr = arg1;
+ expr->op = arg2;
+ return (uintptr_t) expr;
+ case CEXPR_NAMES:
+ add = 1;
+ expr->attr = arg1;
+ expr->op = arg2;
+ ebitmap_init(&negset);
+ while ((id = (char *)queue_remove(id_queue))) {
+ if (expr->attr & CEXPR_USER) {
+ if (!is_id_in_scope(SYM_USERS, id)) {
+ yyerror2("user %s is not within scope", id);
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ user = (user_datum_t *) hashtab_search(policydbp->p_users.table, (hashtab_key_t)
+ id);
+ if (!user) {
+ yyerror2("unknown user %s", id);
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ val = user->s.value;
+ } else if (expr->attr & CEXPR_ROLE) {
+ if (!is_id_in_scope(SYM_ROLES, id)) {
+ yyerror2("role %s is not within scope", id);
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ role = (role_datum_t *) hashtab_search(policydbp->p_roles.table, (hashtab_key_t)
+ id);
+ if (!role) {
+ yyerror2("unknown role %s", id);
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ val = role->s.value;
+ } else if (expr->attr & CEXPR_TYPE) {
+ if (set_types(expr->type_names, id, &add, 0)) {
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ continue;
+ } else {
+ yyerror("invalid constraint expression");
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ if (ebitmap_set_bit(&expr->names, val - 1, TRUE)) {
+ yyerror("out of memory");
+ ebitmap_destroy(&expr->names);
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+ free(id);
+ }
+ ebitmap_destroy(&negset);
+ return (uintptr_t) expr;
+ default:
+ yyerror("invalid constraint expression");
+ constraint_expr_destroy(expr);
+ return 0;
+ }
+
+ yyerror("invalid constraint expression");
+ free(expr);
+ return 0;
+}
+
+int define_conditional(cond_expr_t * expr, avrule_t * t, avrule_t * f)
+{
+ cond_expr_t *e;
+ int depth, retval;
+ cond_node_t cn, *cn_old;
+ avrule_t *tmp, *last_tmp;
+
+ /* expression cannot be NULL */
+ if (!expr) {
+ yyerror("illegal conditional expression");
+ return -1;
+ }
+ if (!t) {
+ if (!f) {
+ /* empty is fine, destroy expression and return */
+ cond_expr_destroy(expr);
+ return 0;
+ }
+ /* Invert */
+ t = f;
+ f = 0;
+ expr = define_cond_expr(COND_NOT, expr, 0);
+ if (!expr) {
+ yyerror("unable to invert");
+ return -1;
+ }
+ }
+
+ /* verify expression */
+ depth = -1;
+ for (e = expr; e; e = e->next) {
+ switch (e->expr_type) {
+ case COND_NOT:
+ if (depth < 0) {
+ yyerror("illegal conditional expression; Bad NOT");
+ return -1;
+ }
+ break;
+ case COND_AND:
+ case COND_OR:
+ case COND_XOR:
+ case COND_EQ:
+ case COND_NEQ:
+ if (depth < 1) {
+ yyerror("illegal conditional expression; Bad binary op");
+ return -1;
+ }
+ depth--;
+ break;
+ case COND_BOOL:
+ if (depth == (COND_EXPR_MAXDEPTH - 1)) {
+ yyerror("conditional expression is like totally too deep");
+ return -1;
+ }
+ depth++;
+ break;
+ default:
+ yyerror("illegal conditional expression");
+ return -1;
+ }
+ }
+ if (depth != 0) {
+ yyerror("illegal conditional expression");
+ return -1;
+ }
+
+ /* use tmp conditional node to partially build new node */
+ memset(&cn, 0, sizeof(cn));
+ cn.expr = expr;
+ cn.avtrue_list = t;
+ cn.avfalse_list = f;
+
+ /* normalize/precompute expression */
+ if (cond_normalize_expr(policydbp, &cn) < 0) {
+ yyerror("problem normalizing conditional expression");
+ return -1;
+ }
+
+ /* get the existing conditional node, or create a new one */
+ cn_old = get_current_cond_list(&cn);
+ if (!cn_old) {
+ return -1;
+ }
+
+ /* verify te rules -- both true and false branches of conditional */
+ tmp = cn.avtrue_list;
+ last_tmp = NULL;
+ while (tmp) {
+ if (!tmp->specified & AVRULE_TRANSITION)
+ continue;
+ retval = insert_check_type_rule(tmp, &policydbp->te_cond_avtab, &cn_old->true_list, &cn_old->false_list);
+ switch (retval) {
+ case 1:{
+ last_tmp = tmp;
+ tmp = tmp->next;
+ break;
+ }
+ case 0:{
+ /* rule conflicted, so remove it from consideration */
+ if (last_tmp == NULL) {
+ cn.avtrue_list = cn.avtrue_list->next;
+ avrule_destroy(tmp);
+ free(tmp);
+ tmp = cn.avtrue_list;
+ } else {
+ last_tmp->next = tmp->next;
+ avrule_destroy(tmp);
+ free(tmp);
+ tmp = last_tmp->next;
+ }
+ break;
+ }
+ case -1:{
+ return -1;
+ }
+ case 2:{
+ return 0;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ }
+
+ tmp = cn.avfalse_list;
+ last_tmp = NULL;
+ while (tmp) {
+ if (!tmp->specified & AVRULE_TRANSITION)
+ continue;
+ retval = insert_check_type_rule(tmp, &policydbp->te_cond_avtab, &cn_old->false_list, &cn_old->true_list);
+ switch (retval) {
+ case 1:{
+ last_tmp = tmp;
+ tmp = tmp->next;
+ break;
+ }
+ case 0:{
+ /* rule conflicted, so remove it from consideration */
+ if (last_tmp == NULL) {
+ cn.avfalse_list = cn.avfalse_list->next;
+ avrule_destroy(tmp);
+ free(tmp);
+ tmp = cn.avfalse_list;
+ } else {
+ last_tmp->next = tmp->next;
+ avrule_destroy(tmp);
+ free(tmp);
+ tmp = last_tmp->next;
+ }
+ break;
+ }
+ case -1:{
+ return -1;
+ }
+ case 2:{
+ return 0;
+ }
+ default:{
+ assert(0); /* should never get here */
+ }
+ }
+ }
+
+ append_cond_list(&cn);
+
+ /* note that there is no check here for duplicate rules, nor
+ * check that rule already exists in base -- that will be
+ * handled during conditional expansion, in expand.c */
+
+ cn.avtrue_list = NULL;
+ cn.avfalse_list = NULL;
+ cond_node_destroy(&cn);
+
+ return 0;
+}
+
+cond_expr_t *define_cond_expr(uint32_t expr_type, void *arg1, void *arg2)
+{
+ struct cond_expr *expr, *e1 = NULL, *e2;
+ cond_bool_datum_t *bool_var;
+ char *id;
+
+ /* expressions are handled in the second pass */
+ if (pass == 1) {
+ if (expr_type == COND_BOOL) {
+ while ((id = queue_remove(id_queue))) {
+ free(id);
+ }
+ }
+ return (cond_expr_t *) 1; /* any non-NULL value */
+ }
+
+ /* create a new expression struct */
+ expr = malloc(sizeof(struct cond_expr));
+ if (!expr) {
+ yyerror("out of memory");
+ return NULL;
+ }
+ memset(expr, 0, sizeof(cond_expr_t));
+ expr->expr_type = expr_type;
+
+ /* create the type asked for */
+ switch (expr_type) {
+ case COND_NOT:
+ e1 = NULL;
+ e2 = (struct cond_expr *)arg1;
+ while (e2) {
+ e1 = e2;
+ e2 = e2->next;
+ }
+ if (!e1 || e1->next) {
+ yyerror("illegal conditional NOT expression");
+ free(expr);
+ return NULL;
+ }
+ e1->next = expr;
+ return (struct cond_expr *)arg1;
+ case COND_AND:
+ case COND_OR:
+ case COND_XOR:
+ case COND_EQ:
+ case COND_NEQ:
+ e1 = NULL;
+ e2 = (struct cond_expr *)arg1;
+ while (e2) {
+ e1 = e2;
+ e2 = e2->next;
+ }
+ if (!e1 || e1->next) {
+ yyerror("illegal left side of conditional binary op expression");
+ free(expr);
+ return NULL;
+ }
+ e1->next = (struct cond_expr *)arg2;
+
+ e1 = NULL;
+ e2 = (struct cond_expr *)arg2;
+ while (e2) {
+ e1 = e2;
+ e2 = e2->next;
+ }
+ if (!e1 || e1->next) {
+ yyerror("illegal right side of conditional binary op expression");
+ free(expr);
+ return NULL;
+ }
+ e1->next = expr;
+ return (struct cond_expr *)arg1;
+ case COND_BOOL:
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("bad conditional; expected boolean id");
+ free(id);
+ free(expr);
+ return NULL;
+ }
+ if (!is_id_in_scope(SYM_BOOLS, id)) {
+ yyerror2("boolean %s is not within scope", id);
+ free(id);
+ free(expr);
+ return NULL;
+ }
+ bool_var = (cond_bool_datum_t *) hashtab_search(policydbp->p_bools.table, (hashtab_key_t) id);
+ if (!bool_var) {
+ yyerror2("unknown boolean %s in conditional expression", id);
+ free(expr);
+ free(id);
+ return NULL;
+ }
+ expr->bool = bool_var->s.value;
+ free(id);
+ return expr;
+ default:
+ yyerror("illegal conditional expression");
+ return NULL;
+ }
+}
+
+static int set_user_roles(role_set_t * set, char *id)
+{
+ role_datum_t *r;
+ unsigned int i;
+ ebitmap_node_t *node;
+
+ if (strcmp(id, "*") == 0) {
+ free(id);
+ yyerror("* is not allowed in user declarations");
+ return -1;
+ }
+
+ if (strcmp(id, "~") == 0) {
+ free(id);
+ yyerror("~ is not allowed in user declarations");
+ return -1;
+ }
+
+ if (!is_id_in_scope(SYM_ROLES, id)) {
+ yyerror2("role %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ r = hashtab_search(policydbp->p_roles.table, id);
+ if (!r) {
+ yyerror2("unknown role %s", id);
+ free(id);
+ return -1;
+ }
+
+ /* set the role and every role it dominates */
+ ebitmap_for_each_bit(&r->dominates, node, i) {
+ if (ebitmap_node_get_bit(node, i))
+ if (ebitmap_set_bit(&set->roles, i, TRUE))
+ goto oom;
+ }
+ free(id);
+ return 0;
+ oom:
+ yyerror("out of memory");
+ return -1;
+}
+
+static int parse_categories(char *id, level_datum_t * levdatum, ebitmap_t * cats)
+{
+ cat_datum_t *cdatum;
+ int range_start, range_end, i;
+
+ if (id_has_dot(id)) {
+ char *id_start = id;
+ char *id_end = strchr(id, '.');
+
+ *(id_end++) = '\0';
+
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t)
+ id_start);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id_start);
+ return -1;
+ }
+ range_start = cdatum->s.value - 1;
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id_end);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id_end);
+ return -1;
+ }
+ range_end = cdatum->s.value - 1;
+
+ if (range_end < range_start) {
+ yyerror2("category range is invalid");
+ return -1;
+ }
+ } else {
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id);
+ return -1;
+ }
+ range_start = range_end = cdatum->s.value - 1;
+ }
+
+ for (i = range_start; i <= range_end; i++) {
+ if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
+ uint32_t level_value = levdatum->level->sens - 1;
+ policydb_index_others(NULL, policydbp, 0);
+ yyerror2("category %s can not be associated "
+ "with level %s", policydbp->p_cat_val_to_name[i], policydbp->p_sens_val_to_name[level_value]);
+ return -1;
+ }
+ if (ebitmap_set_bit(cats, i, TRUE)) {
+ yyerror("out of memory");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_semantic_categories(char *id, level_datum_t * levdatum, mls_semantic_cat_t ** cats)
+{
+ cat_datum_t *cdatum;
+ mls_semantic_cat_t *newcat;
+ unsigned int range_start, range_end;
+
+ if (id_has_dot(id)) {
+ char *id_start = id;
+ char *id_end = strchr(id, '.');
+
+ *(id_end++) = '\0';
+
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t)
+ id_start);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id_start);
+ return -1;
+ }
+ range_start = cdatum->s.value;
+
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id_end);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id_end);
+ return -1;
+ }
+ range_end = cdatum->s.value;
+ } else {
+ cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id);
+ if (!cdatum) {
+ yyerror2("unknown category %s", id);
+ return -1;
+ }
+ range_start = range_end = cdatum->s.value;
+ }
+
+ newcat = (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
+ if (!newcat) {
+ yyerror("out of memory");
+ return -1;
+ }
+
+ mls_semantic_cat_init(newcat);
+ newcat->next = *cats;
+ newcat->low = range_start;
+ newcat->high = range_end;
+
+ *cats = newcat;
+
+ return 0;
+}
+
+int define_user(void)
+{
+ char *id;
+ user_datum_t *usrdatum;
+ level_datum_t *levdatum;
+ int l;
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ if (mlspol) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ id = queue_remove(id_queue);
+ free(id);
+ for (l = 0; l < 2; l++) {
+ while ((id = queue_remove(id_queue))) {
+ free(id);
+ }
+ id = queue_remove(id_queue);
+ if (!id)
+ break;
+ free(id);
+ }
+ }
+ return 0;
+ }
+
+ if ((usrdatum = declare_user()) == NULL) {
+ return -1;
+ }
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_user_roles(&usrdatum->roles, id))
+ continue;
+ }
+
+ if (mlspol) {
+ id = queue_remove(id_queue);
+ if (!id) {
+ yyerror("no default level specified for user");
+ return -1;
+ }
+
+ levdatum = (level_datum_t *)
+ hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id);
+ if (!levdatum) {
+ yyerror2("unknown sensitivity %s used in user" " level definition", id);
+ free(id);
+ return -1;
+ }
+ free(id);
+
+ usrdatum->dfltlevel.sens = levdatum->level->sens;
+
+ while ((id = queue_remove(id_queue))) {
+ if (parse_semantic_categories(id, levdatum, &usrdatum->dfltlevel.cat)) {
+ free(id);
+ return -1;
+ }
+ free(id);
+ }
+
+ id = queue_remove(id_queue);
+
+ for (l = 0; l < 2; l++) {
+ levdatum = (level_datum_t *)
+ hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id);
+ if (!levdatum) {
+ yyerror2("unknown sensitivity %s used in user" " range definition", id);
+ free(id);
+ return -1;
+ }
+ free(id);
+
+ usrdatum->range.level[l].sens = levdatum->level->sens;
+
+ while ((id = queue_remove(id_queue))) {
+ if (parse_semantic_categories(id, levdatum, &usrdatum->range.level[l].cat)) {
+ free(id);
+ return -1;
+ }
+ free(id);
+ }
+
+ id = queue_remove(id_queue);
+ if (!id)
+ break;
+ }
+
+ if (l == 0) {
+ if (mls_semantic_level_cpy(&usrdatum->range.level[1], &usrdatum->range.level[0])) {
+ yyerror("out of memory");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int parse_security_context(context_struct_t * c)
+{
+ char *id;
+ role_datum_t *role;
+ type_datum_t *typdatum;
+ user_datum_t *usrdatum;
+ level_datum_t *levdatum;
+ int l;
+
+ if (pass == 1) {
+ id = queue_remove(id_queue);
+ free(id); /* user */
+ id = queue_remove(id_queue);
+ free(id); /* role */
+ id = queue_remove(id_queue);
+ free(id); /* type */
+ if (mlspol) {
+ id = queue_remove(id_queue);
+ free(id);
+ for (l = 0; l < 2; l++) {
+ while ((id = queue_remove(id_queue))) {
+ free(id);
+ }
+ id = queue_remove(id_queue);
+ if (!id)
+ break;
+ free(id);
+ }
+ }
+ return 0;
+ }
+
+ context_init(c);
+
+ /* extract the user */
+ id = queue_remove(id_queue);
+ if (!id) {
+ yyerror("no effective user?");
+ goto bad;
+ }
+ if (!is_id_in_scope(SYM_USERS, id)) {
+ yyerror2("user %s is not within scope", id);
+ free(id);
+ goto bad;
+ }
+ usrdatum = (user_datum_t *) hashtab_search(policydbp->p_users.table, (hashtab_key_t) id);
+ if (!usrdatum) {
+ yyerror2("user %s is not defined", id);
+ free(id);
+ goto bad;
+ }
+ c->user = usrdatum->s.value;
+
+ /* no need to keep the user name */
+ free(id);
+
+ /* extract the role */
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no role name for sid context definition?");
+ return -1;
+ }
+ if (!is_id_in_scope(SYM_ROLES, id)) {
+ yyerror2("role %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ role = (role_datum_t *) hashtab_search(policydbp->p_roles.table, (hashtab_key_t) id);
+ if (!role) {
+ yyerror2("role %s is not defined", id);
+ free(id);
+ return -1;
+ }
+ c->role = role->s.value;
+
+ /* no need to keep the role name */
+ free(id);
+
+ /* extract the type */
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no type name for sid context definition?");
+ return -1;
+ }
+ if (!is_id_in_scope(SYM_TYPES, id)) {
+ yyerror2("type %s is not within scope", id);
+ free(id);
+ return -1;
+ }
+ typdatum = (type_datum_t *) hashtab_search(policydbp->p_types.table, (hashtab_key_t) id);
+ if (!typdatum || typdatum->flavor == TYPE_ATTRIB) {
+ yyerror2("type %s is not defined or is an attribute", id);
+ free(id);
+ return -1;
+ }
+ c->type = typdatum->s.value;
+
+ /* no need to keep the type name */
+ free(id);
+
+ if (mlspol) {
+ /* extract the low sensitivity */
+ id = (char *)queue_head(id_queue);
+ if (!id) {
+ yyerror("no sensitivity name for sid context" " definition?");
+ return -1;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ for (l = 0; l < 2; l++) {
+ levdatum = (level_datum_t *)
+ hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id);
+ if (!levdatum) {
+ yyerror2("Sensitivity %s is not defined", id);
+ free(id);
+ return -1;
+ }
+ free(id);
+ c->range.level[l].sens = levdatum->level->sens;
+
+ /* extract low category set */
+ while ((id = queue_remove(id_queue))) {
+ if (parse_categories(id, levdatum, &c->range.level[l].cat)) {
+ free(id);
+ return -1;
+ }
+ free(id);
+ }
+
+ /* extract high sensitivity */
+ id = (char *)queue_remove(id_queue);
+ if (!id)
+ break;
+ }
+
+ if (l == 0) {
+ c->range.level[1].sens = c->range.level[0].sens;
+ if (ebitmap_cpy(&c->range.level[1].cat, &c->range.level[0].cat)) {
+
+ yyerror("out of memory");
+ goto bad;
+ }
+ }
+ }
+
+ if (!policydb_context_isvalid(policydbp, c)) {
+ yyerror("invalid security context");
+ goto bad;
+ }
+ return 0;
+
+ bad:
+ context_destroy(c);
+
+ return -1;
+}
+
+int define_initial_sid_context(void)
+{
+ char *id;
+ ocontext_t *c, *head;
+
+ if (pass == 1) {
+ id = (char *)queue_remove(id_queue);
+ free(id);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no sid name for SID context definition?");
+ return -1;
+ }
+ head = policydbp->ocontexts[OCON_ISID];
+ for (c = head; c; c = c->next) {
+ if (!strcmp(id, c->u.name))
+ break;
+ }
+
+ if (!c) {
+ yyerror2("SID %s is not defined", id);
+ free(id);
+ return -1;
+ }
+ if (c->context[0].user) {
+ yyerror2("The context for SID %s is multiply defined", id);
+ free(id);
+ return -1;
+ }
+ /* no need to keep the sid name */
+ free(id);
+
+ if (parse_security_context(&c->context[0]))
+ return -1;
+
+ return 0;
+}
+
+int define_fs_context(unsigned int major, unsigned int minor)
+{
+ ocontext_t *newc, *c, *head;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("fscon not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ parse_security_context(NULL);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = (ocontext_t *) malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.name = (char *)malloc(6);
+ if (!newc->u.name) {
+ yyerror("out of memory");
+ free(newc);
+ return -1;
+ }
+ sprintf(newc->u.name, "%02x:%02x", major, minor);
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ if (parse_security_context(&newc->context[1])) {
+ context_destroy(&newc->context[0]);
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ head = policydbp->ocontexts[OCON_FS];
+
+ for (c = head; c; c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name)) {
+ yyerror2("duplicate entry for file system %s", newc->u.name);
+ context_destroy(&newc->context[0]);
+ context_destroy(&newc->context[1]);
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ }
+
+ newc->next = head;
+ policydbp->ocontexts[OCON_FS] = newc;
+
+ return 0;
+}
+
+int define_pirq_context(unsigned int pirq)
+{
+ ocontext_t *newc, *c, *l, *head;
+ char *id;
+
+#ifndef SEPOL_TARGET_XEN
+ yyerror("pirqcon not supported for target");
+ return -1;
+#else
+ if (policydbp->target_platform != SEPOL_TARGET_XEN) {
+ yyerror("pirqcon not supported for target");
+ return -1;
+ }
+
+ if (pass == 1) {
+ id = (char *)queue_remove(id_queue);
+ free(id);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.pirq = pirq;
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ return -1;
+ }
+
+ head = policydbp->ocontexts[OCON_XEN_PIRQ];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ unsigned int pirq2;
+
+ pirq2 = c->u.pirq;
+ if (pirq == pirq2) {
+ yyerror2("duplicate pirqcon entry for %d ", pirq);
+ goto bad;
+ }
+ }
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_XEN_PIRQ] = newc;
+
+ return 0;
+
+ bad:
+ free(newc);
+ return -1;
+#endif
+}
+
+int define_iomem_context(unsigned long low, unsigned long high)
+{
+ ocontext_t *newc, *c, *l, *head;
+ char *id;
+
+#ifndef SEPOL_TARGET_XEN
+ yyerror("iomemcon not supported for target");
+ return -1;
+#else
+ if (policydbp->target_platform != SEPOL_TARGET_XEN) {
+ yyerror("iomemcon not supported for target");
+ return -1;
+ }
+
+ if (pass == 1) {
+ id = (char *)queue_remove(id_queue);
+ free(id);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.iomem.low_iomem = low;
+ newc->u.iomem.high_iomem = high;
+
+ if (low > high) {
+ yyerror2("low memory 0x%x exceeds high memory 0x%x", low, high);
+ free(newc);
+ return -1;
+ }
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ return -1;
+ }
+
+ head = policydbp->ocontexts[OCON_XEN_IOMEM];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ unsigned int low2, high2;
+
+ low2 = c->u.iomem.low_iomem;
+ high2 = c->u.iomem.high_iomem;
+ if (low <= high2 && low2 <= high) {
+ yyerror2("iomemcon entry for 0x%x-0x%x overlaps with " "earlier entry 0x%x-0x%x", low, high, low2, high2);
+ goto bad;
+ }
+ }
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_XEN_IOMEM] = newc;
+
+ return 0;
+
+ bad:
+ free(newc);
+ return -1;
+#endif
+}
+
+int define_ioport_context(unsigned long low, unsigned long high)
+{
+ ocontext_t *newc, *c, *l, *head;
+ char *id;
+
+#ifndef SEPOL_TARGET_XEN
+ yyerror("ioportcon not supported for target");
+ return -1;
+#else
+ if (policydbp->target_platform != SEPOL_TARGET_XEN) {
+ yyerror("ioportcon not supported for target");
+ return -1;
+ }
+
+ if (pass == 1) {
+ id = (char *)queue_remove(id_queue);
+ free(id);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.ioport.low_ioport = low;
+ newc->u.ioport.high_ioport = high;
+
+ if (low > high) {
+ yyerror2("low ioport 0x%x exceeds high ioport 0x%x", low, high);
+ free(newc);
+ return -1;
+ }
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ return -1;
+ }
+
+ head = policydbp->ocontexts[OCON_XEN_IOPORT];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ unsigned int low2, high2;
+
+ low2 = c->u.ioport.low_ioport;
+ high2 = c->u.ioport.high_ioport;
+ if (low <= high2 && low2 <= high) {
+ yyerror2("ioportcon entry for 0x%x-0x%x overlaps with" "earlier entry 0x%x-0x%x", low, high, low2, high2);
+ goto bad;
+ }
+ }
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_XEN_IOPORT] = newc;
+
+ return 0;
+
+ bad:
+ free(newc);
+ return -1;
+#endif
+}
+
+int define_pcidevice_context(unsigned long device)
+{
+ ocontext_t *newc, *c, *l, *head;
+ char *id;
+
+#ifndef SEPOL_TARGET_XEN
+ yyerror("pcidevicecon not supported for target");
+ return -1;
+#else
+ if (policydbp->target_platform != SEPOL_TARGET_XEN) {
+ yyerror("pcidevicecon not supported for target");
+ return -1;
+ }
+
+ if (pass == 1) {
+ id = (char *)queue_remove(id_queue);
+ free(id);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.device = device;
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ return -1;
+ }
+
+ head = policydbp->ocontexts[OCON_XEN_PCIDEVICE];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ unsigned int device2;
+
+ device2 = c->u.device;
+ if (device == device2) {
+ yyerror2("duplicate pcidevicecon entry for 0x%x ", device);
+ goto bad;
+ }
+ }
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_XEN_PCIDEVICE] = newc;
+
+ return 0;
+
+ bad:
+ free(newc);
+ return -1;
+#endif
+}
+
+int define_port_context(unsigned int low, unsigned int high)
+{
+ ocontext_t *newc, *c, *l, *head;
+ unsigned int protocol;
+ char *id;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("portcon not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ id = (char *)queue_remove(id_queue);
+ free(id);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ free(newc);
+ return -1;
+ }
+ if ((strcmp(id, "tcp") == 0) || (strcmp(id, "TCP") == 0)) {
+ protocol = IPPROTO_TCP;
+ } else if ((strcmp(id, "udp") == 0) || (strcmp(id, "UDP") == 0)) {
+ protocol = IPPROTO_UDP;
+ } else {
+ yyerror2("unrecognized protocol %s", id);
+ free(newc);
+ return -1;
+ }
+
+ newc->u.port.protocol = protocol;
+ newc->u.port.low_port = low;
+ newc->u.port.high_port = high;
+
+ if (low > high) {
+ yyerror2("low port %d exceeds high port %d", low, high);
+ free(newc);
+ return -1;
+ }
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ return -1;
+ }
+
+ /* Preserve the matching order specified in the configuration. */
+ head = policydbp->ocontexts[OCON_PORT];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ unsigned int prot2, low2, high2;
+
+ prot2 = c->u.port.protocol;
+ low2 = c->u.port.low_port;
+ high2 = c->u.port.high_port;
+ if (protocol != prot2)
+ continue;
+ if (low == low2 && high == high2) {
+ yyerror2("duplicate portcon entry for %s %d-%d ", id, low, high);
+ goto bad;
+ }
+ if (low2 <= low && high2 >= high) {
+ yyerror2("portcon entry for %s %d-%d hidden by earlier " "entry for %d-%d", id, low, high, low2, high2);
+ goto bad;
+ }
+ }
+ free(id);
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_PORT] = newc;
+
+ return 0;
+
+ bad:
+ free(newc);
+ return -1;
+}
+
+int define_netif_context(void)
+{
+ ocontext_t *newc, *c, *head;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("netifcon not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ free(queue_remove(id_queue));
+ parse_security_context(NULL);
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = (ocontext_t *) malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.name = (char *)queue_remove(id_queue);
+ if (!newc->u.name) {
+ free(newc);
+ return -1;
+ }
+ if (parse_security_context(&newc->context[0])) {
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ if (parse_security_context(&newc->context[1])) {
+ context_destroy(&newc->context[0]);
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ head = policydbp->ocontexts[OCON_NETIF];
+
+ for (c = head; c; c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name)) {
+ yyerror2("duplicate entry for network interface %s", newc->u.name);
+ context_destroy(&newc->context[0]);
+ context_destroy(&newc->context[1]);
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ }
+
+ newc->next = head;
+ policydbp->ocontexts[OCON_NETIF] = newc;
+ return 0;
+}
+
+int define_ipv4_node_context()
+{
+ char *id;
+ int rc = 0;
+ struct in_addr addr, mask;
+ ocontext_t *newc, *c, *l, *head;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("nodecon not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ free(queue_remove(id_queue));
+ free(queue_remove(id_queue));
+ parse_security_context(NULL);
+ goto out;
+ }
+
+ id = queue_remove(id_queue);
+ if (!id) {
+ yyerror("failed to read ipv4 address");
+ rc = -1;
+ goto out;
+ }
+
+ rc = inet_pton(AF_INET, id, &addr);
+ free(id);
+ if (rc < 1) {
+ yyerror("failed to parse ipv4 address");
+ if (rc == 0)
+ rc = -1;
+ goto out;
+ }
+
+ id = queue_remove(id_queue);
+ if (!id) {
+ yyerror("failed to read ipv4 address");
+ rc = -1;
+ goto out;
+ }
+
+ rc = inet_pton(AF_INET, id, &mask);
+ free(id);
+ if (rc < 1) {
+ yyerror("failed to parse ipv4 mask");
+ if (rc == 0)
+ rc = -1;
+ goto out;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ rc = -1;
+ goto out;
+ }
+
+ memset(newc, 0, sizeof(ocontext_t));
+ newc->u.node.addr = addr.s_addr;
+ newc->u.node.mask = mask.s_addr;
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ return -1;
+ }
+
+ /* Create order of most specific to least retaining
+ the order specified in the configuration. */
+ head = policydbp->ocontexts[OCON_NODE];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ if (newc->u.node.mask > c->u.node.mask)
+ break;
+ }
+
+ newc->next = c;
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_NODE] = newc;
+ rc = 0;
+ out:
+ return rc;
+}
+
+int define_ipv6_node_context(void)
+{
+ char *id;
+ int rc = 0;
+ struct in6_addr addr, mask;
+ ocontext_t *newc, *c, *l, *head;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("nodecon not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ free(queue_remove(id_queue));
+ free(queue_remove(id_queue));
+ parse_security_context(NULL);
+ goto out;
+ }
+
+ id = queue_remove(id_queue);
+ if (!id) {
+ yyerror("failed to read ipv6 address");
+ rc = -1;
+ goto out;
+ }
+
+ rc = inet_pton(AF_INET6, id, &addr);
+ free(id);
+ if (rc < 1) {
+ yyerror("failed to parse ipv6 address");
+ if (rc == 0)
+ rc = -1;
+ goto out;
+ }
+
+ id = queue_remove(id_queue);
+ if (!id) {
+ yyerror("failed to read ipv6 address");
+ rc = -1;
+ goto out;
+ }
+
+ rc = inet_pton(AF_INET6, id, &mask);
+ free(id);
+ if (rc < 1) {
+ yyerror("failed to parse ipv6 mask");
+ if (rc == 0)
+ rc = -1;
+ goto out;
+ }
+
+ newc = malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ rc = -1;
+ goto out;
+ }
+
+ memset(newc, 0, sizeof(ocontext_t));
+ memcpy(&newc->u.node6.addr[0], &addr.s6_addr32[0], 16);
+ memcpy(&newc->u.node6.mask[0], &mask.s6_addr32[0], 16);
+
+ if (parse_security_context(&newc->context[0])) {
+ free(newc);
+ rc = -1;
+ goto out;
+ }
+
+ /* Create order of most specific to least retaining
+ the order specified in the configuration. */
+ head = policydbp->ocontexts[OCON_NODE6];
+ for (l = NULL, c = head; c; l = c, c = c->next) {
+ if (memcmp(&newc->u.node6.mask, &c->u.node6.mask, 16) > 0)
+ break;
+ }
+
+ newc->next = c;
+
+ if (l)
+ l->next = newc;
+ else
+ policydbp->ocontexts[OCON_NODE6] = newc;
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+int define_fs_use(int behavior)
+{
+ ocontext_t *newc, *c, *head;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("fsuse not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ free(queue_remove(id_queue));
+ if (behavior != SECURITY_FS_USE_PSIDS)
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ newc = (ocontext_t *) malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.name = (char *)queue_remove(id_queue);
+ if (!newc->u.name) {
+ free(newc);
+ return -1;
+ }
+ newc->v.behavior = behavior;
+ if (newc->v.behavior != SECURITY_FS_USE_PSIDS) {
+ if (parse_security_context(&newc->context[0])) {
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ } else
+ memset(&newc->context[0], 0, sizeof(context_struct_t) * 2);
+
+ head = policydbp->ocontexts[OCON_FSUSE];
+
+ for (c = head; c; c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name)) {
+ yyerror2("duplicate fs_use entry for filesystem type %s", newc->u.name);
+ context_destroy(&newc->context[0]);
+ free(newc->u.name);
+ free(newc);
+ return -1;
+ }
+ }
+
+ newc->next = head;
+ policydbp->ocontexts[OCON_FSUSE] = newc;
+ return 0;
+}
+
+int define_genfs_context_helper(char *fstype, int has_type)
+{
+ struct genfs *genfs_p, *genfs, *newgenfs;
+ ocontext_t *newc, *c, *head, *p;
+ char *type = NULL;
+ int len, len2;
+
+#ifdef SEPOL_TARGET_XEN
+ if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
+ yyerror("genfs not supported for target");
+ return -1;
+ }
+#endif
+
+ if (pass == 1) {
+ free(fstype);
+ free(queue_remove(id_queue));
+ if (has_type)
+ free(queue_remove(id_queue));
+ parse_security_context(NULL);
+ return 0;
+ }
+
+ for (genfs_p = NULL, genfs = policydbp->genfs; genfs; genfs_p = genfs, genfs = genfs->next) {
+ if (strcmp(fstype, genfs->fstype) <= 0)
+ break;
+ }
+
+ if (!genfs || strcmp(fstype, genfs->fstype)) {
+ newgenfs = malloc(sizeof(struct genfs));
+ if (!newgenfs) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newgenfs, 0, sizeof(struct genfs));
+ newgenfs->fstype = fstype;
+ newgenfs->next = genfs;
+ if (genfs_p)
+ genfs_p->next = newgenfs;
+ else
+ policydbp->genfs = newgenfs;
+ genfs = newgenfs;
+ } else {
+ free(fstype);
+ fstype=NULL;
+ }
+
+ newc = (ocontext_t *) malloc(sizeof(ocontext_t));
+ if (!newc) {
+ yyerror("out of memory");
+ return -1;
+ }
+ memset(newc, 0, sizeof(ocontext_t));
+
+ newc->u.name = (char *)queue_remove(id_queue);
+ if (!newc->u.name)
+ goto fail;
+ if (has_type) {
+ type = (char *)queue_remove(id_queue);
+ if (!type)
+ goto fail;
+ if (type[1] != 0) {
+ yyerror2("invalid type %s", type);
+ goto fail;
+ }
+ switch (type[0]) {
+ case 'b':
+ newc->v.sclass = SECCLASS_BLK_FILE;
+ break;
+ case 'c':
+ newc->v.sclass = SECCLASS_CHR_FILE;
+ break;
+ case 'd':
+ newc->v.sclass = SECCLASS_DIR;
+ break;
+ case 'p':
+ newc->v.sclass = SECCLASS_FIFO_FILE;
+ break;
+ case 'l':
+ newc->v.sclass = SECCLASS_LNK_FILE;
+ break;
+ case 's':
+ newc->v.sclass = SECCLASS_SOCK_FILE;
+ break;
+ case '-':
+ newc->v.sclass = SECCLASS_FILE;
+ break;
+ default:
+ yyerror2("invalid type %s", type);
+ goto fail;
+ }
+ }
+ free(type);
+ type = NULL;
+ if (parse_security_context(&newc->context[0]))
+ goto fail;
+
+ head = genfs->head;
+
+ for (p = NULL, c = head; c; p = c, c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name) && (!newc->v.sclass || !c->v.sclass || newc->v.sclass == c->v.sclass)) {
+ yyerror2("duplicate entry for genfs entry (%s, %s)", fstype, newc->u.name);
+ goto fail;
+ }
+ len = strlen(newc->u.name);
+ len2 = strlen(c->u.name);
+ if (len > len2)
+ break;
+ }
+
+ newc->next = c;
+ if (p)
+ p->next = newc;
+ else
+ genfs->head = newc;
+ return 0;
+ fail:
+ if (type)
+ free(type);
+ context_destroy(&newc->context[0]);
+ if (fstype)
+ free(fstype);
+ if (newc->u.name)
+ free(newc->u.name);
+ free(newc);
+ return -1;
+}
+
+int define_genfs_context(int has_type)
+{
+ return define_genfs_context_helper(queue_remove(id_queue), has_type);
+}
+
+int define_range_trans(int class_specified)
+{
+ char *id;
+ level_datum_t *levdatum = 0;
+ class_datum_t *cladatum;
+ range_trans_rule_t *rule;
+ int l, add = 1;
+
+ if (!mlspol) {
+ yyerror("range_transition rule in non-MLS configuration");
+ return -1;
+ }
+
+ if (pass == 1) {
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ if (class_specified)
+ while ((id = queue_remove(id_queue)))
+ free(id);
+ id = queue_remove(id_queue);
+ free(id);
+ for (l = 0; l < 2; l++) {
+ while ((id = queue_remove(id_queue))) {
+ free(id);
+ }
+ id = queue_remove(id_queue);
+ if (!id)
+ break;
+ free(id);
+ }
+ return 0;
+ }
+
+ rule = malloc(sizeof(struct range_trans_rule));
+ if (!rule) {
+ yyerror("out of memory");
+ return -1;
+ }
+ range_trans_rule_init(rule);
+
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&rule->stypes, id, &add, 0))
+ goto out;
+ }
+ add = 1;
+ while ((id = queue_remove(id_queue))) {
+ if (set_types(&rule->ttypes, id, &add, 0))
+ goto out;
+ }
+
+ if (class_specified) {
+ while ((id = queue_remove(id_queue))) {
+ if (!is_id_in_scope(SYM_CLASSES, id)) {
+ yyerror2("class %s is not within scope", id);
+ free(id);
+ goto out;
+ }
+ cladatum = hashtab_search(policydbp->p_classes.table, id);
+ if (!cladatum) {
+ yyerror2("unknown class %s", id);
+ goto out;
+ }
+
+ ebitmap_set_bit(&rule->tclasses, cladatum->s.value - 1, TRUE);
+ free(id);
+ }
+ } else {
+ cladatum = hashtab_search(policydbp->p_classes.table, "process");
+ if (!cladatum) {
+ yyerror2("could not find process class for " "legacy range_transition statement");
+ goto out;
+ }
+
+ ebitmap_set_bit(&rule->tclasses, cladatum->s.value - 1, TRUE);
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id) {
+ yyerror("no range in range_transition definition?");
+ goto out;
+ }
+ for (l = 0; l < 2; l++) {
+ levdatum = hashtab_search(policydbp->p_levels.table, id);
+ if (!levdatum) {
+ yyerror2("unknown level %s used in range_transition " "definition", id);
+ free(id);
+ goto out;
+ }
+ free(id);
+
+ rule->trange.level[l].sens = levdatum->level->sens;
+
+ while ((id = queue_remove(id_queue))) {
+ if (parse_semantic_categories(id, levdatum, &rule->trange.level[l].cat)) {
+ free(id);
+ goto out;
+ }
+ free(id);
+ }
+
+ id = (char *)queue_remove(id_queue);
+ if (!id)
+ break;
+ }
+ if (l == 0) {
+ if (mls_semantic_level_cpy(&rule->trange.level[1], &rule->trange.level[0])) {
+ yyerror("out of memory");
+ goto out;
+ }
+ }
+
+ append_range_trans(rule);
+ return 0;
+
+ out:
+ range_trans_rule_destroy(rule);
+ return -1;
+}
+
+/* FLASK */
diff --git a/libqpol/src/policy_define.h b/libqpol/src/policy_define.h
new file mode 100644
index 0000000..7be626c
--- /dev/null
+++ b/libqpol/src/policy_define.h
@@ -0,0 +1,75 @@
+/**
+ * @file policy_define.h
+ * This file is based upon checkpolicy/policy_define.h from NSA's SVN
+ * repository.
+ */
+
+/* Functions used to define policy grammar components. */
+
+#ifndef _POLICY_DEFINE_H_
+#define _POLICY_DEFINE_H_
+
+/*
+ * We need the following so we have a valid error return code in yacc
+ * when we have a parse error for a conditional rule. We can't check
+ * for NULL (ie 0) because that is a potentially valid return.
+ */
+#define COND_ERR ((avrule_t *)-1)
+
+#define TRUE 1
+#define FALSE 0
+
+/** parser used to support fs_use_psid declarations, so revert that bit
+ * of code here */
+#define SECURITY_FS_USE_PSIDS 6
+
+avrule_t *define_cond_compute_type(int which);
+avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * stmt);
+avrule_t *define_cond_te_avtab(int which);
+cond_expr_t *define_cond_expr(uint32_t expr_type, void *arg1, void *arg2);
+int define_attrib(void);
+int define_av_perms(int inherits);
+int define_bool(void);
+int define_category(void);
+int define_class(void);
+int define_common_perms(void);
+int define_compute_type(int which);
+int define_conditional(cond_expr_t * expr, avrule_t * t_list, avrule_t * f_list);
+int define_constraint(constraint_expr_t * expr);
+int define_dominance(void);
+int define_fs_context(unsigned int major, unsigned int minor);
+int define_fs_use(int behavior);
+int define_genfs_context(int has_type);
+int define_initial_sid_context(void);
+int define_initial_sid(void);
+int define_ipv4_node_context(void);
+int define_ipv6_node_context(void);
+int define_level(void);
+int define_mls(void);
+int define_netif_context(void);
+int define_permissive(void);
+int define_polcap(void);
+int define_port_context(unsigned int low, unsigned int high);
+int define_pirq_context(unsigned int pirq);
+int define_iomem_context(unsigned long low, unsigned long high);
+int define_ioport_context(unsigned long low, unsigned long high);
+int define_pcidevice_context(unsigned long device);
+int define_range_trans(int class_specified);
+int define_role_allow(void);
+int define_role_trans(void);
+int define_role_types(void);
+int define_sens(void);
+int define_te_avtab(int which);
+int define_typealias(void);
+int define_typeattribute(void);
+int define_typebounds(void);
+int define_type(int alias);
+int define_user(void);
+int define_validatetrans(constraint_expr_t * expr);
+int insert_id(char *id, int push);
+int insert_separator(int push);
+role_datum_t *define_role_dom(role_datum_t * r);
+role_datum_t *merge_roles_dom(role_datum_t * r1, role_datum_t * r2);
+uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2);
+
+#endif /* _POLICY_DEFINE_H_ */
diff --git a/libqpol/src/policy_extend.c b/libqpol/src/policy_extend.c
new file mode 100644
index 0000000..5325a87
--- /dev/null
+++ b/libqpol/src/policy_extend.c
@@ -0,0 +1,1397 @@
+/**
+ * @file
+ * Implementation of the interface for loading and using an extended
+ * policy image.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/conditional.h>
+#include <sepol/policydb/avtab.h>
+#include <sepol/policydb/hashtab.h>
+#include <sepol/policydb/flask.h>
+#include <sepol/policydb/ebitmap.h>
+#include <sepol/policydb/expand.h>
+#ifdef HAVE_SEPOL_ERRCODES
+#include <sepol/errcodes.h>
+#endif
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include <qpol/iterator.h>
+#include <selinux/selinux.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+#include "syn_rule_internal.h"
+
+#ifdef SETOOLS_DEBUG
+#include <math.h>
+#endif
+
+#define OBJECT_R "object_r"
+
+#define QPOL_SYN_RULE_TABLE_BITS 16
+#define QPOL_SYN_RULE_TABLE_SIZE (1 << QPOL_SYN_RULE_TABLE_BITS)
+#define QPOL_SYN_RULE_TABLE_MASK (QPOL_SYN_RULE_TABLE_SIZE - 1)
+
+/* original hashing function below */
+/*
+#define QPOL_SYN_RULE_TABLE_HASH(rule_key) \
+((rule_key->class_val + \
+ (rule_key->target_val << 2) +\
+ (rule_key->source_val << 9)) & \
+ QPOL_SYN_RULE_TABLE_MASK)
+*/
+
+/* new hashing function, introduced in SETools 3.3 */
+#define QPOL_SYN_RULE_TABLE_HASH(rule_key) \
+(((((rule_key->source_val & 0xff) << 8) | (rule_key->target_val & 0xff)) ^ \
+ (rule_key->class_val & 0xf) ^ \
+ ((int) ((size_t) rule_key->cond) & 0xfff0)) & QPOL_SYN_RULE_TABLE_MASK)
+
+typedef struct qpol_syn_rule_key
+{
+ uint32_t rule_type;
+ uint32_t source_val;
+ uint32_t target_val;
+ uint32_t class_val;
+ cond_node_t *cond;
+} qpol_syn_rule_key_t;
+
+typedef struct qpol_syn_rule_list
+{
+ struct qpol_syn_rule *rule;
+ struct qpol_syn_rule_list *next;
+} qpol_syn_rule_list_t;
+
+typedef struct qpol_syn_rule_node
+{
+ qpol_syn_rule_key_t key;
+ qpol_syn_rule_list_t *rules;
+ struct qpol_syn_rule_node *next;
+} qpol_syn_rule_node_t;
+
+typedef struct qpol_syn_rule_table
+{
+ qpol_syn_rule_node_t **buckets;
+} qpol_syn_rule_table_t;
+
+typedef struct qpol_extended_image
+{
+ qpol_syn_rule_table_t *syn_rule_table;
+ struct qpol_syn_rule **syn_rule_master_list;
+ size_t master_list_sz;
+} qpol_extended_image_t;
+
+struct extend_bogus_alias_struct
+{
+ qpol_policy_t *q;
+ int num_bogus_aliases;
+};
+
+static int extend_find_bogus_alias(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *args)
+{
+ struct extend_bogus_alias_struct *e = (struct extend_bogus_alias_struct *)args;
+ /* within libqpol, qpol_type_t is the same a libsepol's type_datum_t */
+ qpol_type_t *qtype = (qpol_type_t *) datum;
+ type_datum_t *type = (type_datum_t *) datum;
+ unsigned char isalias;
+ qpol_type_get_isalias(e->q, qtype, &isalias);
+ return isalias && type->s.value == 0;
+}
+
+static void extend_remove_bogus_alias(hashtab_key_t key, hashtab_datum_t datum, void *args)
+{
+ struct extend_bogus_alias_struct *e = (struct extend_bogus_alias_struct *)args;
+ free(key);
+ type_datum_t *type = (type_datum_t *) datum;
+ type_datum_destroy(type);
+ free(type);
+ e->num_bogus_aliases++;
+}
+
+/**
+ * Search the policy for aliases that have a value of 0. These come
+ * from modular policies with disabled aliases, but end up being
+ * written to the policy anyways due to a bug in libsepol. These
+ * bogus aliases are removed from the policy.
+ * @param policy Policy that may contain broken aliases. This policy
+ * will be altered by this function.
+ * @return 0 on success and < 0 on failure; if the call fails, errno
+ * will be set. On failure, the policy state may be inconsistent.
+ */
+static int qpol_policy_remove_bogus_aliases(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ struct extend_bogus_alias_struct e = { policy, 0 };
+ hashtab_map_remove_on_error(db->p_types.table, extend_find_bogus_alias, extend_remove_bogus_alias, &e);
+
+#ifdef SETOOLS_DEBUG
+ if (e.num_bogus_aliases > 0) {
+ WARN(policy, "%s", "This policy contained disabled aliases; they have been removed.");
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * Builds data for the attributes and inserts them into the policydb.
+ * This function modifies the policydb. Names created for attributes
+ * are of the form @ttr<value> where value is the value of the attribute
+ * as a four digit number (prepended with 0's as needed).
+ * @param policy The policy from which to read the attribute map and
+ * create the type data for the attributes. This policy will be altered
+ * by this function.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set. On failure, the policy state may be inconsistent
+ * especially in the case where the hashtab functions return the error.
+ */
+static int qpol_policy_build_attrs_from_map(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ size_t i;
+ uint32_t bit = 0, count = 0;
+ ebitmap_node_t *node = NULL;
+ type_datum_t *tmp_type = NULL, *orig_type;
+ char *tmp_name = NULL, buff[10];
+ int error = 0, retv;
+
+ INFO(policy, "%s", "Generating attributes for policy. (Step 4 of 5)");
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = &policy->p->p;
+
+ memset(&buff, 0, 10 * sizeof(char));
+
+ for (i = 0; i < db->p_types.nprim; i++) {
+ count = 0;
+ ebitmap_for_each_bit(&db->attr_type_map[i], node, bit) {
+ if (ebitmap_node_get_bit(node, bit))
+ count++;
+ }
+ if (count == 0) {
+ continue;
+ }
+ /* first create a new type_datum_t for the attribute,
+ * with the attribute's type_list consisting of types
+ * with this attribute */
+ /* Does not exist */
+ if (db->p_type_val_to_name[i] == NULL){
+ snprintf(buff, 9, "@ttr%04zd", i + 1);
+ tmp_name = strdup(buff);
+ if (!tmp_name) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ /* Already exists */
+ else
+ tmp_name = db->p_type_val_to_name[i];
+
+ tmp_type = calloc(1, sizeof(type_datum_t));
+ if (!tmp_type) {
+ error = errno;
+ goto err;
+ }
+ tmp_type->primary = 1;
+ tmp_type->flavor = TYPE_ATTRIB;
+ tmp_type->s.value = i + 1;
+ if (ebitmap_cpy(&tmp_type->types, &db->attr_type_map[i])) {
+ error = ENOMEM;
+ goto err;
+ }
+
+ /* now go through each of the member types, and set
+ * their type_list bit to point back */
+ ebitmap_for_each_bit(&tmp_type->types, node, bit) {
+ if (ebitmap_node_get_bit(node, bit)) {
+ orig_type = db->type_val_to_struct[bit];
+ if (ebitmap_set_bit(&orig_type->types, tmp_type->s.value - 1, 1)) {
+ error = ENOMEM;
+ goto err;
+ }
+ }
+ }
+ /* Does not exist - insert new */
+ if (db->p_type_val_to_name[i] == NULL){
+ retv = hashtab_insert(db->p_types.table, (hashtab_key_t) tmp_name, (hashtab_datum_t) tmp_type);
+ if (retv) {
+ if (retv == SEPOL_ENOMEM)
+ error = db->p_types.table ? ENOMEM : EINVAL;
+ else
+ error = EEXIST;
+ goto err;
+ }
+ }
+ /* Already exists - replace old */
+ else {
+ retv = hashtab_replace(db->p_types.table, (hashtab_key_t) tmp_name, (hashtab_datum_t) tmp_type, NULL, NULL);
+ if (retv) {
+ if (retv == SEPOL_ENOMEM)
+ error = db->p_types.table ? ENOMEM : EINVAL;
+ else
+ error = EEXIST;
+ goto err;
+ }
+ }
+
+ db->p_type_val_to_name[i] = tmp_name;
+ db->type_val_to_struct[i] = tmp_type;
+
+ /* memory now owned by symtab do not free */
+ tmp_name = NULL;
+ tmp_type = NULL;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(tmp_name);
+ type_datum_destroy(tmp_type);
+ free(tmp_type);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+}
+
+/**
+ * Builds data for empty attributes and inserts them into the policydb.
+ * This function modifies the policydb. Names created for the attributes
+ * are of the form @ttr<value> where value is the value of the attribute
+ * as a four digit number (prepended with 0's as needed).
+ * @param policy The policy to which to add type data for attributes.
+ * This policy will be altered by this function.
+ * @return Returns 0 on success and < 0 on failure; if the call fails,
+ * errno will be set. On failure, the policy state may be inconsistent
+ * especially in the case where the hashtab functions return the error.
+ */
+static int qpol_policy_fill_attr_holes(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ char *tmp_name = NULL, buff[10];
+ int error = 0, retv = 0;
+ ebitmap_t tmp_bmap = { NULL, 0 };
+ type_datum_t *tmp_type = NULL;
+ size_t i;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ memset(&buff, 0, 10 * sizeof(char));
+
+ for (i = 0; i < db->p_types.nprim; i++) {
+ if (db->type_val_to_struct[i])
+ continue;
+ snprintf(buff, 9, "@ttr%04zd", i + 1);
+ tmp_name = strdup(buff);
+ if (!tmp_name) {
+ error = errno;
+ goto err;
+ }
+ tmp_type = calloc(1, sizeof(type_datum_t));
+ if (!tmp_type) {
+ error = errno;
+ goto err;
+ }
+ tmp_type->primary = 1;
+ tmp_type->flavor = TYPE_ATTRIB;
+ tmp_type->s.value = i + 1;
+ tmp_type->types = tmp_bmap;
+
+ retv = hashtab_insert(db->p_types.table, (hashtab_key_t) tmp_name, (hashtab_datum_t) tmp_type);
+ if (retv) {
+ if (retv == SEPOL_ENOMEM)
+ error = db->p_types.table ? ENOMEM : EINVAL;
+ else
+ error = EEXIST;
+ goto err;
+ }
+ db->p_type_val_to_name[i] = tmp_name;
+ db->type_val_to_struct[i] = tmp_type;
+
+ /* memory now owned by symtab do not free */
+ tmp_name = NULL;
+ tmp_type = NULL;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ free(tmp_type);
+ free(tmp_name);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+}
+
+static char *sidnames[] = {
+ "undefined",
+ "kernel",
+ "security",
+ "unlabeled",
+ "fs",
+ "file",
+ "file_labels",
+ "init",
+ "any_socket",
+ "port",
+ "netif",
+ "netmsg",
+ "node",
+ "igmp_packet",
+ "icmp_socket",
+ "tcp_socket",
+ "sysctl_modprobe",
+ "sysctl",
+ "sysctl_fs",
+ "sysctl_kernel",
+ "sysctl_net",
+ "sysctl_net_unix",
+ "sysctl_vm",
+ "sysctl_dev",
+ "kmod",
+ "policy",
+ "scmp_packet",
+ "devnull"
+};
+
+/**
+ * Uses names from flask to fill in the isid names which are not normally
+ * saved. This function modified the policydb.
+ * @param policy Policy to which to add sid names.
+ * This policy will be altered by this function.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set. On failure, the policy state may be inconsistent.
+ */
+static int qpol_policy_add_isid_names(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ ocontext_t *sid = NULL;
+ uint32_t val = 0;
+ int error = 0;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ for (sid = db->ocontexts[OCON_ISID]; sid; sid = sid->next) {
+ val = (uint32_t) sid->sid[0];
+ if (val > SECINITSID_NUM)
+ val = 0;
+
+ if (!sid->u.name) {
+ sid->u.name = strdup(sidnames[val]);
+ if (!sid->u.name) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int extend_assign_role_to_user(hashtab_key_t k __attribute__ ((unused)), hashtab_datum_t d, void *args)
+{
+ user_datum_t *user = (user_datum_t *) d;
+ uint32_t *value = (uint32_t *) args;
+ if (ebitmap_set_bit(&user->roles.roles, *value - 1, 1)) {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Modify the special role 'object_r' by assigning it to all users,
+ * and all types to object_r. This function modifies the policydb.
+ * @param policy Policy containing object_r. This policy will be
+ * altered by this function.
+ * @return 0 on success and < 0 on failure; if the call fails, errno
+ * will be set. On failure, the policy state may be inconsistent.
+ */
+static int qpol_policy_add_object_r(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ int error = 0;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hashtab_datum_t datum = hashtab_search(db->p_roles.table, (const hashtab_key_t)OBJECT_R);
+ if (datum == NULL) {
+ ERR(policy, "%s", OBJECT_R " not found in policy!");
+ errno = EIO;
+ assert(0);
+ return STATUS_ERR;
+ }
+
+ role_datum_t *role = (role_datum_t *) datum;
+
+ uint32_t value = role->s.value;
+
+ if (hashtab_map(db->p_users.table, extend_assign_role_to_user, &value) < 0) {
+ return STATUS_ERR;
+ }
+
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_type_iter(policy, &iter) < 0) {
+ return STATUS_ERR;
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_type_t *type;
+ unsigned char isattr, isalias;
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 ||
+ qpol_type_get_isattr(policy, type, &isattr) < 0 || qpol_type_get_isalias(policy, type, &isalias) < 0) {
+ error = errno;
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return STATUS_ERR;
+ }
+ if (isattr || isalias) {
+ continue;
+ }
+ if (qpol_type_get_value(policy, type, &value) < 0) {
+ error = errno;
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return STATUS_ERR;
+ }
+ if (ebitmap_set_bit(&role->types.types, value - 1, 1)) {
+ error = errno;
+ qpol_iterator_destroy(&iter);
+ errno = error;
+ return STATUS_ERR;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ return 0;
+}
+
+/**
+ * If the given policy's version is higher than the running system's
+ * version, then mark it as different. In a future version of
+ * libqpol, accessors will return data as if the policy were really
+ * the new version rather than what it actually is.
+ */
+static int qpol_policy_match_system(qpol_policy_t * policy)
+{
+ int kernvers = security_policyvers();
+ int currentvers = policy->p->p.policyvers;
+ int error;
+ if (kernvers < 0) {
+ error = errno;
+ ERR(policy, "%s", "Could not determine running system's policy version.");
+ errno = error;
+ return -1;
+ }
+ if (policy->p->p.policyvers > kernvers) {
+ if (sepol_policydb_set_vers(policy->p, kernvers)) {
+ error = errno;
+ ERR(policy, "Could not downgrade policy to version %d.", kernvers);
+ errno = error;
+ return -1;
+ }
+ WARN(policy, "Policy would be downgraded from version %d to %d.", currentvers, kernvers);
+ }
+ return 0;
+}
+
+/**
+ * Walks the conditional list and adds links for reverse look up from
+ * a te/av rule to the conditional from which it came.
+ * @param policy The policy to which to add conditional trace backs.
+ * This policy will be altered by this function.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set. On failure, the policy state may be inconsistent.
+ */
+static int qpol_policy_add_cond_rule_traceback(qpol_policy_t * policy)
+{
+ policydb_t *db = NULL;
+ cond_node_t *cond = NULL;
+ cond_av_list_t *list_ptr = NULL;
+ qpol_iterator_t *iter = NULL;
+ avtab_ptr_t rule = NULL;
+ int error = 0;
+ uint32_t rules = 0;
+
+ INFO(policy, "%s", "Building conditional rules tables. (Step 5 of 5)");
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ rules = (QPOL_RULE_ALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT);
+ if (!(policy->options & QPOL_POLICY_OPTION_NO_NEVERALLOWS))
+ rules |= QPOL_RULE_NEVERALLOW;
+
+ /* mark all unconditional rules as enabled */
+ if (qpol_policy_get_avrule_iter(policy, rules, &iter))
+ return STATUS_ERR;
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&rule)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+ rule->parse_context = NULL;
+ rule->merged = QPOL_COND_RULE_ENABLED;
+ }
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_terule_iter(policy, (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER), &iter))
+ return STATUS_ERR;
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&rule)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+ rule->parse_context = NULL;
+ rule->merged = QPOL_COND_RULE_ENABLED;
+ }
+ qpol_iterator_destroy(&iter);
+
+ for (cond = db->cond_list; cond; cond = cond->next) {
+ /* evaluate cond */
+ cond->cur_state = cond_evaluate_expr(db, cond->expr);
+ if (cond->cur_state < 0) {
+ ERR(policy, "Error evaluating conditional: %s", strerror(EILSEQ));
+ errno = EILSEQ;
+ return STATUS_ERR;
+ }
+
+ /* walk true list */
+ for (list_ptr = cond->true_list; list_ptr; list_ptr = list_ptr->next) {
+ /* field not used after parse, now stores cond */
+ list_ptr->node->parse_context = (void *)cond;
+ /* field not used (except by write),
+ * now storing list and enabled flags */
+ list_ptr->node->merged = QPOL_COND_RULE_LIST;
+ if (cond->cur_state)
+ list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
+ }
+
+ /* walk false list */
+ for (list_ptr = cond->false_list; list_ptr; list_ptr = list_ptr->next) {
+ /* field not used after parse, now stores cond */
+ list_ptr->node->parse_context = (void *)cond;
+ /* field not used (except by write),
+ * now storing list and enabled flags */
+ list_ptr->node->merged = 0; /* i.e. !QPOL_COND_RULE_LIST */
+ if (!cond->cur_state)
+ list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Free all allocated memory used by a qpol_syn_rule.
+ * @param r Reference pointer to the rule to destroy.
+ */
+static void qpol_syn_rule_destroy(struct qpol_syn_rule **r)
+{
+ if (!r || !(*r))
+ return;
+
+ free(*r);
+ *r = NULL;
+}
+
+/**
+ * Free all memory used by a syn rule list.
+ * @param list Reference pointer to the head node of
+ * the syn rule list to destroy. All nodes in the list will
+ * be destroyed.
+ */
+static void qpol_syn_rule_list_destroy(qpol_syn_rule_list_t ** list)
+{
+ qpol_syn_rule_list_t *cur = NULL, *next = NULL;
+
+ if (!list || !(*list))
+ return;
+
+ for (cur = *list; cur; cur = next) {
+ next = cur->next;
+ free(cur);
+ }
+}
+
+/**
+ * Free all memory used by a syn rule node in the rule table.
+ * @param node Reference pointer to the first node in the chain.
+ * All nodes in the chain will be destroyed.
+ */
+static void qpol_syn_rule_node_destroy(qpol_syn_rule_node_t ** node)
+{
+ qpol_syn_rule_node_t *cur = NULL, *next = NULL;
+
+ if (!node || !(*node))
+ return;
+
+ for (cur = *node; cur; cur = next) {
+ next = cur->next;
+ qpol_syn_rule_list_destroy(&cur->rules);
+ free(cur);
+ }
+}
+
+/**
+ * Free all memory used by the syntactic rule table.
+ * @param t Reference pointer to the table to destroy.
+ */
+static void qpol_syn_rule_table_destroy(qpol_syn_rule_table_t ** t)
+{
+ size_t i = 0;
+
+ if (!t || !(*t))
+ return;
+
+ for (i = 0; i < QPOL_SYN_RULE_TABLE_SIZE; i++)
+ qpol_syn_rule_node_destroy(&((*t)->buckets[i]));
+
+ free((*t)->buckets);
+ free(*t);
+ *t = NULL;
+}
+
+/**
+ * Find the node in the syntactic rule hash table corresponding to a key.
+ * @param table The table to search.
+ * @param key The key for which to search.
+ * @return a valid qpol_syn_rule_node_t pointer on success or NULL on failure.
+ */
+static qpol_syn_rule_node_t *qpol_syn_rule_table_find_node_by_key(const qpol_syn_rule_table_t * table,
+ const qpol_syn_rule_key_t * key)
+{
+ qpol_syn_rule_node_t *node = NULL;
+
+ for (node = table->buckets[QPOL_SYN_RULE_TABLE_HASH(key)]; node; node = node->next) {
+ if ((node->key.rule_type & key->rule_type) &&
+ (node->key.source_val == key->source_val) &&
+ (node->key.target_val == key->target_val) &&
+ (node->key.class_val == key->class_val) && (node->key.cond == key->cond))
+ return node;
+ }
+
+ return NULL;
+}
+
+/**
+ * Given a syn rule key and a syn rule, adds the key/rule pair to the
+ * syn rule table. Note that this function takes ownership of the
+ * key.
+ *
+ * @param policy Policy associated with the rule.
+ * @param table The table to which to add the rule.
+ * @param key Hashtable key for rule lookup.
+ * @param rule The rule to add.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the table may be in an inconsistent state.
+ */
+static int qpol_syn_rule_table_insert_entry(qpol_policy_t * policy,
+ qpol_syn_rule_table_t * table, qpol_syn_rule_key_t * key, struct qpol_syn_rule *rule)
+{
+ int error = 0;
+ qpol_syn_rule_node_t *table_node = NULL;
+ qpol_syn_rule_list_t *list_entry = NULL;
+
+ if (!(list_entry = malloc(sizeof(qpol_syn_rule_list_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ return -1;
+ }
+ list_entry->rule = rule;
+
+ table_node = qpol_syn_rule_table_find_node_by_key(table, key);
+ if (table_node) {
+ list_entry->next = table_node->rules;
+ table_node->rules = list_entry;
+ } else {
+ list_entry->next = NULL;
+ if (!(table_node = malloc(sizeof(qpol_syn_rule_node_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ free(list_entry);
+ return -1;
+ }
+ table_node->key = *key;
+ table_node->rules = list_entry;
+ size_t hash = QPOL_SYN_RULE_TABLE_HASH(key);
+ table_node->next = table->buckets[hash];
+ table->buckets[hash] = table_node;
+ }
+ return 0;
+}
+
+/**
+ * Add a syntactic rule (sepol's avrule_t) to the syntactic rule table.
+ * @param policy Policy associated with the rule.
+ * @param table The table to which to add the rule.
+ * @param rule The rule to add.
+ * @param cond The conditional associated with the rule (NULL if
+ * unconditional). with the rule (needed for conditional tracking).
+ * @param branch If the rule is conditional, then 0 if in the true
+ * branch, 1 if in else.
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set and the table may be in an inconsistent state.
+ */
+static int qpol_syn_rule_table_insert_sepol_avrule(qpol_policy_t * policy, qpol_syn_rule_table_t * table, avrule_t * rule,
+ cond_node_t * cond, int branch)
+{
+ int error = 0;
+ qpol_syn_rule_key_t key = { 0, 0, 0, 0, NULL };
+ struct qpol_syn_rule *new_rule = NULL;
+ ebitmap_t source_types, source_types2, target_types, target_types2;
+ ebitmap_node_t *snode = NULL, *tnode = NULL;
+ unsigned int i, j;
+ class_perm_node_t *class_node = NULL;
+
+ if (!(new_rule = malloc(sizeof(struct qpol_syn_rule)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ new_rule->rule = rule;
+ new_rule->cond = cond;
+ new_rule->cond_branch = branch;
+
+ policy->ext->syn_rule_master_list[policy->ext->master_list_sz] = new_rule;
+ policy->ext->master_list_sz++;
+
+ if (type_set_expand(&rule->stypes, &source_types, &policy->p->p, 0) ||
+ type_set_expand(&rule->stypes, &source_types2, &policy->p->p, 1)) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto err;
+ }
+ if (type_set_expand(&rule->ttypes, &target_types, &policy->p->p, 0) ||
+ type_set_expand(&rule->ttypes, &target_types2, &policy->p->p, 1)) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto err;
+ }
+ if (ebitmap_union(&source_types, &source_types2) || ebitmap_union(&target_types, &target_types2)) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto err;
+ }
+ ebitmap_for_each_bit(&source_types, snode, i) {
+ if (!ebitmap_get_bit(&source_types, i))
+ continue;
+ if (rule->flags & RULE_SELF) {
+ for (class_node = rule->perms; class_node; class_node = class_node->next) {
+ key.rule_type = rule->specified;
+ key.source_val = key.target_val = i + 1;
+ key.class_val = class_node->class;
+ key.cond = cond;
+ if (qpol_syn_rule_table_insert_entry(policy, table, &key, new_rule))
+ goto err;
+ }
+ }
+ ebitmap_for_each_bit(&target_types, tnode, j) {
+ if (!ebitmap_get_bit(&target_types, j))
+ continue;
+ for (class_node = rule->perms; class_node; class_node = class_node->next) {
+ key.rule_type = rule->specified;
+ key.source_val = i + 1;
+ key.target_val = j + 1;
+ key.class_val = class_node->class;
+ key.cond = cond;
+ if (qpol_syn_rule_table_insert_entry(policy, table, &key, new_rule))
+ goto err;
+ }
+ }
+ }
+
+ ebitmap_destroy(&source_types);
+ ebitmap_destroy(&source_types2);
+ ebitmap_destroy(&target_types);
+ ebitmap_destroy(&target_types2);
+ return 0;
+
+ err:
+ ebitmap_destroy(&source_types);
+ ebitmap_destroy(&source_types2);
+ ebitmap_destroy(&target_types);
+ ebitmap_destroy(&target_types2);
+ return -1;
+}
+
+int qpol_policy_build_syn_rule_table(qpol_policy_t * policy)
+{
+ int error = 0, created = 0;
+ avrule_block_t *cur_block = NULL;
+ avrule_decl_t *decl = NULL;
+ avrule_t *cur_rule = NULL;
+ cond_node_t *cur_cond = NULL, *remapped_cond;
+
+ if (!policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!policy->ext) {
+ policy->ext = calloc(1, sizeof(qpol_extended_image_t));
+ if (!policy->ext) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+
+ if (policy->ext->syn_rule_table)
+ return 0; /* already built */
+
+ policy->ext->syn_rule_table = calloc(1, sizeof(qpol_syn_rule_table_t));
+ if (!policy->ext->syn_rule_table) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ policy->ext->syn_rule_table->buckets = calloc(QPOL_SYN_RULE_TABLE_SIZE, sizeof(qpol_syn_rule_node_t *));
+ if (!policy->ext->syn_rule_table->buckets) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ policy->ext->master_list_sz = 0;
+ for (cur_block = policy->p->p.global; cur_block; cur_block = cur_block->next) {
+ decl = cur_block->enabled;
+ if (!decl)
+ continue;
+
+ for (cur_rule = decl->avrules; cur_rule; cur_rule = cur_rule->next) {
+ policy->ext->master_list_sz++;
+ }
+ for (cur_cond = decl->cond_list; cur_cond; cur_cond = cur_cond->next) {
+ for (cur_rule = cur_cond->avtrue_list; cur_rule; cur_rule = cur_rule->next) {
+ policy->ext->master_list_sz++;
+ }
+ for (cur_rule = cur_cond->avfalse_list; cur_rule; cur_rule = cur_rule->next) {
+ policy->ext->master_list_sz++;
+ }
+ }
+ }
+
+ if (policy->ext->master_list_sz == 0) {
+ policy->ext->syn_rule_master_list = NULL;
+ return 0; /* policy is not a source policy */
+ }
+
+ INFO(policy, "%s", "Building syntactic rules tables.");
+
+ policy->ext->syn_rule_master_list = calloc(policy->ext->master_list_sz, sizeof(struct qpol_syn_rule *));
+ if (!policy->ext->syn_rule_master_list) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ /* reset size as it will represent the current number of elements inserted */
+ policy->ext->master_list_sz = 0;
+
+ for (cur_block = policy->p->p.global; cur_block; cur_block = cur_block->next) {
+ decl = cur_block->enabled;
+ if (!decl)
+ continue;
+
+ for (cur_rule = decl->avrules; cur_rule; cur_rule = cur_rule->next) {
+ if (qpol_syn_rule_table_insert_sepol_avrule(policy, policy->ext->syn_rule_table, cur_rule, NULL, 0)) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (cur_cond = decl->cond_list; cur_cond; cur_cond = cur_cond->next) {
+ /* convert the cond within an avrule_decl to
+ * the expanded cond */
+ remapped_cond = cond_node_find(&policy->p->p, cur_cond, policy->p->p.cond_list, &created);
+ if (created || !remapped_cond) {
+ cond_node_destroy(remapped_cond);
+ error = EIO;
+ ERR(policy, "%s", "Inconsistent conditional records");
+ assert(0);
+ goto err;
+ }
+ for (cur_rule = cur_cond->avtrue_list; cur_rule; cur_rule = cur_rule->next) {
+ if (qpol_syn_rule_table_insert_sepol_avrule
+ (policy, policy->ext->syn_rule_table, cur_rule, remapped_cond, 0)) {
+ error = errno;
+ goto err;
+ }
+ }
+ for (cur_rule = cur_cond->avfalse_list; cur_rule; cur_rule = cur_rule->next) {
+ if (qpol_syn_rule_table_insert_sepol_avrule
+ (policy, policy->ext->syn_rule_table, cur_rule, remapped_cond, 1)) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ }
+
+#ifdef SETOOLS_DEBUG
+ /*
+ * Debugging code to measure the how well the syntactic rules
+ * are being hashed. Calculate the min, max, and std
+ * deviation.
+ */
+ size_t bucket;
+ float o2 = 0.0f;
+ long total_entries = 0;
+ for (bucket = 0; bucket < QPOL_SYN_RULE_TABLE_SIZE; bucket++) {
+ qpol_syn_rule_node_t *n = policy->ext->syn_rule_table->buckets[bucket];
+ while (n != NULL) {
+ total_entries++;
+ n = n->next;
+ }
+ }
+ float expected_value = total_entries * 1.0f / QPOL_SYN_RULE_TABLE_SIZE;
+ size_t min_items = total_entries;
+ size_t max_items = 0;
+ for (bucket = 0; bucket < QPOL_SYN_RULE_TABLE_SIZE; bucket++) {
+ size_t num_items = 0;
+ qpol_syn_rule_node_t *n = policy->ext->syn_rule_table->buckets[bucket];
+ while (n != NULL) {
+ num_items++;
+ n = n->next;
+ }
+ if (num_items > max_items) {
+ max_items = num_items;
+ }
+ if (num_items < min_items) {
+ min_items = num_items;
+ }
+ o2 += (num_items - expected_value) * (num_items - expected_value);
+ }
+ float stddev = sqrtf(o2 / (QPOL_SYN_RULE_TABLE_SIZE - 1));
+ fprintf(stderr, "libqpol synrule table %d bits: total entries %lu, expected %g\n", QPOL_SYN_RULE_TABLE_BITS, total_entries,
+ expected_value);
+ fprintf(stderr, " min %zd, max %zd, stddev %g\n", min_items, max_items, stddev);
+#endif
+
+ return 0;
+
+ err:
+ if (policy->ext)
+ qpol_syn_rule_table_destroy(&policy->ext->syn_rule_table);
+ errno = error;
+ return -1;
+}
+
+/**
+ * Free all memory used by a qpol extended image and set it to NULL.
+ * @param ext The extended image to destroy.
+ */
+void qpol_extended_image_destroy(qpol_extended_image_t ** ext)
+{
+ size_t i = 0;
+
+ if (!ext || !(*ext))
+ return;
+
+ qpol_syn_rule_table_destroy(&((*ext)->syn_rule_table));
+
+ for (i = 0; i < (*ext)->master_list_sz; i++) {
+ qpol_syn_rule_destroy(&((*ext)->syn_rule_master_list[i]));
+ }
+ free((*ext)->syn_rule_master_list);
+
+ free(*ext);
+ *ext = NULL;
+}
+
+int policy_extend(qpol_policy_t * policy)
+{
+ int retv, error;
+ policydb_t *db = NULL;
+
+ if (policy == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ db = &policy->p->p;
+
+ retv = qpol_policy_remove_bogus_aliases(policy);
+ if (retv) {
+ error = errno;
+ goto err;
+ }
+ if (db->attr_type_map) {
+ retv = qpol_policy_build_attrs_from_map(policy);
+ if (retv) {
+ error = errno;
+ goto err;
+ }
+ if (db->policy_type == POLICY_KERN) {
+ retv = qpol_policy_fill_attr_holes(policy);
+ if (retv) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ retv = qpol_policy_add_isid_names(policy);
+ if (retv) {
+ error = errno;
+ goto err;
+ }
+ retv = qpol_policy_add_object_r(policy);
+ if (retv) {
+ error = errno;
+ goto err;
+ }
+
+ if ((policy->options & QPOL_POLICY_OPTION_MATCH_SYSTEM) && qpol_policy_match_system(policy)) {
+ error = errno;
+ goto err;
+ }
+
+ if (policy->options & QPOL_POLICY_OPTION_NO_RULES)
+ return STATUS_SUCCESS;
+
+ retv = qpol_policy_add_cond_rule_traceback(policy);
+ if (retv) {
+ error = errno;
+ goto err;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ /* no need to call ERR here as it will already have been called */
+ qpol_extended_image_destroy(&policy->ext);
+ errno = error;
+ return STATUS_ERR;
+}
+
+typedef struct syn_rule_state
+{
+ qpol_syn_rule_node_t *node;
+ qpol_syn_rule_list_t *cur;
+} syn_rule_state_t;
+
+static int syn_rule_state_end(const qpol_iterator_t * iter)
+{
+ syn_rule_state_t *srs = NULL;
+
+ if (!iter || !(srs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return (srs->cur ? 0 : 1);
+}
+
+static void *syn_rule_state_get_cur(const qpol_iterator_t * iter)
+{
+ syn_rule_state_t *srs = NULL;
+
+ if (!iter || !(srs = qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return srs->cur->rule;
+}
+
+static int syn_rule_state_next(qpol_iterator_t * iter)
+{
+ syn_rule_state_t *srs = NULL;
+
+ if (!iter || !(srs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ srs->cur = srs->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t syn_rule_state_size(const qpol_iterator_t * iter)
+{
+ size_t count = 0;
+ qpol_syn_rule_list_t *tmp = NULL;
+ syn_rule_state_t *srs = NULL;
+
+ if (!iter || !(srs = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (tmp = srs->node->rules; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_avrule_get_syn_avrule_iter(const qpol_policy_t * policy, const struct qpol_avrule *rule, qpol_iterator_t ** iter)
+{
+ qpol_syn_rule_key_t *key = NULL;
+ const qpol_type_t *tmp_type;
+ const qpol_class_t *tmp_class;
+ const qpol_cond_t *tmp_cond;
+ syn_rule_state_t *srs = NULL;
+ uint32_t tmp_val;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !policy->ext || !rule || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* build key */
+ if (!(key = calloc(1, sizeof(qpol_syn_rule_key_t)))) {
+ error = errno;
+ ERR(policy, "%S", strerror(error));
+ goto err;
+ }
+
+ if (qpol_avrule_get_rule_type(policy, rule, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->rule_type = (tmp_val == QPOL_RULE_DONTAUDIT ? (AVRULE_AUDITDENY | AVRULE_DONTAUDIT) : tmp_val);
+
+ if (qpol_avrule_get_source_type(policy, rule, &tmp_type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->source_val = tmp_val;
+
+ if (qpol_avrule_get_target_type(policy, rule, &tmp_type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->target_val = tmp_val;
+
+ if (qpol_avrule_get_object_class(policy, rule, &tmp_class)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_class_get_value(policy, tmp_class, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->class_val = tmp_val;
+
+ if (qpol_avrule_get_cond(policy, rule, &tmp_cond)) {
+ error = errno;
+ goto err;
+ }
+ key->cond = (cond_node_t *) tmp_cond;
+
+ /* build state object */
+ if (!(srs = calloc(1, sizeof(syn_rule_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ srs->node = qpol_syn_rule_table_find_node_by_key(policy->ext->syn_rule_table, key);
+ if (!srs->node) {
+ ERR(policy, "%s", "Unable to locate syntactic rules for semantic av rule");
+ errno = ENOENT;
+ goto err;
+ }
+ srs->cur = srs->node->rules;
+
+ if (qpol_iterator_create(policy, (void *)srs,
+ syn_rule_state_get_cur, syn_rule_state_next, syn_rule_state_end, syn_rule_state_size, free, iter))
+ {
+ error = errno;
+ goto err;
+ }
+
+ free(key);
+
+ return 0;
+
+ err:
+ free(key);
+ free(srs);
+ errno = error;
+ return -1;
+}
+
+int qpol_terule_get_syn_terule_iter(const qpol_policy_t * policy, const struct qpol_terule *rule, qpol_iterator_t ** iter)
+{
+ qpol_syn_rule_key_t *key = NULL;
+ const qpol_type_t *tmp_type;
+ const qpol_class_t *tmp_class;
+ const qpol_cond_t *tmp_cond;
+ syn_rule_state_t *srs = NULL;
+ uint32_t tmp_val;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !policy->ext || !rule || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* build key */
+ if (!(key = calloc(1, sizeof(qpol_syn_rule_key_t)))) {
+ error = errno;
+ ERR(policy, "%S", strerror(error));
+ goto err;
+ }
+
+ if (qpol_terule_get_rule_type(policy, rule, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->rule_type = tmp_val;
+
+ if (qpol_terule_get_source_type(policy, rule, &tmp_type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->source_val = tmp_val;
+
+ if (qpol_terule_get_target_type(policy, rule, &tmp_type)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->target_val = tmp_val;
+
+ if (qpol_terule_get_object_class(policy, rule, &tmp_class)) {
+ error = errno;
+ goto err;
+ }
+ if (qpol_class_get_value(policy, tmp_class, &tmp_val)) {
+ error = errno;
+ goto err;
+ }
+ key->class_val = tmp_val;
+
+ if (qpol_terule_get_cond(policy, rule, &tmp_cond)) {
+ error = errno;
+ goto err;
+ }
+ key->cond = (cond_node_t *) tmp_cond;
+
+ /* build state object */
+ if (!(srs = calloc(1, sizeof(syn_rule_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ srs->node = qpol_syn_rule_table_find_node_by_key(policy->ext->syn_rule_table, key);
+ if (!srs->node) {
+ ERR(policy, "%s", "Unable to locate syntactic rules for semantic te rule");
+ error = ENOENT;
+ goto err;
+ }
+ srs->cur = srs->node->rules;
+
+ if (qpol_iterator_create(policy, (void *)srs,
+ syn_rule_state_get_cur, syn_rule_state_next, syn_rule_state_end, syn_rule_state_size, free, iter))
+ {
+ error = errno;
+ goto err;
+ }
+
+ free(key);
+
+ return 0;
+
+ err:
+ free(key);
+ free(srs);
+ errno = error;
+ return -1;
+}
diff --git a/libqpol/src/policy_parse.y b/libqpol/src/policy_parse.y
new file mode 100644
index 0000000..84f4114
--- /dev/null
+++ b/libqpol/src/policy_parse.y
@@ -0,0 +1,834 @@
+/**
+ * @file policy_parse.y
+ *
+ * This file is based upon checkpolicy/policy_parse.y from NSA's SVN
+ * repository. It has been modified to support older policy formats.
+ */
+
+/*
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Updated: David Caplan, <dac@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Updated: Joshua Brindle <jbrindle@tresys.com>
+ * Karl MacMillan <kmacmillan@mentalrootkit.com>
+ * Jason Tang <jtang@tresys.com>
+ *
+ * Added support for binary policy modules
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2008 Tresys Technology, LLC
+ * Copyright (C) 2007 Red Hat Inc.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+/* FLASK */
+
+%{
+#include <config.h>
+
+#include <sys/types.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include <sepol/policydb/expand.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/services.h>
+#include <sepol/policydb/conditional.h>
+#include <sepol/policydb/flask.h>
+#include <sepol/policydb/hierarchy.h>
+#ifdef HAVE_SEPOL_POLICYCAPS
+#include <sepol/policydb/polcaps.h>
+#endif
+
+#include "queue.h"
+#include <qpol/policy.h>
+#include "module_compiler.h"
+#include "policy_define.h"
+
+extern policydb_t *policydbp;
+extern unsigned int pass;
+
+extern char yytext[];
+extern int yylex(void);
+extern int yywarn(char *msg);
+extern int yyerror(char *msg);
+
+typedef int (* require_func_t)();
+
+/* redefine input so we can read from a string */
+/* borrowed from O'Reilly lex and yacc pg 157 */
+extern char qpol_src_input[];
+extern char *qpol_src_inputptr;/* current position in qpol_src_input */
+extern char *qpol_src_inputlim;/* end of data */
+
+%}
+
+%union {
+ unsigned int val;
+ uintptr_t valptr;
+ void *ptr;
+ require_func_t require_func;
+}
+
+%type <ptr> cond_expr cond_expr_prim cond_pol_list cond_else
+%type <ptr> cond_allow_def cond_auditallow_def cond_auditdeny_def cond_dontaudit_def
+%type <ptr> cond_transition_def cond_te_avtab_def cond_rule_def
+%type <ptr> role_def roles
+%type <valptr> cexpr cexpr_prim op role_mls_op
+%type <val> ipv4_addr_def number
+%type <require_func> require_decl_def
+
+%token PATH
+%token CLONE
+%token COMMON
+%token CLASS
+%token CONSTRAIN
+%token VALIDATETRANS
+%token INHERITS
+%token SID
+%token ROLE
+%token ROLES
+%token TYPEALIAS
+%token TYPEATTRIBUTE
+%token TYPEBOUNDS
+%token TYPE
+%token TYPES
+%token ALIAS
+%token ATTRIBUTE
+%token BOOL
+%token IF
+%token ELSE
+%token TYPE_TRANSITION
+%token TYPE_MEMBER
+%token TYPE_CHANGE
+%token ROLE_TRANSITION
+%token RANGE_TRANSITION
+%token SENSITIVITY
+%token DOMINANCE
+%token DOM DOMBY INCOMP
+%token CATEGORY
+%token LEVEL
+%token RANGE
+%token MLSCONSTRAIN
+%token MLSVALIDATETRANS
+%token USER
+%token NEVERALLOW
+%token ALLOW
+%token AUDITALLOW
+%token AUDITDENY
+%token DONTAUDIT
+%token SOURCE
+%token TARGET
+%token SAMEUSER
+%token FSCON PORTCON NETIFCON NODECON
+%token PIRQCON IOMEMCON IOPORTCON PCIDEVICECON
+%token FSUSEXATTR FSUSETASK FSUSETRANS FSUSEPSID
+%token GENFSCON
+%token U1 U2 U3 R1 R2 R3 T1 T2 T3 L1 L2 H1 H2
+%token NOT AND OR XOR
+%token CTRUE CFALSE
+%token IDENTIFIER
+%token NUMBER
+%token EQUALS
+%token NOTEQUAL
+%token IPV4_ADDR
+%token IPV6_ADDR
+%token MODULE VERSION_IDENTIFIER REQUIRE OPTIONAL
+%token POLICYCAP
+%token PERMISSIVE
+
+%left OR
+%left XOR
+%left AND
+%right NOT
+%left EQUALS NOTEQUAL
+%%
+policy : base_policy
+ | module_policy
+ ;
+base_policy : { if (define_policy(pass, 0) == -1) return -1; }
+ classes initial_sids access_vectors
+ { if (pass == 1) { if (policydb_index_classes(policydbp)) return -1; }
+ else if (pass == 2) { if (policydb_index_others(NULL, policydbp, 0)) return -1; }}
+ opt_mls te_rbac users opt_constraints
+ { if (pass == 1) { if (policydb_index_bools(policydbp)) return -1;}
+ else if (pass == 2) { if (policydb_index_others(NULL, policydbp, 0)) return -1;}}
+ initial_sid_contexts opt_fs_contexts opt_fs_uses opt_genfs_contexts net_contexts opt_dev_contexts
+ ;
+classes : class_def
+ | classes class_def
+ ;
+class_def : CLASS identifier
+ {if (define_class()) return -1;}
+ ;
+initial_sids : initial_sid_def
+ | initial_sids initial_sid_def
+ ;
+initial_sid_def : SID identifier
+ {if (define_initial_sid()) return -1;}
+ ;
+access_vectors : opt_common_perms av_perms
+ ;
+opt_common_perms : common_perms
+ |
+ ;
+common_perms : common_perms_def
+ | common_perms common_perms_def
+ ;
+common_perms_def : COMMON identifier '{' identifier_list '}'
+ {if (define_common_perms()) return -1;}
+ ;
+av_perms : av_perms_def
+ | av_perms av_perms_def
+ ;
+av_perms_def : CLASS identifier '{' identifier_list '}'
+ {if (define_av_perms(FALSE)) return -1;}
+ | CLASS identifier INHERITS identifier
+ {if (define_av_perms(TRUE)) return -1;}
+ | CLASS identifier INHERITS identifier '{' identifier_list '}'
+ {if (define_av_perms(TRUE)) return -1;}
+ ;
+opt_mls : mls
+ |
+ ;
+mls : sensitivities dominance opt_categories levels mlspolicy
+ ;
+sensitivities : sensitivity_def
+ | sensitivities sensitivity_def
+ ;
+/* Need to call define_mls here, as we are working with files */
+/* only, not command line options */
+sensitivity_def : SENSITIVITY identifier alias_def ';'
+ {if (define_mls() | define_sens()) return -1;}
+ | SENSITIVITY identifier ';'
+ {if (define_mls() | define_sens()) return -1;}
+ ;
+alias_def : ALIAS names
+ ;
+dominance : DOMINANCE identifier
+ {if (define_dominance()) return -1;}
+ | DOMINANCE '{' identifier_list '}'
+ {if (define_dominance()) return -1;}
+ ;
+opt_categories : categories
+ |
+ ;
+categories : category_def
+ | categories category_def
+ ;
+category_def : CATEGORY identifier alias_def ';'
+ {if (define_category()) return -1;}
+ | CATEGORY identifier ';'
+ {if (define_category()) return -1;}
+ ;
+levels : level_def
+ | levels level_def
+ ;
+level_def : LEVEL identifier ':' id_comma_list ';'
+ {if (define_level()) return -1;}
+ | LEVEL identifier ';'
+ {if (define_level()) return -1;}
+ ;
+mlspolicy : mlspolicy_decl
+ | mlspolicy mlspolicy_decl
+ ;
+mlspolicy_decl : mlsconstraint_def
+ | mlsvalidatetrans_def
+ ;
+mlsconstraint_def : MLSCONSTRAIN names names cexpr ';'
+ { if (define_constraint((constraint_expr_t*)$4)) return -1; }
+ ;
+mlsvalidatetrans_def : MLSVALIDATETRANS names cexpr ';'
+ { if (define_validatetrans((constraint_expr_t*)$3)) return -1; }
+ ;
+te_rbac : te_rbac_decl
+ | te_rbac te_rbac_decl
+ ;
+te_rbac_decl : te_decl
+ | rbac_decl
+ | cond_stmt_def
+ | optional_block
+ | policycap_def
+ | ';'
+ ;
+rbac_decl : role_type_def
+ | role_dominance
+ | role_trans_def
+ | role_allow_def
+ ;
+te_decl : attribute_def
+ | type_def
+ | typealias_def
+ | typeattribute_def
+ | typebounds_def
+ | bool_def
+ | transition_def
+ | range_trans_def
+ | te_avtab_def
+ | permissive_def
+ ;
+attribute_def : ATTRIBUTE identifier ';'
+ { if (define_attrib()) return -1;}
+ ;
+type_def : TYPE identifier alias_def opt_attr_list ';'
+ {if (define_type(1)) return -1;}
+ | TYPE identifier opt_attr_list ';'
+ {if (define_type(0)) return -1;}
+ ;
+typealias_def : TYPEALIAS identifier alias_def ';'
+ {if (define_typealias()) return -1;}
+ ;
+typeattribute_def : TYPEATTRIBUTE identifier id_comma_list ';'
+ {if (define_typeattribute()) return -1;}
+ ;
+typebounds_def : TYPEBOUNDS identifier id_comma_list ';'
+ {if (define_typebounds()) return -1;}
+ ;
+opt_attr_list : ',' id_comma_list
+ |
+ ;
+bool_def : BOOL identifier bool_val ';'
+ {if (define_bool()) return -1;}
+ ;
+bool_val : CTRUE
+ { if (insert_id("T",0)) return -1; }
+ | CFALSE
+ { if (insert_id("F",0)) return -1; }
+ ;
+cond_stmt_def : IF cond_expr '{' cond_pol_list '}' cond_else
+ { if (pass == 2) { if (define_conditional((cond_expr_t*)$2, (avrule_t*)$4, (avrule_t*)$6) < 0) return -1; }}
+ ;
+cond_else : ELSE '{' cond_pol_list '}'
+ { $$ = $3; }
+ | /* empty */
+ { $$ = NULL; }
+cond_expr : '(' cond_expr ')'
+ { $$ = $2;}
+ | NOT cond_expr
+ { $$ = define_cond_expr(COND_NOT, $2, 0);
+ if ($$ == 0) return -1; }
+ | cond_expr AND cond_expr
+ { $$ = define_cond_expr(COND_AND, $1, $3);
+ if ($$ == 0) return -1; }
+ | cond_expr OR cond_expr
+ { $$ = define_cond_expr(COND_OR, $1, $3);
+ if ($$ == 0) return -1; }
+ | cond_expr XOR cond_expr
+ { $$ = define_cond_expr(COND_XOR, $1, $3);
+ if ($$ == 0) return -1; }
+ | cond_expr EQUALS cond_expr
+ { $$ = define_cond_expr(COND_EQ, $1, $3);
+ if ($$ == 0) return -1; }
+ | cond_expr NOTEQUAL cond_expr
+ { $$ = define_cond_expr(COND_NEQ, $1, $3);
+ if ($$ == 0) return -1; }
+ | cond_expr_prim
+ { $$ = $1; }
+ ;
+cond_expr_prim : identifier
+ { $$ = define_cond_expr(COND_BOOL,0, 0);
+ if ($$ == COND_ERR) return -1; }
+ ;
+cond_pol_list : cond_pol_list cond_rule_def
+ { $$ = define_cond_pol_list((avrule_t *)$1, (avrule_t *)$2); }
+ | /* empty */
+ { $$ = NULL; }
+ ;
+cond_rule_def : cond_transition_def
+ { $$ = $1; }
+ | cond_te_avtab_def
+ { $$ = $1; }
+ | require_block
+ { $$ = NULL; }
+ ;
+cond_transition_def : TYPE_TRANSITION names names ':' names identifier ';'
+ { $$ = define_cond_compute_type(AVRULE_TRANSITION) ;
+ if ($$ == COND_ERR) return -1;}
+ | TYPE_MEMBER names names ':' names identifier ';'
+ { $$ = define_cond_compute_type(AVRULE_MEMBER) ;
+ if ($$ == COND_ERR) return -1;}
+ | TYPE_CHANGE names names ':' names identifier ';'
+ { $$ = define_cond_compute_type(AVRULE_CHANGE) ;
+ if ($$ == COND_ERR) return -1;}
+ ;
+cond_te_avtab_def : cond_allow_def
+ { $$ = $1; }
+ | cond_auditallow_def
+ { $$ = $1; }
+ | cond_auditdeny_def
+ { $$ = $1; }
+ | cond_dontaudit_def
+ { $$ = $1; }
+ ;
+cond_allow_def : ALLOW names names ':' names names ';'
+ { $$ = define_cond_te_avtab(AVRULE_ALLOWED) ;
+ if ($$ == COND_ERR) return -1; }
+ ;
+cond_auditallow_def : AUDITALLOW names names ':' names names ';'
+ { $$ = define_cond_te_avtab(AVRULE_AUDITALLOW) ;
+ if ($$ == COND_ERR) return -1; }
+ ;
+cond_auditdeny_def : AUDITDENY names names ':' names names ';'
+ { $$ = define_cond_te_avtab(AVRULE_AUDITDENY) ;
+ if ($$ == COND_ERR) return -1; }
+ ;
+cond_dontaudit_def : DONTAUDIT names names ':' names names ';'
+ { $$ = define_cond_te_avtab(AVRULE_DONTAUDIT);
+ if ($$ == COND_ERR) return -1; }
+ ;
+transition_def : TYPE_TRANSITION names names ':' names identifier ';'
+ {if (define_compute_type(AVRULE_TRANSITION)) return -1;}
+ | TYPE_MEMBER names names ':' names identifier ';'
+ {if (define_compute_type(AVRULE_MEMBER)) return -1;}
+ | TYPE_CHANGE names names ':' names identifier ';'
+ {if (define_compute_type(AVRULE_CHANGE)) return -1;}
+ ;
+range_trans_def : RANGE_TRANSITION names names mls_range_def ';'
+ { if (define_range_trans(0)) return -1; }
+ | RANGE_TRANSITION names names ':' names mls_range_def ';'
+ { if (define_range_trans(1)) return -1; }
+ ;
+te_avtab_def : allow_def
+ | auditallow_def
+ | auditdeny_def
+ | dontaudit_def
+ | neverallow_def
+ ;
+allow_def : ALLOW names names ':' names names ';'
+ {if (define_te_avtab(AVRULE_ALLOWED)) return -1; }
+ ;
+auditallow_def : AUDITALLOW names names ':' names names ';'
+ {if (define_te_avtab(AVRULE_AUDITALLOW)) return -1; }
+ ;
+auditdeny_def : AUDITDENY names names ':' names names ';'
+ {if (define_te_avtab(AVRULE_AUDITDENY)) return -1; }
+ ;
+dontaudit_def : DONTAUDIT names names ':' names names ';'
+ {if (define_te_avtab(AVRULE_DONTAUDIT)) return -1; }
+ ;
+neverallow_def : NEVERALLOW names names ':' names names ';'
+ {if (define_te_avtab(AVRULE_NEVERALLOW)) return -1; }
+ ;
+role_type_def : ROLE identifier TYPES names ';'
+ {if (define_role_types()) return -1;}
+ | ROLE identifier';'
+ {if (define_role_types()) return -1;}
+ ;
+role_dominance : DOMINANCE '{' roles '}'
+ ;
+role_trans_def : ROLE_TRANSITION names names identifier ';'
+ {if (define_role_trans()) return -1; }
+ ;
+role_allow_def : ALLOW names names ';'
+ {if (define_role_allow()) return -1; }
+ ;
+roles : role_def
+ { $$ = $1; }
+ | roles role_def
+ { $$ = merge_roles_dom((role_datum_t*)$1, (role_datum_t*)$2); if ($$ == 0) return -1;}
+ ;
+role_def : ROLE identifier_push ';'
+ {$$ = define_role_dom(NULL); if ($$ == 0) return -1;}
+ | ROLE identifier_push '{' roles '}'
+ {$$ = define_role_dom((role_datum_t*)$4); if ($$ == 0) return -1;}
+ ;
+opt_constraints : constraints
+ |
+ ;
+constraints : constraint_decl
+ | constraints constraint_decl
+ ;
+constraint_decl : constraint_def
+ | validatetrans_def
+ ;
+constraint_def : CONSTRAIN names names cexpr ';'
+ { if (define_constraint((constraint_expr_t*)$4)) return -1; }
+ ;
+validatetrans_def : VALIDATETRANS names cexpr ';'
+ { if (define_validatetrans((constraint_expr_t*)$3)) return -1; }
+ ;
+cexpr : '(' cexpr ')'
+ { $$ = $2; }
+ | NOT cexpr
+ { $$ = define_cexpr(CEXPR_NOT, $2, 0);
+ if ($$ == 0) return -1; }
+ | cexpr AND cexpr
+ { $$ = define_cexpr(CEXPR_AND, $1, $3);
+ if ($$ == 0) return -1; }
+ | cexpr OR cexpr
+ { $$ = define_cexpr(CEXPR_OR, $1, $3);
+ if ($$ == 0) return -1; }
+ | cexpr_prim
+ { $$ = $1; }
+ ;
+cexpr_prim : U1 op U2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_USER, $2);
+ if ($$ == 0) return -1; }
+ | R1 role_mls_op R2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_ROLE, $2);
+ if ($$ == 0) return -1; }
+ | T1 op T2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_TYPE, $2);
+ if ($$ == 0) return -1; }
+ | U1 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, CEXPR_USER, $2);
+ if ($$ == 0) return -1; }
+ | U2 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_USER | CEXPR_TARGET), $2);
+ if ($$ == 0) return -1; }
+ | U3 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_USER | CEXPR_XTARGET), $2);
+ if ($$ == 0) return -1; }
+ | R1 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, CEXPR_ROLE, $2);
+ if ($$ == 0) return -1; }
+ | R2 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_TARGET), $2);
+ if ($$ == 0) return -1; }
+ | R3 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_XTARGET), $2);
+ if ($$ == 0) return -1; }
+ | T1 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, CEXPR_TYPE, $2);
+ if ($$ == 0) return -1; }
+ | T2 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_TARGET), $2);
+ if ($$ == 0) return -1; }
+ | T3 op { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_XTARGET), $2);
+ if ($$ == 0) return -1; }
+ | SAMEUSER
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_USER, CEXPR_EQ);
+ if ($$ == 0) return -1; }
+ | SOURCE ROLE { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, CEXPR_ROLE, CEXPR_EQ);
+ if ($$ == 0) return -1; }
+ | TARGET ROLE { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_TARGET), CEXPR_EQ);
+ if ($$ == 0) return -1; }
+ | ROLE role_mls_op
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_ROLE, $2);
+ if ($$ == 0) return -1; }
+ | SOURCE TYPE { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, CEXPR_TYPE, CEXPR_EQ);
+ if ($$ == 0) return -1; }
+ | TARGET TYPE { if (insert_separator(1)) return -1; } names_push
+ { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_TARGET), CEXPR_EQ);
+ if ($$ == 0) return -1; }
+ | L1 role_mls_op L2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1L2, $2);
+ if ($$ == 0) return -1; }
+ | L1 role_mls_op H2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1H2, $2);
+ if ($$ == 0) return -1; }
+ | H1 role_mls_op L2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_H1L2, $2);
+ if ($$ == 0) return -1; }
+ | H1 role_mls_op H2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_H1H2, $2);
+ if ($$ == 0) return -1; }
+ | L1 role_mls_op H1
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1H1, $2);
+ if ($$ == 0) return -1; }
+ | L2 role_mls_op H2
+ { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L2H2, $2);
+ if ($$ == 0) return -1; }
+ ;
+op : EQUALS
+ { $$ = CEXPR_EQ; }
+ | NOTEQUAL
+ { $$ = CEXPR_NEQ; }
+ ;
+role_mls_op : op
+ { $$ = $1; }
+ | DOM
+ { $$ = CEXPR_DOM; }
+ | DOMBY
+ { $$ = CEXPR_DOMBY; }
+ | INCOMP
+ { $$ = CEXPR_INCOMP; }
+ ;
+users : user_def
+ | users user_def
+ ;
+user_def : USER identifier ROLES names opt_mls_user ';'
+ {if (define_user()) return -1;}
+ ;
+opt_mls_user : LEVEL mls_level_def RANGE mls_range_def
+ |
+ ;
+initial_sid_contexts : initial_sid_context_def
+ | initial_sid_contexts initial_sid_context_def
+ ;
+initial_sid_context_def : SID identifier security_context_def
+ {if (define_initial_sid_context()) return -1;}
+ ;
+opt_dev_contexts : dev_contexts |
+ ;
+dev_contexts : dev_context_def
+ | dev_contexts dev_context_def
+ ;
+dev_context_def : pirq_context_def |
+ iomem_context_def |
+ ioport_context_def |
+ pci_context_def
+ ;
+pirq_context_def : PIRQCON number security_context_def
+ {if (define_pirq_context($2)) return -1;}
+ ;
+iomem_context_def : IOMEMCON number security_context_def
+ {if (define_iomem_context($2,$2)) return -1;}
+ | IOMEMCON number '-' number security_context_def
+ {if (define_iomem_context($2,$4)) return -1;}
+ ;
+ioport_context_def : IOPORTCON number security_context_def
+ {if (define_ioport_context($2,$2)) return -1;}
+ | IOPORTCON number '-' number security_context_def
+ {if (define_ioport_context($2,$4)) return -1;}
+ ;
+pci_context_def : PCIDEVICECON number security_context_def
+ {if (define_pcidevice_context($2)) return -1;}
+ ;
+opt_fs_contexts : fs_contexts
+ |
+ ;
+fs_contexts : fs_context_def
+ | fs_contexts fs_context_def
+ ;
+fs_context_def : FSCON number number security_context_def security_context_def
+ {if (define_fs_context($2,$3)) return -1;}
+ ;
+net_contexts : opt_port_contexts opt_netif_contexts opt_node_contexts
+ ;
+opt_port_contexts : port_contexts
+ |
+ ;
+port_contexts : port_context_def
+ | port_contexts port_context_def
+ ;
+port_context_def : PORTCON identifier number security_context_def
+ {if (define_port_context($3,$3)) return -1;}
+ | PORTCON identifier number '-' number security_context_def
+ {if (define_port_context($3,$5)) return -1;}
+ ;
+opt_netif_contexts : netif_contexts
+ |
+ ;
+netif_contexts : netif_context_def
+ | netif_contexts netif_context_def
+ ;
+netif_context_def : NETIFCON identifier security_context_def security_context_def
+ {if (define_netif_context()) return -1;}
+ ;
+opt_node_contexts : node_contexts
+ |
+ ;
+node_contexts : node_context_def
+ | node_contexts node_context_def
+ ;
+node_context_def : NODECON ipv4_addr_def ipv4_addr_def security_context_def
+ {if (define_ipv4_node_context()) return -1;}
+ | NODECON ipv6_addr ipv6_addr security_context_def
+ {if (define_ipv6_node_context()) return -1;}
+ ;
+opt_fs_uses : fs_uses
+ |
+ ;
+fs_uses : fs_use_def
+ | fs_uses fs_use_def
+ ;
+fs_use_def : FSUSEXATTR identifier security_context_def ';'
+ {if (define_fs_use(SECURITY_FS_USE_XATTR)) return -1;}
+ | FSUSETASK identifier security_context_def ';'
+ {if (define_fs_use(SECURITY_FS_USE_TASK)) return -1;}
+ | FSUSETRANS identifier security_context_def ';'
+ {if (define_fs_use(SECURITY_FS_USE_TRANS)) return -1;}
+ | FSUSEPSID identifier ';'
+ {if (define_fs_use(SECURITY_FS_USE_PSIDS)) return -1;}
+ ;
+opt_genfs_contexts : genfs_contexts
+ |
+ ;
+genfs_contexts : genfs_context_def
+ | genfs_contexts genfs_context_def
+ ;
+genfs_context_def : GENFSCON identifier path '-' identifier security_context_def
+ {if (define_genfs_context(1)) return -1;}
+ | GENFSCON identifier path '-' '-' {insert_id("-", 0);} security_context_def
+ {if (define_genfs_context(1)) return -1;}
+ | GENFSCON identifier path security_context_def
+ {if (define_genfs_context(0)) return -1;}
+ ;
+ipv4_addr_def : IPV4_ADDR
+ { if (insert_id(yytext,0)) return -1; }
+ ;
+security_context_def : identifier ':' identifier ':' identifier opt_mls_range_def
+ ;
+opt_mls_range_def : ':' mls_range_def
+ |
+ ;
+mls_range_def : mls_level_def '-' mls_level_def
+ {if (insert_separator(0)) return -1;}
+ | mls_level_def
+ {if (insert_separator(0)) return -1;}
+ ;
+mls_level_def : identifier ':' id_comma_list
+ {if (insert_separator(0)) return -1;}
+ | identifier
+ {if (insert_separator(0)) return -1;}
+ ;
+id_comma_list : identifier
+ | id_comma_list ',' identifier
+ ;
+tilde : '~'
+ ;
+asterisk : '*'
+ ;
+names : identifier
+ { if (insert_separator(0)) return -1; }
+ | nested_id_set
+ { if (insert_separator(0)) return -1; }
+ | asterisk
+ { if (insert_id("*", 0)) return -1;
+ if (insert_separator(0)) return -1; }
+ | tilde identifier
+ { if (insert_id("~", 0)) return -1;
+ if (insert_separator(0)) return -1; }
+ | tilde nested_id_set
+ { if (insert_id("~", 0)) return -1;
+ if (insert_separator(0)) return -1; }
+ | identifier '-' { if (insert_id("-", 0)) return -1; } identifier
+ { if (insert_separator(0)) return -1; }
+ ;
+tilde_push : tilde
+ { if (insert_id("~", 1)) return -1; }
+ ;
+asterisk_push : asterisk
+ { if (insert_id("*", 1)) return -1; }
+ ;
+names_push : identifier_push
+ | '{' identifier_list_push '}'
+ | asterisk_push
+ | tilde_push identifier_push
+ | tilde_push '{' identifier_list_push '}'
+ ;
+identifier_list_push : identifier_push
+ | identifier_list_push identifier_push
+ ;
+identifier_push : IDENTIFIER
+ { if (insert_id(yytext, 1)) return -1; }
+ ;
+identifier_list : identifier
+ | identifier_list identifier
+ ;
+nested_id_set : '{' nested_id_list '}'
+ ;
+nested_id_list : nested_id_element | nested_id_list nested_id_element
+ ;
+nested_id_element : identifier | '-' { if (insert_id("-", 0)) return -1; } identifier | nested_id_set
+ ;
+identifier : IDENTIFIER
+ { if (insert_id(yytext,0)) return -1; }
+ ;
+path : PATH
+ { if (insert_id(yytext,0)) return -1; }
+ ;
+number : NUMBER
+ { $$ = strtoul(yytext,NULL,0); }
+ ;
+ipv6_addr : IPV6_ADDR
+ { if (insert_id(yytext,0)) return -1; }
+ ;
+policycap_def : POLICYCAP identifier ';'
+ {if (define_polcap()) return -1;}
+ ;
+permissive_def : PERMISSIVE identifier ';'
+ {if (define_permissive()) return -1;}
+
+/*********** module grammar below ***********/
+
+module_policy : module_def avrules_block
+ { if (end_avrule_block(pass) == -1) return -1;
+ if (policydb_index_others(NULL, policydbp, 0)) return -1;
+ }
+ ;
+module_def : MODULE identifier version_identifier ';'
+ { if (define_policy(pass, 1) == -1) return -1; }
+ ;
+version_identifier : VERSION_IDENTIFIER
+ { if (insert_id(yytext,0)) return -1; }
+ | ipv4_addr_def /* version can look like ipv4 address */
+ ;
+avrules_block : avrule_decls avrule_user_defs
+ ;
+avrule_decls : avrule_decls avrule_decl
+ | avrule_decl
+ ;
+avrule_decl : rbac_decl
+ | te_decl
+ | cond_stmt_def
+ | require_block
+ | optional_block
+ | ';'
+ ;
+require_block : REQUIRE '{' require_list '}'
+ ;
+require_list : require_list require_decl
+ | require_decl
+ ;
+require_decl : require_class ';'
+ | require_decl_def require_id_list ';'
+ ;
+require_class : CLASS identifier names
+ { if (require_class(pass)) return -1; }
+ ;
+require_decl_def : ROLE { $$ = require_role; }
+ | TYPE { $$ = require_type; }
+ | ATTRIBUTE { $$ = require_attribute; }
+ | USER { $$ = require_user; }
+ | BOOL { $$ = require_bool; }
+ | SENSITIVITY { $$ = require_sens; }
+ | CATEGORY { $$ = require_cat; }
+ ;
+require_id_list : identifier
+ { if ($<require_func>0 (pass)) return -1; }
+ | require_id_list ',' identifier
+ { if ($<require_func>0 (pass)) return -1; }
+ ;
+optional_block : optional_decl '{' avrules_block '}'
+ { if (end_avrule_block(pass) == -1) return -1; }
+ optional_else
+ { if (end_optional(pass) == -1) return -1; }
+ ;
+optional_else : else_decl '{' avrules_block '}'
+ { if (end_avrule_block(pass) == -1) return -1; }
+ | /* empty */
+ ;
+optional_decl : OPTIONAL
+ { if (begin_optional(pass) == -1) return -1; }
+ ;
+else_decl : ELSE
+ { if (begin_optional_else(pass) == -1) return -1; }
+ ;
+avrule_user_defs : user_def avrule_user_defs
+ | /* empty */
+ ;
diff --git a/libqpol/src/policy_scan.l b/libqpol/src/policy_scan.l
new file mode 100644
index 0000000..75485f3
--- /dev/null
+++ b/libqpol/src/policy_scan.l
@@ -0,0 +1,320 @@
+/**
+ * @file policy_parse.y
+ *
+ * This file is based upon checkpolicy/policy_scan.l fram NSA's SVN
+ * repository. It has been modified to support older policy formats.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ *
+ * Updated: David Caplan, <dac@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Jason Tang <jtang@tresys.com>
+ *
+ * Added support for binary policy modules
+ *
+ * Copyright (C) 2003-5 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+/* FLASK */
+
+%{
+#undef YY_INPUT
+#define YY_INPUT(b, r, ms) (r = qpol_src_yyinput(b, ms))
+%}
+
+%{
+#include <sys/types.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef int (* require_func_t)();
+
+#include "policy_parse.h"
+
+static char linebuf[2][255];
+static unsigned int lno = 0;
+int yywarn(char *msg);
+
+void set_source_file(const char *name);
+
+char source_file[PATH_MAX];
+unsigned long source_lineno = 1;
+
+unsigned long policydb_lineno = 1;
+
+unsigned int policydb_errors = 0;
+
+/* redefine input so we can read from a string */
+/* borrowed from O'Reilly lex and yacc pg 157 */
+extern char qpol_src_input[];
+extern char *qpol_src_inputptr;/* current position in myinput */
+extern char *qpol_src_inputlim;/* end of data */
+int qpol_src_yyinput(char *buf, int max_size);
+
+%}
+
+%option nounput
+%option noyywrap
+
+%array
+letter [A-Za-z]
+digit [0-9]
+alnum [a-zA-Z0-9]
+hexval [0-9A-Fa-f]
+
+%%
+\n.* { strncpy(linebuf[lno], yytext+1, 255);
+ linebuf[lno][254] = 0;
+ lno = 1 - lno;
+ policydb_lineno++;
+ source_lineno++;
+ yyless(1); }
+CLONE |
+clone { return(CLONE); }
+COMMON |
+common { return(COMMON); }
+CLASS |
+class { return(CLASS); }
+CONSTRAIN |
+constrain { return(CONSTRAIN); }
+VALIDATETRANS |
+validatetrans { return(VALIDATETRANS); }
+INHERITS |
+inherits { return(INHERITS); }
+SID |
+sid { return(SID); }
+ROLE |
+role { return(ROLE); }
+ROLES |
+roles { return(ROLES); }
+TYPES |
+types { return(TYPES); }
+TYPEALIAS |
+typealias { return(TYPEALIAS); }
+TYPEATTRIBUTE |
+typeattribute { return(TYPEATTRIBUTE); }
+TYPEBOUNDS |
+typebounds { return(TYPEBOUNDS); }
+TYPE |
+type { return(TYPE); }
+BOOL |
+bool { return(BOOL); }
+IF |
+if { return(IF); }
+ELSE |
+else { return(ELSE); }
+ALIAS |
+alias { return(ALIAS); }
+ATTRIBUTE |
+attribute { return(ATTRIBUTE); }
+TYPE_TRANSITION |
+type_transition { return(TYPE_TRANSITION); }
+TYPE_MEMBER |
+type_member { return(TYPE_MEMBER); }
+TYPE_CHANGE |
+type_change { return(TYPE_CHANGE); }
+ROLE_TRANSITION |
+role_transition { return(ROLE_TRANSITION); }
+RANGE_TRANSITION |
+range_transition { return(RANGE_TRANSITION); }
+SENSITIVITY |
+sensitivity { return(SENSITIVITY); }
+DOMINANCE |
+dominance { return(DOMINANCE); }
+CATEGORY |
+category { return(CATEGORY); }
+LEVEL |
+level { return(LEVEL); }
+RANGE |
+range { return(RANGE); }
+MLSCONSTRAIN |
+mlsconstrain { return(MLSCONSTRAIN); }
+MLSVALIDATETRANS |
+mlsvalidatetrans { return(MLSVALIDATETRANS); }
+USER |
+user { return(USER); }
+NEVERALLOW |
+neverallow { return(NEVERALLOW); }
+ALLOW |
+allow { return(ALLOW); }
+AUDITALLOW |
+auditallow { return(AUDITALLOW); }
+AUDITDENY |
+auditdeny { return(AUDITDENY); }
+DONTAUDIT |
+dontaudit { return(DONTAUDIT); }
+SOURCE |
+source { return(SOURCE); }
+TARGET |
+target { return(TARGET); }
+SAMEUSER |
+sameuser { return(SAMEUSER);}
+module|MODULE { return(MODULE); }
+require|REQUIRE { return(REQUIRE); }
+optional|OPTIONAL { return(OPTIONAL); }
+OR |
+or { return(OR);}
+AND |
+and { return(AND);}
+NOT |
+not { return(NOT);}
+xor |
+XOR { return(XOR); }
+eq |
+EQ { return(EQUALS);}
+true |
+TRUE { return(CTRUE); }
+false |
+FALSE { return(CFALSE); }
+dom |
+DOM { return(DOM);}
+domby |
+DOMBY { return(DOMBY);}
+INCOMP |
+incomp { return(INCOMP);}
+fscon |
+FSCON { return(FSCON);}
+portcon |
+PORTCON { return(PORTCON);}
+netifcon |
+NETIFCON { return(NETIFCON);}
+nodecon |
+NODECON { return(NODECON);}
+fs_use_psid |
+FS_USE_PSID { return(FSUSEPSID);}
+pirqcon |
+PIRQCON { return(PIRQCON);}
+iomemcon |
+IOMEMCON { return(IOMEMCON);}
+ioportcon |
+IOPORTCON { return(IOPORTCON);}
+pcidevicecon |
+PCIDEVICECON { return(PCIDEVICECON);}
+fs_use_xattr |
+FS_USE_XATTR { return(FSUSEXATTR);}
+fs_use_task |
+FS_USE_TASK { return(FSUSETASK);}
+fs_use_trans |
+FS_USE_TRANS { return(FSUSETRANS);}
+genfscon |
+GENFSCON { return(GENFSCON);}
+r1 |
+R1 { return(R1); }
+r2 |
+R2 { return(R2); }
+r3 |
+R3 { return(R3); }
+u1 |
+U1 { return(U1); }
+u2 |
+U2 { return(U2); }
+u3 |
+U3 { return(U3); }
+t1 |
+T1 { return(T1); }
+t2 |
+T2 { return(T2); }
+t3 |
+T3 { return(T3); }
+l1 |
+L1 { return(L1); }
+l2 |
+L2 { return(L2); }
+h1 |
+H1 { return(H1); }
+h2 |
+H2 { return(H2); }
+policycap |
+POLICYCAP { return(POLICYCAP); }
+permissive |
+PERMISSIVE { return(PERMISSIVE); }
+"/"({alnum}|[_\.\-/])* { return(PATH); }
+{letter}({alnum}|[_\-])*([\.]?({alnum}|[_\-]))* { return(IDENTIFIER); }
+{digit}+|0x{hexval}+ { return(NUMBER); }
+{digit}{1,3}(\.{digit}{1,3}){3} { return(IPV4_ADDR); }
+{hexval}{0,4}":"{hexval}{0,4}":"({hexval}|[:.])* { return(IPV6_ADDR); }
+{digit}+(\.({alnum}|[_.])*)? { return(VERSION_IDENTIFIER); }
+#line[ ]1[ ]\"[^\n]*\" { set_source_file(yytext+9); }
+#line[ ]{digit}+ { source_lineno = atoi(yytext+6)-1; }
+#[^\n]* { /* delete comments */ }
+[ \t\f]+ { /* delete whitespace */ }
+"==" { return(EQUALS); }
+"!=" { return (NOTEQUAL); }
+"&&" { return (AND); }
+"||" { return (OR); }
+"!" { return (NOT); }
+"^" { return (XOR); }
+"," |
+":" |
+";" |
+"(" |
+")" |
+"{" |
+"}" |
+"[" |
+"-" |
+"." |
+"]" |
+"~" |
+"*" { return(yytext[0]); }
+. { yywarn("unrecognized character");}
+%%
+int yyerror(char *msg)
+{
+ if (source_file[0])
+ fprintf(stderr, "%s:%ld:",
+ source_file, source_lineno);
+ else
+ fprintf(stderr, "(unknown source)::");
+ fprintf(stderr, "ERROR '%s' at token '%s' on line %ld:\n%s\n%s\n",
+ msg,
+ yytext,
+ policydb_lineno,
+ linebuf[0], linebuf[1]);
+ policydb_errors++;
+ return -1;
+}
+
+int yywarn(char *msg)
+{
+ if (source_file[0])
+ fprintf(stderr, "%s:%ld:",
+ source_file, source_lineno);
+ else
+ fprintf(stderr, "(unknown source)::");
+ fprintf(stderr, "WARNING '%s' at token '%s' on line %ld:\n%s\n%s\n",
+ msg,
+ yytext,
+ policydb_lineno,
+ linebuf[0], linebuf[1]);
+ return 0;
+}
+
+void set_source_file(const char *name)
+{
+ source_lineno = 1;
+ strncpy(source_file, name, sizeof(source_file)-1);
+ source_file[sizeof(source_file)-1] = '\0';
+}
+
+int qpol_src_yyinput(char *buf, int max_size)
+{
+ int n = max_size < (qpol_src_inputlim - qpol_src_inputptr) ? max_size : (qpol_src_inputlim - qpol_src_inputptr);
+ if (n > 0) {
+ memcpy(buf, qpol_src_inputptr, n);
+ qpol_src_inputptr += n;
+ }
+
+ return n;
+}
+
+void init_scanner(void)
+{
+ yy_flush_buffer(YY_CURRENT_BUFFER);
+}
diff --git a/libqpol/src/portcon_query.c b/libqpol/src/portcon_query.c
new file mode 100644
index 0000000..d48bd37
--- /dev/null
+++ b/libqpol/src/portcon_query.c
@@ -0,0 +1,183 @@
+/**
+ * @file
+ * Defines the public interface for searching and iterating over portcon statements.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <netinet/in.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/context_query.h>
+#include <qpol/portcon_query.h>
+#include <sepol/policydb/policydb.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+
+int qpol_policy_get_portcon_by_port(const qpol_policy_t * policy, uint16_t low, uint16_t high, uint8_t protocol,
+ const qpol_portcon_t ** ocon)
+{
+ ocontext_t *tmp = NULL;
+ policydb_t *db = NULL;
+
+ if (ocon != NULL)
+ *ocon = NULL;
+
+ if (policy == NULL || ocon == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ for (tmp = db->ocontexts[OCON_PORT]; tmp; tmp = tmp->next) {
+ if (tmp->u.port.low_port == low && tmp->u.port.high_port == high && tmp->u.port.protocol == protocol)
+ break;
+ }
+
+ *ocon = (qpol_portcon_t *) tmp;
+
+ if (*ocon == NULL) {
+ ERR(policy, "could not find portcon statement for %u-%u:%u", low, high, protocol);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_portcon_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ int error = 0;
+ ocon_state_t *os = NULL;
+
+ if (iter != NULL)
+ *iter = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ os = calloc(1, sizeof(ocon_state_t));
+ if (os == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ os->head = os->cur = db->ocontexts[OCON_PORT];
+
+ if (qpol_iterator_create(policy, (void *)os, ocon_state_get_cur,
+ ocon_state_next, ocon_state_end, ocon_state_size, free, iter)) {
+ free(os);
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_portcon_get_protocol(const qpol_policy_t * policy, const qpol_portcon_t * ocon, uint8_t * protocol)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (protocol != NULL)
+ *protocol = 0;
+
+ if (policy == NULL || ocon == NULL || protocol == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+
+ *protocol = internal_ocon->u.port.protocol;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_portcon_get_low_port(const qpol_policy_t * policy, const qpol_portcon_t * ocon, uint16_t * port)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (port != NULL)
+ *port = 0;
+
+ if (policy == NULL || ocon == NULL || port == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+
+ *port = internal_ocon->u.port.low_port;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_portcon_get_high_port(const qpol_policy_t * policy, const qpol_portcon_t * ocon, uint16_t * port)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (port != NULL)
+ *port = 0;
+
+ if (policy == NULL || ocon == NULL || port == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+
+ *port = internal_ocon->u.port.high_port;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_portcon_get_context(const qpol_policy_t * policy, const qpol_portcon_t * ocon, const qpol_context_t ** context)
+{
+ ocontext_t *internal_ocon = NULL;
+
+ if (context != NULL)
+ *context = NULL;
+
+ if (policy == NULL || ocon == NULL || context == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ocon = (ocontext_t *) ocon;
+ *context = (qpol_context_t *) & (internal_ocon->context[0]);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/qpol_internal.h b/libqpol/src/qpol_internal.h
new file mode 100644
index 0000000..9b51e18
--- /dev/null
+++ b/libqpol/src/qpol_internal.h
@@ -0,0 +1,122 @@
+/**
+ * @file
+ * Defines common debug symbols and the internal policy structure.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QPOL_INTERNAL_H
+#define QPOL_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sepol/handle.h>
+#include <qpol/policy.h>
+#include <stdio.h>
+
+#define STATUS_SUCCESS 0
+#define STATUS_ERR -1
+#define STATUS_NODATA 1
+
+#define QPOL_MSG_ERR 1
+#define QPOL_MSG_WARN 2
+#define QPOL_MSG_INFO 3
+
+ struct qpol_extended_image;
+ struct qpol_policy;
+
+ struct qpol_module
+ {
+ char *name;
+ char *path;
+ char *version;
+ int type;
+ struct sepol_policydb *p;
+ int enabled;
+ struct qpol_policy *parent;
+ };
+
+ struct qpol_policy
+ {
+ struct sepol_policydb *p;
+ struct sepol_handle *sh;
+ qpol_callback_fn_t fn;
+ void *varg;
+ int options;
+ int type;
+ int modified;
+ struct qpol_extended_image *ext;
+ struct qpol_module **modules;
+ size_t num_modules;
+ char *file_data;
+ size_t file_data_sz;
+ int file_data_type;
+ };
+/* qpol_policy_t.file_data_type will be one of the following to denote
+ * the proper method of destroying the data:
+ * _BIN if policy is from a binary source (modular or kernel) destroy is a no-op
+ * _MMAP if policy is from a file and destroy should call munmap
+ * _MEM if policy is from open_from_memory and destroy should call free */
+#define QPOL_POLICY_FILE_DATA_TYPE_BIN 0
+#define QPOL_POLICY_FILE_DATA_TYPE_MMAP 1
+#define QPOL_POLICY_FILE_DATA_TYPE_MEM 2
+
+/**
+ * Create an extended image for a policy. This function modifies the policydb
+ * by adding additional records and information about attributes, initial sids
+ * and other components not normally written to a binary policy file. Subsequent
+ * calls to this function have no effect.
+ * @param policy The policy for which the extended image should be created.
+ * @return Returns 0 on success and < 0 on failure. If the call fails,
+ * errno will be set; the state of the policy is not guaranteed to be stable
+ * if this call fails.
+ */
+ int policy_extend(qpol_policy_t * policy);
+
+ extern void qpol_handle_msg(const qpol_policy_t * policy, int level, const char *fmt, ...);
+ int qpol_is_file_binpol(FILE * fp);
+ int qpol_is_file_mod_pkg(FILE * fp);
+/**
+ * Returns the version number of the binary policy. Note that this
+ * will rewind the file pointer.
+ *
+ * @return Non-negative policy version, or -1 general error for, -2
+ * wrong magic number for file, or -3 problem reading file.
+ */
+ int qpol_binpol_version(FILE * fp);
+
+/**
+ * Returns true if the file is a module package.
+ * @return Returns 1 for module packages, 0 otherwise.
+ */
+ int qpol_is_data_mod_pkg(char * data);
+
+#define ERR(policy, format, ...) qpol_handle_msg(policy, QPOL_MSG_ERR, format, __VA_ARGS__)
+#define WARN(policy, format, ...) qpol_handle_msg(policy, QPOL_MSG_WARN, format, __VA_ARGS__)
+#define INFO(policy, format, ...) qpol_handle_msg(policy, QPOL_MSG_INFO, format, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_INTERNAL_H */
diff --git a/libqpol/src/queue.c b/libqpol/src/queue.c
new file mode 100644
index 0000000..18667d5
--- /dev/null
+++ b/libqpol/src/queue.c
@@ -0,0 +1,183 @@
+/**
+ * @file
+ *
+ * This file is a copy of queue.c from NSA's CVS repository.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/* FLASK */
+
+/*
+ * Implementation of the double-ended queue type.
+ */
+
+#include <stdlib.h>
+#include "queue.h"
+
+queue_t queue_create(void)
+{
+ queue_t q;
+
+ q = (queue_t) malloc(sizeof(struct queue_info));
+ if (q == NULL)
+ return NULL;
+
+ q->head = q->tail = NULL;
+
+ return q;
+}
+
+int queue_insert(queue_t q, queue_element_t e)
+{
+ queue_node_ptr_t newnode;
+
+ if (!q)
+ return -1;
+
+ newnode = (queue_node_ptr_t) malloc(sizeof(struct queue_node));
+ if (newnode == NULL)
+ return -1;
+
+ newnode->element = e;
+ newnode->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = newnode;
+ } else {
+ q->tail->next = newnode;
+ q->tail = newnode;
+ }
+
+ return 0;
+}
+
+int queue_push(queue_t q, queue_element_t e)
+{
+ queue_node_ptr_t newnode;
+
+ if (!q)
+ return -1;
+
+ newnode = (queue_node_ptr_t) malloc(sizeof(struct queue_node));
+ if (newnode == NULL)
+ return -1;
+
+ newnode->element = e;
+ newnode->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = q->tail = newnode;
+ } else {
+ newnode->next = q->head;
+ q->head = newnode;
+ }
+
+ return 0;
+}
+
+queue_element_t queue_remove(queue_t q)
+{
+ queue_node_ptr_t node;
+ queue_element_t e;
+
+ if (!q)
+ return NULL;
+
+ if (q->head == NULL)
+ return NULL;
+
+ node = q->head;
+ q->head = q->head->next;
+ if (q->head == NULL)
+ q->tail = NULL;
+
+ e = node->element;
+ free(node);
+
+ return e;
+}
+
+queue_element_t queue_head(queue_t q)
+{
+ if (!q)
+ return NULL;
+
+ if (q->head == NULL)
+ return NULL;
+
+ return q->head->element;
+}
+
+void queue_destroy(queue_t q)
+{
+ queue_node_ptr_t p, temp;
+
+ if (!q)
+ return;
+
+ p = q->head;
+ while (p != NULL) {
+ temp = p;
+ p = p->next;
+ free(temp);
+ }
+
+ free(q);
+}
+
+int queue_map(queue_t q, int (*f) (queue_element_t, void *), void *vp)
+{
+ queue_node_ptr_t p;
+ int ret;
+
+ if (!q)
+ return 0;
+
+ p = q->head;
+ while (p != NULL) {
+ ret = f(p->element, vp);
+ if (ret)
+ return ret;
+ p = p->next;
+ }
+ return 0;
+}
+
+void queue_map_remove_on_error(queue_t q, int (*f) (queue_element_t, void *), void (*g) (queue_element_t, void *), void *vp)
+{
+ queue_node_ptr_t p, last, temp;
+ int ret;
+
+ if (!q)
+ return;
+
+ last = NULL;
+ p = q->head;
+ while (p != NULL) {
+ ret = f(p->element, vp);
+ if (ret) {
+ if (last) {
+ last->next = p->next;
+ if (last->next == NULL)
+ q->tail = last;
+ } else {
+ q->head = p->next;
+ if (q->head == NULL)
+ q->tail = NULL;
+ }
+
+ temp = p;
+ p = p->next;
+ g(temp->element, vp);
+ free(temp);
+ } else {
+ last = p;
+ p = p->next;
+ }
+ }
+
+ return;
+}
+
+/* FLASK */
diff --git a/libqpol/src/queue.h b/libqpol/src/queue.h
new file mode 100644
index 0000000..bca304b
--- /dev/null
+++ b/libqpol/src/queue.h
@@ -0,0 +1,67 @@
+/**
+ * @file
+ *
+ * This file is a copy of queue.h from NSA's CVS repository.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/* FLASK */
+
+/*
+ * A double-ended queue is a singly linked list of
+ * elements of arbitrary type that may be accessed
+ * at either end.
+ */
+
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+typedef void *queue_element_t;
+
+typedef struct queue_node *queue_node_ptr_t;
+
+typedef struct queue_node
+{
+ queue_element_t element;
+ queue_node_ptr_t next;
+} queue_node_t;
+
+typedef struct queue_info
+{
+ queue_node_ptr_t head;
+ queue_node_ptr_t tail;
+} queue_info_t;
+
+typedef queue_info_t *queue_t;
+
+queue_t queue_create(void);
+int queue_insert(queue_t, queue_element_t);
+int queue_push(queue_t, queue_element_t);
+queue_element_t queue_remove(queue_t);
+queue_element_t queue_head(queue_t);
+void queue_destroy(queue_t);
+
+/*
+ Applies the specified function f to each element in the
+ specified queue.
+
+ In addition to passing the element to f, queue_map
+ passes the specified void* pointer to f on each invocation.
+
+ If f returns a non-zero status, then queue_map will cease
+ iterating through the hash table and will propagate the error
+ return to its caller.
+ */
+int queue_map(queue_t, int (*f) (queue_element_t, void *), void *);
+
+/*
+ Same as queue_map, except that if f returns a non-zero status,
+ then the element will be removed from the queue and the g
+ function will be applied to the element.
+ */
+void queue_map_remove_on_error(queue_t, int (*f) (queue_element_t, void *), void (*g) (queue_element_t, void *), void *);
+
+#endif
+
+/* FLASK */
diff --git a/libqpol/src/rbacrule_query.c b/libqpol/src/rbacrule_query.c
new file mode 100644
index 0000000..7f84459
--- /dev/null
+++ b/libqpol/src/rbacrule_query.c
@@ -0,0 +1,358 @@
+/**
+ * @file
+ * Defines public interface for iterating over RBAC rules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/rbacrule_query.h>
+#include <stdlib.h>
+#include "iterator_internal.h"
+#include "qpol_internal.h"
+#include <sepol/policydb/policydb.h>
+
+typedef struct role_allow_state
+{
+ role_allow_t *head;
+ role_allow_t *cur;
+} role_allow_state_t;
+
+static int role_allow_state_end(const qpol_iterator_t * iter)
+{
+ role_allow_state_t *ras = NULL;
+
+ if (!iter || !(ras = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return ras->cur ? 0 : 1;
+}
+
+static void *role_allow_state_get_cur(const qpol_iterator_t * iter)
+{
+ role_allow_state_t *ras = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(ras = qpol_iterator_state(iter)) || !(db = qpol_iterator_policy(iter)) || role_allow_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return ras->cur;
+}
+
+static int role_allow_state_next(qpol_iterator_t * iter)
+{
+ role_allow_state_t *ras = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(ras = qpol_iterator_state(iter)) || !(db = qpol_iterator_policy(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (role_allow_state_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ ras->cur = ras->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t role_allow_state_size(const qpol_iterator_t * iter)
+{
+ role_allow_state_t *ras = NULL;
+ const policydb_t *db = NULL;
+ role_allow_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(ras = qpol_iterator_state(iter)) || !(db = qpol_iterator_policy(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ for (tmp = ras->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_policy_get_role_allow_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ role_allow_state_t *ras = NULL;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ ras = calloc(1, sizeof(role_allow_state_t));
+ if (!ras) {
+ /* errno set by calloc */
+ ERR(policy, "%s", strerror(errno));
+ return STATUS_ERR;
+ }
+ ras->head = ras->cur = db->role_allow;
+
+ if (qpol_iterator_create
+ (policy, (void *)ras, role_allow_state_get_cur, role_allow_state_next, role_allow_state_end, role_allow_state_size,
+ free, iter)) {
+ error = errno;
+ free(ras);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_allow_get_source_role(const qpol_policy_t * policy, const qpol_role_allow_t * rule, const qpol_role_t ** source)
+{
+ policydb_t *db = NULL;
+ role_allow_t *ra = NULL;
+
+ if (source) {
+ *source = NULL;
+ }
+
+ if (!policy || !rule || !source) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ ra = (role_allow_t *) rule;
+
+ *source = (qpol_role_t *) db->role_val_to_struct[ra->role - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_allow_get_target_role(const qpol_policy_t * policy, const qpol_role_allow_t * rule, const qpol_role_t ** target)
+{
+ policydb_t *db = NULL;
+ role_allow_t *ra = NULL;
+
+ if (target) {
+ *target = NULL;
+ }
+
+ if (!policy || !rule || !target) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ ra = (role_allow_t *) rule;
+
+ *target = (qpol_role_t *) db->role_val_to_struct[ra->new_role - 1];
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct role_trans_state
+{
+ role_trans_t *head;
+ role_trans_t *cur;
+} role_trans_state_t;
+
+static int role_trans_state_end(const qpol_iterator_t * iter)
+{
+ role_trans_state_t *rts = NULL;
+
+ if (!iter || !(rts = qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return rts->cur ? 0 : 1;
+}
+
+static void *role_trans_state_get_cur(const qpol_iterator_t * iter)
+{
+ role_trans_state_t *rts = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(rts = qpol_iterator_state(iter)) || !(db = qpol_iterator_policy(iter)) || role_trans_state_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return rts->cur;
+}
+
+static int role_trans_state_next(qpol_iterator_t * iter)
+{
+ role_trans_state_t *rts = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(rts = qpol_iterator_state(iter)) || !(db = qpol_iterator_policy(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (role_trans_state_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ rts->cur = rts->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t role_trans_state_size(const qpol_iterator_t * iter)
+{
+ role_trans_state_t *rts = NULL;
+ const policydb_t *db = NULL;
+ role_trans_t *tmp = NULL;
+ size_t count = 0;
+
+ if (!iter || !(rts = qpol_iterator_state(iter)) || !(db = qpol_iterator_policy(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ for (tmp = rts->head; tmp; tmp = tmp->next)
+ count++;
+
+ return count;
+}
+
+int qpol_policy_get_role_trans_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db = NULL;
+ role_trans_state_t *rts = NULL;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ rts = calloc(1, sizeof(role_trans_state_t));
+ if (!rts) {
+ /* errno set by calloc */
+ ERR(policy, "%s", strerror(errno));
+ return STATUS_ERR;
+ }
+ rts->head = rts->cur = db->role_tr;
+
+ if (qpol_iterator_create
+ (policy, (void *)rts, role_trans_state_get_cur, role_trans_state_next, role_trans_state_end, role_trans_state_size,
+ free, iter)) {
+ error = errno;
+ free(rts);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_trans_get_source_role(const qpol_policy_t * policy, const qpol_role_trans_t * rule, const qpol_role_t ** source)
+{
+ policydb_t *db = NULL;
+ role_trans_t *rt = NULL;
+
+ if (source) {
+ *source = NULL;
+ }
+
+ if (!policy || !rule || !source) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (role_trans_t *) rule;
+
+ *source = (qpol_role_t *) db->role_val_to_struct[rt->role - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_trans_get_target_type(const qpol_policy_t * policy, const qpol_role_trans_t * rule, const qpol_type_t ** target)
+{
+ policydb_t *db = NULL;
+ role_trans_t *rt = NULL;
+
+ if (target) {
+ *target = NULL;
+ }
+
+ if (!policy || !rule || !target) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (role_trans_t *) rule;
+
+ *target = (qpol_type_t *) db->type_val_to_struct[rt->type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_trans_get_default_role(const qpol_policy_t * policy, const qpol_role_trans_t * rule, const qpol_role_t ** dflt)
+{
+ policydb_t *db = NULL;
+ role_trans_t *rt = NULL;
+
+ if (dflt) {
+ *dflt = NULL;
+ }
+
+ if (!policy || !rule || !dflt) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ rt = (role_trans_t *) rule;
+
+ *dflt = (qpol_role_t *) db->role_val_to_struct[rt->new_role - 1];
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/role_query.c b/libqpol/src/role_query.c
new file mode 100644
index 0000000..daa4c06
--- /dev/null
+++ b/libqpol/src/role_query.c
@@ -0,0 +1,238 @@
+/**
+ * @file
+ * Implementation of the interface for searching and iterating over roles.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/expand.h>
+#include "iterator_internal.h"
+#include <qpol/role_query.h>
+#include <qpol/type_query.h>
+#include "qpol_internal.h"
+
+int qpol_policy_get_role_by_name(const qpol_policy_t * policy, const char *name, const qpol_role_t ** datum)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || datum == NULL) {
+ if (datum != NULL)
+ *datum = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_roles.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ *datum = NULL;
+ ERR(policy, "could not find datum for role %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *datum = (qpol_role_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_role_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_roles.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_get_value(const qpol_policy_t * policy, const qpol_role_t * datum, uint32_t * value)
+{
+ role_datum_t *internal_datum = NULL;
+
+ if (policy == NULL || datum == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (role_datum_t *) datum;
+ *value = internal_datum->s.value;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_get_dominate_iter(const qpol_policy_t * policy, const qpol_role_t * datum, qpol_iterator_t ** dominates)
+{
+ role_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ int error;
+ ebitmap_state_t *es = NULL;
+
+ if (policy == NULL || datum == NULL || dominates == NULL) {
+ if (dominates != NULL)
+ *dominates = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (role_datum_t *) datum;
+ db = &policy->p->p;
+
+ if (!(es = calloc(1, sizeof(ebitmap_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", "unable to create iterator state object");
+ errno = error;
+ return STATUS_ERR;
+ }
+ es->bmap = &internal_datum->dominates;
+
+ if (qpol_iterator_create(policy, (void *)es, ebitmap_state_get_cur_role,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, dominates)) {
+ error = errno;
+ free(es);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*dominates);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_get_type_iter(const qpol_policy_t * policy, const qpol_role_t * datum, qpol_iterator_t ** types)
+{
+ role_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ ebitmap_t *expanded_set = NULL;
+ int error;
+ ebitmap_state_t *es = NULL;
+
+ if (policy == NULL || datum == NULL || types == NULL) {
+ if (types != NULL)
+ *types = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (role_datum_t *) datum;
+ db = &policy->p->p;
+
+ if (!(expanded_set = calloc(1, sizeof(ebitmap_t)))) {
+ error = errno;
+ ERR(policy, "%s", "unable to create bitmap");
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ if (type_set_expand(&internal_datum->types, expanded_set, db, 1)) {
+ ebitmap_destroy(expanded_set);
+ free(expanded_set);
+ ERR(policy, "error reading type set for role %s", db->p_role_val_to_name[internal_datum->s.value - 1]);
+ errno = EIO;
+ return STATUS_ERR;
+ }
+
+ if (!(es = calloc(1, sizeof(ebitmap_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", "unable to create iterator state object");
+ ebitmap_destroy(expanded_set);
+ free(expanded_set);
+ errno = error;
+ return STATUS_ERR;
+ }
+ es->bmap = expanded_set;
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, (void *)es, ebitmap_state_get_cur_type,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, ebitmap_state_destroy, types)) {
+ error = errno;
+ ebitmap_state_destroy(es);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*types);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_role_get_name(const qpol_policy_t * policy, const qpol_role_t * datum, const char **name)
+{
+ role_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (role_datum_t *) datum;
+
+ *name = db->p_role_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/syn_rule_internal.h b/libqpol/src/syn_rule_internal.h
new file mode 100644
index 0000000..8910913
--- /dev/null
+++ b/libqpol/src/syn_rule_internal.h
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Protected definition for syntactic rules from the extended
+ * policy image.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef QPOL_SYN_RULE_INTERNAL_H
+#define QPOL_SYN_RULE_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ struct qpol_syn_rule
+ {
+ avrule_t *rule;
+ cond_node_t *cond;
+ /** 0 if this rule is unconditional or in a conditional's true branch, 1 if in else */
+ int cond_branch;
+/* char *mod_name; for later use */
+ };
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* QPOL_SYN_RULE_INTERNAL_H */
diff --git a/libqpol/src/syn_rule_query.c b/libqpol/src/syn_rule_query.c
new file mode 100644
index 0000000..3e63204
--- /dev/null
+++ b/libqpol/src/syn_rule_query.c
@@ -0,0 +1,782 @@
+/**
+ * @file
+ * Public interface for querying syntactic rules from the extended
+ * policy image.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <qpol/syn_rule_query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/util.h>
+#include <sepol/policydb/conditional.h>
+#include "qpol_internal.h"
+#include "iterator_internal.h"
+#include "syn_rule_internal.h"
+#include <errno.h>
+#include <stdlib.h>
+
+typedef struct syn_rule_class_state
+{
+ class_perm_node_t *head;
+ class_perm_node_t *cur;
+} syn_rule_class_state_t;
+
+static int syn_rule_class_state_end(const qpol_iterator_t * iter)
+{
+ syn_rule_class_state_t *srcs = NULL;
+
+ if (!iter || !(srcs = (syn_rule_class_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (srcs->cur == NULL)
+ return 1;
+ else
+ return 0;
+}
+
+static void *syn_rule_class_state_get_cur(const qpol_iterator_t * iter)
+{
+ syn_rule_class_state_t *srcs = NULL;
+ const policydb_t *db = NULL;
+
+ if (!iter || !(srcs = (syn_rule_class_state_t *) qpol_iterator_state(iter)) ||
+ !(db = qpol_iterator_policy(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return db->class_val_to_struct[srcs->cur->class - 1];
+}
+
+static int syn_rule_class_state_next(qpol_iterator_t * iter)
+{
+ syn_rule_class_state_t *srcs = NULL;
+
+ if (!iter || !(srcs = (syn_rule_class_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ srcs->cur = srcs->cur->next;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t syn_rule_class_state_size(const qpol_iterator_t * iter)
+{
+ syn_rule_class_state_t *srcs = NULL;
+ size_t count = 0;
+ class_perm_node_t *cpn = NULL;
+
+ if (!iter || !(srcs = (syn_rule_class_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ for (cpn = srcs->head; cpn; cpn = cpn->next)
+ count++;
+
+ return count;
+}
+
+typedef struct syn_rule_perm_state
+{
+ char **perm_list;
+ size_t perm_list_sz;
+ size_t cur;
+} syn_rule_perm_state_t;
+
+static int syn_rule_perm_state_end(const qpol_iterator_t * iter)
+{
+ syn_rule_perm_state_t *srps = NULL;
+
+ if (!iter || !(srps = (syn_rule_perm_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return (srps->cur >= srps->perm_list_sz ? 1 : 0);
+}
+
+static void *syn_rule_perm_state_get_cur(const qpol_iterator_t * iter)
+{
+ syn_rule_perm_state_t *srps = NULL;
+
+ if (!iter || !(srps = (syn_rule_perm_state_t *) qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return srps->perm_list[srps->cur];
+}
+
+static int syn_rule_perm_state_next(qpol_iterator_t * iter)
+{
+ syn_rule_perm_state_t *srps = NULL;
+
+ if (!iter || !(srps = (syn_rule_perm_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ if (qpol_iterator_end(iter)) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ srps->cur++;
+
+ return STATUS_SUCCESS;
+}
+
+static size_t syn_rule_perm_state_size(const qpol_iterator_t * iter)
+{
+ syn_rule_perm_state_t *srps = NULL;
+
+ if (!iter || !(srps = (syn_rule_perm_state_t *) qpol_iterator_state(iter))) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ return srps->perm_list_sz;
+}
+
+static void syn_rule_perm_state_free(void *state)
+{
+ size_t i;
+
+ syn_rule_perm_state_t *srps = (syn_rule_perm_state_t *) state;
+
+ if (!srps)
+ return;
+
+ for (i = 0; i < srps->perm_list_sz; i++)
+ free(srps->perm_list[i]);
+ free(srps->perm_list);
+ free(srps);
+}
+
+/***************************** type set functions ****************************/
+
+int qpol_type_set_get_included_types_iter(const qpol_policy_t * policy, const qpol_type_set_t * ts, qpol_iterator_t ** iter)
+{
+ type_set_t *internal_ts = NULL;
+ ebitmap_state_t *es = NULL;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !ts || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ error = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ts = (type_set_t *) ts;
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (!es) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_ts->types);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_type,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, iter)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_set_get_subtracted_types_iter(const qpol_policy_t * policy, const qpol_type_set_t * ts, qpol_iterator_t ** iter)
+{
+ type_set_t *internal_ts = NULL;
+ ebitmap_state_t *es = NULL;
+ int error = 0;
+
+ if (iter)
+ *iter = NULL;
+
+ if (!policy || !ts || !iter) {
+ ERR(policy, "%s", strerror(EINVAL));
+ error = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ts = (type_set_t *) ts;
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (!es) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_ts->negset);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_type,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, iter)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_set_get_is_star(const qpol_policy_t * policy, const qpol_type_set_t * ts, uint32_t * is_star)
+{
+ type_set_t *internal_ts = NULL;
+
+ if (is_star)
+ *is_star = 0;
+
+ if (!policy || !ts || !is_star) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ts = (type_set_t *) ts;
+
+ if (internal_ts->flags == TYPE_STAR)
+ *is_star = 1;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_set_get_is_comp(const qpol_policy_t * policy, const qpol_type_set_t * ts, uint32_t * is_comp)
+{
+ type_set_t *internal_ts = NULL;
+
+ if (is_comp)
+ *is_comp = 0;
+
+ if (!policy || !ts || !is_comp) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_ts = (type_set_t *) ts;
+
+ if (internal_ts->flags == TYPE_COMP)
+ *is_comp = 1;
+
+ return STATUS_SUCCESS;
+}
+
+/***************************** syn_avule functions ****************************/
+
+int qpol_syn_avrule_get_rule_type(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, uint32_t * rule_type)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (rule_type)
+ *rule_type = 0;
+
+ if (!policy || !rule || !rule_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ if (internal_rule->specified == AVRULE_DONTAUDIT)
+ *rule_type = QPOL_RULE_DONTAUDIT;
+ else
+ *rule_type = internal_rule->specified;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_source_type_set(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ const qpol_type_set_t ** source_set)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (source_set)
+ *source_set = NULL;
+
+ if (!policy || !rule || !source_set) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *source_set = (qpol_type_set_t *) (&internal_rule->stypes);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_target_type_set(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule,
+ const qpol_type_set_t ** target_set)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (target_set)
+ *target_set = NULL;
+
+ if (!policy || !rule || !target_set) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *target_set = (qpol_type_set_t *) (&internal_rule->ttypes);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_is_target_self(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, uint32_t * is_self)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (is_self)
+ *is_self = 0;
+
+ if (!policy || !rule || !is_self) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ if (internal_rule->flags & RULE_SELF)
+ *is_self = 1;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_class_iter(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, qpol_iterator_t ** classes)
+{
+ syn_rule_class_state_t *srcs = NULL;
+ avrule_t *internal_rule = NULL;
+ int error = 0;
+
+ if (classes)
+ *classes = NULL;
+
+ if (!policy || !rule || !classes) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(srcs = calloc(1, sizeof(syn_rule_class_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+ srcs->head = srcs->cur = internal_rule->perms;
+
+ if (qpol_iterator_create(policy, (void *)srcs,
+ syn_rule_class_state_get_cur, syn_rule_class_state_next,
+ syn_rule_class_state_end, syn_rule_class_state_size, free, classes)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ free(srcs);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_perm_iter(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, qpol_iterator_t ** perms)
+{
+ avrule_t *internal_rule = NULL;
+ policydb_t *db = NULL;
+ char **perm_list, *tmp = NULL, **tmp_copy = NULL;
+ class_perm_node_t *node = NULL;
+ size_t node_num = 0, i, cur, perm_list_sz = 0;
+ int error = 0;
+ syn_rule_perm_state_t *srps = NULL;
+
+ if (perms)
+ *perms = NULL;
+
+ if (!policy || !rule || !perms) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+ for (node = internal_rule->perms; node; node = node->next)
+ node_num++;
+
+ /* for now allocate space for maximum number of unique perms */
+ perm_list = calloc(node_num * 32, sizeof(char *));
+ if (!perm_list) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ for (node = internal_rule->perms; node; node = node->next) {
+ for (i = 0; i < db->class_val_to_struct[node->class - 1]->permissions.nprim; i++) {
+ if (!(node->data & (1 << i)))
+ continue;
+ tmp = sepol_av_to_string(db, node->class, (sepol_access_vector_t) (1 << i));
+ if (tmp) {
+ tmp++; /* remove prepended space */
+ for (cur = 0; cur < perm_list_sz; cur++)
+ if (!strcmp(tmp, perm_list[cur]))
+ break;
+ if (cur < perm_list_sz)
+ continue;
+ perm_list[perm_list_sz] = strdup(tmp);
+ if (!(perm_list[perm_list_sz])) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ perm_list_sz++;
+ } else {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ }
+ }
+
+ /* shrink to actual needed size */
+ tmp_copy = realloc(perm_list, perm_list_sz * sizeof(char *));
+ if (!tmp_copy) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ perm_list = tmp_copy;
+
+ srps = calloc(1, sizeof(syn_rule_perm_state_t));
+ if (!srps) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+ srps->perm_list = perm_list;
+ srps->perm_list_sz = perm_list_sz;
+ srps->cur = 0;
+
+ if (qpol_iterator_create(policy, (void *)srps,
+ syn_rule_perm_state_get_cur, syn_rule_perm_state_next,
+ syn_rule_perm_state_end, syn_rule_perm_state_size, syn_rule_perm_state_free, perms)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto err;
+ }
+
+ return STATUS_SUCCESS;
+
+ err:
+ for (i = 0; i < perm_list_sz; i++)
+ free(perm_list[i]);
+ free(perm_list);
+ errno = error;
+ return STATUS_ERR;
+}
+
+int qpol_syn_avrule_get_lineno(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, unsigned long *lineno)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (lineno)
+ *lineno = 0;
+
+ if (!policy || !rule || !lineno) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *lineno = internal_rule->line;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_cond(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, const qpol_cond_t ** cond)
+{
+ if (cond)
+ *cond = NULL;
+
+ if (!policy || !rule || !cond) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *cond = (qpol_cond_t *) ((struct qpol_syn_rule *)rule)->cond;
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_avrule_get_is_enabled(const qpol_policy_t * policy, const qpol_syn_avrule_t * rule, uint32_t * is_enabled)
+{
+ int truth;
+ if (is_enabled)
+ *is_enabled = 0;
+
+ if (!policy || !rule || !is_enabled) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!((struct qpol_syn_rule *)rule)->cond)
+ *is_enabled = 1;
+ else {
+ truth = cond_evaluate_expr(&policy->p->p, ((struct qpol_syn_rule *)rule)->cond->expr);
+ if (truth < 0) {
+ ERR(policy, "%s", strerror(ERANGE));
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+ if (!((struct qpol_syn_rule *)rule)->cond_branch)
+ *is_enabled = truth;
+ else
+ *is_enabled = truth ? 0 : 1;
+ }
+ return STATUS_SUCCESS;
+}
+
+/***************************** syn_terule functions ****************************/
+
+int qpol_syn_terule_get_rule_type(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, uint32_t * rule_type)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (rule_type)
+ *rule_type = 0;
+
+ if (!policy || !rule || !rule_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *rule_type = internal_rule->specified;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_source_type_set(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ const qpol_type_set_t ** source_set)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (source_set)
+ *source_set = NULL;
+
+ if (!policy || !rule || !source_set) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *source_set = (qpol_type_set_t *) (&internal_rule->stypes);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_target_type_set(const qpol_policy_t * policy, const qpol_syn_terule_t * rule,
+ const qpol_type_set_t ** target_set)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (target_set)
+ *target_set = NULL;
+
+ if (!policy || !rule || !target_set) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *target_set = (qpol_type_set_t *) (&internal_rule->ttypes);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_class_iter(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, qpol_iterator_t ** classes)
+{
+ syn_rule_class_state_t *srcs = NULL;
+ avrule_t *internal_rule = NULL;
+ int error = 0;
+
+ if (classes)
+ *classes = NULL;
+
+ if (!policy || !rule || !classes) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!(srcs = calloc(1, sizeof(syn_rule_class_state_t)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+ srcs->head = srcs->cur = internal_rule->perms;
+
+ if (qpol_iterator_create(policy, (void *)srcs,
+ syn_rule_class_state_get_cur, syn_rule_class_state_next,
+ syn_rule_class_state_end, syn_rule_class_state_size, free, classes)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ free(srcs);
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_default_type(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, const qpol_type_t ** dflt)
+{
+ avrule_t *internal_rule = NULL;
+ policydb_t *db = NULL;
+
+ if (dflt)
+ *dflt = 0;
+
+ if (!policy || !rule || !dflt) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+ db = &policy->p->p;
+
+ /* since it is required that default be the same for all classes just return the first */
+ *dflt = (qpol_type_t *) (db->type_val_to_struct[internal_rule->perms->data - 1]);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_lineno(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, unsigned long *lineno)
+{
+ avrule_t *internal_rule = NULL;
+
+ if (lineno)
+ *lineno = 0;
+
+ if (!policy || !rule || !lineno) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_rule = ((struct qpol_syn_rule *)rule)->rule;
+
+ *lineno = internal_rule->line;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_cond(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, const qpol_cond_t ** cond)
+{
+ if (cond)
+ *cond = NULL;
+
+ if (!policy || !rule || !cond) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *cond = (qpol_cond_t *) ((struct qpol_syn_rule *)rule)->cond;
+ return STATUS_SUCCESS;
+}
+
+int qpol_syn_terule_get_is_enabled(const qpol_policy_t * policy, const qpol_syn_terule_t * rule, uint32_t * is_enabled)
+{
+ int truth;
+ if (is_enabled)
+ *is_enabled = 0;
+
+ if (!policy || !rule || !is_enabled) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!((struct qpol_syn_rule *)rule)->cond)
+ *is_enabled = 1;
+ else {
+ truth = cond_evaluate_expr(&policy->p->p, ((struct qpol_syn_rule *)rule)->cond->expr);
+ if (truth < 0) {
+ ERR(policy, "%s", strerror(ERANGE));
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+ if (!((struct qpol_syn_rule *)rule)->cond_branch)
+ *is_enabled = truth;
+ else
+ *is_enabled = truth ? 0 : 1;
+ }
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/terule_query.c b/libqpol/src/terule_query.c
new file mode 100644
index 0000000..7973922
--- /dev/null
+++ b/libqpol/src/terule_query.c
@@ -0,0 +1,264 @@
+/**
+ * @file
+ * Implementation for the public interface for searching and iterating over type rules.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "iterator_internal.h"
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <qpol/terule_query.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/avtab.h>
+#include <sepol/policydb/util.h>
+#include <stdlib.h>
+#include "qpol_internal.h"
+
+int qpol_policy_get_terule_iter(const qpol_policy_t * policy, uint32_t rule_type_mask, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ avtab_state_t *state;
+
+ if (iter) {
+ *iter = NULL;
+ }
+ if (policy == NULL || iter == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+#if 1 // Seems to make sediff/sediffx work better without breaking things
+ if (!qpol_policy_has_capability(policy, QPOL_CAP_RULES_LOADED)) {
+ ERR(policy, "%s", "Cannot get terules: Rules not loaded");
+ errno = ENOTSUP;
+ return STATUS_ERR;
+ }
+#endif
+
+ db = &policy->p->p;
+
+ state = calloc(1, sizeof(avtab_state_t));
+ if (state == NULL) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return STATUS_ERR;
+ }
+ state->ucond_tab = &db->te_avtab;
+ state->cond_tab = &db->te_cond_avtab;
+ state->rule_type_mask = rule_type_mask;
+ state->node = db->te_avtab.htable[0];
+
+ if (qpol_iterator_create
+ (policy, state, avtab_state_get_cur, avtab_state_next, avtab_state_end, avtab_state_size, free, iter)) {
+ free(state);
+ return STATUS_ERR;
+ }
+ if (state->node == NULL || !(state->node->key.specified & state->rule_type_mask)) {
+ avtab_state_next(*iter);
+ }
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_source_type(const qpol_policy_t * policy, const qpol_terule_t * rule, const qpol_type_t ** source)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t terule = NULL;
+
+ if (source) {
+ *source = NULL;
+ }
+
+ if (!policy || !rule || !source) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ terule = (avtab_ptr_t) rule;
+
+ *source = (qpol_type_t *) db->type_val_to_struct[terule->key.source_type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_target_type(const qpol_policy_t * policy, const qpol_terule_t * rule, const qpol_type_t ** target)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t terule = NULL;
+
+ if (target) {
+ *target = NULL;
+ }
+
+ if (!policy || !rule || !target) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ terule = (avtab_ptr_t) rule;
+
+ *target = (qpol_type_t *) db->type_val_to_struct[terule->key.target_type - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_object_class(const qpol_policy_t * policy, const qpol_terule_t * rule, const qpol_class_t ** obj_class)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t terule = NULL;
+
+ if (obj_class) {
+ *obj_class = NULL;
+ }
+
+ if (!policy || !rule || !obj_class) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ terule = (avtab_ptr_t) rule;
+
+ *obj_class = (qpol_class_t *) db->class_val_to_struct[terule->key.target_class - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_default_type(const qpol_policy_t * policy, const qpol_terule_t * rule, const qpol_type_t ** dflt)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t terule = NULL;
+
+ if (dflt) {
+ *dflt = NULL;
+ }
+
+ if (!policy || !rule || !dflt) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ terule = (avtab_ptr_t) rule;
+
+ *dflt = (qpol_type_t *) db->type_val_to_struct[terule->datum.data - 1];
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_rule_type(const qpol_policy_t * policy, const qpol_terule_t * rule, uint32_t * rule_type)
+{
+ policydb_t *db = NULL;
+ avtab_ptr_t terule = NULL;
+
+ if (rule_type) {
+ *rule_type = 0;
+ }
+
+ if (!policy || !rule || !rule_type) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ terule = (avtab_ptr_t) rule;
+
+ *rule_type = (terule->key.specified & (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER));
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_cond(const qpol_policy_t * policy, const qpol_terule_t * rule, const qpol_cond_t ** cond)
+{
+ avtab_ptr_t terule = NULL;
+
+ if (cond) {
+ *cond = NULL;
+ }
+
+ if (!policy || !rule || !cond) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ terule = (avtab_ptr_t) rule;
+
+ *cond = (qpol_cond_t *) terule->parse_context;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_is_enabled(const qpol_policy_t * policy, const qpol_terule_t * rule, uint32_t * is_enabled)
+{
+ avtab_ptr_t terule = NULL;
+
+ if (is_enabled) {
+ *is_enabled = 0;
+ }
+
+ if (!policy || !rule || !is_enabled) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ terule = (avtab_ptr_t) rule;
+
+ *is_enabled = ((terule->merged & QPOL_COND_RULE_ENABLED) ? 1 : 0);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_terule_get_which_list(const qpol_policy_t * policy, const qpol_terule_t * rule, uint32_t * which_list)
+{
+ avtab_ptr_t terule = NULL;
+
+ if (which_list) {
+ *which_list = 0;
+ }
+
+ if (!policy || !rule || !which_list) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ terule = (avtab_ptr_t) rule;
+
+ if (!terule->parse_context) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ *which_list = ((terule->merged & QPOL_COND_RULE_LIST) ? 1 : 0);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/type_query.c b/libqpol/src/type_query.c
new file mode 100644
index 0000000..a746180
--- /dev/null
+++ b/libqpol/src/type_query.c
@@ -0,0 +1,468 @@
+/**
+ * @file
+ * Implementation of the interface for searching and iterating over types.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <qpol/iterator.h>
+#include <qpol/policy.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/expand.h>
+#include "iterator_internal.h"
+#include <qpol/type_query.h>
+#include "qpol_internal.h"
+
+int qpol_policy_get_type_by_name(const qpol_policy_t * policy, const char *name, const qpol_type_t ** datum)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || datum == NULL) {
+ if (datum != NULL)
+ *datum = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_types.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ *datum = NULL;
+ ERR(policy, "could not find datum for type %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *datum = (qpol_type_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_type_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ int error = 0;
+ hash_state_t *hs = NULL;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_types.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_get_value(const qpol_policy_t * policy, const qpol_type_t * datum, uint32_t * value)
+{
+ type_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (type_datum_t *) datum;
+ if (internal_datum->flavor == TYPE_ALIAS) {
+ /* aliases that came from modules should use the value
+ * referenced to by that alias */
+ *value = internal_datum->primary;
+ } else {
+ *value = internal_datum->s.value;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+/**
+ * Determine if a type_datum_t is an alias or a non-alias (primary
+ * type or an attribute). For aliases declared in base policies, they
+ * will have no primary value and a flavor of TYPE_TYPE. For aliases
+ * declared in modules, they have a flavor of TYPE_ALIAS; their
+ * primary value points to the new /linked/ type's value.
+ *
+ * @param datum Type datum to check.
+ *
+ * @return 1 if the datum is an alias, 0 if not.
+ */
+static int is_type_really_an_alias(const type_datum_t * datum)
+{
+ if (datum->primary == 0 && datum->flavor == TYPE_TYPE) {
+ return 1;
+ }
+ if (datum->flavor == TYPE_ALIAS) {
+ return 1;
+ }
+ return 0;
+}
+
+int qpol_type_get_isalias(const qpol_policy_t * policy, const qpol_type_t * datum, unsigned char *isalias)
+{
+ type_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || isalias == NULL) {
+ if (isalias != NULL)
+ *isalias = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ internal_datum = (type_datum_t *) datum;
+ *isalias = is_type_really_an_alias(internal_datum);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_get_isattr(const qpol_policy_t * policy, const qpol_type_t * datum, unsigned char *isattr)
+{
+ type_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || isattr == NULL) {
+ if (isattr != NULL)
+ *isattr = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (type_datum_t *) datum;
+ *isattr = (internal_datum->flavor == TYPE_ATTRIB ? 1 : 0);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_get_ispermissive(const qpol_policy_t * policy, const qpol_type_t * datum, unsigned char *ispermissive)
+{
+ if (policy == NULL || datum == NULL || ispermissive == NULL) {
+ if (ispermissive != NULL)
+ *ispermissive = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+#ifdef HAVE_SEPOL_PERMISSIVE_TYPES
+ /* checking internal_datum->flags for permissive won't work,
+ because the type could be an alias. so instead, look up
+ its value within the permissive map */
+ uint32_t value;
+ if (qpol_type_get_value(policy, datum, &value) < 0) {
+ return STATUS_ERR;
+ }
+ policydb_t *p = &policy->p->p;
+ /* note that unlike other bitmaps, this one does not subtract
+ 1 in the bitmap */
+ *ispermissive = ebitmap_get_bit(&p->permissive_map, value);
+#else
+ *ispermissive = 0;
+#endif
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_get_type_iter(const qpol_policy_t * policy, const qpol_type_t * datum, qpol_iterator_t ** types)
+{
+ type_datum_t *internal_datum = NULL;
+ ebitmap_state_t *es = NULL;
+ int error = 0;
+
+ if (types != NULL)
+ *types = NULL;
+
+ if (policy == NULL || datum == NULL || types == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (type_datum_t *) datum;
+
+ if (internal_datum->flavor != TYPE_ATTRIB) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_NODATA;
+ }
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (es == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_datum->types);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_type,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, types)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*types);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_get_attr_iter(const qpol_policy_t * policy, const qpol_type_t * datum, qpol_iterator_t ** attrs)
+{
+ type_datum_t *internal_datum = NULL;
+ ebitmap_state_t *es = NULL;
+ int error = 0;
+
+ if (attrs != NULL)
+ *attrs = NULL;
+
+ if (policy == NULL || datum == NULL || attrs == NULL) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (type_datum_t *) datum;
+
+ if (internal_datum->flavor == TYPE_ATTRIB) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_NODATA;
+ }
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (es == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_datum->types);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_type,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, attrs)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*attrs);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_type_get_name(const qpol_policy_t * policy, const qpol_type_t * datum, const char **name)
+{
+ type_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (type_datum_t *) datum;
+
+ *name = db->p_type_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
+
+typedef struct type_alias_hash_state
+{
+ unsigned int bucket;
+ hashtab_node_t *node;
+ hashtab_t *table;
+ uint32_t val;
+} type_alias_hash_state_t;
+
+/**
+ * For aliases that came from the base policy, their primary type is
+ * referenced by s.value. For aliases that came from modules, their
+ * primary type is referenced by the primary field.
+ *
+ * @param datum Alias whose primary value to get.
+ *
+ * @return The primary type's identifier.
+ */
+static uint32_t get_alias_primary(const type_datum_t * datum)
+{
+ if (datum->flavor == TYPE_TYPE) {
+ return datum->s.value;
+ } else {
+ return datum->primary;
+ }
+}
+
+static int hash_state_next_type_alias(qpol_iterator_t * iter)
+{
+ type_alias_hash_state_t *hs = NULL;
+ type_datum_t *datum = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+ hs = (type_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return STATUS_ERR;
+ }
+
+ do {
+ hash_state_next(iter);
+ datum = hs->node ? (type_datum_t *) hs->node->datum : NULL;
+ } while (datum != NULL && (hs->val != get_alias_primary(datum) || !is_type_really_an_alias(datum)));
+
+ return STATUS_SUCCESS;
+}
+
+static void *hash_state_get_cur_alias(const qpol_iterator_t * iter)
+{
+ type_alias_hash_state_t *hs = NULL;
+
+ if (iter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ hs = (type_alias_hash_state_t *) qpol_iterator_state(iter);
+ if (hs == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (hs->bucket >= (*(hs->table))->size) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ return hs->node->key;
+}
+
+static size_t hash_alias_state_size(const qpol_iterator_t * iter)
+{
+ type_alias_hash_state_t *hs = NULL;
+ type_datum_t *tmp_datum;
+ hashtab_node_t *tmp_node;
+ uint32_t tmp_bucket = 0;
+ size_t count = 0;
+
+ if (iter == NULL || qpol_iterator_state(iter) == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ hs = (type_alias_hash_state_t *) qpol_iterator_state(iter);
+
+ for (tmp_bucket = 0; tmp_bucket < (*(hs->table))->size; tmp_bucket++) {
+ for (tmp_node = (*(hs->table))->htable[tmp_bucket]; tmp_node; tmp_node = tmp_node->next) {
+ tmp_datum = tmp_node ? tmp_node->datum : NULL;
+ if (tmp_datum) {
+ if (hs->val == get_alias_primary(tmp_datum) && is_type_really_an_alias(tmp_datum)) {
+ count++;
+ }
+ }
+ }
+ }
+ return count;
+}
+
+int qpol_type_get_alias_iter(const qpol_policy_t * policy, const qpol_type_t * datum, qpol_iterator_t ** aliases)
+{
+ type_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+ int error = 0;
+ type_alias_hash_state_t *hs = NULL;
+
+ if (policy == NULL || datum == NULL || aliases == NULL) {
+ if (aliases != NULL)
+ *aliases = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (type_datum_t *) datum;
+
+ hs = calloc(1, sizeof(type_alias_hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_types.table;
+ hs->node = (*(hs->table))->htable[0];
+ hs->val = get_alias_primary(internal_datum);
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur_alias,
+ hash_state_next_type_alias, hash_state_end, hash_alias_state_size, free, aliases)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL || hs->val != get_alias_primary((type_datum_t *) (hs->node->datum)) ||
+ !is_type_really_an_alias((type_datum_t *) hs->node->datum))
+ hash_state_next_type_alias(*aliases);
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/user_query.c b/libqpol/src/user_query.c
new file mode 100644
index 0000000..37237d5
--- /dev/null
+++ b/libqpol/src/user_query.c
@@ -0,0 +1,224 @@
+/**
+ * @file
+ * Implementation of the interface for searching and iterating over users.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/expand.h>
+
+#include <qpol/iterator.h>
+#include <qpol/mls_query.h>
+#include <qpol/policy.h>
+#include <qpol/role_query.h>
+#include <qpol/user_query.h>
+#include "iterator_internal.h"
+#include "qpol_internal.h"
+
+int qpol_policy_get_user_by_name(const qpol_policy_t * policy, const char *name, const qpol_user_t ** datum)
+{
+ hashtab_datum_t internal_datum;
+ policydb_t *db;
+
+ if (policy == NULL || name == NULL || datum == NULL) {
+ if (datum != NULL)
+ *datum = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = hashtab_search(db->p_users.table, (const hashtab_key_t)name);
+ if (internal_datum == NULL) {
+ *datum = NULL;
+ ERR(policy, "could not find datum for user %s", name);
+ errno = ENOENT;
+ return STATUS_ERR;
+ }
+ *datum = (qpol_user_t *) internal_datum;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_policy_get_user_iter(const qpol_policy_t * policy, qpol_iterator_t ** iter)
+{
+ policydb_t *db;
+ hash_state_t *hs = NULL;
+ int error = 0;
+
+ if (policy == NULL || iter == NULL) {
+ if (iter != NULL)
+ *iter = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+
+ hs = calloc(1, sizeof(hash_state_t));
+ if (hs == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+ hs->table = &db->p_users.table;
+ hs->node = (*(hs->table))->htable[0];
+
+ if (qpol_iterator_create(policy, (void *)hs, hash_state_get_cur,
+ hash_state_next, hash_state_end, hash_state_size, free, iter)) {
+ free(hs);
+ return STATUS_ERR;
+ }
+
+ if (hs->node == NULL)
+ hash_state_next(*iter);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_user_get_value(const qpol_policy_t * policy, const qpol_user_t * datum, uint32_t * value)
+{
+ user_datum_t *internal_datum;
+
+ if (policy == NULL || datum == NULL || value == NULL) {
+ if (value != NULL)
+ *value = 0;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (user_datum_t *) datum;
+ *value = internal_datum->s.value;
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_user_get_role_iter(const qpol_policy_t * policy, const qpol_user_t * datum, qpol_iterator_t ** roles)
+{
+ user_datum_t *internal_datum = NULL;
+ int error = 0;
+ ebitmap_state_t *es = NULL;
+
+ if (policy == NULL || datum == NULL || roles == NULL) {
+ if (roles != NULL)
+ *roles = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ internal_datum = (user_datum_t *) datum;
+
+ es = calloc(1, sizeof(ebitmap_state_t));
+ if (es == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return STATUS_ERR;
+ }
+
+ es->bmap = &(internal_datum->roles.roles);
+ es->cur = es->bmap->node ? es->bmap->node->startbit : 0;
+
+ if (qpol_iterator_create(policy, es, ebitmap_state_get_cur_role,
+ ebitmap_state_next, ebitmap_state_end, ebitmap_state_size, free, roles)) {
+ free(es);
+ return STATUS_ERR;
+ }
+
+ if (es->bmap->node && !ebitmap_get_bit(es->bmap, es->cur))
+ ebitmap_state_next(*roles);
+
+ return STATUS_SUCCESS;
+}
+
+int qpol_user_get_range(const qpol_policy_t * policy, const qpol_user_t * datum, const qpol_mls_range_t ** range)
+{
+ user_datum_t *internal_datum = NULL;
+
+ if (policy == NULL || datum == NULL || range == NULL) {
+ if (range != NULL)
+ *range = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!qpol_policy_has_capability(policy, QPOL_CAP_MLS)) {
+ *range = NULL;
+ } else {
+ internal_datum = (user_datum_t *) datum;
+ *range = (qpol_mls_range_t *) & internal_datum->exp_range;
+ }
+ return STATUS_SUCCESS;
+}
+
+int qpol_user_get_dfltlevel(const qpol_policy_t * policy, const qpol_user_t * datum, const qpol_mls_level_t ** level)
+{
+ user_datum_t *internal_datum = NULL;
+
+ if (policy == NULL || datum == NULL || level == NULL) {
+ if (level != NULL)
+ *level = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ if (!qpol_policy_has_capability(policy, QPOL_CAP_MLS)) {
+ *level = NULL;
+ } else {
+ internal_datum = (user_datum_t *) datum;
+ *level = (qpol_mls_level_t *) & internal_datum->exp_dfltlevel;
+ }
+ return STATUS_SUCCESS;
+}
+
+int qpol_user_get_name(const qpol_policy_t * policy, const qpol_user_t * datum, const char **name)
+{
+ user_datum_t *internal_datum = NULL;
+ policydb_t *db = NULL;
+
+ if (policy == NULL || datum == NULL || name == NULL) {
+ if (name != NULL)
+ *name = NULL;
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return STATUS_ERR;
+ }
+
+ db = &policy->p->p;
+ internal_datum = (user_datum_t *) datum;
+
+ *name = db->p_user_val_to_name[internal_datum->s.value - 1];
+
+ return STATUS_SUCCESS;
+}
diff --git a/libqpol/src/util.c b/libqpol/src/util.c
new file mode 100644
index 0000000..7c49876
--- /dev/null
+++ b/libqpol/src/util.c
@@ -0,0 +1,231 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "qpol_internal.h"
+
+#include <qpol/util.h>
+
+#include <glob.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <selinux/selinux.h>
+
+const char *libqpol_get_version(void)
+{
+ return LIBQPOL_VERSION_STRING;
+}
+
+static int search_policy_source_file(char **path)
+{
+ int error;
+ char *source_path;
+ if (asprintf(&source_path, "%s/src/policy/policy.conf", selinux_policy_root()) < 0) {
+ return -1;
+ }
+ if (access(source_path, R_OK) < 0) {
+ error = errno;
+ free(source_path);
+ errno = error;
+ return 1;
+ }
+ *path = source_path;
+ return 0;
+}
+
+static int get_binpol_version(const char *policy_fname)
+{
+ FILE *policy_fp = NULL;
+ int ret_version, error;
+
+ policy_fp = fopen(policy_fname, "r");
+ if (policy_fp == NULL) {
+ return -1;
+ }
+ if (!qpol_is_file_binpol(policy_fp)) {
+ error = errno;
+ fclose(policy_fp);
+ errno = error;
+ return -1;
+ }
+ ret_version = qpol_binpol_version(policy_fp);
+ fclose(policy_fp);
+ return ret_version;
+}
+
+static int search_policy_binary_file(char **path)
+{
+ const char *binary_path;
+ if ((binary_path = selinux_binary_policy_path()) == NULL) {
+ return -1;
+ }
+
+ int expected_version = -1, latest_version = -1;
+#ifdef LIBSELINUX
+ /* if the system has SELinux enabled, prefer the policy whose
+ name matches the current policy version */
+ if ((expected_version = security_policyvers()) < 0) {
+ return -1;
+ }
+#endif
+
+ glob_t glob_buf;
+ struct stat fs;
+ int rt, error = 0, retval = -1;
+ size_t i;
+ char *pattern = NULL;
+ if (asprintf(&pattern, "%s.*", binary_path) < 0) {
+ return -1;
+ }
+ glob_buf.gl_offs = 1;
+ glob_buf.gl_pathc = 0;
+ rt = glob(pattern, GLOB_DOOFFS, NULL, &glob_buf);
+ if (rt != 0 && rt != GLOB_NOMATCH) {
+ errno = EIO;
+ return -1;
+ }
+
+ for (i = 0; i < glob_buf.gl_pathc; i++) {
+ char *p = glob_buf.gl_pathv[i + glob_buf.gl_offs];
+ if (stat(p, &fs) != 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (S_ISDIR(fs.st_mode))
+ continue;
+
+ if ((rt = get_binpol_version(p)) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ if (rt > latest_version || rt == expected_version) {
+ free(*path);
+ if ((*path = strdup(p)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if (rt == expected_version) {
+ break;
+ }
+ latest_version = rt;
+ }
+ }
+
+ if (*path == NULL) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ cleanup:
+ free(pattern);
+ globfree(&glob_buf);
+ if (retval == -1) {
+ errno = error;
+ }
+ return retval;
+}
+
+int qpol_default_policy_find(char **path)
+{
+ int rt;
+ if (path == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ *path = NULL;
+ /* Try default source policy first as a source policy contains
+ * more useful information. */
+ if ((rt = search_policy_source_file(path)) <= 0) {
+ return rt;
+ }
+ /* Try a binary policy */
+ return search_policy_binary_file(path);
+}
+
+#include <stdlib.h>
+#include <bzlib.h>
+#include <string.h>
+#include <sys/sendfile.h>
+
+#define BZ2_MAGICSTR "BZh"
+#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1)
+
+/* qpol_bunzip() uncompresses a file to '*data', returning the total number of
+ * uncompressed bytes in the file.
+ * Returns -1 if file could not be decompressed.
+ * Originally from libsemanage/src/direct_api.c, with slight mods */
+ssize_t qpol_bunzip(FILE *f, char **data)
+{
+ BZFILE* b;
+ size_t nBuf;
+ char buf[1<<18];
+ size_t size = sizeof(buf);
+ int bzerror;
+ size_t total=0;
+ int small=0; // Set to 1 to use less memory decompressing (about 2x slower)
+
+ bzerror = fread(buf, 1, BZ2_MAGICLEN, f);
+ rewind(f);
+ if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN))
+ return -1;
+
+ b = BZ2_bzReadOpen ( &bzerror, f, 0, small, NULL, 0 );
+ if ( bzerror != BZ_OK ) {
+ BZ2_bzReadClose ( &bzerror, b );
+ return -1;
+ }
+
+ char *uncompress = realloc(NULL, size);
+
+ while ( bzerror == BZ_OK) {
+ nBuf = BZ2_bzRead ( &bzerror, b, buf, sizeof(buf));
+ if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) {
+ if (total + nBuf > size) {
+ size *= 2;
+ uncompress = realloc(uncompress, size);
+ }
+ memcpy(&uncompress[total], buf, nBuf);
+ total += nBuf;
+ }
+ }
+ if ( bzerror != BZ_STREAM_END ) {
+ BZ2_bzReadClose ( &bzerror, b );
+ free(uncompress);
+ return -1;
+ }
+ BZ2_bzReadClose ( &bzerror, b );
+
+ *data = uncompress;
+ return total;
+}
+
diff --git a/libqpol/swig/Makefile.am b/libqpol/swig/Makefile.am
new file mode 100644
index 0000000..b1e2d4e
--- /dev/null
+++ b/libqpol/swig/Makefile.am
@@ -0,0 +1,15 @@
+if DO_SWIGIFY_PYTHON
+ MAYBE_PYSWIG = python
+endif
+
+if DO_SWIGIFY_JAVA
+ MAYBE_JSWIG = java
+endif
+
+if DO_SWIGIFY_TCL
+ MAYBE_TCLSWIG = tcl
+endif
+
+SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG)
+
+dist_noinst_DATA = qpol.i
diff --git a/libqpol/swig/java/MANIFEST.MF.in b/libqpol/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..0cd71bf
--- /dev/null
+++ b/libqpol/swig/java/MANIFEST.MF.in
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+
+Name: com/tresys/setools/
+Specification-Title: "SETools Java Libraries"
+Specification-Version: "@VERSION@"
+Specification-Vendor: "Tresys Technology"
+Implementation-Title: "com.tresys.setools.qpol"
+Implementation-Version: "@libqpol_version@"
+Implementation-Vendor: "Tresys Technology"
diff --git a/libqpol/swig/java/Makefile.am b/libqpol/swig/java/Makefile.am
new file mode 100644
index 0000000..a25eacb
--- /dev/null
+++ b/libqpol/swig/java/Makefile.am
@@ -0,0 +1,97 @@
+wrappedso_DATA = libjqpol.so.@libqpol_version@
+wrappedso_SONAME = @libqpol_jswig_soname@
+short_name = libjqpol.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.qpol
+package_dir = $(dir $(subst .,/,$(package_name)))qpol
+
+wrappedjar_DATA = qpol.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../qpol.i
+BUILT_SOURCES = qpol_wrap.c \
+ qpol_avrule_t.java \
+ qpol_bool_t.java \
+ qpol_capability_e.java \
+ qpol_cat_t.java \
+ qpol_class_t.java \
+ qpol_common_t.java \
+ qpol_cond_expr_node_t.java \
+ qpol_cond_t.java \
+ qpolConstants.java \
+ qpol_constraint_expr_node_t.java \
+ qpol_constraint_t.java \
+ qpol_context_t.java \
+ qpol_fs_use_t.java \
+ qpol_genfscon_t.java \
+ qpol_isid_t.java \
+ qpol_iterator_t.java \
+ qpol.java \
+ qpolJNI.java \
+ qpol_level_t.java \
+ qpol_mls_level_t.java \
+ qpol_mls_range_t.java \
+ qpol_module_t.java \
+ qpol_netifcon_t.java \
+ qpol_nodecon_t.java \
+ qpol_policy_t.java \
+ qpol_portcon_t.java \
+ qpol_range_trans_t.java \
+ qpol_role_allow_t.java \
+ qpol_role_t.java \
+ qpol_role_trans_t.java \
+ qpol_syn_avrule_t.java \
+ qpol_syn_terule_t.java \
+ qpol_terule_t.java \
+ qpol_type_set_t.java \
+ qpol_type_t.java \
+ qpol_user_t.java \
+ qpol_validatetrans_t.java \
+ SWIGTYPE_p_int.java \
+ SWIGTYPE_p_unsigned_int.java \
+ SWIGTYPE_p_void.java
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libqpol/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @QPOL_LIB_FLAG@
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_JAVA_OPT) -package $(package_name) -o $@ -I$(top_srcdir)/libqpol/include $<
+
+$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES))
+
+$(wrappedso_DATA): $(filter %.c, $(BUILT_SOURCES))
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_JAVA_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+classes = $(patsubst %.java,$(package_dir)/%.class,$(filter %.java, $(BUILT_SOURCES)))
+
+# Because the Java compiler can generate multiple class files from the
+# same source .java file, putting all of the classes below will result
+# in repeated invocations of javac. Therefore, an alternative is to
+# just depend upon the first class file, and let the Java compiler
+# create the rest of them.
+$(firstword $(classes)): $(filter %.java, $(BUILT_SOURCES))
+ $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^
+
+$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes))
+
+$(wrappedjar_DATA): MANIFEST.MF
+
+$(wrappedjar_DATA): $(classes)
+ $(JAR) cfm $@ MANIFEST.MF $^
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name)
+ $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name)
+ -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA)
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name)
diff --git a/libqpol/swig/python/Makefile.am b/libqpol/swig/python/Makefile.am
new file mode 100644
index 0000000..cc6d681
--- /dev/null
+++ b/libqpol/swig/python/Makefile.am
@@ -0,0 +1,34 @@
+wrappedso_DATA = _qpol.so.@libqpol_version@
+wrappedso_SONAME = @libqpol_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+pkgpython_PYTHON = __init__.py
+wrappedpy_DATA = qpol.py $(pkgpython_PYTHON)
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../qpol.i
+BUILT_SOURCES = qpol_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libqpol/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @QPOL_LIB_FLAG@ @PYTHON_LDFLAGS@
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_PYTHON_OPT) -o $@ $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _qpol.so
+
+$(wrappedpy_DATA): $(BUILT_SOURCES)
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _qpol.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_qpol.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) qpol.py $(wrappedso_SONAME) _qpol.so qpol.pyc
diff --git a/libqpol/swig/python/__init__.py b/libqpol/swig/python/__init__.py
new file mode 100644
index 0000000..218d892
--- /dev/null
+++ b/libqpol/swig/python/__init__.py
@@ -0,0 +1 @@
+# This file intentionally left blank.
diff --git a/libqpol/swig/qpol.i b/libqpol/swig/qpol.i
new file mode 100644
index 0000000..45a2403
--- /dev/null
+++ b/libqpol/swig/qpol.i
@@ -0,0 +1,2879 @@
+/**
+ * SWIG declarations for libqpol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%module qpol
+
+%{
+#include "../include/qpol/avrule_query.h"
+#include "../include/qpol/bool_query.h"
+#include "../include/qpol/class_perm_query.h"
+#include "../include/qpol/cond_query.h"
+#include "../include/qpol/constraint_query.h"
+#include "../include/qpol/context_query.h"
+#include "../include/qpol/fs_use_query.h"
+#include "../include/qpol/genfscon_query.h"
+#include "../include/qpol/isid_query.h"
+#include "../include/qpol/iterator.h"
+#include "../include/qpol/mls_query.h"
+#include "../include/qpol/mlsrule_query.h"
+#include "../include/qpol/module.h"
+#include "../include/qpol/netifcon_query.h"
+#include "../include/qpol/nodecon_query.h"
+#include "../include/qpol/policy.h"
+#include "../include/qpol/policy_extend.h"
+#include "../include/qpol/portcon_query.h"
+#include "../include/qpol/rbacrule_query.h"
+#include "../include/qpol/role_query.h"
+#include "../include/qpol/syn_rule_query.h"
+#include "../include/qpol/terule_query.h"
+#include "../include/qpol/type_query.h"
+#include "../include/qpol/user_query.h"
+#include "../include/qpol/util.h"
+
+/* Provide hooks so that language-specific modules can define the
+ * callback function, used by the handler in
+ * qpol_policy_open_from_file().
+ */
+SWIGEXPORT qpol_callback_fn_t qpol_swig_message_callback = NULL;
+SWIGEXPORT void * qpol_swig_message_callback_arg = NULL;
+
+%}
+
+#ifdef SWIGJAVA
+%javaconst(1);
+/* get the java environment so we can throw exceptions */
+%{
+ static JNIEnv *qpol_global_jenv;
+ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ (*vm)->AttachCurrentThread(vm, (void **)&qpol_global_jenv, NULL);
+ return JNI_VERSION_1_2;
+ }
+%}
+#endif
+
+%include exception.i
+%include stdint.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+#ifdef SWIGJAVA
+
+%exception {
+ qpol_global_jenv = jenv;
+ $action
+}
+
+%{
+#define BEGIN_EXCEPTION JNIEnv *local_jenv = qpol_global_jenv; {
+#define END_EXCEPTION }
+%}
+
+/* handle size_t correctly in java as architecture independent */
+%typemap(jni) size_t "jlong"
+%typemap(jtype) size_t "long"
+%typemap(jstype) size_t "long"
+%typemap(javabody) SWIGTYPE, void* %{
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ public $javaclassname(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ public static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libqpol_get_version();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("jqpol");
+ }
+ }
+%}
+
+#else
+/* not in java so handle size_t as architecture dependent */
+#ifdef SWIGWORDSIZE64
+typedef uint64_t size_t;
+#else
+typedef uint32_t size_t;
+#endif
+%{
+#define BEGIN_EXCEPTION
+#define END_EXCEPTION
+%}
+#endif
+
+/* utility functions */
+const char *libqpol_get_version(void);
+
+%rename(qpol_default_policy_find) wrap_qpol_default_policy_find;
+%newobject wrap_qpol_default_policy_find();
+
+/* if java, pass the new exception macro to C not just SWIG */
+#ifdef SWIGJAVA
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%inline %{
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%}
+#endif
+
+#ifdef SWIGTCL
+/* implement a custom non thread-safe error handler */
+%{
+static char *message = NULL;
+static void tcl_clear_error(void)
+{
+ free(message);
+ message = NULL;
+}
+static void tcl_throw_error(const char *s)
+{
+ free(message);
+ message = strdup(s);
+}
+static char *tcl_get_error(void)
+{
+ return message;
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+%}
+
+%wrapper %{
+/* Tcl module's initialization routine is expected to be named
+ * Qpol_Init(), but the output file will be called libtqpol.so instead
+ * of libqpol.so. Therefore add an alias from Tqpol_Init() to the
+ * real Qpol_Init().
+ */
+SWIGEXPORT int Tqpol_Init(Tcl_Interp *interp) {
+ return SWIG_init(interp);
+}
+%}
+
+%exception {
+ char *err;
+ tcl_clear_error();
+ $action
+ if ((err = tcl_get_error()) != NULL) {
+ Tcl_Obj *obj = Tcl_NewStringObj(message, -1);
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, obj);
+ goto fail;
+ }
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+#endif
+
+%inline %{
+ /* cast void * to char * as it can't have a constructor */
+ const char * to_str(void *x) {
+ return (const char *)x;
+ }
+
+ char * wrap_qpol_default_policy_find() {
+ char *path;
+ int retv;
+ BEGIN_EXCEPTION
+ retv = qpol_default_policy_find(&path);
+ if (retv < 0) {
+ SWIG_exception(SWIG_IOError, "Error searching for default policy");
+ } else if (retv > 0) {
+ SWIG_exception(SWIG_RuntimeError, "Could not find default policy");
+ } else {
+ return path;
+ }
+ END_EXCEPTION
+ fail: /* SWIG_exception calls goto fail */
+ return NULL;
+ }
+%}
+
+/* qpol_module */
+#define QPOL_MODULE_UNKNOWN 0
+#define QPOL_MODULE_BASE 1
+#define QPOL_MODULE_OTHER 2
+typedef struct qpol_module {} qpol_module_t;
+%extend qpol_module_t {
+ qpol_module_t(const char *path) {
+ qpol_module_t *m;
+ BEGIN_EXCEPTION
+ if (qpol_module_create_from_file(path, &m)) {
+ SWIG_exception(SWIG_IOError, "Error opening module");
+ }
+ return m;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_module_t() {
+ qpol_module_destroy(&self);
+ };
+ const char *get_path() {
+ const char *p;
+ BEGIN_EXCEPTION
+ if (qpol_module_get_path(self, &p)) {
+ SWIG_exception(SWIG_ValueError,"Could not get module path");
+ }
+ return p;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ const char *get_name() {
+ const char *n;
+ BEGIN_EXCEPTION
+ if (qpol_module_get_name(self, &n)) {
+ SWIG_exception(SWIG_ValueError,"Could not get module name");
+ }
+ return n;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ const char *get_version() {
+ const char *v;
+ BEGIN_EXCEPTION
+ if (qpol_module_get_version(self, &v)) {
+ SWIG_exception(SWIG_ValueError,"Could not get module version");
+ }
+ return v;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ int get_type() {
+ int t;
+ BEGIN_EXCEPTION
+ if (qpol_module_get_type(self, &t)) {
+ SWIG_exception(SWIG_ValueError,"Could not get module type");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ int get_enabled() {
+ int e;
+ BEGIN_EXCEPTION
+ if (qpol_module_get_enabled(self, &e)) {
+ SWIG_exception(SWIG_ValueError,"Could not get module state");
+ }
+ END_EXCEPTION
+ fail:
+ return e;
+ };
+ void set_enabled(int state) {
+ BEGIN_EXCEPTION
+ if (qpol_module_set_enabled(self, state)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set module state");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+};
+
+/* qpol_policy */
+#define QPOL_POLICY_OPTION_NO_NEVERALLOWS 0x00000001
+#define QPOL_POLICY_OPTION_NO_RULES 0x00000002
+#define QPOL_POLICY_OPTION_MATCH_SYSTEM 0x00000004
+typedef struct qpol_policy {} qpol_policy_t;
+typedef void (*qpol_callback_fn_t) (void *varg, struct qpol_policy * policy, int level, const char *fmt, va_list va_args);
+#define QPOL_POLICY_UNKNOWN -1
+#define QPOL_POLICY_KERNEL_SOURCE 0
+#define QPOL_POLICY_KERNEL_BINARY 1
+#define QPOL_POLICY_MODULE_BINARY 2
+typedef enum qpol_capability
+{
+ QPOL_CAP_ATTRIB_NAMES,
+ QPOL_CAP_SYN_RULES,
+ QPOL_CAP_LINE_NUMBERS,
+ QPOL_CAP_CONDITIONALS,
+ QPOL_CAP_MLS,
+ QPOL_CAP_MODULES,
+ QPOL_CAP_RULES_LOADED,
+ QPOL_CAP_SOURCE,
+ QPOL_CAP_NEVERALLOW
+} qpol_capability_e;
+
+%extend qpol_policy_t {
+ qpol_policy_t(const char *path, const int options) {
+ qpol_policy_t *p;
+ BEGIN_EXCEPTION
+ if (qpol_policy_open_from_file(path, &p, qpol_swig_message_callback, qpol_swig_message_callback_arg, options) < 0) {
+ SWIG_exception(SWIG_IOError, "Error opening policy");
+ }
+ END_EXCEPTION
+ return p;
+ fail:
+ return NULL;
+ }
+ ~qpol_policy_t() {
+ qpol_policy_destroy(&self);
+ };
+ void reevaluate_conds() {
+ BEGIN_EXCEPTION
+ if (qpol_policy_reevaluate_conds(self)) {
+ SWIG_exception(SWIG_ValueError, "Error evaluating conditional expressions");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_module(qpol_module_t *mod) {
+ BEGIN_EXCEPTION
+ if (qpol_policy_append_module(self, mod)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void rebuild (const int options) {
+ BEGIN_EXCEPTION
+ if (qpol_policy_rebuild(self, options)) {
+ SWIG_exception(SWIG_RuntimeError, "Failed rebuilding policy");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ int get_version () {
+ unsigned int v;
+ (void)qpol_policy_get_policy_version(self, &v); /* only error is on null parameters neither can be here */
+ return (int) v;
+ };
+ int get_type () {
+ int t;
+ (void)qpol_policy_get_type(self, &t); /* only error is on null parameters neither can be here */
+ return t;
+ };
+ int has_capability (qpol_capability_e cap) {
+ return qpol_policy_has_capability(self, cap);
+ };
+ void build_syn_rule_table() {
+ BEGIN_EXCEPTION
+ if (qpol_policy_build_syn_rule_table(self)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ %newobject get_module_iter();
+ qpol_iterator_t *get_module_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_module_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_type_iter();
+ qpol_iterator_t *get_type_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_type_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_role_iter();
+ qpol_iterator_t *get_role_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_role_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_level_iter();
+ qpol_iterator_t *get_level_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_level_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_cat_iter();
+ qpol_iterator_t *get_cat_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_cat_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_user_iter();
+ qpol_iterator_t *get_user_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_user_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_bool_iter();
+ qpol_iterator_t *get_bool_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_bool_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_class_iter(char*);
+ qpol_iterator_t *get_class_iter(char *perm=NULL) {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (perm) {
+ if (qpol_perm_get_class_iter(self, perm, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get class iterator");
+ }
+ } else {
+ if (qpol_policy_get_class_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_common_iter(char*);
+ qpol_iterator_t *get_common_iter(char *perm=NULL) {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (perm) {
+ if (qpol_perm_get_common_iter(self, perm, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get common iterator");
+ }
+ } else {
+ if (qpol_policy_get_common_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_fs_use_iter();
+ qpol_iterator_t *get_fs_use_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_fs_use_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_genfscon_iter();
+ qpol_iterator_t *get_genfscon_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_genfscon_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_isid_iter();
+ qpol_iterator_t *get_isid_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_isid_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_netifcon_iter();
+ qpol_iterator_t *get_netifcon_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_netifcon_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_nodecon_iter();
+ qpol_iterator_t *get_nodecon_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_nodecon_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_portcon_iter();
+ qpol_iterator_t *get_portcon_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_portcon_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_constraint_iter();
+ qpol_iterator_t *get_constraint_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_constraint_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_validatetrans_iter();
+ qpol_iterator_t *get_validatetrans_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_validatetrans_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_role_allow_iter();
+ qpol_iterator_t *get_role_allow_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_role_allow_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_role_trans_iter();
+ qpol_iterator_t *get_role_trans_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_role_trans_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_range_trans_iter();
+ qpol_iterator_t *get_range_trans_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_range_trans_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_avrule_iter(int);
+ qpol_iterator_t *get_avrule_iter(int rule_types) {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_avrule_iter(self, rule_types, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_terule_iter(int);
+ qpol_iterator_t *get_terule_iter(int rule_types) {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_terule_iter(self, rule_types, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ %newobject get_cond_iter();
+ qpol_iterator_t *get_cond_iter() {
+ BEGIN_EXCEPTION
+ qpol_iterator_t *iter;
+ if (qpol_policy_get_cond_iter(self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ return iter;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+};
+
+/* qpol iterator */
+typedef struct qpol_iterator {} qpol_iterator_t;
+%extend qpol_iterator_t {
+ /* user never directly creates, but SWIG expects a constructor */
+ qpol_iterator_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_TypeError, "User may not create iterators difectly");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_iterator_t() {
+ qpol_iterator_destroy(&self);
+ };
+ void *get_item() {
+ BEGIN_EXCEPTION
+ void *i;
+ if (qpol_iterator_get_item(self, &i)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get item");
+ }
+ return i;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ void next() {
+ BEGIN_EXCEPTION
+ if (qpol_iterator_next(self)) {
+ SWIG_exception(SWIG_RuntimeError, "Error advancing iterator");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ int end() {
+ return qpol_iterator_end(self);
+ };
+ size_t get_size() {
+ BEGIN_EXCEPTION
+ size_t s;
+ if (qpol_iterator_get_size(self, &s)) {
+ SWIG_exception(SWIG_ValueError, "Could not get iterator size");
+ }
+ return s;
+ END_EXCEPTION
+ fail:
+ return 0;
+ };
+};
+
+/* qpol type */
+typedef struct qpol_type {} qpol_type_t;
+%extend qpol_type_t {
+ qpol_type_t(qpol_policy_t *p, const char *name) {
+ BEGIN_EXCEPTION
+ const qpol_type_t *t;
+ if (qpol_policy_get_type_by_name(p, name, &t)) {
+ SWIG_exception(SWIG_RuntimeError, "Type does not exist");
+ }
+ return (qpol_type_t*)t;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_type_t() {
+ /* no op */
+ return;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ BEGIN_EXCEPTION
+ const char *name;
+ if (qpol_type_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get type name");
+ }
+ return name;
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_type_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get type value");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ int get_isalias(qpol_policy_t *p) {
+ unsigned char i;
+ BEGIN_EXCEPTION
+ if (qpol_type_get_isalias(p, self, &i)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine whether type is an alias");
+ }
+ END_EXCEPTION
+ fail:
+ return (int)i;
+ };
+ int get_isattr(qpol_policy_t *p) {
+ unsigned char i;
+ BEGIN_EXCEPTION
+ if (qpol_type_get_isattr(p, self, &i)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine whether type is an attribute");
+ }
+ END_EXCEPTION
+ fail:
+ return (int)i;
+ };
+ int get_ispermissive(qpol_policy_t *p) {
+ unsigned char i;
+ BEGIN_EXCEPTION
+ if (qpol_type_get_ispermissive(p, self, &i)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine whether type is permissive");
+ }
+ END_EXCEPTION
+ fail:
+ return (int)i;
+ };
+ %newobject get_type_iter(qpol_policy_t*);
+ qpol_iterator_t *get_type_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ int retv = qpol_type_get_type_iter(p, self, &iter);
+ if (retv < 0) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get attribute types");
+ } else if (retv > 0) {
+ SWIG_exception(SWIG_TypeError, "Type is not an attribute");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_attr_iter(qpol_policy_t*);
+ qpol_iterator_t *get_attr_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ int retv = qpol_type_get_attr_iter(p, self, &iter);
+ if (retv < 0) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get type attributes");
+ } else if (retv > 0) {
+ SWIG_exception(SWIG_TypeError, "Type is an attribute");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_alias_iter(qpol_policy_t*);
+ qpol_iterator_t *get_alias_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_type_get_alias_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get type aliases");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ };
+%inline %{
+ qpol_type_t *qpol_type_from_void(void *x) {
+ return (qpol_type_t*)x;
+ };
+%}
+
+/* qpol role */
+typedef struct qpol_role {} qpol_role_t;
+%extend qpol_role_t {
+ qpol_role_t(qpol_policy_t *p, const char *name) {
+ const qpol_role_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_role_by_name(p, name, &r)) {
+ SWIG_exception(SWIG_RuntimeError, "Role does not exist");
+ }
+ END_EXCEPTION
+ return (qpol_role_t*)r;
+ fail:
+ return NULL;
+ };
+ ~qpol_role_t() {
+ /* no op */
+ return;
+ };
+ int get_value (qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_role_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get role value");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_role_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get role name");
+ }
+ END_EXCEPTION
+ return name;
+ fail:
+ return NULL;
+ };
+ %newobject get_type_iter(qpol_policy_t*);
+ qpol_iterator_t *get_type_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_role_get_type_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get role types");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_dominate_iter(qpol_policy_t*);
+ qpol_iterator_t *get_dominate_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_role_get_dominate_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get dominated roles");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_role_t *qpol_role_from_void(void *x) {
+ return (qpol_role_t*)x;
+ };
+%}
+
+/* qpol level */
+typedef struct qpol_level {} qpol_level_t;
+%extend qpol_level_t {
+ qpol_level_t(qpol_policy_t *p, const char *name) {
+ const qpol_level_t *l;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_level_by_name(p, name, &l)) {
+ SWIG_exception(SWIG_RuntimeError, "Level does not exist");
+ }
+ END_EXCEPTION
+ return (qpol_level_t*)l;
+ fail:
+ return NULL;
+ };
+ ~qpol_level_t() {
+ /* no op */
+ return;
+ };
+ int get_isalias(qpol_policy_t *p) {
+ unsigned char i;
+ BEGIN_EXCEPTION
+ if (qpol_level_get_isalias(p, self, &i)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine whether level is an alias");
+ }
+ END_EXCEPTION
+ fail:
+ return (int)i;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_level_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get level sensitivity value");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_level_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get level sensitivity name");
+ }
+ END_EXCEPTION
+ return name;
+ fail:
+ return NULL;
+ };
+ %newobject get_cat_iter(qpol_policy_t*);
+ qpol_iterator_t *get_cat_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_level_get_cat_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get level categories");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_alias_iter(qpol_policy_t*);
+ qpol_iterator_t *get_alias_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_level_get_alias_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get level aliases");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_level_t *qpol_level_from_void(void *x) {
+ return (qpol_level_t*)x;
+ };
+%}
+
+/* qpol cat */
+typedef struct qpol_cat {} qpol_cat_t;
+%extend qpol_cat_t {
+ qpol_cat_t(qpol_policy_t *p, const char *name) {
+ const qpol_cat_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_cat_by_name(p, name, &c)) {
+ SWIG_exception(SWIG_RuntimeError, "Category does not exist");
+ }
+ END_EXCEPTION
+ return (qpol_cat_t*)c;
+ fail:
+ return NULL;
+ };
+ ~qpol_cat_t() {
+ /* no op */
+ return;
+ };
+ int get_isalias(qpol_policy_t *p) {
+ unsigned char i;
+ BEGIN_EXCEPTION
+ if (qpol_cat_get_isalias(p, self, &i)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine whether category is an alias");
+ }
+ END_EXCEPTION
+ fail:
+ return (int)i;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_cat_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get category value");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_cat_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get category name");
+ }
+ END_EXCEPTION
+ return name;
+ fail:
+ return NULL;
+ };
+ %newobject get_alias_iter(qpol_policy_t*);
+ qpol_iterator_t *get_alias_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_cat_get_alias_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get category aliases");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_cat_t *qpol_cat_from_void(void *x) {
+ return (qpol_cat_t*)x;
+ };
+%}
+
+/* qpol mls range */
+typedef struct qpol_mls_range {} qpol_mls_range_t;
+%extend qpol_mls_range_t {
+ qpol_mls_range_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_mls_range_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~qpol_mls_range_t() {
+ /* no op */
+ return;
+ };
+ const qpol_mls_level_t *get_high_level(qpol_policy_t *p) {
+ const qpol_mls_level_t *l;
+ BEGIN_EXCEPTION
+ if (qpol_mls_range_get_high_level(p, self, &l)) {
+ SWIG_exception(SWIG_ValueError, "Could not get range high levl");
+ }
+ END_EXCEPTION
+ fail:
+ return l;
+ };
+ const qpol_mls_level_t *get_low_level(qpol_policy_t *p) {
+ const qpol_mls_level_t *l;
+ BEGIN_EXCEPTION
+ if (qpol_mls_range_get_low_level(p, self, &l)) {
+ SWIG_exception(SWIG_ValueError, "Could not get range low levl");
+ }
+ END_EXCEPTION
+ fail:
+ return l;
+ };
+};
+%inline %{
+ qpol_mls_range_t *qpol_mls_range_from_void(void *x) {
+ return (qpol_mls_range_t*)x;
+ };
+%}
+
+/* qpol mls level */
+typedef struct qpol_mls_level {} qpol_mls_level_t;
+%extend qpol_mls_level_t {
+ qpol_mls_level_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_mls_level_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ }
+ ~qpol_mls_level_t() {
+ /* no op */
+ return;
+ };
+ const char *get_sens_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_mls_level_get_sens_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get level sensitivity name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+ %newobject get_cat_iter(qpol_policy_t*);
+ qpol_iterator_t *get_cat_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_mls_level_get_cat_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get level categories");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_mls_level_t *qpol_mls_level_from_void(void *x) {
+ return (qpol_mls_level_t*)x;
+ };
+%}
+
+/* qpol user */
+typedef struct qpol_user {} qpol_user_t;
+%extend qpol_user_t {
+ qpol_user_t(qpol_policy_t *p, const char *name) {
+ const qpol_user_t *u;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_user_by_name(p, name, &u)) {
+ SWIG_exception(SWIG_RuntimeError, "User does not exist");
+ }
+ END_EXCEPTION
+ return (qpol_user_t*)u;
+ fail:
+ return NULL;
+ };
+ ~qpol_user_t() {
+ /* no op */
+ return;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_user_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get user value");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ %newobject get_role_iter(qpol_policy_t*);
+ qpol_iterator_t *get_role_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_user_get_role_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of Memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ const qpol_mls_range_t *get_range(qpol_policy_t *p) {
+ const qpol_mls_range_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_user_get_range(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get user range");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_user_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get user name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+ const qpol_mls_level_t *get_dfltlevel(qpol_policy_t *p) {
+ const qpol_mls_level_t *l;
+ BEGIN_EXCEPTION
+ if (qpol_user_get_dfltlevel(p, self, &l)) {
+ SWIG_exception(SWIG_ValueError, "Could not get user default level");
+ }
+ END_EXCEPTION
+ fail:
+ return l;
+ };
+};
+%inline %{
+ qpol_user_t *qpol_user_from_void(void *x) {
+ return (qpol_user_t*)x;
+ };
+%}
+
+/* qpol bool */
+typedef struct qpol_bool {} qpol_bool_t;
+%extend qpol_bool_t {
+ qpol_bool_t(qpol_policy_t *p, const char *name) {
+ qpol_bool_t *b;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_bool_by_name(p, name, &b)) {
+ SWIG_exception(SWIG_RuntimeError, "Boolean does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return b;
+ };
+ ~qpol_bool_t() {
+ /* no op */
+ return;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_bool_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get boolean value");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ int get_state(qpol_policy_t *p) {
+ int s;
+ BEGIN_EXCEPTION
+ if (qpol_bool_get_state(p, self, &s)) {
+ SWIG_exception(SWIG_ValueError, "Could not get boolean state");
+ }
+ END_EXCEPTION
+ fail:
+ return s;
+ };
+ void set_state(qpol_policy_t *p, int state) {
+ BEGIN_EXCEPTION
+ if (qpol_bool_set_state(p, self, state)) {
+ SWIG_exception(SWIG_RuntimeError, "Error setting boolean state");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_state_no_eval(qpol_policy_t *p, int state) {
+ BEGIN_EXCEPTION
+ if (qpol_bool_set_state_no_eval(p, self, state)) {
+ SWIG_exception(SWIG_RuntimeError, "Error setting boolean state");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_bool_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get boolean name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+};
+%inline %{
+ qpol_bool_t *qpol_bool_from_void(void *x) {
+ return (qpol_bool_t*)x;
+ };
+%}
+
+/* qpol context */
+typedef struct qpol_context {} qpol_context_t;
+%extend qpol_context_t {
+ qpol_context_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_context_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_context_t() {
+ /* no op */
+ return;
+ };
+ const qpol_user_t *get_user(qpol_policy_t *p) {
+ const qpol_user_t *u;
+ BEGIN_EXCEPTION
+ if (qpol_context_get_user(p, self, &u)) {
+ SWIG_exception(SWIG_ValueError, "Could not get user from context");
+ }
+ END_EXCEPTION
+ fail:
+ return u;
+ };
+ const qpol_role_t *get_role(qpol_policy_t *p) {
+ const qpol_role_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_context_get_role(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get role from context");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+ const qpol_type_t *get_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_context_get_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get type from context");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_mls_range_t *get_range(qpol_policy_t *p) {
+ const qpol_mls_range_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_context_get_range(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get range from context");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+};
+%inline %{
+ qpol_context_t *qpol_context_from_void(void *x) {
+ return (qpol_context_t*)x;
+ };
+%}
+
+/* qpol class */
+typedef struct qpol_class {} qpol_class_t;
+%extend qpol_class_t {
+ qpol_class_t(qpol_policy_t *p, const char *name) {
+ const qpol_class_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_class_by_name(p, name, &c)) {
+ SWIG_exception(SWIG_RuntimeError, "Class does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return (qpol_class_t*)c;
+ };
+ ~qpol_class_t() {
+ /* no op */
+ return;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_class_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get value for class");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ const qpol_common_t *get_common(qpol_policy_t *p) {
+ const qpol_common_t *c;
+ BEGIN_EXCEPTION
+ if(qpol_class_get_common(p, self, &c)) {
+ SWIG_exception(SWIG_ValueError, "Could not get common for class");
+ }
+ END_EXCEPTION
+ fail:
+ return c;
+ };
+ %newobject get_perm_iter(qpol_policy_t*);
+ qpol_iterator_t *get_perm_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if(qpol_class_get_perm_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get class permissions");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_constraint_iter(qpol_policy_t*);
+ qpol_iterator_t *get_constraint_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if(qpol_class_get_constraint_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get class constraints");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_validatetrans_iter(qpol_policy_t*);
+ qpol_iterator_t *get_validatetrans_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if(qpol_class_get_validatetrans_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get class validatetrans statements");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_class_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get class name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+};
+%inline %{
+ qpol_class_t *qpol_class_from_void(void *x) {
+ return (qpol_class_t*)x;
+ };
+%}
+
+/* qpol common */
+typedef struct qpol_common {} qpol_common_t;
+%extend qpol_common_t {
+ qpol_common_t(qpol_policy_t *p, const char *name) {
+ const qpol_common_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_common_by_name(p, name, &c)) {
+ SWIG_exception(SWIG_RuntimeError, "Common does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return (qpol_common_t*)c;
+ };
+ ~qpol_common_t() {
+ /* no op */
+ return;
+ };
+ int get_value(qpol_policy_t *p) {
+ uint32_t v;
+ BEGIN_EXCEPTION
+ if (qpol_common_get_value(p, self, &v)) {
+ SWIG_exception(SWIG_ValueError, "Could not get value for common");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) v;
+ };
+ %newobject get_perm_iter(qpol_policy_t*);
+ qpol_iterator_t *get_perm_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if(qpol_common_get_perm_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get common permissions");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_common_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get common name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+};
+%inline %{
+ qpol_common_t *qpol_common_from_void(void *x) {
+ return (qpol_common_t*)x;
+ };
+%}
+
+/* qpol fs_use */
+/* The defines QPOL_FS_USE_XATTR through QPOL_FS_USE_NONE are
+ * copied from sepol/policydb/services.h.
+ * QPOL_FS_USE_PSID is an extension to support v12 policies. */
+#ifdef SWIGJAVA /* java does not understand unsigned constants */
+#define QPOL_FS_USE_XATTR 1
+#define QPOL_FS_USE_TRANS 2
+#define QPOL_FS_USE_TASK 3
+#define QPOL_FS_USE_GENFS 4
+#define QPOL_FS_USE_NONE 5
+#define QPOL_FS_USE_PSID 6
+#else
+#define QPOL_FS_USE_XATTR 1U
+#define QPOL_FS_USE_TRANS 2U
+#define QPOL_FS_USE_TASK 3U
+#define QPOL_FS_USE_GENFS 4U
+#define QPOL_FS_USE_NONE 5U
+#define QPOL_FS_USE_PSID 6U
+#endif
+typedef struct qpol_fs_use {} qpol_fs_use_t;
+%extend qpol_fs_use_t {
+ qpol_fs_use_t(qpol_policy_t *p, const char *name) {
+ const qpol_fs_use_t *f;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_fs_use_by_name(p, name, &f)) {
+ SWIG_exception(SWIG_RuntimeError, "FS Use Statement does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return (qpol_fs_use_t*)f;
+ };
+ ~qpol_fs_use_t() {
+ /* no op */
+ return;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_fs_use_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get file system name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+ int get_behavior(qpol_policy_t *p) {
+ uint32_t behav;
+ BEGIN_EXCEPTION
+ if (qpol_fs_use_get_behavior(p, self, &behav)) {
+ SWIG_exception(SWIG_ValueError, "Could not get file system labeling behavior");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) behav;
+ };
+ const qpol_context_t *get_context(qpol_policy_t *p) {
+ uint32_t behav;
+ const qpol_context_t *ctx = NULL;
+ BEGIN_EXCEPTION
+ qpol_fs_use_get_behavior(p, self, &behav);
+ if (behav == QPOL_FS_USE_PSID) {
+ SWIG_exception(SWIG_TypeError, "Cannot get context for fs_use_psid statements");
+ } else if (qpol_fs_use_get_context(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get file system context");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+};
+%inline %{
+ qpol_fs_use_t *qpol_fs_use_from_void(void *x) {
+ return (qpol_fs_use_t*)x;
+ };
+%}
+
+/* qpol genfscon */
+/* values from flask do not change */
+#ifdef SWIGJAVA /* java does not understand unsigned constants */
+#define QPOL_CLASS_ALL 0
+#define QPOL_CLASS_BLK_FILE 11
+#define QPOL_CLASS_CHR_FILE 10
+#define QPOL_CLASS_DIR 7
+#define QPOL_CLASS_FIFO_FILE 13
+#define QPOL_CLASS_FILE 6
+#define QPOL_CLASS_LNK_FILE 9
+#define QPOL_CLASS_SOCK_FILE 12
+#else
+#define QPOL_CLASS_ALL 0U
+#define QPOL_CLASS_BLK_FILE 11U
+#define QPOL_CLASS_CHR_FILE 10U
+#define QPOL_CLASS_DIR 7U
+#define QPOL_CLASS_FIFO_FILE 13U
+#define QPOL_CLASS_FILE 6U
+#define QPOL_CLASS_LNK_FILE 9U
+#define QPOL_CLASS_SOCK_FILE 12U
+#endif
+typedef struct qpol_genfscon {} qpol_genfscon_t;
+%extend qpol_genfscon_t {
+ qpol_genfscon_t(qpol_policy_t *p, const char *name, const char *path) {
+ qpol_genfscon_t *g;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_genfscon_by_name(p, name, path, &g)) {
+ SWIG_exception(SWIG_RuntimeError, "Genfscon statement does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return g;
+ };
+ ~qpol_genfscon_t() {
+ free(self);
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_genfscon_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get file system name");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+ const char *get_path(qpol_policy_t *p) {
+ const char *path;
+ BEGIN_EXCEPTION
+ if (qpol_genfscon_get_path(p, self, &path)) {
+ SWIG_exception(SWIG_ValueError, "Could not get file system path");
+ }
+ END_EXCEPTION
+ fail:
+ return path;
+ };
+ int get_class(qpol_policy_t *p) {
+ uint32_t cls;
+ BEGIN_EXCEPTION
+ if (qpol_genfscon_get_class(p, self, &cls)) {
+ SWIG_exception(SWIG_ValueError, "Could not get genfscon statement class");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) cls;
+ };
+ const qpol_context_t *get_context(qpol_policy_t *p) {
+ const qpol_context_t *ctx;
+ BEGIN_EXCEPTION
+ if (qpol_genfscon_get_context(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get context for genfscon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+};
+%inline %{
+ qpol_genfscon_t *qpol_genfscon_from_void(void *x) {
+ return (qpol_genfscon_t*)x;
+ };
+%}
+
+/* qpol isid */
+typedef struct qpol_isid {} qpol_isid_t;
+%extend qpol_isid_t {
+ qpol_isid_t(qpol_policy_t *p, const char *name) {
+ const qpol_isid_t *i;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_isid_by_name(p, name, &i)) {
+ SWIG_exception(SWIG_RuntimeError, "Isid does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return (qpol_isid_t*)i;
+ };
+ ~qpol_isid_t() {
+ /* no op */
+ return;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_isid_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get name for initial sid");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+ const qpol_context_t *get_context(qpol_policy_t *p) {
+ const qpol_context_t *ctx;
+ BEGIN_EXCEPTION
+ if (qpol_isid_get_context(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get context for initial sid");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+};
+%inline %{
+ qpol_isid_t *qpol_isid_from_void(void *x) {
+ return (qpol_isid_t*)x;
+ };
+%}
+
+/* qpol netifcon */
+typedef struct qpol_netifcon {} qpol_netifcon_t;
+%extend qpol_netifcon_t {
+ qpol_netifcon_t(qpol_policy_t *p, const char *name) {
+ const qpol_netifcon_t *n;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_netifcon_by_name(p, name, &n)) {
+ SWIG_exception(SWIG_RuntimeError, "Netifcon statement does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return (qpol_netifcon_t*)n;
+ };
+ ~qpol_netifcon_t() {
+ /* no op */
+ return;
+ };
+ const char *get_name(qpol_policy_t *p) {
+ const char *name;
+ BEGIN_EXCEPTION
+ if (qpol_netifcon_get_name(p, self, &name)) {
+ SWIG_exception(SWIG_ValueError, "Could not get name for netifcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return name;
+ };
+ const qpol_context_t *get_msg_con(qpol_policy_t *p) {
+ const qpol_context_t *ctx;
+ BEGIN_EXCEPTION
+ if (qpol_netifcon_get_msg_con(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get message context for netifcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+ const qpol_context_t *get_if_con(qpol_policy_t *p) {
+ const qpol_context_t *ctx;
+ BEGIN_EXCEPTION
+ if (qpol_netifcon_get_if_con(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get interface context for netifcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+};
+%inline %{
+ qpol_netifcon_t *qpol_netifcon_from_void(void *x) {
+ return (qpol_netifcon_t*)x;
+ };
+%}
+
+/* qpol nodecon */
+#define QPOL_IPV4 0
+#define QPOL_IPV6 1
+typedef struct qpol_nodecon {} qpol_nodecon_t;
+%extend qpol_nodecon_t {
+ qpol_nodecon_t(qpol_policy_t *p, int addr[4], int mask[4], int protocol) {
+ uint32_t a[4], m[4];
+ qpol_nodecon_t *n;
+ BEGIN_EXCEPTION
+ a[0] = (uint32_t) addr[0]; a[1] = (uint32_t) addr[1];
+ a[2] = (uint32_t) addr[2]; a[3] = (uint32_t) addr[3];
+ m[0] = (uint32_t) mask[0]; m[1] = (uint32_t) mask[1];
+ m[2] = (uint32_t) mask[2]; m[3] = (uint32_t) mask[3];
+ if (qpol_policy_get_nodecon_by_node(p, a, m, protocol, &n)) {
+ SWIG_exception(SWIG_RuntimeError, "Nodecon statement does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return n;
+ }
+ ~qpol_nodecon_t() {
+ free(self);
+ };
+ uint32_t *get_addr(qpol_policy_t *p) {
+ uint32_t *a;
+ BEGIN_EXCEPTION
+ unsigned char proto; /* currently dropped; stores the protocol - call get_protocol() */
+ if (qpol_nodecon_get_addr(p, self, &a, &proto)) {
+ SWIG_exception(SWIG_ValueError, "Could not get address of nodecon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return a;
+ };
+ uint32_t *get_mask(qpol_policy_t *p) {
+ uint32_t *m;
+ BEGIN_EXCEPTION
+ unsigned char proto; /* currently dropped; stores the protocol - call get_protocol() */
+ if (qpol_nodecon_get_mask(p, self, &m, &proto)) {
+ SWIG_exception(SWIG_ValueError, "Could not get mask of nodecon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return m;
+ };
+ int get_protocol(qpol_policy_t *p) {
+ unsigned char proto;
+ BEGIN_EXCEPTION
+ if (qpol_nodecon_get_protocol(p, self, &proto)) {
+ SWIG_exception(SWIG_ValueError, "Could not get protocol for nodecon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return proto;
+ };
+ const qpol_context_t *get_context(qpol_policy_t *p) {
+ const qpol_context_t *ctx;
+ BEGIN_EXCEPTION
+ if (qpol_nodecon_get_context(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get context for nodecon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+};
+%inline %{
+ qpol_nodecon_t *qpol_nodecon_from_void(void *x) {
+ return (qpol_nodecon_t*)x;
+ };
+%}
+
+/* qpol portcon */
+/* from netinet/in.h */
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+typedef struct qpol_portcon {} qpol_portcon_t;
+%extend qpol_portcon_t {
+ qpol_portcon_t(qpol_policy_t *p, uint16_t low, uint16_t high, uint8_t protocol) {
+ const qpol_portcon_t *qp;
+ BEGIN_EXCEPTION
+ if (qpol_policy_get_portcon_by_port(p, low, high, protocol, &qp)) {
+ SWIG_exception(SWIG_RuntimeError, "Portcon statement does not exist");
+ }
+ END_EXCEPTION
+ fail:
+ return (qpol_portcon_t*)qp;
+ };
+ ~qpol_portcon_t() {
+ /* no op */
+ return;
+ };
+ uint16_t get_low_port(qpol_policy_t *p) {
+ uint16_t port = 0;
+ BEGIN_EXCEPTION
+ if(qpol_portcon_get_low_port(p, self, &port)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get low port for portcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return port;
+ };
+ uint16_t get_high_port(qpol_policy_t *p) {
+ uint16_t port = 0;
+ BEGIN_EXCEPTION
+ if(qpol_portcon_get_high_port(p, self, &port)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get high port for portcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return port;
+ };
+ uint8_t get_protocol(qpol_policy_t *p) {
+ uint8_t proto = 0;
+ BEGIN_EXCEPTION
+ if (qpol_portcon_get_protocol(p, self, &proto)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not get protocol for portcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return proto;
+ };
+ const qpol_context_t *get_context(qpol_policy_t *p) {
+ const qpol_context_t *ctx;
+ BEGIN_EXCEPTION
+ if (qpol_portcon_get_context(p, self, &ctx)) {
+ SWIG_exception(SWIG_ValueError, "Could not get context for portcon statement");
+ }
+ END_EXCEPTION
+ fail:
+ return ctx;
+ };
+}
+%inline %{
+ qpol_portcon_t *qpol_portcon_from_void(void *x) {
+ return (qpol_portcon_t*)x;
+ };
+%}
+
+/* qpol constraint */
+typedef struct qpol_constraint {} qpol_constraint_t;
+%extend qpol_constraint_t {
+ qpol_constraint_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_constraint_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_constraint_t() {
+ free(self);
+ };
+ const qpol_class_t *get_class(qpol_policy_t *p) {
+ const qpol_class_t *cls;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_get_class(p, self, &cls)) {
+ SWIG_exception(SWIG_ValueError, "Could not get class for constraint");
+ }
+ END_EXCEPTION
+ fail:
+ return cls;
+ };
+ %newobject get_perm_iter(qpol_policy_t*);
+ qpol_iterator_t *get_perm_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_get_perm_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_expr_iter(qpol_policy_t*);
+ qpol_iterator_t *get_expr_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_get_expr_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_constraint_t *qpol_constraint_from_void(void *x) {
+ return (qpol_constraint_t*)x;
+ };
+%}
+
+/* qpol validatetrans */
+typedef struct qpol_validatetrans {} qpol_validatetrans_t;
+%extend qpol_validatetrans_t {
+ qpol_validatetrans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_validatetrans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_validatetrans_t() {
+ free(self);
+ };
+ const qpol_class_t *get_class(qpol_policy_t *p) {
+ const qpol_class_t *cls;
+ BEGIN_EXCEPTION
+ if (qpol_validatetrans_get_class(p, self, &cls)) {
+ SWIG_exception(SWIG_ValueError, "Could not get class for validatetrans");
+ }
+ END_EXCEPTION
+ fail:
+ return cls;
+ };
+ %newobject get_expr_iter(qpol_policy_t*);
+ qpol_iterator_t *get_expr_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_validatetrans_get_expr_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_validatetrans_t *qpol_validatetrans_from_void(void *x) {
+ return (qpol_validatetrans_t*)x;
+ };
+%}
+
+/* qpol constraint expression node */
+/* expr_type values */
+#define QPOL_CEXPR_TYPE_NOT 1
+#define QPOL_CEXPR_TYPE_AND 2
+#define QPOL_CEXPR_TYPE_OR 3
+#define QPOL_CEXPR_TYPE_ATTR 4
+#define QPOL_CEXPR_TYPE_NAMES 5
+/* symbol type values */
+#define QPOL_CEXPR_SYM_USER 1
+#define QPOL_CEXPR_SYM_ROLE 2
+#define QPOL_CEXPR_SYM_TYPE 4
+#define QPOL_CEXPR_SYM_TARGET 8
+#define QPOL_CEXPR_SYM_XTARGET 16
+#define QPOL_CEXPR_SYM_L1L2 32
+#define QPOL_CEXPR_SYM_L1H2 64
+#define QPOL_CEXPR_SYM_H1L2 128
+#define QPOL_CEXPR_SYM_H1H2 256
+#define QPOL_CEXPR_SYM_L1H1 512
+#define QPOL_CEXPR_SYM_L2H2 1024
+/* op values */
+#define QPOL_CEXPR_OP_EQ 1
+#define QPOL_CEXPR_OP_NEQ 2
+#define QPOL_CEXPR_OP_DOM 3
+#define QPOL_CEXPR_OP_DOMBY 4
+#define QPOL_CEXPR_OP_INCOMP 5
+typedef struct qpol_constraint_expr_node {} qpol_constraint_expr_node_t;
+%extend qpol_constraint_expr_node_t {
+ qpol_constraint_expr_node_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_constraint_expr_node_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_constraint_expr_node_t() {
+ /* no op */
+ return;
+ };
+ int get_expr_type(qpol_policy_t *p) {
+ uint32_t et;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_expr_node_get_expr_type(p, self, &et)) {
+ SWIG_exception(SWIG_ValueError, "Could not get expression type for node");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) et;
+ };
+ int get_sym_type(qpol_policy_t *p) {
+ uint32_t st;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_expr_node_get_sym_type(p, self, &st)) {
+ SWIG_exception(SWIG_ValueError, "Could not get symbol type for node");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) st;
+ };
+ int get_op(qpol_policy_t *p) {
+ uint32_t op;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_expr_node_get_op(p, self, &op)) {
+ SWIG_exception(SWIG_ValueError, "Could not get operator for node");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) op;
+ };
+ %newobject get_names_iter(qpol_policy_t*);
+ qpol_iterator_t *get_names_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_constraint_expr_node_get_names_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_constraint_expr_node_t *qpol_constraint_expr_node_from_void(void *x) {
+ return (qpol_constraint_expr_node_t*)x;
+ };
+%}
+
+/* qpol role allow */
+typedef struct qpol_role_allow {} qpol_role_allow_t;
+%extend qpol_role_allow_t {
+ qpol_role_allow_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_role_allow_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_role_allow_t() {
+ /* no op */
+ return;
+ };
+ const qpol_role_t *get_source_role(qpol_policy_t *p) {
+ const qpol_role_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_role_allow_get_source_role(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source for role allow rule");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+ const qpol_role_t *get_target_role(qpol_policy_t *p) {
+ const qpol_role_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_role_allow_get_target_role(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target for role allow rule");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+};
+%inline %{
+ qpol_role_allow_t *qpol_role_allow_from_void(void *x) {
+ return (qpol_role_allow_t*)x;
+ };
+%}
+
+/* qpol role trans */
+typedef struct qpol_role_trans {} qpol_role_trans_t;
+%extend qpol_role_trans_t {
+ qpol_role_trans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_role_trans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_role_trans_t() {
+ /* no op */
+ return;
+ };
+ const qpol_role_t *get_source_role(qpol_policy_t *p) {
+ const qpol_role_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_role_trans_get_source_role(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source for role_transition rule");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+ const qpol_type_t *get_target_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_role_trans_get_target_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target for role_transition rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_role_t *get_default_role(qpol_policy_t *p) {
+ const qpol_role_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_role_trans_get_default_role(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get default for role_transition rule");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+};
+%inline %{
+ qpol_role_trans_t *qpol_role_trans_from_void(void *x) {
+ return (qpol_role_trans_t*)x;
+ };
+%}
+
+/* qpol range trans */
+typedef struct qpol_range_trans {} qpol_range_trans_t;
+%extend qpol_range_trans_t {
+ qpol_range_trans_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_range_trans_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_range_trans_t() {
+ /* no op */
+ return;
+ };
+ const qpol_type_t *get_source_type (qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_range_trans_get_source_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source for range_transition rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_type_t *get_target_type (qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_range_trans_get_target_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target for range_transition rule"); }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_class_t *get_target_class(qpol_policy_t *p) {
+ const qpol_class_t *cls;
+ BEGIN_EXCEPTION
+ if (qpol_range_trans_get_target_class(p, self, &cls)) {
+ SWIG_exception(SWIG_ValueError, "Could not get class for range_transition rule"); }
+ END_EXCEPTION
+ fail:
+ return cls;
+ };
+ const qpol_mls_range_t *get_range(qpol_policy_t *p) {
+ const qpol_mls_range_t *r;
+ BEGIN_EXCEPTION
+ if (qpol_range_trans_get_range(p, self, &r)) {
+ SWIG_exception(SWIG_ValueError, "Could not get range for range_transition rule");
+ }
+ END_EXCEPTION
+ fail:
+ return r;
+ };
+};
+%inline %{
+ qpol_range_trans_t *qpol_range_trans_from_void(void *x) {
+ return (qpol_range_trans_t*)x;
+ };
+%}
+
+/* qpol av rule */
+#define QPOL_RULE_ALLOW 1
+#define QPOL_RULE_NEVERALLOW 128
+#define QPOL_RULE_AUDITALLOW 2
+#define QPOL_RULE_DONTAUDIT 4
+typedef struct qpol_avrule {} qpol_avrule_t;
+%extend qpol_avrule_t {
+ qpol_avrule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_avrule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_avrule_t() {
+ /* no op */
+ return;
+ };
+ int get_rule_type(qpol_policy_t *p) {
+ uint32_t rt;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_rule_type(p, self, &rt)) {
+ SWIG_exception(SWIG_ValueError, "Could not get rule type for av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) rt;
+ };
+ const qpol_type_t *get_source_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_source_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source for av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_type_t *get_target_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_target_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target for av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_class_t *get_object_class(qpol_policy_t *p) {
+ const qpol_class_t *cls;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_object_class(p, self, &cls)) {
+ SWIG_exception(SWIG_ValueError, "Could not get class for av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return cls;
+ };
+ %newobject get_perm_iter(qpol_policy_t *p);
+ qpol_iterator_t *get_perm_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_perm_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ const qpol_cond_t *get_cond(qpol_policy_t *p) {
+ const qpol_cond_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_cond(p, self, &c)) {
+ SWIG_exception(SWIG_ValueError, "Could not get conditional for av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return c;
+ };
+ int get_is_enabled(qpol_policy_t *p) {
+ uint32_t e;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_is_enabled(p, self, &e)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if av rule is enabled");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) e;
+ };
+ int get_which_list(qpol_policy_t *p) {
+ const qpol_cond_t *c;
+ uint32_t which = 0;
+ BEGIN_EXCEPTION
+ qpol_avrule_get_cond(p, self, &c);
+ if (c == NULL) {
+ SWIG_exception(SWIG_TypeError, "Rule is not conditional");
+ } else if (qpol_avrule_get_which_list(p, self, &which)) {
+ SWIG_exception(SWIG_ValueError, "Could not get conditional list for av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) which;
+ };
+ %newobject get_syn_avrule_iter(qpol_policy_t*);
+ qpol_iterator_t *get_syn_avrule_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_avrule_get_syn_avrule_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_avrule_t *qpol_avrule_from_void(void *x) {
+ return (qpol_avrule_t*)x;
+ };
+%}
+
+/* qpol te rule */
+#define QPOL_RULE_TYPE_TRANS 16
+#define QPOL_RULE_TYPE_CHANGE 64
+#define QPOL_RULE_TYPE_MEMBER 32
+typedef struct qpol_terule {} qpol_terule_t;
+%extend qpol_terule_t {
+ qpol_terule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_terule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_terule_t() {
+ /* no op */
+ return;
+ };
+ int get_rule_type(qpol_policy_t *p) {
+ uint32_t rt;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_rule_type(p, self, &rt)) {
+ SWIG_exception(SWIG_ValueError, "Could not get rule type for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) rt;
+ };
+ const qpol_type_t *get_source_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_source_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_type_t *get_target_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_target_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_class_t *get_object_class(qpol_policy_t *p) {
+ const qpol_class_t *cls;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_object_class(p, self, &cls)) {
+ SWIG_exception(SWIG_ValueError, "Could not get class for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return cls;
+ };
+ const qpol_type_t *get_default_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_default_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get default for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ const qpol_cond_t *get_cond(qpol_policy_t *p) {
+ const qpol_cond_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_cond(p, self, &c)) {
+ SWIG_exception(SWIG_ValueError, "Could not get conditional for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return c;
+ };
+ int get_is_enabled(qpol_policy_t *p) {
+ uint32_t e;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_is_enabled(p, self, &e)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if te rule is enabled");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) e;
+ };
+ int get_which_list(qpol_policy_t *p) {
+ const qpol_cond_t *c;
+ uint32_t which = 0;
+ BEGIN_EXCEPTION
+ qpol_terule_get_cond(p, self, &c);
+ if (c == NULL) {
+ SWIG_exception(SWIG_TypeError, "Rule is not conditional");
+ } else if (qpol_terule_get_which_list(p, self, &which)) {
+ SWIG_exception(SWIG_ValueError, "Could not get conditional list for te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) which;
+ };
+ %newobject get_syn_terule_iter(qpol_policy_t*);
+ qpol_iterator_t *get_syn_terule_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_terule_get_syn_terule_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+};
+%inline %{
+ qpol_terule_t *qpol_terule_from_void(void *x) {
+ return (qpol_terule_t*)x;
+ };
+%}
+
+/* qpol conditional */
+typedef struct qpol_cond {} qpol_cond_t;
+%extend qpol_cond_t {
+ qpol_cond_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_cond_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_cond_t() {
+ /* no op */
+ return;
+ };
+ %newobject get_expr_node_iter(qpol_policy_t*);
+ qpol_iterator_t *get_expr_node_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_cond_get_expr_node_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_av_true_iter(qpol_policy_t*, int);
+ qpol_iterator_t *get_av_true_iter(qpol_policy_t *p, int rule_types) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_cond_get_av_true_iter(p, self, rule_types, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_av_false_iter(qpol_policy_t*, int);
+ qpol_iterator_t *get_av_false_iter(qpol_policy_t *p, int rule_types) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_cond_get_av_false_iter(p, self, rule_types, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_te_true_iter(qpol_policy_t*, int);
+ qpol_iterator_t *get_te_true_iter(qpol_policy_t *p, int rule_types) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_cond_get_te_true_iter(p, self, rule_types, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_te_false_iter(qpol_policy_t*, int);
+ qpol_iterator_t *get_te_false_iter(qpol_policy_t *p, int rule_types) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_cond_get_te_false_iter(p, self, rule_types, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ int eval(qpol_policy_t *p) {
+ uint32_t e;
+ BEGIN_EXCEPTION
+ if (qpol_cond_eval(p, self, &e)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not evaluate conditional");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) e;
+ };
+};
+%inline %{
+ qpol_cond_t *qpol_cond_from_void(void *x) {
+ return (qpol_cond_t*)x;
+ };
+%}
+
+/* qpol conditional expression node */
+#define QPOL_COND_EXPR_BOOL 1 /* plain bool */
+#define QPOL_COND_EXPR_NOT 2 /* !bool */
+#define QPOL_COND_EXPR_OR 3 /* bool || bool */
+#define QPOL_COND_EXPR_AND 4 /* bool && bool */
+#define QPOL_COND_EXPR_XOR 5 /* bool ^ bool */
+#define QPOL_COND_EXPR_EQ 6 /* bool == bool */
+#define QPOL_COND_EXPR_NEQ 7 /* bool != bool */
+typedef struct qpol_cond_expr_node {} qpol_cond_expr_node_t;
+%extend qpol_cond_expr_node_t {
+ qpol_cond_expr_node_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_cond_expr_node_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_cond_expr_node_t() {
+ /* no op */
+ return;
+ };
+ int get_expr_type(qpol_policy_t *p) {
+ uint32_t et;
+ BEGIN_EXCEPTION
+ if (qpol_cond_expr_node_get_expr_type(p, self, &et)) {
+ SWIG_exception(SWIG_ValueError, "Could not get node expression type");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) et;
+ };
+ qpol_bool_t *get_bool(qpol_policy_t *p) {
+ uint32_t et;
+ qpol_bool_t *b = NULL;
+ BEGIN_EXCEPTION
+ qpol_cond_expr_node_get_expr_type(p, self, &et);
+ if (et != QPOL_COND_EXPR_BOOL) {
+ SWIG_exception(SWIG_TypeError, "Node does not contain a boolean");
+ } else if (qpol_cond_expr_node_get_bool(p, self, &b)) {
+ SWIG_exception(SWIG_ValueError, "Could not get boolean for node");
+ }
+ END_EXCEPTION
+ fail:
+ return b;
+ };
+};
+%inline %{
+ qpol_cond_expr_node_t *qpol_cond_expr_node_from_void(void *x) {
+ return (qpol_cond_expr_node_t*)x;
+ };
+%}
+
+/* qpol type set */
+typedef struct qpol_type_set {} qpol_type_set_t;
+%extend qpol_type_set_t {
+ qpol_type_set_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_type_set_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_type_set_t() {
+ /* no op */
+ return;
+ };
+ %newobject get_included_types_iter(qpol_policy_t*);
+ qpol_iterator_t *get_included_types_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_type_set_get_included_types_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_subtracted_types_iter(qpol_policy_t*);
+ qpol_iterator_t *get_subtracted_types_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_type_set_get_subtracted_types_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ int get_is_star(qpol_policy_t *p) {
+ uint32_t s;
+ BEGIN_EXCEPTION
+ if (qpol_type_set_get_is_star(p, self, &s)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if type set contains star");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) s;
+ };
+ int get_is_comp(qpol_policy_t *p) {
+ uint32_t c;
+ BEGIN_EXCEPTION
+ if (qpol_type_set_get_is_comp(p, self, &c)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if type set is complemented");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) c;
+ };
+};
+%inline %{
+ qpol_type_set_t *qpol_type_set_from_void(void *x) {
+ return (qpol_type_set_t*)x;
+ };
+%}
+
+/* qpol syn av rule */
+typedef struct qpol_syn_avrule {} qpol_syn_avrule_t;
+%extend qpol_syn_avrule_t {
+ qpol_syn_avrule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_syn_avrule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_syn_avrule_t() {
+ /* no op */
+ return;
+ };
+ int get_rule_type(qpol_policy_t *p) {
+ uint32_t rt;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_rule_type(p, self, &rt)) {
+ SWIG_exception(SWIG_ValueError, "Could not get rule type for syn av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) rt;
+ };
+ const qpol_type_set_t *get_source_type_set(qpol_policy_t *p) {
+ const qpol_type_set_t *ts;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_source_type_set(p, self, &ts)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source type set for syn av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return ts;
+ };
+ const qpol_type_set_t *get_target_type_set(qpol_policy_t *p) {
+ const qpol_type_set_t *ts;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_target_type_set(p, self, &ts)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target type set for syn av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return ts;
+ };
+ int get_is_target_self(qpol_policy_t *p) {
+ uint32_t i;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_is_target_self(p, self, &i)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if target is self for syn av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) i;
+ };
+ %newobject get_class_iter(qpol_policy_t*);
+ qpol_iterator_t *get_class_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_class_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ %newobject get_perm_iter(qpol_policy_t*);
+ qpol_iterator_t *get_perm_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_perm_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ long get_lineno(qpol_policy_t *p) {
+ unsigned long l;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_lineno(p, self, &l)) {
+ SWIG_exception(SWIG_ValueError, "Could not get line number for syn av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (long)l;
+ };
+ const qpol_cond_t *get_cond(qpol_policy_t *p) {
+ const qpol_cond_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_cond(p, self, &c)) {
+ SWIG_exception(SWIG_ValueError, "Could not get conditional for syn av rule");
+ }
+ END_EXCEPTION
+ fail:
+ return c;
+ };
+ int get_is_enabled(qpol_policy_t *p) {
+ uint32_t e;
+ BEGIN_EXCEPTION
+ if (qpol_syn_avrule_get_is_enabled(p, self, &e)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if syn av rule is enabled");
+ }
+ END_EXCEPTION
+ fail:
+ return e;
+ };
+};
+%inline %{
+ qpol_syn_avrule_t *qpol_syn_avrule_from_void(void *x) {
+ return (qpol_syn_avrule_t*)x;
+ };
+%}
+
+/* qpol syn te rule */
+typedef struct qpol_syn_terule {} qpol_syn_terule_t;
+%extend qpol_syn_terule_t {
+ qpol_syn_terule_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create qpol_syn_terule_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~qpol_syn_terule_t() {
+ /* no op */
+ return;
+ };
+ int get_rule_type(qpol_policy_t *p) {
+ uint32_t rt;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_rule_type(p, self, &rt)) {
+ SWIG_exception(SWIG_ValueError, "Could not get rule type for syn te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return rt;
+ };
+ const qpol_type_set_t *get_source_type_set(qpol_policy_t *p) {
+ const qpol_type_set_t *ts;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_source_type_set(p, self, &ts)) {
+ SWIG_exception(SWIG_ValueError, "Could not get source type set for syn te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return ts;
+ };
+ const qpol_type_set_t *get_target_type_set(qpol_policy_t *p) {
+ const qpol_type_set_t *ts;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_target_type_set(p, self, &ts)) {
+ SWIG_exception(SWIG_ValueError, "Could not get target type set for syn te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return ts;
+ };
+ %newobject get_class_iter(qpol_policy_t*);
+ qpol_iterator_t *get_class_iter(qpol_policy_t *p) {
+ qpol_iterator_t *iter;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_class_iter(p, self, &iter)) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return iter;
+ };
+ const qpol_type_t *get_default_type(qpol_policy_t *p) {
+ const qpol_type_t *t;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_default_type(p, self, &t)) {
+ SWIG_exception(SWIG_ValueError, "Could not get default type for syn te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ long get_lineno(qpol_policy_t *p) {
+ unsigned long l;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_lineno(p, self, &l)) {
+ SWIG_exception(SWIG_ValueError, "Could not get line number for syn te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return (long)l;
+ };
+ const qpol_cond_t *get_cond(qpol_policy_t *p) {
+ const qpol_cond_t *c;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_cond(p, self, &c)) {
+ SWIG_exception(SWIG_ValueError, "Could not get conditional for syn te rule");
+ }
+ END_EXCEPTION
+ fail:
+ return c;
+ };
+ int get_is_enabled(qpol_policy_t *p) {
+ uint32_t e;
+ BEGIN_EXCEPTION
+ if (qpol_syn_terule_get_is_enabled(p, self, &e)) {
+ SWIG_exception(SWIG_ValueError, "Could not determine if syn te rule is enabled");
+ }
+ END_EXCEPTION
+ fail:
+ return (int) e;
+ };
+};
+%inline %{
+ qpol_syn_terule_t *qpol_syn_terule_from_void(void *x) {
+ return (qpol_syn_terule_t*)x;
+ };
+%}
+// vim:ft=c noexpandtab
diff --git a/libqpol/swig/tcl/Makefile.am b/libqpol/swig/tcl/Makefile.am
new file mode 100644
index 0000000..f65d301
--- /dev/null
+++ b/libqpol/swig/tcl/Makefile.am
@@ -0,0 +1,35 @@
+wrappedso_DATA = libtqpol.so.@libqpol_version@
+wrappedso_SONAME = @libqpol_tswig_soname@
+short_name = libtqpol.so
+wrappedsodir = $(libdir)/setools/qpol
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../qpol.i
+BUILT_SOURCES = qpol_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libqpol/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @QPOL_LIB_FLAG@ @TCL_LIB_SPEC@
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_TCL_OPT) -pkgversion @libqpol_version@ -o $@ $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+libdirs = $(top_builddir)/libqpol/src
+
+$(package_SCRIPTS): $(wrappedso_DATA)
+ echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) qpol
+ cp $(wrappedso_DATA) $@ qpol
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) qpol/$(wrappedso_DATA) qpol/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libqpol/tests/Makefile.am b/libqpol/tests/Makefile.am
new file mode 100644
index 0000000..bad0b82
--- /dev/null
+++ b/libqpol/tests/Makefile.am
@@ -0,0 +1,17 @@
+TESTS = libqpol-tests
+check_PROGRAMS = libqpol-tests
+
+libqpol_tests_SOURCES = \
+ capabilities-tests.c capabilities-tests.h \
+ iterators-tests.c iterators-tests.h \
+ policy-features-tests.c policy-features-tests.h \
+ libqpol-tests.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libqpol_tests_DEPENDENCIES = ../src/libqpol.so
diff --git a/libqpol/tests/capabilities-tests.c b/libqpol/tests/capabilities-tests.c
new file mode 100644
index 0000000..c428c71
--- /dev/null
+++ b/libqpol/tests/capabilities-tests.c
@@ -0,0 +1,542 @@
+/**
+ * @file
+ *
+ * Test policy loading capabilities that were introduced in SETools
+ * 3.2.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007-2008 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <qpol/policy.h>
+
+#include <stdbool.h>
+
+#define POLICY_ROOT TEST_POLICIES "/policy-versions"
+
+struct capability_answer
+{
+ const char *policy_name;
+ int policy_type;
+ unsigned int policy_version;
+ bool has_attributes;
+ bool has_syn_rules;
+ bool has_line_numbers;
+ bool has_conditionals;
+ bool has_mls;
+ bool has_polcaps;
+ bool has_source;
+ bool has_modules;
+ char *enforcing_type, *permissive_type;
+};
+
+static void capability_test(const struct capability_answer *ca)
+{
+ qpol_policy_t *q = NULL;
+ int policy_type = qpol_policy_open_from_file(ca->policy_name, &q, NULL, NULL, QPOL_POLICY_OPTION_NO_NEVERALLOWS);
+ CU_ASSERT_FATAL(policy_type >= 0);
+ CU_ASSERT_EQUAL(policy_type, ca->policy_type);
+
+ unsigned policy_version;
+ int retval;
+ retval = qpol_policy_get_policy_version(q, &policy_version);
+ CU_ASSERT_EQUAL_FATAL(retval, 0);
+ CU_ASSERT_EQUAL(policy_version, ca->policy_version);
+
+ bool cap;
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_ATTRIB_NAMES);
+ CU_ASSERT_EQUAL(cap, ca->has_attributes);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_SYN_RULES);
+ CU_ASSERT_EQUAL(cap, ca->has_syn_rules);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_LINE_NUMBERS);
+ CU_ASSERT_EQUAL(cap, ca->has_line_numbers);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_CONDITIONALS);
+ CU_ASSERT_EQUAL(cap, ca->has_conditionals);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_MLS);
+ CU_ASSERT_EQUAL(cap, ca->has_mls);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_POLCAPS);
+ CU_ASSERT_EQUAL(cap, ca->has_polcaps);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_SOURCE);
+ CU_ASSERT_EQUAL(cap, ca->has_source);
+
+ cap = (bool) qpol_policy_has_capability(q, QPOL_CAP_MODULES);
+ CU_ASSERT_EQUAL(cap, ca->has_modules);
+
+ unsigned char ispermissive;
+ const qpol_type_t *type;
+
+ if (ca->enforcing_type != NULL) {
+ retval = qpol_policy_get_type_by_name(q, ca->enforcing_type, &type);
+ CU_ASSERT(retval == 0 && type != NULL);
+ retval = qpol_type_get_ispermissive(q, type, &ispermissive);
+ CU_ASSERT(retval == 0 && ispermissive == 0);
+ }
+ if (ca->permissive_type != NULL) {
+ retval = qpol_policy_get_type_by_name(q, ca->permissive_type, &type);
+ CU_ASSERT(retval == 0 && type != NULL);
+ retval = qpol_type_get_ispermissive(q, type, &ispermissive);
+ CU_ASSERT(retval == 0 && ispermissive == 1);
+ }
+
+ qpol_policy_destroy(&q);
+}
+
+static void capability_v12_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-12.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 12U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ false, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v15_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-15.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 15U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ false, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v15_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy.15",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 15U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ false, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v16_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-16.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 16U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v16_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy.16",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 16U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v17_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-17.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 17U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v17_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy.17",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 17U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "fs_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v18_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-18.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 18U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "wing_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v18_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy.18",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 18U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "wing_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v19_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy.19",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 19U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "wing_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v19_binary_mls(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls.19",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 19U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v20_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy.20",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 20U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ false, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "wing_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v20_binary_mls(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls.20",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 20U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v21_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls-21.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 21U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ false, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v21_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls.21",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 21U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ false, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v22_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls-22.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 22U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ true, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v22_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls.22",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 22U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ true, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v23_source(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls-23.conf",
+ QPOL_POLICY_KERNEL_SOURCE, // policy type
+ 23U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ true, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ true, // has policy capabilities
+ true, // has source
+ false, // has modules
+ "root_t", "system_t" // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_v23_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/policy-mls.23",
+ QPOL_POLICY_KERNEL_BINARY, // policy type
+ 23U, // policy version
+ false, // has attributes
+ false, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ true, // has policy capabilities
+ false, // has source
+ false, // has modules
+ "root_t", "system_t" // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_modv6_base_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/base-6.pp",
+ QPOL_POLICY_MODULE_BINARY, // policy type
+ 6U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ false, // has policy capabilities
+ false, // has source
+ true, // has modules
+ "root_t", NULL // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+static void capability_modv8_base_binary(void)
+{
+ struct capability_answer cap = {
+ POLICY_ROOT "/base-8.pp",
+ QPOL_POLICY_MODULE_BINARY, // policy type
+ 8U, // policy version
+ true, // has attributes
+ true, // has syntactic rules
+ false, // has line numbers
+ true, // has conditionals
+ true, // has mls
+ true, // has policy capabilities
+ false, // has source
+ true, // has modules
+ "root_t", "system_t" // enforcing / permissive types
+ };
+ capability_test(&cap);
+}
+
+CU_TestInfo capabilities_tests[] = {
+ {"v12, source", capability_v12_source},
+ {"v15, source", capability_v15_source},
+ {"v15, binary", capability_v15_binary},
+ {"v16, source", capability_v16_source},
+ {"v16, binary", capability_v16_binary},
+ {"v17, source", capability_v17_source},
+ {"v17, binary", capability_v17_binary},
+ {"v18, source", capability_v18_source},
+ {"v18, binary", capability_v18_binary},
+ {"v19, binary", capability_v19_binary},
+ {"v19, binary mls", capability_v19_binary_mls},
+ {"v20, binary", capability_v20_binary},
+ {"v20, binary mls", capability_v20_binary_mls},
+ {"v21, source", capability_v21_source},
+ {"v21, binary", capability_v21_binary},
+ {"v22, source", capability_v22_source},
+ {"v22, binary", capability_v22_binary},
+ {"v23, source", capability_v23_source},
+ {"v23, binary", capability_v23_binary},
+ {"mod v6, base binary", capability_modv6_base_binary},
+ {"mod v8, base binary", capability_modv8_base_binary},
+ CU_TEST_INFO_NULL
+};
+
+int capabilities_init()
+{
+ return 0;
+}
+
+int capabilities_cleanup()
+{
+ return 0;
+}
diff --git a/libqpol/tests/capabilities-tests.h b/libqpol/tests/capabilities-tests.h
new file mode 100644
index 0000000..b305b77
--- /dev/null
+++ b/libqpol/tests/capabilities-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libqpol capabilities tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef CAPABILITES_TESTS_H
+#define CAPABILITES_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo capabilities_tests[];
+extern int capabilities_init();
+extern int capabilities_cleanup();
+
+#endif
diff --git a/libqpol/tests/iterators-tests.c b/libqpol/tests/iterators-tests.c
new file mode 100644
index 0000000..384f878
--- /dev/null
+++ b/libqpol/tests/iterators-tests.c
@@ -0,0 +1,87 @@
+/**
+ * @file
+ *
+ * Test qpol iterators.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <qpol/policy.h>
+#include <stdio.h>
+
+#define SOURCE_POLICY TEST_POLICIES "/snapshots/fc4_targeted.policy.conf"
+
+static qpol_policy_t *qp = NULL;
+
+static void iterators_alias(void)
+{
+ qpol_iterator_t *iter = NULL;
+ CU_ASSERT_FATAL(qpol_policy_get_type_iter(qp, &iter) == 0);
+ while (!qpol_iterator_end(iter)) {
+ void *v;
+ CU_ASSERT_FATAL(qpol_iterator_get_item(iter, &v) == 0);
+ qpol_type_t *type = (qpol_type_t *) v;
+
+ qpol_iterator_t *alias_iter = NULL;
+ size_t alias_size;
+ unsigned char isalias = 0;
+ CU_ASSERT_FATAL(qpol_type_get_isalias(qp, type, &isalias) == 0);
+ CU_ASSERT_FATAL(qpol_type_get_alias_iter(qp, type, &alias_iter) == 0);
+ CU_ASSERT_FATAL(qpol_iterator_get_size(alias_iter, &alias_size) == 0);
+
+ if (alias_size > 0) {
+ /* isalias could be 0 or 1, depending upon if
+ type is a primary or an alias */
+ CU_ASSERT(!qpol_iterator_end(alias_iter));
+ } else {
+ /* impossible for isalias to be true if the
+ alias iterator is empty */
+ CU_ASSERT(!isalias && qpol_iterator_end(alias_iter));
+ }
+
+ qpol_iterator_destroy(&alias_iter);
+ CU_ASSERT_FATAL(qpol_iterator_next(iter) == 0);
+ }
+ qpol_iterator_destroy(&iter);
+}
+
+CU_TestInfo iterators_tests[] = {
+ {"alias iterator", iterators_alias}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int iterators_init()
+{
+ int policy_type = qpol_policy_open_from_file(SOURCE_POLICY, &qp, NULL, NULL, QPOL_POLICY_OPTION_NO_RULES);
+ if (policy_type < 0) {
+ return 1;
+ }
+ return 0;
+}
+
+int iterators_cleanup()
+{
+ qpol_policy_destroy(&qp);
+ return 0;
+}
diff --git a/libqpol/tests/iterators-tests.h b/libqpol/tests/iterators-tests.h
new file mode 100644
index 0000000..275f3a2
--- /dev/null
+++ b/libqpol/tests/iterators-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libqpol iterator tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ITERATORS_TESTS_H
+#define ITERATORS_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo iterators_tests[];
+extern int iterators_init();
+extern int iterators_cleanup();
+
+#endif
diff --git a/libqpol/tests/libqpol-tests.c b/libqpol/tests/libqpol-tests.c
new file mode 100644
index 0000000..eda58d6
--- /dev/null
+++ b/libqpol/tests/libqpol-tests.c
@@ -0,0 +1,57 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for libqpol.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "capabilities-tests.h"
+#include "iterators-tests.h"
+#include "policy-features-tests.h"
+
+int main(void)
+{
+ if (CU_initialize_registry() != CUE_SUCCESS) {
+ return CU_get_error();
+ }
+
+ CU_SuiteInfo suites[] = {
+ {"Capabilities", capabilities_init, capabilities_cleanup, capabilities_tests}
+ ,
+ {"Iterators", iterators_init, iterators_cleanup, iterators_tests}
+ ,
+ {"Policy Featurens", policy_features_init, policy_features_cleanup, policy_features_tests}
+ ,
+ CU_SUITE_INFO_NULL
+ };
+
+ CU_register_suites(suites);
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ unsigned int num_failures = CU_get_number_of_failure_records();
+ CU_cleanup_registry();
+ return (int)num_failures;
+}
diff --git a/libqpol/tests/policy-features-tests.c b/libqpol/tests/policy-features-tests.c
new file mode 100644
index 0000000..915dbaf
--- /dev/null
+++ b/libqpol/tests/policy-features-tests.c
@@ -0,0 +1,145 @@
+/**
+ * @file
+ *
+ * Test qpol loading of special types of policies.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <qpol/policy.h>
+#include "../src/qpol_internal.h"
+#include <stdio.h>
+
+#define BROKEN_ALIAS_POLICY TEST_POLICIES "/setools-3.3/policy-features/broken-alias-mod.21"
+#define NOT_BROKEN_ALIAS_POLICY TEST_POLICIES "/setools-3.3/policy-features/not-broken-alias-mod.21"
+#define NOGENFS_POLICY TEST_POLICIES "/setools-3.3/policy-features/nogenfscon-policy.21"
+
+static void policy_features_alias_count(void *varg, const qpol_policy_t * policy
+ __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ if (level == QPOL_MSG_WARN) {
+ int *num_removed_aliases = (int *)varg;
+ (*num_removed_aliases)++;
+ } else if (level == QPOL_MSG_ERR) {
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+ }
+}
+
+/**
+ * If a module has any disabled aliases, test that libqpol removed them.
+ */
+static void policy_features_invalid_alias(void)
+{
+ qpol_policy_t *qp = NULL;
+ int policy_features_removed_aliases = 0;
+ void *v;
+ unsigned char isalias = 0;
+ const char *name;
+
+ int policy_type = qpol_policy_open_from_file(NOT_BROKEN_ALIAS_POLICY, &qp, policy_features_alias_count,
+ &policy_features_removed_aliases, QPOL_POLICY_OPTION_NO_RULES);
+ CU_ASSERT_FATAL(policy_type == QPOL_POLICY_KERNEL_BINARY);
+ CU_ASSERT(policy_features_removed_aliases == 0)
+
+ qpol_iterator_t *iter = NULL;
+ CU_ASSERT_FATAL(qpol_policy_get_type_iter(qp, &iter) == 0);
+ while (!qpol_iterator_end(iter)) {
+ CU_ASSERT_FATAL(qpol_iterator_get_item(iter, &v) == 0);
+ qpol_type_t *type = (qpol_type_t *) v;
+ CU_ASSERT_FATAL(qpol_type_get_isalias(qp, type, &isalias) == 0);
+ if (isalias) {
+ CU_ASSERT_FATAL(qpol_type_get_name(qp, type, &name) == 0);
+ CU_ASSERT_STRING_EQUAL(name, "fs_t");
+ }
+ CU_ASSERT_FATAL(qpol_iterator_next(iter) == 0);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_policy_destroy(&qp);
+
+ policy_features_removed_aliases = 0;
+ policy_type =
+ qpol_policy_open_from_file(BROKEN_ALIAS_POLICY, &qp, policy_features_alias_count, &policy_features_removed_aliases,
+ QPOL_POLICY_OPTION_NO_RULES);
+ CU_ASSERT_FATAL(policy_type == QPOL_POLICY_KERNEL_BINARY);
+ CU_ASSERT(policy_features_removed_aliases == 1)
+
+ CU_ASSERT_FATAL(qpol_policy_get_type_iter(qp, &iter) == 0);
+ while (!qpol_iterator_end(iter)) {
+ CU_ASSERT_FATAL(qpol_iterator_get_item(iter, &v) == 0);
+ qpol_type_t *type = (qpol_type_t *) v;
+ CU_ASSERT_FATAL(qpol_type_get_isalias(qp, type, &isalias) == 0);
+ CU_ASSERT(isalias == 0);
+ CU_ASSERT_FATAL(qpol_iterator_next(iter) == 0);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_policy_destroy(&qp);
+}
+
+/** Test that getting an iterator of genfscon statements does not
+ * fail if there are no genfscon statements. */
+static void policy_features_nogenfscon_iter(void)
+{
+ qpol_policy_t *qp = NULL;
+
+ /* open a policy with no genfscon statements */
+ int policy_type = qpol_policy_open_from_file(NOGENFS_POLICY, &qp, NULL, NULL, QPOL_POLICY_OPTION_NO_RULES);
+ CU_ASSERT_FATAL(policy_type == QPOL_POLICY_KERNEL_BINARY);
+
+ qpol_iterator_t *iter = NULL;
+
+ /* iterator should be safe to request but should be at end */
+ CU_ASSERT_FATAL(qpol_policy_get_genfscon_iter(qp, &iter) == 0);
+ CU_ASSERT(qpol_iterator_end(iter));
+ qpol_iterator_destroy(&iter);
+ qpol_policy_destroy(&qp);
+
+ /* open a policy with genfscon statements */
+ policy_type = qpol_policy_open_from_file(NOT_BROKEN_ALIAS_POLICY, &qp, NULL, NULL, QPOL_POLICY_OPTION_NO_RULES);
+ CU_ASSERT_FATAL(policy_type == QPOL_POLICY_KERNEL_BINARY);
+
+ /* iterator should be safe to request and not at end */
+ CU_ASSERT_FATAL(qpol_policy_get_genfscon_iter(qp, &iter) == 0);
+ CU_ASSERT(!qpol_iterator_end(iter));
+ qpol_iterator_destroy(&iter);
+ qpol_policy_destroy(&qp);
+}
+
+CU_TestInfo policy_features_tests[] = {
+ {"invalid alias", policy_features_invalid_alias}
+ ,
+ {"No genfscon", policy_features_nogenfscon_iter}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int policy_features_init()
+{
+ return 0;
+}
+
+int policy_features_cleanup()
+{
+ return 0;
+}
diff --git a/libqpol/tests/policy-features-tests.h b/libqpol/tests/policy-features-tests.h
new file mode 100644
index 0000000..71463dd
--- /dev/null
+++ b/libqpol/tests/policy-features-tests.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libqpol tests for reading special types of policies.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLICY_FEATURES_TESTS_H
+#define POLICY_FEATURES_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo policy_features_tests[];
+extern int policy_features_init();
+extern int policy_features_cleanup();
+
+#endif
diff --git a/libseaudit/Makefile.am b/libseaudit/Makefile.am
new file mode 100644
index 0000000..b1a8149
--- /dev/null
+++ b/libseaudit/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libseaudit.a libseaudit.so:
+ $(MAKE) -C src $@
diff --git a/libseaudit/include/Makefile.am b/libseaudit/include/Makefile.am
new file mode 100644
index 0000000..e922703
--- /dev/null
+++ b/libseaudit/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = seaudit \ No newline at end of file
diff --git a/libseaudit/include/seaudit/Makefile.am b/libseaudit/include/seaudit/Makefile.am
new file mode 100644
index 0000000..7f11f38
--- /dev/null
+++ b/libseaudit/include/seaudit/Makefile.am
@@ -0,0 +1,14 @@
+seauditdir = $(includedir)/seaudit
+
+seaudit_HEADERS = \
+ avc_message.h \
+ bool_message.h \
+ filter.h \
+ load_message.h \
+ log.h \
+ message.h \
+ model.h \
+ parse.h \
+ report.h \
+ sort.h \
+ util.h
diff --git a/libseaudit/include/seaudit/avc_message.h b/libseaudit/include/seaudit/avc_message.h
new file mode 100644
index 0000000..b7263ea
--- /dev/null
+++ b/libseaudit/include/seaudit/avc_message.h
@@ -0,0 +1,374 @@
+/**
+ * @file
+ * Public interface for a single AVC log message. This is a subclass
+ * of seaudit_message.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_AVC_MESSAGE_H
+#define SEAUDIT_AVC_MESSAGE_H
+
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct seaudit_avc_message seaudit_avc_message_t;
+
+/**
+ * AVC messages may be either a granted (i.e., an allow) or a denied.
+ */
+ typedef enum seaudit_avc_message_type
+ {
+ SEAUDIT_AVC_UNKNOWN = 0,
+ SEAUDIT_AVC_DENIED,
+ SEAUDIT_AVC_GRANTED
+ } seaudit_avc_message_type_e;
+
+/**
+ * Return the type of avc message this is, either a granted (i.e., an
+ * allow) or a denied.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return One of SEAUDIT_AVC_DENIED or SEAUDIT_AVC_GRANTED, or
+ * SEAUDIT_AVC_UNKNOWN upon error or if unknown.
+ */
+ extern seaudit_avc_message_type_e seaudit_avc_message_get_message_type(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the avc message's timestamp, measured in nanoseconds.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Timestamp, in nanoseconds, or 0 upon error or if unknown.
+ */
+ extern long seaudit_avc_message_get_timestamp_nano(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source context's user of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source user, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_source_user(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source context's role of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source role, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_source_role(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source context's type of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source target, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_source_type(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source context's mls level of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source target, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_source_mls_lvl(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source context's mls clearance of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source target, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_source_mls_clr(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the target context's user of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Target user, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_target_user(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the target context's role of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Target role, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_target_role(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the target context's type of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Target type, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_target_type(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the target context's mls level of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Target type, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_target_mls_lvl(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the target context's mls clearance of an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Target type, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_target_mls_clr(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the object class from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Object class, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_object_class(const seaudit_avc_message_t * avc);
+
+/**
+ * Return a vector of permissions (type char *) from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Vector of permission strings, or NULL upon error or if
+ * unknown. Do not modify the vector in any way.
+ */
+ extern const apol_vector_t *seaudit_avc_message_get_perm(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the executable and path from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Executable string, or NULL upon error or if unknown. Do
+ * not free() this string.
+ */
+ extern const char *seaudit_avc_message_get_exe(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the command from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Command, or NULL upon error or if unknown. Do not free()
+ * this string.
+ */
+ extern const char *seaudit_avc_message_get_comm(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the name from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Name, or NULL upon error or if unknown. Do not free() this
+ * string.
+ */
+ extern const char *seaudit_avc_message_get_name(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the process ID from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Process's PID, or 0 upon error or if unknown.
+ */
+ extern unsigned int seaudit_avc_message_get_pid(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the inode from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Process's PID, or 0 upon error or if unknown.
+ */
+ extern unsigned long seaudit_avc_message_get_inode(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the path of the object from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Object's path, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_path(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the device for the object from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Object's device, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_dev(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the network interface for the object from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Network interface, or NULL upon error or if unknown. Do
+ * not free() this string.
+ */
+ extern const char *seaudit_avc_message_get_netif(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the port number from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Port, or 0 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_port(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the local address from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Local address, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_laddr(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the local port from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Local port, or 0 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_lport(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the foreign address from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Foreign address, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_faddr(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the foreign port from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Foreign port, or 0 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_fport(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source address from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source address, or NULL upon error or if unknown. Do not
+ * free() this string.
+ */
+ extern const char *seaudit_avc_message_get_saddr(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the source port from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Source port, or 0 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_sport(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the destination address from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Destination address, or NULL upon error or if unknown. Do
+ * not free() this string.
+ */
+ extern const char *seaudit_avc_message_get_daddr(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the destination port from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Destination port, or 0 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_dport(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the IPC key from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Key, or -1 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_key(const seaudit_avc_message_t * avc);
+
+/**
+ * Return the process capability from an avc message.
+ *
+ * @param avc AVC message to check.
+ *
+ * @return Capability, or -1 upon error or if unknown.
+ */
+ extern int seaudit_avc_message_get_cap(const seaudit_avc_message_t * avc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/bool_message.h b/libseaudit/include/seaudit/bool_message.h
new file mode 100644
index 0000000..4ff9df3
--- /dev/null
+++ b/libseaudit/include/seaudit/bool_message.h
@@ -0,0 +1,43 @@
+/**
+ * @file
+ * Public interface for a single boolean change log message. This is
+ * a subclass of seaudit_message; it has no publicly accessible
+ * functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_BOOL_MESSAGE_H
+#define SEAUDIT_BOOL_MESSAGE_H
+
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct seaudit_bool_message seaudit_bool_message_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/filter.h b/libseaudit/include/seaudit/filter.h
new file mode 100644
index 0000000..ce40b5e
--- /dev/null
+++ b/libseaudit/include/seaudit/filter.h
@@ -0,0 +1,1025 @@
+/**
+ * @file
+ *
+ * Public interface to a seaudit_filter. A filter is used to modify
+ * the list of messages returned from a seaudit_model.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_FILTER_H
+#define SEAUDIT_FILTER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <seaudit/avc_message.h>
+
+#include <apol/vector.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <time.h>
+
+ typedef struct seaudit_filter seaudit_filter_t;
+
+/**
+ * By default, all criteria of a filter must be met for a message to
+ * be accepted. This behavior can be changed such that a message is
+ * accepted if any of the criteria pass.
+ */
+ typedef enum seaudit_filter_match
+ {
+ SEAUDIT_FILTER_MATCH_ALL = 0,
+ SEAUDIT_FILTER_MATCH_ANY
+ } seaudit_filter_match_e;
+
+/**
+ * By default, only messages accepted by filters will be shown by the
+ * model. This behavior can be changed such that filters are used to
+ * select messages to hide.
+ */
+ typedef enum seaudit_filter_visible
+ {
+ SEAUDIT_FILTER_VISIBLE_SHOW = 0,
+ SEAUDIT_FILTER_VISIBLE_HIDE
+ } seaudit_filter_visible_e;
+
+/**
+ * When specifying a date/time for the filter, one must also give how
+ * to match the date and time.
+ */
+ typedef enum seaudit_filter_date_match
+ {
+ SEAUDIT_FILTER_DATE_MATCH_BEFORE = 0,
+ SEAUDIT_FILTER_DATE_MATCH_AFTER,
+ SEAUDIT_FILTER_DATE_MATCH_BETWEEN
+ } seaudit_filter_date_match_e;
+
+/**
+ * Create a new filter object. The default matching behavior is to
+ * accept all messages.
+ *
+ * @param name Name for the filter; the string will be duplicated. If
+ * NULL then the filter will be assigned a default name.
+ *
+ * @return A newly allocated filter. The caller is responsible for
+ * calling seaudit_filter_destroy() afterwards.
+ */
+ extern seaudit_filter_t *seaudit_filter_create(const char *name);
+
+/**
+ * Create a new filter object, initialized with the data from an
+ * existing filter. This will do a deep copy of the original filter.
+ * The new filter will not be attached to any model.
+ *
+ * @param filter Filter to clone.
+ *
+ * @return A cloned filter, or NULL upon error. The caller is
+ * responsible for calling seaudit_filter_destroy() afterwards.
+ */
+ extern seaudit_filter_t *seaudit_filter_create_from_filter(const seaudit_filter_t * filter);
+
+/**
+ * Create and return a vector of filters (type seaudit_filter),
+ * initialized from the contents of a XML configuration file.
+ *
+ * @param filename File containing one or more filter data.
+ *
+ * @return Vector of filters created from that file, or NULL upon
+ * error. The caller is responsible for apol_vector_destroy().
+ *
+ * @see seaudit_filter_save_to_file()
+ */
+ extern apol_vector_t *seaudit_filter_create_from_file(const char *filename);
+
+/**
+ * Destroy the referenced seaudit_filter object.
+ *
+ * @param filter Filter object to destroy. The pointer will be set to
+ * NULL afterwards. (If pointer is already NULL then do nothing.)
+ */
+ extern void seaudit_filter_destroy(seaudit_filter_t ** filter);
+
+/**
+ * Save to disk, in XML format, the given filter's values. This
+ * includes the filter's criteria.
+ *
+ * @param filter Filter to save.
+ * @param filename Name of the file to write. If the file already
+ * exists it will be overwritten.
+ *
+ * @return 0 on success, < 0 on error.
+ *
+ * @see seaudit_filter_create_from_file()
+ */
+ extern int seaudit_filter_save_to_file(const seaudit_filter_t * filter, const char *filename);
+
+/**
+ * Set a filter to accept a message if all criteria are met (default
+ * behavior) or if any criterion is met.
+ *
+ * @param filter Filter to modify.
+ * @param match Matching behavior if filter has multiple criteria.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_match(seaudit_filter_t * filter, seaudit_filter_match_e match);
+
+/**
+ * Get the current match value for a filter.
+ *
+ * @param filter Filter containing match value.
+ *
+ * @return One of SEAUDIT_FILTER_MATCH_ALL or SEAUDIT_FILTER_MATCH_ANY.
+ */
+ extern seaudit_filter_match_e seaudit_filter_get_match(const seaudit_filter_t * filter);
+
+/**
+ * Set the name of this filter, overwriting any previous name.
+ *
+ * @param filter Filter to modify.
+ * @param name New name for this filter. This function will duplicate
+ * the string. If this is NULL then clear the existing name.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_name(seaudit_filter_t * filter, const char *name);
+
+/**
+ * Get the name of this filter.
+ *
+ * @param filter Filter from which to get name.
+ *
+ * @return Name of the filter, or NULL if no name has been set. Do
+ * not free() or otherwise modify this string.
+ */
+ extern const char *seaudit_filter_get_name(const seaudit_filter_t * filter);
+
+/**
+ * Set the description of this filter, overwriting any previous
+ * description.
+ *
+ * @param filter Filter to modify.
+ * @param desc New description for this filter. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * description.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_description(seaudit_filter_t * filter, const char *desc);
+
+/**
+ * Get the description of this filter.
+ *
+ * @param filter Filter from which to get description.
+ *
+ * @return Description of the filter, or NULL if no description has
+ * been set. Do not free() or otherwise modify this string.
+ */
+ extern const char *seaudit_filter_get_description(const seaudit_filter_t * filter);
+
+/**
+ * Set the strictness of this filter. By default, the filter's
+ * criteria are not "strict", meaning if a message does not have a
+ * field then the criterion will match it. For example, an AVC denied
+ * message might not have an 'laddr' field in it. If a filter was
+ * created with seaudit_filter_set_laddr(), the filter would still
+ * accept the message.
+ *
+ * If instead a filter is set as strict, then messages that do not
+ * have the field in question will be rejected. For the example
+ * above, a strict filter would eliminate that AVC message. In
+ * addition, an empty filter (i.e., one without any criterion set)
+ * does not match any messages if it is set to strict.
+ *
+ * @param filter Filter to modify.
+ * @param strict If true, enable strict matching.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_strict(seaudit_filter_t * filter, bool is_strict);
+
+/**
+ * Get the strictness of this filter.
+ *
+ * @param filter Filter from which to get strictness.
+ *
+ * @return True if the filter will reject messages that do not contain
+ * fields being filtered, false if they are accepted.
+ */
+ extern bool seaudit_filter_get_strict(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of source users. A message is accepted if its source
+ * user is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_source_user(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of source users for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_source_user(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of source roles. A message is accepted if its source
+ * role is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_source_role(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of source roles for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_source_role(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of source types. A message is accepted if its source
+ * type is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_source_type(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of source types for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_source_type(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of source mls levels. A message is accepted if its source
+ * mls level is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_source_mls_lvl(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of source mls levels for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_source_mls_lvl(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of source mls clearance. A message is accepted if its source
+ * mls clearance is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_source_mls_clr(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of source mls clearance for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_source_mls_clr(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of target users. A message is accepted if its target
+ * user is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_target_user(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of target users for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_target_user(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of target roles. A message is accepted if its target
+ * role is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_target_role(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of target roles for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_target_role(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of target types. A message is accepted if its target
+ * type is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_target_type(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of target types for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_target_type(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of target mls levels. A message is accepted if its target
+ * mls level is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_target_mls_lvl(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of target mls levels for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_target_mls_lvl(const seaudit_filter_t * filter);
+
+ /**
+ * Set the list of target mls clearance. A message is accepted if its target
+ * mls clearance is within this list. The filter will duplicate the vector and
+ * the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_target_mls_clr(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of target mls clearance for a filter. This will be
+ * a vector of strings. Treat the vector and its contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_target_mls_clr(const seaudit_filter_t * filter);
+
+/**
+ * Set the list of target object classes. A message is accepted if
+ * its target class is within this list. The filter will duplicate
+ * the vector and the strings within.
+ *
+ * @param filter Filter to modify.
+ * @param v Vector of strings, or NULL to clear current settings.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_target_class(seaudit_filter_t * filter, const apol_vector_t * v);
+
+/**
+ * Return the current list of target object classes for a filter.
+ * This will be a vector of strings. Treat the vector and its
+ * contents as const.
+ *
+ * @param filter Filter to get values.
+ *
+ * @return Vector of strings, or NULL if no value has been set.
+ */
+ extern const apol_vector_t *seaudit_filter_get_target_class(const seaudit_filter_t * filter);
+
+/**
+ * Set the permission criterion, as a glob expression. A message is
+ * accepted if at least one of its AVC permissions match the
+ * criterion.
+ *
+ * @param filter Filter to modify.
+ * @param perm Glob expression for permission. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * permission.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_permission(seaudit_filter_t * filter, const char *perm);
+
+/**
+ * Return the current permission for a filter. Treat this string as
+ * const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for permission, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_permission(const seaudit_filter_t * filter);
+
+/**
+ * Set the executable criterion, as a glob expression. A message is
+ * accepted if its executable matches this expression.
+ *
+ * @param filter Filter to modify.
+ * @param exe Glob expression for executable. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * executable.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_executable(seaudit_filter_t * filter, const char *exe);
+
+/**
+ * Return the current executable for a filter. Treat this string as
+ * const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for executable, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_executable(const seaudit_filter_t * filter);
+
+/**
+ * Set the host criterion, as a glob expression. A message is
+ * accepted if its host matches this expression.
+ *
+ * @param filter Filter to modify.
+ * @param host Glob expression for host. This function will duplicate
+ * the string. If this is NULL then clear the existing host.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_host(seaudit_filter_t * filter, const char *host);
+
+/**
+ * Return the current host for a filter. Treat this string as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for host, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_host(const seaudit_filter_t * filter);
+
+/**
+ * Set the path criterion, as a glob expression. A message is
+ * accepted if its path matches this expression.
+ *
+ * @param filter Filter to modify.
+ * @param path Glob expression for path. This function will duplicate
+ * the string. If this is NULL then clear the existing path.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_path(seaudit_filter_t * filter, const char *path);
+
+/**
+ * Return the current path for a filter. Treat this string as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for path, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_path(const seaudit_filter_t * filter);
+
+/**
+ * Set the inode criterion. A message is accepted if its inode
+ * exactly matches this inode value.
+ *
+ * @param filter Filter to modify.
+ * @param inode inode value to match. If this is 0 then clear the
+ * existing inode.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_inode(seaudit_filter_t * filter, unsigned long inode);
+
+/**
+ * Return the current inode for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current inode value, or 0 if none set.
+ */
+ extern unsigned long seaudit_filter_get_inode(const seaudit_filter_t * filter);
+
+/**
+ * Set the pid criterion. A message is accepted if its pid value
+ * exactly matches this pid value.
+ *
+ * @param filter Filter to modify.
+ * @param pid value to match. If this is 0 then clear the existing pid.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_pid(seaudit_filter_t * filter, unsigned int pid);
+
+/**
+ * Return the current pid for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current pid value, or 0 if none set.
+ */
+ extern unsigned int seaudit_filter_get_pid(const seaudit_filter_t * filter);
+
+/**
+ * Set the command criterion, as a glob expression. A message is
+ * accepted if its command matches this expression.
+ *
+ * @param filter Filter to modify.
+ * @param command Glob expression for command. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * command.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_command(seaudit_filter_t * filter, const char *command);
+
+/**
+ * Return the current command for a filter. Treat this string as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for command, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_command(const seaudit_filter_t * filter);
+
+/**
+ * Set the IP address criterion, as a glob expression. A message is
+ * accepted if any of its IP addresses (ipaddr, saddr, daddr, faddr,
+ * or laddr) matches this expression.
+ *
+ * @param filter Filter to modify.
+ * @param ipaddr Glob expression for IP address. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * address.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_anyaddr(seaudit_filter_t * filter, const char *ipaddr);
+
+/**
+ * Return the current IP address for a filter. Treat this string as
+ * const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for address, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_anyaddr(const seaudit_filter_t * filter);
+
+/**
+ * Set the port criterion. A message is accepted if any of its ports
+ * (port, source, dest, fport, or lport) matches this port.
+ *
+ * @param filter Filter to modify.
+ * @param port Port criterion. If this is zero or negative then clear
+ * the existing port.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_anyport(seaudit_filter_t * filter, const int port);
+
+/**
+ * Return the current port for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current port criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_anyport(const seaudit_filter_t * filter);
+
+/**
+ * Set the local address criterion, as a glob expression. A message
+ * is accepted if its local address (laddr) matches this expression.
+ * Note that if seaudit_filter_set_anyaddr() is also set, then the
+ * message must match both ipaddr and laddr for it to be accepted
+ * (assuming that the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param laddr Glob expression for local address. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * address.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_laddr(seaudit_filter_t * filter, const char *laddr);
+
+/**
+ * Return the current local address for a filter. Treat this string
+ * as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for address, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_laddr(const seaudit_filter_t * filter);
+
+/**
+ * Set the local port criterion. A message is accepted if its local
+ * port (lport) matches this port. Note that if
+ * seaudit_filter_set_anyport() is also set, then the message must
+ * match both anyport and lport for it to be accepted (assuming that
+ * the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param lport Local port criterion. If this is zero or negative
+ * then clear the existing port.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_lport(seaudit_filter_t * filter, const int lport);
+
+/**
+ * Return the current local port for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current port criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_lport(const seaudit_filter_t * filter);
+
+/**
+ * Set the foreign address criterion, as a glob expression. A message
+ * is accepted if its foreign address (faddr) matches this expression.
+ * Note that if seaudit_filter_set_anyaddr() is also set, then the
+ * message must match both ipaddr and faddr for it to be accepted
+ * (assuming that the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param faddr Glob expression for foreign address. This function
+ * will duplicate the string. If this is NULL then clear the existing
+ * address.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_faddr(seaudit_filter_t * filter, const char *faddr);
+
+/**
+ * Return the current foreign address for a filter. Treat this string
+ * as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for address, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_faddr(const seaudit_filter_t * filter);
+
+/**
+ * Set the foreign port criterion. A message is accepted if its
+ * foreign port (fport) matches this port. Note that if
+ * seaudit_filter_set_anyport() is also set, then the message must
+ * match both anyport and fport for it to be accepted (assuming that
+ * the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param fport Foreign port criterion. If this is zero or negative
+ * then clear the existing port.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_fport(seaudit_filter_t * filter, const int fport);
+
+/**
+ * Return the current foreign port for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current port criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_fport(const seaudit_filter_t * filter);
+
+/**
+ * Set the source address criterion, as a glob expression. A message
+ * is accepted if its source address (saddr) matches this expression.
+ * Note that if seaudit_filter_set_anyaddr() is also set, then the
+ * message must match both ipaddr and saddr for it to be accepted
+ * (assuming that the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param saddr Glob expression for source address. This function
+ * will duplicate the string. If this is NULL then clear the existing
+ * address.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_saddr(seaudit_filter_t * filter, const char *saddr);
+
+/**
+ * Return the current source address for a filter. Treat this string
+ * as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for address, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_saddr(const seaudit_filter_t * filter);
+
+/**
+ * Set the source port criterion. A message is accepted if its source
+ * port (sport) matches this port. Note that if
+ * seaudit_filter_set_anyport() is also set, then the message must
+ * match both anyport and sport for it to be accepted (assuming that
+ * the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param sport Source port criterion. If this is zero or negative
+ * then clear the existing port.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_sport(seaudit_filter_t * filter, const int sport);
+
+/**
+ * Return the current source port for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current port criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_sport(const seaudit_filter_t * filter);
+
+/**
+ * Set the destination address criterion, as a glob expression. A
+ * message is accepted if its destination address (daddr) matches this
+ * expression. Note that if seaudit_filter_set_anyaddr() is also set,
+ * then the message must match both ipaddr and daddr for it to be
+ * accepted (assuming that the match is set to
+ * SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param daddr Glob expression for destination address. This
+ * function will duplicate the string. If this is NULL then clear the
+ * existing address.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_daddr(seaudit_filter_t * filter, const char *daddr);
+
+/**
+ * Return the current destination address for a filter. Treat this
+ * string as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Glob expression for address, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_daddr(const seaudit_filter_t * filter);
+
+/**
+ * Set the destination port criterion. A message is accepted if its
+ * destination port (dport) matches this port. Note that if
+ * seaudit_filter_set_anyport() is also set, then the message must
+ * match both anyport and dport for it to be accepted (assuming that
+ * the match is set to SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param dport Destination port criterion. If this is zero or
+ * negative then clear the existing port.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_dport(seaudit_filter_t * filter, const int dport);
+
+/**
+ * Return the current destination port for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current port criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_dport(const seaudit_filter_t * filter);
+
+/**
+ * Set the port criterion. A message is accepted if its port matches
+ * this port value exactly. Note that if seaudit_filter_set_anyport()
+ * is also set, then the message must match both anyport and port for
+ * it to be accepted (assuming that the match is set to
+ * SEAUDIT_FILTER_MATCH_ALL).
+ *
+ * @param filter Filter to modify.
+ * @param port Port criterion. If this is zero or negative then clear
+ * the existing port.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_port(seaudit_filter_t * filter, const int port);
+
+/**
+ * Return the current port for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current port criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_port(const seaudit_filter_t * filter);
+
+/**
+ * Set the network interface criterion. A message is accepted if its
+ * interface matches exactly with this string.
+ *
+ * @param filter Filter to modify.
+ * @param netif Network interface criterion. This function will
+ * duplicate the string. If this is NULL then clear the existing
+ * criterion.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_netif(seaudit_filter_t * filter, const char *netif);
+
+/**
+ * Return the current network interface for a filter. Treat this
+ * string as const.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return String for netif, or NULL if none set.
+ */
+ extern const char *seaudit_filter_get_netif(const seaudit_filter_t * filter);
+
+/**
+ * Set the key criterion. A message is accepted if its IPC key
+ * matches exactly with this value.
+ *
+ * @param filter Filter to modify.
+ * @param key Key criterion. If this is zero or negative then clear
+ * the existing key.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_key(seaudit_filter_t * filter, const int key);
+
+/**
+ * Return the current key for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current key criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_key(const seaudit_filter_t * filter);
+
+/**
+ * Set the capability criterion. A message is accepted if its
+ * capability matches exactly with this value.
+ *
+ * @param filter Filter to modify.
+ * @param cap Capability criterion. If this is zero or negative then
+ * clear the existing capability.
+ *
+ * @return Always 0.
+ */
+ extern int seaudit_filter_set_cap(seaudit_filter_t * filter, const int cap);
+
+/**
+ * Return the current capability for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Current capability criterion, or 0 if none set.
+ */
+ extern int seaudit_filter_get_cap(const seaudit_filter_t * filter);
+
+/**
+ * Set the type of AVC criterion. A message is accepted if it matches
+ * this value exactly. If the message type is not SEAUDIT_AVC_UNKNOWN
+ * and the message is not an AVC then it will be rejected.
+ *
+ * @param filter Filter to modify.
+ * @param message_type One of SEAUDIT_AVC_DENIED, SEAUDIT_AVC_GRANTED,
+ * SEAUDIT_AVC_UNKNOWN. If SEAUDIT_AVC_UNKNOWN then unset this
+ * criterion.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_message_type(seaudit_filter_t * filter, const seaudit_avc_message_type_e message_type);
+
+/**
+ * Return the current message type for a filter.
+ *
+ * @param filter Filter to get value.
+ *
+ * @return Type of AVC message to filter, or SEAUDIT_AVC_UNKNOWN if
+ * none set.
+ */
+ extern seaudit_avc_message_type_e seaudit_filter_get_message_type(const seaudit_filter_t * filter);
+
+/**
+ * Set the date/time criterion. A message is accepted if its
+ * date/time falls within the allowable range.
+ *
+ * @param filter Filter to modify.
+ * @param start Starting time. This structure will be duplicated. If
+ * NULL, then do not filter by dates.
+ * @param end Ending time. This structure will be duplicated. It
+ * will be ignored (and hence may be NULL) if date_match is not
+ * SEAUDIT_FILTER_DATE_MATCH_BETWEEN.
+ * @param date_match How to match dates, either ones falling before
+ * start, ones falling after start, or ones between start and end.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_filter_set_date(seaudit_filter_t * filter, const struct tm *start, const struct tm *end,
+ seaudit_filter_date_match_e match);
+
+/**
+ * Return the current date/time for a filter. Note that if no
+ * date/time has been set then both reference pointers will be set to
+ * NULL (match will be set to an invalid value).
+ *
+ * @param filter Filter to get value.
+ * @param start Pointer to location to store starting time. Do not
+ * free() or otherwise modify this pointer.
+ * @param end Pointer to location to store ending time. Do not free()
+ * or otherwise modify this pointer. If match is not
+ * SEAUDIT_FILTER_DATE_MATCH_BETWEEN then the contents of this
+ * structure are invalid.
+ * @param date_match Pointer to location to set date matching option.
+ */
+ extern void seaudit_filter_get_date(const seaudit_filter_t * filter, const struct tm **start, const struct tm **end,
+ seaudit_filter_date_match_e * match);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/load_message.h b/libseaudit/include/seaudit/load_message.h
new file mode 100644
index 0000000..52cedd6
--- /dev/null
+++ b/libseaudit/include/seaudit/load_message.h
@@ -0,0 +1,41 @@
+/**
+ * @file
+ * Public interface for a single loaded policy log message. This is
+ * a subclass of seaudit_message; it has no publicly accessible
+ * functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_LOAD_MESSAGE_H
+#define SEAUDIT_LOAD_MESSAGE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct seaudit_load_message seaudit_load_message_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/log.h b/libseaudit/include/seaudit/log.h
new file mode 100644
index 0000000..b94a2a1
--- /dev/null
+++ b/libseaudit/include/seaudit/log.h
@@ -0,0 +1,162 @@
+/**
+ * @file
+ *
+ * Public interface for the main libseaudit object, seaudit_log.
+ * Note that there is no public way to get at the messages stored
+ * within a model. For that, the caller must create a seaudit_model
+ * and then access messages through the model.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_LOG_H
+#define SEAUDIT_LOG_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdarg.h>
+#include <apol/vector.h>
+
+ typedef struct seaudit_log seaudit_log_t;
+ typedef void (*seaudit_handle_fn_t) (void *arg, const seaudit_log_t * log, int level, const char *fmt, va_list va_args);
+
+/**
+ * Define the types of logs that this library can parse.
+ */
+ typedef enum seaudit_log_type
+ {
+ SEAUDIT_LOG_TYPE_INVALID = 0,
+ SEAUDIT_LOG_TYPE_SYSLOG,
+ SEAUDIT_LOG_TYPE_AUDITD
+ } seaudit_log_type_e;
+
+/**
+ * Allocate and initialize a new seaudit log structure. This
+ * structure holds log messages from one or more files; call
+ * seaudit_log_parse() to actually add messages to this log.
+ *
+ * @param fn Function to be called by the error handler. If NULL
+ * then write messages to standard error.
+ * @param callback_arg Argument for the callback.
+ *
+ * @return A newly allocated and initialized seaudit log structure or
+ * NULL on error; if the call fails, errno will be set. The caller is
+ * responsible for calling seaudit_log_destroy() to free memory used
+ * by this structure.
+ */
+ extern seaudit_log_t *seaudit_log_create(seaudit_handle_fn_t fn, void *callback_arg);
+
+/**
+ * Free all memory used by an seaudit log structure and set it to
+ * NULL.
+ *
+ * @param log Reference pointer to the log structure to destroy. This
+ * pointer will be set to NULL. (If already NULL, function is a
+ * no-op.)
+ */
+ extern void seaudit_log_destroy(seaudit_log_t ** log);
+
+/**
+ * Remove all messages from the log. The next time the model(s) that
+ * are watching this log are accessed, they will be refreshed. Note
+ * that any existing pointers to messages within this log will become
+ * invalid. (This function does not actually delete the log file from
+ * disk; it just removes them from memory.)
+ *
+ * @param log Log to clear.
+ */
+ extern void seaudit_log_clear(seaudit_log_t * log);
+
+/**
+ * Return a vector of strings corresponding to all users found within
+ * the log file. The vector will be sorted alphabetically.
+ *
+ * @param log Log file to access.
+ *
+ * @return Vector of sorted users, or NULL upon error. The caller
+ * must call apol_vector_destroy() upon the return value.
+ */
+ apol_vector_t *seaudit_log_get_users(const seaudit_log_t * log);
+
+/**
+ * Return a vector of strings corresponding to all roles found within
+ * the log file. The vector will be sorted alphabetically.
+ *
+ * @param log Log file to access.
+ *
+ * @return Vector of sorted roles, or NULL upon error. The caller
+ * must call apol_vector_destroy() upon the return value.
+ */
+ apol_vector_t *seaudit_log_get_roles(const seaudit_log_t * log);
+
+/**
+ * Return a vector of strings corresponding to all types found within
+ * the log file. The vector will be sorted alphabetically.
+ *
+ * @param log Log file to access.
+ *
+ * @return Vector of sorted types, or NULL upon error. The caller
+ * must call apol_vector_destroy() upon the return value.
+ */
+ apol_vector_t *seaudit_log_get_types(const seaudit_log_t * log);
+
+/**
+ * Return a vector of strings corresponding to all mls levels found within
+ * the log file. The vector will be sorted alphabetically.
+ *
+ * @param log Log file to access.
+ *
+ * @return Vector of sorted types, or NULL upon error. The caller
+ * must call apol_vector_destroy() upon the return value.
+ */
+ apol_vector_t *seaudit_log_get_mls_lvl(const seaudit_log_t * log);
+
+/**
+ * Return a vector of strings corresponding to all mls clearance found within
+ * the log file. The vector will be sorted alphabetically.
+ *
+ * @param log Log file to access.
+ *
+ * @return Vector of sorted types, or NULL upon error. The caller
+ * must call apol_vector_destroy() upon the return value.
+ */
+ apol_vector_t *seaudit_log_get_mls_clr(const seaudit_log_t * log);
+
+/**
+ * Return a vector of strings corresponding to all object classes
+ * found within the log file. The vector will be sorted
+ * alphabetically.
+ *
+ * @param log Log file to access.
+ *
+ * @return Vector of sorted classes, or NULL upon error. The caller
+ * must call apol_vector_destroy() upon the return value.
+ */
+ apol_vector_t *seaudit_log_get_classes(const seaudit_log_t * log);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/message.h b/libseaudit/include/seaudit/message.h
new file mode 100644
index 0000000..2266ee8
--- /dev/null
+++ b/libseaudit/include/seaudit/message.h
@@ -0,0 +1,133 @@
+/**
+ * @file
+ * Public interface for a single seaudit log message. Note that this
+ * is an abstract class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_MESSAGE_H
+#define SEAUDIT_MESSAGE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <time.h>
+
+ typedef struct seaudit_message seaudit_message_t;
+
+/**
+ * This enum defines the different types of audit messages this
+ * library will handle. Message types are put in alphabetical order
+ * to make msg_field_compare() in sort.c easier.
+ */
+ typedef enum seaudit_message_type
+ {
+ SEAUDIT_MESSAGE_TYPE_INVALID = 0,
+ /** BOOL is the message that results when changing
+ booleans in a conditional policy. */
+ SEAUDIT_MESSAGE_TYPE_BOOL,
+ /** AVC is a standard 'allowed' or 'denied' type
+ message. */
+ SEAUDIT_MESSAGE_TYPE_AVC,
+ /** LOAD is the message that results when a policy is
+ loaded into the system. */
+ SEAUDIT_MESSAGE_TYPE_LOAD
+ } seaudit_message_type_e;
+
+/**
+ * Get a pointer to a message's specific data. This returns a void
+ * pointer; the caller must cast it to one of seaudit_avc_message_t,
+ * seaudit_bool_message_t, or seaudit_load_message_t. Use the
+ * returned value from the second parameter to determine which type
+ * this message really is.
+ *
+ * @param msg Message from which to get data.
+ * @param type Reference to the message specific type.
+ *
+ * @return Pointer to message's specific type, or NULL upon error.
+ */
+ extern void *seaudit_message_get_data(const seaudit_message_t * msg, seaudit_message_type_e * type);
+
+/**
+ * Return the time that this audit message was generated.
+ *
+ * @param msg Message from which to get its time.
+ *
+ * @return Time of the message. Treat the contents of this struct as
+ * const.
+ *
+ * @see localtime(3)
+ */
+ extern const struct tm *seaudit_message_get_time(const seaudit_message_t * msg);
+
+/**
+ * Return the name of the host that generated this audit message.
+ *
+ * @param msg Message from which to get its time.
+ *
+ * @return Host of the message. Do not modify this string.
+ */
+ extern const char *seaudit_message_get_host(const seaudit_message_t * msg);
+
+/**
+ * Given a message, allocate and return a string that approximates the
+ * message as it had appeared within the original log file.
+ *
+ * @param msg Message to convert.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+ extern char *seaudit_message_to_string(const seaudit_message_t * msg);
+
+/**
+ * Given a message, allocate and return a string, formatted in HTML,
+ * that approximates the message as it had appeared within the
+ * original log file.
+ *
+ * @param msg Message to convert.
+ *
+ * @return HTML String representation for message, or NULL upon error.
+ * The caller is responsible for free()ing the string afterwards.
+ */
+ extern char *seaudit_message_to_string_html(const seaudit_message_t * msg);
+
+/**
+ * Given a message, allocate and return a string that gives
+ * miscellaneous (i.e., uncategorized) information about the message.
+ * To get the more important values you will need to use more specific
+ * accessor methods.
+ *
+ * @param msg Message from which to get miscellaneous information.
+ *
+ * @return Miscellaneous message string representation, or NULL upon
+ * error. The caller is responsible for free()ing the string
+ * afterwards.
+ */
+ extern char *seaudit_message_to_misc_string(const seaudit_message_t * msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/model.h b/libseaudit/include/seaudit/model.h
new file mode 100644
index 0000000..e5daacf
--- /dev/null
+++ b/libseaudit/include/seaudit/model.h
@@ -0,0 +1,362 @@
+/**
+ * @file
+ *
+ * Public interface to a seaudit_model. This represents a subset of
+ * log messages from one or more seaudit_log, where the subset is
+ * defined by a finite set of seaudit_filter and sorted by some
+ * criterion or criteria.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_MODEL_H
+#define SEAUDIT_MODEL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "filter.h"
+#include "log.h"
+#include "message.h"
+#include "sort.h"
+
+#include <stdlib.h>
+
+ typedef struct seaudit_model seaudit_model_t;
+
+/**
+ * Create a seaudit_model based upon the messages from some particular
+ * seaudit_log. The model will be initialized with the default filter
+ * (i.e., accept all of the messages from the log).
+ *
+ * @param name Name for the model; the string will be duplicated. If
+ * NULL then the model will be assigned a non-unique default name.
+ * @param log Initial log for this model to watch. If NULL then do
+ * not watch any log files.
+ *
+ * @return An initialized model, or NULL upon error. The caller must
+ * call seaudit_model_destroy() afterwards.
+ */
+ extern seaudit_model_t *seaudit_model_create(const char *name, seaudit_log_t * log);
+
+/**
+ * Create a new seaudit_model object, initialized with the data from
+ * an existing model. This will do a deep copy of the original model.
+ * The new model will be watch the same logs that the original model
+ * was watching.
+ *
+ * @param model Model to clone.
+ *
+ * @return A cloned model, or NULL upon error. The caller must call
+ * seaudit_model_destroy() afterwards.
+ */
+ extern seaudit_model_t *seaudit_model_create_from_model(const seaudit_model_t * model);
+
+/**
+ * Create and return a model initialized from the contents of a XML
+ * configuration file. This will also load filters into the model.
+ * The model will not be associated with any logs; for that call
+ * seaudit_model_append_log().
+ *
+ * @param filename File containing model data.
+ *
+ * @return An initialized model, or NULL upon error. The caller must
+ * call seaudit_model_destroy() afterwards.
+ *
+ * @see seaudit_model_save_to_file()
+ */
+ extern seaudit_model_t *seaudit_model_create_from_file(const char *filename);
+
+/**
+ * Destroy the referenced seadit_model object.
+ *
+ * @param model Model to destroy. The pointer will be set to NULL
+ * afterwards. (If pointer is already NULL then do nothing.)
+ */
+ extern void seaudit_model_destroy(seaudit_model_t ** model);
+
+/**
+ * Save to disk, in XML format, the given model's values. This
+ * includes the filters contained within the model as well. Note that
+ * this does not save the messages within the model nor the associated
+ * logs.
+ *
+ * @param model Model to save.
+ * @param filename Name of the file to write. If the file already
+ * exists it will be overwritten.
+ *
+ * @return 0 on success, < 0 on error.
+ *
+ * @see seaudit_model_create_from_file()
+ */
+ extern int seaudit_model_save_to_file(const seaudit_model_t * model, const char *filename);
+
+/**
+ * Get the name of this model.
+ *
+ * @param model Model whose name to get.
+ *
+ * @return Name of the model, or NULL upon error. Do not modify this
+ * string.
+ */
+ extern const char *seaudit_model_get_name(const seaudit_model_t * model);
+
+/**
+ * Set the name of this model, overwriting any previous name.
+ *
+ * @param model Model whose name to set.
+ * @param name New name for the model; the string will be duplicated.
+ * If NULL then the model will be assigned a non-unique default name.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_set_name(seaudit_model_t * model, const char *name);
+
+/**
+ * Have the given model start watching the given log file, in addition
+ * to any other log files the model was watching.
+ *
+ * @param model Model to modify.
+ * @param log Additional log file to watch.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_append_log(seaudit_model_t * model, seaudit_log_t * log);
+
+/**
+ * Append a filter to a model. The next time the model's messages are
+ * retrieved only those messages that match this filter will be
+ * returned. Multiple filters may be applied to a model. Upon
+ * success, the model takes ownership of the filter.
+ *
+ * @param model Model to modify.
+ * @param filter Additional filter to be applied.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_append_filter(seaudit_model_t * model, seaudit_filter_t * filter);
+
+/**
+ * Get the list of filters for a model. Whenever a filter is modified
+ * the model will be recomputed. Note: to remove a filter from the
+ * model use seaudit_model_remove_filter().
+ *
+ * @param model Model containing filters.
+ *
+ * @return Vector of seaudit_filter objects, or NULL upon error. Note
+ * that the vector my be empty. Do not destroy or otherwise modify
+ * this vector. (It is safe to manipulate the elements within the
+ * vector.)
+ */
+ extern const apol_vector_t *seaudit_model_get_filters(const seaudit_model_t * model);
+
+/**
+ * Remove a filter from a model. The given parameter must match one
+ * of the filters stored within the model; call
+ * seaudit_model_get_filters() to get a list of the model's filters.
+ *
+ * @param model Model to modify.
+ * @param filter Filter to remove. Upon success the pointer becomes
+ * invalid.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_remove_filter(seaudit_model_t * model, seaudit_filter_t * filter);
+
+/**
+ * Set a model to accept a message if all filters are met (default
+ * behavior) or if any filter is met. Note that is independent from
+ * the setting given to seaudit_model_set_filter_visible().
+ *
+ * @param model Model to modify.
+ * @param match Matching behavior if model has multiple filters.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_set_filter_match(seaudit_model_t * model, seaudit_filter_match_e match);
+
+/**
+ * Get the current filter match value for a model.
+ *
+ * @param model Model containing filter match value.
+ *
+ * @return One of SEAUDIT_FILTER_MATCH_ALL or SEAUDIT_FILTER_MATCH_ANY.
+ */
+ extern seaudit_filter_match_e seaudit_model_get_filter_match(const seaudit_model_t * model);
+
+/**
+ * Set a model to either show (default behavior) or hide messages
+ * accepted by the filters. Note that is independent from the setting
+ * given to seaudit_model_set_filter_match().
+ *
+ * @param model Model to modify.
+ * @param visible Messages to show if model has any filters.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_set_filter_visible(seaudit_model_t * model, seaudit_filter_visible_e visible);
+
+/**
+ * Get the current filter visibility value for a model.
+ *
+ * @param model Model containing filter visibility value.
+ *
+ * @return One of SEAUDIT_FILTER_VISIBLE_SHOW or
+ * SEAUDIT_FILTER_VISIBLE_HIDE.
+ */
+ extern seaudit_filter_visible_e seaudit_model_get_filter_visible(const seaudit_model_t * model);
+
+/**
+ * Append a sort criterion to a model. The next time the model's
+ * messages are retrieved they will be sorted by this criterion. If
+ * the model already has sort criteria, they will have a higher
+ * priority than this new criterion. Upon success, the model takes
+ * ownership of the sort object
+ *
+ * @param model Model to modify.
+ * @param sort Additional sort criterion.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_append_sort(seaudit_model_t * model, seaudit_sort_t * sort);
+
+/**
+ * Remove all sort criteria from this model. The next time the
+ * model's messages are retrieved they will be in the same order as
+ * provided by the model's log(s).
+ *
+ * @param model Model to modify.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_model_clear_sorts(seaudit_model_t * model);
+
+/**
+ * Return a value indicating if this model has changed since the last
+ * time seaudit_model_get_messages() was called. Note that upon a
+ * non-zero return value, the vector returned by
+ * seaudit_model_get_messages() might contain the same messages. For
+ * example, the user could have removed all sorts but then re-inserted
+ * them in the same order.
+ *
+ * @param model Model to check.
+ *
+ * @return 0 if the model is unchanged, non-zero if it may have
+ * changed.
+ */
+ extern int seaudit_model_is_changed(const seaudit_model_t * model);
+
+/**
+ * Return a sorted list of messages associated with this model. This
+ * will cause the model to recalculate, as necessary, all messages
+ * according to its filters and then sort them.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model containing messages.
+ *
+ * @return A newly allocated vector of seaudit_message_t, pre-filtered
+ * and pre-sorted, or NULL upon error. The caller is responsible for
+ * calling apol_vector_destroy() upon this value.
+ */
+ extern apol_vector_t *seaudit_model_get_messages(const seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Return a sorted list of malformed messages associated with this
+ * model. This is the union of all malformed messages from the
+ * model's logs. This will cause the model to recalculate, as
+ * necessary, all messages according to its filters.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model containing malformed messages.
+ *
+ * @return A newly allocated vector of strings, or NULL upon error.
+ * Treat the contents of the vector as const char *. The caller is
+ * responsible for calling apol_vector_destroy() upon this value.
+ */
+ extern apol_vector_t *seaudit_model_get_malformed_messages(const seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Hide a message from a model such that the next time
+ * seaudit_model_get_messages() is called, the given message will not
+ * be returned within the vector.
+ *
+ * @param model Model containing message to hide.
+ * @param message Message to be marked hidden. If NULL, then do
+ * nothing. It is safe to make duplicate calls to this function with
+ * the same message.
+ */
+ extern void seaudit_model_hide_message(seaudit_model_t * model, const seaudit_message_t * message);
+
+/**
+ * Return the number of avc allow messages currently within the model.
+ * This will cause the model to recalculate, as necessary, all
+ * messages according to its filters.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model to get statistics.
+ *
+ * @return Number of allow messages in the model. This could be zero.
+ */
+ extern size_t seaudit_model_get_num_allows(const seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Return the number of avc deny messages currently within the model.
+ * This will cause the model to recalculate, as necessary, all
+ * messages according to its filters.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model to get statistics.
+ *
+ * @return Number of deny messages in the model. This could be zero.
+ */
+ extern size_t seaudit_model_get_num_denies(const seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Return the number of boolean change messages currently within the
+ * model. This will cause the model to recalculate, as necessary, all
+ * messages according to its filters.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model to get statistics.
+ *
+ * @return Number of boolean messages in the model. This could be
+ * zero.
+ */
+ extern size_t seaudit_model_get_num_bools(const seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Return the number of load messages currently within the model.
+ * This will cause the model to recalculate, as necessary, all
+ * messages according to its filters.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model to get statistics.
+ *
+ * @return Number of load messages in the model. This could be zero.
+ */
+ extern size_t seaudit_model_get_num_loads(const seaudit_log_t * log, seaudit_model_t * model);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/parse.h b/libseaudit/include/seaudit/parse.h
new file mode 100644
index 0000000..385f855
--- /dev/null
+++ b/libseaudit/include/seaudit/parse.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Public interface for parsing an audit log.
+ *
+ * @author Meggan Whalen mwhalen@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_PARSE_H
+#define SEAUDIT_PARSE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "log.h"
+#include <stdio.h>
+
+/**
+ * Parse the file specified by syslog and put all selinux audit
+ * messages into the log. It is assumed that log will be created
+ * before this function. If the log already has messages, new
+ * messages will be appended to it. Afterwards all models watching
+ * this log will be notified of the changes.
+ *
+ * @param log Audit log to which append messages.
+ * @param syslog Handler to an opened file containing audit messages.
+ *
+ * @return 0 on success, > 0 on warnings, < 0 on error and errno will
+ * be set.
+ */
+ extern int seaudit_log_parse(seaudit_log_t * log, FILE * syslog);
+
+/**
+ * Parse a string buffer representing a syslog (or just lines from it)
+ * and put all selinux audit messages into the log. It is assumed
+ * that log will be created before this function. If the log already
+ * has messages, new messages will be appended to it. Afterwards all
+ * models watching this log will be notified of the changes.
+ *
+ * @param log Audit log to which append messages.
+ * @param buffer Buffer containing SELinux audit messages.
+ * @param bufsize Number of bytes in the buffer.
+ *
+ * @return 0 on success, > 0 on warnings, < 0 on error and errno will
+ * be set.
+ */
+ extern int seaudit_log_parse_buffer(seaudit_log_t * log, const char *buffer, const size_t bufsize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/report.h b/libseaudit/include/seaudit/report.h
new file mode 100644
index 0000000..3df4b43
--- /dev/null
+++ b/libseaudit/include/seaudit/report.h
@@ -0,0 +1,140 @@
+/**
+ * @file
+ *
+ * This is the interface for processing SELinux audit logs and/or
+ * seaudit views to generate concise reports containing standard
+ * information as well as customized information using seaudit views.
+ * Reports are rendered in either HTML or plain text. Future support
+ * will provide rendering into XML. The HTML report can be formatted
+ * by providing an alternate stylesheet file or by configuring the
+ * default stylesheet.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_REPORT_H
+#define SEAUDIT_REPORT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "model.h"
+
+ typedef struct seaudit_report seaudit_report_t;
+
+ typedef enum seaudit_report_format
+ {
+ SEAUDIT_REPORT_FORMAT_TEXT, SEAUDIT_REPORT_FORMAT_HTML
+ } seaudit_report_format_e;
+
+/**
+ * Allocate and return a new seaudit_report_t for a particular model.
+ * This will not actually write the report to disk; for that call
+ * seaudit_report_write().
+ *
+ * @param model Model containing messages that will be written.
+ *
+ * @return A newly allocated report, or NULL upon error. The caller
+ * must call seaudit_report_destroy() afterwards.
+ */
+ extern seaudit_report_t *seaudit_report_create(seaudit_model_t * model);
+
+/**
+ * Destroy the referenced seaudit_report_t object.
+ *
+ * @param report Report to destroy. The pointer will be set to NULL
+ * afterwards. (If pointer is already NULL then do nothing.)
+ */
+ extern void seaudit_report_destroy(seaudit_report_t ** report);
+
+/**
+ * Write the report with the messages currently stored in the report's
+ * model.
+ *
+ * @param log Error handler.
+ * @param report Report to write.
+ * @param out_file File name for the report. If this is NULL then
+ * write to standard output.
+ *
+ * @return 0 on successful write, < 0 on error.
+ */
+ extern int seaudit_report_write(const seaudit_log_t * log, const seaudit_report_t * report, const char *out_file);
+
+/**
+ * Set the output format of the report. The default format is plain
+ * text.
+ *
+ * @param log Error handler.
+ * @param report Report whose format to set.
+ * @param format Output formate.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_report_set_format(const seaudit_log_t * log, seaudit_report_t * report, seaudit_report_format_e format);
+
+/**
+ * Set the report to use a particular report configuration file.
+ *
+ * @param log Error handler.
+ * @param report Report whose configuration to set.
+ * @param file Name of the configuration report. If NULL then use the
+ * default installed file. (The name will be duplicated by this
+ * function.)
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_report_set_configuration(const seaudit_log_t * log, seaudit_report_t * report, const char *file);
+
+/**
+ * Set the report to use a particular HTML stylesheet file. Note that
+ * this option is ignored if not generating an HTML report.
+ *
+ * @param log Error handler.
+ * @param report Report whose stylesheet to set.
+ * @param file Name of the stylesheet. If NULL then use the default
+ * installed file. (The name will be duplicated by this function.)
+ * @param use_stylesheet If non-zero, then use the stylesheet given by
+ * the parameter 'file'. Otherwise completely disable stylesheets.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_report_set_stylesheet(const seaudit_log_t * log, seaudit_report_t * report, const char *file,
+ const int use_stylesheet);
+
+/**
+ * Set the report to print messages that did not parse cleanly (i.e.,
+ * "malformed messages").
+ *
+ * @param log Error handler.
+ * @param report Report whose malformed messagse to print.
+ * @param do_malformed If non-zero then print malformed messages.
+ * Otherwise do not print them.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+ extern int seaudit_report_set_malformed(const seaudit_log_t * log, seaudit_report_t * report, const int do_malformed);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/sort.h b/libseaudit/include/seaudit/sort.h
new file mode 100644
index 0000000..505ff60
--- /dev/null
+++ b/libseaudit/include/seaudit/sort.h
@@ -0,0 +1,491 @@
+/**
+ * @file
+ *
+ * Public interface to a seaudit_sort. This represents an abstract
+ * object that specifies how to sort messages within a particular
+ * seaudit_model. The caller obtains a specific sort object and
+ * appends it to a model via seaudit_model_append_sort(); the caller
+ * cannot get a "generic" sort object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_SORT_H
+#define SEAUDIT_SORT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct seaudit_sort seaudit_sort_t;
+
+/**
+ * Create a new sort object, initialized with the data from an
+ * existing sort. The new sort will not be attached to any model.
+ *
+ * @param sort Sort to clone.
+ *
+ * @return A cloned sort object, or NULL upon error. The caller is
+ * responsible for calling seaudit_sort_destroy() afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_create_from_sort(const seaudit_sort_t * sort);
+
+/**
+ * Destroy the referenced seaudit_sort object.
+ *
+ * @param sort Sort object to destroy. The pointer will be set to
+ * NULL afterwards. (If pointer is already NULL then do nothing.)
+ */
+ extern void seaudit_sort_destroy(seaudit_sort_t ** sort);
+
+/**
+ * Instruct a model to sort messages by message type: boolean changes,
+ * then avc denies, then avc allows, then policy load messages.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_message_type(const int direction);
+
+/**
+ * Instruct a model to sort messages by chronological order.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_date(const int direction);
+
+/**
+ * Instruct a model to sort messages by host name, alphabetically.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_host(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by permissions,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_permission(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by source context's user,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_source_user(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by source context's role,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_source_role(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by source context's type,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_source_type(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by source context's mls level.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_source_mls_lvl(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by source context's mls clearance.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_source_mls_clr(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by target context's user,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_target_user(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by target context's role,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_target_role(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by target context's type,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_target_type(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by target context's mls level.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_target_mls_lvl(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by target context's mls clearance.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_target_mls_clr(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by object class,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_object_class(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the executable,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_executable(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the command,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_command(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the name, alphabetically.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_name(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the path, alphabetically.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_path(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the device, alphabetically.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_device(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the object's inode.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_inode(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the process ID. Non-AVC
+ * messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_pid(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the port number. Non-AVC
+ * messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_port(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by local address,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_laddr(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the local port number.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_lport(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by foreign address,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_faddr(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the foreign port number.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_fport(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by source address,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_saddr(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the source port number.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_sport(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by destination address,
+ * alphabetically. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_daddr(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the destination port
+ * number. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_dport(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the IPC call's key.
+ * Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_key(const int direction);
+
+/**
+ * Instruct a model to sort AVC messages by the process capability
+ * value. Non-AVC messages will be placed below AVC ones.
+ *
+ * @param direction Direction to sort. Non-negative for ascending,
+ * negative for descending.
+ *
+ * @return Sort object for this criterion, or NULL upon error. The
+ * caller is responsible for calling seaudit_sort_destroy()
+ * afterwards.
+ */
+ extern seaudit_sort_t *seaudit_sort_by_cap(const int direction);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/include/seaudit/util.h b/libseaudit/include/seaudit/util.h
new file mode 100644
index 0000000..dd0c031
--- /dev/null
+++ b/libseaudit/include/seaudit/util.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ * Miscellaneous, uncategorized functions for libseaudit.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_UTIL_H
+#define SEAUDIT_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libseaudit_get_version(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libseaudit/src/Makefile.am b/libseaudit/src/Makefile.am
new file mode 100644
index 0000000..74ccb26
--- /dev/null
+++ b/libseaudit/src/Makefile.am
@@ -0,0 +1,52 @@
+lib_LIBRARIES = libseaudit.a
+
+seauditso_DATA = libseaudit.so.@libseaudit_version@
+seauditsodir = $(libdir)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @APOL_CFLAGS@ @QPOL_CFLAGS@ @XML_CFLAGS@ -I$(srcdir)/../include -fpic
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libseaudit_a_SOURCES = \
+ avc_message.c \
+ bool_message.c \
+ filter.c filter-internal.c filter-internal.h \
+ load_message.c \
+ log.c \
+ message.c \
+ model.c \
+ parse.c \
+ report.c \
+ sort.c \
+ util.c \
+ seaudit_internal.h
+
+libseaudit_a_DEPENDENCIES = $(top_builddir)/libapol/src/libapol.so
+
+libseaudit_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libseaudit_a_SOURCES)))
+LIBSEAUDIT_SONAME = @libseaudit_soname@
+
+dist_noinst_DATA = libseaudit.map
+
+$(seauditso_DATA): $(libseaudit_so_OBJS) libseaudit.map
+ $(CC) -shared -o $@ $(libseaudit_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBSEAUDIT_SONAME),--version-script=$(srcdir)/libseaudit.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so $(top_builddir)/libapol/src/libapol.so $(XML_LIBS) -lselinux
+ $(LN_S) -f $@ @libseaudit_soname@
+ $(LN_S) -f $@ libseaudit.so
+
+libseaudit.so: $(seauditso_DATA)
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+install-data-hook:
+ cd $(DESTDIR)$(seauditsodir) && $(LN_S) -f $(seauditso_DATA) @libseaudit_soname@
+ cd $(DESTDIR)$(seauditsodir) && $(LN_S) -f $(seauditso_DATA) libseaudit.so
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libseaudit.so @libseaudit_soname@ $(seauditso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(seauditsodir)/$(seauditso_DATA) $(DESTDIR)$(seauditsodir)/@libseaudit_soname@ $(DESTDIR)$(seauditsodir)/libseaudit.so
diff --git a/libseaudit/src/avc_message.c b/libseaudit/src/avc_message.c
new file mode 100644
index 0000000..c054a72
--- /dev/null
+++ b/libseaudit/src/avc_message.c
@@ -0,0 +1,630 @@
+/**
+ * @file
+ * Implementation of a single avc log message.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <apol/util.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+seaudit_avc_message_type_e seaudit_avc_message_get_message_type(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return SEAUDIT_AVC_UNKNOWN;
+ }
+ return avc->msg;
+}
+
+long seaudit_avc_message_get_timestamp_nano(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avc->tm_stmp_sec;
+}
+
+const char *seaudit_avc_message_get_source_user(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->suser;
+}
+
+const char *seaudit_avc_message_get_source_role(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->srole;
+}
+
+const char *seaudit_avc_message_get_source_type(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->stype;
+}
+
+const char *seaudit_avc_message_get_source_mls_lvl(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->smls_lvl;
+}
+
+const char *seaudit_avc_message_get_source_mls_clr(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->smls_clr;
+}
+
+const char *seaudit_avc_message_get_target_user(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->tuser;
+}
+
+const char *seaudit_avc_message_get_target_role(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->trole;
+}
+
+const char *seaudit_avc_message_get_target_type(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->ttype;
+}
+
+const char *seaudit_avc_message_get_target_mls_lvl(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->tmls_lvl;
+}
+
+const char *seaudit_avc_message_get_target_mls_clr(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->tmls_clr;
+}
+
+const char *seaudit_avc_message_get_object_class(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->tclass;
+}
+
+const apol_vector_t *seaudit_avc_message_get_perm(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->perms;
+}
+
+const char *seaudit_avc_message_get_exe(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->exe;
+}
+
+const char *seaudit_avc_message_get_comm(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->comm;
+}
+
+const char *seaudit_avc_message_get_name(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->name;
+}
+
+unsigned int seaudit_avc_message_get_pid(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (!avc->is_pid) {
+ return 0;
+ }
+ return avc->pid;
+}
+
+unsigned long seaudit_avc_message_get_inode(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (!avc->is_inode) {
+ return 0;
+ }
+ return avc->inode;
+}
+
+const char *seaudit_avc_message_get_path(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->path;
+}
+
+const char *seaudit_avc_message_get_dev(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->dev;
+}
+
+const char *seaudit_avc_message_get_netif(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->netif;
+}
+
+int seaudit_avc_message_get_port(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avc->port;
+}
+
+const char *seaudit_avc_message_get_laddr(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->laddr;
+}
+
+int seaudit_avc_message_get_lport(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avc->lport;
+}
+
+const char *seaudit_avc_message_get_faddr(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->faddr;
+}
+
+int seaudit_avc_message_get_fport(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avc->fport;
+}
+
+const char *seaudit_avc_message_get_saddr(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->saddr;
+}
+
+int seaudit_avc_message_get_sport(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avc->source;
+}
+
+const char *seaudit_avc_message_get_daddr(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return avc->daddr;
+}
+
+int seaudit_avc_message_get_dport(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return avc->dest;
+}
+
+int seaudit_avc_message_get_key(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!avc->is_key) {
+ return -1;
+ }
+ return avc->key;
+}
+
+int seaudit_avc_message_get_cap(const seaudit_avc_message_t * avc)
+{
+ if (avc == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!avc->is_capability) {
+ return -1;
+ }
+ return avc->capability;
+}
+
+/******************** protected functions below ********************/
+
+seaudit_avc_message_t *avc_message_create(void)
+{
+ seaudit_avc_message_t *avc = calloc(1, sizeof(seaudit_avc_message_t));
+ if (avc == NULL) {
+ return NULL;
+ }
+ if ((avc->perms = apol_vector_create_with_capacity(1, NULL)) == NULL) {
+ int error = errno;
+ avc_message_free(avc);
+ errno = error;
+ return NULL;
+ }
+ return avc;
+}
+
+void avc_message_free(seaudit_avc_message_t * avc)
+{
+ if (avc != NULL) {
+ free(avc->exe);
+ free(avc->comm);
+ free(avc->path);
+ free(avc->dev);
+ free(avc->netif);
+ free(avc->laddr);
+ free(avc->faddr);
+ free(avc->saddr);
+ free(avc->daddr);
+ free(avc->name);
+ free(avc->ipaddr);
+ apol_vector_destroy(&avc->perms);
+ free(avc);
+ }
+}
+
+/**
+ * Build the misc string sans timestamp and serial number.
+ */
+static char *avc_message_get_misc_string(const seaudit_avc_message_t * avc)
+{
+ char *s = NULL;
+ size_t len = 0;
+ if (avc->dev && apol_str_appendf(&s, &len, "dev=%s ", avc->dev) < 0) {
+ return NULL;
+ }
+ if (avc->ipaddr && apol_str_appendf(&s, &len, "ipaddr=%s ", avc->ipaddr) < 0) {
+ return NULL;
+ }
+ if (avc->laddr && apol_str_appendf(&s, &len, "laddr=%s ", avc->laddr) < 0) {
+ return NULL;
+ }
+ if (avc->lport != 0 && apol_str_appendf(&s, &len, "lport=%d ", avc->lport) < 0) {
+ return NULL;
+ }
+ if (avc->faddr && apol_str_appendf(&s, &len, "faddr=%s ", avc->faddr) < 0) {
+ return NULL;
+ }
+ if (avc->fport != 0 && apol_str_appendf(&s, &len, "fport=%d ", avc->fport) < 0) {
+ return NULL;
+ }
+ if (avc->daddr && apol_str_appendf(&s, &len, "daddr=%s ", avc->daddr) < 0) {
+ return NULL;
+ }
+ if (avc->dest != 0 && apol_str_appendf(&s, &len, "dest=%d ", avc->dest) < 0) {
+ return NULL;
+ }
+ if (avc->port != 0 && apol_str_appendf(&s, &len, "port=%d ", avc->port) < 0) {
+ return NULL;
+ }
+ if (avc->saddr && apol_str_appendf(&s, &len, "saddr=%s ", avc->saddr) < 0) {
+ return NULL;
+ }
+ if (avc->source != 0 && apol_str_appendf(&s, &len, "source=%d ", avc->source) < 0) {
+ return NULL;
+ }
+ if (avc->netif && apol_str_appendf(&s, &len, "netif=%s ", avc->netif) < 0) {
+ return NULL;
+ }
+ if (avc->is_key && apol_str_appendf(&s, &len, "key=%d ", avc->key) < 0) {
+ return NULL;
+ }
+ if (avc->is_capability && apol_str_appendf(&s, &len, "capability=%d ", avc->capability) < 0) {
+ return NULL;
+ }
+ if (s == NULL) {
+ return strdup("");
+ }
+ return s;
+}
+
+char *avc_message_to_string(const seaudit_message_t * msg, const char *date)
+{
+ seaudit_avc_message_t *avc = msg->data.avc;
+ const char *host = msg->host;
+ const char *manager = msg->manager;
+ char *s = NULL, *misc_string = NULL, *perm;
+ size_t i, len = 0;
+ if (apol_str_appendf(&s, &len, "%s %s %s: ", date, host, manager) < 0) {
+ return NULL;
+ }
+ if (!(avc->tm_stmp_sec == 0 && avc->tm_stmp_nano == 0 && avc->serial == 0)) {
+ if (apol_str_appendf(&s, &len, "audit(%lu.%03lu:%u): ", avc->tm_stmp_sec, avc->tm_stmp_nano, avc->serial) < 0) {
+ return NULL;
+ }
+ }
+ if (apol_str_appendf(&s, &len,
+ "avc: %s ",
+ (avc->msg == SEAUDIT_AVC_DENIED ? "denied" :
+ avc->msg == SEAUDIT_AVC_GRANTED ? "granted" : "<unknown>")) < 0) {
+ return NULL;
+ }
+
+ if (apol_vector_get_size(avc->perms) > 0) {
+ if (apol_str_append(&s, &len, "{ ") < 0) {
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(avc->perms); i++) {
+ perm = apol_vector_get_element(avc->perms, i);
+ if (apol_str_appendf(&s, &len, "%s ", perm) < 0) {
+ return NULL;
+ }
+ }
+ if (apol_str_append(&s, &len, "} for ") < 0) {
+ return NULL;
+ }
+ }
+ if (avc->is_pid && apol_str_appendf(&s, &len, "pid=%d ", avc->pid) < 0) {
+ return NULL;
+ }
+ if (avc->exe && apol_str_appendf(&s, &len, "exe=%s ", avc->exe) < 0) {
+ return NULL;
+ }
+ if (avc->comm && apol_str_appendf(&s, &len, "comm=%s ", avc->comm) < 0) {
+ return NULL;
+ }
+ if (avc->path && apol_str_appendf(&s, &len, "path=%s ", avc->path) < 0) {
+ return NULL;
+ }
+ if (avc->name && apol_str_appendf(&s, &len, "name=%s ", avc->name) < 0) {
+ return NULL;
+ }
+ if (avc->is_inode && apol_str_appendf(&s, &len, "ino=%lu ", avc->inode) < 0) {
+ return NULL;
+ }
+ if ((misc_string = avc_message_get_misc_string(avc)) == NULL || apol_str_append(&s, &len, misc_string) < 0) {
+ int error = errno;
+ free(misc_string);
+ errno = error;
+ return NULL;
+ }
+ free(misc_string);
+
+ if (strcmp(avc->smls_lvl, avc->smls_clr)) { /* level and clearance are not the same - show both */
+ if (avc->suser && apol_str_appendf(&s, &len, "scontext=%s:%s:%s:%s-%s ", avc->suser, avc->srole, avc->stype, avc->smls_lvl, avc->smls_clr) < 0) {
+ return NULL;
+ }
+ } else if (avc->suser && apol_str_appendf(&s, &len, "scontext=%s:%s:%s:%s ", avc->suser, avc->srole, avc->stype, avc->smls_lvl) < 0) {
+ /* level and clearance are the same - only show one */
+ return NULL;
+ }
+ if (strcmp(avc->tmls_lvl, avc->tmls_clr)) { /* level and clearance are not the same - show both */
+ if (avc->tuser && apol_str_appendf(&s, &len, "tcontext=%s:%s:%s:%s-%s ", avc->tuser, avc->trole, avc->ttype, avc->tmls_lvl, avc->tmls_clr) < 0) {
+ return NULL;
+ }
+ } else if (avc->tuser && apol_str_appendf(&s, &len, "tcontext=%s:%s:%s:%s ", avc->tuser, avc->trole, avc->ttype, avc->tmls_lvl) < 0) {
+ /* level and clearance are the same - only show one */
+ return NULL;
+ }
+ if (avc->tclass && apol_str_appendf(&s, &len, "tclass=%s ", avc->tclass) < 0) {
+ return NULL;
+ }
+ return s;
+}
+
+char *avc_message_to_string_html(const seaudit_message_t * msg, const char *date)
+{
+ seaudit_avc_message_t *avc = msg->data.avc;
+ const char *host = msg->host;
+ const char *manager = msg->manager;
+ char *s = NULL, *misc_string = NULL, *perm;
+ size_t i, len = 0;
+ if (apol_str_appendf(&s, &len,
+ "<font class=\"message_date\">%s</font> "
+ "<font class=\"host_name\">%s</font> " "%s: ", date, host, manager) < 0) {
+ return NULL;
+ }
+ if (!(avc->tm_stmp_sec == 0 && avc->tm_stmp_nano == 0 && avc->serial == 0)) {
+ if (apol_str_appendf(&s, &len,
+ "<font class=\"syscall_timestamp\">audit(%lu.%03lu:%u): </font>",
+ avc->tm_stmp_sec, avc->tm_stmp_nano, avc->serial) < 0) {
+ return NULL;
+ }
+ }
+ if (apol_str_appendf(&s, &len,
+ "avc: %s ",
+ (avc->msg == SEAUDIT_AVC_DENIED ? "<font class=\"avc_deny\">denied</font> " :
+ avc->msg == SEAUDIT_AVC_GRANTED ? "<font class=\"avc_grant\">granted</font>" : "<unknown>")) < 0) {
+ return NULL;
+ }
+
+ if (apol_vector_get_size(avc->perms) > 0) {
+ if (apol_str_append(&s, &len, "{ ") < 0) {
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(avc->perms); i++) {
+ perm = apol_vector_get_element(avc->perms, i);
+ if (apol_str_appendf(&s, &len, "%s ", perm) < 0) {
+ return NULL;
+ }
+ }
+ if (apol_str_append(&s, &len, "} for ") < 0) {
+ return NULL;
+ }
+ }
+ if (avc->is_pid && apol_str_appendf(&s, &len, "pid=%d ", avc->pid) < 0) {
+ return NULL;
+ }
+ if (avc->exe && apol_str_appendf(&s, &len, "<font class=\"exe\">exe=%s</font> ", avc->exe) < 0) {
+ return NULL;
+ }
+ if (avc->comm && apol_str_appendf(&s, &len, "comm=%s ", avc->comm) < 0) {
+ return NULL;
+ }
+ if (avc->path && apol_str_appendf(&s, &len, "path=%s ", avc->path) < 0) {
+ return NULL;
+ }
+ if (avc->name && apol_str_appendf(&s, &len, "name=%s ", avc->name) < 0) {
+ return NULL;
+ }
+ if (avc->is_inode && apol_str_appendf(&s, &len, "ino=%lu ", avc->inode) < 0) {
+ return NULL;
+ }
+ if ((misc_string = avc_message_get_misc_string(avc)) == NULL || apol_str_append(&s, &len, misc_string) < 0) {
+ int error = errno;
+ free(misc_string);
+ errno = error;
+ return NULL;
+ }
+ free(misc_string);
+ if(strcmp(avc->smls_lvl, avc->smls_clr)) { /* level and clearance are not the same - show both */
+ if (avc->suser &&
+ apol_str_appendf(&s, &len, "<font class=\"src_context\">scontext=%s:%s:%s:%s-%s</font> ",
+ avc->suser, avc->srole, avc->stype, avc->smls_lvl, avc->smls_clr) < 0) {
+ return NULL;
+ }
+ } else if (avc->suser && apol_str_appendf(&s, &len, "<font class=\"src_context\">scontext=%s:%s:%s:%s</font> ",
+ avc->suser, avc->srole, avc->stype, avc->smls_lvl) < 0) {
+ /* level and clearance are the same - only show one */
+ return NULL;
+ }
+ if(strcmp(avc->tmls_lvl, avc->tmls_clr)) { /* level and clearance are not the same - show both */
+ if (avc->tuser &&
+ apol_str_appendf(&s, &len, "<font class=\"tgt_context\">tcontext=%s:%s:%s:%s-%s</font> ",
+ avc->tuser, avc->trole, avc->ttype, avc->tmls_lvl, avc->tmls_clr) < 0) {
+ return NULL;
+ }
+ } else if (avc->tuser &&
+ apol_str_appendf(&s, &len, "<font class=\"tgt_context\">tcontext=%s:%s:%s:%s</font> ",
+ avc->tuser, avc->trole, avc->ttype, avc->tmls_lvl) < 0) {
+ /* level and clearance are the same - only show one */
+ return NULL;
+ }
+ if (avc->tclass && apol_str_appendf(&s, &len, "<font class=\"obj_class\">tclass=%s</font> ", avc->tclass) < 0) {
+ return NULL;
+ }
+ if (apol_str_appendf(&s, &len, "<br>") < 0) {
+ return NULL;
+ }
+ return s;
+}
+
+char *avc_message_to_misc_string(const seaudit_avc_message_t * avc)
+{
+ char *s = avc_message_get_misc_string(avc);
+ size_t len;
+ if (s == NULL) {
+ return NULL;
+ }
+ len = strlen(s) + 1;
+ if (!(avc->tm_stmp_sec == 0 && avc->tm_stmp_nano == 0 && avc->serial == 0)) {
+ if (apol_str_appendf(&s, &len, "%stimestamp=%lu.%03lu serial=%u",
+ (len > 1 ? " " : ""), avc->tm_stmp_sec, avc->tm_stmp_nano, avc->serial) < 0) {
+ return NULL;
+ }
+ }
+ return s;
+}
diff --git a/libseaudit/src/bool_message.c b/libseaudit/src/bool_message.c
new file mode 100644
index 0000000..f105cf0
--- /dev/null
+++ b/libseaudit/src/bool_message.c
@@ -0,0 +1,153 @@
+/**
+ * @file
+ * Implementation of a single boolean change log message.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <apol/util.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/******************** protected functions below ********************/
+
+static void seaudit_bool_change_free(void *elem)
+{
+ if (elem != NULL) {
+ seaudit_bool_message_change_t *b = elem;
+ free(b);
+ }
+}
+
+seaudit_bool_message_t *bool_message_create(void)
+{
+ seaudit_bool_message_t *boolm = calloc(1, sizeof(seaudit_bool_message_t));
+ if (boolm == NULL) {
+ return NULL;
+ }
+ if ((boolm->changes = apol_vector_create(seaudit_bool_change_free)) == NULL) {
+ bool_message_free(boolm);
+ return NULL;
+ }
+ return boolm;
+}
+
+int bool_change_append(seaudit_log_t * log, seaudit_bool_message_t * boolm, const char *name, int value)
+{
+ char *s = strdup(name);
+ seaudit_bool_message_change_t *bc = NULL;
+ int error;
+ if (s == NULL || apol_bst_insert_and_get(log->bools, (void **)&s, NULL) < 0) {
+ error = errno;
+ free(s);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ if ((bc = calloc(1, sizeof(*bc))) == NULL || apol_vector_append(boolm->changes, bc) < 0) {
+ error = errno;
+ free(s);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ bc->boolean = s;
+ bc->value = value;
+ return 0;
+}
+
+void bool_message_free(seaudit_bool_message_t * boolm)
+{
+ if (boolm != NULL) {
+ apol_vector_destroy(&boolm->changes);
+ free(boolm);
+ }
+}
+
+char *bool_message_to_string(const seaudit_message_t * msg, const char *date)
+{
+ seaudit_bool_message_t *boolm = msg->data.boolm;
+ const char *host = msg->host;
+ const char *manager = msg->manager;
+ char *s = NULL, *misc_string;
+ size_t len = 0;
+ char *open_brace = "", *close_brace = "";
+ if (apol_vector_get_size(boolm->changes) > 0) {
+ open_brace = "{ ";
+ close_brace = " }";
+ }
+ if (apol_str_appendf(&s, &len, "%s %s %s: security: committed booleans: %s", date, host, manager, open_brace) < 0) {
+ return NULL;
+ }
+ if ((misc_string = bool_message_to_misc_string(boolm)) == NULL ||
+ apol_str_appendf(&s, &len, misc_string) < 0 || apol_str_append(&s, &len, close_brace) < 0) {
+ free(misc_string);
+ return NULL;
+ }
+ free(misc_string);
+ return s;
+}
+
+char *bool_message_to_string_html(const seaudit_message_t * msg, const char *date)
+{
+ seaudit_bool_message_t *boolm = msg->data.boolm;
+ const char *host = msg->host;
+ const char *manager = msg->manager;
+ char *s = NULL, *misc_string;
+ size_t len = 0;
+ char *open_brace = "", *close_brace = "";
+ if (apol_vector_get_size(boolm->changes) > 0) {
+ open_brace = "{ ";
+ close_brace = " }";
+ }
+ if (apol_str_appendf(&s, &len,
+ "<font class=\"message_date\">%s</font> "
+ "<font class=\"host_name\">%s</font> "
+ "%s: security: committed booleans: %s", date, host, manager, open_brace) < 0) {
+ return NULL;
+ }
+ if ((misc_string = bool_message_to_misc_string(boolm)) == NULL ||
+ apol_str_appendf(&s, &len, misc_string) < 0 || apol_str_appendf(&s, &len, "%s%s<br>", s, close_brace) < 0) {
+ free(misc_string);
+ return NULL;
+ }
+ free(misc_string);
+ return s;
+}
+
+char *bool_message_to_misc_string(const seaudit_bool_message_t * boolm)
+{
+ char *s = NULL;
+ size_t len = 0, i;
+ for (i = 0; i < apol_vector_get_size(boolm->changes); i++) {
+ seaudit_bool_message_change_t *bc = apol_vector_get_element(boolm->changes, i);
+ if (apol_str_appendf(&s, &len, "%s%s:%d", (i == 0 ? "" : ", "), bc->boolean, bc->value) < 0) {
+ return NULL;
+ }
+ }
+ if (s == NULL) {
+ return strdup("");
+ }
+ return s;
+}
diff --git a/libseaudit/src/filter-internal.c b/libseaudit/src/filter-internal.c
new file mode 100644
index 0000000..9aa7c86
--- /dev/null
+++ b/libseaudit/src/filter-internal.c
@@ -0,0 +1,1526 @@
+/**
+ * @file
+ * Implementation of seaudit filters private functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+#include "filter-internal.h"
+
+#include <apol/util.h>
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <libxml/uri.h>
+
+/******************** support functions ********************/
+
+static int filter_string_vector_read(apol_vector_t ** v, const xmlChar * ch)
+{
+ char *s;
+ if (*v == NULL && (*v = apol_vector_create_with_capacity(1, free)) == NULL) {
+ return -1;
+ }
+ if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL || apol_vector_append(*v, s) < 0) {
+ free(s);
+ return -1;
+ }
+ return 0;
+}
+
+static int filter_string_read(char **dest, const xmlChar * ch)
+{
+ free(*dest);
+ *dest = NULL;
+ if ((*dest = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+static int filter_ulong_read(unsigned long *dest, const xmlChar * ch)
+{
+ char *s, *endptr;
+ int retval = -1;
+ if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
+ return -1;
+ }
+ *dest = strtoul(s, &endptr, 10);
+ if (*s != '\0' && *endptr == '\0') {
+ retval = 0;
+ }
+ free(s);
+ return retval;
+}
+
+static unsigned int filter_uint_read(unsigned int *dest, const xmlChar * ch)
+{
+ char *s, *endptr;
+ int retval = -1;
+ if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
+ return -1;
+ }
+ *dest = (unsigned int)(strtoul(s, &endptr, 10));
+ if (*s != '\0' && *endptr == '\0') {
+ retval = 0;
+ }
+ free(s);
+ return retval;
+}
+
+static int filter_int_read(int *dest, const xmlChar * ch)
+{
+ char *s, *endptr;
+ int retval = -1;
+ if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
+ return -1;
+ }
+ *dest = (int)(strtol(s, &endptr, 10));
+ if (*s != '\0' && *endptr == '\0') {
+ retval = 0;
+ }
+ free(s);
+ return retval;
+}
+
+static void filter_string_vector_print(const char *criteria_name, apol_vector_t * v, FILE * f, int tabs)
+{
+ int i;
+ size_t j;
+ if (v == NULL) {
+ return;
+ }
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
+ for (j = 0; j < apol_vector_get_size(v); j++) {
+ xmlChar *s = xmlCharStrdup(apol_vector_get_element(v, j));
+ xmlChar *escaped = xmlURIEscapeStr(s, NULL);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%s</item>\n", escaped);
+ free(escaped);
+ free(s);
+ }
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+}
+
+static void filter_string_print(const char *criteria_name, const char *s, FILE * f, int tabs)
+{
+ int i;
+ xmlChar *t, *escaped;
+ if (s == NULL) {
+ return;
+ }
+ t = xmlCharStrdup(s);
+ escaped = xmlURIEscapeStr(t, NULL);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%s</item>\n", escaped);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+ free(escaped);
+ free(t);
+}
+
+static void filter_ulong_print(const char *criteria_name, const unsigned long val, FILE * f, int tabs)
+{
+ int i;
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%lu</item>\n", val);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+}
+
+static void filter_uint_print(const char *criteria_name, const unsigned int val, FILE * f, int tabs)
+{
+ int i;
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%u</item>\n", val);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+}
+
+static void filter_int_print(const char *criteria_name, const int val, FILE * f, int tabs)
+{
+ int i;
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", criteria_name);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%d</item>\n", val);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+}
+
+/******************** filter private functions ********************/
+
+static bool filter_src_user_is_set(const seaudit_filter_t * filter)
+{
+ return filter->src_users != NULL;
+}
+
+static int filter_src_user_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->suser != NULL;
+}
+
+static int filter_src_user_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->src_users, msg->data.avc->suser, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static int filter_src_user_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->src_users, ch);
+}
+
+static void filter_src_user_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->src_users, f, tabs);
+}
+
+static bool filter_src_role_is_set(const seaudit_filter_t * filter)
+{
+ return filter->src_roles != NULL;
+}
+
+static int filter_src_role_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->srole != NULL;
+}
+
+static int filter_src_role_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->src_roles, msg->data.avc->srole, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static int filter_src_role_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->src_roles, ch);
+}
+
+static void filter_src_role_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->src_roles, f, tabs);
+}
+
+static bool filter_src_type_is_set(const seaudit_filter_t * filter)
+{
+ return filter->src_types != NULL;
+}
+
+static int filter_src_type_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->stype != NULL;
+}
+
+static int filter_src_type_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->src_types, ch);
+}
+
+static int filter_src_type_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->src_types, msg->data.avc->stype, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static void filter_src_type_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->src_types, f, tabs);
+}
+
+static bool filter_src_mls_lvl_is_set(const seaudit_filter_t * filter)
+{
+ return filter->src_mls_lvl != NULL;
+}
+
+static int filter_src_mls_lvl_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->smls_lvl != NULL;
+}
+
+static int filter_src_mls_lvl_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->src_mls_lvl, ch);
+}
+
+static int filter_src_mls_lvl_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->src_mls_lvl, msg->data.avc->smls_lvl, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static void filter_src_mls_lvl_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->src_mls_lvl, f, tabs);
+}
+
+static bool filter_src_mls_clr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->src_mls_clr != NULL;
+}
+
+static int filter_src_mls_clr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->smls_clr != NULL;
+}
+
+static int filter_src_mls_clr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->src_mls_clr, ch);
+}
+
+static int filter_src_mls_clr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->src_mls_clr, msg->data.avc->smls_clr, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static void filter_src_mls_clr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->src_mls_clr, f, tabs);
+}
+
+static bool filter_tgt_user_is_set(const seaudit_filter_t * filter)
+{
+ return filter->tgt_users != NULL;
+}
+
+static int filter_tgt_user_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tuser != NULL;
+}
+
+static int filter_tgt_user_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->tgt_users, msg->data.avc->tuser, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static int filter_tgt_user_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->tgt_users, ch);
+}
+
+static void filter_tgt_user_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->tgt_users, f, tabs);
+}
+
+static bool filter_tgt_role_is_set(const seaudit_filter_t * filter)
+{
+ return filter->tgt_roles != NULL;
+}
+
+static int filter_tgt_role_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->trole != NULL;
+}
+
+static int filter_tgt_role_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->tgt_roles, msg->data.avc->trole, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static int filter_tgt_role_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->tgt_roles, ch);
+}
+
+static void filter_tgt_role_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->tgt_roles, f, tabs);
+}
+
+static bool filter_tgt_type_is_set(const seaudit_filter_t * filter)
+{
+ return filter->tgt_types != NULL;
+}
+
+static int filter_tgt_type_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->ttype != NULL;
+}
+
+static int filter_tgt_type_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->tgt_types, msg->data.avc->ttype, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static int filter_tgt_type_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->tgt_types, ch);
+}
+
+static void filter_tgt_type_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->tgt_types, f, tabs);
+}
+
+static bool filter_tgt_mls_lvl_is_set(const seaudit_filter_t * filter)
+{
+ return filter->tgt_mls_lvl != NULL;
+}
+
+static int filter_tgt_mls_lvl_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tmls_lvl != NULL;
+}
+
+static int filter_tgt_mls_lvl_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->tgt_mls_lvl, ch);
+}
+
+static int filter_tgt_mls_lvl_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->tgt_mls_lvl, msg->data.avc->tmls_lvl, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static void filter_tgt_mls_lvl_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->tgt_mls_lvl, f, tabs);
+}
+
+static bool filter_tgt_mls_clr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->tgt_mls_clr != NULL;
+}
+
+static int filter_tgt_mls_clr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tmls_clr != NULL;
+}
+
+static int filter_tgt_mls_clr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->tgt_mls_clr, ch);
+}
+
+static int filter_tgt_mls_clr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->tgt_mls_clr, msg->data.avc->tmls_clr, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static void filter_tgt_mls_clr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->tgt_mls_clr, f, tabs);
+}
+
+
+static bool filter_tgt_class_is_set(const seaudit_filter_t * filter)
+{
+ return filter->tgt_classes != NULL;
+}
+
+static int filter_tgt_class_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tclass != NULL;
+}
+
+static int filter_tgt_class_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ return apol_vector_get_index(filter->tgt_classes, msg->data.avc->tclass, apol_str_strcmp, NULL, &i) == 0;
+}
+
+static int filter_tgt_class_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_vector_read(&filter->tgt_classes, ch);
+}
+
+static void filter_tgt_class_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_vector_print(name, filter->tgt_classes, f, tabs);
+}
+
+static bool filter_perm_is_set(const seaudit_filter_t * filter)
+{
+ return filter->perm != NULL;
+}
+
+static int filter_perm_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->perms != NULL &&
+ apol_vector_get_size(msg->data.avc->perms) >= 1;
+}
+
+static int filter_perm_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(msg->data.avc->perms); i++) {
+ const char *p = apol_vector_get_element(msg->data.avc->perms, i);
+ if (fnmatch(filter->perm, p, 0) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int filter_perm_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->perm, ch);
+}
+
+static void filter_perm_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->perm, f, tabs);
+}
+
+static bool filter_exe_is_set(const seaudit_filter_t * filter)
+{
+ return filter->exe != NULL;
+}
+
+static int filter_exe_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->exe != NULL;
+}
+
+static int filter_exe_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->exe, msg->data.avc->exe, 0) == 0;
+}
+
+static int filter_exe_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->exe, ch);
+}
+
+static void filter_exe_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->exe, f, tabs);
+}
+
+static bool filter_host_is_set(const seaudit_filter_t * filter)
+{
+ return filter->host != NULL;
+}
+
+static int filter_host_support(const seaudit_message_t * msg)
+{
+ return msg->host != NULL;
+}
+
+static int filter_host_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->host, msg->host, 0) == 0;
+}
+
+static int filter_host_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->host, ch);
+}
+
+static void filter_host_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->host, f, tabs);
+}
+
+static bool filter_path_is_set(const seaudit_filter_t * filter)
+{
+ return filter->path != NULL;
+}
+
+static int filter_path_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->path != NULL;
+}
+
+static int filter_path_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->path, msg->data.avc->path, 0) == 0;
+}
+
+static int filter_path_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->path, ch);
+}
+
+static void filter_path_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->path, f, tabs);
+}
+
+static bool filter_inode_is_set(const seaudit_filter_t * filter)
+{
+ return filter->inode != 0;
+}
+
+static int filter_inode_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_inode;
+}
+
+static int filter_inode_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->inode == msg->data.avc->inode;
+}
+
+static int filter_inode_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_ulong_read(&filter->inode, ch);
+}
+
+static void filter_inode_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_ulong_print(name, filter->inode, f, tabs);
+}
+
+static bool filter_pid_is_set(const seaudit_filter_t * filter)
+{
+ return filter->pid != 0;
+}
+
+static int filter_pid_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_pid;
+}
+
+static int filter_pid_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->pid == msg->data.avc->pid;
+}
+
+static int filter_pid_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_uint_read(&filter->pid, ch);
+}
+
+static void filter_pid_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_uint_print(name, filter->pid, f, tabs);
+}
+
+static bool filter_comm_is_set(const seaudit_filter_t * filter)
+{
+ return filter->comm != NULL;
+}
+
+static int filter_comm_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->comm != NULL;
+}
+
+static int filter_comm_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->comm, msg->data.avc->comm, 0) == 0;
+}
+
+static int filter_comm_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->comm, ch);
+}
+
+static void filter_comm_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->comm, f, tabs);
+}
+
+static bool filter_anyaddr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->anyaddr != NULL;
+}
+
+static int filter_anyaddr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && (msg->data.avc->saddr != NULL
+ || msg->data.avc->daddr != NULL
+ || msg->data.avc->faddr != NULL
+ || msg->data.avc->laddr != NULL || msg->data.avc->ipaddr != NULL);
+}
+
+static int filter_anyaddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ if (msg->data.avc->saddr && fnmatch(filter->anyaddr, msg->data.avc->saddr, 0) == 0)
+ return 1;
+ if (msg->data.avc->daddr && fnmatch(filter->anyaddr, msg->data.avc->daddr, 0) == 0)
+ return 1;
+ if (msg->data.avc->faddr && fnmatch(filter->anyaddr, msg->data.avc->faddr, 0) == 0)
+ return 1;
+ if (msg->data.avc->laddr && fnmatch(filter->anyaddr, msg->data.avc->laddr, 0) == 0)
+ return 1;
+ if (msg->data.avc->ipaddr && fnmatch(filter->anyaddr, msg->data.avc->ipaddr, 0) == 0)
+ return 1;
+ return 0;
+}
+
+static int filter_anyaddr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->anyaddr, ch);
+}
+
+static void filter_anyaddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->anyaddr, f, tabs);
+}
+
+static bool filter_anyport_is_set(const seaudit_filter_t * filter)
+{
+ return filter->anyport != 0;
+}
+
+static int filter_anyport_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && (msg->data.avc->port != 0 ||
+ msg->data.avc->source != 0 ||
+ msg->data.avc->dest != 0 ||
+ msg->data.avc->fport != 0 || msg->data.avc->lport != 0);
+}
+
+static int filter_anyport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ if (msg->data.avc->port != 0 && filter->anyport == msg->data.avc->port) {
+ return 1;
+ }
+ if (msg->data.avc->source != 0 && filter->anyport == msg->data.avc->source) {
+ return 1;
+ }
+ if (msg->data.avc->dest != 0 && filter->anyport == msg->data.avc->dest) {
+ return 1;
+ }
+ if (msg->data.avc->fport != 0 && filter->anyport == msg->data.avc->fport) {
+ return 1;
+ }
+ if (msg->data.avc->lport != 0 && filter->anyport == msg->data.avc->lport) {
+ return 1;
+ }
+ return 0;
+}
+
+static int filter_anyport_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->anyport, ch);
+}
+
+static void filter_anyport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->anyport, f, tabs);
+}
+
+static bool filter_laddr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->laddr != NULL;
+}
+
+static int filter_laddr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->laddr != NULL;
+}
+
+static int filter_laddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->laddr, msg->data.avc->laddr, 0) == 0;
+}
+
+static int filter_laddr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->laddr, ch);
+}
+
+static void filter_laddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->laddr, f, tabs);
+}
+
+static bool filter_lport_is_set(const seaudit_filter_t * filter)
+{
+ return filter->lport != 0;
+}
+
+static int filter_lport_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->lport != 0;
+}
+
+static int filter_lport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->lport == msg->data.avc->lport;
+}
+
+static int filter_lport_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->lport, ch);
+}
+
+static void filter_lport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->lport, f, tabs);
+}
+
+static bool filter_faddr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->faddr != NULL;
+}
+
+static int filter_faddr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->faddr != NULL;
+}
+
+static int filter_faddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->faddr, msg->data.avc->faddr, 0) == 0;
+}
+
+static int filter_faddr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->faddr, ch);
+}
+
+static void filter_faddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->faddr, f, tabs);
+}
+
+static bool filter_fport_is_set(const seaudit_filter_t * filter)
+{
+ return filter->fport != 0;
+}
+
+static int filter_fport_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->fport != 0;
+}
+
+static int filter_fport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->fport == msg->data.avc->fport;
+}
+
+static int filter_fport_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->fport, ch);
+}
+
+static void filter_fport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->fport, f, tabs);
+}
+
+static bool filter_saddr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->saddr != NULL;
+}
+
+static int filter_saddr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->saddr != NULL;
+}
+
+static int filter_saddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->saddr, msg->data.avc->saddr, 0) == 0;
+}
+
+static int filter_saddr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->saddr, ch);
+}
+
+static void filter_saddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->saddr, f, tabs);
+}
+
+static bool filter_sport_is_set(const seaudit_filter_t * filter)
+{
+ return filter->sport != 0;
+}
+
+static int filter_sport_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->source != 0;
+}
+
+static int filter_sport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->sport == msg->data.avc->source;
+}
+
+static int filter_sport_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->sport, ch);
+}
+
+static void filter_sport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->sport, f, tabs);
+}
+
+static bool filter_daddr_is_set(const seaudit_filter_t * filter)
+{
+ return filter->daddr != NULL;
+}
+
+static int filter_daddr_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->daddr != NULL;
+}
+
+static int filter_daddr_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return fnmatch(filter->daddr, msg->data.avc->daddr, 0) == 0;
+}
+
+static int filter_daddr_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->daddr, ch);
+}
+
+static void filter_daddr_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->daddr, f, tabs);
+}
+
+static bool filter_dport_is_set(const seaudit_filter_t * filter)
+{
+ return filter->dport != 0;
+}
+
+static int filter_dport_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->dest != 0;
+}
+
+static int filter_dport_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->dport == msg->data.avc->dest;
+}
+
+static int filter_dport_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->dport, ch);
+}
+
+static void filter_dport_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->dport, f, tabs);
+}
+
+static bool filter_port_is_set(const seaudit_filter_t * filter)
+{
+ return filter->port != 0;
+}
+
+static int filter_port_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->port != 0;
+}
+
+static int filter_port_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->port == msg->data.avc->port;
+}
+
+static int filter_port_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->port, ch);
+}
+
+static void filter_port_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->port, f, tabs);
+}
+
+static bool filter_netif_is_set(const seaudit_filter_t * filter)
+{
+ return filter->netif != NULL;
+}
+
+static int filter_netif_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->netif != NULL;
+}
+
+static int filter_netif_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return strcmp(filter->netif, msg->data.avc->netif) == 0;
+}
+
+static int filter_netif_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_string_read(&filter->netif, ch);
+}
+
+static void filter_netif_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_string_print(name, filter->netif, f, tabs);
+}
+
+static bool filter_key_is_set(const seaudit_filter_t * filter)
+{
+ return filter->key != 0;
+}
+
+static int filter_key_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_key;
+}
+
+static int filter_key_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->key == msg->data.avc->key;
+}
+
+static int filter_key_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->key, ch);
+}
+
+static void filter_key_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->key, f, tabs);
+}
+
+static bool filter_cap_is_set(const seaudit_filter_t * filter)
+{
+ return filter->cap != 0;
+}
+
+static int filter_cap_support(const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_capability;
+}
+
+static int filter_cap_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return filter->key == msg->data.avc->capability;
+}
+
+static int filter_cap_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ return filter_int_read(&filter->cap, ch);
+}
+
+static void filter_cap_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ filter_int_print(name, filter->cap, f, tabs);
+}
+
+static bool filter_avc_msg_type_is_set(const seaudit_filter_t * filter)
+{
+ return filter->avc_msg_type != SEAUDIT_AVC_UNKNOWN;
+}
+
+static int filter_avc_msg_type_support(const seaudit_message_t * msg __attribute__ ((unused)))
+{
+ return 1;
+}
+
+static int filter_avc_msg_type_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && filter->avc_msg_type == msg->data.avc->msg;
+}
+
+static int filter_avc_msg_type_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ char *s;
+ if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
+ return -1;
+ }
+ filter->avc_msg_type = atoi(s);
+ free(s);
+ return 0;
+}
+
+static void filter_avc_msg_type_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ int i;
+ if (filter->avc_msg_type == SEAUDIT_AVC_UNKNOWN) {
+ return;
+ }
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", name);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%d</item>\n", filter->avc_msg_type);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+}
+
+static bool filter_date_is_set(const seaudit_filter_t * filter)
+{
+ return filter->start != NULL;
+}
+
+static int filter_date_support(const seaudit_message_t * msg)
+{
+ return msg->date_stamp != NULL;
+}
+
+/**
+ * Given two dates compare them. If both structs have years that are
+ * not zeroes then also compare their years.
+ */
+static int filter_date_comp(const struct tm *t1, const struct tm *t2)
+{
+ /* tm has year, month, day, hour, min, sec */
+ /* check if we should compare the years */
+ int retval;
+ if (t1->tm_year != 0 && t2->tm_year != 0 && (retval = t1->tm_year - t2->tm_year) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_mon - t2->tm_mon) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_mday - t2->tm_mday) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_hour - t2->tm_hour) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_min - t2->tm_min) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_sec - t2->tm_sec) != 0) {
+ return retval;
+ }
+ return 0;
+}
+
+static int filter_date_accept(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ int compval = filter_date_comp(filter->start, msg->date_stamp);
+ if (filter->date_match == SEAUDIT_FILTER_DATE_MATCH_BEFORE) {
+ return compval > 0;
+ } else if (filter->date_match == SEAUDIT_FILTER_DATE_MATCH_AFTER) {
+ return compval < 0;
+ } else {
+ if (compval > 0)
+ return 0;
+ compval = filter_date_comp(msg->date_stamp, filter->end);
+ return compval < 0;
+ }
+}
+
+static int filter_date_read(seaudit_filter_t * filter, const xmlChar * ch)
+{
+ char *s;
+ if ((s = xmlURIUnescapeString((const char *)ch, 0, NULL)) == NULL) {
+ return -1;
+ }
+ if (filter->start == NULL) {
+ if ((filter->start = calloc(1, sizeof(*(filter->start)))) == NULL) {
+ free(s);
+ return -1;
+ }
+ strptime(s, "%a %b %d %T %Y", filter->start);
+ } else if (filter->end == NULL) {
+ if ((filter->end = calloc(1, sizeof(*(filter->end)))) == NULL) {
+ free(s);
+ return -1;
+ }
+ strptime(s, "%a %b %d %T %Y", filter->end);
+ } else {
+ filter->date_match = atoi(s);
+ }
+ free(s);
+ return 0;
+}
+
+static void filter_date_print(const seaudit_filter_t * filter, const char *name, FILE * f, int tabs)
+{
+ int i;
+ xmlChar *s, *escaped;
+ if (filter->start == NULL) {
+ return;
+ }
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<criteria type=\"%s\">\n", name);
+ s = xmlCharStrdup(asctime(filter->start));
+ escaped = xmlURIEscapeStr(s, NULL);
+ for (i = 0; i < tabs + 1; i++) {
+ fprintf(f, "\t");
+ }
+ fprintf(f, "<item>%s</item>\n", escaped);
+ free(s);
+ free(escaped);
+ s = xmlCharStrdup(asctime(filter->end));
+ escaped = xmlURIEscapeStr(s, NULL);
+ for (i = 0; i < tabs + 1; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<item>%s</item>\n", escaped);
+ free(s);
+ free(escaped);
+ for (i = 0; i < tabs + 1; i++)
+ fprintf(f, "\t");
+ fprintf(f, "<item>%d</item>\n", filter->date_match);
+ for (i = 0; i < tabs; i++)
+ fprintf(f, "\t");
+ fprintf(f, "</criteria>\n");
+}
+
+typedef bool(filter_is_set_func) (const seaudit_filter_t * filter);
+typedef int (filter_support_func) (const seaudit_message_t * msg);
+typedef int (filter_accept_func) (const seaudit_filter_t * filter, const seaudit_message_t * msg);
+typedef void (filter_print_func) (const seaudit_filter_t * filter, const char *name, FILE * f, int tabs);
+
+struct filter_criteria_t
+{
+ const char *name;
+ filter_is_set_func *is_set;
+ filter_support_func *support;
+ filter_accept_func *accept;
+ filter_read_func *read;
+ filter_print_func *print;
+};
+
+/**
+ * Filter criteria are actually implemented as entries within this
+ * function pointer table. During filter_is_accepted() each element
+ * of this table is retrieved; if the support functions returns
+ * non-zero then the accept function is called. To add new filter
+ * criteria, implement their support and accept functions and then
+ * append new entries to this table.
+ */
+static const struct filter_criteria_t filter_criteria[] = {
+ {"src_user", filter_src_user_is_set, filter_src_user_support, filter_src_user_accept, filter_src_user_read,
+ filter_src_user_print},
+ {"src_role", filter_src_role_is_set, filter_src_role_support, filter_src_role_accept, filter_src_role_read,
+ filter_src_role_print},
+ {"src_type", filter_src_type_is_set, filter_src_type_support, filter_src_type_accept, filter_src_type_read,
+ filter_src_type_print},
+ {"src_mls_lvl", filter_src_mls_lvl_is_set, filter_src_mls_lvl_support, filter_src_mls_lvl_accept, filter_src_mls_lvl_read,
+ filter_src_mls_lvl_print},
+ {"src_mls_clr", filter_src_mls_clr_is_set, filter_src_mls_clr_support, filter_src_mls_clr_accept, filter_src_mls_clr_read,
+ filter_src_mls_clr_print},
+ {"tgt_user", filter_tgt_user_is_set, filter_tgt_user_support, filter_tgt_user_accept, filter_tgt_user_read,
+ filter_tgt_user_print},
+ {"tgt_role", filter_tgt_role_is_set, filter_tgt_role_support, filter_tgt_role_accept, filter_tgt_role_read,
+ filter_tgt_role_print},
+ {"tgt_type", filter_tgt_type_is_set, filter_tgt_type_support, filter_tgt_type_accept, filter_tgt_type_read,
+ filter_tgt_type_print},
+ {"tgt_mls_lvl", filter_tgt_mls_lvl_is_set, filter_tgt_mls_lvl_support, filter_tgt_mls_lvl_accept, filter_tgt_mls_lvl_read,
+ filter_src_mls_lvl_print},
+ {"tgt_mls_clr", filter_tgt_mls_clr_is_set, filter_tgt_mls_clr_support, filter_tgt_mls_clr_accept, filter_tgt_mls_clr_read,
+ filter_src_mls_clr_print},
+ {"obj_class", filter_tgt_class_is_set, filter_tgt_class_support, filter_tgt_class_accept, filter_tgt_class_read,
+ filter_tgt_class_print},
+ {"perm", filter_perm_is_set, filter_perm_support, filter_perm_accept, filter_perm_read, filter_perm_print},
+ {"exe", filter_exe_is_set, filter_exe_support, filter_exe_accept, filter_exe_read, filter_exe_print},
+ {"host", filter_host_is_set, filter_host_support, filter_host_accept, filter_host_read, filter_host_print},
+ {"path", filter_path_is_set, filter_path_support, filter_path_accept, filter_path_read, filter_path_print},
+ {"inode", filter_inode_is_set, filter_inode_support, filter_inode_accept, filter_inode_read, filter_inode_print},
+ {"pid", filter_pid_is_set, filter_pid_support, filter_pid_accept, filter_pid_read, filter_pid_print},
+ {"comm", filter_comm_is_set, filter_comm_support, filter_comm_accept, filter_comm_read, filter_comm_print},
+ {"ipaddr", filter_anyaddr_is_set, filter_anyaddr_support, filter_anyaddr_accept, filter_anyaddr_read, filter_anyaddr_print},
+ {"anyport", filter_anyport_is_set, filter_anyport_support, filter_anyport_accept, filter_anyport_read,
+ filter_anyport_print},
+ {"laddr", filter_laddr_is_set, filter_laddr_support, filter_laddr_accept, filter_laddr_read, filter_laddr_print},
+ {"lport", filter_lport_is_set, filter_lport_support, filter_lport_accept, filter_lport_read, filter_lport_print},
+ {"faddr", filter_faddr_is_set, filter_faddr_support, filter_faddr_accept, filter_faddr_read, filter_faddr_print},
+ {"fport", filter_fport_is_set, filter_fport_support, filter_fport_accept, filter_fport_read, filter_fport_print},
+ {"saddr", filter_saddr_is_set, filter_saddr_support, filter_saddr_accept, filter_saddr_read, filter_saddr_print},
+ {"sport", filter_sport_is_set, filter_sport_support, filter_sport_accept, filter_sport_read, filter_sport_print},
+ {"daddr", filter_daddr_is_set, filter_daddr_support, filter_daddr_accept, filter_daddr_read, filter_daddr_print},
+ {"dport", filter_dport_is_set, filter_dport_support, filter_dport_accept, filter_dport_read, filter_dport_print},
+ {"port", filter_port_is_set, filter_port_support, filter_port_accept, filter_port_read, filter_port_print},
+ {"netif", filter_netif_is_set, filter_netif_support, filter_netif_accept, filter_netif_read, filter_netif_print},
+ {"key", filter_key_is_set, filter_key_support, filter_key_accept, filter_key_read, filter_key_print},
+ {"cap", filter_cap_is_set, filter_cap_support, filter_cap_accept, filter_cap_read, filter_cap_print},
+ {"msg", filter_avc_msg_type_is_set, filter_avc_msg_type_support, filter_avc_msg_type_accept, filter_avc_msg_type_read,
+ filter_avc_msg_type_print},
+ {"date_time", filter_date_is_set, filter_date_support, filter_date_accept, filter_date_read, filter_date_print}
+};
+
+/******************** protected functions below ********************/
+
+int filter_is_accepted(const seaudit_filter_t * filter, const seaudit_message_t * msg)
+{
+ bool tried_criterion = false;
+ int acceptval;
+ size_t i;
+
+ for (i = 0; i < sizeof(filter_criteria) / sizeof(filter_criteria[0]); i++) {
+ if (filter_criteria[i].is_set(filter)) {
+ tried_criterion = true;
+ if (filter_criteria[i].support(msg)) {
+ acceptval = filter_criteria[i].accept(filter, msg);
+ } else if (filter->strict) {
+ /* if filter is strict, then an
+ unsupported criterion is assumed to
+ not match */
+ acceptval = 0;
+ } else {
+ /* for unstrict filters, unsupported
+ criterion is assumed to be a don't
+ care state */
+ continue;
+ }
+ if (filter->match == SEAUDIT_FILTER_MATCH_ANY && acceptval == 1) {
+ return 1;
+ }
+ if (filter->match == SEAUDIT_FILTER_MATCH_ALL && acceptval == 0) {
+ return 0;
+ }
+ }
+ }
+ if (!tried_criterion) {
+ /* if got here, then the filter had no set criterion */
+ if (filter->strict) {
+ /* a strict empty filter matches nothing */
+ return 0;
+ }
+ return 1;
+ }
+ if (filter->match == SEAUDIT_FILTER_MATCH_ANY) {
+ /* if got here, then no criterion was met */
+ return 0;
+ }
+ /* if got here, then all criteria were met */
+ return 1;
+}
+
+static bool filter_parse_is_valid_tag(const xmlChar * tag)
+{
+ static const char *parse_valid_tags[] = { "item", "criteria", "view", "filter", "desc", NULL };
+ size_t i;
+ for (i = 0; parse_valid_tags[i] != NULL; i++) {
+ if (xmlStrcmp(tag, (xmlChar *) parse_valid_tags[i]) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static filter_read_func *filter_get_read_func(const xmlChar * name)
+{
+ size_t i;
+ for (i = 0; i < sizeof(filter_criteria) / sizeof(filter_criteria[0]); i++) {
+ if (xmlStrcmp(name, (xmlChar *) filter_criteria[i].name) == 0) {
+ return filter_criteria[i].read;
+ }
+ }
+ return NULL;
+}
+
+static void filter_parse_start_element(void *user_data, const xmlChar * name, const xmlChar ** attrs)
+{
+ struct filter_parse_state *state = user_data;
+ size_t i;
+ if (!filter_parse_is_valid_tag(name)) {
+ state->warnings = 1;
+ return;
+ }
+ if (xmlStrcmp(name, (xmlChar *) "view") == 0) {
+ for (i = 0; attrs[i] != NULL && attrs[i + 1] != NULL; i += 2) {
+ if (xmlStrcmp(attrs[i], (xmlChar *) "name") == 0) {
+ free(state->view_name);
+ state->view_name = xmlURIUnescapeString((const char *)attrs[i + 1], 0, NULL);
+ } else if (xmlStrcmp(attrs[i], (xmlChar *) "match") == 0) {
+ if (xmlStrcmp(attrs[i + 1], (xmlChar *) "all") == 0) {
+ state->view_match = SEAUDIT_FILTER_MATCH_ALL;
+ } else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "any") == 0) {
+ state->view_match = SEAUDIT_FILTER_MATCH_ANY;
+ }
+ } else if (xmlStrcmp(attrs[i], (xmlChar *) "show") == 0) {
+ if (xmlStrcmp(attrs[i + 1], (xmlChar *) "true") == 0) {
+ state->view_visible = SEAUDIT_FILTER_VISIBLE_SHOW;
+ } else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "hide") == 0) {
+ state->view_visible = SEAUDIT_FILTER_VISIBLE_HIDE;
+ }
+ }
+ }
+ } else if (xmlStrcmp(name, (xmlChar *) "filter") == 0) {
+ /* create a new filter and set it to be the one that is currently being parsed */
+ char *filter_name = NULL;
+ seaudit_filter_match_e match = SEAUDIT_FILTER_MATCH_ALL;
+ bool strict = false;
+ for (i = 0; attrs[i] != NULL && attrs[i + 1] != NULL; i += 2) {
+ if (xmlStrcmp(attrs[i], (xmlChar *) "name") == 0) {
+ free(filter_name);
+ filter_name = xmlURIUnescapeString((const char *)attrs[i + 1], 0, NULL);
+ } else if (xmlStrcmp(attrs[i], (xmlChar *) "match") == 0) {
+ if (xmlStrcmp(attrs[i + 1], (xmlChar *) "all") == 0) {
+ match = SEAUDIT_FILTER_MATCH_ALL;
+ } else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "any") == 0) {
+ match = SEAUDIT_FILTER_MATCH_ANY;
+ }
+ } else if (xmlStrcmp(attrs[i], (xmlChar *) "strict") == 0) {
+ if (xmlStrcmp(attrs[i + 1], (xmlChar *) "true") == 0) {
+ strict = true;
+ } else if (xmlStrcmp(attrs[i + 1], (xmlChar *) "false") == 0) {
+ strict = false;
+ }
+ }
+ }
+ if ((state->cur_filter = seaudit_filter_create(filter_name)) != NULL) {
+ if (apol_vector_append(state->filters, state->cur_filter) < 0) {
+ seaudit_filter_destroy(&state->cur_filter);
+ } else {
+ seaudit_filter_set_match(state->cur_filter, match);
+ seaudit_filter_set_strict(state->cur_filter, strict);
+ }
+ }
+ free(filter_name);
+ } else if (xmlStrcmp(name, (xmlChar *) "criteria") == 0) {
+ for (i = 0; attrs[i] != NULL && attrs[i + 1] != NULL; i += 2) {
+ if (xmlStrcmp(attrs[i], (xmlChar *) "type") == 0) {
+ state->cur_filter_read = filter_get_read_func(attrs[i + 1]);
+ }
+ }
+ }
+ free(state->cur_string);
+ state->cur_string = NULL;
+}
+
+static void filter_parse_end_element(void *user_data, const xmlChar * name)
+{
+ struct filter_parse_state *state = user_data;
+ char *s;
+ if (!filter_parse_is_valid_tag(name)) {
+ state->warnings = 1;
+ return;
+ }
+ if (xmlStrcmp(name, (xmlChar *) "desc") == 0) {
+ if (state->cur_filter == NULL) {
+ state->warnings = 1;
+ } else {
+ s = xmlURIUnescapeString((const char *)state->cur_string, 0, NULL);
+ seaudit_filter_set_description(state->cur_filter, s);
+ free(s);
+ }
+ } else if (xmlStrcmp(name, (xmlChar *) "item") == 0) {
+ if (state->cur_filter == NULL || state->cur_filter_read == NULL) {
+ state->warnings = 1;
+ } else {
+ state->cur_filter_read(state->cur_filter, state->cur_string);
+ }
+ } else if (xmlStrcmp(name, (xmlChar *) "filter") == 0) {
+ state->cur_filter = NULL;
+ } else if (xmlStrcmp(name, (xmlChar *) "criteria") == 0) {
+ state->cur_filter_read = NULL;
+ }
+ free(state->cur_string);
+ state->cur_string = NULL;
+}
+
+static void filter_parse_characters(void *user_data, const xmlChar * ch, int len)
+{
+ struct filter_parse_state *state = user_data;
+ free(state->cur_string);
+ state->cur_string = xmlStrndup(ch, len);
+}
+
+int filter_parse_xml(struct filter_parse_state *state, const char *filename)
+{
+ xmlSAXHandler handler;
+ int err;
+
+ memset(&handler, 0, sizeof(xmlSAXHandler));
+ handler.startElement = filter_parse_start_element;
+ handler.endElement = filter_parse_end_element;
+ handler.characters = filter_parse_characters;
+ err = xmlSAXUserParseFile(&handler, state, filename);
+ free(state->cur_string);
+ state->cur_string = NULL;
+ if (err) {
+ errno = EIO;
+ return -1;
+ }
+ if (state->warnings) {
+ return 1;
+ }
+ return 0;
+}
+
+void filter_append_to_file(const seaudit_filter_t * filter, FILE * file, int tabs)
+{
+ xmlChar *escaped;
+ xmlChar *str_xml;
+ int i;
+ size_t j;
+
+ if (filter == NULL || file == NULL) {
+ errno = EINVAL;
+ return;
+ }
+
+ if (filter->name == NULL) {
+ str_xml = xmlCharStrdup("Unnamed");
+ } else {
+ str_xml = xmlCharStrdup(filter->name);
+ }
+ escaped = xmlURIEscapeStr(str_xml, NULL);
+ for (i = 0; i < tabs; i++)
+ fprintf(file, "\t");
+ fprintf(file, "<filter name=\"%s\" match=\"%s\" strict=\"%s\">\n", escaped,
+ filter->match == SEAUDIT_FILTER_MATCH_ALL ? "all" : "any", filter->strict ? "true" : "false");
+ free(escaped);
+ free(str_xml);
+
+ if (filter->desc != NULL) {
+ str_xml = xmlCharStrdup(filter->desc);
+ escaped = xmlURIEscapeStr(str_xml, NULL);
+ for (i = 0; i < tabs + 1; i++)
+ fprintf(file, "\t");
+ fprintf(file, "<desc>%s</desc>\n", escaped);
+ free(escaped);
+ free(str_xml);
+ }
+ for (j = 0; j < sizeof(filter_criteria) / sizeof(filter_criteria[0]); j++) {
+ filter_criteria[j].print(filter, filter_criteria[j].name, file, tabs + 1);
+ }
+ for (i = 0; i < tabs; i++)
+ fprintf(file, "\t");
+ fprintf(file, "</filter>\n");
+}
diff --git a/libseaudit/src/filter-internal.h b/libseaudit/src/filter-internal.h
new file mode 100644
index 0000000..abfa908
--- /dev/null
+++ b/libseaudit/src/filter-internal.h
@@ -0,0 +1,109 @@
+/**
+ * @file
+ * Protected interface for seaudit filters.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_FILTER_INTERNAL_H
+#define SEAUDIT_FILTER_INTERNAL_H
+
+#include "seaudit_internal.h"
+
+struct seaudit_filter
+{
+ seaudit_filter_match_e match;
+ char *name;
+ char *desc;
+ bool strict;
+ /** model that is watching this filter */
+ seaudit_model_t *model;
+ /** vector of strings, for source users */
+ apol_vector_t *src_users;
+ /** vector of strings, for source roles */
+ apol_vector_t *src_roles;
+ /** vector of strings, for source types */
+ apol_vector_t *src_types;
+ /** vector of strings, for source mls levels */
+ apol_vector_t *src_mls_lvl;
+ /** vector of strings, for source mls clearance */
+ apol_vector_t *src_mls_clr;
+ /** vector of strings, for target users */
+ apol_vector_t *tgt_users;
+ /** vector of strings, for target roles */
+ apol_vector_t *tgt_roles;
+ /** vector of strings, for target types */
+ apol_vector_t *tgt_types;
+ /** vector of strings, for target mls levels */
+ apol_vector_t *tgt_mls_lvl;
+ /** vector of strings, for target mls clearance */
+ apol_vector_t *tgt_mls_clr;
+ /** vector of strings, for target object classes */
+ apol_vector_t *tgt_classes;
+ /** criteria for permissions, glob expression */
+ char *perm;
+ /** criteria for executable, glob expression */
+ char *exe;
+ /** criteria for host, glob expression */
+ char *host;
+ /** criteria for path, glob expression */
+ char *path;
+ /** inode criterion, as a literal value */
+ unsigned long inode;
+ /** pid criterion, as a literal value */
+ unsigned int pid;
+ /** criterion for command, glob expression */
+ char *comm;
+ /** criterion for IP address, glob expression */
+ char *anyaddr;
+ /** criterion for local address, glob expression */
+ char *laddr;
+ /** criterion for foreign address, glob expression */
+ char *faddr;
+ /** criterion for source address, glob expression */
+ char *saddr;
+ /** criterion for destination address, glob expression */
+ char *daddr;
+ /** criterion for any of the ports, exact match */
+ int anyport;
+ /** criterion for local port, exact match */
+ int lport;
+ /** criterion for foreign port, exact match */
+ int fport;
+ /** criterion for source port, exact match */
+ int sport;
+ /** criterion for destination port, exact match */
+ int dport;
+ /** criterion for just plain port, exact match */
+ int port;
+ /** criterion for netif, exact match */
+ char *netif;
+ /** criterion for IPC key, exact match */
+ int key;
+ /** criterion for capability, exact match */
+ int cap;
+ /** criterion for AVC message type */
+ seaudit_avc_message_type_e avc_msg_type;
+ struct tm *start, *end;
+ seaudit_filter_date_match_e date_match;
+};
+
+#endif
diff --git a/libseaudit/src/filter.c b/libseaudit/src/filter.c
new file mode 100644
index 0000000..298a309
--- /dev/null
+++ b/libseaudit/src/filter.c
@@ -0,0 +1,1124 @@
+/**
+ * @file
+ * Implementation of seaudit filters.
+ *
+ * If adding new filter criteria, make sure you do the following:
+ *
+ * <ol>
+ * <li>add field(s) to seaudit_filter_t</li>
+ * <li>update filter constructor, seaudit_filter_create()</li>
+ * <li>update copy-constructor, seaudit_filter_create_from_filter()</li>
+ * <li>update destructor, seaudit_filter_destroy()</li>
+ * <li>add accessor(s) and modifier(s) as necessary</li>
+ * <li>add a record to filter_criteria table (in filter-internal.c),
+ * implementing the five necessary functions</li>
+ * </ol>
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+#include "filter-internal.h"
+
+#include <apol/util.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+seaudit_filter_t *seaudit_filter_create(const char *name)
+{
+ seaudit_filter_t *s = calloc(1, sizeof(*s));
+ if (s == NULL) {
+ return NULL;
+ }
+ if (name == NULL) {
+ name = "Untitled";
+ }
+ if ((s->name = strdup(name)) == NULL) {
+ int error = errno;
+ seaudit_filter_destroy(&s);
+ errno = error;
+ return NULL;
+ }
+ return s;
+}
+
+seaudit_filter_t *seaudit_filter_create_from_filter(const seaudit_filter_t * filter)
+{
+ seaudit_filter_t *f = NULL;
+ int error = 0;
+ if (filter == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((f = seaudit_filter_create(filter->name)) == NULL || (filter->desc != NULL && (f->desc = strdup(filter->desc)) == NULL)) {
+ error = errno;
+ goto cleanup;
+ }
+ f->strict = filter->strict;
+ if ((filter->src_users != NULL
+ && (f->src_users = apol_vector_create_from_vector(filter->src_users, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->src_roles != NULL
+ && (f->src_roles = apol_vector_create_from_vector(filter->src_roles, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->src_types != NULL
+ && (f->src_types = apol_vector_create_from_vector(filter->src_types, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->src_mls_lvl != NULL
+ && (f->src_mls_lvl = apol_vector_create_from_vector(filter->src_mls_lvl, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->src_mls_clr != NULL
+ && (f->src_mls_clr = apol_vector_create_from_vector(filter->src_mls_clr, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->tgt_users != NULL
+ && (f->tgt_users = apol_vector_create_from_vector(filter->tgt_users, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->tgt_roles != NULL
+ && (f->tgt_roles = apol_vector_create_from_vector(filter->tgt_roles, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->tgt_types != NULL
+ && (f->tgt_types = apol_vector_create_from_vector(filter->tgt_types, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->tgt_mls_lvl != NULL
+ && (f->tgt_mls_lvl = apol_vector_create_from_vector(filter->tgt_mls_lvl, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->tgt_mls_clr != NULL
+ && (f->tgt_mls_clr = apol_vector_create_from_vector(filter->tgt_mls_clr, apol_str_strdup, NULL, free)) == NULL)
+ || (filter->tgt_classes != NULL
+ && (f->tgt_classes = apol_vector_create_from_vector(filter->tgt_classes, apol_str_strdup, NULL, free)) == NULL)) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((filter->perm != NULL && (f->perm = strdup(filter->perm)) == NULL) ||
+ (filter->exe != NULL && (f->exe = strdup(filter->exe)) == NULL) ||
+ (filter->host != NULL && (f->host = strdup(filter->host)) == NULL) ||
+ (filter->path != NULL && (f->path = strdup(filter->path)) == NULL) ||
+ (filter->comm != NULL && (f->comm = strdup(filter->comm)) == NULL) ||
+ (filter->anyaddr != NULL && (f->anyaddr = strdup(filter->anyaddr)) == NULL) ||
+ (filter->netif != NULL && (f->netif = strdup(filter->netif)) == NULL)) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((filter->laddr != NULL && (f->laddr = strdup(filter->laddr)) == NULL) ||
+ (filter->faddr != NULL && (f->faddr = strdup(filter->faddr)) == NULL) ||
+ (filter->saddr != NULL && (f->saddr = strdup(filter->saddr)) == NULL) ||
+ (filter->daddr != NULL && (f->daddr = strdup(filter->daddr)) == NULL)) {
+ error = errno;
+ goto cleanup;
+ }
+ f->match = filter->match;
+ f->inode = filter->inode;
+ f->pid = filter->pid;
+ f->anyport = filter->anyport;
+ f->lport = filter->lport;
+ f->fport = filter->fport;
+ f->sport = filter->sport;
+ f->dport = filter->dport;
+ f->port = filter->port;
+ f->key = filter->key;
+ f->cap = filter->cap;
+ f->avc_msg_type = filter->avc_msg_type;
+ if (filter->start != NULL) {
+ if ((f->start = calloc(1, sizeof(*f->start))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ memcpy(f->start, filter->start, sizeof(*f->start));
+ }
+ if (filter->end != NULL) {
+ if ((f->end = calloc(1, sizeof(*f->end))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ memcpy(f->end, filter->end, sizeof(*f->end));
+ }
+ f->date_match = filter->date_match;
+ f->model = NULL;
+ cleanup:
+ if (error != 0) {
+ seaudit_filter_destroy(&f);
+ errno = error;
+ return NULL;
+ }
+ return f;
+}
+
+/**
+ * Callback invoked when free()ing a vector of filters.
+ *
+ * @param v Filter object to free.
+ */
+static void filter_free(void *v)
+{
+ seaudit_filter_t *f = v;
+ seaudit_filter_destroy(&f);
+}
+
+apol_vector_t *seaudit_filter_create_from_file(const char *filename)
+{
+ struct filter_parse_state state;
+ int retval, error;
+ memset(&state, 0, sizeof(state));
+ if ((state.filters = apol_vector_create(filter_free)) == NULL) {
+ return NULL;
+ }
+ retval = filter_parse_xml(&state, filename);
+ error = errno;
+ free(state.view_name);
+ if (retval < 0) {
+ apol_vector_destroy(&state.filters);
+ errno = error;
+ return NULL;
+ }
+ return state.filters;
+}
+
+void seaudit_filter_destroy(seaudit_filter_t ** filter)
+{
+ if (filter != NULL && *filter != NULL) {
+ free((*filter)->name);
+ free((*filter)->desc);
+ apol_vector_destroy(&(*filter)->src_users);
+ apol_vector_destroy(&(*filter)->src_roles);
+ apol_vector_destroy(&(*filter)->src_types);
+ apol_vector_destroy(&(*filter)->src_mls_lvl);
+ apol_vector_destroy(&(*filter)->src_mls_clr);
+ apol_vector_destroy(&(*filter)->tgt_users);
+ apol_vector_destroy(&(*filter)->tgt_roles);
+ apol_vector_destroy(&(*filter)->tgt_types);
+ apol_vector_destroy(&(*filter)->tgt_mls_lvl);
+ apol_vector_destroy(&(*filter)->tgt_mls_clr);
+ apol_vector_destroy(&(*filter)->tgt_classes);
+ free((*filter)->perm);
+ free((*filter)->exe);
+ free((*filter)->host);
+ free((*filter)->path);
+ free((*filter)->comm);
+ free((*filter)->anyaddr);
+ free((*filter)->laddr);
+ free((*filter)->faddr);
+ free((*filter)->saddr);
+ free((*filter)->daddr);
+ free((*filter)->netif);
+ free((*filter)->start);
+ free((*filter)->end);
+ free(*filter);
+ *filter = NULL;
+ }
+}
+
+int seaudit_filter_set_match(seaudit_filter_t * filter, seaudit_filter_match_e match)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ filter->match = match;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ return 0;
+}
+
+seaudit_filter_match_e seaudit_filter_get_match(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->match;
+}
+
+int seaudit_filter_set_name(seaudit_filter_t * filter, const char *name)
+{
+ char *new_name = NULL;
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (name != filter->name) {
+ if (name != NULL && (new_name = strdup(name)) == NULL) {
+ return -1;
+ }
+ free(filter->name);
+ filter->name = new_name;;
+ }
+ return 0;
+}
+
+const char *seaudit_filter_get_name(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->name;
+}
+
+int seaudit_filter_set_description(seaudit_filter_t * filter, const char *desc)
+{
+ char *new_desc = NULL;
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (desc != filter->desc) {
+ if (desc != NULL && (new_desc = strdup(desc)) == NULL) {
+ return -1;
+ }
+ free(filter->desc);
+ filter->desc = new_desc;
+ }
+ return 0;
+}
+
+const char *seaudit_filter_get_description(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->desc;
+}
+
+int seaudit_filter_set_strict(seaudit_filter_t * filter, bool is_strict)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (filter->strict != is_strict) {
+ filter->strict = is_strict;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ }
+ return 0;
+}
+
+bool seaudit_filter_get_strict(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return false;
+ }
+ return filter->strict;
+}
+
+/**
+ * Helper function to set a criterion's vector, by duping the vector
+ * and its strings. Dupe the vector before destroying the existing
+ * one, in case v is the same as tgt.
+ */
+static int filter_set_vector(seaudit_filter_t * filter, apol_vector_t ** tgt, const apol_vector_t * v)
+{
+ apol_vector_t *new_v = NULL;
+ if (v != NULL) {
+ if ((new_v = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ return -1;
+ }
+ }
+ apol_vector_destroy(tgt);
+ *tgt = new_v;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ return 0;
+}
+
+/**
+ * Helper function to set a criterion string, by dupping the src
+ * string. As a check, if the pointers are already the same then do
+ * nothing.
+ */
+static int filter_set_string(seaudit_filter_t * filter, char **dest, const char *src)
+{
+ if (src != *dest) {
+ char *new_s = NULL;
+ if (src != NULL && (new_s = strdup(src)) == NULL) {
+ return -1;
+ }
+ free(*dest);
+ *dest = new_s;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ }
+ return 0;
+}
+
+static int filter_set_ulong(seaudit_filter_t * filter, unsigned long *dest, const ulong src)
+{
+ if (src != *dest) {
+ *dest = src;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ }
+ return 0;
+}
+
+static int filter_set_uint(seaudit_filter_t * filter, unsigned int *dest, const ulong src)
+{
+ if (src != *dest) {
+ *dest = src;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ }
+ return 0;
+}
+
+static int filter_set_int(seaudit_filter_t * filter, int *dest, const int src)
+{
+ int s = src;
+ if (src <= 0) {
+ s = 0;
+ }
+ if (s != *dest) {
+ *dest = s;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ }
+ return 0;
+}
+
+/******************** public accessors / modifiers ********************/
+
+int seaudit_filter_set_source_user(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->src_users, v);
+}
+
+const apol_vector_t *seaudit_filter_get_source_user(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->src_users;
+}
+
+int seaudit_filter_set_source_role(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->src_roles, v);
+}
+
+const apol_vector_t *seaudit_filter_get_source_role(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->src_roles;
+}
+
+int seaudit_filter_set_source_type(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->src_types, v);
+}
+
+const apol_vector_t *seaudit_filter_get_source_type(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->src_types;
+}
+
+int seaudit_filter_set_source_mls_lvl(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->src_mls_lvl, v);
+}
+
+const apol_vector_t *seaudit_filter_get_source_mls_lvl(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->src_mls_lvl;
+}
+
+int seaudit_filter_set_source_mls_clr(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->src_mls_clr, v);
+}
+
+const apol_vector_t *seaudit_filter_get_source_mls_clr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->src_mls_clr;
+}
+int seaudit_filter_set_target_user(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->tgt_users, v);
+}
+
+const apol_vector_t *seaudit_filter_get_target_user(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->tgt_users;
+}
+
+int seaudit_filter_set_target_role(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->tgt_roles, v);
+}
+
+const apol_vector_t *seaudit_filter_get_target_role(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->tgt_roles;
+}
+
+int seaudit_filter_set_target_type(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->tgt_types, v);
+}
+
+const apol_vector_t *seaudit_filter_get_target_type(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->tgt_types;
+}
+
+int seaudit_filter_set_target_mls_lvl(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->tgt_mls_lvl, v);
+}
+
+const apol_vector_t *seaudit_filter_get_target_mls_lvl(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->tgt_mls_lvl;
+}
+
+int seaudit_filter_set_target_mls_clr(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->tgt_mls_clr, v);
+}
+
+const apol_vector_t *seaudit_filter_get_target_mls_clr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->tgt_mls_clr;
+}
+
+int seaudit_filter_set_target_class(seaudit_filter_t * filter, const apol_vector_t * v)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_vector(filter, &filter->tgt_classes, v);
+}
+
+const apol_vector_t *seaudit_filter_get_target_class(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->tgt_classes;
+}
+
+int seaudit_filter_set_permission(seaudit_filter_t * filter, const char *perm)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->perm, perm);
+}
+
+const char *seaudit_filter_get_permission(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->perm;
+}
+
+int seaudit_filter_set_executable(seaudit_filter_t * filter, const char *exe)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->exe, exe);
+}
+
+const char *seaudit_filter_get_executable(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->exe;
+}
+
+int seaudit_filter_set_host(seaudit_filter_t * filter, const char *host)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->host, host);
+}
+
+const char *seaudit_filter_get_host(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->host;
+}
+
+int seaudit_filter_set_path(seaudit_filter_t * filter, const char *path)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->path, path);
+}
+
+const char *seaudit_filter_get_path(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->path;
+}
+
+int seaudit_filter_set_inode(seaudit_filter_t * filter, unsigned long inode)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_ulong(filter, &filter->inode, inode);
+ return 0;
+}
+
+unsigned long seaudit_filter_get_inode(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->inode;
+}
+
+int seaudit_filter_set_pid(seaudit_filter_t * filter, unsigned int pid)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_uint(filter, &filter->pid, pid);
+ return 0;
+}
+
+unsigned int seaudit_filter_get_pid(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->pid;
+}
+
+int seaudit_filter_set_command(seaudit_filter_t * filter, const char *command)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->comm, command);
+}
+
+const char *seaudit_filter_get_command(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->comm;
+}
+
+int seaudit_filter_set_anyaddr(seaudit_filter_t * filter, const char *ipaddr)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->anyaddr, ipaddr);
+}
+
+const char *seaudit_filter_get_anyaddr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->anyaddr;
+}
+
+int seaudit_filter_set_anyport(seaudit_filter_t * filter, const int port)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->anyport, port);
+}
+
+int seaudit_filter_get_anyport(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->anyport;
+}
+
+int filter_set_ipaddress_vers_4_1(seaudit_filter_t * filter, const char *ipaddr)
+{
+ return seaudit_filter_set_anyaddr(filter, ipaddr);
+}
+
+const char *filter_get_ipaddress_vers_4_1(const seaudit_filter_t * filter)
+{
+ return seaudit_filter_get_anyaddr(filter);
+}
+
+int filter_set_port_vers_4_1(seaudit_filter_t * filter, const int port)
+{
+ return seaudit_filter_set_anyport(filter, port);
+}
+
+int filter_get_port_vers_4_1(const seaudit_filter_t * filter)
+{
+ return seaudit_filter_get_anyport(filter);
+}
+
+#if LINK_SHARED == 1
+__asm__(".symver filter_set_ipaddress_vers_4_1,seaudit_filter_set_ipaddress@VERS_4.1");
+__asm__(".symver filter_get_ipaddress_vers_4_1,seaudit_filter_get_ipaddress@VERS_4.1");
+__asm__(".symver filter_set_port_vers_4_1,seaudit_filter_set_port@VERS_4.1");
+__asm__(".symver filter_get_port_vers_4_1,seaudit_filter_get_port@VERS_4.1");
+#endif
+
+int seaudit_filter_set_laddr(seaudit_filter_t * filter, const char *laddr)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->laddr, laddr);
+}
+
+const char *seaudit_filter_get_laddr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->laddr;
+}
+
+int seaudit_filter_set_lport(seaudit_filter_t * filter, const int lport)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->lport, lport);
+}
+
+int seaudit_filter_get_lport(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->lport;
+}
+
+int seaudit_filter_set_faddr(seaudit_filter_t * filter, const char *faddr)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->faddr, faddr);
+}
+
+const char *seaudit_filter_get_faddr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->faddr;
+}
+
+int seaudit_filter_set_fport(seaudit_filter_t * filter, const int fport)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->fport, fport);
+}
+
+int seaudit_filter_get_fport(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->fport;
+}
+
+int seaudit_filter_set_saddr(seaudit_filter_t * filter, const char *saddr)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->saddr, saddr);
+}
+
+const char *seaudit_filter_get_saddr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->saddr;
+}
+
+int seaudit_filter_set_sport(seaudit_filter_t * filter, const int sport)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->sport, sport);
+}
+
+int seaudit_filter_get_sport(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->sport;
+}
+
+int seaudit_filter_set_daddr(seaudit_filter_t * filter, const char *daddr)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->daddr, daddr);
+}
+
+const char *seaudit_filter_get_daddr(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->daddr;
+}
+
+int seaudit_filter_set_dport(seaudit_filter_t * filter, const int dport)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->dport, dport);
+}
+
+int seaudit_filter_get_dport(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->dport;
+}
+
+int filter_set_port_vers_4_2(seaudit_filter_t * filter, const int port)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->port, port);
+}
+
+int filter_get_port_vers_4_2(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->port;
+}
+
+#if LINK_SHARED == 1
+__asm__(".symver filter_set_port_vers_4_2,seaudit_filter_set_port@@VERS_4.2");
+__asm__(".symver filter_get_port_vers_4_2,seaudit_filter_get_port@@VERS_4.2");
+#endif
+
+int seaudit_filter_set_key(seaudit_filter_t * filter, const int key)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->key, key);
+}
+
+int seaudit_filter_get_key(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->key;
+}
+
+int seaudit_filter_set_cap(seaudit_filter_t * filter, const int cap)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter_set_int(filter, &filter->cap, cap);
+}
+
+int seaudit_filter_get_cap(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+ return filter->cap;
+}
+
+int seaudit_filter_set_netif(seaudit_filter_t * filter, const char *netif)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return filter_set_string(filter, &filter->netif, netif);
+}
+
+const char *seaudit_filter_get_netif(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return filter->netif;
+}
+
+int seaudit_filter_set_message_type(seaudit_filter_t * filter, const seaudit_avc_message_type_e message_type)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ filter->avc_msg_type = message_type;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ return 0;
+}
+
+seaudit_avc_message_type_e seaudit_filter_get_message_type(const seaudit_filter_t * filter)
+{
+ if (filter == NULL) {
+ errno = EINVAL;
+ return SEAUDIT_AVC_UNKNOWN;
+ }
+ return filter->avc_msg_type;
+}
+
+int seaudit_filter_set_date(seaudit_filter_t * filter, const struct tm *start, const struct tm *end,
+ seaudit_filter_date_match_e date_match)
+{
+ struct tm *new_tm = NULL;
+ if (filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* the following weird branching exists because start and end
+ * could be shadowing filter->start and filter->end. if
+ * filters->start and filter->end are free()d to early, then
+ * there may be a dereference of free()d memory */
+ if (filter->start != start) {
+ new_tm = NULL;
+ if (start != NULL) {
+ if ((new_tm = calloc(1, sizeof(*new_tm))) == NULL) {
+ return -1;
+ }
+ memcpy(new_tm, start, sizeof(*start));
+ }
+ free(filter->start);
+ filter->start = new_tm;
+ }
+ if (start != NULL) {
+ if (filter->end != end) {
+ new_tm = NULL;
+ if (end != NULL) {
+ if ((new_tm = calloc(1, sizeof(*new_tm))) == NULL) {
+ return -1;
+ }
+ memcpy(new_tm, end, sizeof(*end));
+ }
+ free(filter->end);
+ filter->end = new_tm;
+ }
+ } else {
+ free(filter->end);
+ filter->end = NULL;
+ }
+ filter->date_match = date_match;
+ if (filter->model != NULL) {
+ model_notify_filter_changed(filter->model, filter);
+ }
+ return 0;
+}
+
+void seaudit_filter_get_date(const seaudit_filter_t * filter, const struct tm **start, const struct tm **end,
+ seaudit_filter_date_match_e * match)
+{
+ if (start != NULL) {
+ *start = NULL;
+ }
+ if (end != NULL) {
+ *end = NULL;
+ }
+ if (match != NULL) {
+ *match = SEAUDIT_FILTER_DATE_MATCH_BEFORE;
+ }
+ if (filter == NULL || start == NULL || end == NULL || match == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ *start = filter->start;
+ *end = filter->end;
+ *match = filter->date_match;
+}
+
+int seaudit_filter_save_to_file(const seaudit_filter_t * filter, const char *filename)
+{
+ FILE *file;
+ const char *XML_VER = "<?xml version=\"1.0\"?>\n";
+
+ if (filter == NULL || filename == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if ((file = fopen(filename, "w")) == NULL) {
+ return -1;
+ }
+ fprintf(file, XML_VER);
+ fprintf(file, "<view xmlns=\"http://oss.tresys.com/projects/setools/seaudit-%s/\">\n", FILTER_FILE_FORMAT_VERSION);
+ filter_append_to_file(filter, file, 1);
+ fprintf(file, "</view>\n");
+ fclose(file);
+ return 0;
+}
+
+/******************** protected functions below ********************/
+
+void filter_set_model(seaudit_filter_t * filter, seaudit_model_t * model)
+{
+ filter->model = model;
+}
diff --git a/libseaudit/src/libseaudit.map b/libseaudit/src/libseaudit.map
new file mode 100644
index 0000000..e0ad723
--- /dev/null
+++ b/libseaudit/src/libseaudit.map
@@ -0,0 +1,88 @@
+VERS_4.1{
+ global:
+ seaudit_avc_message_*;
+ seaudit_filter_*;
+ seaudit_handle_msg;
+ seaudit_log_*;
+ seaudit_message_*;
+ seaudit_model_*;
+ seaudit_report_*;
+ seaudit_sort_*;
+ libseaudit_get_version;
+ local: *;
+};
+
+VERS_4.2{
+ global:
+ seaudit_avc_message_get_port;
+ seaudit_filter_get_anyaddr;
+ seaudit_filter_set_anyaddr;
+ seaudit_filter_get_anyport;
+ seaudit_filter_set_anyport;
+ seaudit_filter_get_cap;
+ seaudit_filter_set_cap;
+ seaudit_filter_get_daddr;
+ seaudit_fliter_set_daddr;
+ seaudit_filter_get_dport;
+ seaudit_filter_set_dport;
+ seaudit_filter_get_faddr;
+ seaudit_fliter_set_faddr;
+ seaudit_filter_get_fport;
+ seaudit_filter_set_fport;
+ seaudit_filter_get_inode;
+ seaudit_filter_set_inode;
+ seaudit_filter_get_key;
+ seaudit_filter_set_key;
+ seaudit_filter_get_laddr;
+ seaudit_fliter_set_laddr;
+ seaudit_filter_get_lport;
+ seaudit_filter_set_lport;
+ seaudit_filter_get_permission;
+ seaudit_filter_set_permission;
+ seaudit_filter_get_pid;
+ seaudit_filter_set_pid;
+ seaudit_filter_get_port;
+ seaudit_filter_set_port;
+ seaudit_filter_get_saddr;
+ seaudit_fliter_set_saddr;
+ seaudit_filter_get_sport;
+ seaudit_filter_set_sport;
+ seaudit_filter_get_strict;
+ seaudit_filter_set_strict;
+ seaudit_log_clear;
+ seaudit_model_hide_message;
+ seaudit_sort_by_cap;
+ seaudit_sort_by_daddr;
+ seaudit_sort_by_dport;
+ seaudit_sort_by_faddr;
+ seaudit_sort_by_fport;
+ seaudit_sort_by_key;
+ seaudit_sort_by_laddr;
+ seaudit_sort_by_lport;
+ seaudit_sort_by_port;
+ seaudit_sort_by_saddr;
+ seaudit_sort_by_sport;
+ seaudit_sort_create_from_sort;
+} VERS_4.1;
+
+VERS_4.3{
+ global:
+ seaudit_avc_message_get_source__mls_lvl;
+ seaudit_avc_message_get_source__mls_clr;
+ seaudit_avc_message_get_target_mls_lvl;
+ seaudit_avc_message_get_target_mls_clr;
+ seaudit_filter_get_source_mls_lvl;
+ seaudit_filter_get_source_mls_clr;
+ seaudit_filter_set_source_mls_lvl;
+ seaudit_filter_set_source_mls_clr;
+ seaudit_filter_get_target_mls_lvl;
+ seaudit_filter_get_target_mls_clr;
+ seaudit_filter_set_target_mls_lvl;
+ seaudit_filter_set_target_mls_clr;
+ seaudit_log_get_mls_lvl;
+ seaudit_log_get_mls_clr;
+ seaudit_sort_by_source_mls_lvl;
+ seaudit_sort_by_source_mls_clr;
+ seaudit_sort_by_target_mls_lvl;
+ seaudit_sort_by_target_mls_clr;
+} VERS_4.2;
diff --git a/libseaudit/src/load_message.c b/libseaudit/src/load_message.c
new file mode 100644
index 0000000..7951eb2
--- /dev/null
+++ b/libseaudit/src/load_message.c
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * Implementation of a single policy load log message.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/******************** protected functions below ********************/
+
+seaudit_load_message_t *load_message_create(void)
+{
+ return calloc(1, sizeof(seaudit_load_message_t));
+}
+
+void load_message_free(seaudit_load_message_t * msg)
+{
+ if (msg != NULL) {
+ free(msg->binary);
+ free(msg);
+ }
+}
+
+char *load_message_to_string(const seaudit_message_t * msg, const char *date)
+{
+ seaudit_load_message_t *load = msg->data.load;
+ const char *host = msg->host;
+ const char *manager = msg->manager;
+ char *s = NULL;
+ if (asprintf(&s,
+ "%s %s %s: security: %d users, %d roles, %d types, %d bools\n"
+ "%s %s %s: security: %d classes, %d rules",
+ date, host, manager, load->users, load->roles, load->types, load->bools, date, host, manager, load->classes,
+ load->rules) < 0) {
+ return NULL;
+ }
+ return s;
+}
+
+char *load_message_to_string_html(const seaudit_message_t * msg, const char *date)
+{
+ seaudit_load_message_t *load = msg->data.load;
+ const char *host = msg->host;
+ const char *manager = msg->manager;
+ char *s = NULL;
+ if (asprintf(&s,
+ "<font class=\"message_date\">%s</font> "
+ "<font class=\"host_name\">%s</font> "
+ "%s: security: %d users, %d roles, %d types, %d bools<br>\n"
+ "<font class=\"message_date\">%s</font> "
+ "<font class=\"host_name\">%s</font> "
+ "%s: security: %d classes, %d rules<br>",
+ date, host, manager, load->users, load->roles, load->types, load->bools, date, host, manager, load->classes,
+ load->rules) < 0) {
+ return NULL;
+ }
+ return s;
+}
+
+char *load_message_to_misc_string(const seaudit_load_message_t * load)
+{
+ char *s = NULL;
+ if (asprintf(&s,
+ "users=%d roles=%d types=%d bools=%d classes=%d rules=%d",
+ load->users, load->roles, load->types, load->bools, load->classes, load->rules) < 0) {
+ return NULL;
+ }
+ return s;
+}
diff --git a/libseaudit/src/log.c b/libseaudit/src/log.c
new file mode 100644
index 0000000..6665cd0
--- /dev/null
+++ b/libseaudit/src/log.c
@@ -0,0 +1,253 @@
+/**
+ * @file
+ * Implementation for the main libseaudit object, seaudit_log_t.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <apol/util.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+seaudit_log_t *seaudit_log_create(seaudit_handle_fn_t fn, void *callback_arg)
+{
+ seaudit_log_t *log = NULL;
+ int error;
+ if ((log = calloc(1, sizeof(*log))) == NULL) {
+ return NULL;
+ }
+ log->fn = fn;
+ log->handle_arg = callback_arg;
+ if ((log->messages = apol_vector_create(message_free)) == NULL ||
+ (log->malformed_msgs = apol_vector_create(free)) == NULL ||
+ (log->models = apol_vector_create(NULL)) == NULL ||
+ (log->types = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->classes = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->roles = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->users = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->perms = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->mls_lvl = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->mls_clr = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->hosts = apol_bst_create(apol_str_strcmp, free)) == NULL
+ || (log->bools = apol_bst_create(apol_str_strcmp, free)) == NULL
+ || (log->managers = apol_bst_create(apol_str_strcmp, free)) == NULL) {
+ error = errno;
+ seaudit_log_destroy(&log);
+ errno = error;
+ return NULL;
+ }
+ return log;
+}
+
+void seaudit_log_destroy(seaudit_log_t ** log)
+{
+ size_t i;
+ if (log == NULL || *log == NULL) {
+ return;
+ }
+ for (i = 0; i < apol_vector_get_size((*log)->models); i++) {
+ seaudit_model_t *m = apol_vector_get_element((*log)->models, i);
+ model_remove_log(m, *log);
+ }
+ apol_vector_destroy(&(*log)->messages);
+ apol_vector_destroy(&(*log)->malformed_msgs);
+ apol_vector_destroy(&(*log)->models);
+ apol_bst_destroy(&(*log)->types);
+ apol_bst_destroy(&(*log)->classes);
+ apol_bst_destroy(&(*log)->roles);
+ apol_bst_destroy(&(*log)->users);
+ apol_bst_destroy(&(*log)->perms);
+ apol_bst_destroy(&(*log)->hosts);
+ apol_bst_destroy(&(*log)->bools);
+ apol_bst_destroy(&(*log)->managers);
+ apol_bst_destroy(&(*log)->mls_lvl);
+ apol_bst_destroy(&(*log)->mls_clr);
+ free(*log);
+ *log = NULL;
+}
+
+void seaudit_log_clear(seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ apol_vector_destroy(&log->messages);
+ apol_vector_destroy(&log->malformed_msgs);
+ apol_bst_destroy(&log->types);
+ apol_bst_destroy(&log->classes);
+ apol_bst_destroy(&log->roles);
+ apol_bst_destroy(&log->users);
+ apol_bst_destroy(&log->perms);
+ apol_bst_destroy(&log->hosts);
+ apol_bst_destroy(&log->bools);
+ apol_bst_destroy(&log->managers);
+ apol_bst_destroy(&log->mls_lvl);
+ apol_bst_destroy(&log->mls_clr);
+ if ((log->messages = apol_vector_create(message_free)) == NULL ||
+ (log->malformed_msgs = apol_vector_create(free)) == NULL ||
+ (log->types = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->classes = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->roles = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->users = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->perms = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->mls_lvl = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->mls_clr = apol_bst_create(apol_str_strcmp, free)) == NULL ||
+ (log->hosts = apol_bst_create(apol_str_strcmp, free)) == NULL
+ || (log->bools = apol_bst_create(apol_str_strcmp, free)) == NULL
+ || (log->managers = apol_bst_create(apol_str_strcmp, free)) == NULL) {
+ /* hopefully will never get here... */
+ return;
+ }
+ for (size_t i = 0; i < apol_vector_get_size(log->models); i++) {
+ seaudit_model_t *m = apol_vector_get_element(log->models, i);
+ model_notify_log_changed(m, log);
+ }
+}
+
+apol_vector_t *seaudit_log_get_users(const seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_bst_get_vector(log->users, 0);
+}
+
+apol_vector_t *seaudit_log_get_roles(const seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_bst_get_vector(log->roles, 0);
+}
+
+apol_vector_t *seaudit_log_get_types(const seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_bst_get_vector(log->types, 0);
+}
+
+apol_vector_t *seaudit_log_get_mls_lvl(const seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_bst_get_vector(log->mls_lvl, 0);
+}
+
+apol_vector_t *seaudit_log_get_mls_clr(const seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_bst_get_vector(log->mls_clr, 0);
+}
+
+apol_vector_t *seaudit_log_get_classes(const seaudit_log_t * log)
+{
+ if (log == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return apol_bst_get_vector(log->classes, 0);
+}
+
+/******************** protected functions below ********************/
+
+int log_append_model(seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (apol_vector_append(log->models, model) < 0) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+void log_remove_model(seaudit_log_t * log, seaudit_model_t * model)
+{
+ size_t i;
+ if (apol_vector_get_index(log->models, model, NULL, NULL, &i) == 0) {
+ apol_vector_remove(log->models, i);
+ }
+}
+
+const apol_vector_t *log_get_messages(const seaudit_log_t * log)
+{
+ return log->messages;
+}
+
+const apol_vector_t *log_get_malformed_messages(const seaudit_log_t * log)
+{
+ return log->malformed_msgs;
+}
+
+static void seaudit_handle_default_callback(void *arg __attribute__ ((unused)),
+ const seaudit_log_t * log __attribute__ ((unused)),
+ int level, const char *fmt, va_list va_args)
+{
+ switch (level) {
+ case SEAUDIT_MSG_INFO:
+ {
+ /* by default do not display these messages */
+ return;
+ }
+ case SEAUDIT_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case SEAUDIT_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+void seaudit_handle_msg(const seaudit_log_t * log, int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (log == NULL || log->fn == NULL) {
+ seaudit_handle_default_callback(NULL, NULL, level, fmt, ap);
+ } else {
+ log->fn(log->handle_arg, log, level, fmt, ap);
+ }
+ va_end(ap);
+}
diff --git a/libseaudit/src/message.c b/libseaudit/src/message.c
new file mode 100644
index 0000000..4e767d0
--- /dev/null
+++ b/libseaudit/src/message.c
@@ -0,0 +1,204 @@
+/**
+ * @file
+ * Implementation of a single seaudit log message. Because C does
+ * not have RTTI, fake it below.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+void *seaudit_message_get_data(const seaudit_message_t * msg, seaudit_message_type_e * type)
+{
+ if (type != NULL) {
+ *type = SEAUDIT_MESSAGE_TYPE_INVALID;
+ }
+ if (msg == NULL || type == NULL || msg->type == SEAUDIT_MESSAGE_TYPE_INVALID) {
+ errno = EINVAL;
+ return NULL;
+ }
+ switch ((*type = msg->type)) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ return msg->data.avc;
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ return msg->data.boolm;
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ return msg->data.load;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+const struct tm *seaudit_message_get_time(const seaudit_message_t * msg)
+{
+ if (!msg) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return msg->date_stamp;
+}
+
+const char *seaudit_message_get_host(const seaudit_message_t * msg)
+{
+ if (!msg) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return msg->host;
+}
+
+#define DATE_STR_SIZE 256
+
+char *seaudit_message_to_string(const seaudit_message_t * msg)
+{
+ char date[DATE_STR_SIZE];
+ if (msg == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ strftime(date, DATE_STR_SIZE, "%b %d %H:%M:%S", msg->date_stamp);
+ switch (msg->type) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ return avc_message_to_string(msg, date);
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ return bool_message_to_string(msg, date);
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ return load_message_to_string(msg, date);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+char *seaudit_message_to_string_html(const seaudit_message_t * msg)
+{
+ char date[DATE_STR_SIZE];
+ if (msg == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ strftime(date, DATE_STR_SIZE, "%b %d %H:%M:%S", msg->date_stamp);
+ switch (msg->type) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ return avc_message_to_string_html(msg, date);
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ return bool_message_to_string_html(msg, date);
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ return load_message_to_string_html(msg, date);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+char *seaudit_message_to_misc_string(const seaudit_message_t * msg)
+{
+ if (msg == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (msg->type) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ return avc_message_to_misc_string(msg->data.avc);
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ return bool_message_to_misc_string(msg->data.boolm);
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ return load_message_to_misc_string(msg->data.load);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+/******************** protected functions below ********************/
+
+seaudit_message_t *message_create(seaudit_log_t * log, seaudit_message_type_e type)
+{
+ seaudit_message_t *m;
+ int error, rt = 0;
+ if (type == SEAUDIT_MESSAGE_TYPE_INVALID) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((m = calloc(1, sizeof(*m))) == NULL || apol_vector_append(log->messages, m) < 0) {
+ error = errno;
+ message_free(m);
+ ERR(log, "%s", strerror(error));
+ errno = errno;
+ return NULL;
+ }
+ m->type = type;
+ switch (m->type) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ if ((m->data.avc = avc_message_create()) == NULL) {
+ rt = -1;
+ }
+ break;
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ if ((m->data.boolm = bool_message_create()) == NULL) {
+ rt = -1;
+ }
+ break;
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ if ((m->data.load = load_message_create()) == NULL) {
+ rt = -1;
+ }
+ break;
+ default: /* shouldn't get here */
+ assert(0);
+ }
+ if (rt < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = errno;
+ return NULL;
+ }
+ return m;
+}
+
+void message_free(void *msg)
+{
+ if (msg != NULL) {
+ seaudit_message_t *m = (seaudit_message_t *) msg;
+ free(m->date_stamp);
+ switch (m->type) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ avc_message_free(m->data.avc);
+ break;
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ bool_message_free(m->data.boolm);
+ break;
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ load_message_free(m->data.load);
+ break;
+ default:
+ break;
+ }
+ free(m);
+ }
+}
diff --git a/libseaudit/src/model.c b/libseaudit/src/model.c
new file mode 100644
index 0000000..1bc4a23
--- /dev/null
+++ b/libseaudit/src/model.c
@@ -0,0 +1,808 @@
+/**
+ * @file
+ * Implementation of seaudit_model_t.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <apol/bst.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/uri.h>
+
+#define DEFAULT_MODEL_NAME "Untitled"
+
+struct seaudit_model
+{
+ char *name;
+ /** vector of seaudit_log_t pointers; this model will get
+ * messages from these logs */
+ apol_vector_t *logs;
+ /** vector of seaudit_message_t pointers; these point into
+ * messages from the watched logs (only valid if dirty == 0) */
+ apol_vector_t *messages;
+ /** vector of char * pointers; these point into malformed
+ * messages from the watched logs (only valid if dirty == 0) */
+ apol_vector_t *malformed_messages;
+ /** list of messages to hide */
+ apol_bst_t *hidden_messages;
+ /** vector of seaudit_filter_t */
+ apol_vector_t *filters;
+ /** if more than one filter is being applied, then accept
+ * messages if any match or if all match */
+ seaudit_filter_match_e match;
+ /** if a filter is being applied, then either show/hide
+ * messages selected by filter */
+ seaudit_filter_visible_e visible;
+ /** vector of seaudit_sort_t, order from highest priority to lowest */
+ apol_vector_t *sorts;
+ /** number of allow messages in the model (only valid if dirty == 0) */
+ size_t num_allows;
+ /** number of deny messages in the model (only valid if dirty == 0) */
+ size_t num_denies;
+ /** number of boolean changes in the model (only valid if dirty == 0) */
+ size_t num_bools;
+ /** number of policy loads in the model (only valid if dirty == 0) */
+ size_t num_loads;
+ /** non-zero whenever this model needs to be recalculated */
+ int dirty;
+};
+
+/**
+ * Apply all of the model's filters to the message.
+ *
+ * @param model Model containing filters to apply.
+ * @param m Message to check.
+ *
+ * @return Non-zero if the message is accepted by the filters, 0 if not.
+ */
+static int model_filter_message(seaudit_model_t * model, const seaudit_message_t * m)
+{
+ size_t i;
+ int compval, filters_passed = 0;
+ if (apol_vector_get_size(model->filters) == 0) {
+ return 1;
+ }
+ for (i = 0; i < apol_vector_get_size(model->filters); i++) {
+ seaudit_filter_t *f = apol_vector_get_element(model->filters, i);
+ compval = filter_is_accepted(f, m);
+ if (compval) {
+ if (model->match == SEAUDIT_FILTER_MATCH_ANY) {
+ return 1;
+ }
+ filters_passed++;
+ } else {
+ if (model->match == SEAUDIT_FILTER_MATCH_ALL) {
+ return 0;
+ }
+ }
+ }
+ if (model->match == SEAUDIT_FILTER_MATCH_ANY) {
+ /* if got here, then no filters were met */
+ return 0;
+ }
+ /* if got here, then all criteria were met */
+ if (filters_passed) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Callback for sorting the model's messages vector.
+ *
+ * @param a First message to compare.
+ * @param b Second message to compare.
+ * @param data Pointer to the model being sorted.
+ *
+ * @return 0 if the messages are equivalent, < 0 if a is first, > 0 if
+ * b is first.
+ */
+static int message_comp(const void *a, const void *b, void *data)
+{
+ const seaudit_message_t *m1 = a;
+ const seaudit_message_t *m2 = b;
+ seaudit_model_t *model = data;
+ size_t i;
+ seaudit_sort_t *s;
+ int compval, s1, s2;
+ for (i = 0; i < apol_vector_get_size(model->sorts); i++) {
+ s = apol_vector_get_element(model->sorts, i);
+ s1 = sort_is_supported(s, m1);
+ s2 = sort_is_supported(s, m2);
+ if (!s1 && !s2) {
+ continue;
+ }
+ if (!s2) {
+ return -1;
+ }
+ if (!s1) {
+ return 1;
+ }
+ if ((compval = sort_comp(s, m1, m2)) != 0) {
+ return compval;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Sort the model's messages. Create two temporary vectors. The
+ * first holds messages that are sortable, according to the list of
+ * sort objects. Sort them in their priority order. The second
+ * vector holds messages that are not sortable; append those messages
+ * to the end of the first (now sorted) vector.
+ *
+ * @param log Error handling log.
+ * @param model Model to sort.
+ *
+ * @return 0 on successful sort, < 0 on error.
+ */
+static int model_sort(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ size_t i, j, num_messages = apol_vector_get_size(model->messages);
+ apol_vector_t *sup = NULL, *unsup = NULL;
+ seaudit_message_t *m;
+ seaudit_sort_t *s;
+ int supported = 0, retval = -1, error = 0;
+ if (apol_vector_get_size(model->sorts) == 0) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ if ((sup = apol_vector_create_with_capacity(num_messages, NULL)) == NULL ||
+ (unsup = apol_vector_create_with_capacity(num_messages, NULL)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < num_messages; i++) {
+ m = apol_vector_get_element(model->messages, i);
+ supported = 0;
+ for (j = 0; j < apol_vector_get_size(model->sorts); j++) {
+ s = apol_vector_get_element(model->sorts, j);
+ if ((supported = sort_is_supported(s, m)) != 0) {
+ break;
+ }
+ }
+ if ((supported && apol_vector_append(sup, m) < 0) || (!supported && apol_vector_append(unsup, m) < 0)) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ apol_vector_sort(sup, message_comp, model);
+ if (apol_vector_cat(sup, unsup) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ apol_vector_destroy(&model->messages);
+ model->messages = sup;
+ sup = NULL;
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&sup);
+ apol_vector_destroy(&unsup);
+ if (retval != 0) {
+ errno = error;
+ }
+ return retval;
+}
+
+/**
+ * Iterate through the model's messages and recalculate the number of
+ * each type of message is stored within.
+ *
+ * @param model Model to recalculate.
+ */
+static void model_recalc_stats(seaudit_model_t * model)
+{
+ size_t i;
+ seaudit_message_t *msg;
+ seaudit_message_type_e type;
+ void *v;
+ seaudit_avc_message_t *avc;
+ model->num_allows = model->num_denies = model->num_bools = model->num_loads = 0;
+ for (i = 0; i < apol_vector_get_size(model->messages); i++) {
+ msg = apol_vector_get_element(model->messages, i);
+ v = seaudit_message_get_data(msg, &type);
+ if (type == SEAUDIT_MESSAGE_TYPE_AVC) {
+ avc = (seaudit_avc_message_t *) v;
+ if (avc->msg == SEAUDIT_AVC_DENIED) {
+ model->num_denies++;
+ } else if (avc->msg == SEAUDIT_AVC_GRANTED) {
+ model->num_allows++;
+ }
+ } else if (type == SEAUDIT_MESSAGE_TYPE_BOOL) {
+ model->num_bools++;
+ } else if (type == SEAUDIT_MESSAGE_TYPE_LOAD) {
+ model->num_loads++;
+ }
+ }
+}
+
+/**
+ * Recalculate all of the messages associated with a particular model,
+ * based upon that model's criteria. If the model is marked as not
+ * dirty then do nothing and return success.
+ *
+ * @param log Log to which report error messages.
+ * @param model Model whose messages list to refresh.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int model_refresh(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ size_t i, j;
+ seaudit_log_t *l;
+ const apol_vector_t *v;
+ seaudit_message_t *message;
+ void *result;
+ int error, filter_match;
+
+ if (!model->dirty) {
+ return 0;
+ }
+ apol_vector_destroy(&model->messages);
+ apol_vector_destroy(&model->malformed_messages);
+ if ((model->messages = apol_vector_create(NULL)) == NULL || (model->malformed_messages = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ for (i = 0; i < apol_vector_get_size(model->logs); i++) {
+ l = apol_vector_get_element(model->logs, i);
+ v = log_get_messages(l);
+ for (j = 0; j < apol_vector_get_size(v); j++) {
+ message = apol_vector_get_element(v, j);
+ if (apol_bst_get_element(model->hidden_messages, message, NULL, &result) == 0) {
+ continue;
+ }
+ filter_match = model_filter_message(model, message);
+ if (((filter_match && model->visible == SEAUDIT_FILTER_VISIBLE_SHOW) ||
+ (!filter_match && model->visible == SEAUDIT_FILTER_VISIBLE_HIDE)) &&
+ apol_vector_append(model->messages, message) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+ v = log_get_malformed_messages(l);
+ if (apol_vector_cat(model->malformed_messages, v) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+ if (model_sort(log, model) < 0) {
+ return -1;
+ }
+ model_recalc_stats(model);
+ model->dirty = 0;
+ return 0;
+}
+
+/**
+ * Callback invoked when free()ing a vector of filters.
+ *
+ * @param v Filter object to free.
+ */
+static void filter_free(void *v)
+{
+ seaudit_filter_t *f = v;
+ seaudit_filter_destroy(&f);
+}
+
+/**
+ * Callback invoked when free()ing a vector of sort objects.
+ *
+ * @param v Sort object to free.
+ */
+static void sort_free(void *v)
+{
+ seaudit_sort_t *sort = v;
+ seaudit_sort_destroy(&sort);
+}
+
+seaudit_model_t *seaudit_model_create(const char *name, seaudit_log_t * log)
+{
+ seaudit_model_t *m = NULL;
+ int error;
+ if ((m = calloc(1, sizeof(*m))) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ if (name == NULL) {
+ name = DEFAULT_MODEL_NAME;
+ }
+ if ((m->name = strdup(name)) == NULL ||
+ (m->logs = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ (m->hidden_messages = apol_bst_create(NULL, NULL)) == NULL ||
+ (m->filters = apol_vector_create_with_capacity(1, filter_free)) == NULL ||
+ (m->sorts = apol_vector_create_with_capacity(1, sort_free)) == NULL) {
+ error = errno;
+ seaudit_model_destroy(&m);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ if (log != NULL) {
+ if (apol_vector_append(m->logs, log) < 0 || log_append_model(log, m)) {
+ error = errno;
+ seaudit_model_destroy(&m);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return NULL;
+ }
+ }
+ m->dirty = 1;
+ return m;
+}
+
+static void *model_filter_dup(const void *elem, void *data)
+{
+ const seaudit_filter_t *filter = elem;
+ seaudit_model_t *model = data;
+ seaudit_filter_t *f;
+ if ((f = seaudit_filter_create_from_filter(filter)) == NULL) {
+ return NULL;
+ }
+ filter_set_model(f, model);
+ return f;
+}
+
+static void *model_sort_dup(const void *elem, void *data __attribute__ ((unused)))
+{
+ const seaudit_sort_t *sort = elem;
+ seaudit_model_t *model = data;
+ seaudit_sort_t *s;
+ if ((s = sort_create_from_sort(sort)) == NULL) {
+ return NULL;
+ }
+ if (seaudit_model_append_sort(model, s) < 0) {
+ seaudit_sort_destroy(&s);
+ return NULL;
+ }
+ return s;
+}
+
+seaudit_model_t *seaudit_model_create_from_model(const seaudit_model_t * model)
+{
+ seaudit_model_t *m = NULL;
+ int error = 0;
+ size_t i;
+ const char *name;
+
+ if (model == NULL) {
+ error = EINVAL;
+ goto cleanup;
+ }
+ if ((m = calloc(1, sizeof(*m))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((name = model->name) == NULL) {
+ name = "Untitled";
+ }
+ if ((m->name = strdup(name)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ m->dirty = 1;
+ if ((m->logs = apol_vector_create_from_vector(model->logs, NULL, NULL, NULL)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((m->filters = apol_vector_create_from_vector(model->filters, model_filter_dup, (void *)m, filter_free)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((m->sorts = apol_vector_create_from_vector(model->sorts, model_sort_dup, (void *)m, sort_free)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ m->match = model->match;
+ m->visible = model->visible;
+ /* link this new model to the old model's logs */
+ for (i = 0; i < apol_vector_get_size(m->logs); i++) {
+ seaudit_log_t *log = apol_vector_get_element(m->logs, i);
+ if (log_append_model(log, m) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ cleanup:
+ if (error != 0) {
+ seaudit_model_destroy(&m);
+ errno = error;
+ return NULL;
+ }
+ return m;
+}
+
+seaudit_model_t *seaudit_model_create_from_file(const char *filename)
+{
+ struct filter_parse_state state;
+ int retval, error;
+ seaudit_model_t *m;
+ memset(&state, 0, sizeof(state));
+ if ((state.filters = apol_vector_create(filter_free)) == NULL) {
+ return NULL;
+ }
+ retval = filter_parse_xml(&state, filename);
+ if (retval < 0) {
+ error = errno;
+ free(state.view_name);
+ apol_vector_destroy(&state.filters);
+ errno = errno;
+ return NULL;
+ }
+ if ((m = seaudit_model_create(state.view_name, NULL)) == NULL) {
+ error = errno;
+ free(state.view_name);
+ apol_vector_destroy(&state.filters);
+ errno = error;
+ return NULL;
+ }
+ free(state.view_name);
+ apol_vector_destroy(&m->filters);
+ m->filters = state.filters;
+ state.filters = NULL;
+ seaudit_model_set_filter_match(m, state.view_match);
+ seaudit_model_set_filter_visible(m, state.view_visible);
+ return m;
+}
+
+void seaudit_model_destroy(seaudit_model_t ** model)
+{
+ size_t i;
+ if (model == NULL || *model == NULL) {
+ return;
+ }
+ for (i = 0; i < apol_vector_get_size((*model)->logs); i++) {
+ seaudit_log_t *l = apol_vector_get_element((*model)->logs, i);
+ log_remove_model(l, *model);
+ }
+ free((*model)->name);
+ apol_vector_destroy(&(*model)->logs);
+ apol_vector_destroy(&(*model)->filters);
+ apol_vector_destroy(&(*model)->sorts);
+ apol_vector_destroy(&(*model)->messages);
+ apol_vector_destroy(&(*model)->malformed_messages);
+ apol_bst_destroy(&(*model)->hidden_messages);
+ free(*model);
+ *model = NULL;
+}
+
+int seaudit_model_save_to_file(const seaudit_model_t * model, const char *filename)
+{
+ FILE *file;
+ const char *XML_VER = "<?xml version=\"1.0\"?>\n";
+ seaudit_filter_t *filter;
+ size_t i;
+
+ if (model == NULL || filename == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if ((file = fopen(filename, "w")) == NULL) {
+ return -1;
+ }
+ fprintf(file, XML_VER);
+ fprintf(file, "<view xmlns=\"http://oss.tresys.com/projects/setools/seaudit-%s/\" name=\"%s\" match=\"%s\" show=\"%s\">\n",
+ FILTER_FILE_FORMAT_VERSION, model->name,
+ model->match == SEAUDIT_FILTER_MATCH_ALL ? "all" : "any",
+ model->visible == SEAUDIT_FILTER_VISIBLE_SHOW ? "true" : "false");
+ for (i = 0; i < apol_vector_get_size(model->filters); i++) {
+ filter = apol_vector_get_element(model->filters, i);
+ filter_append_to_file(filter, file, 1);
+ }
+ fprintf(file, "</view>\n");
+ fclose(file);
+ return 0;
+}
+
+const char *seaudit_model_get_name(const seaudit_model_t * model)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return model->name;
+}
+
+int seaudit_model_set_name(seaudit_model_t * model, const char *name)
+{
+ char *s;
+ if (model == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (name == NULL) {
+ name = DEFAULT_MODEL_NAME;
+ }
+ if ((s = strdup(name)) == NULL) {
+ return -1;
+ }
+ free(model->name);
+ model->name = s;
+ return 0;
+}
+
+int seaudit_model_append_log(seaudit_model_t * model, seaudit_log_t * log)
+{
+ if (model == NULL || log == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (apol_vector_append(model->logs, log) < 0 || log_append_model(log, model) < 0) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ model->dirty = 1;
+ return 0;
+}
+
+int seaudit_model_append_filter(seaudit_model_t * model, seaudit_filter_t * filter)
+{
+ if (model == NULL || filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (apol_vector_append(model->filters, filter) < 0) {
+ return -1;
+ }
+ filter_set_model(filter, model);
+ model->dirty = 1;
+ return 0;
+}
+
+const apol_vector_t *seaudit_model_get_filters(const seaudit_model_t * model)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return model->filters;
+}
+
+int seaudit_model_remove_filter(seaudit_model_t * model, seaudit_filter_t * filter)
+{
+ size_t i;
+ if (model == NULL || filter == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (apol_vector_get_index(model->filters, filter, NULL, NULL, &i) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ seaudit_filter_destroy(&filter);
+ apol_vector_remove(model->filters, i);
+ model->dirty = 1;
+ return 0;
+}
+
+int seaudit_model_set_filter_match(seaudit_model_t * model, seaudit_filter_match_e match)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ model->match = match;
+ model->dirty = 1;
+ return 0;
+}
+
+seaudit_filter_match_e seaudit_model_get_filter_match(const seaudit_model_t * model)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return SEAUDIT_FILTER_MATCH_ALL;
+ }
+ return model->match;
+}
+
+int seaudit_model_set_filter_visible(seaudit_model_t * model, seaudit_filter_visible_e visible)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ model->visible = visible;
+ model->dirty = 1;
+ return 0;
+}
+
+seaudit_filter_visible_e seaudit_model_get_filter_visible(const seaudit_model_t * model)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return SEAUDIT_FILTER_VISIBLE_SHOW;
+ }
+ return model->visible;
+}
+
+int seaudit_model_append_sort(seaudit_model_t * model, seaudit_sort_t * sort)
+{
+ if (model == NULL || sort == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (apol_vector_append(model->sorts, sort) < 0) {
+ return -1;
+ }
+ model->dirty = 1;
+ return 0;
+}
+
+int seaudit_model_clear_sorts(seaudit_model_t * model)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ apol_vector_destroy(&model->sorts);
+ if ((model->sorts = apol_vector_create_with_capacity(1, sort_free)) == NULL) {
+ return -1;
+ }
+ model->dirty = 1;
+ return 0;
+}
+
+int seaudit_model_is_changed(const seaudit_model_t * model)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ return model->dirty;
+}
+
+apol_vector_t *seaudit_model_get_messages(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (log == NULL || model == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (model_refresh(log, model) < 0) {
+ return NULL;
+ }
+ return apol_vector_create_from_vector(model->messages, NULL, NULL, NULL);
+}
+
+apol_vector_t *seaudit_model_get_malformed_messages(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (log == NULL || model == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ if (model_refresh(log, model) < 0) {
+ return NULL;
+ }
+ return apol_vector_create_from_vector(model->malformed_messages, NULL, NULL, NULL);
+}
+
+void seaudit_model_hide_message(seaudit_model_t * model, const seaudit_message_t * message)
+{
+ if (model == NULL) {
+ errno = EINVAL;
+ return;
+ }
+ if (message == NULL) {
+ return;
+ }
+ if (apol_bst_insert(model->hidden_messages, (seaudit_message_t *) message, NULL) == 0) {
+ model->dirty = 1;
+ }
+}
+
+size_t seaudit_model_get_num_allows(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (log == NULL || model == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ if (model_refresh(log, model) < 0) {
+ return 0;
+ }
+ return model->num_allows;
+}
+
+size_t seaudit_model_get_num_denies(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (log == NULL || model == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ if (model_refresh(log, model) < 0) {
+ return 0;
+ }
+ return model->num_denies;
+}
+
+size_t seaudit_model_get_num_bools(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (log == NULL || model == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ if (model_refresh(log, model) < 0) {
+ return 0;
+ }
+ return model->num_bools;
+}
+
+size_t seaudit_model_get_num_loads(const seaudit_log_t * log, seaudit_model_t * model)
+{
+ if (log == NULL || model == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ if (model_refresh(log, model) < 0) {
+ return 0;
+ }
+ return model->num_loads;
+}
+
+/******************** protected functions below ********************/
+
+void model_remove_log(seaudit_model_t * model, seaudit_log_t * log)
+{
+ size_t i;
+ if (apol_vector_get_index(model->logs, log, NULL, NULL, &i) == 0) {
+ apol_vector_remove(model->logs, i);
+ model->dirty = 1;
+ }
+}
+
+void model_notify_log_changed(seaudit_model_t * model, seaudit_log_t * log)
+{
+ size_t i;
+ if (apol_vector_get_index(model->logs, log, NULL, NULL, &i) == 0) {
+ model->dirty = 1;
+ }
+}
+
+void model_notify_filter_changed(seaudit_model_t * model, seaudit_filter_t * filter)
+{
+ size_t i;
+ if (apol_vector_get_index(model->filters, filter, NULL, NULL, &i) == 0) {
+ model->dirty = 1;
+ }
+}
diff --git a/libseaudit/src/parse.c b/libseaudit/src/parse.c
new file mode 100644
index 0000000..f1d44ba
--- /dev/null
+++ b/libseaudit/src/parse.c
@@ -0,0 +1,1513 @@
+/**
+ * @file
+ * Implementation for the audit log parser.
+ *
+ * @author Meggan Whalen mwhalen@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+#include <seaudit/parse.h>
+#include <apol/util.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <selinux/context.h>
+
+#define ALT_SYSCALL_STRING "msg=audit(" /* should contain SYSCALL_STRING */
+#define AUDITD_MSG "type="
+#define AVCMSG " avc: "
+#define BOOLMSG "committed booleans"
+#define LOADMSG " security: "
+#define NUM_TIME_COMPONENTS 3
+#define OLD_LOAD_POLICY_STRING "loadingpolicyconfigurationfrom"
+#define PARSE_NUM_SYSCALL_FIELDS 3
+#define SYSCALL_STRING "audit("
+
+/**
+ * Given a line from an audit log, create and return a vector of
+ * tokens from that line. The caller is responsible for calling
+ * apol_vector_destroy() upon that vector. Note that this function
+ * will modify the passed in line.
+ */
+static int get_tokens(seaudit_log_t * log, char *line, apol_vector_t ** tokens)
+{
+ char *line_ptr, *next;
+ *tokens = NULL;
+ int error = 0;
+
+ if ((*tokens = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ line_ptr = line;
+ /* Tokenize line while ignoring any adjacent whitespace chars. */
+ while ((next = strsep(&line_ptr, " ")) != NULL) {
+ if (strcmp(next, "") && !apol_str_is_only_white_space(next)) {
+ if (apol_vector_append(*tokens, next) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ }
+ cleanup:
+ if (error != 0) {
+ apol_vector_destroy(tokens);
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Given a line, determine what type of audit message it is.
+ */
+static seaudit_message_type_e is_selinux(const char *line)
+{
+ if (strstr(line, BOOLMSG) && (strstr(line, "kernel") || strstr(line, AUDITD_MSG)))
+ return SEAUDIT_MESSAGE_TYPE_BOOL;
+ else if (strstr(line, LOADMSG) && (strstr(line, "kernel") || strstr(line, AUDITD_MSG)))
+ return SEAUDIT_MESSAGE_TYPE_LOAD;
+ else if (strstr(line, AVCMSG) && (strstr(line, "kernel") || strstr(line, AUDITD_MSG)))
+ return SEAUDIT_MESSAGE_TYPE_AVC;
+ else
+ return SEAUDIT_MESSAGE_TYPE_INVALID;
+}
+
+extern int daylight;
+
+ /**
+ * Fill in the date_stamp field of a message. If the stamp was not
+ * already allocated space then do it here.
+ *
+ * @return 0 on success, > 0 on warning, < 0 on error.
+ */
+static int insert_time(const seaudit_log_t * log, const apol_vector_t * tokens, size_t * position, seaudit_message_t * msg)
+{
+ char *t = NULL;
+ size_t i, length = 0;
+ int error;
+
+ if (*position + NUM_TIME_COMPONENTS >= apol_vector_get_size(tokens)) {
+ WARN(log, "%s", "Not enough tokens for time.");
+ return 1;
+ }
+ for (i = 0; i < NUM_TIME_COMPONENTS; i++) {
+ length += strlen((char *)apol_vector_get_element(tokens, i + *position));
+ }
+
+ /* Increase size for terminating string char and whitespace within. */
+ length += NUM_TIME_COMPONENTS;
+ if ((t = (char *)calloc(1, length)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ for (i = 0; i < NUM_TIME_COMPONENTS; i++) {
+ if (i > 0) {
+ strcat(t, " ");
+ }
+ strcat(t, (char *)apol_vector_get_element(tokens, *position));
+ (*position)++;
+ }
+
+ if (!msg->date_stamp) {
+ if ((msg->date_stamp = (struct tm *)calloc(1, sizeof(struct tm))) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ free(t);
+ errno = error;
+ return -1;
+ }
+ }
+
+ if (strptime(t, "%b %d %T", msg->date_stamp) != NULL) {
+ /* set year to 1900 since we know no valid logs were
+ * generated. this will tell us that the msg does not
+ * really have a year */
+ msg->date_stamp->tm_isdst = 0;
+ msg->date_stamp->tm_year = 0;
+ }
+ free(t);
+ return 0;
+}
+
+/**
+ * Fill in the host field of a message.
+ *
+ * @return 0 on success, > 0 on warning, < 0 on error.
+ */
+static int insert_hostname(const seaudit_log_t * log, const apol_vector_t * tokens, size_t * position, seaudit_message_t * msg)
+{
+ char *s, *host;
+ if (*position >= apol_vector_get_size(tokens)) {
+ WARN(log, "%s", "Not enough tokens for hostname.");
+ return 1;
+ }
+ s = apol_vector_get_element(tokens, *position);
+ /* Make sure this is not the kernel string identifier, which
+ * may indicate that the hostname is empty. */
+ if (strstr(s, "kernel")) {
+ msg->host = NULL;
+ return 1;
+ }
+ (*position)++;
+ if ((host = strdup(s)) == NULL || apol_bst_insert_and_get(log->hosts, (void **)&host, NULL) < 0) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ msg->host = host;
+ return 0;
+}
+
+static int insert_standard_msg_header(const seaudit_log_t * log, const apol_vector_t * tokens, size_t * position,
+ seaudit_message_t * msg)
+{
+ int ret = 0;
+ if ((ret = insert_time(log, tokens, position, msg)) != 0) {
+ return ret;
+ }
+ if ((ret = insert_hostname(log, tokens, position, msg)) != 0) {
+ return ret;
+ }
+ return ret;
+}
+
+/**
+ * Parse the object manager that generated this audit message.
+ */
+static int insert_manager(const seaudit_log_t * log, seaudit_message_t * msg, const char *manager)
+{
+ char *m;
+ if ((m = strdup(manager)) == NULL || apol_bst_insert_and_get(log->managers, (void **)&m, NULL) < 0) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ msg->manager = m;
+ return 0;
+}
+
+/**
+ * Parse a context (user:role:type). For each of the pieces, add them
+ * to the log's BSTs. Set reference pointers to those strings.
+ */
+static int parse_context(seaudit_log_t * log, char *token, char **user, char **role, char **type, char **mls_lvl, char **mls_clr)
+{
+ char *s, *range;
+ int error, ret = 0;
+ context_t con = context_new(token);
+ *user = *role = *type = *mls_lvl = *mls_clr = NULL;
+
+ if (con == NULL) {
+ WARN(log, "%s", "Error parsing context.");
+ ret = 1;
+ goto out;
+ }
+
+ if ((s = strdup(context_user_get(con))) == NULL || apol_bst_insert_and_get(log->users, (void **)&s, NULL) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ ret = -1;
+ goto out;
+ }
+ *user = s;
+
+ if ((s = strdup(context_role_get(con))) == NULL || apol_bst_insert_and_get(log->roles, (void **)&s, NULL) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ ret = -1;
+ goto out;
+ }
+ *role = s;
+
+ if ((s = strdup(context_type_get(con))) == NULL || apol_bst_insert_and_get(log->types, (void **)&s, NULL) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ ret = -1;
+ goto out;
+ }
+ *type = s;
+
+ if (range = context_range_get(con)) {
+ char *lvl, *clr;
+ lvl = strsep(&range, "-");
+ clr = strsep(&range, "-");
+ if (clr == NULL)
+ /* level and clearance are the same */
+ clr = lvl;
+
+ if ((s = strdup(lvl)) == NULL || apol_bst_insert_and_get(log->mls_lvl, (void **)&s, NULL) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ ret = -1;
+ goto out;
+ }
+ *mls_lvl = s;
+
+ if ((s = strdup(clr)) == NULL || apol_bst_insert_and_get(log->mls_clr, (void **)&s, NULL) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ ret = -1;
+ goto out;
+ }
+ *mls_clr = s;
+ }
+
+out:
+ context_free(con);
+ return ret;
+}
+
+/******************** AVC message parsing ********************/
+
+/**
+ * Given a token, determine if it is the new AVC header or not.
+ */
+static int avc_msg_is_token_new_audit_header(const char *token)
+{
+ return (strstr(token, SYSCALL_STRING) ? 1 : 0);
+}
+
+/**
+ * If the given token begins with prefix, then set reference pointer
+ * result to everything following prefix and return 1. Otherwise
+ * return 0.
+ */
+static int avc_msg_is_prefix(char *token, char *prefix, char **result)
+{
+ size_t i = 0, length;
+
+ length = strlen(prefix);
+ if (strlen(token) < length)
+ return 0;
+
+ for (i = 0; i < length; i++) {
+ if (token[i] != prefix[i]) {
+ return 0;
+ }
+ }
+
+ *result = token + length;
+ return 1;
+}
+
+/**
+ * Beginning with element *position, fill in the given avc message
+ * with all permissions found. Afterwards update *position to point
+ * to the next unprocessed token. Permissions should start and end
+ * with braces and if not, then this is invalid.
+ *
+ * @return 0 on success, > 0 on warning, < 0 on error.
+ */
+static int avc_msg_insert_perms(const seaudit_log_t * log, apol_vector_t * tokens, size_t * position, seaudit_avc_message_t * avc)
+{
+ char *s, *perm;
+ int error;
+ if ((s = apol_vector_get_element(tokens, *position)) == NULL || strcmp(s, "{") != 0) {
+ WARN(log, "%s", "Expected an opening brace while parsing permissions.");
+ return 1;
+ }
+ (*position)++;
+
+ while (*position < apol_vector_get_size(tokens)) {
+ s = apol_vector_get_element(tokens, *position);
+ assert(s != NULL);
+ (*position)++;
+ if (strcmp(s, "}") == 0) {
+ return 0;
+ }
+
+ if ((perm = strdup(s)) == NULL ||
+ apol_bst_insert_and_get(log->perms, (void **)&perm, NULL) < 0 || apol_vector_append(avc->perms, perm) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+
+ /* if got here, then message is too short */
+ WARN(log, "%s", "Expected a closing brace while parsing permissions.");
+ return 1;
+}
+
+static int avc_msg_insert_syscall_info(const seaudit_log_t * log, char *token, seaudit_message_t * msg, seaudit_avc_message_t * avc)
+{
+ size_t length, header_len = 0, i = 0;
+ char *fields[PARSE_NUM_SYSCALL_FIELDS];
+ char *time_str = NULL;
+ time_t temp;
+
+ length = strlen(token);
+
+ /* Chop off the ':' at the end of the syscall info token */
+ if (token[length - 1] == ':') {
+ token[length - 1] = '\0';
+ length--;
+ }
+ /* Chop off the ')' at the end of the syscall info token */
+ if (token[length - 1] == ')') {
+ token[length - 1] = '\0';
+ length--;
+ }
+ header_len = strlen(SYSCALL_STRING);
+
+ /* Check to see if variations on syscall header exist */
+ if (strstr(token, ALT_SYSCALL_STRING)) {
+ header_len = strlen(ALT_SYSCALL_STRING);
+ }
+
+ time_str = token + header_len;
+ /* Parse seconds.nanoseconds:serial */
+ while (i < PARSE_NUM_SYSCALL_FIELDS && (fields[i] = strsep(&time_str, ".:")) != NULL) {
+ i++;
+ }
+
+ if (i != PARSE_NUM_SYSCALL_FIELDS) {
+ WARN(log, "%s", "Not enough fields for syscall info.");
+ return 1;
+ }
+
+ temp = (time_t) atol(fields[0]);
+ avc->tm_stmp_sec = temp;
+ avc->tm_stmp_nano = atoi(fields[1]);
+ avc->serial = atoi(fields[2]);
+
+ if (msg->date_stamp == NULL) {
+ if ((msg->date_stamp = (struct tm *)malloc(sizeof(struct tm))) == NULL) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ }
+ localtime_r(&temp, msg->date_stamp);
+ return 0;
+}
+
+static int avc_msg_insert_access_type(const seaudit_log_t * log, const char *token, seaudit_avc_message_t * avc)
+{
+ if (strcmp(token, "granted") == 0) {
+ avc->msg = SEAUDIT_AVC_GRANTED;
+ return 0;
+ } else if (strcmp(token, "denied") == 0) {
+ avc->msg = SEAUDIT_AVC_DENIED;
+ return 0;
+ }
+ WARN(log, "%s", "No AVC message type found, assuming it was a denial.");
+ avc->msg = SEAUDIT_AVC_DENIED;
+ return 1;
+}
+
+static int avc_msg_insert_scon(seaudit_log_t * log, seaudit_avc_message_t * avc, char *tmp)
+{
+ char *user, *role, *type, *mls_lvl, *mls_clr;
+ int retval;
+ if (tmp == NULL) {
+ WARN(log, "%s", "Invalid source context.");
+ return 1;
+ }
+ retval = parse_context(log, tmp, &user, &role, &type, &mls_lvl, &mls_clr);
+ if (retval != 0) {
+ return retval;
+ }
+ avc->suser = user;
+ avc->srole = role;
+ avc->stype = type;
+ avc->smls_lvl = mls_lvl;
+ avc->smls_clr = mls_clr;
+ return 0;
+}
+
+static int avc_msg_insert_tcon(seaudit_log_t * log, seaudit_avc_message_t * avc, char *tmp)
+{
+ char *user, *role, *type, *mls_lvl, *mls_clr;
+ int retval;
+ if (tmp == NULL) {
+ WARN(log, "%s", "Invalid target context.");
+ return 1;
+ }
+ retval = parse_context(log, tmp, &user, &role, &type, &mls_lvl, &mls_clr);
+ if (retval != 0) {
+ return retval;
+ }
+ avc->tuser = user;
+ avc->trole = role;
+ avc->ttype = type;
+ avc->tmls_lvl = mls_lvl;
+ avc->tmls_clr = mls_clr;
+ return 0;
+}
+
+static int avc_msg_insert_tclass(seaudit_log_t * log, seaudit_avc_message_t * avc, const char *tmp)
+{
+ char *tclass;
+ if ((tclass = strdup(tmp)) == NULL || apol_bst_insert_and_get(log->classes, (void **)&tclass, NULL) < 0) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ avc->tclass = tclass;
+ return 0;
+}
+
+static int avc_msg_insert_string(const seaudit_log_t * log, char *src, char **dest)
+{
+ if ((*dest = strdup(src)) == NULL) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Removes quotes from a string, this is currently to remove quotes
+ * from the command argument.
+ */
+static int avc_msg_remove_quotes_insert_string(const seaudit_log_t * log, char *src, char **dest)
+{
+ size_t i, j, l;
+
+ l = strlen(src);
+ /* see if there are any quotes to begin with if there aren't
+ * just run insert string */
+ if (src[0] == '\"' && l > 0 && src[l - 1] == '\"') {
+ if ((*dest = calloc(1, l + 1)) == NULL) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ for (i = 0, j = 0; i < l; i++) {
+ if (src[i] != '\"') {
+ (*dest)[j] = src[i];
+ j++;
+ }
+ }
+ return 0;
+ } else
+ return avc_msg_insert_string(log, src, dest);
+}
+
+/**
+ * If there is exactly one equal sign in orig_token then return 1.
+ * Otherwise return 0.
+ */
+static int avc_msg_is_valid_additional_field(const char *orig_token)
+{
+ char *first_eq = strchr(orig_token, '=');
+
+ if (first_eq == NULL) {
+ return 0;
+ }
+ if (strchr(first_eq + 1, '=') != NULL) {
+ return 0;
+ }
+ return 1;
+}
+
+static int avc_msg_reformat_path(const seaudit_log_t * log, seaudit_avc_message_t * avc, const char *token)
+{
+ int error;
+ if (avc->path == NULL) {
+ if ((avc->path = strdup(token)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ } else {
+ size_t len = strlen(avc->path) + strlen(token) + 2;
+ char *s = realloc(avc->path, len);
+ if (s == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ avc->path = s;
+ strcat(avc->path, " ");
+ strcat(avc->path, token);
+ }
+ return 0;
+}
+
+/**
+ * Parse the remaining tokens of an AVC message, filling as much
+ * information as possible.
+ *
+ * @return 0 on success, > 0 if warnings, < 0 on error
+ */
+static int avc_msg_insert_additional_field_data(seaudit_log_t * log, apol_vector_t * tokens, seaudit_avc_message_t * avc,
+ size_t * position)
+{
+ char *token, *v;
+ int retval, has_warnings = 0;
+
+ avc->avc_type = SEAUDIT_AVC_DATA_FS;
+ for (; (*position) < apol_vector_get_size(tokens); (*position)++) {
+ token = apol_vector_get_element(tokens, (*position));
+ v = NULL;
+ if (strcmp(token, "") == 0) {
+ break;
+ }
+
+ if (!avc->is_pid && avc_msg_is_prefix(token, "pid=", &v)) {
+ avc->pid = atoi(v);
+ avc->is_pid = 1;
+ continue;
+ }
+
+ if (!avc->exe && avc_msg_is_prefix(token, "exe=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->exe) < 0) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->comm && avc_msg_is_prefix(token, "comm=", &v)) {
+ if (avc_msg_remove_quotes_insert_string(log, v, &avc->comm) < 0) {
+ return -1;
+ }
+ continue;
+ }
+
+ /* Gather all tokens located after the path=XXXX token
+ * until we encounter a valid additional field. This
+ * is because a path name file name may be seperated
+ * by whitespace. Look ahead at the next token, but we
+ * make sure not to access memory beyond the total
+ * number of tokens. */
+ if (!avc->path && avc_msg_is_prefix(token, "path=", &v)) {
+ if (avc_msg_reformat_path(log, avc, v) < 0) {
+ return -1;
+ }
+ while (*position + 1 < apol_vector_get_size(tokens)) {
+ token = apol_vector_get_element(tokens, *position + 1);
+ if (avc_msg_is_valid_additional_field(token)) {
+ break;
+ }
+ (*position)++;
+ if (avc_msg_reformat_path(log, avc, token) < 0) {
+ return -1;
+ }
+ }
+ continue;
+ }
+
+ if (!avc->name && avc_msg_is_prefix(token, "name=", &v)) {
+ if (avc_msg_remove_quotes_insert_string(log, v, &avc->name) < 0) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->dev && avc_msg_is_prefix(token, "dev=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->dev) < 0) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->saddr && avc_msg_is_prefix(token, "saddr=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->saddr) < 0) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->source && (avc_msg_is_prefix(token, "source=", &v) || avc_msg_is_prefix(token, "src=", &v))) {
+ avc->source = atoi(v);
+ continue;
+ }
+
+ if (!avc->daddr && avc_msg_is_prefix(token, "daddr=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->daddr)) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->dest && avc_msg_is_prefix(token, "dest=", &v)) {
+ avc->dest = atoi(v);
+ continue;
+ }
+
+ if (!avc->netif && avc_msg_is_prefix(token, "netif=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->netif)) {
+ return -1;
+ }
+ avc->avc_type = SEAUDIT_AVC_DATA_NET;
+ continue;
+ }
+
+ if (!avc->laddr && avc_msg_is_prefix(token, "laddr=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->laddr)) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->lport && avc_msg_is_prefix(token, "lport=", &v)) {
+ avc->lport = atoi(v);
+ avc->avc_type = SEAUDIT_AVC_DATA_NET;
+ continue;
+ }
+
+ if (!avc->faddr && avc_msg_is_prefix(token, "faddr=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->faddr)) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->fport && avc_msg_is_prefix(token, "fport=", &v)) {
+ avc->fport = atoi(v);
+ continue;
+ }
+
+ if (!avc->port && avc_msg_is_prefix(token, "port=", &v)) {
+ avc->port = atoi(v);
+ avc->avc_type = SEAUDIT_AVC_DATA_NET;
+ continue;
+ }
+
+ if (!avc->is_src_sid && avc_msg_is_prefix(token, "ssid=", &v)) {
+ avc->src_sid = (unsigned int)strtoul(v, NULL, 10);
+ avc->is_src_sid = 1;
+ continue;
+ }
+
+ if (!avc->is_tgt_sid && avc_msg_is_prefix(token, "tsid=", &v)) {
+ avc->tgt_sid = (unsigned int)strtoul(v, NULL, 10);
+ avc->is_tgt_sid = 1;
+ continue;
+ }
+
+ if (!avc->is_capability && avc_msg_is_prefix(token, "capability=", &v)) {
+ avc->capability = atoi(v);
+ avc->is_capability = 1;
+ avc->avc_type = SEAUDIT_AVC_DATA_CAP;
+ continue;
+ }
+
+ if (!avc->is_key && avc_msg_is_prefix(token, "key=", &v)) {
+ avc->key = atoi(v);
+ avc->is_key = 1;
+ avc->avc_type = SEAUDIT_AVC_DATA_IPC;
+ continue;
+ }
+
+ if (!avc->is_inode && avc_msg_is_prefix(token, "ino=", &v)) {
+ avc->inode = strtoul(v, NULL, 10);
+ avc->is_inode = 1;
+ continue;
+ }
+
+ if (!avc->ipaddr && avc_msg_is_prefix(token, "ipaddr=", &v)) {
+ if (avc_msg_insert_string(log, v, &avc->ipaddr)) {
+ return -1;
+ }
+ continue;
+ }
+
+ if (!avc->suser && avc_msg_is_prefix(token, "scontext=", &v)) {
+ retval = avc_msg_insert_scon(log, avc, v);
+ if (retval < 0) {
+ return retval;
+ } else if (retval > 0) {
+ has_warnings = 1;
+ }
+ continue;
+ }
+
+ if (!avc->tuser && avc_msg_is_prefix(token, "tcontext=", &v)) {
+ retval = avc_msg_insert_tcon(log, avc, v);
+ if (retval < 0) {
+ return retval;
+ } else if (retval > 0) {
+ has_warnings = 1;
+ }
+ continue;
+ }
+
+ if (!avc->tclass && avc_msg_is_prefix(token, "tclass=", &v)) {
+ if (avc_msg_insert_tclass(log, avc, v) < 0) {
+ return -1;
+ }
+ continue;
+ }
+ /* found a field that this parser did not understand,
+ * so flag the entire message as a warning */
+ has_warnings = 1;
+ }
+
+ /* can't have both a sid and a context */
+ if ((avc->is_src_sid && avc->suser) || (avc->is_tgt_sid && avc->tuser)) {
+ has_warnings = 1;
+ }
+
+ if (!avc->tclass) {
+ has_warnings = 1;
+ }
+
+ if (has_warnings) {
+ avc->avc_type = SEAUDIT_AVC_DATA_MALFORMED;
+ }
+
+ return has_warnings;
+}
+
+static int avc_parse(seaudit_log_t * log, apol_vector_t * tokens)
+{
+ seaudit_message_t *msg;
+ seaudit_avc_message_t *avc;
+ seaudit_message_type_e type;
+ int ret, has_warnings = 0;
+ size_t position = 0, num_tokens = apol_vector_get_size(tokens);
+ char *token, *t;
+
+ if ((msg = message_create(log, SEAUDIT_MESSAGE_TYPE_AVC)) == NULL) {
+ return -1;
+ }
+ avc = seaudit_message_get_data(msg, &type);
+
+ token = apol_vector_get_element(tokens, position);
+
+ /* Check for new auditd log format */
+ if (strstr(token, AUDITD_MSG)) {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for audit header.");
+ return 1;
+ }
+ log->logtype = SEAUDIT_LOG_TYPE_AUDITD;
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ /* Insert the audit header if it exists */
+ if (avc_msg_is_token_new_audit_header(token)) {
+ ret = avc_msg_insert_syscall_info(log, token, msg, avc);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+ } else {
+ ret = insert_standard_msg_header(log, tokens, &position, msg);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ }
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+
+ /* for now, only let avc messages set their object
+ * manager */
+ if ((t = strrchr(token, ':')) == NULL) {
+ WARN(log, "%s", "Expected to find an object manager here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ *t = '\0';
+ if ((ret = insert_manager(log, msg, token)) < 0) {
+ return ret;
+ }
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ /* new style audit messages can show up in syslog
+ * files starting with FC5. This means that both the
+ * old kernel: header and the new audit header might
+ * be present. So, here we check again for the audit
+ * message.
+ */
+ if (avc_msg_is_token_new_audit_header(token)) {
+ ret = avc_msg_insert_syscall_info(log, token, msg, avc);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+ }
+ }
+
+ /* Make sure the following token is the string "avc:" */
+ if (strcmp(token, "avc:") != 0) {
+ /* Hold the position */
+ has_warnings = 1;
+ WARN(log, "%s", "Expected an avc: token here.");
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ /* Insert denied or granted */
+ if (avc_msg_insert_access_type(log, token, avc)) {
+ has_warnings = 1;
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ /* Insert perm(s) */
+ ret = avc_msg_insert_perms(log, tokens, &position, avc);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ }
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Message appears to be truncated.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+
+ if (strcmp(token, "for") != 0) {
+ /* Hold the position */
+ has_warnings = 1;
+ WARN(log, "%s", "Expected a 'for' token here.");
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for new audit header.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ /* At this point we have a valid message, for we have gathered
+ * all of the standard fields so insert anything else. If
+ * nothing else is left, the message is still considered
+ * valid. */
+ ret = avc_msg_insert_additional_field_data(log, tokens, avc, &position);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ }
+
+ return has_warnings;
+}
+
+/******************** boolean parsing ********************/
+
+static int boolean_msg_insert_bool(seaudit_log_t * log, seaudit_bool_message_t * boolm, char *token)
+{
+ size_t len = strlen(token);
+ int value;
+
+ /* Strip off ending comma */
+ if (token[len - 1] == ',') {
+ token[len - 1] = '\0';
+ len--;
+ }
+
+ if (token[len - 2] != ':') {
+ WARN(log, "%s", "Boolean change was not in correct format.");
+ return 1;
+ }
+
+ if (token[len - 1] == '0')
+ value = 0;
+ else if (token[len - 1] == '1')
+ value = 1;
+ else {
+ WARN(log, "%s", "Invalid new boolean value.");
+ return 1;
+ }
+
+ token[len - 2] = '\0';
+
+ return bool_change_append(log, boolm, token, value);
+}
+
+static int bool_parse(seaudit_log_t * log, apol_vector_t * tokens)
+{
+ seaudit_message_t *msg;
+ seaudit_bool_message_t *boolm;
+ seaudit_message_type_e type;
+ int ret, has_warnings = 0, next_line = log->next_line;
+ size_t position = 0, num_tokens = apol_vector_get_size(tokens);
+ char *token;
+
+ if (log->next_line) {
+ /* still processing a boolean change message, so don't
+ * create a new one */
+ size_t num_messages = apol_vector_get_size(log->messages);
+ assert(num_messages > 0);
+ msg = apol_vector_get_element(log->messages, num_messages - 1);
+ log->next_line = 0;
+ } else {
+ if ((msg = message_create(log, SEAUDIT_MESSAGE_TYPE_BOOL)) == NULL) {
+ return -1;
+ }
+ }
+ boolm = seaudit_message_get_data(msg, &type);
+
+ ret = insert_standard_msg_header(log, tokens, &position, msg);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ }
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for boolean change.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+
+ /* Make sure the following token is the string "kernel:" */
+ if (!strstr(token, "kernel:")) {
+ WARN(log, "%s", "Expected to see kernel here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ if ((ret = insert_manager(log, msg, "kernel")) < 0) {
+ return ret;
+ }
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for boolean change.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ if (!next_line) {
+ if (!strstr(token, "security:")) {
+ WARN(log, "%s", "Expected to see security here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for boolean change.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ if (!strstr(token, "committed")) {
+ WARN(log, "%s", "Expected to see committed here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for boolean change.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ if (!strstr(token, "booleans")) {
+ WARN(log, "%s", "Expected to see booleans here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for boolean change.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ if (!strstr(token, "{")) {
+ WARN(log, "%s", "Expected to see '{' here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for boolean change.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+ }
+
+ /* keep parsing until a closing brace is found. if end of
+ * tokens is reached, then keep parsing the next line */
+ while (position < num_tokens) {
+ token = apol_vector_get_element(tokens, position);
+ position++;
+
+ if (!strcmp(token, "}")) {
+ if (position < num_tokens) {
+ WARN(log, "%s", "Excess tokens after closing brace");
+ has_warnings = 1;
+ }
+ return has_warnings;
+ }
+
+ ret = boolean_msg_insert_bool(log, boolm, token);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ }
+ }
+
+ /* did not find a closing brace yet */
+ log->next_line = 1;
+ return has_warnings;
+}
+
+/******************** policy load parsing ********************/
+
+/**
+ * Determine if a series of tokens represents the older style of a
+ * policy load.
+ *
+ * @return 0 if not older style, 1 if it is the older style, < 0 on
+ * error. If it is the older style, then increment reference pointer
+ * position to point to the next unprocessed token.
+ */
+static int load_policy_msg_is_old_load_policy_string(const seaudit_log_t * log, const apol_vector_t * tokens, size_t * position)
+{
+ size_t i, length = 0;
+ int rt;
+ char *tmp = NULL;
+ if (*position + 4 >= apol_vector_get_size(tokens)) {
+ return 0;
+ }
+
+ for (i = 0; i < 4; i++) {
+ length += strlen((char *)apol_vector_get_element(tokens, i + *position));
+ }
+
+ if ((tmp = (char *)calloc(length + 1, sizeof(char))) == NULL) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ for (i = 0; i < 4; i++) {
+ strcat(tmp, (char *)apol_vector_get_element(tokens, i + *position));
+ }
+
+ rt = strcmp(tmp, OLD_LOAD_POLICY_STRING);
+ free(tmp);
+
+ if (rt == 0) {
+ *position += 4;
+ return 1;
+ } else
+ return 0;
+}
+
+static void load_policy_msg_get_policy_components(seaudit_load_message_t * load, const apol_vector_t * tokens, size_t * position)
+{
+ char *arg = apol_vector_get_element(tokens, *position);
+ char *endptr;
+ unsigned int val = (unsigned int)strtoul(arg, &endptr, 10);
+ if (*endptr != '\0') {
+ /* found a key-value pair where the key is not a
+ * number, so skip this */
+ (*position)++;
+ return;
+ }
+ char *id = apol_vector_get_element(tokens, *position + 1);
+ assert(id != NULL && arg != NULL);
+ if (load->classes == 0 && strstr(id, "classes")) {
+ load->classes = val;
+ } else if (load->rules == 0 && strstr(id, "rules")) {
+ load->rules = val;
+ } else if (load->users == 0 && strstr(id, "users")) {
+ load->users = val;
+ } else if (load->roles == 0 && strstr(id, "roles")) {
+ load->roles = val;
+ } else if (load->types == 0 && strstr(id, "types")) {
+ load->types = val;
+ } else if (load->bools == 0 && strstr(id, "bools")) {
+ load->bools = val;
+ }
+ *position += 2;
+}
+
+static int load_parse(seaudit_log_t * log, const apol_vector_t * tokens)
+{
+ seaudit_message_t *msg;
+ seaudit_load_message_t *load;
+ seaudit_message_type_e type;
+ int ret, error, has_warnings = 0;
+ size_t position = 0, num_tokens = apol_vector_get_size(tokens);
+ char *token;
+
+ if (log->next_line) {
+ /* still processing a load message, so don't create a
+ * new one */
+ size_t num_messages = apol_vector_get_size(log->messages);
+ assert(num_messages > 0);
+ msg = apol_vector_get_element(log->messages, num_messages - 1);
+ log->next_line = 0;
+ } else {
+ if ((msg = message_create(log, SEAUDIT_MESSAGE_TYPE_LOAD)) == NULL) {
+ return -1;
+ }
+ }
+ load = seaudit_message_get_data(msg, &type);
+
+ ret = insert_standard_msg_header(log, tokens, &position, msg);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ has_warnings = 1;
+ }
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for policy load.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+
+ if (strcmp(token, "invalidating") == 0) {
+ WARN(log, "%s", "Got an unexpected invalidating message.");
+ return 1;
+ }
+
+ if (position + 1 >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for policy load.");
+ return 1;
+ }
+ if (strcmp((char *)apol_vector_get_element(tokens, position + 1), "bools") == 0) {
+ WARN(log, "%s", "Got an unexpected bools message.");
+ return 1;
+ }
+
+ /* Check the following token for the string "kernel:" */
+ if (!strstr(token, "kernel:")) {
+ WARN(log, "%s", "Expected to see kernel here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ if ((ret = insert_manager(log, msg, "kernel")) < 0) {
+ return ret;
+ }
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for policy load.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ if (strcmp(token, "security:")) {
+ WARN(log, "%s", "Expected to see security here.");
+ has_warnings = 1;
+ /* Hold the position */
+ } else {
+ position++;
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for policy load.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ }
+
+ ret = load_policy_msg_is_old_load_policy_string(log, tokens, &position);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ if (position >= num_tokens) {
+ WARN(log, "%s", "Not enough tokens for policy load.");
+ return 1;
+ }
+ token = apol_vector_get_element(tokens, position);
+ if ((load->binary = strdup(token)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ log->next_line = 1;
+ } else {
+ while (position < num_tokens) {
+ load_policy_msg_get_policy_components(load, tokens, &position);
+ }
+ /* Check to see if we have gathered ALL policy
+ * components. If not, we need to load the next
+ * line. */
+ if (load->classes == 0 || load->rules == 0 || load->users == 0 || load->roles == 0 || load->types == 0) {
+ log->next_line = 1;
+ }
+ }
+ return has_warnings;
+}
+
+/**
+ * Parse a single nul-terminated line from an selinux audit log.
+ */
+static int seaudit_log_parse_line(seaudit_log_t * log, char *line)
+{
+ char *orig_line = NULL;
+ seaudit_message_t *prev_message;
+ seaudit_message_type_e is_sel, prev_message_type;
+ apol_vector_t *tokens = NULL;
+ int retval = -1, retval2, has_warnings = 0, error = 0;
+
+ is_sel = is_selinux(line);
+ if (log->next_line) {
+ prev_message = apol_vector_get_element(log->messages, apol_vector_get_size(log->messages) - 1);
+ seaudit_message_get_data(prev_message, &prev_message_type);
+ if (!(is_sel == SEAUDIT_MESSAGE_TYPE_INVALID && prev_message_type == SEAUDIT_MESSAGE_TYPE_BOOL) &&
+ !(is_sel == SEAUDIT_MESSAGE_TYPE_LOAD && prev_message_type == SEAUDIT_MESSAGE_TYPE_LOAD)) {
+ WARN(log, "%s", "Parser was in the middle of a line, but next message was not the correct format.");
+ has_warnings = 1;
+ log->next_line = 0;
+ } else {
+ is_sel = prev_message_type;
+ }
+ }
+ if (is_sel == SEAUDIT_MESSAGE_TYPE_INVALID) {
+ /* unknown line, so ignore it */
+ return 0;
+ }
+
+ if ((orig_line = strdup(line)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (get_tokens(log, line, &tokens) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+
+ switch (is_sel) {
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ retval2 = avc_parse(log, tokens);
+ break;
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ retval2 = bool_parse(log, tokens);
+ break;
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ retval2 = load_parse(log, tokens);
+ break;
+ default:
+ /* should never get here */
+ assert(0);
+ errno = EINVAL;
+ retval2 = -1;
+ }
+ if (retval2 < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ } else if (retval2 > 0) {
+ if (apol_vector_append(log->malformed_msgs, orig_line) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ orig_line = NULL;
+ has_warnings = 1;
+ }
+
+ retval = 0;
+ cleanup:
+ free(orig_line);
+ apol_vector_destroy(&tokens);
+ if (retval < 0) {
+ errno = error;
+ return -1;
+ }
+ return has_warnings;
+}
+
+/******************** public functions below ********************/
+
+int seaudit_log_parse(seaudit_log_t * log, FILE * syslog)
+{
+ FILE *audit_file = syslog;
+ char *line = NULL;
+ int retval = -1, retval2, has_warnings = 0, error = 0;
+ size_t line_size = 0, i;
+
+ if (log == NULL || syslog == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ error = EINVAL;
+ goto cleanup;
+ }
+
+ if (!log->tz_initialized) {
+ tzset();
+ log->tz_initialized = 1;
+ }
+
+ clearerr(audit_file);
+
+ while (1) {
+ if (getline(&line, &line_size, audit_file) < 0) {
+ error = errno;
+ if (!feof(audit_file)) {
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ break;
+ }
+ apol_str_trim(line);
+ retval2 = seaudit_log_parse_line(log, line);
+ if (retval2 < 0) {
+ error = errno;
+ goto cleanup;
+ } else if (retval2 > 0) {
+ has_warnings = 1;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ free(line);
+ for (i = 0; i < apol_vector_get_size(log->models); i++) {
+ seaudit_model_t *m = apol_vector_get_element(log->models, i);
+ model_notify_log_changed(m, log);
+ }
+ if (retval < 0) {
+ errno = error;
+ return -1;
+ }
+ if (has_warnings) {
+ WARN(log, "%s", "Audit log was parsed, but there were one or more invalid message found within it.");
+ }
+ return has_warnings;
+}
+
+int seaudit_log_parse_buffer(seaudit_log_t * log, const char *buffer, const size_t bufsize)
+{
+ const char *s;
+ char *line = NULL, *l;
+ int retval = -1, retval2, has_warnings = 0, error = 0;
+ size_t offset = 0, line_size, i;
+
+ if (log == NULL || buffer == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ error = EINVAL;
+ goto cleanup;
+ }
+
+ if (!log->tz_initialized) {
+ tzset();
+ log->tz_initialized = 1;
+ }
+
+ while (offset < bufsize) {
+ /* create a new string up to the first newline or end of
+ * buffer, whichever comes first */
+ for (s = buffer + offset; s < buffer + bufsize && *s != '\n'; s++) ;
+ line_size = s - (buffer + offset);
+ assert(line_size > 0);
+ if ((l = realloc(line, line_size + 1)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ line = l;
+ memcpy(line, buffer + offset, line_size);
+ line[line_size] = '\0';
+ offset += line_size;
+ if (s < buffer + bufsize) {
+ /* this branch can only be true if not at end of file */
+ assert(*s == '\n');
+ offset++;
+ }
+ apol_str_trim(line);
+ retval2 = seaudit_log_parse_line(log, line);
+ if (retval2 < 0) {
+ error = errno;
+ goto cleanup;
+ } else if (retval2 > 0) {
+ has_warnings = 1;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ free(line);
+ for (i = 0; i < apol_vector_get_size(log->models); i++) {
+ seaudit_model_t *m = apol_vector_get_element(log->models, i);
+ model_notify_log_changed(m, log);
+ }
+ if (retval < 0) {
+ errno = error;
+ return -1;
+ }
+ if (has_warnings) {
+ WARN(log, "%s", "Audit log was parsed, but there were one or more invalid message found within it.");
+ }
+ return has_warnings;
+}
diff --git a/libseaudit/src/report.c b/libseaudit/src/report.c
new file mode 100644
index 0000000..9b198c5
--- /dev/null
+++ b/libseaudit/src/report.c
@@ -0,0 +1,1060 @@
+/**
+ * @file
+ * Implementation of seaudit report generator.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <seaudit/report.h>
+
+#include <apol/util.h>
+#include <libxml/xmlreader.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define CONFIG_FILE "seaudit-report.conf"
+#define STYLESHEET_FILE "seaudit-report.css"
+#define LINE_MAX 1024
+
+struct seaudit_report
+{
+ /** output format for the report */
+ seaudit_report_format_e format;
+ /** path to configuration file, or NULL to use system configuration */
+ char *config;
+ /** path to HTML stylesheet, or NULL to use system stylesheet */
+ char *stylesheet;
+ /** if non-zero, then use a stylesheet when generating HTML reports */
+ int use_stylesheet;
+ /** if non-zero, then print malformed messages */
+ int malformed;
+ /** model from which messages will be obtained */
+ seaudit_model_t *model;
+};
+
+static const char *seaudit_report_node_names[] = {
+ "seaudit-report",
+ "standard-section",
+ "custom-section",
+ "view",
+ NULL
+};
+
+static const char *seaudit_standard_section_names[] = {
+ "PolicyLoads",
+ "EnforcementToggles",
+ "PolicyBooleans",
+ "Statistics",
+ "AllowListing",
+ "DenyListing",
+ NULL
+};
+
+seaudit_report_t *seaudit_report_create(seaudit_model_t * model)
+{
+ seaudit_report_t *r = calloc(1, sizeof(*r));
+ if (r == NULL) {
+ return NULL;
+ }
+ r->model = model;
+ return r;
+}
+
+void seaudit_report_destroy(seaudit_report_t ** report)
+{
+ if (report == NULL || *report == NULL) {
+ return;
+ }
+ free((*report)->config);
+ free((*report)->stylesheet);
+ free(*report);
+ *report = NULL;
+}
+
+int seaudit_report_set_format(const seaudit_log_t * log, seaudit_report_t * report, seaudit_report_format_e format)
+{
+ if (report == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ report->format = format;
+ return 0;
+}
+
+/**
+ * Set the report's configuration file to the default system file.
+ */
+static int report_set_default_configuration(const seaudit_log_t * log, seaudit_report_t * report)
+{
+ char *config_dir = apol_file_find(CONFIG_FILE);
+ int error;
+
+ if (config_dir == NULL) {
+ error = errno;
+ ERR(log, "%s", "Could not find default configuration file.");
+ errno = error;
+ return -1;
+ }
+ if (asprintf(&report->config, "%s/%s", config_dir, CONFIG_FILE) < 0) {
+ error = errno;
+ report->config = NULL;
+ free(config_dir);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ free(config_dir);
+
+ /* check if can read the file */
+ if (access(report->config, R_OK) != 0) {
+ error = errno;
+ ERR(log, "Could not read default config file %s.", report->config);
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+int seaudit_report_set_configuration(const seaudit_log_t * log, seaudit_report_t * report, const char *file)
+{
+ if (report == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ free(report->config);
+ report->config = NULL;
+ if (file == NULL) {
+ return report_set_default_configuration(log, report);
+ } else {
+ if ((report->config = strdup(file)) == NULL) {
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ return 0;
+ }
+}
+
+/**
+ * Set the report's stylesheet to the default system stylesheet.
+ */
+static int report_set_default_stylesheet(const seaudit_log_t * log, seaudit_report_t * report)
+{
+ char *dir = apol_file_find(STYLESHEET_FILE);
+ int error;
+ if (dir == NULL) {
+ error = errno;
+ ERR(log, "%s", "Could not find default stylesheet.");
+ errno = error;
+ return -1;
+ }
+
+ if (asprintf(&report->stylesheet, "%s/%s", dir, STYLESHEET_FILE) < 0) {
+ error = errno;
+ report->stylesheet = NULL;
+ free(dir);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ free(dir);
+
+ return 0;
+}
+
+int seaudit_report_set_stylesheet(const seaudit_log_t * log, seaudit_report_t * report, const char *file, const int use_stylesheet)
+{
+ if (report == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ free(report->stylesheet);
+ report->stylesheet = NULL;
+ report->use_stylesheet = use_stylesheet;
+ if (file == NULL) {
+ return report_set_default_stylesheet(log, report);
+ } else {
+ if ((report->stylesheet = strdup(file)) == NULL) {
+ return -1;
+ int error = errno;
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ return 0;
+ }
+}
+
+int seaudit_report_set_malformed(const seaudit_log_t * log, seaudit_report_t * report, const int do_malformed)
+{
+ if (report == NULL) {
+ ERR(log, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ report->malformed = do_malformed;
+ return 0;
+}
+
+/**
+ * Insert the contents of the stylesheet into the output file. If it
+ * is not readable then generate a warning. This is not an error
+ * because the stylesheet is not strictly necessary.
+ */
+static int report_import_html_stylesheet(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ char line[LINE_MAX], *line_ptr = NULL;
+ FILE *fp;
+
+ if (report->use_stylesheet) {
+ fp = fopen(report->stylesheet, "r");
+ if (fp == NULL) {
+ WARN(log, "Cannot open stylesheet file %s.", report->stylesheet);
+ return 1;
+ }
+ fprintf(outfile, "<style type=\"text/css\">\n");
+
+ while (fgets(line, LINE_MAX, fp) != NULL) {
+ free(line_ptr);
+ line_ptr = NULL;
+ if ((line_ptr = strdup(line)) == NULL) {
+ int error = errno;
+ free(line_ptr);
+ fclose(fp);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ apol_str_trim(line_ptr);
+ if (line_ptr[0] == '#' || apol_str_is_only_white_space(line_ptr))
+ continue;
+ fprintf(outfile, "%s\n", line_ptr);
+ }
+ fprintf(outfile, "</style>\n");
+ fclose(fp);
+ free(line_ptr);
+ }
+ return 0;
+}
+
+static int report_print_header(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ time_t ltime;
+
+ time(&ltime);
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n");
+ fprintf(outfile, "<html>\n<head>\n");
+ if (report_import_html_stylesheet(log, report, outfile) < 0) {
+ return -1;
+ }
+ fprintf(outfile, "<title>seaudit-report</title>\n</head>\n");
+ fprintf(outfile, "<body>\n");
+ fprintf(outfile, "<b class=\"report_date\"># Report generated by seaudit-report on %s</b><br>\n", ctime(&ltime));
+ } else {
+ fprintf(outfile, "# Begin\n\n");
+ fprintf(outfile, "# Report generated by seaudit-report on %s\n", ctime(&ltime));
+ }
+ return 0;
+}
+
+static int report_print_footer(const seaudit_report_t * report, FILE * outfile)
+{
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "</body>\n</html>\n");
+ } else {
+ fprintf(outfile, "# End\n");
+ }
+ return 0;
+}
+
+static int report_is_valid_node_name(const char *name)
+{
+ size_t i;
+ for (i = 0; seaudit_report_node_names[i] != NULL; i++)
+ if (strcmp(seaudit_report_node_names[i], name) == 0)
+ return 1;
+ return 0;
+}
+
+static int report_is_valid_section_name(const char *name)
+{
+ size_t i;
+ for (i = 0; seaudit_standard_section_names[i] != NULL; i++)
+ if (strcmp(seaudit_standard_section_names[i], name) == 0)
+ return 1;
+ return 0;
+}
+
+static int report_parse_seaudit_report(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)),
+ xmlTextReaderPtr reader, xmlChar ** id_value
+ __attribute__ ((unused)), xmlChar ** title_value)
+{
+ int rt, error;
+ xmlChar *name = NULL;
+
+ if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) {
+ /* Parse attributes */
+ rt = xmlTextReaderMoveToNextAttribute(reader);
+ while (rt > 0) {
+ name = xmlTextReaderName(reader);
+ if (name == NULL) {
+ error = errno;
+ ERR(log, "%s", "Attribute name unavailable.");
+ errno = error;
+ return -1;
+ }
+ if (strcmp((char *)name, "title") == 0) {
+ *title_value = xmlTextReaderValue(reader);
+ }
+
+ xmlFree(name);
+ rt = xmlTextReaderMoveToNextAttribute(reader);
+ }
+ if (rt < 0) {
+ error = errno;
+ ERR(log, "%s", "Error parsing attribute for seaudit-report node.");
+ errno = error;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int report_parse_standard_attribs(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)),
+ xmlTextReaderPtr reader, xmlChar ** id_value, xmlChar ** title_value)
+{
+ int rt, error;
+ xmlChar *name = NULL;
+
+ if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) {
+ /* Parse attributes */
+ rt = xmlTextReaderMoveToNextAttribute(reader);
+ while (rt > 0) {
+ name = xmlTextReaderName(reader);
+ if (name == NULL) {
+ error = errno;
+ ERR(log, "%s", "Attribute name unavailable.");
+ errno = error;
+ return -1;
+ }
+ if (strcmp((char *)name, "id") == 0) {
+ *id_value = xmlTextReaderValue(reader);
+ } else if (strcmp((char *)name, "title") == 0) {
+ *title_value = xmlTextReaderValue(reader);
+ }
+ xmlFree(name);
+ rt = xmlTextReaderMoveToNextAttribute(reader);
+ }
+ if (rt < 0) {
+ error = errno;
+ ERR(log, "%s", "Error parsing attribute for standard-section node.");
+ errno = error;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int report_parse_custom_attribs(const seaudit_log_t * log, const seaudit_report_t * report __attribute__ ((unused)),
+ xmlTextReaderPtr reader, xmlChar ** title_value)
+{
+ int rt, error;
+ xmlChar *name = NULL;
+
+ if (xmlTextReaderNodeType(reader) == 1 && xmlTextReaderAttributeCount(reader) > 0) {
+ /* Parse attributes */
+ rt = xmlTextReaderMoveToNextAttribute(reader);
+ while (rt > 0) {
+ name = xmlTextReaderName(reader);
+ if (name == NULL) {
+ error = errno;
+ ERR(log, "%s", "Attribute name unavailable.");
+ errno = error;
+ return -1;
+ }
+ if (strcmp((char *)name, "title") == 0) {
+ *title_value = xmlTextReaderValue(reader);
+ }
+
+ xmlFree(name);
+ rt = xmlTextReaderMoveToNextAttribute(reader);
+ }
+ if (rt < 0) {
+ error = errno;
+ ERR(log, "%s", "Error parsing attribute for custom-section node.");
+ errno = error;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Allocate and return a filter for setenforce toggles. (Actually, it
+ * can't filter on permissions.)
+ */
+static seaudit_filter_t *report_enforce_toggle_filter_create(const seaudit_log_t * log, const seaudit_report_t * report
+ __attribute__ ((unused)))
+{
+ seaudit_filter_t *filter = NULL;
+ apol_vector_t *type_v = NULL, *class_v;
+ int retval = -1, error = 0;
+ char *tgt_type = "security_t";
+ char *obj_class = "security";
+
+ if ((filter = seaudit_filter_create(NULL)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ if ((type_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(type_v, tgt_type) < 0 || seaudit_filter_set_target_type(filter, type_v) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ if ((class_v = apol_vector_create_with_capacity(1, NULL)) == NULL ||
+ apol_vector_append(class_v, obj_class) < 0 || seaudit_filter_set_target_class(filter, class_v) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&type_v);
+ apol_vector_destroy(&class_v);
+ if (retval != 0) {
+ seaudit_filter_destroy(&filter);
+ errno = error;
+ return NULL;
+ }
+ return filter;
+}
+
+static int report_print_enforce_toggles(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ seaudit_filter_t *filter = NULL;
+ seaudit_model_t *dup_model = NULL;
+ size_t i, j, num_setenforce = 0;
+ apol_vector_t *v = NULL;
+ seaudit_message_t *msg;
+ seaudit_avc_message_t *avc;
+ seaudit_message_type_e type;
+ char *s;
+ char *perm = "setenforce";
+ int retval = -1, error = 0;
+
+ if ((filter = report_enforce_toggle_filter_create(log, report)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((dup_model = seaudit_model_create_from_model(report->model)) == NULL ||
+ seaudit_model_append_filter(dup_model, filter) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ filter = NULL;
+ /* Loop through and get the number of avc allow messages with
+ * the setenforce permission. */
+ v = seaudit_model_get_messages(log, dup_model);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ msg = apol_vector_get_element(v, i);
+ avc = seaudit_message_get_data(msg, &type);
+ if (type != SEAUDIT_MESSAGE_TYPE_AVC || avc->msg == SEAUDIT_AVC_DENIED)
+ continue;
+ if (apol_vector_get_index(avc->perms, perm, apol_str_strcmp, NULL, &j) == 0) {
+ /* Increment number of setenforce messages */
+ num_setenforce++;
+ }
+ }
+
+ /* Since we cannot filter by setenforce permission within the
+ * view, we do so manually within the following for loop. */
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile,
+ "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
+ num_setenforce);
+ else
+ fprintf(outfile, "Number of messages: %zd\n\n", num_setenforce);
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ msg = apol_vector_get_element(v, i);
+ avc = seaudit_message_get_data(msg, &type);
+ if (type != SEAUDIT_MESSAGE_TYPE_AVC ||
+ avc->msg == SEAUDIT_AVC_DENIED || apol_vector_get_index(avc->perms, perm, apol_str_strcmp, NULL, &j) < 0) {
+ continue;
+ }
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ s = seaudit_message_to_string_html(msg);
+ } else {
+ s = seaudit_message_to_string(msg);
+ }
+ if (s == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ fputs(s, outfile);
+ fputc('\n', outfile);
+ free(s);
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ seaudit_filter_destroy(&filter);
+ seaudit_model_destroy(&dup_model);
+ if (error != 0) {
+ errno = error;
+ }
+ return retval;
+}
+
+static int report_print_policy_booleans(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ size_t i, num = seaudit_model_get_num_bools(log, report->model);
+ apol_vector_t *v = seaudit_model_get_messages(log, report->model);
+ seaudit_message_t *m;
+ seaudit_message_type_e type;
+ char *s;
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile,
+ "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
+ num);
+ else
+ fprintf(outfile, "Number of messages: %zd\n\n", num);
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ m = apol_vector_get_element(v, i);
+ seaudit_message_get_data(m, &type);
+ if (type == SEAUDIT_MESSAGE_TYPE_BOOL) {
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ s = seaudit_message_to_string_html(m);
+ } else {
+ s = seaudit_message_to_string(m);
+ }
+ if (s == NULL) {
+ int error = errno;
+ apol_vector_destroy(&v);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ fputs(s, outfile);
+ fputc('\n', outfile);
+ free(s);
+ }
+ }
+ apol_vector_destroy(&v);
+ return 0;
+}
+
+static int report_print_policy_loads(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ size_t i, num = seaudit_model_get_num_loads(log, report->model);
+ apol_vector_t *v = seaudit_model_get_messages(log, report->model);
+ seaudit_message_t *m;
+ seaudit_message_type_e type;
+ char *s;
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile,
+ "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
+ num);
+ else
+ fprintf(outfile, "Number of messages: %zd\n\n", num);
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ m = apol_vector_get_element(v, i);
+ seaudit_message_get_data(m, &type);
+ if (type == SEAUDIT_MESSAGE_TYPE_LOAD) {
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ s = seaudit_message_to_string_html(m);
+ } else {
+ s = seaudit_message_to_string(m);
+ }
+ if (s == NULL) {
+ int error = errno;
+ apol_vector_destroy(&v);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ fputs(s, outfile);
+ fputc('\n', outfile);
+ free(s);
+ }
+ }
+ apol_vector_destroy(&v);
+ return 0;
+}
+
+static int report_print_avc_listing(const seaudit_log_t * log, const seaudit_report_t * report, seaudit_avc_message_type_e avc_type,
+ FILE * outfile)
+{
+ size_t i, num;
+ apol_vector_t *v = seaudit_model_get_messages(log, report->model);
+ seaudit_message_t *m;
+ seaudit_avc_message_t *avc;
+ seaudit_message_type_e type;
+ char *s;
+ if (avc_type == SEAUDIT_AVC_GRANTED) {
+ num = seaudit_model_get_num_allows(log, report->model);
+ } else {
+ num = seaudit_model_get_num_denies(log, report->model);
+ }
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile,
+ "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
+ num);
+ else
+ fprintf(outfile, "Number of messages: %zd\n\n", num);
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ m = apol_vector_get_element(v, i);
+ avc = seaudit_message_get_data(m, &type);
+ if (type == SEAUDIT_MESSAGE_TYPE_AVC && avc->msg == avc_type) {
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ s = seaudit_message_to_string_html(m);
+ } else {
+ s = seaudit_message_to_string(m);
+ }
+ if (s == NULL) {
+ int error = errno;
+ apol_vector_destroy(&v);
+ ERR(log, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ fputs(s, outfile);
+ fputc('\n', outfile);
+ free(s);
+ }
+ }
+ apol_vector_destroy(&v);
+ return 0;
+}
+
+static int report_print_stats(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ apol_vector_t *v = seaudit_model_get_messages(log, report->model);
+ size_t num_messages = apol_vector_get_size(v);
+ apol_vector_destroy(&v);
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile,
+ "<font class=\"stats_label\">Number of total messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
+ num_messages);
+ fprintf(outfile,
+ "<font class=\"stats_label\">Number of policy load messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
+ seaudit_model_get_num_loads(log, report->model));
+ fprintf(outfile,
+ "<font class=\"stats_label\">Number of policy boolean messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
+ seaudit_model_get_num_bools(log, report->model));
+ fprintf(outfile,
+ "<font class=\"stats_label\">Number of allow messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
+ seaudit_model_get_num_allows(log, report->model));
+ fprintf(outfile,
+ "<font class=\"stats_label\">Number of denied messages:</font> <b class=\"stats_count\">%zd</b><br>\n",
+ seaudit_model_get_num_denies(log, report->model));
+ } else {
+ fprintf(outfile, "Number of total messages: %zd\n", num_messages);
+ fprintf(outfile, "Number of policy load messages: %zd\n", seaudit_model_get_num_loads(log, report->model));
+ fprintf(outfile, "Number of policy boolean messages: %zd\n", seaudit_model_get_num_bools(log, report->model));
+ fprintf(outfile, "Number of allow messages: %zd\n", seaudit_model_get_num_allows(log, report->model));
+ fprintf(outfile, "Number of denied messages: %zd\n", seaudit_model_get_num_denies(log, report->model));
+ }
+ return 0;
+}
+
+static int report_print_standard_section(const seaudit_log_t * log, const seaudit_report_t * report,
+ xmlChar * id, xmlChar * title, FILE * outfile)
+{
+ size_t sz, len, i;
+ int rt = 0;
+
+ if (!report_is_valid_section_name((char *)id)) {
+ ERR(log, "%s", "Invalid standard section ID.");
+ errno = EINVAL;
+ return -1;
+ }
+ sz = strlen((char *)id);
+ if (title != NULL) {
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "<h2 class=\"standard_section_title\"><u>%s</h2></u>\n", title);
+ } else {
+ fprintf(outfile, "%s\n", title);
+ len = strlen((char *)title);
+ for (i = 0; i < len; i++) {
+ fprintf(outfile, "-");
+ }
+ fprintf(outfile, "\n");
+ }
+ }
+ if (strncasecmp((char *)id, "PolicyLoads", sz) == 0) {
+ rt = report_print_policy_loads(log, report, outfile);
+ } else if (strncasecmp((char *)id, "EnforcementToggles", sz) == 0) {
+ rt = report_print_enforce_toggles(log, report, outfile);
+ } else if (strncasecmp((char *)id, "PolicyBooleans", sz) == 0) {
+ rt = report_print_policy_booleans(log, report, outfile);
+ } else if (strncasecmp((char *)id, "AllowListing", sz) == 0) {
+ rt = report_print_avc_listing(log, report, SEAUDIT_AVC_GRANTED, outfile);
+ } else if (strncasecmp((char *)id, "DenyListing", sz) == 0) {
+ rt = report_print_avc_listing(log, report, SEAUDIT_AVC_DENIED, outfile);
+ } else if (strncasecmp((char *)id, "Statistics", sz) == 0) {
+ rt = report_print_stats(log, report, outfile);
+ }
+ if (rt < 0) {
+ return rt;
+ }
+
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile, "<br>\n");
+ else
+ fprintf(outfile, "\n");
+
+ return 0;
+}
+
+static int report_print_loaded_view(const seaudit_log_t * log, const seaudit_report_t * report, xmlChar * view_filePath,
+ FILE * outfile)
+{
+ size_t i, filters_added = 0;
+ apol_vector_t *loaded_filters = NULL;
+ seaudit_model_t *dup_model = NULL;
+ seaudit_filter_t *filter;
+ seaudit_message_t *msg;
+ char *s;
+ apol_vector_t *v = NULL;
+ int retval = -1, error = 0;
+
+ if ((loaded_filters = seaudit_filter_create_from_file((char *)view_filePath)) == NULL) {
+ error = errno;
+ ERR(log, "Error parsing file %s.", view_filePath);
+ goto cleanup;
+ }
+ if ((dup_model = seaudit_model_create_from_model(report->model)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(loaded_filters); i++, filters_added++) {
+ filter = apol_vector_get_element(loaded_filters, i);
+ if (seaudit_model_append_filter(dup_model, filter) < 0) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ }
+ if ((v = seaudit_model_get_messages(log, dup_model)) == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "View file: %s<br>\n", view_filePath);
+ fprintf(outfile,
+ "<font class=\"message_count_label\">Number of messages:</font> <b class=\"message_count\">%zd</b><br>\n<br>\n",
+ apol_vector_get_size(v));
+ } else {
+ fprintf(outfile, "View file: %s\n", view_filePath);
+ fprintf(outfile, "Number of messages: %zd\n\n", apol_vector_get_size(v));
+ }
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ msg = apol_vector_get_element(v, i);
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ s = seaudit_message_to_string_html(msg);
+ } else {
+ s = seaudit_message_to_string(msg);
+ }
+ if (s == NULL) {
+ error = errno;
+ ERR(log, "%s", strerror(error));
+ goto cleanup;
+ }
+ fputs(s, outfile);
+ fputc('\n', outfile);
+ free(s);
+ }
+ retval = 0;
+ cleanup:
+ /* only destroy filters that were not added to the model
+ * (recall that model takes ownership of filters) */
+ if (loaded_filters != NULL) {
+ for (i = filters_added; i < apol_vector_get_size(loaded_filters); i++) {
+ filter = apol_vector_get_element(loaded_filters, i);
+ seaudit_filter_destroy(&filter);
+ }
+ apol_vector_destroy(&loaded_filters);
+ }
+ seaudit_model_destroy(&dup_model);
+ apol_vector_destroy(&v);
+ if (error != 0) {
+ errno = error;
+ }
+ return retval;
+}
+
+static int report_print_custom_section(const seaudit_log_t * log, const seaudit_report_t * report,
+ xmlTextReaderPtr reader, xmlChar * title, FILE * outfile)
+{
+ size_t len, i;
+ int rt, error = 0, retval = -1, end_of_element = 0;
+ xmlChar *view_filePath = NULL, *name = NULL;
+
+ if (title != NULL) {
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "<h2 class=\"custom_section_title\"><u>%s</h2></u>\n", title);
+ } else {
+ fprintf(outfile, "%s\n", title);
+ len = strlen((char *)title);
+ for (i = 0; i < len; i++) {
+ fprintf(outfile, "-");
+ }
+ fprintf(outfile, "\n");
+ }
+ }
+
+ /* Moves the position of the current instance to the next node
+ * in the stream, which should be a view node */
+ rt = xmlTextReaderRead(reader);
+ while (rt == 1) {
+ /* Read inner child view node(s) */
+ name = xmlTextReaderName(reader);
+ if (name == NULL) {
+ error = errno;
+ ERR(log, "%s", "Unavailable node name within.");
+ goto cleanup;
+ }
+ /* We have reached the end-of-element for the
+ * custom-section node (indicated by 15) */
+ if (strcmp((char *)name, "custom-section") == 0 && xmlTextReaderNodeType(reader) == 15) {
+ xmlFree(name);
+ end_of_element = 1;
+ break;
+ }
+ if (strcmp((char *)name, "view") == 0 && xmlTextReaderNodeType(reader) == 1 && xmlTextReaderHasAttributes(reader)) {
+ view_filePath = xmlTextReaderGetAttribute(reader, (const xmlChar *)"file");
+ if (view_filePath == NULL) {
+ error = errno;
+ ERR(log, "%s", "Error getting file attribute for view node.");
+ goto cleanup;
+ }
+ if (report_print_loaded_view(log, report, view_filePath, outfile) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ xmlFree(view_filePath);
+ }
+ xmlFree(name);
+ rt = xmlTextReaderRead(reader);
+ }
+ if (!end_of_element && rt != 0) {
+ error = EIO;
+ ERR(log, "Error parsing config file %s. (rt:%d)", report->config, rt);
+ goto cleanup;
+ }
+
+ if (!end_of_element) {
+ error = EIO;
+ ERR(log, "%s", "Encountered end of file before finding end of element for custom-section node.");
+ goto cleanup;;
+ }
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile, "<br>\n");
+ else
+ fprintf(outfile, "\n");
+
+ return 0;
+ cleanup:
+ if (view_filePath)
+ xmlFree(view_filePath);
+ if (name)
+ xmlFree(name);
+ if (error != 0) {
+ errno = error;
+ }
+ return retval;
+}
+
+static int report_process_xmlNode(const seaudit_log_t * log, const seaudit_report_t * report, xmlTextReaderPtr reader,
+ FILE * outfile)
+{
+ xmlChar *name = NULL, *id_attr = NULL, *title_attr = NULL;
+ int retval = -1, error = 0;
+
+ if ((name = xmlTextReaderName(reader)) == NULL) {
+ error = errno;
+ ERR(log, "%s", "Unavailable node name.");
+ goto cleanup;
+ }
+
+ if (!report_is_valid_node_name((char *)name)) {
+ retval = 0;
+ goto cleanup;
+ }
+
+ if (strcmp((char *)name, "seaudit-report") == 0 && xmlTextReaderNodeType(reader) == 1) {
+ if (report_parse_seaudit_report(log, report, reader, &id_attr, &title_attr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "<h1 class=\"report_title\">Title: %s</h1>\n", title_attr);
+ } else {
+ fprintf(outfile, "Title: %s\n", title_attr);
+ }
+ } else if (strcmp((char *)name, "standard-section") == 0 && xmlTextReaderNodeType(reader) == 1) {
+ if (report_parse_standard_attribs(log, report, reader, &id_attr, &title_attr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ if (id_attr == NULL) {
+ ERR(log, "%s", "Missing required id attribute for standard section node.");
+ error = EIO;
+ goto cleanup;
+ }
+ /* NOTE: If a title wasn't provided, we still continue. */
+ if (report_print_standard_section(log, report, id_attr, title_attr, outfile) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ } else if (strcmp((char *)name, "custom-section") == 0 && xmlTextReaderNodeType(reader) == 1) {
+ if (report_parse_custom_attribs(log, report, reader, &title_attr) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ /* NOTE: If a title wasn't provided, we still continue. */
+ if (report_print_custom_section(log, report, reader, title_attr, outfile) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ xmlFree(name);
+ xmlFree(id_attr);
+ xmlFree(title_attr);
+ if (retval < 0) {
+ errno = error;
+ }
+ return retval;
+}
+
+static int report_print_malformed(const seaudit_log_t * log, const seaudit_report_t * report, FILE * outfile)
+{
+ size_t i, len;
+ apol_vector_t *v = seaudit_model_get_malformed_messages(log, report->model);
+ if (v == NULL) {
+ return -1;
+ }
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(outfile, "<b><u>Malformed messages</b></u>\n");
+ fprintf(outfile, "<br>\n<br>\n");
+ } else {
+ fprintf(outfile, "Malformed messages\n");
+ len = strlen("Malformed messages\n");
+ for (i = 0; i < len; i++) {
+ fprintf(outfile, "-");
+ }
+ fprintf(outfile, "\n");
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ char *malformed_msg;
+ malformed_msg = apol_vector_get_element(v, i);
+ if (report->format == SEAUDIT_REPORT_FORMAT_HTML)
+ fprintf(outfile, "%s<br>\n", malformed_msg);
+ else
+ fprintf(outfile, "%s\n", malformed_msg);
+ }
+ fprintf(outfile, "\n");
+ apol_vector_destroy(&v);
+ return 0;
+}
+
+int seaudit_report_write(const seaudit_log_t * log, const seaudit_report_t * report, const char *out_file)
+{
+ xmlTextReaderPtr reader;
+ FILE *outfile = NULL;
+ int rt, retval = -1, error = 0;
+
+ /* Set/Open the output stream */
+ if (out_file == NULL) {
+ outfile = stdout;
+ } else {
+ if ((outfile = fopen(out_file, "w+")) == NULL) {
+ error = errno;
+ ERR(log, "Could not open %s for writing.", out_file);
+ goto cleanup;
+ }
+ }
+
+ /* Print report header */
+ if (report_print_header(log, report, outfile) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ /* Parse the xml config file and output report */
+ reader = xmlNewTextReaderFilename(report->config);
+ if (reader == NULL) {
+ error = errno;
+ ERR(log, "Unable to open config file (%s).", report->config);
+ goto cleanup;
+ }
+ rt = xmlTextReaderRead(reader);
+ while (rt == 1) {
+ report_process_xmlNode(log, report, reader, outfile);
+ rt = xmlTextReaderRead(reader);
+ }
+ error = errno;
+ xmlFreeTextReader(reader);
+ if (rt != 0) {
+ ERR(log, "Failed to parse config file %s.", report->config);
+ goto cleanup;
+ }
+ if (report->malformed && report_print_malformed(log, report, outfile) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ report_print_footer(report, outfile);
+
+ retval = 0;
+ cleanup:
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ if (retval < 0) {
+ errno = error;
+ }
+ return retval;
+}
diff --git a/libseaudit/src/seaudit_internal.h b/libseaudit/src/seaudit_internal.h
new file mode 100644
index 0000000..272dfbd
--- /dev/null
+++ b/libseaudit/src/seaudit_internal.h
@@ -0,0 +1,664 @@
+/**
+ * @file
+ * Protected interface seaudit library.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_SEAUDIT_INTERNAL_H
+#define SEAUDIT_SEAUDIT_INTERNAL_H
+
+#include <config.h>
+
+#include <seaudit/avc_message.h>
+#include <seaudit/bool_message.h>
+#include <seaudit/filter.h>
+#include <seaudit/load_message.h>
+#include <seaudit/log.h>
+#include <seaudit/message.h>
+#include <seaudit/model.h>
+#include <seaudit/sort.h>
+
+#include <apol/bst.h>
+#include <apol/vector.h>
+
+#include <libxml/uri.h>
+
+#define FILTER_FILE_FORMAT_VERSION "1.3"
+
+/*************** master seaudit log object (defined in log.c) ***************/
+
+struct seaudit_log
+{
+ /** vector of seaudit_message_t pointers */
+ apol_vector_t *messages;
+ /** vector of strings, corresponding to log messages that did
+ * not parse cleanly */
+ apol_vector_t *malformed_msgs;
+ /** vector of seaudit_model_t that are watching this log */
+ apol_vector_t *models;
+ apol_bst_t *types, *classes, *roles, *users;
+ apol_bst_t *perms, *hosts, *bools, *managers;
+ apol_bst_t *mls_lvl, *mls_clr;
+ seaudit_log_type_e logtype;
+ seaudit_handle_fn_t fn;
+ void *handle_arg;
+ /** non-zero if tzset() has been called */
+ int tz_initialized;
+ /** non-zero if the parser is in the middle of a line */
+ int next_line;
+};
+
+/**
+ * Notify a log that model is now watching it.
+ *
+ * @param log Log to append model.
+ * @param model Model that is watching.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int log_append_model(seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Notify a log that model is no longer watching it.
+ *
+ * @param log Log to append model.
+ * @param model Model that stopped watching.
+ */
+void log_remove_model(seaudit_log_t * log, seaudit_model_t * model);
+
+/**
+ * Get a vector of all messages from this seaudit log object.
+ *
+ * @param log Log object containing messages.
+ *
+ * @return Vector of seaudit_message_t pointers. Do not free() or
+ * otherwise modify this vector or its contents.
+ */
+const apol_vector_t *log_get_messages(const seaudit_log_t * log);
+
+/**
+ * Get a vector of all malformed messages from this seaudit log
+ * object. These are SELinux messages that did not parse cleanly for
+ * some reason. They will be returned in the same order in which they
+ * were read from the log file.
+ *
+ * @param log Log object containing malformed messages.
+ *
+ * @return Vector of strings. Do not free() or otherwise modify this
+ * vector or its contents.
+ */
+const apol_vector_t *log_get_malformed_messages(const seaudit_log_t * log);
+
+/*************** messages (defined in message.c) ***************/
+
+struct seaudit_message
+{
+ /** when this message was generated */
+ struct tm *date_stamp;
+ /** pointer into log->hosts for the hostname that generated
+ * this message, or NULL if none found */
+ char *host;
+ /** pointer intor log->managers for the object manager that
+ * generated this message, or NULL if none found */
+ char *manager;
+ /** type of message this really is */
+ seaudit_message_type_e type;
+ /** fake polymorphism by having a union of possible subclasses */
+ union
+ {
+ seaudit_avc_message_t *avc;
+ seaudit_bool_message_t *boolm;
+ seaudit_load_message_t *load;
+ } data;
+};
+
+/**
+ * Allocate a new seaudit message, append the message to the log, and
+ * return the message.
+ *
+ * @param log Log to which append the message.
+ * @param type Message type for the newly constructed message.
+ *
+ * @return A newly allocated message. The caller must not free the
+ * value.
+ */
+seaudit_message_t *message_create(seaudit_log_t * log, seaudit_message_type_e type);
+
+/**
+ * Deallocate all space associated with a message, recursing into the
+ * message's data field.
+ *
+ * @param msg If not NULL, message to free.
+ */
+void message_free(void *msg);
+
+/*************** avc messages (defined in avc_message.c) ***************/
+
+typedef enum seaudit_avc_message_class
+{
+ SEAUDIT_AVC_DATA_INVALID = 0,
+ SEAUDIT_AVC_DATA_MALFORMED,
+ SEAUDIT_AVC_DATA_IPC,
+ SEAUDIT_AVC_DATA_CAP, /* capability */
+ SEAUDIT_AVC_DATA_FS,
+ SEAUDIT_AVC_DATA_NET,
+} seaudit_avc_message_class_e;
+
+/**
+ * Definition of an avc message. Note that unless stated otherwise,
+ * character pointers are into the message's log's respective BST.
+ */
+struct seaudit_avc_message
+{
+ seaudit_avc_message_type_e msg;
+ /** type of avc message this is, either a deny or a granted
+ * (i.e., auditallow) */
+ seaudit_avc_message_class_e avc_type;
+ /** executable and path - free() this */
+ char *exe;
+ /** command - free() this */
+ char *comm;
+ /** path of the OBJECT - free() this */
+ char *path;
+ /** device for the object - free() this */
+ char *dev;
+ /** network interface - free() this */
+ char *netif;
+ /** local address - free() this */
+ char *laddr;
+ /** foreign address - free() this */
+ char *faddr;
+ /** source address - free() this */
+ char *saddr;
+ /** destination address - free() this */
+ char *daddr;
+ /** free() this */
+ char *name;
+ /** free() this */
+ char *ipaddr;
+ /** source context's user */
+ char *suser;
+ /** source context's role */
+ char *srole;
+ /** source context's type */
+ char *stype;
+ /** source context's mls level */
+ char *smls_lvl;
+ /** source context's mls clearance */
+ char *smls_clr;
+ /** target context's user */
+ char *tuser;
+ /** target context's role */
+ char *trole;
+ /** target context's type */
+ char *ttype;
+ /** target context's mls level */
+ char *tmls_lvl;
+ /** target context's mls clearance */
+ char *tmls_clr;
+ /** target class */
+ char *tclass;
+ /** audit header timestamp (seconds) */
+ time_t tm_stmp_sec;
+ /** audit header timestamp (nanoseconds) */
+ long tm_stmp_nano;
+ /** audit header serial number */
+ unsigned int serial;
+ /** pointers into log->perms BST (hence char *) */
+ apol_vector_t *perms;
+ /** key for an IPC call */
+ int key;
+ int is_key;
+ /** process capability (corresponds with class 'capability') */
+ int capability;
+ int is_capability;
+ /** inode of the object */
+ unsigned long inode;
+ int is_inode;
+ /** source port */
+ int source;
+ /** destination port */
+ int dest;
+ /** local port */
+ int lport;
+ /** foreign port */
+ int fport;
+ int port;
+ /** source sid */
+ unsigned int src_sid;
+ int is_src_sid;
+ /** target sid */
+ unsigned int tgt_sid;
+ int is_tgt_sid;
+ /** process ID of the subject */
+ unsigned int pid;
+ int is_pid;
+};
+
+/**
+ * Allocate and return a new seaudit AVC message.
+ *
+ * @return A newly allocated AVC message. The caller must not call
+ * avc_message_free() upon the returned value afterwards.
+ */
+seaudit_avc_message_t *avc_message_create(void);
+
+/**
+ * Deallocate all space associated with an AVC message.
+ *
+ * @param msg If not NULL, message to free.
+ */
+void avc_message_free(seaudit_avc_message_t * avc);
+
+/**
+ * Given an avc message, allocate and return a string that
+ * approximates the message as it had appeared within the log file.
+ *
+ * @param msg Message whose string representation to get.
+ * @param date Date and time when message was generated.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+char *avc_message_to_string(const seaudit_message_t * msg, const char *date);
+
+/**
+ * Given an avc change message, allocate and return a string,
+ * formatted in HTML, that approximates the message as it had appeared
+ * within the log file.
+ *
+ * @param msg Message whose string representation to get.
+ * @param date Date and time when message was generated.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+char *avc_message_to_string_html(const seaudit_message_t * msg, const char *date);
+
+/**
+ * Given an avc change message, allocate and return a string that
+ * gives miscellaneous info (e.g., ports, IP addresses).
+ *
+ * @param avc Message from which to get miscellaneous information.
+ *
+ * @return Miscellaneous message string representation, or NULL upon
+ * error. The caller is responsible for free()ing the string
+ * afterwards.
+ */
+char *avc_message_to_misc_string(const seaudit_avc_message_t * avc);
+
+/*************** bool messages (defined in bool_message.c) ***************/
+
+typedef struct seaudit_bool_message_change
+{
+ /** pointer into log's bools BST */
+ char *boolean;
+ /** new value for the boolean */
+ int value;
+} seaudit_bool_message_change_t;
+
+struct seaudit_bool_message
+{
+ /** vector of seaudit_bool_change_t pointers; vector owns objects. */
+ apol_vector_t *changes;
+};
+
+/**
+ * Allocate and return a new seaudit boolean change message.
+ *
+ * @return A newly allocated boolean change message. The caller must
+ * not call bool_message_free() upon the returned value afterwards.
+ */
+seaudit_bool_message_t *bool_message_create(void);
+
+/**
+ * Append a boolean change to a particular boolean message. This will
+ * add the boolean name to the log's BST as needed.
+ *
+ * @param log Log containing boolean name BST.
+ * @param bool Boolean message to change.
+ * @param name Name of the boolean that was changed. This function
+ * will dup the incoming name.
+ * @param value New value for the boolean.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int bool_change_append(seaudit_log_t * log, seaudit_bool_message_t * boolm, const char *name, int value);
+
+/**
+ * Deallocate all space associated with a boolean change message.
+ *
+ * @param msg If not NULL, message to free.
+ */
+void bool_message_free(seaudit_bool_message_t * boolm);
+
+/**
+ * Given a boolean change message, allocate and return a string that
+ * approximates the message as it had appeared within the log file.
+ *
+ * @param msg Message whose string representation to get.
+ * @param date Date and time when message was generated.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+char *bool_message_to_string(const seaudit_message_t * msg, const char *date);
+
+/**
+ * Given a boolean change message, allocate and return a string,
+ * formatted in HTML, that approximates the message as it had appeared
+ * within the log file.
+ *
+ * @param msg Message whose string representation to get.
+ * @param date Date and time when message was generated.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+char *bool_message_to_string_html(const seaudit_message_t * msg, const char *date);
+
+/**
+ * Given a boolean change message, allocate and return a string that
+ * gives miscellaneous info (i.e., list of boolean names and their new
+ * values.)
+ *
+ * @param bool Message from which to get miscellaneous information.
+ *
+ * @return Miscellaneous message string representation, or NULL upon
+ * error. The caller is responsible for free()ing the string
+ * afterwards.
+ */
+char *bool_message_to_misc_string(const seaudit_bool_message_t * boolm);
+
+/*************** load messages (defined in load_message.c) ***************/
+
+struct seaudit_load_message
+{
+ unsigned int users; /* number of users */
+ unsigned int roles; /* number of roles */
+ unsigned int types; /* number of types */
+ unsigned int classes; /* number of classes */
+ unsigned int rules; /* number of rules */
+ unsigned int bools; /* number of bools */
+ char *binary; /* path for binary that was loaded */
+};
+
+/**
+ * Allocate and return a new seaudit policy load message.
+ *
+ * @return A newly allocated policy load message. The caller must
+ * not call load_message_free() upon the returned value afterwards.
+ */
+seaudit_load_message_t *load_message_create(void);
+
+/**
+ * Deallocate all space associated with a policy load message.
+ *
+ * @param msg If not NULL, message to free.
+ */
+void load_message_free(seaudit_load_message_t * msg);
+
+/**
+ * Given a load message, allocate and return a string that
+ * approximates the message as it had appeared within the log file.
+ *
+ * @param msg Message whose string representation to get.
+ * @param date Date and time when message was generated.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+char *load_message_to_string(const seaudit_message_t * msg, const char *date);
+
+/**
+ * Given a load message, allocate and return a string, formatted in
+ * HTML, that approximates the message as it had appeared within the
+ * log file.
+ *
+ * @param msg Message whose string representation to get.
+ * @param date Date and time when message was generated.
+ *
+ * @return String representation for message, or NULL upon error. The
+ * caller is responsible for free()ing the string afterwards.
+ */
+char *load_message_to_string_html(const seaudit_message_t * msg, const char *date);
+
+/**
+ * Given a load message, allocate and return a string that gives
+ * miscellaneous info (e.g., number of types in the new policy).
+ *
+ * @param load Message from which to get miscellaneous information.
+ *
+ * @return Miscellaneous message string representation, or NULL upon
+ * error. The caller is responsible for free()ing the string
+ * afterwards.
+ */
+char *load_message_to_misc_string(const seaudit_load_message_t * load);
+
+/*************** model functions (defined in model.h) ***************/
+
+/**
+ * Notify a model to stop watching a log.
+ *
+ * @param model Model to notify.
+ * @param log Log to stop watching.
+ */
+void model_remove_log(seaudit_model_t * model, seaudit_log_t * log);
+
+/**
+ * Notify a model that a log has been changed; the model will need to
+ * recalculate its messages.
+ *
+ * @param model Model to notify.
+ * @param log Log that has been changed.
+ */
+void model_notify_log_changed(seaudit_model_t * model, seaudit_log_t * log);
+
+/**
+ * Notify a model that a filter has been changed; the model will need
+ * to recalculate its messages.
+ *
+ * @param model Model to notify.
+ * @param filter Filter that has been changed.
+ */
+void model_notify_filter_changed(seaudit_model_t * model, seaudit_filter_t * filter);
+
+/*************** filter functions (defined in filter.c) ***************/
+
+/**
+ * Link a model to a filter. Whenever the filter changes, it should
+ * call model_notify_filter_changed(); that way the model will
+ * recalculate itself.
+ *
+ * @param filter Filter to be watched.
+ * @param model Model that is watching.
+ */
+void filter_set_model(seaudit_filter_t * filter, seaudit_model_t * model);
+
+/********** more filter functions (defined in filter-internal.c) **********/
+
+typedef int (filter_read_func) (seaudit_filter_t * filter, const xmlChar * ch);
+
+struct filter_parse_state
+{
+ /** vector of filters created, appended to by <filter> tags;
+ caller must destroy this */
+ apol_vector_t *filters;
+ /** string from name attribute in a <view> tag; caller must free()
+ this */
+ char *view_name;
+ /** value from match attribute in a <view> tag */
+ seaudit_filter_match_e view_match;
+ /** value form show attribute in a <view> tag */
+ seaudit_filter_visible_e view_visible;
+
+ /****
+ The following are to be considered private data and may only
+ be used by filter-internal.c.
+ ****/
+ /** the most recently read string that was not part of a tag */
+ xmlChar *cur_string;
+ int warnings;
+ /** filter currently being parsed, set by most recent <filter> tag */
+ seaudit_filter_t *cur_filter;
+ /** pointer to a filter parsing function, set by <criteria> tag */
+ filter_read_func *cur_filter_read;
+};
+
+/**
+ * Given a filter and a message, return non-zero if the msg is
+ * accepted by the filter according to the filter's criteria. If the
+ * filter does not have enough information to decide (because the
+ * message is incomplete) then this should return 0.
+ *
+ * @param filter Filter to apply.
+ * @param msg Message to check.
+ *
+ * @return Non-zero if message is accepted, 0 if not.
+ */
+int filter_is_accepted(const seaudit_filter_t * filter, const seaudit_message_t * msg);
+
+/**
+ * Parse the given XML file and fill in the passed in struct. The
+ * caller must create the struct and the vector within. Upon return,
+ * the caller must destroy the vector and free view_name.
+ *
+ * @param state An initialized state struct for parsing.
+ * @param filename Name of XML file to parse.
+ *
+ * @return 0 on success, > 0 if parse warnings, < 0 on error.
+ */
+int filter_parse_xml(struct filter_parse_state *state, const char *filename);
+
+/**
+ * Append the given filter's values, in XML format, to a file handler.
+ * This includes the filter's name and criteria.
+ *
+ * @param filter Filter to save.
+ * @param file File to which write.
+ *
+ * @see seaudit_filter_create_from_file()
+ */
+void filter_append_to_file(const seaudit_filter_t * filter, FILE * file, int tabs);
+
+/*************** sort functions (defined in sort.c) ***************/
+
+/**
+ * Create and return a new sort object, initialized with the data from
+ * an existing sort object. The new sort object will not be attached
+ * to any models.
+ *
+ * @param sort Sort object to clone.
+ *
+ * @return A cloned sort object, or NULL upon error. The caller is
+ * responsible for calling seaudit_sort_destroy() afterwards.
+ */
+seaudit_sort_t *sort_create_from_sort(const seaudit_sort_t * sort);
+
+/**
+ * Create and return a new sort object based upon the name of the sort
+ * (as returned by sort_get_name()). The new sort object will not be
+ * attached to any models.
+ *
+ * @param name Name of the type of sort to create.
+ * @param direction Direction to sort, non-negative for ascending,
+ * negative for descending.
+ *
+ * @return A new sort object, or NULL upon error. The caller is
+ * responsible for calling seaudit_sort_destroy() afterwards.
+ */
+seaudit_sort_t *sort_create_from_name(const char *name, int direction);
+
+/**
+ * Given a sort object and a message, return non-zero if this sort
+ * object could operate on the message, 0 if not. (Messages may have
+ * incomplete information due to parser warnings.)
+ *
+ * @param sort Sort object to query.
+ * @param msg Message to check.
+ *
+ * @return Non-zero if sort supports the message, 0 if not.
+ */
+int sort_is_supported(const seaudit_sort_t * sort, const seaudit_message_t * msg);
+
+/**
+ * Invoke a sort object's comparison function.
+ *
+ * @param sort Sort object that contains a comparator.
+ * @param m1 First message to compare.
+ * @param m2 Second message to compare.
+ *
+ * @return 0 if the messages are equivalent, < 0 if a is first, > 0 if
+ * b is first.
+ */
+int sort_comp(const seaudit_sort_t * sort, const seaudit_message_t * a, const seaudit_message_t * b);
+
+/**
+ * Return the type of sort this sort object is. The name is valid for
+ * sort_create_from_name()'s first parameter.
+ *
+ * @param sort Sort object to query.
+ *
+ * @return Type of sort this object is.
+ */
+const char *sort_get_name(const seaudit_sort_t * sort);
+
+/**
+ * Return the sort direction for a sort object.
+ *
+ * @param sort Sort object to query.
+ *
+ * @return Non-negative for ascending, negative for descending.
+ */
+int sort_get_direction(const seaudit_sort_t * sort);
+
+/*************** error handling code (defined in log.c) ***************/
+
+#define SEAUDIT_MSG_ERR 1
+#define SEAUDIT_MSG_WARN 2
+#define SEAUDIT_MSG_INFO 3
+
+/**
+ * Write a message to the callback stored within a seaudit_log_t
+ * handler. If the msg_callback field is empty then suppress the
+ * message.
+ *
+ * @param log Error reporting handler. If NULL then write message to
+ * stderr.
+ * @param level Severity of message, one of SEAUDIT_MSG_ERR,
+ * SEAUDIT_MSG_WARN, or SEAUDIT_MSG_INFO.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+extern void seaudit_handle_msg(const seaudit_log_t * log, int level, const char *fmt, ...);
+
+__attribute__ ((format(printf, 3, 4)))
+extern void seaudit_handle_msg(const seaudit_log_t * log, int level, const char *fmt, ...);
+
+#undef ERR
+#undef WARN
+#undef INFO
+
+#define ERR(handle, format, ...) seaudit_handle_msg(handle, SEAUDIT_MSG_ERR, format, __VA_ARGS__)
+#define WARN(handle, format, ...) seaudit_handle_msg(handle, SEAUDIT_MSG_WARN, format, __VA_ARGS__)
+#define INFO(handle, format, ...) seaudit_handle_msg(handle, SEAUDIT_MSG_INFO, format, __VA_ARGS__)
+
+#endif
diff --git a/libseaudit/src/sort.c b/libseaudit/src/sort.c
new file mode 100644
index 0000000..2d119b9
--- /dev/null
+++ b/libseaudit/src/sort.c
@@ -0,0 +1,744 @@
+/**
+ * @file
+ * Implementation of seaudit sort routines.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "seaudit_internal.h"
+
+#include <apol/util.h>
+
+#include <errno.h>
+#include <string.h>
+
+/**
+ * Callback that compares two messages.
+ */
+typedef int (sort_comp_func) (const seaudit_sort_t * sort, const seaudit_message_t * a, const seaudit_message_t * b);
+
+/**
+ * Callback that returns non-zero if the sort routine can handle the
+ * given message, 0 if not supported.
+ */
+typedef int (sort_supported_func) (const seaudit_sort_t * sort, const seaudit_message_t * m);
+
+struct seaudit_sort
+{
+ const char *name;
+ sort_comp_func *comp;
+ sort_supported_func *support;
+ int direction;
+};
+
+seaudit_sort_t *seaudit_sort_create_from_sort(const seaudit_sort_t * sort)
+{
+ seaudit_sort_t *s;
+ if (sort == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if ((s = calloc(1, sizeof(*s))) == NULL) {
+ return NULL;
+ }
+ s->name = sort->name;
+ s->comp = sort->comp;
+ s->support = sort->support;
+ s->direction = sort->direction;
+ return s;
+}
+
+void seaudit_sort_destroy(seaudit_sort_t ** sort)
+{
+ if (sort != NULL && *sort != NULL) {
+ free(*sort);
+ *sort = NULL;
+ }
+}
+
+static seaudit_sort_t *sort_create(const char *name, sort_comp_func * comp, sort_supported_func support, const int direction)
+{
+ seaudit_sort_t *s = calloc(1, sizeof(*s));
+ if (s == NULL) {
+ return NULL;
+ }
+ s->name = name;
+ s->comp = comp;
+ s->support = support;
+ s->direction = direction;
+ return s;
+}
+
+seaudit_sort_t *sort_create_from_sort(const seaudit_sort_t * sort)
+{
+ if (sort == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return sort_create(sort->name, sort->comp, sort->support, sort->direction);
+}
+
+static int sort_message_type_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ if (a->type != b->type) {
+ return a->type - b->type;
+ }
+ if (a->type == SEAUDIT_MESSAGE_TYPE_AVC) {
+ return a->data.avc->msg - b->data.avc->msg;
+ }
+ return 0;
+}
+
+static int sort_message_type_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type != SEAUDIT_MESSAGE_TYPE_INVALID;
+}
+
+seaudit_sort_t *seaudit_sort_by_message_type(const int direction)
+{
+ return sort_create("message_type", sort_message_type_comp, sort_message_type_support, direction);
+}
+
+/**
+ * Given two dates compare them, checking to see if the dates passed
+ * in have valid years and correcting if not before comparing.
+ */
+static int sort_date_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ /* tm has year, month, day, hour, min, sec */
+ /* if we should compare the years */
+ struct tm *t1 = a->date_stamp;
+ struct tm *t2 = b->date_stamp;
+ int retval;
+ if (t1->tm_year != 0 && t2->tm_year != 0 && (retval = t1->tm_year - t2->tm_year) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_mon - t2->tm_mon) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_mday - t2->tm_mday) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_hour - t2->tm_hour) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_min - t2->tm_min) != 0) {
+ return retval;
+ }
+ if ((retval = t1->tm_sec - t2->tm_sec) != 0) {
+ return retval;
+ }
+ return 0;
+}
+
+static int sort_date_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->date_stamp != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_date(const int direction)
+{
+ return sort_create("date", sort_date_comp, sort_date_support, direction);
+}
+
+static int sort_host_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->host, b->host);
+}
+
+static int sort_host_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->host != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_host(const int direction)
+{
+ return sort_create("host", sort_host_comp, sort_host_support, direction);
+}
+
+static int sort_perm_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ size_t i;
+ return apol_vector_compare(a->data.avc->perms, b->data.avc->perms, apol_str_strcmp, NULL, &i);
+}
+
+static int sort_perm_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC &&
+ msg->data.avc->perms != NULL && apol_vector_get_size(msg->data.avc->perms) >= 1;
+}
+
+seaudit_sort_t *seaudit_sort_by_permission(const int direction)
+{
+ return sort_create("permission", sort_perm_comp, sort_perm_support, direction);
+}
+
+static int sort_source_user_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->suser, b->data.avc->suser);
+}
+
+static int sort_source_user_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->suser != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_source_user(const int direction)
+{
+ return sort_create("source_user", sort_source_user_comp, sort_source_user_support, direction);
+}
+
+static int sort_source_role_comp(const seaudit_sort_t * sort __attribute((unused)), const seaudit_message_t * a,
+ const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->srole, b->data.avc->srole);
+}
+
+static int sort_source_role_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->srole != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_source_role(const int direction)
+{
+ return sort_create("source_role", sort_source_role_comp, sort_source_role_support, direction);
+}
+
+static int sort_source_type_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->stype, b->data.avc->stype);
+}
+
+static int sort_source_type_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->stype != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_source_type(const int direction)
+{
+ return sort_create("source_type", sort_source_type_comp, sort_source_type_support, direction);
+}
+
+static int sort_source_mls_lvl_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->smls_lvl, b->data.avc->smls_lvl);
+}
+
+static int sort_source_mls_lvl_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->smls_lvl != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_source_mls_lvl(const int direction)
+{
+ return sort_create("source_mls_lvl", sort_source_mls_lvl_comp, sort_source_mls_lvl_support, direction);
+}
+
+static int sort_source_mls_clr_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->smls_clr, b->data.avc->smls_clr);
+}
+
+static int sort_source_mls_clr_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->smls_clr != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_source_mls_clr(const int direction)
+{
+ return sort_create("source_mls_clr", sort_source_mls_clr_comp, sort_source_mls_clr_support, direction);
+}
+
+static int sort_target_user_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->tuser, b->data.avc->tuser);
+}
+
+static int sort_target_user_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tuser != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_target_user(const int direction)
+{
+ return sort_create("target_user", sort_target_user_comp, sort_target_user_support, direction);
+}
+
+static int sort_target_role_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->trole, b->data.avc->trole);
+}
+
+static int sort_target_role_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->trole != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_target_role(const int direction)
+{
+ return sort_create("target_role", sort_target_role_comp, sort_target_role_support, direction);
+}
+
+static int sort_target_type_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->ttype, b->data.avc->ttype);
+}
+
+static int sort_target_type_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->ttype != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_target_type(const int direction)
+{
+ return sort_create("target_type", sort_target_type_comp, sort_target_type_support, direction);
+}
+
+static int sort_target_mls_lvl_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->tmls_lvl, b->data.avc->tmls_lvl);
+}
+
+static int sort_target_mls_lvl_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tmls_lvl != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_target_mls_lvl(const int direction)
+{
+ return sort_create("target_mls_lvl", sort_target_mls_lvl_comp, sort_target_mls_lvl_support, direction);
+}
+
+static int sort_target_mls_clr_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->tmls_clr, b->data.avc->tmls_clr);
+}
+
+static int sort_target_mls_clr_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tmls_clr != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_target_mls_clr(const int direction)
+{
+ return sort_create("target_mls_clr", sort_target_mls_clr_comp, sort_target_mls_clr_support, direction);
+}
+
+static int sort_object_class_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->tclass, b->data.avc->tclass);
+}
+
+static int sort_object_class_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->tclass != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_object_class(const int direction)
+{
+ return sort_create("object_class", sort_object_class_comp, sort_object_class_support, direction);
+}
+
+static int sort_executable_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->exe, b->data.avc->exe);
+}
+
+static int sort_executable_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->exe != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_executable(const int direction)
+{
+ return sort_create("executable", sort_executable_comp, sort_executable_support, direction);
+}
+
+static int sort_command_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->comm, b->data.avc->comm);
+}
+
+static int sort_command_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->comm != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_command(const int direction)
+{
+ return sort_create("command", sort_command_comp, sort_command_support, direction);
+}
+
+static int sort_name_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->name, b->data.avc->name);
+}
+
+static int sort_name_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->name != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_name(const int direction)
+{
+ return sort_create("name", sort_name_comp, sort_name_support, direction);
+}
+
+static int sort_path_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->path, b->data.avc->path);
+}
+
+static int sort_path_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->path != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_path(const int direction)
+{
+ return sort_create("path", sort_path_comp, sort_path_support, direction);
+}
+
+static int sort_device_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->dev, b->data.avc->dev);
+}
+
+static int sort_device_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->dev != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_device(const int direction)
+{
+ return sort_create("device", sort_device_comp, sort_device_support, direction);
+}
+
+static int sort_inode_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ /* need this logic because inodes are unsigned, so subtraction
+ * could overflow */
+ if (a->data.avc->inode < b->data.avc->inode) {
+ return -1;
+ }
+ return a->data.avc->inode - b->data.avc->inode;
+}
+
+static int sort_inode_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->inode > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_inode(const int direction)
+{
+ return sort_create("inode", sort_inode_comp, sort_inode_support, direction);
+}
+
+static int sort_pid_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ /* need this logic because pids are unsigned, so subtraction
+ * could overflow */
+ if (a->data.avc->pid < b->data.avc->pid) {
+ return -1;
+ }
+ return a->data.avc->pid - b->data.avc->pid;
+}
+
+static int sort_pid_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->pid > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_pid(const int direction)
+{
+ return sort_create("pid", sort_pid_comp, sort_pid_support, direction);
+}
+
+static int sort_port_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->port - b->data.avc->port;
+}
+
+static int sort_port_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->port > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_port(const int direction)
+{
+ return sort_create("port", sort_port_comp, sort_port_support, direction);
+}
+
+static int sort_laddr_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->laddr, b->data.avc->laddr);
+}
+
+static int sort_laddr_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->laddr != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_laddr(const int direction)
+{
+ return sort_create("laddr", sort_laddr_comp, sort_laddr_support, direction);
+}
+
+static int sort_lport_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->lport - b->data.avc->lport;
+}
+
+static int sort_lport_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->lport > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_lport(const int direction)
+{
+ return sort_create("lport", sort_lport_comp, sort_lport_support, direction);
+}
+
+static int sort_faddr_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->faddr, b->data.avc->faddr);
+}
+
+static int sort_faddr_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->faddr != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_faddr(const int direction)
+{
+ return sort_create("faddr", sort_faddr_comp, sort_faddr_support, direction);
+}
+
+static int sort_fport_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->fport - b->data.avc->fport;
+}
+
+static int sort_fport_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->fport > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_fport(const int direction)
+{
+ return sort_create("fport", sort_fport_comp, sort_fport_support, direction);
+}
+
+static int sort_saddr_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->saddr, b->data.avc->saddr);
+}
+
+static int sort_saddr_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->saddr != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_saddr(const int direction)
+{
+ return sort_create("saddr", sort_saddr_comp, sort_saddr_support, direction);
+}
+
+static int sort_sport_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->source - b->data.avc->source;
+}
+
+static int sort_sport_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->source > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_sport(const int direction)
+{
+ return sort_create("sport", sort_sport_comp, sort_sport_support, direction);
+}
+
+static int sort_daddr_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return strcmp(a->data.avc->daddr, b->data.avc->daddr);
+}
+
+static int sort_daddr_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->daddr != NULL;
+}
+
+seaudit_sort_t *seaudit_sort_by_daddr(const int direction)
+{
+ return sort_create("daddr", sort_daddr_comp, sort_daddr_support, direction);
+}
+
+static int sort_dport_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->dest - b->data.avc->dest;
+}
+
+static int sort_dport_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->dest > 0;
+}
+
+seaudit_sort_t *seaudit_sort_by_dport(const int direction)
+{
+ return sort_create("dport", sort_dport_comp, sort_dport_support, direction);
+}
+
+static int sort_key_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->key - b->data.avc->key;
+}
+
+static int sort_key_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_key;
+}
+
+seaudit_sort_t *seaudit_sort_by_key(const int direction)
+{
+ return sort_create("key", sort_key_comp, sort_key_support, direction);
+}
+
+static int sort_cap_comp(const seaudit_sort_t * sort
+ __attribute__ ((unused)), const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ return a->data.avc->capability - b->data.avc->capability;
+}
+
+static int sort_cap_support(const seaudit_sort_t * sort __attribute__ ((unused)), const seaudit_message_t * msg)
+{
+ return msg->type == SEAUDIT_MESSAGE_TYPE_AVC && msg->data.avc->is_capability;
+}
+
+seaudit_sort_t *seaudit_sort_by_cap(const int direction)
+{
+ return sort_create("cap", sort_cap_comp, sort_cap_support, direction);
+}
+
+/******************** protected functions below ********************/
+
+struct sort_name_map
+{
+ const char *name;
+ seaudit_sort_t *(*create_fn) (int);
+};
+
+static const struct sort_name_map create_map[] = {
+ {"message_type", seaudit_sort_by_message_type},
+ {"date", seaudit_sort_by_date},
+ {"host", seaudit_sort_by_host},
+ {"permission", seaudit_sort_by_permission},
+ {"source_user", seaudit_sort_by_source_user},
+ {"source_role", seaudit_sort_by_source_role},
+ {"source_type", seaudit_sort_by_source_type},
+ {"target_user", seaudit_sort_by_target_user},
+ {"target_role", seaudit_sort_by_target_role},
+ {"target_type", seaudit_sort_by_target_type},
+ {"object_class", seaudit_sort_by_object_class},
+ {"executable", seaudit_sort_by_executable},
+ {"name", seaudit_sort_by_name},
+ {"command", seaudit_sort_by_command},
+ {"path", seaudit_sort_by_path},
+ {"device", seaudit_sort_by_device},
+ {"inode", seaudit_sort_by_inode},
+ {"pid", seaudit_sort_by_pid},
+ {"port", seaudit_sort_by_port},
+ {"laddr", seaudit_sort_by_laddr},
+ {"lport", seaudit_sort_by_lport},
+ {"faddr", seaudit_sort_by_faddr},
+ {"fport", seaudit_sort_by_fport},
+ {"saddr", seaudit_sort_by_saddr},
+ {"sport", seaudit_sort_by_sport},
+ {"daddr", seaudit_sort_by_daddr},
+ {"dport", seaudit_sort_by_dport},
+ {"key", seaudit_sort_by_key},
+ {"cap", seaudit_sort_by_cap},
+ {NULL, NULL}
+};
+
+seaudit_sort_t *sort_create_from_name(const char *name, int direction)
+{
+ size_t i;
+ for (i = 0; create_map[i].name != NULL; i++) {
+ if (strcmp(create_map[i].name, name) == 0) {
+ return create_map[i].create_fn(direction);
+ }
+ }
+ errno = EINVAL;
+ return NULL;
+}
+
+int sort_is_supported(const seaudit_sort_t * sort, const seaudit_message_t * msg)
+{
+ return sort->support(sort, msg);
+}
+
+int sort_comp(const seaudit_sort_t * sort, const seaudit_message_t * a, const seaudit_message_t * b)
+{
+ int retval = sort->comp(sort, a, b);
+ return (sort->direction >= 0 ? retval : -1 * retval);
+}
+
+const char *sort_get_name(const seaudit_sort_t * sort)
+{
+ return sort->name;
+}
+
+int sort_get_direction(const seaudit_sort_t * sort)
+{
+ return sort->direction;
+}
diff --git a/libseaudit/src/util.c b/libseaudit/src/util.c
new file mode 100644
index 0000000..4df653c
--- /dev/null
+++ b/libseaudit/src/util.c
@@ -0,0 +1,32 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+#include <seaudit/util.h>
+
+const char *libseaudit_get_version(void)
+{
+ return LIBSEAUDIT_VERSION_STRING;
+}
diff --git a/libseaudit/swig/Makefile.am b/libseaudit/swig/Makefile.am
new file mode 100644
index 0000000..8eb23d4
--- /dev/null
+++ b/libseaudit/swig/Makefile.am
@@ -0,0 +1,15 @@
+if DO_SWIGIFY_PYTHON
+ MAYBE_PYSWIG = python
+endif
+
+if DO_SWIGIFY_JAVA
+ MAYBE_JSWIG = java
+endif
+
+if DO_SWIGIFY_TCL
+ MAYBE_TCLSWIG = tcl
+endif
+
+SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG)
+
+dist_noinst_DATA = seaudit.i
diff --git a/libseaudit/swig/java/MANIFEST.MF.in b/libseaudit/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..070cc28
--- /dev/null
+++ b/libseaudit/swig/java/MANIFEST.MF.in
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+
+Name: com/tresys/setools/
+Specification-Title: "SETools Java Libraries"
+Specification-Version: "@VERSION@"
+Specification-Vendor: "Tresys Technology"
+Implementation-Title: "com.tresys.setools.seaudit"
+Implementation-Version: "@libseaudit_version@"
+Implementation-Vendor: "Tresys Technology"
+Extension-List: qpol apol
+qpol-Extension-Name: com.tresys.setools.qpol
+qpol-Implementation-Version: @libqpol_version@
+apol-Extension-Name: com.tresys.setools.apol
+apol-Implementation-Version: @libapol_version@
diff --git a/libseaudit/swig/java/Makefile.am b/libseaudit/swig/java/Makefile.am
new file mode 100644
index 0000000..f843204
--- /dev/null
+++ b/libseaudit/swig/java/Makefile.am
@@ -0,0 +1,90 @@
+wrappedso_DATA = libjseaudit.so.@libseaudit_version@
+wrappedso_SONAME = @libseaudit_jswig_soname@
+short_name = libjseaudit.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.seaudit
+package_dir = $(dir $(subst .,/,$(package_name)))seaudit
+
+wrappedjar_DATA = seaudit.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../seaudit.i
+BUILT_SOURCES = seaudit_wrap.c \
+ seaudit_avc_message_t.java \
+ seaudit_avc_message_type_e.java \
+ seaudit_bool_message_t.java \
+ seaudit_filter_date_match_e.java \
+ seaudit_filter_match_e.java \
+ seaudit_filter_t.java \
+ seaudit_filter_visible_e.java \
+ seaudit.java \
+ seauditJNI.java \
+ seaudit_load_message_t.java \
+ seaudit_log_t.java \
+ seaudit_log_type_e.java \
+ seaudit_message_t.java \
+ seaudit_message_type_e.java \
+ seaudit_model_t.java \
+ seaudit_report_format_e.java \
+ seaudit_report_t.java \
+ seaudit_sort_t.java \
+ tm_t.java \
+ SWIGTYPE_p_void.java
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libseaudit/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \
+ -classpath $(top_builddir)/libqpol/swig/java/qpol.jar:$(top_builddir)/libapol/swig/java/apol.jar
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @SEAUDIT_LIB_FLAG@ @XML_LIBS@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libseaudit/src/libseaudit.so
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_JAVA_OPT) -package $(package_name) -o $@ \
+ -I$(top_srcdir)/libseaudit/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES))
+
+$(wrappedso_DATA): $(filter %.c, $(BUILT_SOURCES))
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_JAVA_CFLAGS) -DSWIGJAVA=1 $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+# Intentionally do not include SWIGTYPE_p_void.java below so that the
+# Java compiler uses the one created in package
+# com.tresys.setools.qpol instead of the one from package
+# com.tresys.setools.seaudit.
+java_files = $(filter-out SWIGTYPE_p_void.java, $(filter %.java, $(BUILT_SOURCES)))
+
+classes = $(patsubst %.java, $(package_dir)/%.class, $(java_files))
+
+# Because the Java compiler can generate multiple class files from the
+# same source .java file, putting all of the classes below will result
+# in repeated invocations of javac. Therefore, an alternative is to
+# just depend upon the first class file, and let the Java compiler
+# create the rest of them.
+$(firstword $(classes)): $(java_files)
+ $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^
+
+$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes))
+
+$(wrappedjar_DATA): MANIFEST.MF
+
+$(wrappedjar_DATA): $(classes)
+ $(JAR) cfm $@ MANIFEST.MF $^
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name)
+ $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name)
+ -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA)
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name)
diff --git a/libseaudit/swig/python/Makefile.am b/libseaudit/swig/python/Makefile.am
new file mode 100644
index 0000000..ddf9e42
--- /dev/null
+++ b/libseaudit/swig/python/Makefile.am
@@ -0,0 +1,39 @@
+wrappedso_DATA = _seaudit.so.@libseaudit_version@
+wrappedso_SONAME = @libseaudit_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+wrappedpy_DATA = seaudit.py
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../seaudit.i
+BUILT_SOURCES = seaudit_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libseaudit/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @SEAUDIT_LIB_FLAG@ @XML_LIBS@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libseaudit/src/libseaudit.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_PYTHON_OPT) -o $@ \
+ -I$(top_srcdir)/libseaudit/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _seaudit.so
+
+$(wrappedpy_DATA): $(BUILT_SOURCES)
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _seaudit.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_seaudit.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _seaudit.so seaudit.pyc
diff --git a/libseaudit/swig/seaudit.i b/libseaudit/swig/seaudit.i
new file mode 100644
index 0000000..8c96d89
--- /dev/null
+++ b/libseaudit/swig/seaudit.i
@@ -0,0 +1,1373 @@
+/**
+ * @file
+ * SWIG declarations for libseaudit.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%module seaudit
+
+%{
+#include <seaudit/avc_message.h>
+#include <seaudit/bool_message.h>
+#include <seaudit/filter.h>
+#include <seaudit/load_message.h>
+#include <seaudit/log.h>
+#include <seaudit/message.h>
+#include <seaudit/model.h>
+#include <seaudit/parse.h>
+#include <seaudit/report.h>
+#include <seaudit/sort.h>
+#include <seaudit/util.h>
+#include <time.h>
+
+/* Provide hooks so that language-specific modules can define the
+ * callback function, used by the handler in seaudit_log_create().
+ */
+SWIGEXPORT seaudit_handle_fn_t seaudit_swig_message_callback = NULL;
+SWIGEXPORT void * seaudit_swig_message_callback_arg = NULL;
+
+%}
+
+#ifdef SWIGJAVA
+%javaconst(1);
+/* get the java environment so we can throw exceptions */
+%{
+ static JNIEnv *seaudit_global_jenv;
+ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ (*vm)->AttachCurrentThread(vm, (void **)&seaudit_global_jenv, NULL);
+ return JNI_VERSION_1_2;
+ }
+%}
+#endif
+
+%include exception.i
+%include stdint.i
+%import apol.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+#ifdef SWIGJAVA
+
+%exception {
+ seaudit_global_jenv = jenv;
+ $action
+}
+
+%{
+#define BEGIN_EXCEPTION JNIEnv *local_jenv = seaudit_global_jenv; {
+#define END_EXCEPTION }
+%}
+
+/* handle size_t correctly in java as architecture independent */
+%typemap(jni) size_t "jlong"
+%typemap(jtype) size_t "long"
+%typemap(jstype) size_t "long"
+%typemap("javaimports") SWIGTYPE, FILE* %{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%typemap(javabody) SWIGTYPE %{
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ public $javaclassname(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ public static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+%}
+/* the following handles the dependencies on qpol and apol */
+%pragma(java) jniclassimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libseaudit_get_version ();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("jseaudit");
+ }
+ }
+%}
+%pragma(java) moduleimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+#else
+/* not in java so handle size_t as architecture dependent */
+#ifdef SWIGWORDSIZE64
+typedef uint64_t size_t;
+#else
+typedef uint32_t size_t;
+#endif
+%{
+#define BEGIN_EXCEPTION
+#define END_EXCEPTION
+%}
+#endif
+
+#ifdef SWIGJAVA
+/* if java, pass the new exception macro to C not just SWIG */
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%inline %{
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {SWIG_JavaException(local_jenv, code, msg); goto fail;}
+%}
+#endif
+
+#ifdef SWIGTCL
+/* implement a custom non thread-safe error handler */
+%{
+static char *message = NULL;
+static void tcl_clear_error(void)
+{
+ free(message);
+ message = NULL;
+}
+static void tcl_throw_error(const char *s)
+{
+ free(message);
+ message = strdup(s);
+}
+static char *tcl_get_error(void)
+{
+ return message;
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+%}
+
+%wrapper %{
+/* Tcl module's initialization routine is expected to be named
+ * Seaudit_Init(), but the output file will be called libtseaudit.so instead
+ * of libseaudit.so. Therefore add an alias from Tseaudit_Init() to the
+ * real Seaudit_Init().
+ */
+SWIGEXPORT int Tseaudit_Init(Tcl_Interp *interp) {
+ return SWIG_init(interp);
+}
+%}
+
+%exception {
+ char *err;
+ tcl_clear_error();
+ $action
+ if ((err = tcl_get_error()) != NULL) {
+ Tcl_Obj *obj = Tcl_NewStringObj(message, -1);
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, obj);
+ goto fail;
+ }
+}
+#undef SWIG_exception
+#define SWIG_exception(code, msg) {tcl_throw_error(msg); goto fail;}
+#endif
+
+%inline %{
+ typedef struct apol_string_vector apol_string_vector_t;
+%}
+
+
+#ifdef SWIGPYTHON
+/* map python file to C FILE struct pointer */
+%typemap(in) FILE * {
+ if (!PyFile_Check($input)) {
+ PyErr_SetString(PyExc_TypeError, "Need a file!");
+ return NULL;
+ }
+ $1 = PyFile_AsFile($input);
+}
+/* map string into C-style memory buffer */
+%typemap(in) (const char *buffer, const size_t bufsize) {
+ $1 = PyString_AsString($input);
+ $2 = (size_t) PyString_Size($input);
+}
+#endif
+#ifdef SWIGJAVA
+/* map string into C-style memory buffer */
+%typemap(in, noblock=1) (const char *buffer, const size_t bufsize) {
+ $1 = 0;
+ $2 = 0;
+ if ($input) {
+ $1 = ($1_ltype)JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!$1) return $null;
+ $2 = strlen($1);
+ }
+}
+%typemap(freearg, noblock=1) (const char *buffer, const size_t bufsize) {
+ if ($1) JCALL2(ReleaseStringUTFChars, jenv, $input, $1);
+}
+#endif
+#ifdef SWIGTCL
+%typemap(in) FILE * {
+ ClientData c;
+ if (Tcl_GetOpenFile(interp, Tcl_GetString($input), 0, 1, &c) == TCL_ERROR)
+ SWIG_exception(SWIG_RuntimeError, Tcl_GetStringResult(interp));
+ $1 = (FILE*)c;
+}
+#endif
+
+/* from <time.h> */
+%{
+ typedef struct tm tm_t;
+%}
+typedef struct tm {
+ int tm_sec; /* seconds */
+ int tm_min; /* minutes */
+ int tm_hour; /* hours */
+ int tm_mday; /* day of the month */
+ int tm_mon; /* month */
+ int tm_year; /* year */
+ int tm_wday; /* day of the week */
+ int tm_yday; /* day in the year */
+ int tm_isdst; /* daylight saving time */
+} tm_t;
+%extend tm_t {
+ tm_t() {
+ struct tm *t;
+ BEGIN_EXCEPTION
+ t = calloc(1, sizeof(struct tm));
+ if (!t) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return t;
+ };
+ ~tm_t() {
+ free(self);
+ }
+ /* use default accessor style for the rest */
+};
+
+const char *libseaudit_get_version(void);
+
+/* seaudit log */
+typedef enum seaudit_log_type
+{
+ SEAUDIT_LOG_TYPE_INVALID = 0,
+ SEAUDIT_LOG_TYPE_SYSLOG,
+ SEAUDIT_LOG_TYPE_AUDITD
+} seaudit_log_type_e;
+typedef struct seaudit_log {} seaudit_log_t;
+%extend seaudit_log_t {
+ seaudit_log_t() {
+ seaudit_log_t *slog;
+ BEGIN_EXCEPTION
+ slog = seaudit_log_create(seaudit_swig_message_callback, seaudit_swig_message_callback_arg);
+ if (!slog) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return slog;
+ };
+ ~seaudit_log_t() {
+ seaudit_log_destroy(&self);
+ };
+ void clear () {
+ seaudit_log_clear(self);
+ };
+ %newobject get_users();
+ apol_string_vector_t *get_users() {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = seaudit_log_get_users(self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ %newobject get_roles();
+ apol_string_vector_t *get_roles() {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = seaudit_log_get_roles(self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ %newobject get_types();
+ apol_string_vector_t *get_types() {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = seaudit_log_get_types(self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+ %newobject get_classes();
+ apol_string_vector_t *get_classes() {
+ apol_vector_t *v;
+ BEGIN_EXCEPTION
+ v = seaudit_log_get_classes(self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return (apol_string_vector_t*)v;
+ };
+};
+
+/* seaudit message */
+typedef enum seaudit_message_type
+{
+ SEAUDIT_MESSAGE_TYPE_INVALID = 0,
+ SEAUDIT_MESSAGE_TYPE_BOOL,
+ SEAUDIT_MESSAGE_TYPE_AVC,
+ SEAUDIT_MESSAGE_TYPE_LOAD
+} seaudit_message_type_e;
+typedef struct seaudit_message {} seaudit_message_t;
+%extend seaudit_message_t {
+ seaudit_message_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Canot directly create seaudit_message_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~seaudit_message_t() {
+ /* no op */
+ return;
+ };
+ seaudit_message_type_e get_type() {
+ seaudit_message_type_e te;
+ (void)seaudit_message_get_data(self, &te);
+ return te;
+ };
+ void *get_data() {
+ seaudit_message_type_e te;
+ return seaudit_message_get_data(self, &te);
+ };
+ const char *get_host() {
+ return seaudit_message_get_host(self);
+ };
+ const tm_t *get_time() {
+ return seaudit_message_get_time(self);
+ }
+ %newobject to_string();
+ char *to_string() {
+ char *str;
+ BEGIN_EXCEPTION
+ str = seaudit_message_to_string(self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ %newobject to_string_html();
+ char *to_string_html() {
+ char *str;
+ BEGIN_EXCEPTION
+ str = seaudit_message_to_string_html(self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+ %newobject to_misc_string();
+ char *to_misc_string() {
+ char *str;
+ BEGIN_EXCEPTION
+ str = seaudit_message_to_misc_string(self);
+ if (!str) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return str;
+ };
+};
+%inline %{
+ seaudit_message_t *seaudit_message_from_void(void *x) {
+ return (seaudit_message_t*)x;
+ };
+%}
+
+/* seaudit load message */
+typedef struct seaudit_load_message {} seaudit_load_message_t;
+%extend seaudit_load_message_t {
+ seaudit_load_message_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create seaudit_load_message_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~seaudit_load_message_t() {
+ /* no op */
+ return;
+ };
+};
+%inline %{
+ seaudit_load_message_t *seaudit_load_message_from_void(void *msg) {
+ return (seaudit_load_message_t*)msg;
+ };
+%}
+
+/* seaudit bool message */
+typedef struct seaudit_bool_message {} seaudit_bool_message_t;
+%extend seaudit_bool_message_t {
+ seaudit_bool_message_t(void *msg) {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create seaudit_bool_message_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~seaudit_bool_message_t() {
+ /* no op */
+ return;
+ };
+};
+%inline %{
+ seaudit_bool_message_t *seaudit_bool_message_from_void(void *msg) {
+ return (seaudit_bool_message_t*)msg;
+ };
+%}
+
+/* seaudit avc message */
+typedef enum seaudit_avc_message_type
+{
+ SEAUDIT_AVC_UNKNOWN = 0,
+ SEAUDIT_AVC_DENIED,
+ SEAUDIT_AVC_GRANTED
+} seaudit_avc_message_type_e;
+typedef struct seaudit_avc_message {} seaudit_avc_message_t;
+%extend seaudit_avc_message_t {
+ seaudit_avc_message_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create seaudit_avc_message_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ ~seaudit_avc_message_t() {
+ /* no op */
+ return;
+ };
+ seaudit_avc_message_type_e get_message_type() {
+ return seaudit_avc_message_get_message_type(self);
+ };
+ long get_timestamp_nano() {
+ return seaudit_avc_message_get_timestamp_nano(self);
+ };
+ const char *get_source_user() {
+ return seaudit_avc_message_get_source_user(self);
+ };
+ const char *get_source_role() {
+ return seaudit_avc_message_get_source_role(self);
+ };
+ const char *get_source_type() {
+ return seaudit_avc_message_get_source_type(self);
+ };
+ const char *get_target_user() {
+ return seaudit_avc_message_get_target_user(self);
+ };
+ const char *get_target_role() {
+ return seaudit_avc_message_get_target_role(self);
+ };
+ const char *get_target_type() {
+ return seaudit_avc_message_get_target_type(self);
+ };
+ const char *get_object_class() {
+ return seaudit_avc_message_get_object_class(self);
+ };
+ const apol_string_vector_t *get_perm() {
+ return (apol_string_vector_t*)seaudit_avc_message_get_perm(self);
+ };
+ const char *get_exe() {
+ return seaudit_avc_message_get_exe(self);
+ };
+ const char *get_comm() {
+ return seaudit_avc_message_get_comm(self);
+ };
+ const char *get_name() {
+ return seaudit_avc_message_get_name(self);
+ };
+ int get_pid() {
+ return (int)seaudit_avc_message_get_pid(self);
+ };
+ long get_inode() {
+ return (long)seaudit_avc_message_get_inode(self);
+ };
+ const char *get_path() {
+ return seaudit_avc_message_get_path(self);
+ };
+ const char *get_dev() {
+ return seaudit_avc_message_get_dev(self);
+ };
+ const char *get_netif() {
+ return seaudit_avc_message_get_netif(self);
+ };
+ int get_port() {
+ return seaudit_avc_message_get_port(self);
+ };
+ const char *get_laddr() {
+ return seaudit_avc_message_get_laddr(self);
+ };
+ int get_lport() {
+ return seaudit_avc_message_get_lport(self);
+ };
+ const char *get_faddr() {
+ return seaudit_avc_message_get_faddr(self);
+ };
+ int get_fport() {
+ return seaudit_avc_message_get_fport(self);
+ };
+ const char *get_saddr() {
+ return seaudit_avc_message_get_saddr(self);
+ };
+ int get_sport() {
+ return seaudit_avc_message_get_sport(self);
+ };
+ const char *get_daddr() {
+ return seaudit_avc_message_get_daddr(self);
+ };
+ int get_dport() {
+ return seaudit_avc_message_get_dport(self);
+ };
+ int get_key() {
+ return seaudit_avc_message_get_key(self);
+ };
+ int get_cap() {
+ return seaudit_avc_message_get_cap(self);
+ };
+};
+%inline %{
+ seaudit_avc_message_t *seaudit_avc_message_from_void(void *msg) {
+ return (seaudit_avc_message_t*)msg;
+ };
+%}
+
+/* Java does not permit parsing directly from a file; parsing may only
+ be done through a memory buffer. */
+#ifndef SWIGJAVA
+int seaudit_log_parse(seaudit_log_t * log, FILE * syslog);
+#endif
+int seaudit_log_parse_buffer(seaudit_log_t * log, const char *buffer, const size_t bufsize);
+
+/* seaudit filter */
+typedef enum seaudit_filter_match
+{
+ SEAUDIT_FILTER_MATCH_ALL = 0,
+ SEAUDIT_FILTER_MATCH_ANY
+} seaudit_filter_match_e;
+typedef enum seaudit_filter_visible
+{
+ SEAUDIT_FILTER_VISIBLE_SHOW = 0,
+ SEAUDIT_FILTER_VISIBLE_HIDE
+} seaudit_filter_visible_e;
+typedef enum seaudit_filter_date_match
+{
+ SEAUDIT_FILTER_DATE_MATCH_BEFORE = 0,
+ SEAUDIT_FILTER_DATE_MATCH_AFTER,
+ SEAUDIT_FILTER_DATE_MATCH_BETWEEN
+} seaudit_filter_date_match_e;
+typedef struct seaudit_filter {} seaudit_filter_t;
+%extend seaudit_filter_t {
+ seaudit_filter_t(char *name = NULL) {
+ seaudit_filter_t *sf = NULL;
+ BEGIN_EXCEPTION
+ sf = seaudit_filter_create(name);
+ if (!sf) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return sf;
+ };
+ seaudit_filter_t(seaudit_filter_t *in) {
+ seaudit_filter_t *sf = NULL;
+ BEGIN_EXCEPTION
+ sf = seaudit_filter_create_from_filter(in);
+ if (!sf) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return sf;
+ };
+ ~seaudit_filter_t() {
+ seaudit_filter_destroy(&self);
+ };
+ void save(char *path) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_save_to_file(self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not save filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_match(seaudit_filter_match_e match) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_match(self, match)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set filter matching method");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ }
+ seaudit_filter_match_e get_match() {
+ return seaudit_filter_get_match(self);
+ };
+ void set_name(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_name(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set filter name");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_name() {
+ return seaudit_filter_get_name(self);
+ };
+ void set_description(char *description) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_description(self, description)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set filter description");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_description() {
+ return seaudit_filter_get_description(self);
+ };
+ void set_strict(bool is_strict) {
+ seaudit_filter_set_strict(self, is_strict);
+ };
+ bool get_strict() {
+ return seaudit_filter_get_strict(self);
+ };
+ void set_source_user(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_source_user(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source user list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_source_user() {
+ return (apol_string_vector_t*)seaudit_filter_get_source_user(self);
+ };
+ void set_source_role(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_source_role(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source role list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_source_role() {
+ return (apol_string_vector_t*)seaudit_filter_get_source_role(self);
+ };
+ void set_source_type(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_source_type(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source type list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_source_type() {
+ return (apol_string_vector_t*)seaudit_filter_get_source_type(self);
+ };
+ void set_target_user(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_target_user(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target user list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_target_user() {
+ return (apol_string_vector_t*)seaudit_filter_get_target_user(self);
+ };
+ void set_target_role(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_target_role(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target role list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_target_role() {
+ return (apol_string_vector_t*)seaudit_filter_get_target_role(self);
+ };
+ void set_target_type(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_target_type(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target type list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_target_type() {
+ return (apol_string_vector_t*)seaudit_filter_get_target_type(self);
+ };
+ void set_target_class(apol_string_vector_t *v) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_target_class(self, (apol_vector_t*)v)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set target class list for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_string_vector_t *get_target_class() {
+ return (apol_string_vector_t*)seaudit_filter_get_target_class(self);
+ };
+ void set_permission(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_permission(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set permission for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_permission() {
+ return seaudit_filter_get_permission(self);
+ };
+ void set_executable(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_executable(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set executable for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_executable() {
+ return seaudit_filter_get_executable(self);
+ };
+ void set_host(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_host(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set host for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_host() {
+ return seaudit_filter_get_host(self);
+ };
+ void set_path(char *path) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_path(self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set path for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_path() {
+ return seaudit_filter_get_path(self);
+ };
+ void set_command(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_command(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set command for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_inode(long inode) {
+ seaudit_filter_set_inode(self, (long) inode);
+ };
+ long get_inode() {
+ return (long) seaudit_filter_get_inode(self);
+ };
+ void set_pid(long pid) {
+ seaudit_filter_set_pid(self, (unsigned int) pid);
+ };
+ long get_pid() {
+ return (long) seaudit_filter_get_pid(self);
+ };
+ const char *get_command() {
+ return seaudit_filter_get_command(self);
+ };
+ void set_anyaddr(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_anyaddr(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set ip address for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_anyaddr() {
+ return seaudit_filter_get_anyaddr(self);
+ };
+ void set_anyport(int port) {
+ seaudit_filter_set_anyport(self, port);
+ };
+ int get_anyport() {
+ return seaudit_filter_get_anyport(self);
+ };
+ void set_laddr(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_laddr(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set local address for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_laddr() {
+ return seaudit_filter_get_laddr(self);
+ };
+ void set_lport(int port) {
+ seaudit_filter_set_lport(self, port);
+ };
+ int get_lport() {
+ return seaudit_filter_get_lport(self);
+ };
+ void set_faddr(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_faddr(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set foreign address for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_faddr() {
+ return seaudit_filter_get_faddr(self);
+ };
+ void set_fport(int port) {
+ seaudit_filter_set_fport(self, port);
+ };
+ int get_fport() {
+ return seaudit_filter_get_fport(self);
+ };
+ void set_saddr(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_saddr(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set source address for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_saddr() {
+ return seaudit_filter_get_saddr(self);
+ };
+ void set_sport(int port) {
+ seaudit_filter_set_sport(self, port);
+ };
+ int get_sport() {
+ return seaudit_filter_get_sport(self);
+ };
+ void set_daddr(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_daddr(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set destination address for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_daddr() {
+ return seaudit_filter_get_daddr(self);
+ };
+ void set_dport(int port) {
+ seaudit_filter_set_dport(self, port);
+ };
+ int get_dport() {
+ return seaudit_filter_get_dport(self);
+ };
+ void set_port(int port) {
+ seaudit_filter_set_port(self, port);
+ };
+ int get_port() {
+ return seaudit_filter_get_port(self);
+ };
+ void set_netif(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_netif(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set network interface for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const char *get_netif() {
+ return seaudit_filter_get_netif(self);
+ };
+ void set_key(int key) {
+ seaudit_filter_set_key(self, key);
+ };
+ int get_key() {
+ return seaudit_filter_get_key(self);
+ };
+ void set_cap(int cap) {
+ seaudit_filter_set_cap(self, cap);
+ };
+ int get_cap() {
+ return seaudit_filter_get_cap(self);
+ };
+ void set_message_type(seaudit_avc_message_type_e mtype) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_message_type(self, mtype)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set message type for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ seaudit_message_type_e get_message_type() {
+ return seaudit_filter_get_message_type(self);
+ };
+ void set_date(struct tm *start, struct tm *end, seaudit_filter_date_match_e match) {
+ BEGIN_EXCEPTION
+ if (seaudit_filter_set_date(self, start, end, match)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set date for filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const struct tm *get_start_date() {
+ const struct tm *s;
+ const struct tm *e;
+ seaudit_filter_date_match_e m;
+ seaudit_filter_get_date(self, &s, &e, &m);
+ return s;
+ };
+ const struct tm *get_end_date() {
+ const struct tm *s;
+ const struct tm *e;
+ seaudit_filter_date_match_e m;
+ seaudit_filter_get_date(self, &s, &e, &m);
+ return e;
+ };
+ seaudit_filter_date_match_e get_date_match() {
+ const struct tm *s;
+ const struct tm *e;
+ seaudit_filter_date_match_e m;
+ seaudit_filter_get_date(self, &s, &e, &m);
+ return m;
+ };
+};
+%newobject seaudit_filter_create_from_file(const char*);
+apol_vector_t *seaudit_filter_create_from_file(const char *filename);
+%inline %{
+ seaudit_filter_t *seaudit_filter_from_void(void *x) {
+ return (seaudit_filter_t*)x;
+ };
+%}
+
+/* seaudit sort */
+typedef struct seaudit_sort {} seaudit_sort_t;
+%extend seaudit_sort_t {
+ seaudit_sort_t() {
+ BEGIN_EXCEPTION
+ SWIG_exception(SWIG_RuntimeError, "Cannot directly create seaudit_sort_t objects");
+ END_EXCEPTION
+ fail:
+ return NULL;
+ };
+ seaudit_sort_t(seaudit_sort_t *in) {
+ seaudit_sort_t *ss = NULL;
+ BEGIN_EXCEPTION
+ ss = seaudit_sort_create_from_sort(in);
+ if (!ss) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return ss;
+ };
+ ~seaudit_sort_t() {
+ seaudit_sort_destroy(&self);
+ };
+};
+%newobject seaudit_sort_by_message_type(const int);
+seaudit_sort_t *seaudit_sort_by_message_type(const int direction);
+%newobject seaudit_sort_by_date(const int);
+seaudit_sort_t *seaudit_sort_by_date(const int direction);
+%newobject seaudit_sort_by_host(const int);
+seaudit_sort_t *seaudit_sort_by_host(const int direction);
+%newobject seaudit_sort_by_permission(const int);
+seaudit_sort_t *seaudit_sort_by_permission(const int direction);
+%newobject seaudit_sort_by_source_user(const int);
+seaudit_sort_t *seaudit_sort_by_source_user(const int direction);
+%newobject seaudit_sort_by_source_role(const int);
+seaudit_sort_t *seaudit_sort_by_source_role(const int direction);
+%newobject seaudit_sort_by_source_type(const int);
+seaudit_sort_t *seaudit_sort_by_source_type(const int direction);
+%newobject seaudit_sort_by_target_user(const int);
+seaudit_sort_t *seaudit_sort_by_target_user(const int direction);
+%newobject seaudit_sort_by_target_role(const int);
+seaudit_sort_t *seaudit_sort_by_target_role(const int direction);
+%newobject seaudit_sort_by_target_type(const int);
+seaudit_sort_t *seaudit_sort_by_target_type(const int direction);
+%newobject seaudit_sort_by_object_class(const int);
+seaudit_sort_t *seaudit_sort_by_object_class(const int direction);
+%newobject seaudit_sort_by_executable(const int);
+seaudit_sort_t *seaudit_sort_by_executable(const int direction);
+%newobject seaudit_sort_by_command(const int);
+seaudit_sort_t *seaudit_sort_by_command(const int direction);
+%newobject seaudit_sort_by_name(const int);
+seaudit_sort_t *seaudit_sort_by_name(const int direction);
+%newobject seaudit_sort_by_path(const int);
+seaudit_sort_t *seaudit_sort_by_path(const int direction);
+%newobject seaudit_sort_by_device(const int);
+seaudit_sort_t *seaudit_sort_by_device(const int direction);
+%newobject seaudit_sort_by_inode(const int);
+seaudit_sort_t *seaudit_sort_by_inode(const int direction);
+%newobject seaudit_sort_by_pid(const int);
+seaudit_sort_t *seaudit_sort_by_pid(const int direction);
+%newobject seaudit_sort_by_port(const int);
+extern seaudit_sort_t *seaudit_sort_by_port(const int direction);
+%newobject seaudit_sort_by_laddr(const int);
+extern seaudit_sort_t *seaudit_sort_by_laddr(const int direction);
+%newobject seaudit_sort_by_lport(const int);
+extern seaudit_sort_t *seaudit_sort_by_lport(const int direction);
+%newobject seaudit_sort_by_faddr(const int);
+extern seaudit_sort_t *seaudit_sort_by_faddr(const int direction);
+%newobject seaudit_sort_by_fport(const int);
+extern seaudit_sort_t *seaudit_sort_by_fport(const int direction);
+%newobject seaudit_sort_by_saddr(const int);
+extern seaudit_sort_t *seaudit_sort_by_saddr(const int direction);
+%newobject seaudit_sort_by_sport(const int);
+extern seaudit_sort_t *seaudit_sort_by_sport(const int direction);
+%newobject seaudit_sort_by_daddr(const int);
+extern seaudit_sort_t *seaudit_sort_by_daddr(const int direction);
+%newobject seaudit_sort_by_dport(const int);
+extern seaudit_sort_t *seaudit_sort_by_dport(const int direction);
+%newobject seaudit_sort_by_key(const int);
+extern seaudit_sort_t *seaudit_sort_by_key(const int direction);
+%newobject seaudit_sort_by_cap(const int);
+extern seaudit_sort_t *seaudit_sort_by_cap(const int direction);
+
+/* seaudit model */
+#ifdef SWIGPYTHON
+/* handle ownership of filters and sorts passed to the model */
+%typemap(in) seaudit_filter_t *filter {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_seaudit_filter, 0 | 0 );
+ $1 = (seaudit_filter_t*)x;
+}
+%typemap(in) seaudit_sort_t *ssort {
+ void *x = NULL;
+ Py_IncRef($input);
+ SWIG_ConvertPtr($input, &x,SWIGTYPE_p_seaudit_sort, 0 | 0 );
+ $1 = (seaudit_sort_t*)x;
+}
+#endif
+typedef struct seaudit_model {} seaudit_model_t;
+%extend seaudit_model_t {
+ seaudit_model_t(char *name = NULL, seaudit_log_t *slog = NULL) {
+ seaudit_model_t *smod;
+ BEGIN_EXCEPTION
+ smod = seaudit_model_create(name, slog);
+ if (!smod) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return smod;
+ };
+ seaudit_model_t(seaudit_model_t *in) {
+ seaudit_model_t *smod;
+ BEGIN_EXCEPTION
+ smod = seaudit_model_create_from_model(in);
+ if (!smod) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return smod;
+ };
+ seaudit_model_t(char *path) {
+ seaudit_model_t *smod;
+ BEGIN_EXCEPTION
+ smod = seaudit_model_create_from_file(path);
+ if (!smod) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return smod;
+ }
+ ~seaudit_model_t() {
+ seaudit_model_destroy(&self);
+ };
+ void save(char *path) {
+ BEGIN_EXCEPTION
+ if (seaudit_model_save_to_file(self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not save seaudit model");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ }
+ const char *get_name() {
+ return seaudit_model_get_name(self);
+ };
+ void set_name(char *name) {
+ BEGIN_EXCEPTION
+ if (seaudit_model_set_name(self, name)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set model name");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_log(seaudit_log_t *slog) {
+ BEGIN_EXCEPTION
+ if (seaudit_model_append_log(self, slog)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append log to model");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void append_filter(seaudit_filter_t *filter) {
+ BEGIN_EXCEPTION
+#ifdef SWIGJAVA /* duplicate so the garbage collector does not double free */
+ seaudit_filter_t *tmp = seaudit_filter_create_from_filter(filter);
+ if (seaudit_model_append_filter(self, tmp)) {
+ seaudit_filter_destroy(&tmp);
+ SWIG_exception(SWIG_RuntimeError, "Could not append filter to model");
+ }
+#else
+ if (seaudit_model_append_filter(self, filter)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append filter to model");
+ }
+#endif
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ const apol_vector_t *get_filters() {
+ return seaudit_model_get_filters(self);
+ };
+ %delobject remove_filter();
+ void remove_filter(seaudit_filter_t *filter) {
+ BEGIN_EXCEPTION
+ if (seaudit_model_remove_filter(self, filter)) {
+ SWIG_exception(SWIG_ValueError, "Could not remove filter");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_filter_match(seaudit_filter_match_e match) {
+ BEGIN_EXCEPTION
+ if (seaudit_model_set_filter_match(self, match)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set filter matching method for model");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ seaudit_filter_match_e get_filter_match() {
+ return seaudit_model_get_filter_match(self);
+ };
+ void set_filter_visible(seaudit_filter_visible_e vis) {
+ BEGIN_EXCEPTION
+ if (seaudit_model_set_filter_visible(self, vis)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set filter visibility for model");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ seaudit_filter_visible_e get_filter_visible() {
+ return seaudit_model_get_filter_visible(self);
+ };
+ void append_sort(seaudit_sort_t *ssort) {
+ BEGIN_EXCEPTION
+#ifdef SWIGJAVA
+ seaudit_sort_t *tmp = seaudit_sort_create_from_sort(ssort);
+ if (seaudit_model_append_sort(self, tmp)) {
+ seaudit_sort_destroy(&tmp);
+ SWIG_exception(SWIG_RuntimeError, "Could not append sort to model");
+ }
+#else
+ if (seaudit_model_append_sort(self, ssort)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not append sort to model");
+ }
+#endif
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void clear_sorts() {
+ BEGIN_EXCEPTION
+ if (seaudit_model_clear_sorts(self)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not clear model sorting criteria");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ int is_changed() {
+ return seaudit_model_is_changed(self);
+ };
+ %newobject get_messages(seaudit_log_t*);
+ apol_vector_t *get_messages(seaudit_log_t *slog) {
+ apol_vector_t *v = NULL;
+ BEGIN_EXCEPTION
+ v = seaudit_model_get_messages(slog, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ %newobject get_malformed_messages(seaudit_log_t*);
+ apol_vector_t *get_malformed_messages(seaudit_log_t *slog) {
+ apol_vector_t *v = NULL;
+ BEGIN_EXCEPTION
+ v = seaudit_model_get_malformed_messages(slog, self);
+ if (!v) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return v;
+ };
+ void hide_message(seaudit_message_t *message) {
+ seaudit_model_hide_message(self, message);
+ };
+ size_t get_num_allows(seaudit_log_t *slog) {
+ return seaudit_model_get_num_allows(slog, self);
+ };
+ size_t get_num_denies(seaudit_log_t *slog) {
+ return seaudit_model_get_num_denies(slog, self);
+ };
+ size_t get_num_bools(seaudit_log_t *slog) {
+ return seaudit_model_get_num_bools(slog, self);
+ };
+ size_t get_num_loads(seaudit_log_t *slog) {
+ return seaudit_model_get_num_loads(slog, self);
+ };
+};
+
+/* seaudit report */
+typedef enum seaudit_report_format
+{
+ SEAUDIT_REPORT_FORMAT_TEXT,
+ SEAUDIT_REPORT_FORMAT_HTML
+} seaudit_report_format_e;
+typedef struct seaudit_report {} seaudit_report_t;
+%extend seaudit_report_t {
+ seaudit_report_t(seaudit_model_t *m) {
+ seaudit_report_t *sr;
+ BEGIN_EXCEPTION
+ sr = seaudit_report_create(m);
+ if (!sr) {
+ SWIG_exception(SWIG_MemoryError, "Out of memory");
+ }
+ END_EXCEPTION
+ fail:
+ return sr;
+ };
+ ~seaudit_report_t() {
+ seaudit_report_destroy(&self);
+ };
+ void write(seaudit_log_t *slog, char *path) {
+ BEGIN_EXCEPTION
+ if (seaudit_report_write(slog, self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not write report to file");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_format(seaudit_log_t *slog, seaudit_report_format_e format) {
+ BEGIN_EXCEPTION
+ if (seaudit_report_set_format(slog, self, format)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set report format");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_configuration(seaudit_log_t *slog, char *path) {
+ BEGIN_EXCEPTION
+ if (seaudit_report_set_configuration(slog, self, path)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set report configuration file");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_stylesheet(seaudit_log_t *slog, char *path, int use_stylesheet) {
+ BEGIN_EXCEPTION
+ if (seaudit_report_set_stylesheet(slog, self, path, use_stylesheet)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set report stylesheet");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+ void set_malformed(seaudit_log_t *slog, int do_malformed) {
+ BEGIN_EXCEPTION
+ if (seaudit_report_set_malformed(slog, self, do_malformed)) {
+ SWIG_exception(SWIG_RuntimeError, "Could not set report malformed flag");
+ }
+ END_EXCEPTION
+ fail:
+ return;
+ };
+};
diff --git a/libseaudit/swig/tcl/Makefile.am b/libseaudit/swig/tcl/Makefile.am
new file mode 100644
index 0000000..b3c7c06
--- /dev/null
+++ b/libseaudit/swig/tcl/Makefile.am
@@ -0,0 +1,37 @@
+wrappedso_DATA = libtseaudit.so.@libseaudit_version@
+wrappedso_SONAME = @libseaudit_tswig_soname@
+short_name = libtseaudit.so
+wrappedsodir = $(libdir)/setools/seaudit
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../seaudit.i
+BUILT_SOURCES = seaudit_wrap.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libseaudit/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @SEAUDIT_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @XML_LIBS@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libseaudit/src/libseaudit.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) $(SWIG_TCL_OPT) -pkgversion @libseaudit_version@ -o $@ -I$(top_srcdir)/libseaudit/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libapol/swig -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+$(package_SCRIPTS): $(wrappedso_DATA)
+ echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src:$(top_builddir)/libseaudit/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) seaudit
+ cp $(wrappedso_DATA) $@ seaudit
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) seaudit/$(wrappedso_DATA) seaudit/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libseaudit/tests/Makefile.am b/libseaudit/tests/Makefile.am
new file mode 100644
index 0000000..47cef42
--- /dev/null
+++ b/libseaudit/tests/Makefile.am
@@ -0,0 +1,16 @@
+TESTS = libseaudit-tests
+check_PROGRAMS = libseaudit-tests
+
+libseaudit_tests_SOURCES = \
+ filters.c filters.h \
+ parse_file.c parse_file.h \
+ libseaudit-tests.c
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEAUDIT_CFLAGS@
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @SEAUDIT_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libseaudit_tests_DEPENDENCIES = ../src/libseaudit.so
diff --git a/libseaudit/tests/filters.c b/libseaudit/tests/filters.c
new file mode 100644
index 0000000..4279173
--- /dev/null
+++ b/libseaudit/tests/filters.c
@@ -0,0 +1,114 @@
+/**
+ * @file
+ *
+ * Test libseaudit's filtering capabilities.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <apol/util.h>
+#include <seaudit/log.h>
+#include <seaudit/message.h>
+#include <seaudit/model.h>
+#include <seaudit/parse.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#define MESSAGES_NOWARNS TEST_POLICIES "/setools-3.1/seaudit/messages-nowarns"
+
+static seaudit_log_t *l = NULL;
+static seaudit_model_t *m = NULL;
+
+static void filters_simple()
+{
+ seaudit_filter_t *f = seaudit_filter_create("simple filter");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(f);
+
+ int retval;
+ apol_vector_t *v = apol_str_split("system_u", ":");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(v);
+ retval = seaudit_filter_set_source_user(f, v);
+ CU_ASSERT(retval == 0);
+ apol_vector_destroy(&v);
+
+ retval = seaudit_model_append_filter(m, f);
+ CU_ASSERT(retval == 0);
+
+ v = seaudit_model_get_messages(l, m);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 5 + 5);
+ apol_vector_destroy(&v);
+
+ retval = seaudit_filter_set_strict(f, true);
+ CU_ASSERT(retval == 0);
+ v = seaudit_model_get_messages(l, m);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 5);
+ apol_vector_destroy(&v);
+
+ retval = seaudit_filter_set_strict(f, false);
+ CU_ASSERT(retval == 0);
+ v = seaudit_model_get_messages(l, m);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(v);
+ CU_ASSERT(apol_vector_get_size(v) == 5 + 5);
+ apol_vector_destroy(&v);
+}
+
+CU_TestInfo filters_tests[] = {
+ {"simple filter", filters_simple},
+ CU_TEST_INFO_NULL
+};
+
+int filters_init()
+{
+ l = seaudit_log_create(NULL, NULL);
+ if (l == NULL) {
+ return 1;
+ }
+ m = seaudit_model_create("filters", l);
+ if (m == NULL) {
+ return 1;
+ }
+
+ FILE *f = fopen(MESSAGES_NOWARNS, "r");
+ if (f == NULL) {
+ return 1;
+ }
+ int retval;
+ retval = seaudit_log_parse(l, f);
+ if (retval != 0) {
+ fclose(f);
+ return 1;
+ }
+
+ fclose(f);
+ return 0;
+}
+
+int filters_cleanup()
+{
+ seaudit_log_destroy(&l);
+ seaudit_model_destroy(&m);
+ return 0;
+}
diff --git a/libseaudit/tests/filters.h b/libseaudit/tests/filters.h
new file mode 100644
index 0000000..97f634b
--- /dev/null
+++ b/libseaudit/tests/filters.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for using filters in libseaudit.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FILTERS_H
+#define FILTERS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo filters_tests[];
+extern int filters_init();
+extern int filters_cleanup();
+
+#endif
diff --git a/libseaudit/tests/libseaudit-tests.c b/libseaudit/tests/libseaudit-tests.c
new file mode 100644
index 0000000..2b65ec6
--- /dev/null
+++ b/libseaudit/tests/libseaudit-tests.c
@@ -0,0 +1,54 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for libseaudit.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "filters.h"
+#include "parse_file.h"
+
+int main(void)
+{
+ if (CU_initialize_registry() != CUE_SUCCESS) {
+ return CU_get_error();
+ }
+
+ CU_SuiteInfo suites[] = {
+ {"Parse File", parse_file_init, parse_file_cleanup, parse_file_tests}
+ ,
+ {"Filters", filters_init, filters_cleanup, filters_tests}
+ ,
+ CU_SUITE_INFO_NULL
+ };
+
+ CU_register_suites(suites);
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ unsigned int num_failures = CU_get_number_of_failure_records();
+ CU_cleanup_registry();
+ return (int)num_failures;
+}
diff --git a/libseaudit/tests/parse_file.c b/libseaudit/tests/parse_file.c
new file mode 100644
index 0000000..a4bac21
--- /dev/null
+++ b/libseaudit/tests/parse_file.c
@@ -0,0 +1,113 @@
+/**
+ * @file
+ *
+ * Test libseaudit's log file parsing ability.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <seaudit/log.h>
+#include <seaudit/parse.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+
+struct log_answer
+{
+ const char *log_name;
+ bool has_warnings;
+};
+
+static void parse_file_test(const struct log_answer *la)
+{
+ seaudit_log_t *l = seaudit_log_create(NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(l);
+
+ FILE *f = fopen(la->log_name, "r");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(f);
+
+ int retval;
+ retval = seaudit_log_parse(l, f);
+ if (la->has_warnings) {
+ CU_ASSERT(retval > 0);
+ } else {
+ CU_ASSERT(retval == 0);
+ }
+
+ fclose(f);
+ seaudit_log_destroy(&l);
+}
+
+static void parse_file_fc4()
+{
+ struct log_answer l = {
+ TEST_POLICIES "/setools-3.0/seaudit/messages-FC4",
+ false
+ };
+ parse_file_test(&l);
+}
+
+static void parse_file_fc5()
+{
+ struct log_answer l = {
+ TEST_POLICIES "/setools-3.0/seaudit/messages-FC5",
+ false
+ };
+ parse_file_test(&l);
+}
+
+static void parse_file_nowarns()
+{
+ struct log_answer l = {
+ TEST_POLICIES "/setools-3.1/seaudit/messages-nowarns",
+ false
+ };
+ parse_file_test(&l);
+}
+
+static void parse_file_warnings()
+{
+ struct log_answer l = {
+ TEST_POLICIES "/setools-3.1/seaudit/messages-warnings",
+ true
+ };
+ parse_file_test(&l);
+}
+
+CU_TestInfo parse_file_tests[] = {
+ {"FC4 log", parse_file_fc4},
+ {"FC5 log", parse_file_fc5},
+ {"messages-nowarns", parse_file_nowarns},
+ {"messages-warnings", parse_file_warnings},
+ CU_TEST_INFO_NULL
+};
+
+int parse_file_init()
+{
+ return 0;
+}
+
+int parse_file_cleanup()
+{
+ return 0;
+}
diff --git a/libseaudit/tests/parse_file.h b/libseaudit/tests/parse_file.h
new file mode 100644
index 0000000..e403e57
--- /dev/null
+++ b/libseaudit/tests/parse_file.h
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for parsing selinux audit logs from a file pointer.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PARSE_FILE_H
+#define PARSE_FILE_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo parse_file_tests[];
+extern int parse_file_init();
+extern int parse_file_cleanup();
+
+#endif
diff --git a/libsefs/Makefile.am b/libsefs/Makefile.am
new file mode 100644
index 0000000..5a0aa81
--- /dev/null
+++ b/libsefs/Makefile.am
@@ -0,0 +1,8 @@
+if DO_SWIGIFY
+ MAYBE_SWIG = swig
+endif
+
+SUBDIRS = src include tests $(MAYBE_SWIG)
+
+libsefs.a libsefs.so:
+ $(MAKE) -C src $@
diff --git a/libsefs/include/Makefile.am b/libsefs/include/Makefile.am
new file mode 100644
index 0000000..6f6c411
--- /dev/null
+++ b/libsefs/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = sefs
diff --git a/libsefs/include/sefs/Makefile.am b/libsefs/include/sefs/Makefile.am
new file mode 100644
index 0000000..f38dc4b
--- /dev/null
+++ b/libsefs/include/sefs/Makefile.am
@@ -0,0 +1,10 @@
+sefsdir = $(includedir)/sefs
+
+sefs_HEADERS = \
+ db.hh \
+ entry.hh \
+ fcfile.hh \
+ fclist.hh \
+ filesystem.hh \
+ query.hh \
+ util.h
diff --git a/libsefs/include/sefs/db.hh b/libsefs/include/sefs/db.hh
new file mode 100644
index 0000000..2355547
--- /dev/null
+++ b/libsefs/include/sefs/db.hh
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * Defines the public interface for the database fc list object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_DB_H
+#define SEFS_DB_H
+
+#include <sefs/fclist.hh>
+#include <sefs/filesystem.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <time.h>
+#include <apol/bst.h>
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents a database that maps files to their SELinux
+ * file contexts.
+ */
+class sefs_db:public sefs_fclist
+{
+#ifndef SWIG_FRIENDS
+ // private functions -- do not call these directly from
+ // outside of the library
+ friend int db_create_from_filesystem(sefs_fclist * fclist __attribute__ ((unused)), const sefs_entry * entry, void *arg);
+ friend struct sefs_context_node *db_get_context(sefs_db *, const char *, const char *, const char *,
+ const char *) throw(std::bad_alloc);
+ friend sefs_entry *db_get_entry(sefs_db *, const struct sefs_context_node *, uint32_t, const char *, ino64_t,
+ const char *) throw(std::bad_alloc);
+#endif
+
+ public:
+
+ /**
+ * Allocate and return a new sefs database initialized with
+ * entries from the filesystem \a fs.
+ * @param fs Sefs filesystem from which to create the database.
+ * @param msg_callback Callback to invoke as errors/warnings are
+ * generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to the
+ * callback function.
+ * @exception std::invalid_argument Filesystem does not exist.
+ * @exception std::runtime_error Error while reading the
+ * database.
+ */
+ sefs_db(sefs_filesystem * fs, sefs_callback_fn_t msg_callback, void *varg) throw(std::invalid_argument,
+ std::runtime_error);
+
+ /**
+ * Allocate and return a new sefs database, loading the
+ * entries from an existing database stored at \a path.
+ * @param filename Name of a sefs database from which to load.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::invalid_argument Database does not exist.
+ * @exception std::runtime_error Error while reading the
+ * database.
+ */
+ sefs_db(const char *filename, sefs_callback_fn_t msg_callback, void *varg) throw(std::invalid_argument,
+ std::runtime_error);
+
+ ~sefs_db();
+
+ /**
+ * Perform a sefs query on this database object, and then
+ * invoke a callback upon each matching entry. Entries will
+ * be returned in alphabetical order by path.
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this database, pointer to a matching entry, and an
+ * arbitrary data pointer. It should return a non-negative
+ * value upon success, negative value upon error and to abort
+ * the mapping.
+ * @param data Arbitrary pointer to be passed into \fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the database has no entries then
+ * return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the database.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts stored in this database contain
+ * MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ bool isMLS() const;
+
+ /**
+ * Write a database to disk, overwriting any existing file.
+ * The database may then be read by calling the appropriate
+ * constructor.
+ * @param filename Name of file to which write.
+ * @exception std::invalid_argument No filename given.
+ * @exception std::runtime_error Error while writing the
+ * database.
+ */
+ void save(const char *filename) throw(std::invalid_argument, std::runtime_error);
+
+ /**
+ * Get the creation time of a sefs database.
+ * @return Creation time of the database, or 0 on error.
+ */
+ time_t getCTime() const;
+
+ /**
+ * Determine if the given file is a valid sefs_db. This
+ * does not thoroughly load the file, rather just the header
+ * of the file.
+ * @param filename Name of file to check.
+ * @return True if the file appears to be a database, false if not.
+ */
+ static bool isDB(const char *filename);
+
+ private:
+ /**
+ * Upgrade an existing version 1 database to version 2.
+ */
+ void upgradeToDB2() throw(std::runtime_error);
+
+ const struct sefs_context_node *getContextNode(const sefs_entry * entry);
+ sefs_entry *getEntry(const struct sefs_context_node *context, uint32_t objectClass, const char *path, ino64_t inode,
+ const char *dev) throw(std::bad_alloc);
+ struct sqlite3 *_db;
+ time_t _ctime;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_db sefs_db_t;
+ typedef struct sefs_filesystem sefs_filesystem_t;
+
+/**
+ * Allocate and return a new sefs database from the filesystem \a fs.
+ * @see sefs_db::sefs_db(const sefs_filesystem &fs, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_db_create_from_filesystem(sefs_filesystem_t * fs, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Allocate and return a new sefs database, loading the entries from
+ * the saved database \a path.
+ * @see sefs_db::sefs_db(const char *filename, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_db_create_from_file(const char *path, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Write a database to disk, overwriting any existing file.
+ * @see sefs_db::save()
+ */
+ extern int sefs_db_save(sefs_db_t * db, const char *filename);
+
+/**
+ * Get the creation time of a sefs database.
+ * @see sefs_db::getCTime()
+ */
+ extern time_t sefs_db_get_ctime(sefs_db_t * db);
+
+/**
+ * Determine if the given file is a valid sefs_db.
+ * @see sefs_db::isDB()
+ */
+ extern bool sefs_db_is_db(const char *filename);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_DB_H */
diff --git a/libsefs/include/sefs/entry.hh b/libsefs/include/sefs/entry.hh
new file mode 100644
index 0000000..34fae73
--- /dev/null
+++ b/libsefs/include/sefs/entry.hh
@@ -0,0 +1,222 @@
+/**
+ * @file
+ * Defines the public interface for file context entries.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_ENTRY_H
+#define SEFS_ENTRY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/types.h>
+#include <apol/context-query.h>
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+class sefs_fclist;
+struct sefs_context_node;
+
+/**
+ * This class represents an individual entry within a list an fcfile
+ * object. Note that the entry's contents (even upon a
+ * copy-constructed version of the entry) are always tied to its
+ * fclist, so do not access entries whose fclist has been destroyed.
+ */
+class sefs_entry
+{
+ friend class sefs_db;
+ friend class sefs_fcfile;
+ friend class sefs_filesystem;
+
+ public:
+
+ /**
+ * Perform a deep copy of an entry object.
+ */
+ sefs_entry(const sefs_entry * e);
+
+ ~sefs_entry();
+
+ /**
+ * Get the context from a sefs entry. If the entry has no
+ * context (such as being marked <tt>&lt;&lt;none&gt;&gt;</tt>
+ * in a file_contexts file) then apol_context_get_user() and
+ * others will return an empty string.
+ * @return A pointer to the context, or NULL on error. The
+ * caller should not modify or destroy the returned context.
+ */
+ const apol_context_t *context() const;
+
+ /**
+ * Get the inode number associated with a sefs entry.
+ * @return Inode number associated with the entry or 0 on
+ * error. Entries originating from a file_contexts object
+ * will have no inode and thus return 0.
+ */
+ ino64_t inode() const;
+
+ /**
+ * Get the device name associated with a sefs entry. For
+ * example, if /dev/sda5 is mounted as /home, the device name
+ * for entry "/home/gburdell" will be "/dev/sda5".
+ * @return Device number associated with the entry or NULL on
+ * error. Do not free() this value. Entries originating from
+ * a file_contexts object will have no device name and thus
+ * return NULL.
+ */
+ const char *dev() const;
+
+ /**
+ * Get the object class associated with a sefs entry. The
+ * returned value will be one of one of QPOL_CLASS_ALL,
+ * QPOL_CLASS_FILE, etc., as defined in
+ * <qpol/genfscon_query.h>. If this returns QPOL_CLASS_ALL
+ * then the entry is associated with all object classes.
+ * @return Entry's object class. Upon error return
+ * QPOL_CLASS_ALL.
+ * @see apol_objclass_to_str() to convert the value to a
+ * string.
+ */
+ uint32_t objectClass() const;
+
+ /**
+ * Get the paths associated with a sefs entry.
+ * @return Path for the entry.If the entry came from a
+ * file_contexts object the paths will be a regular expression
+ * rather than literal paths. Do not free() this pointer.
+ */
+ const char *path() const;
+
+ /**
+ * Get the file from which a sefs entry originated.
+ * This function is only meaningful when entries are returned
+ * from a query on a modular file context file.
+ * @return The path of the file (policy package or source
+ * file) providing the entry or NULL if the entry is not from
+ * a module. Do not free() this pointer.
+ */
+ const char *origin() const;
+
+ /**
+ * Return a string representation of this entry. The string
+ * is suitable for printing to the screen or to a
+ * file_contexts file.
+ * @return An allocated string representation. The caller is
+ * responsibily for free()ing the string afterwards.
+ * @exception std::bad_alloc Out of memory.
+ */
+ char *toString() const throw(std::bad_alloc);
+
+ private:
+ /**
+ * Create a blank entry. The entity creating this entry is
+ * responsible for setting additional values as needed.
+ * @param fclist List that will contain this entry. This
+ * constructor will not add itself to the fclist.
+ * @param new_context Context node containing the SELinux
+ * context.
+ * @param new_objectClass Object class for the entry.
+ * @param new_path Path to this entry. The entry will share
+ * this pointer.
+ * @param new_origin Name of file_contexts file from which
+ * this entry originated. The entry will share this pointer.
+ * @exception std::bad_alloc Out of memory.
+ */
+ sefs_entry(class sefs_fclist * fclist, const struct sefs_context_node *new_context, uint32_t new_objectClass,
+ const char *new_path, const char *new_origin = NULL);
+
+ // note that entry does not own any of these pointers; they
+ // are shallow copies into the fclist's BST
+ class sefs_fclist *_fclist;
+ const struct sefs_context_node *_context;
+ ino64_t _inode;
+ const char *_dev;
+ uint32_t _objectClass;
+ const char *_path, *_origin;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_entry sefs_entry_t;
+
+/**
+ * Get the context from a sefs entry.
+ * @see sefs_entry::context()
+ */
+ extern const apol_context_t *sefs_entry_get_context(const sefs_entry_t * ent);
+
+/**
+ * Get the inode number associated with a sefs entry.
+ * @see sefs_entry::inode()
+ */
+ extern ino64_t sefs_entry_get_inode(const sefs_entry_t * ent);
+
+/**
+ * Get the device number associated with a sefs entry.
+ * @see sefs_entry::dev()
+ */
+ extern const char *sefs_entry_get_dev(const sefs_entry_t * ent);
+
+/**
+ * Get the object class associated with a sefs entry.
+ * @see sefs_entry::objectClass()
+ */
+ extern uint32_t sefs_entry_get_object_class(const sefs_entry_t * ent);
+
+/**
+ * Get the path associated with a sefs entry.
+ * @see sefs_entry::path()
+ */
+ extern const char *sefs_entry_get_path(const sefs_entry_t * ent);
+
+/**
+ * Get the file from which a sefs entry originated.
+ * @see sefs_entry::origin()
+ */
+ extern const char *sefs_entry_get_origin(const sefs_entry_t * ent);
+
+/**
+ * Return a string representation of this entry.
+ * @see sefs_entry::toString()
+ */
+ extern char *sefs_entry_to_string(const sefs_entry_t * ent);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_ENTRY_H */
diff --git a/libsefs/include/sefs/fcfile.hh b/libsefs/include/sefs/fcfile.hh
new file mode 100644
index 0000000..280a957
--- /dev/null
+++ b/libsefs/include/sefs/fcfile.hh
@@ -0,0 +1,261 @@
+/**
+ * @file
+ * Defines the public interface for the file_context set fc list object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_FCFILE_H
+#define SEFS_FCFILE_H
+
+#include <sefs/fclist.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents file contexts entry as read from a file,
+ * typically name file_contexts.
+ */
+class sefs_fcfile:public sefs_fclist
+{
+ public:
+
+ /**
+ * Allocate and return a new (and empty) sefs file_context set
+ * structure.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::bad_alloc if out of memory
+ */
+ sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc);
+
+ /**
+ * Allocate and return a new sefs file_context set structure
+ * from a single file_contexts file.
+ * @param file File contexts file to read.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the vector is NULL
+ * @exception std::runtime_error if the give file could not be
+ * read or is the wrong format
+ */
+ sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc, std::invalid_argument,
+ std::runtime_error);
+
+ /**
+ * Allocate and return a new sefs file_context set structure
+ * from a list of file_context files.
+ * @param files Vector of file contexts filenames (of type
+ * char *) to read.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the vector is NULL
+ * @exception std::runtime_error if a given file could not
+ * be read or is the wrong format
+ */
+ sefs_fcfile(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc,
+ std::invalid_argument,
+ std::runtime_error);
+
+ ~sefs_fcfile();
+
+ /**
+ * Perform a sefs query on this fcfile object, and then invoke
+ * a callback upon each matching entry. Mapping occurs in the
+ * order of entries as given by the file_contexts, and in the
+ * order that file_contexts were appended (via appendFile())
+ * to this object.
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this fclist, pointer to a matching entry, and an arbitrary
+ * data pointer. It should return a non-negative value upon
+ * success, negative value upon error and to abort the
+ * mapping.
+ * @param data Arbitrary pointer to be passed into \fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the fcfile has no entries then return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the fclist.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts in the fcfile contain MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ bool isMLS() const;
+
+ /**
+ * Append a file_contexts file to a sefs file contexts file
+ * set. If the fcfile already has a non-MLS file, subsequent
+ * appends must also be to non-MLS files. Likewise, if the
+ * fcfile already has an MLS file the file to be append must
+ * also be MLS.
+ * @param file File containing entries to append.
+ * @return 0 on success or < 0 on failure; if the call fails,
+ * the fcfile will be unchanged.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the file name is NULL
+ * @exception std::runtime_error if a given file could not
+ * be read or is the wrong format
+ */
+ int appendFile(const char *file) throw(std::bad_alloc, std::invalid_argument, std::runtime_error);
+
+ /**
+ * Append a list of file_context files to a sefs file contexts
+ * file set. If the fcfile already has a non-MLS file,
+ * subsequent appends must also be to non-MLS files.
+ * Likewise, if the fcfile already has an MLS file the file to
+ * be append must also be MLS.
+ * @param files Vector of filenames (type char *) to append;
+ * these files will be appended in the order they appear in
+ * the vector.
+ * @return The number of files successfully appended. If the
+ * value returned is less than the size of the vector, then
+ * file at index (returned value) failed. If append fails for
+ * any file, the operation stops at that file; it is safe to
+ * attempt to append the files remaining after the
+ * unsuccessful file.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::invalid_argument if the vector is NULL
+ * @exception std::runtime_error if a given file could not
+ * be read or is the wrong format
+ */
+ size_t appendFileList(const apol_vector_t * files) throw(std::bad_alloc, std::invalid_argument, std::runtime_error);
+
+ /**
+ * Get a list of all files contributing to the entries in a
+ * sefs file_contexts set.
+ * @return Vector of file paths (char *) of all files
+ * contributing to the set; the caller should not destroy or
+ * otherwise modify the returned vector.
+ */
+ const apol_vector_t *fileList() const;
+
+ private:
+
+ /**
+ * Parse a single line from a file_contexts file (or from any
+ * other source of file contexts information), and then add
+ * the resulting sefs_entry into the vector of entries.
+ * @param origin File from which this line originated.
+ * @param line File contexts line to parse.
+ * @param line_regex Compiled regular expression pattern for
+ * an entire line.
+ * @param context_regex Compiled regular expression pattern
+ * for the SELinux portion of a line.
+ * @exception std::bad_alloc if out of memory
+ * @exception std::runtime_error if the give file could not be
+ * read or is the wrong format
+ */
+ void parse_line(const char *origin, const char *line, regex_t * line_regex, regex_t * context_regex) throw(std::bad_alloc,
+ std::
+ runtime_error);
+
+ apol_vector_t *_files, *_entries;
+ bool _mls, _mls_set;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_fcfile sefs_fcfile_t;
+
+/**
+ * Allocate and return a new sefs file_context set structure.
+ * @see sefs_fcfile::sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_fcfile_create(sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Allocate and return a new sefs file_context set structure from a
+ * single file_contexts file.
+ * @see sefs_fcfile::sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_fcfile_create_from_file(const char *file, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Allocate and return a new sefs file_context set structure from a
+ * list of file_context files.
+ * @see sefs_fcfile::sefs_fcfile(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg)
+ */
+ extern sefs_fclist_t *sefs_fcfile_create_from_file_list(const apol_vector_t * files, sefs_callback_fn_t msg_callback,
+ void *varg);
+
+/**
+ * Append a file_contexts file to a sefs file contexts file set.
+ * @return 0 on success or < 0 on failure; if the call fails, the
+ * fcfile will be unchanged.
+ * @see sefs_fcfile::appendFile()
+ */
+ extern int sefs_fcfile_append_file(sefs_fcfile_t * fcfile, const char *file);
+
+/**
+ * Append a list of file_context files to a sefs file contexts file
+ * set.
+ * @see sefs_fcfile::appendFileList()
+ */
+ extern size_t sefs_fcfile_append_file_list(sefs_fcfile_t * fcfile, const apol_vector_t * files);
+
+/**
+ * Get a list of all files contributing to the entries in a sefs
+ * file_contexts set.
+ * @see sefs_fcfile::fileList()
+ */
+ extern const apol_vector_t *sefs_fcfile_get_file_list(const sefs_fcfile_t * fcfile);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_FCFILE_H */
diff --git a/libsefs/include/sefs/fclist.hh b/libsefs/include/sefs/fclist.hh
new file mode 100644
index 0000000..63eeeba
--- /dev/null
+++ b/libsefs/include/sefs/fclist.hh
@@ -0,0 +1,323 @@
+/**
+ * @file
+ * Defines the public interface for the file context list abstract
+ * object. A user must call a constructor for one of sefs_fcfile_t,
+ * sefs_db_t, or sefs_filesystem_t to create a sefs_fclist_t object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_FCLIST_H
+#define SEFS_FCLIST_H
+
+#include <sefs/entry.hh>
+#include <sefs/query.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <selinux/selinux.h>
+#include <stdarg.h>
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include <apol/policy.h>
+
+#define SEFS_MSG_ERR 1 /*!< Message describes a fatal error. */
+#define SEFS_MSG_WARN 2 /*!< Message is issued as a warning but does not represent a fatal error. */
+#define SEFS_MSG_INFO 3 /*!< Message is issued for inormational reasons and does not represent an atypical state. */
+
+ struct sefs_fclist;
+
+ typedef void (*sefs_callback_fn_t) (void *varg, const struct sefs_fclist * fclist, int level, const char *fmt,
+ va_list argp);
+
+/**
+ * Possible types of fclist for use with sefs_fclist_get_data().
+ */
+ typedef enum sefs_fclist_type
+ {
+ SEFS_FCLIST_TYPE_NONE = 0, /*!< Not an actual type, used for error conditions */
+ SEFS_FCLIST_TYPE_FILESYSTEM, /*!< get_data returns sefs_filesystem_t, a representation of a file system */
+ SEFS_FCLIST_TYPE_FCFILE, /*!< get_data returns sefs_fcfile_t, a representation of a collection of file_context files */
+ SEFS_FCLIST_TYPE_DB /*!< get_data returns sefs_db_t, a representation of a database of file system contexts */
+ } sefs_fclist_type_e;
+
+/**
+ * Invoke a sefs_fclist_t's callback for an error, passing it a format
+ * string and arguments.
+ */
+#define SEFS_ERR(fclist, format, ...) sefs_fclist_handleMsg(fclist, SEFS_MSG_ERR, format, __VA_ARGS__)
+
+/**
+ * Invoke a sefs_fclist_t's callback for a warning, passing it a
+ * format string and arguments.
+ */
+#define SEFS_WARN(fclist, format, ...) sefs_fclist_handleMsg(fclist, SEFS_MSG_WARN, format, __VA_ARGS__)
+
+/**
+ * Invoke a sefs_fclist's callback for an informational message,
+ * passing it a format string and arguments.
+ */
+#define SEFS_INFO(fclist, format, ...) sefs_fclist_handleMsg(fclist, SEFS_MSG_INFO, format, __VA_ARGS__)
+
+ extern void sefs_fclist_handleMsg(const struct sefs_fclist *fclist, int level, const char *fmt, ...);
+
+ __attribute__ ((format(printf, 3, 4))) void sefs_fclist_handleMsg(const struct sefs_fclist *fclist, int level,
+ const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+struct apol_bst;
+struct context_node;
+
+#define SEFS_MAP_FUNC_DEFINED
+
+/**
+ * Function invoked upon each matching file context entry during a query.
+ */
+typedef int (*sefs_fclist_map_fn_t) (sefs_fclist *, const sefs_entry *, void *);
+/**
+ * An abstract class the represents a list of file contexts. Contexts
+ * may be read from a filesystem, inferred from a file_contexts file,
+ * or read from a database.
+ */
+class sefs_fclist
+{
+#ifndef SWIG_FRIENDS
+ friend void sefs_fclist_handleMsg(const sefs_fclist * fclist, int level, const char *fmt, ...);
+ friend class sefs_entry;
+#endif
+
+ public:
+ virtual ~sefs_fclist();
+
+ /**
+ * Perform a sefs query on the given file context list object,
+ * and then invoke a callback upon each matching entry.
+ * Mapping occurs in the order of entries as specified by the
+ * file context list.
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this fclist, pointer to a matching entry, and an arbitrary
+ * data pointer. It should return a non-negative value upon
+ * success, negative value upon error and to abort the
+ * mapping. Be aware that the entry may go out of scope upon
+ * conclusion of runQueryMap(), so \a fn will need to clone
+ * the entry if it needs it later.
+ * <p>
+ * <b>This function must not throw any exceptions.</b> Doing
+ * so will most likely corrupt fclist's internal state.
+ * Instead, return a negative value to abort processing.
+ * @param data Arbitrary pointer to be passed into \a fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the fclist has no entries then return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the fclist.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ virtual int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
+ std::invalid_argument) = 0;
+
+ /**
+ * Perform a sefs query on the given file context list object
+ * and return a list of matching entries.
+ * @param query Query object containing search parameters. If
+ * NULL, return all contexts.
+ * @return A newly allocated unsorted vector (of class
+ * sefs_entry *) containing all entries matching the query.
+ * Do not modify the returned entries. Note that the vector
+ * may be empty. The caller is responsible for calling
+ * apol_vector_destroy() on the returned vector.
+ * @exception std::bad_alloc Out of memory.
+ * @exception std::runtime_error Error while reading contexts
+ * from the fclist.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ apol_vector_t *runQuery(sefs_query * query) throw(std::bad_alloc, std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts in the fclist contain MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ virtual bool isMLS() const = 0;
+
+ /**
+ * Associate a policy with the fclist. This is needed to
+ * resolve attributes and MLS ranges in queries. If a policy
+ * is already associated, then calling this function removes
+ * that previous association.
+ * @param policy Policy to associate with \a fclist. If NULL,
+ * remove any policy association. While \a policy is
+ * associated with \a fclist the caller should not destroy \a
+ * policy.
+ * @see sefs_query_set_type()
+ * @see sefs_query_set_range()
+ */
+ void associatePolicy(apol_policy_t * new_policy);
+
+ /**
+ * Return the policy currently associated with this fclist.
+ * Do not destroy the policy without first unassociating it
+ * (via call to sefs_fclist::associatePolicy(NULL)).
+ * @return Currently associated policy, or NULL if none is
+ * set.
+ */
+ apol_policy_t *associatePolicy() const;
+
+ /**
+ * Get the type of fclist object represented by \a fclist.
+ * @return The type of fclist object or SEFS_FCLIST_TYPE_NONE
+ * on error.
+ */
+ sefs_fclist_type_e fclist_type() const;
+
+ protected:
+ sefs_fclist(sefs_fclist_type_e type, sefs_callback_fn_t callback, void *varg) throw(std::bad_alloc);
+
+ /**
+ * Given the parts of a context, return a context node (which
+ * would contain an apol_context_t). If the context already
+ * exists, then a pointer to the existing one is returned.
+ *
+ * @param user User component of the context. The string will
+ * be duplicated.
+ * @param role Role component of the context. The string will
+ * be duplicated.
+ * @param type Type component of the context. The string will
+ * be duplicated.
+ * @param range Range component of the context. The string
+ * will be duplicated, or NULL if no range exists.
+ *
+ * @return A context node. Do not free() it.
+ */
+ struct sefs_context_node *getContext(const char *user, const char *role, const char *type,
+ const char *range) throw(std::bad_alloc);
+
+ /**
+ * Given a SELinux security context, return a context node
+ * (which would contain an apol_context_t). If the context
+ * already exists, then a pointer to the existing one is
+ * returned.
+ *
+ * @param scon Security context from which to obtain a node.
+ *
+ * @return A context node. Do not free() it.
+ */
+ struct sefs_context_node *getContext(const security_context_t scon) throw(std::bad_alloc);
+
+ apol_policy_t *policy;
+ struct apol_bst *user_tree, *role_tree, *type_tree, *range_tree, *path_tree;
+ struct apol_bst *dev_tree;
+ struct apol_bst *context_tree;
+
+ private:
+
+ /**
+ * Write a message to the callback stored within a fclist
+ * error handler. If the msg_callback field is empty, then
+ * the default message callback will be used.
+ * @param level Severity of message, one of SEFS_MSG_*.
+ * @param fmt Format string to print, using syntax of
+ * printf(3).
+ */
+ void handleMsg(int level, const char *fmt, va_list va_args) const;
+
+ sefs_callback_fn_t _callback;
+ void *_varg;
+ sefs_fclist_type_e _fclist_type;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_fclist sefs_fclist_t;
+
+#ifndef SEFS_MAP_FUNC_DEFINED
+ struct sefs_fclist;
+ struct sefs_entry;
+ typedef int (*sefs_fclist_map_fn_t) (struct sefs_fclist *, const struct sefs_entry *, void *);
+#endif
+
+/**
+ * Deallocate all memory associated with the referenced fclist object,
+ * and then set it to NULL. This function does nothing if the fclist
+ * object is already NULL.
+ * @param Reference to a fclist object to destroy.
+ */
+ extern void sefs_fclist_destroy(sefs_fclist_t ** fclist);
+
+/**
+ * Perform a sefs query on the given file context list object.
+ * @see sefs_fclist::runQueryMap()
+ */
+ extern int sefs_fclist_run_query_map(sefs_fclist_t * fclist, sefs_query_t * query, sefs_fclist_map_fn_t fn, void *data);
+
+/**
+ * Perform a sefs query on the given file context list object.
+ * @see sefs_fclist::runQuery()
+ */
+ extern apol_vector_t *sefs_fclist_run_query(sefs_fclist_t * fclist, sefs_query_t * query);
+
+/**
+ * Determine if the contexts in the fclist contain MLS fields.
+ * @see sefs_fclist::isMLS()
+ */
+ extern bool sefs_fclist_get_is_mls(const sefs_fclist_t * fclist);
+
+/**
+ * Associate a policy with the fclist.
+ * @see sefs_fclist::associatePolicy()
+ * @see sefs_query_set_type()
+ * @see sefs_query_set_range()
+ */
+ extern void sefs_fclist_associate_policy(sefs_fclist_t * fclist, apol_policy_t * policy);
+
+/**
+ * Get the type of fclist object represented by \a fclist.
+ * @see sefs_fclist::fclist_type()
+ */
+ extern sefs_fclist_type_e sefs_fclist_get_fclist_type(const sefs_fclist_t * fclist);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_FCLIST_H */
diff --git a/libsefs/include/sefs/filesystem.hh b/libsefs/include/sefs/filesystem.hh
new file mode 100644
index 0000000..1d2c9ed
--- /dev/null
+++ b/libsefs/include/sefs/filesystem.hh
@@ -0,0 +1,181 @@
+/**
+ * @file
+ * Defines the public interface for the filesystem fc list object.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_FILESYSTEM_H
+#define SEFS_FILESYSTEM_H
+
+#include <sefs/fclist.hh>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents the SELinux file contexts on a local on-disk
+ * filesystem. Be aware that the object will recurse beginning from
+ * the root directory, so if there are circular mounts (e.g., via
+ * something mounted with the 'bind' option) then queries against the
+ * filesystem will never terminate.
+ */
+class sefs_filesystem:public sefs_fclist
+{
+#ifndef SWIG_FRIENDS
+ // private functions -- do not call these directly from
+ // outside of the library
+ friend struct sefs_context_node *filesystem_get_context(sefs_filesystem *, security_context_t) throw(std::bad_alloc);
+ friend sefs_entry *filesystem_get_entry(sefs_filesystem *, const struct sefs_context_node *, uint32_t,
+ const char *, ino64_t, const char *) throw(std::bad_alloc);
+ friend bool filesystem_is_query_match(sefs_filesystem *, const sefs_query *, const char *, const char *,
+ const struct stat64 *, apol_vector_t *, apol_mls_range_t *) throw(std::runtime_error);
+#endif
+
+ public:
+
+ /**
+ * Allocate and return a new sefs filesystem structure
+ * representing the filesystem rooted at directory \a root.
+ * <b>Be aware that the constructor is not thread-safe.</b>
+ * @param new_root Directory to use as the root of the
+ * filesystem. This object represents this directory and all
+ * subdirectories, including other mounted filesystems.
+ * @param msg_callback Callback to invoke as errors/warnings
+ * are generated. If NULL, write messages to standard error.
+ * @param varg Value to be passed as the first parameter to
+ * the callback function.
+ * @exception bad_alloc Out of memory.
+ * @exception invalid_argument Root directory does not exist.
+ * @exception runtime_error Could not open root directory or
+ * /etc/mtab.
+ */
+ sefs_filesystem(const char *new_root, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc,
+ std::invalid_argument,
+ std::runtime_error);
+
+ ~sefs_filesystem();
+
+ /**
+ * Perform a sefs query on this filesystem object, and then
+ * invoke a callback upon each matching entry. Mapping is in
+ * pre-order (i.e., directories will be mapped prior to files
+ * and subdirectories they contain.)
+ * @param query Query object containing search parameters. If
+ * NULL, invoke the callback on all entries.
+ * @param fn Function to invoke upon matching entries. This
+ * function will be called with three parameters: a pointer to
+ * this filesystem, pointer to a matching entry, and an
+ * arbitrary data pointer. It should return a non-negative
+ * value upon success, negative value upon error and to abort
+ * the mapping.
+ * @param data Arbitrary pointer to be passed into \fn as a
+ * third parameter.
+ * @return Last value returned by fn() (i.e., >= on success, <
+ * 0 on failure). If the filesystem has no entries then
+ * return 0.
+ * @exception std::runtime_error Error while reading contexts
+ * from the filesystem.
+ * @exception std::invalid_argument One or more query arguments
+ * is invalid.
+ */
+ int runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument);
+
+ /**
+ * Determine if the contexts stored in this filesystem contain
+ * MLS fields.
+ * @return \a true if MLS fields are present, \a false if not
+ * or undeterminable.
+ */
+ bool isMLS() const;
+
+ /**
+ * Get the root directory of a sefs filesystem structure.
+ * @return The root directory of the filesystem or NULL on
+ * error. Do not free() this string.
+ */
+ const char *root() const;
+
+ /**
+ * Look up the given device number on the currently running
+ * system, and convert it to its device name.
+ * @param dev Device number to look up.
+ * @return Name of the device, or NULL if the device number
+ * was not found. Do not free() this pointer.
+ * @exception std::runtime_error Error while querying system.
+ */
+ const char *getDevName(const dev_t dev) throw(std::runtime_error);
+
+ private:
+ apol_vector_t * buildDevMap(void) throw(std::runtime_error);
+ bool isQueryMatch(const sefs_query * query, const char *path, const char *dev, const struct stat64 *sb,
+ apol_vector_t * type_list, apol_mls_range_t * range) throw(std::runtime_error);
+ sefs_entry *getEntry(const struct sefs_context_node *context, uint32_t objectClass, const char *path, ino64_t ino,
+ const char *dev_name) throw(std::bad_alloc);
+ char *_root;
+ bool _rw, _mls;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_filesystem sefs_filesystem_t;
+
+/**
+ * Allocate and return a new sefs filesystem structure representing
+ * the filesystem rooted at directory \a root.
+ * @see sefs_filesystem::sefs_filesystem()
+ */
+ extern sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg);
+
+/**
+ * Get the root directory of a sefs filesystem structure.
+ * @see sefs_filesystem::root()
+ */
+ extern const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs);
+
+/**
+ * Look up the given device number on the currently running
+ * system, and convert it to its device name.
+ * @see sefs_filesystem::ged_dev_name()
+ */
+ extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_FILESYSTEM_H */
diff --git a/libsefs/include/sefs/query.hh b/libsefs/include/sefs/query.hh
new file mode 100644
index 0000000..c0e8921
--- /dev/null
+++ b/libsefs/include/sefs/query.hh
@@ -0,0 +1,329 @@
+/**
+ * @file
+ * Defines the public interface for file context queries.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_QUERY_H
+#define SEFS_QUERY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <sys/types.h>
+#include <regex.h>
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#include <apol/context-query.h>
+#include <apol/mls-query.h>
+#include <apol/policy-query.h>
+#include <apol/vector.h>
+
+#ifdef __cplusplus
+}
+
+#include <stdexcept>
+
+/**
+ * This class represents a query into a (subclass of) fclist. Create
+ * a query, fill in all accessors are needed, and then run the query.
+ * All fields must match for an entry to be returned. Where a fclist
+ * does not support a particular criterion (e.g., inode numbers for
+ * fcfile) that portion of the query is considered to be matching.
+ */
+class sefs_query
+{
+ friend class sefs_db;
+ friend class sefs_fcfile;
+ friend class sefs_filesystem;
+
+ public:
+
+ /**
+ * Allocate and return a new sefs query structure. All fields
+ * are initialized, such that running this blank query results
+ * in returning all entries within a fclist.
+ */
+ sefs_query();
+
+ ~sefs_query();
+
+ /**
+ * Set a sefs query to match only entries with contexts with
+ * the user \a name.
+ * @param name Limit query to only contexts with this user, or
+ * NULL to clear this field. The string will be duplicated.
+ * @exception std::bad_alloc Out of memory.
+ */
+ void user(const char *name) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with contexts with
+ * the role \a name.
+ * @param name Limit query to only contexts with this role, or
+ * NULL to clear this field. The string will be duplicated.
+ * @exception std::bad_alloc Out of memory.
+ */
+ void role(const char *name) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with contexts with
+ * the type \a name.
+ * @param name Limit query to only contexts with this type, or
+ * NULL to clear this field. The string will be duplicated.
+ * @param indirect If true and if the fclist queried has
+ * access to a policy, also match contexts with types in
+ * attribute \a name or types which are an alias for \a name.
+ * If a policy is not available, this field is ignored, and
+ * exact string matching is used instead. This paramater is
+ * ignored if \a name is NULL.
+ * @exception std::bad_alloc Out of memory.
+ * @see sefs_fclist::associatePolicy() to associate a policy
+ * with a fclist.
+ */
+ void type(const char *name, bool indirect) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with contexts with a
+ * range of \a range. If the fclist is not MLS then \a name
+ * and \a match will be ignored.
+ * @param name Limit query to only contexts matching this
+ * string representing the MLS range, or NULL to clear this
+ * field. The string will be duplicated.
+ * @param match If non-zero and the fclist queried has access
+ * to a policy, match the range using the specified semantics;
+ * this should be one of APOL_QUERY_SUB, APOL_QUERY_SUPER, or
+ * APOL_QUERY_EXACT. (The range string will be converted
+ * automatically into an apol_mls_range_t object.) If a
+ * policy is not available or \a match is zero, exact string
+ * matching is used instead. Note, if a policy is available
+ * the regex flag is ignored if \a match is non-zero. This
+ * parameter is ignored if \a range is NULL.
+ * @exception std::bad_alloc Out of memory.
+ * @see sefs_fclist::associatePolicy() to associate a policy
+ * with a fclist.
+ */
+ void range(const char *name, int match) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with object class \a
+ * objclass.
+ *
+ * <em>Note:</em> If the query is run against a fcfile, then
+ * entries without explicit object classes (i.e., no explicit
+ * <tt>--</tt>, <tt>-d</tt>, etc.) will always match
+ * irrespective of the query's object class field.
+ *
+ * @param Numeric identifier for an objclass, one of
+ * QPOL_CLASS_FILE, QPOL_CLASS_DIR, etc., as defined in
+ * <qpol/genfscon_query.h>. Use QPOL_CLASS_ALL to match all
+ * object classes.
+ */
+ void objectClass(uint32_t objclass);
+
+ /**
+ * Set a sefs query to match only entries with object class \a
+ * name. The \a name parameter is not affected by regex().
+ *
+ * @param name Limit query to only entries with this object
+ * class, or NULL to clear this field. The incoming string
+ * must be legal according to apol_str_to_objclass().
+ *
+ * @see objectClass(uint32_t) for note about fcfiles.
+ */
+ void objectClass(const char *name);
+
+ /**
+ * Set a sefs query to match only entries with path \a path.
+ *
+ * <em>Note:</em> If the query is run against a fcfile, the
+ * behavior of matching paths is slightly different. For each
+ * of fcfile's entries, that entry's regular expression is
+ * matched against \a path. This is the reverse for other
+ * types of fclist, where \a path matches an entry's path if
+ * \a path is a substring. (If sefs_query::regex() is set to
+ * true, \a path is instead treated as a regular expression.)
+ *
+ * @param str Limit query to only entries containing this
+ * path, or NULL to clear this field. The string will be
+ * duplicated.
+ * @exception std::bad_alloc Out of memory.
+ */
+ void path(const char *str) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to match only entries with a given inode
+ * number.
+ * @param ino Limit query to only entries with this inode
+ * number, or 0 to clear this field.
+ */
+ void inode(ino64_t ino);
+
+ /**
+ * Set a sefs query to match only entries with a given device
+ * name.
+ * @param str Limit query to only entries with this device
+ * name, or NULL to clear this string. The string will be
+ * duplicated.
+ * @exception std::bad_alloc Out of memory.
+ * @see sefs_filesystem::getDevName() to convert between dev_t
+ * and a name.
+ */
+ void dev(const char *str) throw(std::bad_alloc);
+
+ /**
+ * Set a sefs query to use regular expression matching for
+ * string fields.
+ * @param r If true then use regular expression matching;
+ * otherwise use only exact string matching.
+ */
+ void regex(bool r);
+
+ private:
+ /**
+ * Compile the regular expressions stored within this query
+ * object. It is safe to call this function multiple times.
+ *
+ * @exception std::bad_alloc Out of memory.
+ * @exception std::invalid_argument One or more invalid regular
+ * expressions is invalid.
+ */
+ void compile() throw(std::bad_alloc, std::invalid_argument);
+
+ char *_user, *_role, *_type, *_range, *_path, *_dev;
+ uint32_t _objclass;
+ bool _indirect, _regex, _recursive;
+ int _rangeMatch;
+ ino64_t _inode;
+ bool _recompiled;
+ regex_t *_reuser, *_rerole, *_retype, *_rerange, *_repath, *_redev;
+};
+
+extern "C"
+{
+#endif
+
+//we do not want to wrap two copies of everything so have SWIG ignore
+//the compatibility section.
+#ifndef SWIG
+
+ typedef struct sefs_query sefs_query_t;
+
+/**
+ * Allocate and return a new sefs query structure.
+ * @see sefs_query::sefs_query()
+ */
+ extern sefs_query_t *sefs_query_create();
+
+/**
+ * Deallocate all memory associated with the referenced sefs query,
+ * and then set it to NULL. This function does nothing if the query
+ * is already NULL.
+ * @param query Reference to a sefs query structure to destroy.
+ */
+ extern void sefs_query_destroy(sefs_query_t ** query);
+
+/**
+ * Set a sefs query to match only entries with contexts with the user
+ * \a name.
+ * @see sefs_query::user()
+ */
+ extern int sefs_query_set_user(sefs_query_t * query, const char *name);
+
+/**
+ * Set a sefs query to match only entries with contexts with the role
+ * \a name.
+ * @see sefs_query::role()
+ */
+ extern int sefs_query_set_role(sefs_query_t * query, const char *name);
+
+/**
+ * Set a sefs query to match only entries with contexts with the type
+ * \a name.
+ * @see sefs_query::type()
+ * @see sefs_fclist_associate_policy() to associate a policy with a
+ * fclist.
+ */
+ extern int sefs_query_set_type(sefs_query_t * query, const char *name, bool indirect);
+
+/**
+ * Set a sefs query to match only entries with contexts with a range
+ * of \a range.
+ * @see sefs_query::range()
+ * @see sefs_fclist_associate_policy() to associate a policy with a
+ * fclist.
+ */
+ extern int sefs_query_set_range(sefs_query_t * query, const char *range, int match);
+
+/**
+ * Set a sefs query to match only entries with object class \a
+ * objclass.
+ * @return Always 0.
+ * @see sefs_query::objectClass(uint32_t)
+ */
+ extern int sefs_query_set_object_class(sefs_query_t * query, uint32_t objclass);
+
+/**
+ * Set a sefs query to match only entries with object class \a name.
+ * @return Always 0.
+ * @see sefs_query::objectClass(const char *)
+ */
+ extern int sefs_query_set_object_class_str(sefs_query_t * query, const char *name);
+
+/**
+ * Set a sefs query to match only entries with path \a path.
+ * @see sefs_query::path()
+ */
+ extern int sefs_query_set_path(sefs_query_t * query, const char *path);
+
+/**
+ * Set a sefs query to match only entries with a given inode number.
+ * @return Always 0.
+ * @see sefs_query::inode()
+ */
+ extern int sefs_query_set_inode(sefs_query_t * query, ino64_t inode);
+
+/**
+ * Set a sefs query to match only entries with a given device number.
+ * @see sefs_query::dev()
+ */
+ extern int sefs_query_set_dev(sefs_query_t * query, const char *dev);
+
+/**
+ * Set a sefs query to use regular expression matching for string
+ * fields.
+ * @return Always 0.
+ * @see sefs_query::regex()
+ */
+ extern int sefs_query_set_regex(sefs_query_t * query, bool regex);
+
+#endif /* SWIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEFS_QUERY_H */
diff --git a/libsefs/include/sefs/util.h b/libsefs/include/sefs/util.h
new file mode 100644
index 0000000..24480ea
--- /dev/null
+++ b/libsefs/include/sefs/util.h
@@ -0,0 +1,56 @@
+/**
+ * @file
+ *
+ * Miscellaneous, uncategorized functions for libsefs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_UTIL_H
+#define SEFS_UTIL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Return an immutable string describing this library's version.
+ *
+ * @return String describing this library.
+ */
+ extern const char *libsefs_get_version(void);
+
+/**
+ * Return the name (path + filename) of the file_contexts file for the
+ * currently running SELinux system. If the system is not running
+ * SELinux then return an empty string ("").
+ *
+ * @return The name of the default file_contexts file (if system is
+ * running SELinux), an empty string (if not SELinux), or NULL upon
+ * error. The caller must free() the string afterwards.
+ */
+ extern char *sefs_default_file_contexts_get_path(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libsefs/src/Makefile.am b/libsefs/src/Makefile.am
new file mode 100644
index 0000000..a9e61bc
--- /dev/null
+++ b/libsefs/src/Makefile.am
@@ -0,0 +1,52 @@
+lib_LIBRARIES = libsefs.a
+
+sefsso_DATA = libsefs.so.@libsefs_version@
+sefssodir = $(libdir)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SQLITE3_CFLAGS@ -I$(srcdir)/../include -fpic
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SQLITE3_CFLAGS@ -I$(srcdir)/../include -fpic
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+libsefs_a_SOURCES = \
+ db.cc \
+ entry.cc \
+ fcfile.cc \
+ fclist.cc \
+ filesystem.cc \
+ new_ftw.c new_ftw.h \
+ query.cc \
+ sefs_internal.hh \
+ util.c
+
+libsefs_a_DEPENDENCIES = \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libqpol/src/libqpol.so
+
+libsefs_so_OBJS = $(patsubst %.c,%.o,$(filter %.c,$(libsefs_a_SOURCES))) $(patsubst %.cc,%.o,$(filter %.cc,$(libsefs_a_SOURCES)))
+libsefs_so_OBJS += $(patsubst %.c,libsqlite_a-%.o,$(filter %.c,$(notdir $(libsqlite_a_SOURCES))))
+
+LIBSEFS_SONAME = @libsefs_soname@
+
+dist_noinst_DATA = libsefs.map
+
+$(sefsso_DATA): $(libsefs_so_OBJS) libsefs.map
+ $(CXX) -shared -o $@ $(libsefs_so_OBJS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(LIBSEFS_SONAME),--version-script=$(srcdir)/libsefs.map,-z,defs $(top_builddir)/libqpol/src/libqpol.so $(top_builddir)/libapol/src/libapol.so $(SQLITE3_LIBS) -lselinux -lsepol
+ $(LN_S) -f $@ @libsefs_soname@
+ $(LN_S) -f $@ libsefs.so
+
+libsefs.so: $(sefso_DATA)
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+install-data-hook:
+ cd $(DESTDIR)$(sefssodir) && $(LN_S) -f $(sefsso_DATA) @libsefs_soname@
+ cd $(DESTDIR)$(sefssodir) && $(LN_S) -f $(sefsso_DATA) libsefs.so
+
+mostlyclean-local:
+ -rm -rf *.gcno *.gcda *.gprof *.gcov libsefs.so @libsefs_soname@ $(sefsso_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(sefssodir)/$(sefsso_DATA) $(DESTDIR)$(sefssodir)/@libsefs_soname@ $(DESTDIR)$(sefssodir)/libsefs.so
diff --git a/libsefs/src/db.cc b/libsefs/src/db.cc
new file mode 100644
index 0000000..88ad588
--- /dev/null
+++ b/libsefs/src/db.cc
@@ -0,0 +1,1304 @@
+/**
+ * @file
+ *
+ * Routines for creating, saving, and loading a sqlite3 database
+ * containing paths + file contexts.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sefs_internal.hh"
+
+#include <sefs/db.hh>
+#include <sefs/filesystem.hh>
+#include <sefs/entry.hh>
+#include <apol/util.h>
+
+#include <sqlite3.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define DB_MAX_VERSION "2"
+
+#define DB_SCHEMA_NONMLS \
+ "CREATE TABLE users (user_id INTEGER PRIMARY KEY, user_name varchar (24));" \
+ "CREATE TABLE roles (role_id INTEGER PRIMARY KEY, role_name varchar (24));" \
+ "CREATE TABLE types (type_id INTEGER PRIMARY KEY, type_name varchar (48));" \
+ "CREATE TABLE devs (dev_id INTEGER PRIMARY KEY, dev_name varchar (32));" \
+ "CREATE TABLE paths (path varchar (128) PRIMARY KEY, ino int(64), dev int, user int, role int, type int, range int, obj_class int, symlink_target varchar (128));" \
+ "CREATE TABLE info (key varchar, value varchar);"
+
+#define DB_SCHEMA_MLS DB_SCHEMA_NONMLS \
+ "CREATE TABLE mls (mls_id INTEGER PRIMARY KEY, mls_range varchar (64));"
+
+// wrapper functions to go between non-OO land into OO member functions
+
+inline struct sefs_context_node *db_get_context(sefs_db * db, const char *user, const char *role, const char *type,
+ const char *range) throw(std::bad_alloc)
+{
+ return db->getContext(user, role, type, range);
+}
+
+inline sefs_entry *db_get_entry(sefs_db * db, const struct sefs_context_node * node, uint32_t objClass,
+ const char *path, ino64_t inode, const char *dev)throw(std::bad_alloc)
+{
+ return db->getEntry(node, objClass, path, inode, dev);
+}
+
+/******************** sqlite3 callback functions ********************/
+
+struct db_callback_arg
+{
+ struct sqlite3 *db;
+ char *errmsg;
+ const char *source_db, *target_db;
+};
+
+struct db_query_arg
+{
+ sefs_db *db;
+ char *user, *role, *type, *range, *path, *dev;
+ bool regex, db_is_mls;
+ regex_t *reuser, *rerole, *retype, *rerange, *repath, *redev;
+ int rangeMatch;
+ sefs_fclist_map_fn_t fn;
+ void *data;
+ apol_vector_t *type_list;
+ apol_mls_range_t *apol_range;
+ apol_policy_t *policy;
+ bool aborted;
+ int retval;
+};
+
+/**
+ * Callback invoked when selecting names of tables from a database.
+ */
+static int db_copy_schema(void *arg, int argc __attribute__ ((unused)), char *argv[], char *column_names[] __attribute__ ((unused)))
+{
+ // argv[0] contains a SQL statement that, if executed against a
+ // db, will create a table
+ struct db_callback_arg *db = static_cast < struct db_callback_arg *>(arg);
+ if (sqlite3_exec(db->db, argv[0], NULL, NULL, &(db->errmsg)) != SQLITE_OK)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Callback invoked when selecting each row from a table.
+ */
+static int db_copy_table(void *arg, int argc __attribute__ ((unused)), char *argv[], char *column_names[] __attribute__ ((unused)))
+{
+ // argv[0] contains the name of a table
+ struct db_callback_arg *db = static_cast < struct db_callback_arg *>(arg);
+ char *insert = NULL;
+ if (asprintf(&insert, "INSERT INTO %s%s SELECT * FROM %s%s", db->target_db, argv[0], db->source_db, argv[0]) < 0)
+ {
+ db->errmsg = strdup(strerror(errno));
+ return -1;
+ }
+ int rc = sqlite3_exec(db->db, insert, NULL, NULL, &(db->errmsg));
+ free(insert);
+ if (rc != SQLITE_OK)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Callback invoked when selecting a user (for a sefs_query).
+ */
+static void db_user_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv)
+{
+ void *arg = sqlite3_user_data(context);
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
+ const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0]));
+ bool retval = query_str_compare(text, q->user, q->reuser, q->regex);
+ sqlite3_result_int(context, (retval ? 1 : 0));
+}
+
+/**
+ * Callback invoked when selecting a role (for a sefs_query).
+ */
+static void db_role_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv)
+{
+ void *arg = sqlite3_user_data(context);
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
+ const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0]));
+ bool retval = query_str_compare(text, q->role, q->rerole, q->regex);
+ sqlite3_result_int(context, (retval ? 1 : 0));
+}
+
+/**
+ * Callback invoked when selecting a type (for a sefs_query).
+ */
+static void db_type_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv)
+{
+ void *arg = sqlite3_user_data(context);
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
+ const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0]));
+ bool retval;
+ if (q->type_list != NULL)
+ {
+ assert(q->policy != NULL);
+ size_t index;
+ retval = (apol_vector_get_index(q->type_list, text, apol_str_strcmp, NULL, &index) >= 0);
+ if (retval)
+ {
+ sqlite3_result_int(context, 1);
+ return;
+ }
+ }
+ retval = query_str_compare(text, q->type, q->retype, q->regex);
+ sqlite3_result_int(context, (retval ? 1 : 0));
+}
+
+/**
+ * Callback invoked when selecting a range (for a sefs_query).
+ */
+static void db_range_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv)
+{
+ void *arg = sqlite3_user_data(context);
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
+ const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0]));
+ bool retval;
+ if (q->apol_range == NULL)
+ {
+ retval = query_str_compare(text, q->range, q->rerange, q->regex);
+ }
+ else
+ {
+ assert(q->policy != NULL);
+ apol_mls_range_t *db_range = apol_mls_range_create_from_string(q->policy, text);
+ int ret;
+ ret = apol_mls_range_compare(q->policy, q->apol_range, db_range, q->rangeMatch);
+ apol_mls_range_destroy(&db_range);
+ retval = (ret > 0);
+ }
+ sqlite3_result_int(context, (retval ? 1 : 0));
+}
+
+/**
+ * Callback invoked when selecting a path (for a sefs_query).
+ */
+static void db_path_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv)
+{
+ void *arg = sqlite3_user_data(context);
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
+ const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0]));
+ bool retval = query_str_compare(text, q->path, q->repath, q->regex);
+ sqlite3_result_int(context, (retval ? 1 : 0));
+}
+
+/**
+ * Callback invoked when selecting a device name (for a sefs_query).
+ */
+static void db_dev_compare(sqlite3_context * context, int argc __attribute__ ((unused)), sqlite3_value ** argv)
+{
+ void *arg = sqlite3_user_data(context);
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(sqlite3_value_type(argv[0]) == SQLITE_TEXT);
+ const char *text = reinterpret_cast < const char *>(sqlite3_value_text(argv[0]));
+ bool retval = query_str_compare(text, q->dev, q->redev, q->regex);
+ sqlite3_result_int(context, (retval ? 1 : 0));
+}
+
+/**
+ * Callback invoked when selecting rows during a query.
+ */
+static int db_query_callback(void *arg, int argc, char *argv[], char *column_names[] __attribute__ ((unused)))
+{
+ struct db_query_arg *q = static_cast < struct db_query_arg *>(arg);
+ assert(argc == (q->db_is_mls ? 9 : 8));
+ char *path = argv[0];
+ ino64_t ino = static_cast < ino64_t > (strtoul(argv[1], NULL, 10));
+ char *dev = argv[2];
+ char *user = argv[3];
+ char *role = argv[4];
+ char *type = argv[5];
+ char *range, *objclass_str, *link_target;
+
+ if (q->db_is_mls)
+ {
+ range = argv[6];
+ objclass_str = argv[7];
+ link_target = argv[8];
+ }
+ else
+ {
+ range = NULL;
+ objclass_str = argv[6];
+ link_target = argv[7];
+ }
+ struct sefs_context_node *node = NULL;
+ try
+ {
+ node = db_get_context(q->db, user, role, type, range);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+
+ uint32_t objClass = static_cast < uint32_t > (atoi(objclass_str));
+ sefs_entry *entry = NULL;
+ try
+ {
+ entry = db_get_entry(q->db, node, objClass, path, ino, dev);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+
+ // invoke real callback (not just the sqlite3 exec callback)
+ q->retval = q->fn(q->db, entry, q->data);
+ delete entry;
+ if (q->retval < 0)
+ {
+ q->aborted = true;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Callback invoked when checking if there exists any row with the
+ * given select parameters.
+ */
+static int db_row_exist_callback(void *arg,
+ int argc __attribute__ ((unused)),
+ char **argv __attribute__ ((unused)), char **col_names __attribute__ ((unused)))
+{
+ bool *answer = static_cast < bool * >(arg);
+ *answer = true;
+ return 0;
+}
+
+/**
+ * Callback invoked when obtaining the ctime value from the database.
+ */
+static int db_ctime_callback(void *arg, int argc __attribute__ ((unused)), char **argv, char **col_names __attribute__ ((unused)))
+{
+ time_t *ctime = static_cast < time_t * >(arg);
+ // argv has the result of a call to ctime_r(); convert the string
+ // back to a time_t value
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+ if (strptime(argv[0], "%a %b %d %T %Y", &t) == NULL)
+ {
+ return -1;
+ }
+ *ctime = mktime(&t);
+ return 0;
+}
+
+/**
+ * Callback invoked to determine how many rows match a particular
+ * select statement.
+ */
+static int db_count_callback(void *arg, int argc __attribute__ ((unused)), char **argv, char **column_names
+ __attribute__ ((unused)))
+{
+ int *count = static_cast < int *>(arg);
+ *count = atoi(argv[0]);
+ return 0;
+}
+
+/******************** convert from a filesystem to a db ********************/
+
+struct strindex
+{
+ const char *str;
+ int id;
+};
+
+static int db_strindex_comp(const void *a, const void *b, void *arg __attribute__ ((unused)))
+{
+ const struct strindex *n1 = static_cast < const struct strindex *>(a);
+ const struct strindex *n2 = static_cast < const struct strindex *>(b);
+ return strcmp(n1->str, n2->str);
+}
+
+class db_convert
+{
+ public:
+ db_convert(sefs_db * db, struct sqlite3 * &target_db)throw(std::runtime_error)
+ {
+ _db = db;
+ _target_db = target_db;
+ _user = _role = _type = _range = _dev = NULL;
+ _user_id = _role_id = _type_id = _range_id = _dev_id = 0;
+ _errmsg = NULL;
+ try
+ {
+ if ((_user = apol_bst_create(db_strindex_comp, free)) == NULL)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if ((_role = apol_bst_create(db_strindex_comp, free)) == NULL)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if ((_type = apol_bst_create(db_strindex_comp, free)) == NULL)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if ((_range = apol_bst_create(db_strindex_comp, free)) == NULL)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if ((_dev = apol_bst_create(db_strindex_comp, free)) == NULL)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ catch(...)
+ {
+ apol_bst_destroy(&_user);
+ apol_bst_destroy(&_role);
+ apol_bst_destroy(&_type);
+ apol_bst_destroy(&_range);
+ apol_bst_destroy(&_dev);
+ throw;
+ }
+ }
+ ~db_convert()
+ {
+ apol_bst_destroy(&_user);
+ apol_bst_destroy(&_role);
+ apol_bst_destroy(&_type);
+ apol_bst_destroy(&_range);
+ apol_bst_destroy(&_dev);
+ sqlite3_free(_errmsg);
+ }
+ int getID(const char *sym, apol_bst_t * tree, int &id, const char *table) throw(std::bad_alloc)
+ {
+ struct strindex st = { sym, -1 }, *result;
+ if (apol_bst_get_element(tree, &st, NULL, (void **)&result) == 0)
+ {
+ return result->id;
+ }
+ if ((result = static_cast < struct strindex * >(malloc(sizeof(*result)))) == NULL)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ result->str = sym;
+ result->id = id++;
+ if (apol_bst_insert(tree, result, NULL) < 0)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ free(result);
+ throw std::bad_alloc();
+ }
+ char *insert_stmt = NULL;
+ if (asprintf(&insert_stmt, "INSERT INTO %s VALUES (%d, '%s')", table, result->id, result->str) < 0)
+ {
+ SEFS_ERR(_db, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (sqlite3_exec(_target_db, insert_stmt, NULL, NULL, &_errmsg) != SQLITE_OK)
+ {
+ SEFS_ERR(_db, "%s", _errmsg);
+ free(insert_stmt);
+ throw std::runtime_error(_errmsg);
+ }
+ free(insert_stmt);
+ return result->id;
+ }
+ apol_bst_t *_user, *_role, *_type, *_range, *_dev;
+ int _user_id, _role_id, _type_id, _range_id, _dev_id;
+ bool _isMLS;
+ char *_errmsg;
+ sefs_db *_db;
+ struct sqlite3 *_target_db;
+};
+
+int db_create_from_filesystem(sefs_fclist * fclist __attribute__ ((unused)), const sefs_entry * entry, void *arg)
+{
+ db_convert *dbc = static_cast < db_convert * >(arg);
+
+ const struct sefs_context_node *context = dbc->_db->getContextNode(entry);
+ try
+ {
+
+ // add the user, role, type, range, and dev into the
+ // target_db if needed
+ int user_id = dbc->getID(context->user, dbc->_user, dbc->_user_id, "users");
+ int role_id = dbc->getID(context->role, dbc->_role, dbc->_role_id, "roles");
+ int type_id = dbc->getID(context->type, dbc->_type, dbc->_type_id, "types");
+ int range_id = 0;
+ if (dbc->_isMLS)
+ {
+ range_id = dbc->getID(context->range, dbc->_range, dbc->_range_id, "mls");
+ }
+ int dev_id = dbc->getID(entry->dev(), dbc->_dev, dbc->_dev_id, "devs");
+ const char *path = entry->path();
+ const ino64_t inode = entry->inode();
+ const uint32_t objclass = entry->objectClass();
+ char link_target[128] = "";
+ // determine the link target as necessary
+ struct stat64 sb;
+ if (stat64(path, &sb) == -1)
+ {
+ SEFS_ERR(dbc->_db, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (S_ISLNK(sb.st_mode))
+ {
+ if (readlink(path, link_target, 128) == 0)
+ {
+ SEFS_ERR(dbc->_db, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ link_target[127] = '\0';
+ }
+
+ char *insert_stmt = NULL;
+ if (asprintf
+ (&insert_stmt, "INSERT INTO paths VALUES ('%s', %lu, %d, %d, %d, %d, %d, %u, '%s')", path,
+ static_cast < long unsigned int >(inode), dev_id, user_id, role_id, type_id, range_id, objclass,
+ link_target) < 0)
+ {
+ SEFS_ERR(dbc->_db, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (sqlite3_exec(dbc->_target_db, insert_stmt, NULL, NULL, &dbc->_errmsg) != SQLITE_OK)
+ {
+ SEFS_ERR(dbc->_db, "%s", dbc->_errmsg);
+ free(insert_stmt);
+ throw std::runtime_error(dbc->_errmsg);
+ }
+ free(insert_stmt);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+/******************** public functions below ********************/
+
+sefs_db::sefs_db(sefs_filesystem * fs, sefs_callback_fn_t msg_callback, void *varg)throw(std::invalid_argument, std::runtime_error):sefs_fclist
+ (SEFS_FCLIST_TYPE_DB, msg_callback,
+ varg)
+{
+ if (fs == NULL)
+ {
+ errno = EINVAL;
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+
+ SEFS_INFO(this, "Reading contexts from filesystem %s.", fs->root());
+ char *errmsg = NULL;
+ try
+ {
+ if (sqlite3_open(":memory:", &_db) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", sqlite3_errmsg(_db));
+ throw std::runtime_error(sqlite3_errmsg(_db));
+ }
+ int rc;
+ if (fs->isMLS())
+ {
+ rc = sqlite3_exec(_db, DB_SCHEMA_MLS, NULL, 0, &errmsg);
+ }
+ else
+ {
+ rc = sqlite3_exec(_db, DB_SCHEMA_NONMLS, NULL, 0, &errmsg);
+ }
+ if (rc != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ db_convert dbc(this, _db);
+ dbc._isMLS = fs->isMLS();
+ if (fs->runQueryMap(NULL, db_create_from_filesystem, &dbc) < 0)
+ {
+ throw std::runtime_error(strerror(errno));
+ }
+
+ // store metadata about the database
+ const char *dbversion = DB_MAX_VERSION;
+ char hostname[64];
+ gethostname(hostname, sizeof(hostname));
+ hostname[63] = '\0';
+ _ctime = time(NULL);
+ char datetime[32];
+ ctime_r(&_ctime, datetime);
+
+ char *info_insert = NULL;
+ if (asprintf(&info_insert,
+ "INSERT INTO info (key,value) VALUES ('dbversion','%s');"
+ "INSERT INTO info (key,value) VALUES ('hostname','%s');"
+ "INSERT INTO info (key,value) VALUES ('datetime','%s');", dbversion, hostname, datetime) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ rc = sqlite3_exec(_db, info_insert, NULL, NULL, &errmsg);
+ free(info_insert);
+ if (rc != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ }
+ catch(...)
+ {
+ if (errmsg != NULL)
+ {
+ sqlite3_free(errmsg);
+ }
+ sqlite3_close(_db);
+ throw;
+ }
+}
+
+sefs_db::sefs_db(const char *filename, sefs_callback_fn_t msg_callback, void *varg) throw(std::invalid_argument,
+ std::
+ runtime_error):sefs_fclist
+ (SEFS_FCLIST_TYPE_DB, msg_callback, varg)
+{
+ if (filename == NULL)
+ {
+ errno = EINVAL;
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+
+ if (!sefs_db::isDB(filename))
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ if (sqlite3_open(filename, &_db) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", sqlite3_errmsg(_db));
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+
+ char *errmsg = NULL;
+
+ const char *select_stmt = "SELECT * FROM info WHERE key = 'dbversion' AND value >= " DB_MAX_VERSION;
+ bool answer = false;
+ if (sqlite3_exec(_db, select_stmt, db_row_exist_callback, &answer, &errmsg) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ sqlite3_free(errmsg);
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+ if (!answer)
+ {
+ SEFS_INFO(this, "Upgrading database %s.", filename);
+ SEFS_WARN(this, "%s is a pre-libsefs-4.0 database and will be upgraded.", filename);
+ upgradeToDB2();
+ }
+
+ // get ctime from db
+ _ctime = 0;
+ const char *ctime_stmt = "SELECT value FROM info WHERE key='datetime'";
+ if (sqlite3_exec(_db, ctime_stmt, db_ctime_callback, &_ctime, &errmsg) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ sqlite3_free(errmsg);
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+}
+
+sefs_db::~sefs_db()
+{
+ if (_db != NULL)
+ {
+ sqlite3_close(_db);
+ _db = NULL;
+ }
+}
+
+int sefs_db::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error, std::invalid_argument)
+{
+ // copy the query fields over to the C land struct; this is
+ // because the query members are private, and thus not accessible
+ // from a C callback
+ struct db_query_arg q;
+ memset(&q, 0, sizeof(q));
+
+ q.db = this;
+ if (query != NULL)
+ {
+ query->compile();
+ if (policy != NULL)
+ {
+ if (query->_type != NULL && query->_indirect)
+ {
+ q.type_list =
+ query_create_candidate_type(policy, query->_type, query->_retype, query->_regex,
+ query->_indirect);
+ if (q.type_list == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ if (query->_range != NULL && query->_rangeMatch != 0)
+ {
+ q.apol_range = apol_mls_range_create_from_string(policy, query->_range);
+ if (q.apol_range == NULL)
+ {
+ apol_vector_destroy(&q.type_list);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ }
+ q.user = query->_user;
+ q.role = query->_role;
+ q.type = query->_type;
+ q.range = query->_range;
+ q.path = query->_path;
+ q.dev = query->_dev;
+ q.regex = query->_regex;
+ q.reuser = query->_reuser;
+ q.rerole = query->_rerole;
+ q.retype = query->_retype;
+ q.rerange = query->_rerange;
+ q.repath = query->_repath;
+ q.redev = query->_redev;
+ q.rangeMatch = query->_rangeMatch;
+ }
+ q.policy = this->policy;
+ q.db_is_mls = isMLS();
+ q.fn = fn;
+ q.data = data;
+ q.retval = 0;
+ q.aborted = false;
+
+ char *select_stmt = NULL, *errmsg = NULL;
+ size_t len = 0;
+
+ try
+ {
+ bool where_added = false;
+
+ if (apol_str_append
+ (&select_stmt, &len,
+ "SELECT paths.path, paths.ino, devs.dev_name, users.user_name, roles.role_name, types.type_name") < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (q.db_is_mls && apol_str_append(&select_stmt, &len, ", mls.mls_range") < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_append(&select_stmt, &len,
+ ", paths.obj_class, paths.symlink_target FROM paths, devs, users, roles, types") < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (q.db_is_mls && apol_str_append(&select_stmt, &len, ", mls") < 0)
+ {
+ throw std::runtime_error(strerror(errno));
+ }
+
+ if (q.user != NULL)
+ {
+ if (sqlite3_create_function(_db, "user_compare", 1, SQLITE_UTF8, &q, db_user_compare, NULL, NULL) !=
+ SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (user_compare(users.user_name))", (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (q.role != NULL)
+ {
+ if (sqlite3_create_function(_db, "role_compare", 1, SQLITE_UTF8, &q, db_role_compare, NULL, NULL) !=
+ SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (role_compare(roles.role_name))", (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (q.type != NULL)
+ {
+ if (sqlite3_create_function(_db, "type_compare", 1, SQLITE_UTF8, &q, db_type_compare, NULL, NULL) !=
+ SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (type_compare(types.type_name))", (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (q.db_is_mls && q.range != NULL)
+ {
+ if (sqlite3_create_function(_db, "range_compare", 1, SQLITE_UTF8, &q, db_range_compare, NULL, NULL) !=
+ SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (range_compare(mls.mls_range))", (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (query->_objclass != 0)
+ {
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (paths.obj_class = %d)", (where_added ? " AND" : " WHERE"), query->_objclass) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (q.path != NULL)
+ {
+ if (sqlite3_create_function(_db, "path_compare", 1, SQLITE_UTF8, &q, db_path_compare, NULL, NULL) !=
+ SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (path_compare(paths.path))", (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (query->_inode != 0)
+ {
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (paths.ino = %lu)", (where_added ? " AND" : " WHERE"),
+ static_cast < long unsigned int >(query->_inode)) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (query->_dev != 0)
+ {
+ if (sqlite3_create_function(_db, "dev_compare", 1, SQLITE_UTF8, &q, db_dev_compare, NULL, NULL) !=
+ SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (dev_compare(devs.dev_name)", (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ where_added = true;
+ }
+
+ if (apol_str_appendf(&select_stmt, &len,
+ "%s (paths.user = users.user_id AND paths.role = roles.role_id AND paths.type = types.type_id",
+ (where_added ? " AND" : " WHERE")) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (q.db_is_mls && apol_str_appendf(&select_stmt, &len, " AND paths.range = mls.mls_id") < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_str_append(&select_stmt, &len, " AND paths.dev = devs.dev_id) ORDER BY paths.path ASC") < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ int rc = sqlite3_exec(_db, select_stmt, db_query_callback, &q, &errmsg);
+ if (rc != SQLITE_OK && (rc != SQLITE_ABORT || !q.aborted))
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ throw std::runtime_error(errmsg);
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&q.type_list);
+ apol_mls_range_destroy(&q.apol_range);
+ free(select_stmt);
+ sqlite3_free(errmsg);
+ throw;
+ }
+
+ apol_vector_destroy(&q.type_list);
+ apol_mls_range_destroy(&q.apol_range);
+ free(select_stmt);
+ sqlite3_free(errmsg);
+ return q.retval;
+}
+
+bool sefs_db::isMLS() const
+{
+ int rc;
+ bool answer = false;
+ char *errmsg = NULL;
+ const char *select_stmt = "SELECT * FROM sqlite_master WHERE name='mls'";
+ rc = sqlite3_exec(_db, select_stmt, db_row_exist_callback, &answer, &errmsg);
+ if (rc != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ sqlite3_free(errmsg);
+ answer = false;
+ }
+ return answer;
+}
+
+void sefs_db::save(const char *filename) throw(std::invalid_argument, std::runtime_error)
+{
+ FILE *fp = NULL;
+ struct db_callback_arg diskdb;
+ diskdb.db = NULL;
+ diskdb.errmsg = NULL;
+ bool in_transaction = false;
+
+ try
+ {
+ if (filename == NULL)
+ {
+ errno = EINVAL;
+ throw std::invalid_argument(strerror(errno));
+ }
+ // check that target file is creatable; this will also
+ // remove the file if it already exists
+ if ((fp = fopen(filename, "w")) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ fclose(fp);
+ fp = NULL;
+
+ // copy database schema from in-memory db to the one on disk
+ if (sqlite3_open(filename, &(diskdb.db)) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", sqlite3_errmsg(diskdb.db));
+ throw std::runtime_error(sqlite3_errmsg(diskdb.db));
+ }
+ if (sqlite3_exec(_db, "SELECT sql FROM sqlite_master WHERE sql NOT NULL", db_copy_schema, &diskdb, &diskdb.errmsg)
+ != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", diskdb.errmsg);
+ throw std::runtime_error(diskdb.errmsg);
+ }
+ sqlite3_close(diskdb.db);
+
+ char *attach = NULL;
+ if (asprintf(&attach, "ATTACH '%s' AS diskdb", filename) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ diskdb.db = _db;
+ diskdb.source_db = "";
+ diskdb.target_db = "diskdb.";
+ int rc = sqlite3_exec(_db, attach, NULL, NULL, &diskdb.errmsg);
+ free(attach);
+ if (rc != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", diskdb.errmsg);
+ throw std::runtime_error(diskdb.errmsg);
+ }
+
+ // copy contents from in-memory db to the one on disk
+ if (sqlite3_exec(_db, "BEGIN TRANSACTION", NULL, NULL, &(diskdb.errmsg)) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", diskdb.errmsg);
+ throw std::runtime_error(diskdb.errmsg);
+ }
+ in_transaction = true;
+ if (sqlite3_exec(_db, "SELECT name FROM sqlite_master WHERE type ='table'", db_copy_table, &diskdb, &diskdb.errmsg)
+ != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", diskdb.errmsg);
+ throw std::runtime_error(diskdb.errmsg);
+ }
+
+ sqlite3_exec(_db, "DETACH diskdb", NULL, NULL, NULL);
+
+ if (sqlite3_exec(_db, "END TRANSACTION", NULL, 0, &(diskdb.errmsg)) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", diskdb.errmsg);
+ throw std::runtime_error(diskdb.errmsg);
+ }
+ in_transaction = false;
+ }
+ catch(...)
+ {
+ if (fp != NULL)
+ {
+ fclose(fp);
+ }
+ if (in_transaction)
+ {
+ sqlite3_exec(_db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
+ }
+ if (diskdb.db != NULL)
+ {
+ sqlite3_close(diskdb.db);
+ }
+ sqlite3_free(diskdb.errmsg);
+ throw;
+ }
+ // sqlite3_close(diskdb.db); don't close the database -- it's pointing to this->_db
+ sqlite3_free(diskdb.errmsg);
+}
+
+time_t sefs_db::getCTime() const
+{
+ return _ctime;
+}
+
+bool sefs_db::isDB(const char *filename)
+{
+ if (filename == NULL)
+ {
+ errno = EINVAL;
+ return false;
+ }
+
+ int rc = access(filename, R_OK);
+ if (rc != 0)
+ {
+ return false;
+ }
+
+ struct sqlite3 *db = NULL;
+ rc = sqlite3_open(filename, &db);
+ if (rc != SQLITE_OK)
+ {
+ sqlite3_close(db);
+ errno = EIO;
+ return false;
+ }
+
+ // Run a simple query to check that the database is legal.
+ int list_size;
+ char *errmsg = NULL;
+ rc = sqlite3_exec(db, "SELECT type_name FROM types", db_count_callback, &list_size, &errmsg);
+ if (rc != SQLITE_OK)
+ {
+ sqlite3_close(db);
+ sqlite3_free(errmsg);
+ errno = EIO;
+ return false;
+ }
+ sqlite3_close(db);
+ return true;
+}
+
+/******************** private functions below ********************/
+
+const struct sefs_context_node *sefs_db::getContextNode(const sefs_entry * entry)
+{
+ return entry->_context;
+}
+
+/**
+ * Callback invoked while upgrading a libsefs database version 1 to
+ * version 2. Merge the inodes and paths table into one, remap the
+ * object class value, and explicitly set the role and dev fields to
+ * zero.
+ */
+static int db_upgrade_reinsert(void *arg, int argc, char *argv[], char *column_names[])
+{
+ struct sqlite3 *db = static_cast < struct sqlite3 *>(arg);
+ bool mls = (argc == 7);
+ assert(argc >= 6 && argc <= 7);
+ uint32_t obj_class = static_cast < uint32_t > (atoi(argv[(mls ? 5 : 4)]));
+
+ switch (obj_class)
+ {
+ case 16:
+ obj_class = QPOL_CLASS_BLK_FILE;
+ break;
+ case 8:
+ obj_class = QPOL_CLASS_CHR_FILE;
+ break;
+ case 2:
+ obj_class = QPOL_CLASS_DIR;
+ break;
+ case 64:
+ obj_class = QPOL_CLASS_FIFO_FILE;
+ break;
+ case 1:
+ obj_class = QPOL_CLASS_FILE;
+ break;
+ case 4:
+ obj_class = QPOL_CLASS_LNK_FILE;
+ break;
+ case 32:
+ obj_class = QPOL_CLASS_SOCK_FILE;
+ break;
+ }
+
+ char *insert_stmt = NULL;
+ if (mls)
+ {
+ if (asprintf(&insert_stmt,
+ "INSERT INTO new_paths (path, ino, dev, user, role, type, range, obj_class, symlink_target) VALUES ('%s', %s, 0, %s, 0, %s, %s, %u, '%s')",
+ argv[0], argv[1], argv[2], argv[3], argv[4], obj_class, argv[6]) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if (asprintf(&insert_stmt,
+ "INSERT INTO new_paths (path, ino, dev, user, role, type, range, obj_class, symlink_target) VALUES ('%s', %s, 0, %s, 0, %s, 0, %u, '%s')",
+ argv[0], argv[1], argv[2], argv[3], obj_class, argv[5]) < 0)
+ {
+ return -1;
+ }
+ }
+ if (sqlite3_exec(db, insert_stmt, NULL, NULL, NULL) != SQLITE_OK)
+ {
+ free(insert_stmt);
+ return -1;
+ }
+ free(insert_stmt);
+ return 0;
+}
+
+void sefs_db::upgradeToDB2() throw(std::runtime_error)
+{
+ char *errmsg;
+
+ // Add a role field for each inode entry within the database;
+ // assume that the role is 'object_r'. Also update the object
+ // class values, from older class values to new ones. Old
+ // class_id values come from the old libsefs < 4.0 definitions
+ // that were in fsdata.h; the new style is in
+ // qpol/genfscon_query.h.
+ _ctime = time(NULL);
+ char datetime[32];
+ ctime_r(&_ctime, datetime);
+ char *alter_stmt = NULL;
+ if (asprintf(&alter_stmt, "BEGIN TRANSACTION;" "CREATE TABLE roles (role_id INTEGER PRIMARY KEY, role_name varchar (24));" // add a roles table
+ "INSERT INTO roles (role_id, role_name) VALUES (0, 'object_r');" // assume that all previous contexts had as their role 'object_r'
+ "CREATE TABLE devs (dev_id INTEGER PRIMARY KEY, dev_name varchar (32));" // add a table that maps between device names and some numeric ID
+ "INSERT INTO devs (dev_id, dev_name) VALUES (0, '<<unknown>>');" // device names were not stored in old DB
+ "CREATE TABLE new_paths (path varchar (128) PRIMARY KEY, ino int(64), dev int, user int, role int, type int, range int, obj_class int, symlink_target varchar (128));" // create new paths table
+ "SELECT paths.path, inodes.ino, inodes.user, inodes.type, %sinodes.obj_class, inodes.symlink_target FROM paths, inodes WHERE (inodes.inode_id = paths.inode)", // rebuild new paths table from older tables
+ isMLS()? "inodes.range, " : "") < 0)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ sqlite3_free(errmsg);
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+ if (sqlite3_exec(_db, alter_stmt, db_upgrade_reinsert, _db, &errmsg) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ free(alter_stmt);
+ sqlite3_free(errmsg);
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+
+ free(alter_stmt);
+ alter_stmt = NULL;
+
+ if (asprintf(&alter_stmt, "DROP TABLE inodes; DROP TABLE paths;" // drop the old tables
+ "ALTER TABLE new_paths RENAME TO paths;" // move ver 2 paths table as main table
+ "UPDATE info SET value = '%s' WHERE key = 'datetime';"
+ "UPDATE info SET value = '%s' WHERE key = 'dbversion';"
+ "END TRANSACTION;" "VACUUM", datetime, DB_MAX_VERSION) < 0)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ sqlite3_free(errmsg);
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+ if (sqlite3_exec(_db, alter_stmt, NULL, NULL, &errmsg) != SQLITE_OK)
+ {
+ SEFS_ERR(this, "%s", errmsg);
+ free(alter_stmt);
+ sqlite3_free(errmsg);
+ sqlite3_close(_db);
+ throw std::runtime_error(strerror(errno));
+ }
+ free(alter_stmt);
+}
+
+sefs_entry *sefs_db::getEntry(const struct sefs_context_node *context, uint32_t objectClass, const char *path, ino64_t inode,
+ const char *dev) throw(std::bad_alloc)
+{
+ char *s = strdup(path);
+ if (s == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (apol_bst_insert_and_get(path_tree, (void **)&s, NULL) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ free(s);
+ throw std::bad_alloc();
+ }
+ sefs_entry *e = new sefs_entry(this, context, objectClass, s);
+ e->_inode = inode;
+
+ s = NULL;
+ if ((s = strdup(dev)) == NULL || apol_bst_insert_and_get(dev_tree, (void **)&s, NULL) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ free(s);
+ throw std::bad_alloc();
+ }
+ e->_dev = dev;
+ return e;
+}
+
+/******************** C functions below ********************/
+
+sefs_fclist_t *sefs_db_create_from_filesystem(sefs_filesystem_t * fs, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist_t *fc = NULL;
+ try
+ {
+ fc = new sefs_db(fs, msg_callback, varg);
+ }
+ catch(...)
+ {
+ return NULL;
+ }
+ return fc;
+}
+
+sefs_fclist_t *sefs_db_create_from_file(const char *filename, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist_t *fc = NULL;
+ try
+ {
+ fc = new sefs_db(filename, msg_callback, varg);
+ }
+ catch(...)
+ {
+ return NULL;
+ }
+ return fc;
+}
+
+int sefs_db_save(sefs_db_t * db, const char *filename)
+{
+ if (db == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ db->save(filename);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+time_t sefs_db_get_ctime(sefs_db_t * db)
+{
+ if (db == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return static_cast < time_t > (-1);
+ }
+ return db->getCTime();
+}
+
+bool sefs_db_is_db(const char *filename)
+{
+ return sefs_db::isDB(filename);
+}
diff --git a/libsefs/src/entry.cc b/libsefs/src/entry.cc
new file mode 100644
index 0000000..cf9a5e6
--- /dev/null
+++ b/libsefs/src/entry.cc
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * Implementation of the sefs_entry class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sefs_internal.hh"
+
+#include <sefs/entry.hh>
+#include <apol/util.h>
+#include <qpol/genfscon_query.h>
+
+#include <assert.h>
+#include <errno.h>
+
+/******************** public functions below ********************/
+
+sefs_entry::sefs_entry(const sefs_entry * e)
+{
+ _fclist = e->_fclist;
+ _context = e->_context;
+ _inode = e->_inode;
+ _dev = e->_dev;
+ _objectClass = e->_objectClass;
+ _path = e->_path;
+ _origin = e->_origin;
+}
+
+sefs_entry::~sefs_entry()
+{
+ // do nothing
+}
+
+const apol_context_t *sefs_entry::context() const
+{
+ return _context->context;
+}
+
+ino64_t sefs_entry::inode() const
+{
+ return _inode;
+}
+
+const char *sefs_entry::dev() const
+{
+ return _dev;
+}
+
+uint32_t sefs_entry::objectClass() const
+{
+ return _objectClass;
+}
+
+const char *sefs_entry::path() const
+{
+ return _path;
+}
+
+const char *sefs_entry::origin() const
+{
+ return _origin;
+}
+
+char *sefs_entry::toString() const throw(std::bad_alloc)
+{
+ char *class_str;
+
+ switch (_objectClass)
+ {
+ case QPOL_CLASS_ALL:
+ class_str = " ";
+ break;
+ case QPOL_CLASS_BLK_FILE:
+ class_str = "-b";
+ break;
+ case QPOL_CLASS_CHR_FILE:
+ class_str = "-c";
+ break;
+ case QPOL_CLASS_DIR:
+ class_str = "-d";
+ break;
+ case QPOL_CLASS_FIFO_FILE:
+ class_str = "-p";
+ break;
+ case QPOL_CLASS_FILE:
+ class_str = "--";
+ break;
+ case QPOL_CLASS_LNK_FILE:
+ class_str = "-l";
+ break;
+ case QPOL_CLASS_SOCK_FILE:
+ class_str = "-s";
+ break;
+ default:
+ // should never get here
+ assert(0);
+ class_str = "-?";
+ }
+
+ char *s = NULL;
+ if (asprintf(&s, "%s\t%s\t%s", _path, class_str, _context->context_str) < 0)
+ {
+ SEFS_ERR(_fclist, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ return s;
+}
+
+/******************** private functions below ********************/
+
+sefs_entry::sefs_entry(class sefs_fclist * fclist, const struct sefs_context_node * new_context, uint32_t new_objectClass,
+ const char *new_path, const char *new_origin)
+{
+ _fclist = fclist;
+ _context = new_context;
+ _objectClass = new_objectClass;
+ _inode = 0;
+ _dev = NULL;
+ _path = new_path;
+ _origin = new_origin;
+}
+
+/******************** C functions below ********************/
+
+const apol_context_t *sefs_entry_get_context(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ return ent->context();
+}
+
+ino64_t sefs_entry_get_inode(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return 0;
+ }
+ return ent->inode();
+}
+
+const char *sefs_entry_get_dev(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return 0;
+ }
+ return ent->dev();
+}
+
+uint32_t sefs_entry_get_object_class(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return QPOL_CLASS_ALL;
+ }
+ return ent->objectClass();
+}
+
+const char *sefs_entry_get_path(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ return ent->path();
+}
+
+const char *sefs_entry_get_origin(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ return ent->origin();
+}
+
+char *sefs_entry_to_string(const sefs_entry_t * ent)
+{
+ if (ent == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ return ent->toString();
+}
diff --git a/libsefs/src/fcfile.cc b/libsefs/src/fcfile.cc
new file mode 100644
index 0000000..fcd4096
--- /dev/null
+++ b/libsefs/src/fcfile.cc
@@ -0,0 +1,691 @@
+/**
+ * @file
+ * Implementation of the sefs_fcfile class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sefs_internal.hh"
+
+#include <sefs/entry.hh>
+#include <sefs/fcfile.hh>
+#include <apol/util.h>
+#include <qpol/genfscon_query.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <regex.h>
+#include <stdio.h>
+
+/******************** public functions below ********************/
+
+static void fcfile_entry_free(void *elem)
+{
+ if (elem != NULL)
+ {
+ sefs_entry *entry = static_cast < sefs_entry * >(elem);
+ delete entry;
+ }
+}
+
+sefs_fcfile::sefs_fcfile(sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc):sefs_fclist(SEFS_FCLIST_TYPE_FCFILE,
+ msg_callback, varg)
+{
+ _files = _entries = NULL;
+ _mls_set = false;
+ try
+ {
+ if ((_files = apol_vector_create(free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+ throw;
+ }
+}
+
+sefs_fcfile::sefs_fcfile(const char *file, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc, std::invalid_argument,
+ std::
+ runtime_error):sefs_fclist
+ (SEFS_FCLIST_TYPE_FCFILE, msg_callback, varg)
+{
+ _files = _entries = NULL;
+ _mls_set = false;
+ try
+ {
+ if ((_files = apol_vector_create_with_capacity(1, free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (appendFile(file) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error("Could not construct fcfile with the given file.");
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+ throw;
+ }
+}
+
+sefs_fcfile::sefs_fcfile(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg) throw(std::bad_alloc,
+ std::invalid_argument,
+ std::
+ runtime_error):sefs_fclist
+ (SEFS_FCLIST_TYPE_FCFILE, msg_callback, varg)
+{
+ _files = _entries = NULL;
+ _mls_set = false;
+ try
+ {
+ if (files == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+ if ((_files = apol_vector_create_with_capacity(apol_vector_get_size(files), free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if ((_entries = apol_vector_create(fcfile_entry_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (appendFileList(files) != apol_vector_get_size(files))
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error("Could not construct fcfile with the given vector.");
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+ throw;
+ }
+}
+
+sefs_fcfile::~sefs_fcfile()
+{
+ apol_vector_destroy(&_files);
+ apol_vector_destroy(&_entries);
+}
+
+int sefs_fcfile::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
+ std::invalid_argument)
+{
+ apol_vector_t *type_list = NULL;
+ apol_mls_range_t *range = NULL;
+ int retval = 0;
+ try
+ {
+ if (query != NULL)
+ {
+ query->compile();
+ if (policy != NULL)
+ {
+ if (query->_type != NULL && query->_indirect &&
+ (type_list =
+ query_create_candidate_type(policy, query->_type, query->_retype, query->_regex,
+ query->_indirect)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (query->_range != NULL && query->_rangeMatch != 0 &&
+ (range = apol_mls_range_create_from_string(policy, query->_range)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ }
+
+ for (size_t i = 0; i < apol_vector_get_size(_entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i));
+ if (query != NULL)
+ {
+ const struct sefs_context_node *context = e->_context;
+ if (!query_str_compare(context->user, query->_user, query->_reuser, query->_regex))
+ {
+ continue;
+ }
+ if (!query_str_compare(context->role, query->_role, query->_rerole, query->_regex))
+ {
+ continue;
+ }
+
+ bool str_matched = false, pol_matched = false;
+ str_matched = query_str_compare(context->type, query->_type, query->_retype, query->_regex);
+ if (type_list != NULL && !str_matched)
+ {
+ size_t index;
+ pol_matched =
+ (apol_vector_get_index(type_list, context->type, apol_str_strcmp, NULL, &index) <
+ 0);
+ }
+ if (!str_matched && !pol_matched)
+ {
+ continue;
+ }
+
+ if (isMLS())
+ {
+ if (range == NULL)
+ {
+ if (!query_str_compare
+ (context->range, query->_range, query->_rerange, query->_regex))
+ {
+ continue;
+ }
+ }
+ else
+ {
+ const apol_mls_range_t *context_range = apol_context_get_range(context->context);
+ int ret;
+ ret = apol_mls_range_compare(policy, context_range, range, query->_rangeMatch);
+ if (ret <= 0)
+ {
+ continue;
+ }
+ }
+ }
+
+ if (e->_objectClass != QPOL_CLASS_ALL && query->_objclass != QPOL_CLASS_ALL &&
+ e->_objectClass != query->_objclass)
+ {
+ continue;
+ }
+
+ bool path_matched;
+
+ if (query->_path == NULL || query->_path[0] == '\0')
+ {
+ path_matched = true;
+ }
+ else
+ {
+ path_matched = false;
+ char *anchored_path = NULL;
+ if (asprintf(&anchored_path, "^%s$", e->_path) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ regex_t regex;
+ if (regcomp(&regex, anchored_path, REG_EXTENDED | REG_NOSUB) != 0)
+ {
+ free(anchored_path);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ bool compval = query_str_compare(query->_path, anchored_path, &regex, true);
+ free(anchored_path);
+ regfree(&regex);
+ if (compval)
+ {
+ path_matched = true;
+ }
+ }
+ if (!path_matched)
+ {
+ continue;
+ }
+ }
+
+ // if reached this point, then all criteria passed, so
+ // invoke the mapping function
+
+ if ((retval = fn(this, e, data)) < 0)
+ {
+ return retval;
+ }
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&type_list);
+ apol_mls_range_destroy(&range);
+ throw;
+ }
+ apol_vector_destroy(&type_list);
+ return retval;
+}
+
+bool sefs_fcfile::isMLS() const
+{
+ if (_mls_set)
+ {
+ return _mls;
+ }
+ return false;
+}
+
+int sefs_fcfile::appendFile(const char *file) throw(std::bad_alloc, std::invalid_argument, std::runtime_error)
+{
+ FILE *fc_file = NULL;
+ char *line = NULL, *name_dup = NULL;
+ size_t line_len = 0;
+ size_t last_entry = apol_vector_get_size(_entries);
+ int retval, error = 0;
+
+ regex_t line_regex, context_regex;
+ bool is_line_compiled = false;
+ bool is_context_compiled = false;
+
+ try
+ {
+ if (file == NULL)
+ {
+ errno = EINVAL;
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+
+ fc_file = fopen(file, "r");
+ if (!fc_file)
+ {
+ SEFS_ERR(this, "Unable to open file %s", file);
+ throw std::runtime_error(strerror(error));
+ }
+
+ if ((name_dup = strdup(file)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+
+ if (regcomp(&line_regex, "^([^[:blank:]]+)[[:blank:]]+(-.[[:blank:]]+)?([^-].+)$", REG_EXTENDED) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ is_line_compiled = true;
+
+ if (regcomp(&context_regex, "^([^:]+):([^:]+):([^:]+):?(.*)$", REG_EXTENDED) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ is_context_compiled = true;
+
+ while (!feof(fc_file))
+ {
+ if (getline(&line, &line_len, fc_file) == -1)
+ {
+ if (feof(fc_file))
+ {
+ break;
+ }
+ else
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ }
+ parse_line(name_dup, line, &line_regex, &context_regex);
+ }
+
+ if (apol_vector_append(_files, name_dup) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ name_dup = NULL;
+
+ retval = 0;
+ }
+ catch(...)
+ {
+ error = errno;
+ // discard all entries that were read from this file_contexts
+ size_t i = apol_vector_get_size(_entries);
+ for (; i > last_entry; i--)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(_entries, i - 1));
+ fcfile_entry_free(e);
+ apol_vector_remove(_entries, i - 1);
+ }
+ retval = -1;
+ }
+
+ if (fc_file != NULL)
+ {
+ fclose(fc_file);
+ }
+ if (is_line_compiled)
+ {
+ regfree(&line_regex);
+ }
+ if (is_context_compiled)
+ {
+ regfree(&context_regex);
+ }
+ free(name_dup);
+ free(line);
+ errno = error;
+ return retval;
+}
+
+size_t sefs_fcfile::appendFileList(const apol_vector_t * files)throw(std::bad_alloc, std::invalid_argument, std::runtime_error)
+{
+ size_t i;
+ if (files == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw new std::invalid_argument(strerror(EINVAL));
+ }
+ for (i = 0; i < apol_vector_get_size(files); i++)
+ {
+ if (appendFile(static_cast < char *>(apol_vector_get_element(files, i))) < 0)
+ {
+ return i;
+ }
+ }
+ return i;
+}
+
+const apol_vector_t *sefs_fcfile::fileList() const
+{
+ return _files;
+}
+
+/******************** private functions below ********************/
+
+void sefs_fcfile::parse_line(const char *origin, const char *line, regex_t * line_regex,
+ regex_t * context_regex) throw(std::bad_alloc, std::runtime_error)
+{
+ int error = 0;
+
+ char *s = strdup(line);
+ char *path;
+
+ if (s == NULL)
+ {
+ error = errno;
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+
+ apol_str_trim(s);
+ if (s[0] == '#' || s[0] == '\0')
+ {
+ free(s);
+ return;
+ }
+
+ try
+ {
+ const size_t nmatch = 5;
+ regmatch_t pmatch[nmatch];
+
+ if (regexec(line_regex, s, nmatch, pmatch, 0) != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile line is not legal:\n%s", s);
+ throw std::runtime_error(strerror(error));
+ }
+
+ assert(pmatch[1].rm_so == 0);
+ s[pmatch[1].rm_eo] = '\0';
+ if ((path = strdup(s)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(error));
+ }
+ if (apol_bst_insert_and_get(path_tree, (void **)&path, NULL) < 0)
+ {
+ free(path);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(error));
+ }
+
+ uint32_t objclass;
+ if (pmatch[2].rm_so != -1)
+ {
+ switch (s[pmatch[2].rm_so + 1])
+ {
+ case '-':
+ objclass = QPOL_CLASS_FILE;
+ break;
+ case 'd':
+ objclass = QPOL_CLASS_DIR;
+ break;
+ case 'c':
+ objclass = QPOL_CLASS_CHR_FILE;
+ break;
+ case 'b':
+ objclass = QPOL_CLASS_BLK_FILE;
+ break;
+ case 'p':
+ objclass = QPOL_CLASS_FIFO_FILE;
+ break;
+ case 'l':
+ objclass = QPOL_CLASS_LNK_FILE;
+ break;
+ case 's':
+ objclass = QPOL_CLASS_SOCK_FILE;
+ break;
+ default:
+ error = EIO;
+ SEFS_ERR(this, "%s", "Invalid file context object class.");
+ throw std::runtime_error(strerror(error));
+ }
+ }
+ else
+ {
+ // no object class explicitly given
+ objclass = QPOL_CLASS_ALL;
+ }
+
+ assert(pmatch[3].rm_so != -1);
+ char *context_str = s + pmatch[3].rm_so;
+ char *user, *role, *type, *range;
+
+ if (strcmp(context_str, "<<none>>") == 0)
+ {
+ user = role = type = range = "";
+ }
+ else
+ {
+ if (regexec(context_regex, context_str, nmatch, pmatch, 0) != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is not legal:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+
+ assert(pmatch[1].rm_so == 0);
+ context_str[pmatch[1].rm_eo] = '\0';
+ user = context_str;
+
+ assert(pmatch[2].rm_so != -1);
+ context_str[pmatch[2].rm_eo] = '\0';
+ role = context_str + pmatch[2].rm_so;
+
+ assert(pmatch[3].rm_so != -1);
+ context_str[pmatch[3].rm_eo] = '\0';
+ type = context_str + pmatch[3].rm_so;
+
+ range = NULL;
+ if (pmatch[4].rm_so != -1)
+ {
+ range = context_str + pmatch[4].rm_so;
+ }
+ }
+ if (range != NULL & range[0] != '\0')
+ {
+ if (_mls_set && !_mls)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is MLS, but fcfile is not:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+ _mls = true;
+ _mls_set = true;
+ }
+ else
+ {
+ if (_mls_set && !_mls && strcmp(context_str, "<<none>>") != 0)
+ {
+ error = EIO;
+ SEFS_ERR(this, "fcfile context is not MLS, but fcfile is:\n%s", context_str);
+ throw std::runtime_error(strerror(error));
+ }
+ _mls = true;
+ _mls_set = false;
+ }
+ struct sefs_context_node *context = getContext(user, role, type, range);
+ sefs_entry *entry = new sefs_entry(this, context, objclass, path, origin);
+
+ if (apol_vector_append(_entries, static_cast < void *>(entry)) < 0)
+ {
+ error = errno;
+ delete entry;
+ SEFS_ERR(this, "%s", strerror(error));
+ throw std::bad_alloc();
+ }
+ }
+
+ catch(...)
+ {
+ free(s);
+ errno = error;
+ throw;
+ }
+
+ free(s);
+}
+
+/******************** C functions below ********************/
+
+sefs_fclist_t *sefs_fcfile_create(sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+sefs_fclist_t *sefs_fcfile_create_from_file(const char *file, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(file, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+sefs_fclist_t *sefs_fcfile_create_from_file_list(const apol_vector_t * files, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_fclist *fclist;
+ try
+ {
+ fclist = new sefs_fcfile(files, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fclist;
+}
+
+int sefs_fcfile_append_file(sefs_fcfile_t * fcfile, const char *file)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ fcfile->appendFile(file);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+size_t sefs_fcfile_append_file_list(sefs_fcfile_t * fcfile, const apol_vector_t * files)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return 0;
+ }
+ return fcfile->appendFileList(files);
+}
+
+const apol_vector_t *sefs_fcfile_get_file_list(const sefs_fcfile_t * fcfile)
+{
+ if (fcfile == NULL)
+ {
+ SEFS_ERR(fcfile, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return fcfile->fileList();
+}
diff --git a/libsefs/src/fclist.cc b/libsefs/src/fclist.cc
new file mode 100644
index 0000000..051a6f2
--- /dev/null
+++ b/libsefs/src/fclist.cc
@@ -0,0 +1,766 @@
+/**
+ * @file
+ * Implementation of the sefs_fclist class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sefs_internal.hh"
+
+#include <sefs/entry.hh>
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <selinux/context.h>
+#include <sys/types.h>
+
+static int fclist_sefs_context_node_comp(const void *a, const void *b, void *arg __attribute__ ((unused)))
+{
+ const struct sefs_context_node *n1 = static_cast < const struct sefs_context_node *>(a);
+ const struct sefs_context_node *n2 = static_cast < const struct sefs_context_node *>(b);
+ if (n1->type != n2->type)
+ {
+ return static_cast < int >(reinterpret_cast < ssize_t > (n1->type) - reinterpret_cast < ssize_t > (n2->type));
+ }
+ if (n1->user != n2->user)
+ {
+ return static_cast < int >(reinterpret_cast < ssize_t > (n1->user) - reinterpret_cast < ssize_t > (n2->user));
+ }
+ if (n1->role != n2->role)
+ {
+ return static_cast < int >(reinterpret_cast < ssize_t > (n1->role) - reinterpret_cast < ssize_t > (n2->role));
+ }
+ return static_cast < int >(reinterpret_cast < ssize_t > (n1->range) - reinterpret_cast < ssize_t > (n2->range));
+}
+
+static void fclist_sefs_context_node_free(void *elem)
+{
+ if (elem != NULL)
+ {
+ struct sefs_context_node *node = static_cast < struct sefs_context_node *>(elem);
+ apol_context_destroy(&node->context);
+ free(node->context_str);
+ free(node);
+ }
+}
+
+static int fclist_sefs_node_make_string(struct sefs_context_node *node)
+{
+ free(node->context_str);
+ node->context_str = NULL;
+ if (node->user[0] == '\0' && node->role[0] == '\0' && node->type[0] == '\0' &&
+ (node->range == NULL || node->range[0] == '\0'))
+ {
+ if ((node->context_str = strdup("<<none>>")) == NULL)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ // instead of calling apol_context_render(), use a custom
+ // rendering function if no range is set
+ char *s = NULL;
+ if (asprintf(&s, "%s:%s:%s", node->user, node->role, node->type) < 0)
+ {
+ return -1;
+ }
+ if (node->range != NULL)
+ {
+ size_t len = strlen(s) + 1;
+ if (apol_str_appendf(&s, &len, ":%s", node->range) < 0)
+ {
+ free(s);
+ return -1;
+ }
+ }
+ node->context_str = s;
+ }
+ return 0;
+}
+
+static int fclist_sefs_node_convert(void *data, void *arg)
+{
+ struct sefs_context_node *node = static_cast < struct sefs_context_node *>(data);
+ sefs_fclist *fclist = static_cast < sefs_fclist * >(arg);
+ apol_policy_t *p = fclist->associatePolicy();
+ if (p != NULL)
+ {
+ int retval = apol_context_convert(p, node->context);
+ if (retval < 0)
+ {
+ return retval;
+ }
+ if ((retval = fclist_sefs_node_make_string(node)) < 0)
+ {
+ return retval;
+ }
+ }
+ return 0;
+}
+
+/******************** public functions below ********************/
+
+sefs_fclist::~sefs_fclist()
+{
+ apol_bst_destroy(&user_tree);
+ apol_bst_destroy(&role_tree);
+ apol_bst_destroy(&type_tree);
+ apol_bst_destroy(&range_tree);
+ apol_bst_destroy(&path_tree);
+ apol_bst_destroy(&dev_tree);
+ apol_bst_destroy(&context_tree);
+}
+
+static int map_to_vector(sefs_fclist * fclist, const sefs_entry * entry, void *data)
+{
+ apol_vector_t *v = static_cast < apol_vector_t * >(data);
+ sefs_entry *new_entry = new sefs_entry(entry);
+ if (apol_vector_append(v, new_entry) < 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+static void fclist_entry_free(void *elem)
+{
+ if (elem != NULL)
+ {
+ sefs_entry *entry = static_cast < sefs_entry * >(elem);
+ delete entry;
+ }
+}
+
+apol_vector_t *sefs_fclist::runQuery(sefs_query * query) throw(std::bad_alloc, std::runtime_error, std::invalid_argument)
+{
+ apol_vector_t *v = NULL;
+ try
+ {
+ if ((v = apol_vector_create(fclist_entry_free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if (runQueryMap(query, map_to_vector, v) < 0)
+ {
+ throw std::bad_alloc();
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&v);
+ throw;
+ }
+ return v;
+}
+
+void sefs_fclist::associatePolicy(apol_policy_t * new_policy)
+{
+ policy = new_policy;
+ if (policy != NULL)
+ {
+ if (apol_bst_inorder_map(context_tree, fclist_sefs_node_convert, policy) < 0)
+ {
+ throw new std::bad_alloc();
+ }
+ }
+}
+
+apol_policy_t *sefs_fclist::associatePolicy() const
+{
+ return policy;
+}
+
+sefs_fclist_type_e sefs_fclist::fclist_type() const
+{
+ return _fclist_type;
+}
+
+/******************** protected functions below ********************/
+
+sefs_fclist::sefs_fclist(sefs_fclist_type_e type, sefs_callback_fn_t callback, void *varg)throw(std::bad_alloc)
+{
+ _fclist_type = type;
+ _callback = callback;
+ _varg = varg;
+ policy = NULL;
+ user_tree = role_tree = type_tree = range_tree = path_tree = NULL;
+ dev_tree = NULL;
+ context_tree = NULL;
+ try
+ {
+ if ((user_tree = apol_bst_create(apol_str_strcmp, free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((role_tree = apol_bst_create(apol_str_strcmp, free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((type_tree = apol_bst_create(apol_str_strcmp, free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((range_tree = apol_bst_create(apol_str_strcmp, free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((path_tree = apol_bst_create(apol_str_strcmp, free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((dev_tree = apol_bst_create(apol_str_strcmp, free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((context_tree = apol_bst_create(fclist_sefs_context_node_comp, fclist_sefs_context_node_free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ }
+ catch(...)
+ {
+ apol_bst_destroy(&user_tree);
+ apol_bst_destroy(&role_tree);
+ apol_bst_destroy(&type_tree);
+ apol_bst_destroy(&range_tree);
+ apol_bst_destroy(&path_tree);
+ apol_bst_destroy(&dev_tree);
+ apol_bst_destroy(&context_tree);
+ throw;
+ }
+}
+
+static void sefs_handle_default_callback(void *arg __attribute__ ((unused)),
+ const sefs_fclist * f
+ __attribute__ ((unused)), int level, const char *fmt, va_list va_args)
+{
+ switch (level)
+ {
+ case SEFS_MSG_INFO:
+ {
+ /* by default do not display these messages */
+ return;
+ }
+ case SEFS_MSG_WARN:
+ {
+ fprintf(stderr, "WARNING: ");
+ break;
+ }
+ case SEFS_MSG_ERR:
+ default:
+ {
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+ }
+ vfprintf(stderr, fmt, va_args);
+ fprintf(stderr, "\n");
+}
+
+struct sefs_context_node *sefs_fclist::getContext(const char *user, const char *role, const char *type,
+ const char *range) throw(std::bad_alloc)
+{
+ char *u = NULL, *r = NULL, *t = NULL, *m = NULL;
+ if ((u = strdup(user)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_bst_insert_and_get(user_tree, (void **)&u, NULL) < 0)
+ {
+ free(u);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ if ((r = strdup(role)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_bst_insert_and_get(role_tree, (void **)&r, NULL) < 0)
+ {
+ free(r);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ if ((t = strdup(type)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_bst_insert_and_get(type_tree, (void **)&t, NULL) < 0)
+ {
+ free(t);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ if (range == NULL || range[0] == '\0')
+ {
+ m = NULL;
+ }
+ else
+ {
+ if ((m = strdup(range)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_bst_insert_and_get(range_tree, (void **)&m, NULL) < 0)
+ {
+ free(m);
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+
+ struct sefs_context_node *node = NULL;
+ apol_context_t *context = NULL;
+ try
+ {
+ if ((node = static_cast < struct sefs_context_node * >(calloc(1, sizeof(*node)))) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ node->user = u;
+ node->role = r;
+ node->type = t;
+ node->range = m;
+
+ void *v;
+ if (apol_bst_get_element(context_tree, node, NULL, &v) == 0)
+ {
+ // context already exists
+ fclist_sefs_context_node_free(node);
+ return static_cast < struct sefs_context_node *>(v);
+ }
+
+ apol_mls_range_t *apol_range = NULL;
+ if (m != NULL)
+ {
+ if ((apol_range = apol_mls_range_create_from_literal(m)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ }
+
+ if ((context = apol_context_create()) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ apol_mls_range_destroy(&apol_range);
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_context_set_user(NULL, context, u) < 0 ||
+ apol_context_set_role(NULL, context, r) < 0 || apol_context_set_type(NULL, context, t) < 0 ||
+ apol_context_set_range(NULL, context, apol_range) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ apol_mls_range_destroy(&apol_range);
+ throw std::runtime_error(strerror(errno));
+ }
+
+ node->context = context;
+ context = NULL;
+
+ if (fclist_sefs_node_make_string(node) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+
+ if (apol_bst_insert(context_tree, node, NULL) != 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ catch(...)
+ {
+ fclist_sefs_context_node_free(node);
+ apol_context_destroy(&context);
+ throw;
+ }
+
+ return node;
+}
+
+struct sefs_context_node *sefs_fclist::getContext(const security_context_t scon) throw(std::bad_alloc)
+{
+ context_t con;
+ if ((con = context_new(scon)) == 0)
+ {
+ throw std::bad_alloc();
+ }
+ const char *user = context_user_get(con);
+ const char *role = context_role_get(con);
+ const char *type = context_type_get(con);
+ const char *range = context_range_get(con);
+ struct sefs_context_node *node = NULL;
+ try
+ {
+ node = getContext(user, role, type, range);
+ }
+ catch(...)
+ {
+ context_free(con);
+ throw;
+ }
+ context_free(con);
+ return node;
+}
+
+/******************** private functions below ********************/
+
+void sefs_fclist::handleMsg(int level, const char *fmt, va_list va_args) const
+{
+ if (_callback == NULL)
+ {
+ sefs_handle_default_callback(NULL, this, level, fmt, va_args);
+ }
+ else
+ {
+ _callback(_varg, this, level, fmt, va_args);
+ }
+}
+
+/******************** C functions below ********************/
+
+void sefs_fclist_handleMsg(const struct sefs_fclist *fclist, int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ if (fclist == NULL)
+ {
+ sefs_handle_default_callback(NULL, NULL, level, fmt, ap);
+ }
+ else
+ {
+ fclist->handleMsg(level, fmt, ap);
+ }
+ va_end(ap);
+}
+
+void sefs_fclist_destroy(sefs_fclist_t ** fclist)
+{
+ if (fclist != NULL && *fclist != NULL)
+ {
+ delete(*fclist);
+ *fclist = NULL;
+ }
+}
+
+int sefs_fclist_run_query_map(sefs_fclist_t * fclist, sefs_query_t * query, sefs_fclist_map_fn_t fn, void *data)
+{
+ if (fclist == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ int retval;
+ try
+ {
+ retval = fclist->runQueryMap(query, fn, data);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return retval;
+}
+
+apol_vector_t *sefs_fclist_run_query(sefs_fclist_t * fclist, sefs_query_t * query)
+{
+ if (fclist == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ apol_vector_t *v = NULL;
+ try
+ {
+ v = fclist->runQuery(query);
+ }
+ catch(...)
+ {
+ return NULL;
+ }
+ return v;
+}
+
+bool sefs_fclist_get_is_mls(const sefs_fclist_t * fclist)
+{
+ if (fclist == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ return false;
+ }
+ return fclist->isMLS();
+}
+
+void sefs_fclist_associate_policy(sefs_fclist_t * fclist, apol_policy_t * policy)
+{
+ if (fclist == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ }
+ else
+ {
+ fclist->associatePolicy(policy);
+ }
+}
+
+sefs_fclist_type_e sefs_fclist_get_fclist_type(const sefs_fclist_t * fclist)
+{
+ if (fclist == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ return SEFS_FCLIST_TYPE_NONE;
+ }
+ return fclist->fclist_type();
+}
+
+/******************** private static functions below ********************/
+
+/**
+ * Given a type name, obtain its qpol_type_t pointer (relative to a
+ * policy). If the type is really its alias, get its primary instead.
+ * (Attributes are considered to be always primary.)
+ *
+ * @param p Policy in which to look up types.
+ * @param type_name Name of type to find.
+ *
+ * @return Qpol datum for type, or NULL if not found.
+ */
+static const qpol_type_t *query_get_type(apol_policy_t * p, const char *type_name)
+{
+ unsigned char isalias;
+ const qpol_type_t *type = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ if (qpol_policy_get_type_by_name(q, type_name, &type) < 0 || qpol_type_get_isalias(q, type, &isalias) < 0)
+ {
+ return NULL;
+ }
+ if (isalias)
+ {
+ const char *primary_name;
+ if (qpol_type_get_name(q, type, &primary_name) < 0 || qpol_policy_get_type_by_name(q, primary_name, &type) < 0)
+ {
+ return NULL;
+ }
+ }
+ return type;
+}
+
+/**
+ * Append a non-aliased type name to a vector. If the passed in type
+ * is an alias, find its primary type and append that name instead.
+ *
+ * @param p Policy in which to look up types.
+ * @param v Vector in which append the non-aliased type name.
+ * @param type Type or attribute to append. If this is an alias,
+ * append its primary.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int query_append_type(apol_policy_t * p, apol_vector_t * v, const qpol_type_t * type)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+ unsigned char isalias;
+ const qpol_type_t *real_type = type;
+ const char *name;
+ if (qpol_type_get_isattr(q, type, &isalias) < 0)
+ {
+ return -1;
+ }
+ if (isalias)
+ {
+ if (qpol_type_get_name(q, type, &name) < 0 || qpol_policy_get_type_by_name(q, name, &real_type) < 0)
+ {
+ return -1;
+ }
+ }
+ if (qpol_type_get_name(q, type, &name) < 0 ||
+ apol_vector_append(v, const_cast < void *>(static_cast < const void *>(name))) < 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+apol_vector_t *query_create_candidate_type(apol_policy_t * policy, const char *str, const regex_t * regex, const bool regex_flag,
+ const bool indirect)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ apol_vector_t *list = apol_vector_create(NULL);
+ const qpol_type_t *type;
+ qpol_iterator_t *iter = NULL, *alias_iter = NULL;
+ const char *type_name;
+ bool compval;
+
+ try
+ {
+ if (list == NULL)
+ {
+ throw new std::bad_alloc();
+ }
+
+ if (!regex_flag && (type = query_get_type(policy, str)) != NULL)
+ {
+ if (query_append_type(policy, list, type) < 0)
+ {
+ throw new std::bad_alloc();
+ }
+ }
+
+ if (regex_flag)
+ {
+ if (qpol_policy_get_type_iter(q, &iter) < 0)
+ {
+ throw new std::bad_alloc();
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter))
+ {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0 || qpol_type_get_name(q, type, &type_name) < 0)
+ {
+ throw new std::runtime_error(strerror(errno));
+ }
+ compval = query_str_compare(type_name, str, regex, true);
+ if (compval)
+ {
+ if (query_append_type(policy, list, type) < 0)
+ {
+ throw new std::bad_alloc();
+ }
+ continue;
+ }
+ if (qpol_type_get_alias_iter(q, type, &alias_iter) < 0)
+ {
+ throw new std::bad_alloc();
+ }
+ for (; !qpol_iterator_end(alias_iter); qpol_iterator_next(alias_iter))
+ {
+ if (qpol_iterator_get_item(alias_iter, (void **)&type_name) < 0)
+ {
+ throw new std::runtime_error(strerror(errno));
+ }
+ compval = query_str_compare(type_name, str, regex, true);
+ if (compval)
+ {
+ if (query_append_type(policy, list, type))
+ {
+ throw new std::bad_alloc();
+ }
+ break;
+ }
+ }
+ qpol_iterator_destroy(&alias_iter);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ if (indirect)
+ {
+ size_t orig_vector_size = apol_vector_get_size(list);
+ unsigned char isattr, isalias;
+ for (size_t i = 0; i < orig_vector_size; i++)
+ {
+ type_name = static_cast < char *>(apol_vector_get_element(list, i));
+ qpol_policy_get_type_by_name(q, type_name, &type);
+ assert(type != NULL);
+ if (qpol_type_get_isalias(q, type, &isalias) < 0 || qpol_type_get_isattr(q, type, &isattr) < 0)
+ {
+ throw new std::runtime_error(strerror(errno));
+ }
+ if (isalias)
+ {
+ continue;
+ }
+ if ((isattr &&
+ qpol_type_get_type_iter(q, type, &iter) < 0) ||
+ (!isattr && qpol_type_get_attr_iter(q, type, &iter) < 0))
+ {
+ throw new std::bad_alloc();
+ }
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter))
+ {
+ if (qpol_iterator_get_item(iter, (void **)&type) < 0)
+ {
+ throw new std::runtime_error(strerror(errno));
+ }
+ if (query_append_type(policy, list, type))
+ {
+ throw new std::bad_alloc();
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ }
+
+ apol_vector_sort_uniquify(list, NULL, NULL);
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&list);
+ }
+ qpol_iterator_destroy(&iter);
+ qpol_iterator_destroy(&alias_iter);
+ return list;
+}
+
+bool query_str_compare(const char *target, const char *str, const regex_t * regex, const bool regex_flag)
+{
+ if (str == NULL || str[0] == '\0')
+ {
+ return true;
+ }
+ if (target == NULL || target[0] == '\0')
+ {
+ return false;
+ }
+ if (regex_flag)
+ {
+ if (regexec(regex, target, 0, NULL, 0) == 0)
+ {
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ if (strcmp(target, str) == 0)
+ {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/libsefs/src/filesystem.cc b/libsefs/src/filesystem.cc
new file mode 100644
index 0000000..8dfd32b
--- /dev/null
+++ b/libsefs/src/filesystem.cc
@@ -0,0 +1,733 @@
+/**
+ * @file
+ * Implementation of the sefs_filesystem class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sefs_internal.hh"
+#include "new_ftw.h"
+
+#include <sefs/entry.hh>
+#include <sefs/filesystem.hh>
+#include <apol/util.h>
+#include <selinux/context.h>
+#include <selinux/selinux.h>
+#include <assert.h>
+#include <errno.h>
+#include <mntent.h>
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+extern int lgetfilecon_raw(const char *, security_context_t *) __attribute__ ((weak));
+
+/**
+ * As that setools must work with older libselinux versions that may
+ * not have the _raw() functions, declare them as weak. If libselinux
+ * does indeed have the new functions then use them; otherwise
+ * fallback to the originals.
+ */
+static int filesystem_lgetfilecon(const char *path, security_context_t * context)
+{
+ if (lgetfilecon_raw != NULL)
+ {
+ return lgetfilecon_raw(path, context);
+ }
+ else
+ {
+ return lgetfilecon(path, context);
+ }
+}
+
+#if 0
+
+/**
+ * Given a directory, find all bounded mounted filesystems within that
+ * directory (or subdirectory within.) This function consults the
+ * entries written to /etc/mtab to determine if something is mounted
+ * or not and if it has the "bind" option. Note that if \a dir itself
+ * is a mount, it will not be reported; a subdirectory might.
+ *
+ * Note that the returned vector in never actually used by this
+ * library. This function existed in previous versions of libsefs,
+ * but was never documented why it existed. Rather than eliminate
+ * this function, it is retained (but effectively unused), in case a
+ * future revision of libsefs necessitates finding bind mounts.
+ *
+ * @param dir Directory to begin search.
+ *
+ * @return An allocated vector containing pathnames (type char *) to
+ * each mounted location with the "bind" option. The caller is
+ * responsible for calling apol_vector_destroy() afterwards.
+ */
+static apol_vector_t *filesystem_find_mount_points(const char *dir) throw(std::bad_alloc, std::runtime_error)
+{
+ char *dirdup = NULL;
+ apol_vector_t *v = NULL;
+ FILE *mtab = NULL;
+ struct mntent *entry;
+
+ try
+ {
+ if ((dirdup = strdup(dir)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ size_t len = strlen(dirdup);
+ if (len > 1 && dirdup[len - 1] == '/')
+ {
+ dirdup[len - 1] = '\0';
+ }
+ if ((v = apol_vector_create(free)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((mtab = fopen("/etc/mtab", "r")) == NULL)
+ {
+ throw std::runtime_error(strerror(errno));
+ }
+ // note non thread-safeness below
+ while ((entry = getmntent(mtab)) != NULL)
+ {
+ if (strstr(entry->mnt_dir, dir) != entry->mnt_dir)
+ {
+ continue;
+ }
+ if (strcmp(entry->mnt_dir, dirdup) == 0)
+ {
+ continue;
+ }
+ if (strstr(entry->mnt_opts, "bind") != NULL)
+ {
+ char *s = strdup(entry->mnt_dir);
+ if (s == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if (apol_vector_append(v, s) < 0)
+ {
+ free(s);
+ throw std::bad_alloc();
+ }
+ }
+ }
+
+ }
+ catch(...)
+ {
+ free(dirdup);
+ apol_vector_destroy(&v);
+ if (mtab != NULL)
+ {
+ fclose(mtab);
+ }
+ throw;
+ }
+ free(dirdup);
+ fclose(mtab);
+ return v;
+}
+
+#endif
+
+/******************** public functions below ********************/
+
+sefs_filesystem::sefs_filesystem(const char *new_root, sefs_callback_fn_t msg_callback, void *varg)throw(std::bad_alloc, std::invalid_argument, std::runtime_error):sefs_fclist(SEFS_FCLIST_TYPE_FILESYSTEM,
+ msg_callback,
+ varg)
+{
+ if (new_root == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+ _root = NULL;
+ _mls = false;
+ try
+ {
+ // check that root exists and is readable
+ struct stat64 sb;
+ if (stat64(new_root, &sb) != 0 && !S_ISDIR(sb.st_mode))
+ {
+ SEFS_ERR(this, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ throw std::invalid_argument(strerror(EINVAL));
+ }
+
+ // determine if filesystem is MLS or not
+ security_context_t scon;
+ if (filesystem_lgetfilecon(new_root, &scon) < 0)
+ {
+ SEFS_ERR(this, "Could not read SELinux file context for %s.", new_root);
+ throw std::runtime_error(strerror(errno));
+ }
+ context_t con;
+ if ((con = context_new(scon)) == 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ freecon(scon);
+ throw std::runtime_error(strerror(errno));
+ }
+ freecon(scon);
+ const char *range = context_range_get(con);
+ if (range != NULL && range[0] != '\0')
+ {
+ _mls = true;
+ }
+ context_free(con);
+
+ if ((_root = strdup(new_root)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ }
+ catch(...)
+ {
+ free(_root);
+ throw;
+ }
+}
+
+sefs_filesystem::~sefs_filesystem()
+{
+ free(_root);
+}
+
+struct filesystem_ftw_struct
+{
+ sefs_filesystem *fs;
+ sefs_query *query;
+ apol_vector_t *dev_map; //< vector of filesystem_dev entries
+ apol_vector_t *type_list;
+ apol_mls_range_t *range;
+ sefs_fclist_map_fn_t fn;
+ void *data;
+ bool aborted;
+ int retval;
+};
+
+// wrapper functions to go between non-OO land into OO member functions
+
+inline struct sefs_context_node *filesystem_get_context(sefs_filesystem * fs, security_context_t scon) throw(std::bad_alloc)
+{
+ return fs->getContext(scon);
+}
+
+inline sefs_entry *filesystem_get_entry(sefs_filesystem * fs, const struct sefs_context_node * node, uint32_t objClass,
+ const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc)
+{
+ return fs->getEntry(node, objClass, path, ino, dev_name);
+}
+
+inline bool filesystem_is_query_match(sefs_filesystem * fs, const sefs_query * query, const char *path, const char *dev,
+ const struct stat64 * sb, apol_vector_t * type_list,
+ apol_mls_range_t * range)throw(std::runtime_error)
+{
+ return fs->isQueryMatch(query, path, dev, sb, type_list, range);
+}
+
+static uint32_t filesystem_stat_to_objclass(const struct stat64 *sb)
+{
+ if (S_ISREG(sb->st_mode))
+ {
+ return QPOL_CLASS_FILE;
+ }
+ if (S_ISDIR(sb->st_mode))
+ {
+ return QPOL_CLASS_DIR;
+ }
+ if (S_ISCHR(sb->st_mode))
+ {
+ return QPOL_CLASS_CHR_FILE;
+ }
+ if (S_ISBLK(sb->st_mode))
+ {
+ return QPOL_CLASS_BLK_FILE;
+ }
+ if (S_ISFIFO(sb->st_mode))
+ {
+ return QPOL_CLASS_FIFO_FILE;
+ }
+ if (S_ISLNK(sb->st_mode))
+ {
+ return QPOL_CLASS_LNK_FILE;
+ }
+ if (S_ISSOCK(sb->st_mode))
+ {
+ return QPOL_CLASS_SOCK_FILE;
+ }
+ assert(0); // should never get here
+ return 0;
+}
+
+struct filesystem_dev
+{
+ dev_t dev;
+ char *dev_name; //< pointer into the dev_tree
+};
+
+static int filesystem_dev_cmp(const void *a, const void *b __attribute__ ((unused)), void *arg)
+{
+ const struct filesystem_dev *d1 = static_cast < const struct filesystem_dev *>(a);
+ dev_t *d2 = static_cast < dev_t * >(arg);
+ if (d1->dev < *d2)
+ {
+ return -1;
+ }
+ else if (d1->dev > *d2)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+static int filesystem_ftw_handler(const char *fpath, const struct stat64 *sb, int typeflag
+ __attribute__ ((unused)), struct FTW *ftwbuf __attribute__ ((unused)), void *data)
+{
+ struct filesystem_ftw_struct *s = static_cast < struct filesystem_ftw_struct *>(data);
+
+ size_t i;
+ void *dev_num = const_cast < void *>(static_cast < const void *>(&(sb->st_dev)));
+ int rc = apol_vector_get_index(s->dev_map, NULL, filesystem_dev_cmp, dev_num, &i);
+ const char *dev = "<unknown>";
+ if (rc == 0)
+ {
+ // if the device number was discovered in buildDevMap
+ // then store the device name within the entry
+ struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(s->dev_map, i));
+ dev = d->dev_name;
+ }
+ else
+ {
+ SEFS_WARN(s->fs, "Unknown device for %s.", fpath);
+ }
+ try
+ {
+ if (!filesystem_is_query_match(s->fs, s->query, fpath, dev, sb, s->type_list, s->range))
+ {
+ return 0;
+ }
+ }
+ catch(...)
+ {
+ return -1;
+ }
+
+ security_context_t scon;
+ if (filesystem_lgetfilecon(fpath, &scon) < 0)
+ {
+ SEFS_ERR(s->fs, "Could not read SELinux file context for %s.", fpath);
+ return -1;
+ }
+ struct sefs_context_node *node = NULL;
+ try
+ {
+ node = filesystem_get_context(s->fs, scon);
+ }
+ catch(...)
+ {
+ freecon(scon);
+ return -1;
+ }
+ freecon(scon);
+
+ uint32_t objClass = filesystem_stat_to_objclass(sb);
+
+ sefs_entry *entry = NULL;
+ try
+ {
+ entry = filesystem_get_entry(s->fs, node, objClass, fpath, sb->st_ino, dev);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+
+ // invoke real callback (not just the nftw handler)
+ s->retval = s->fn(s->fs, entry, s->data);
+ delete entry;
+ if (s->retval < 0)
+ {
+ s->aborted = true;
+ return s->retval;
+ }
+
+ return 0;
+}
+
+int sefs_filesystem::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
+ std::invalid_argument)
+{
+ struct filesystem_ftw_struct s;
+ s.dev_map = NULL;
+ s.type_list = NULL;
+ s.range = NULL;
+ try
+ {
+ s.dev_map = buildDevMap();
+ if (query != NULL)
+ {
+ query->compile();
+ if (policy != NULL)
+ {
+ if (query->_type != NULL && query->_indirect &&
+ (s.type_list =
+ query_create_candidate_type(policy, query->_type, query->_retype, query->_regex,
+ query->_indirect)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (query->_range != NULL && query->_rangeMatch != 0 &&
+ (s.range = apol_mls_range_create_from_string(policy, query->_range)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ }
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&s.dev_map);
+ apol_vector_destroy(&s.type_list);
+ apol_mls_range_destroy(&s.range);
+ throw;
+ }
+ s.fs = this;
+ s.query = query;
+ s.fn = fn;
+ s.data = data;
+ s.aborted = false;
+ s.retval = 0;
+
+ int retval = new_nftw64(_root, filesystem_ftw_handler, 1024, 0, &s);
+ apol_vector_destroy(&s.dev_map);
+ apol_vector_destroy(&s.type_list);
+ apol_mls_range_destroy(&s.range);
+ if (retval != 0 && !s.aborted)
+ {
+ // error was generated by new_nftw64() itself, not
+ // from callback
+ return retval;
+ }
+ return s.retval;
+}
+
+bool sefs_filesystem::isMLS() const
+{
+ return _mls;
+}
+
+const char *sefs_filesystem::root() const
+{
+ return _root;
+}
+
+/******************** private functions below ********************/
+
+static void filesystem_dev_free(void *elem)
+{
+ if (elem != NULL)
+ {
+ struct filesystem_dev *d = static_cast < struct filesystem_dev *>(elem);
+ // don't free the device name pointer, because it's pointing
+ // into the dev_tree BST
+ free(d);
+ }
+}
+
+const char *sefs_filesystem::getDevName(const dev_t dev) throw(std::runtime_error)
+{
+ apol_vector_t *dev_map = buildDevMap();
+ size_t i;
+ void *devp = const_cast < dev_t * >(&dev);
+ int rc = apol_vector_get_index(dev_map, NULL, filesystem_dev_cmp, devp, &i);
+ if (rc < 0)
+ {
+ apol_vector_destroy(&dev_map);
+ return NULL;
+ }
+ struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(dev_map, i));
+ const char *dev_name = d->dev_name; // this is pointing into this->_dev_tree
+ apol_vector_destroy(&dev_map);
+ return dev_name;
+}
+
+/**
+ * For each entry in /etc/mtab, record the device number and the name
+ * of the mounted file system. This provides the mapping between a
+ * device number and its source device.
+ *
+ * @return Vector of filesystem_dev entries. The caller must call
+ * apol_vector_destroy() upon the vector afterwards.
+ * @exception If error allocating space, unable to open /etc/mtab, or
+ * unable to parse mtab file.
+ */
+apol_vector_t *sefs_filesystem::buildDevMap(void)throw(std::runtime_error)
+{
+ apol_vector_t *dev_map;
+ if ((dev_map = apol_vector_create(filesystem_dev_free)) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ FILE *f = NULL;
+ try
+ {
+ if ((f = fopen("/etc/mtab", "r")) == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ char buf[256];
+ struct mntent mntbuf;
+ while (getmntent_r(f, &mntbuf, buf, 256) != NULL)
+ {
+ struct stat sb;
+ if (stat(mntbuf.mnt_dir, &sb) == -1)
+ {
+ // could not open this device, so skip
+ // it (and hope it won't be examined
+ // during runQuery())
+ continue;
+ }
+ else
+ {
+ struct filesystem_dev *d = static_cast < struct filesystem_dev *>(calloc(1, sizeof(*d)));
+ if (d == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_vector_append(dev_map, d) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ filesystem_dev_free(d);
+ throw std::runtime_error(strerror(errno));
+ }
+ d->dev = sb.st_dev;
+ char *mnt_fsname = strdup(mntbuf.mnt_fsname);
+ if (mnt_fsname == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ if (apol_bst_insert_and_get(dev_tree, (void **)&mnt_fsname, NULL) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ free(mnt_fsname);
+ throw std::runtime_error(strerror(errno));
+ }
+ d->dev_name = mnt_fsname;
+ }
+ }
+ }
+ catch(...)
+ {
+ apol_vector_destroy(&dev_map);
+ if (f != NULL)
+ {
+ fclose(f);
+ }
+ throw;
+ }
+ fclose(f);
+ return dev_map;
+}
+
+bool sefs_filesystem::isQueryMatch(const sefs_query * query, const char *path, const char *dev, const struct stat64 * sb,
+ apol_vector_t * type_list, apol_mls_range_t * range)throw(std::runtime_error)
+{
+ if (query == NULL)
+ {
+ return true;
+ }
+ security_context_t scon;
+ if (filesystem_lgetfilecon(path, &scon) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::runtime_error(strerror(errno));
+ }
+ context_t con;
+ if ((con = context_new(scon)) == 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ freecon(scon);
+ throw std::runtime_error(strerror(errno));
+ }
+ freecon(scon);
+
+ if (!query_str_compare(context_user_get(con), query->_user, query->_reuser, query->_regex))
+ {
+ context_free(con);
+ return false;
+ }
+ if (!query_str_compare(context_role_get(con), query->_role, query->_rerole, query->_regex))
+ {
+ context_free(con);
+ return false;
+ }
+
+ bool str_matched = false, pol_matched = false;
+ str_matched = query_str_compare(context_type_get(con), query->_type, query->_retype, query->_regex);
+ if (type_list != NULL && !str_matched)
+ {
+ size_t index;
+ pol_matched = (apol_vector_get_index(type_list, context_type_get(con), apol_str_strcmp, NULL, &index) < 0);
+ }
+ if (!str_matched && !pol_matched)
+ {
+ context_free(con);
+ return false;
+ }
+
+ if (isMLS())
+ {
+ if (range == NULL)
+ {
+ if (!query_str_compare(context_range_get(con), query->_range, query->_rerange, query->_regex))
+ {
+ context_free(con);
+ return false;
+ }
+ }
+ else
+ {
+ assert(policy != NULL);
+ apol_mls_range_t *context_range = apol_mls_range_create_from_string(policy, context_range_get(con));
+ if (context_range == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ context_free(con);
+ throw std::runtime_error(strerror(errno));
+ }
+ int ret;
+ ret = apol_mls_range_compare(policy, range, context_range, query->_rangeMatch);
+ apol_mls_range_destroy(&context_range);
+ if (ret <= 0)
+ {
+ context_free(con);
+ return false;
+ }
+ }
+ }
+
+ context_free(con);
+
+ if (query->_objclass != 0 && query->_objclass != filesystem_stat_to_objclass(sb))
+ {
+ return false;
+ }
+
+ if (!query_str_compare(path, query->_path, query->_repath, query->_regex))
+ {
+ return false;
+ }
+
+ if (query->_inode != 0 && query->_inode != sb->st_ino)
+ {
+ return false;
+ }
+
+ if (!query_str_compare(dev, query->_dev, query->_redev, query->_regex))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+sefs_entry *sefs_filesystem::getEntry(const struct sefs_context_node * context, uint32_t objectClass,
+ const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc)
+{
+ char *s = strdup(path);
+ if (s == NULL)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ throw std::bad_alloc();
+ }
+ if (apol_bst_insert_and_get(path_tree, (void **)&s, NULL) < 0)
+ {
+ SEFS_ERR(this, "%s", strerror(errno));
+ free(s);
+ throw std::bad_alloc();
+ }
+ sefs_entry *e = new sefs_entry(this, context, objectClass, s);
+ e->_inode = ino;
+ e->_dev = dev_name;
+ return e;
+}
+
+/******************** C functions below ********************/
+
+sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg)
+{
+ sefs_filesystem_t *fs;
+ try
+ {
+ fs = new sefs_filesystem(root, msg_callback, varg);
+ }
+ catch(...)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return fs;
+}
+
+const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs)
+{
+ if (fs == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ return fs->root();
+}
+
+extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev)
+{
+ if (fs == NULL)
+ {
+ SEFS_ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return NULL;
+ }
+ const char *dev_name = NULL;
+ try
+ {
+ dev_name = fs->getDevName(dev);
+ }
+ catch(...)
+ {
+ return NULL;
+ }
+ return dev_name;
+}
diff --git a/libsefs/src/libsefs.map b/libsefs/src/libsefs.map
new file mode 100644
index 0000000..f6a6b28
--- /dev/null
+++ b/libsefs/src/libsefs.map
@@ -0,0 +1,29 @@
+VERS_4.0 {
+ global:
+ extern "C++" {
+ # typeinfo exports
+ *sefs_db;
+ *sefs_entry;
+ *sefs_fcfile;
+ *sefs_fclist;
+ *sefs_filesystem;
+ *sefs_query;
+ *std::invalid_argument;
+
+ sefs_db::*;
+ sefs_entry::*;
+ sefs_fcfile::*;
+ sefs_fclist::*;
+ sefs_filesystem::*;
+ sefs_query::*;
+ };
+ sefs_db_*;
+ sefs_default_file_contexts_get_path;
+ sefs_entry_*;
+ sefs_fcfile_*;
+ sefs_fclist_*;
+ sefs_filesystem_*;
+ sefs_query_*;
+ libsefs_get_version;
+ local: *;
+};
diff --git a/libsefs/src/new_ftw.c b/libsefs/src/new_ftw.c
new file mode 100644
index 0000000..5ebaf29
--- /dev/null
+++ b/libsefs/src/new_ftw.c
@@ -0,0 +1,749 @@
+/* File tree walker functions.
+ Copyright (C) 1996-2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/**
+ * @file
+ *
+ * Implementation of the improved new_ftw() and new_nftw() functions.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if __GNUC__
+# define alloca __builtin_alloca
+#else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+# pragma alloca
+# else
+char *alloca();
+# endif
+# endif
+#endif
+
+#if defined _LIBC
+# include <dirent.h>
+# define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
+#else
+# if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen ((dirent)->d_name)
+# else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+# endif
+#endif
+
+#include <dirent.h>
+#undef NAMLEN
+#define NAMLEN(dirent) strlen ((dirent)->d_name)
+
+#include <errno.h>
+#include "new_ftw.h"
+#include <limits.h>
+#include <search.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if HAVE_SYS_PARAM_H || defined _LIBC
+# include <sys/param.h>
+#endif
+#ifdef _LIBC
+# include <include/sys/stat.h>
+#else
+# include <sys/stat.h>
+#endif
+
+#if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy
+char *stpcpy();
+#endif
+
+#if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy
+/* Be CAREFUL that there are no side effects in N. */
+# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
+#endif
+
+/* #define NDEBUG 1 */
+#include <assert.h>
+
+#ifndef _LIBC
+# undef __chdir
+# define __chdir chdir
+# undef __closedir
+# define __closedir closedir
+# undef __fchdir
+# define __fchdir fchdir
+# undef __getcwd
+# define __getcwd(P, N) xgetcwd ()
+extern char *xgetcwd(void);
+# undef __mempcpy
+# define __mempcpy mempcpy
+# undef __opendir
+# define __opendir opendir
+# undef __readdir64
+# define __readdir64 readdir
+# undef __stpcpy
+# define __stpcpy stpcpy
+# undef __tdestroy
+# define __tdestroy tdestroy
+# undef __tfind
+# define __tfind tfind
+# undef __tsearch
+# define __tsearch tsearch
+# undef internal_function
+# define internal_function /* empty */
+# undef dirent64
+# define dirent64 dirent
+# undef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+/* Arrange to make lstat calls go through the wrapper function
+ on systems with an lstat function that does not dereference symlinks
+ that are specified with a trailing slash. */
+#if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK
+int rpl_lstat(const char *, struct stat *);
+# undef lstat
+# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
+#endif
+
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
+/* Support for the LFS API version. */
+#ifndef NEW_FTW_NAME
+# define NEW_FTW_NAME new_ftw
+# define NEW_NFTW_NAME new_nftw
+# define NEW_NFTW_OLD_NAME __new_old_nftw
+# define NEW_NFTW_NEW_NAME __new_new_nftw
+# define INO_T ino_t
+# define STAT stat
+# ifdef _LIBC
+# define LXSTAT __lxstat
+# define XSTAT __xstat
+# else
+# define LXSTAT(V,f,sb) lstat (f,sb)
+# define XSTAT(V,f,sb) stat (f,sb)
+# endif
+# define NEW_FTW_FUNC_T __new_ftw_func_t
+# define NEW_NFTW_FUNC_T __new_nftw_func_t
+#endif
+
+/* We define PATH_MAX if the system does not provide a definition.
+ This does not artificially limit any operation. PATH_MAX is simply
+ used as a guesstimate for the expected maximal path length.
+ Buffers will be enlarged if necessary. */
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+struct dir_data
+{
+ DIR *stream;
+ char *content;
+};
+
+struct known_object
+{
+ dev_t dev;
+ INO_T ino;
+};
+
+struct new_ftw_data
+{
+ /* Array with pointers to open directory streams. */
+ struct dir_data **dirstreams;
+ size_t actdir;
+ size_t maxdir;
+
+ /* Buffer containing name of currently processed object. */
+ char *dirbuf;
+ size_t dirbufsize;
+
+ /* Passed as fourth argument to `nftw' callback. The `base' member
+ tracks the content of the `dirbuf'. */
+ struct FTW ftw;
+
+ /* Flags passed to `nftw' function. 0 for `ftw'. */
+ int flags;
+
+ /* Conversion array for flag values. It is the identity mapping for
+ `nftw' calls, otherwise it maps the values to those known by
+ `ftw'. */
+ const int *cvt_arr;
+
+ /* Callback function. We always use the `nftw' form. */
+ NEW_NFTW_FUNC_T func;
+ void *data;
+
+ /* Device of starting point. Needed for FTW_MOUNT. */
+ dev_t dev;
+
+ /* Data structure for keeping fingerprints of already processed
+ object. This is needed when not using FTW_PHYS. */
+ void *known_objects;
+};
+
+/* Internally we use the FTW_* constants used for `nftw'. When invoked
+ as `ftw', map each flag to the subset of values used by `ftw'. */
+static const int nftw_arr[] = {
+ FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
+};
+
+static const int ftw_arr[] = {
+ FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
+};
+
+/* Forward declarations of local functions. */
+static int ftw_dir(struct new_ftw_data *data, struct STAT *st, struct dir_data *old_dir) internal_function;
+
+static int object_compare(const void *p1, const void *p2)
+{
+ /* We don't need a sophisticated and useful comparison. We are only
+ interested in equality. However, we must be careful not to
+ accidentally compare `holes' in the structure. */
+ const struct known_object *kp1 = p1, *kp2 = p2;
+ int cmp1;
+ cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
+ if (cmp1 != 0)
+ return cmp1;
+ return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
+}
+
+static inline int add_object(struct new_ftw_data *data, struct STAT *st)
+{
+ struct known_object *newp = malloc(sizeof(struct known_object));
+ if (newp == NULL)
+ return -1;
+ newp->dev = st->st_dev;
+ newp->ino = st->st_ino;
+ return __tsearch(newp, &data->known_objects, object_compare) ? 0 : -1;
+}
+
+static inline int find_object(struct new_ftw_data *data, struct STAT *st)
+{
+ struct known_object obj;
+ obj.dev = st->st_dev;
+ obj.ino = st->st_ino;
+ return __tfind(&obj, &data->known_objects, object_compare) != NULL;
+}
+
+static inline int __attribute((always_inline)) open_dir_stream(struct new_ftw_data *data, struct dir_data *dirp)
+{
+ int result = 0;
+
+ if (data->dirstreams[data->actdir] != NULL) {
+ /* Oh, oh. We must close this stream. Get all remaining
+ entries and store them as a list in the `content' member of
+ the `struct dir_data' variable. */
+ size_t bufsize = 1024;
+ char *buf = malloc(bufsize);
+
+ if (buf == NULL)
+ result = -1;
+ else {
+ DIR *st = data->dirstreams[data->actdir]->stream;
+ struct dirent64 *d;
+ size_t actsize = 0;
+
+ while ((d = __readdir64(st)) != NULL) {
+ size_t this_len = NAMLEN(d);
+ if (actsize + this_len + 2 >= bufsize) {
+ char *newp;
+ bufsize += MAX(1024, 2 * this_len);
+ newp = (char *)realloc(buf, bufsize);
+ if (newp == NULL) {
+ /* No more memory. */
+ int save_err = errno;
+ free(buf);
+ __set_errno(save_err);
+ result = -1;
+ break;
+ }
+ buf = newp;
+ }
+
+ *((char *)__mempcpy(buf + actsize, d->d_name, this_len))
+ = '\0';
+ actsize += this_len + 1;
+ }
+
+ /* Terminate the list with an additional NUL byte. */
+ buf[actsize++] = '\0';
+
+ /* Shrink the buffer to what we actually need. */
+ data->dirstreams[data->actdir]->content = realloc(buf, actsize);
+ if (data->dirstreams[data->actdir]->content == NULL) {
+ int save_err = errno;
+ free(buf);
+ __set_errno(save_err);
+ result = -1;
+ } else {
+ __closedir(st);
+ data->dirstreams[data->actdir]->stream = NULL;
+ data->dirstreams[data->actdir] = NULL;
+ }
+ }
+ }
+
+ /* Open the new stream. */
+ if (result == 0) {
+ const char *name = ((data->flags & FTW_CHDIR)
+ ? data->dirbuf + data->ftw.base : data->dirbuf);
+ assert(data->dirstreams[data->actdir] == NULL);
+
+ dirp->stream = __opendir(name);
+ if (dirp->stream == NULL)
+ result = -1;
+ else {
+ dirp->content = NULL;
+ data->dirstreams[data->actdir] = dirp;
+
+ if (++data->actdir == data->maxdir)
+ data->actdir = 0;
+ }
+ }
+
+ return result;
+}
+
+static int internal_function process_entry(struct new_ftw_data *data, struct dir_data *dir, const char *name, size_t namlen)
+{
+ struct STAT st;
+ int result = 0;
+ int flag = 0;
+ size_t new_buflen;
+
+ if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
+ /* Don't process the "." and ".." entries. */
+ return 0;
+
+ new_buflen = data->ftw.base + namlen + 2;
+ if (data->dirbufsize < new_buflen) {
+ /* Enlarge the buffer. */
+ char *newp;
+
+ data->dirbufsize = 2 * new_buflen;
+ newp = (char *)realloc(data->dirbuf, data->dirbufsize);
+ if (newp == NULL)
+ return -1;
+ data->dirbuf = newp;
+ }
+
+ *((char *)__mempcpy(data->dirbuf + data->ftw.base, name, namlen)) = '\0';
+
+ if ((data->flags & FTW_CHDIR) == 0)
+ name = data->dirbuf;
+
+ if (((data->flags & FTW_PHYS)
+ ? LXSTAT(_STAT_VER, name, &st)
+ : XSTAT(_STAT_VER, name, &st)) < 0) {
+ if (errno != EACCES && errno != ENOENT)
+ result = -1;
+ else if (!(data->flags & FTW_PHYS)
+ && LXSTAT(_STAT_VER, name, &st) == 0 && S_ISLNK(st.st_mode))
+ flag = FTW_SLN;
+ else
+ flag = FTW_NS;
+ } else {
+ if (S_ISDIR(st.st_mode))
+ flag = FTW_D;
+ else if (S_ISLNK(st.st_mode))
+ flag = FTW_SL;
+ else
+ flag = FTW_F;
+ }
+
+ if (result == 0 && (flag == FTW_NS || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev)) {
+ if (flag == FTW_D) {
+ if ((data->flags & FTW_PHYS)
+ || (!find_object(data, &st)
+ /* Remember the object. */
+ && (result = add_object(data, &st)) == 0))
+ result = ftw_dir(data, &st, dir);
+ } else
+ result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], &data->ftw, data->data);
+ }
+
+ if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
+ result = 0;
+
+ return result;
+}
+
+static int __attribute((noinline))
+internal_function ftw_dir(struct new_ftw_data *data, struct STAT *st, struct dir_data *old_dir)
+{
+ struct dir_data dir;
+ struct dirent64 *d;
+ int previous_base = data->ftw.base;
+ int result;
+ char *startp;
+
+ /* Open the stream for this directory. This might require that
+ another stream has to be closed. */
+ result = open_dir_stream(data, &dir);
+ if (result != 0) {
+ if (errno == EACCES)
+ /* We cannot read the directory. Signal this with a special flag. */
+ result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw, data->data);
+
+ return result;
+ }
+
+ /* First, report the directory (if not depth-first). */
+ if (!(data->flags & FTW_DEPTH)) {
+ result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw, data->data);
+ if (result != 0) {
+ int save_err;
+ fail:
+ save_err = errno;
+ __closedir(dir.stream);
+ __set_errno(save_err);
+
+ if (data->actdir-- == 0)
+ data->actdir = data->maxdir - 1;
+ data->dirstreams[data->actdir] = NULL;
+ return result;
+ }
+ }
+
+ /* If necessary, change to this directory. */
+ if (data->flags & FTW_CHDIR) {
+ if (__fchdir(dirfd(dir.stream)) < 0) {
+ result = -1;
+ goto fail;
+ }
+ }
+
+ /* Next, update the `struct FTW' information. */
+ ++data->ftw.level;
+ startp = strchr(data->dirbuf, '\0');
+ /* There always must be a directory name. */
+ assert(startp != data->dirbuf);
+ if (startp[-1] != '/')
+ *startp++ = '/';
+ data->ftw.base = startp - data->dirbuf;
+
+ while (dir.stream != NULL && (d = __readdir64(dir.stream)) != NULL) {
+ result = process_entry(data, &dir, d->d_name, NAMLEN(d));
+ if (result != 0)
+ break;
+ }
+
+ if (dir.stream != NULL) {
+ /* The stream is still open. I.e., we did not need more
+ descriptors. Simply close the stream now. */
+ int save_err = errno;
+
+ assert(dir.content == NULL);
+
+ __closedir(dir.stream);
+ __set_errno(save_err);
+
+ if (data->actdir-- == 0)
+ data->actdir = data->maxdir - 1;
+ data->dirstreams[data->actdir] = NULL;
+ } else {
+ int save_err;
+ char *runp = dir.content;
+
+ while (result == 0 && *runp != '\0') {
+ char *endp = strchr(runp, '\0');
+
+ result = process_entry(data, &dir, runp, endp - runp);
+
+ runp = endp + 1;
+ }
+
+ save_err = errno;
+ free(dir.content);
+ __set_errno(save_err);
+ }
+
+ if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
+ result = 0;
+
+ /* Prepare the return, revert the `struct FTW' information. */
+ data->dirbuf[data->ftw.base - 1] = '\0';
+ --data->ftw.level;
+ data->ftw.base = previous_base;
+
+ /* Finally, if we process depth-first report the directory. */
+ if (result == 0 && (data->flags & FTW_DEPTH))
+ result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw, data->data);
+
+ if (old_dir && (data->flags & FTW_CHDIR)
+ && (result == 0 || ((data->flags & FTW_ACTIONRETVAL)
+ && (result != -1 && result != FTW_STOP)))) {
+ /* Change back to the parent directory. */
+ int done = 0;
+ if (old_dir->stream != NULL)
+ if (__fchdir(dirfd(old_dir->stream)) == 0)
+ done = 1;
+
+ if (!done) {
+ if (data->ftw.base == 1) {
+ if (__chdir("/") < 0)
+ result = -1;
+ } else if (__chdir("..") < 0)
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+static int __attribute((noinline))
+internal_function new_ftw_startup(const char *dir, int is_nftw, void *func, int descriptors, int flags, void *func_data)
+{
+ struct new_ftw_data data;
+ struct STAT st;
+ int result = 0;
+ int save_err;
+ char *cwd = NULL;
+ char *cp;
+
+ /* First make sure the parameters are reasonable. */
+ if (dir[0] == '\0') {
+ __set_errno(ENOENT);
+ return -1;
+ }
+
+ data.maxdir = descriptors < 1 ? 1 : descriptors;
+ data.actdir = 0;
+ data.dirstreams = (struct dir_data **)alloca(data.maxdir * sizeof(struct dir_data *));
+ memset(data.dirstreams, '\0', data.maxdir * sizeof(struct dir_data *));
+
+ /* PATH_MAX is always defined when we get here. */
+ data.dirbufsize = MAX(2 * strlen(dir), PATH_MAX);
+ data.dirbuf = (char *)malloc(data.dirbufsize);
+ if (data.dirbuf == NULL)
+ return -1;
+ cp = __stpcpy(data.dirbuf, dir);
+ /* Strip trailing slashes. */
+ while (cp > data.dirbuf + 1 && cp[-1] == '/')
+ --cp;
+ *cp = '\0';
+
+ data.ftw.level = 0;
+
+ /* Find basename. */
+ while (cp > data.dirbuf && cp[-1] != '/')
+ --cp;
+ data.ftw.base = cp - data.dirbuf;
+
+ data.flags = flags;
+
+ /* This assignment might seem to be strange but it is what we want.
+ The trick is that the first three arguments to the `ftw' and
+ `nftw' callback functions are equal. Therefore we can call in
+ every case the callback using the format of the `nftw' version
+ and get the correct result since the stack layout for a function
+ call in C allows this. */
+ data.func = (NEW_NFTW_FUNC_T) func;
+ data.data = func_data;
+
+ /* Since we internally use the complete set of FTW_* values we need
+ to reduce the value range before calling a `ftw' callback. */
+ data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
+
+ /* No object known so far. */
+ data.known_objects = NULL;
+
+ /* Now go to the directory containing the initial file/directory. */
+ if (flags & FTW_CHDIR) {
+ /* GNU extension ahead. */
+ cwd = __getcwd(NULL, 0);
+ if (cwd == NULL)
+ result = -1;
+ else if (data.ftw.base > 0) {
+ /* Change to the directory the file is in. In data.dirbuf
+ we have a writable copy of the file name. Just NUL
+ terminate it for now and change the directory. */
+ if (data.ftw.base == 1)
+ /* I.e., the file is in the root directory. */
+ result = __chdir("/");
+ else {
+ char ch = data.dirbuf[data.ftw.base - 1];
+ data.dirbuf[data.ftw.base - 1] = '\0';
+ result = __chdir(data.dirbuf);
+ data.dirbuf[data.ftw.base - 1] = ch;
+ }
+ }
+ }
+
+ /* Get stat info for start directory. */
+ if (result == 0) {
+ const char *name = ((data.flags & FTW_CHDIR)
+ ? data.dirbuf + data.ftw.base : data.dirbuf);
+
+ if (((flags & FTW_PHYS)
+ ? LXSTAT(_STAT_VER, name, &st)
+ : XSTAT(_STAT_VER, name, &st)) < 0) {
+ if (!(flags & FTW_PHYS)
+ && errno == ENOENT && LXSTAT(_STAT_VER, name, &st) == 0 && S_ISLNK(st.st_mode))
+ result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN], &data.ftw, data.data);
+ else
+ /* No need to call the callback since we cannot say anything
+ about the object. */
+ result = -1;
+ } else {
+ if (S_ISDIR(st.st_mode)) {
+ /* Remember the device of the initial directory in case
+ FTW_MOUNT is given. */
+ data.dev = st.st_dev;
+
+ /* We know this directory now. */
+ if (!(flags & FTW_PHYS))
+ result = add_object(&data, &st);
+
+ if (result == 0)
+ result = ftw_dir(&data, &st, NULL);
+ } else {
+ int flag = S_ISLNK(st.st_mode) ? FTW_SL : FTW_F;
+
+ result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag], &data.ftw, data.data);
+ }
+ }
+
+ if ((flags & FTW_ACTIONRETVAL)
+ && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
+ result = 0;
+ }
+
+ /* Return to the start directory (if necessary). */
+ if (cwd != NULL) {
+ int save_err = errno;
+ __chdir(cwd);
+ free(cwd);
+ __set_errno(save_err);
+ }
+
+ /* Free all memory. */
+ save_err = errno;
+ __tdestroy(data.known_objects, free);
+ free(data.dirbuf);
+ __set_errno(save_err);
+
+ return result;
+}
+
+/* Entry points. */
+
+int NEW_FTW_NAME(path, func, descriptors, data)
+const char *path;
+NEW_FTW_FUNC_T func;
+int descriptors;
+void *data;
+{
+ return new_ftw_startup(path, 0, func, descriptors, 0, data);
+}
+
+#ifndef _LIBC
+int NEW_NFTW_NAME(path, func, descriptors, flags, data)
+const char *path;
+NEW_NFTW_FUNC_T func;
+int descriptors;
+int flags;
+void *data;
+{
+ return new_ftw_startup(path, 1, func, descriptors, flags, data);
+}
+#else
+
+#include <shlib-compat.h>
+
+int NEW_NFTW_NEW_NAME(const char *, NEW_NFTW_FUNC_T, int, int, void *);
+
+int NEW_NFTW_NEW_NAME(path, func, descriptors, flags, data)
+const char *path;
+NEW_NFTW_FUNC_T func;
+int descriptors;
+int flags;
+void *data;
+{
+ if (flags & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL)) {
+ __set_errno(EINVAL);
+ return -1;
+ }
+ return new_ftw_startup(path, 1, func, descriptors, flags, data);
+}
+
+//versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3);
+
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
+
+/* Older nftw* version just ignored all unknown flags. */
+
+int NEW_NFTW_OLD_NAME(const char *, NEW_NFTW_FUNC_T, int, int, void *);
+
+int attribute_compat_text_section NFTW_OLD_NAME(path, func, descriptors, flags, void *)
+const char *path;
+NFTW_FUNC_T func;
+int descriptors;
+int flags;
+void *data;
+{
+ flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
+ return ftw_startup(path, 1, func, descriptors, flags, data);
+}
+
+compat_symbol(libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1);
+#endif
+#endif
+
+int rpl_lstat(file, sbuf)
+const char *file;
+struct stat *sbuf;
+{
+ if (file && *file == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return lstat(file, sbuf);
+}
+
+#include <stdio.h>
+
+char *xgetcwd(void)
+{
+ char *cwd = getcwd(NULL, 0);
+ if (!cwd && errno == ENOMEM) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ return cwd;
+}
diff --git a/libsefs/src/new_ftw.h b/libsefs/src/new_ftw.h
new file mode 100644
index 0000000..dfc6905
--- /dev/null
+++ b/libsefs/src/new_ftw.h
@@ -0,0 +1,183 @@
+/* Copyright (C) 1992,1996-1999,2003,2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/**
+ * @file
+ *
+ * This file looks and acts like /usr/include/ftw.h, with one
+ * <em>significant</em> difference. The standard ftw() and nftw()
+ * functions do not permit passing an arbitrary pointer into the
+ * callback. This greatly limits the functions' usefulness on
+ * object-oriented systems and in thread-safe libraries. Thus the
+ * header file declares two new functions, new_ftw() and new_nftw(),
+ * that act the same as the original, with the addition of an
+ * arbitrary void pointer as the last argument. This void pointer,
+ * which may be NULL, is passed into the callback function's last
+ * parameter.
+ */
+
+/*
+ * X/Open Portability Guide 4.2: ftw.h
+ */
+
+#ifndef _NEW_FTW_H
+#define _NEW_FTW_H 1
+
+#include <features.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+__BEGIN_DECLS
+/* Values for the FLAG argument to the user function passed to `ftw'
+ and 'nftw'. */
+ enum
+{
+ FTW_F, /* Regular file. */
+#define FTW_F FTW_F
+ FTW_D, /* Directory. */
+#define FTW_D FTW_D
+ FTW_DNR, /* Unreadable directory. */
+#define FTW_DNR FTW_DNR
+ FTW_NS, /* Unstatable file. */
+#define FTW_NS FTW_NS
+
+#if defined __USE_BSD || defined __USE_XOPEN_EXTENDED
+
+ FTW_SL, /* Symbolic link. */
+# define FTW_SL FTW_SL
+#endif
+
+#ifdef __USE_XOPEN_EXTENDED
+/* These flags are only passed from the `nftw' function. */
+ FTW_DP, /* Directory, all subdirs have been visited. */
+# define FTW_DP FTW_DP
+ FTW_SLN /* Symbolic link naming non-existing file. */
+# define FTW_SLN FTW_SLN
+#endif /* extended X/Open */
+};
+
+#ifdef __USE_XOPEN_EXTENDED
+/* Flags for fourth argument of `nftw'. */
+enum
+{
+ FTW_PHYS = 1, /* Perform physical walk, ignore symlinks. */
+# define FTW_PHYS FTW_PHYS
+ FTW_MOUNT = 2, /* Report only files on same file system as the
+ argument. */
+# define FTW_MOUNT FTW_MOUNT
+ FTW_CHDIR = 4, /* Change to current directory while processing it. */
+# define FTW_CHDIR FTW_CHDIR
+ FTW_DEPTH = 8 /* Report files in directory before directory itself. */
+# define FTW_DEPTH FTW_DEPTH
+# ifdef __USE_GNU
+ ,
+ FTW_ACTIONRETVAL = 16 /* Assume callback to return FTW_* values instead of
+ zero to continue and non-zero to terminate. */
+# define FTW_ACTIONRETVAL FTW_ACTIONRETVAL
+# endif
+};
+
+#ifdef __USE_GNU
+/* Return values from callback functions. */
+enum
+{
+ FTW_CONTINUE = 0, /* Continue with next sibling or for FTW_D with the
+ first child. */
+# define FTW_CONTINUE FTW_CONTINUE
+ FTW_STOP = 1, /* Return from `ftw' or `nftw' with FTW_STOP as return
+ value. */
+# define FTW_STOP FTW_STOP
+ FTW_SKIP_SUBTREE = 2, /* Only meaningful for FTW_D: Don't walk through the
+ subtree, instead just continue with its next
+ sibling. */
+# define FTW_SKIP_SUBTREE FTW_SKIP_SUBTREE
+ FTW_SKIP_SIBLINGS = 3 /* Continue with FTW_DP callback for current directory
+ (if FTW_DEPTH) and then its siblings. */
+# define FTW_SKIP_SIBLINGS FTW_SKIP_SIBLINGS
+};
+#endif
+
+/* Structure used for fourth argument to callback function for `nftw'. */
+struct FTW
+{
+ int base;
+ int level;
+};
+#endif /* extended X/Open */
+
+/* Convenient types for callback functions. */
+typedef int (*__new_ftw_func_t) (__const char *__filename, __const struct stat * __status, int __flag, void *__data);
+#ifdef __USE_LARGEFILE64
+typedef int (*__new_ftw64_func_t) (__const char *__filename, __const struct stat64 * __status, int __flag, void *__data);
+#endif
+#ifdef __USE_XOPEN_EXTENDED
+typedef int (*__new_nftw_func_t) (__const char *__filename,
+ __const struct stat * __status, int __flag, struct FTW * __info, void *__data);
+# ifdef __USE_LARGEFILE64
+typedef int (*__new_nftw64_func_t) (__const char *__filename,
+ __const struct stat64 * __status, int __flag, struct FTW * __info, void *__data);
+# endif
+#endif
+
+/* Call a function on every element in a directory tree.
+
+ This function is a possible cancellation point and therefore not
+ marked with __THROW. */
+#ifndef __USE_FILE_OFFSET64
+extern int new_ftw(__const char *__dir, __new_ftw_func_t __func, int __descriptors, void *__data) __nonnull((1, 2));
+#else
+# ifdef __REDIRECT
+extern int __REDIRECT(new_ftw, (__const char *__dir, __new_ftw_func_t __func,
+ int __descriptors, void *__data), new_ftw64) __nonnull((1, 2));
+# else
+# define new_ftw new_ftw64
+# endif
+#endif
+#ifdef __USE_LARGEFILE64
+extern int new_ftw64(__const char *__dir, __new_ftw64_func_t __func, int __descriptors, void *__data) __nonnull((1, 2));
+#endif
+
+#ifdef __USE_XOPEN_EXTENDED
+/* Call a function on every element in a directory tree. FLAG allows
+ to specify the behaviour more detailed.
+
+ This function is a possible cancellation point and therefore not
+ marked with __THROW. */
+# ifndef __USE_FILE_OFFSET64
+extern int new_nftw(__const char *__dir, __new_nftw_func_t __func, int __descriptors, int __flag, void *__data) __nonnull((1, 2));
+# else
+# ifdef __REDIRECT
+extern int __REDIRECT(new_nftw, (__const char *__dir, __new_nftw_func_t __func,
+ int __descriptors, int __flag, void *__data), new_nftw64) __nonnull((1, 2));
+# else
+# define new_nftw new_nftw64
+# endif
+# endif
+# ifdef __USE_LARGEFILE64
+extern int new_nftw64(__const char *__dir, __new_nftw64_func_t __func,
+ int __descriptors, int __flag, void *__data) __nonnull((1, 2));
+# endif
+#endif
+
+/* Compile new_nftw() to always have LFS enabled. */
+extern int __REDIRECT(new_nftw, (__const char *__dir, __new_nftw_func_t __func,
+ int __descriptors, int __flag, void *__data), new_nftw64) __nonnull((1, 2));
+
+__END_DECLS
+#endif /* ftw.h */
diff --git a/libsefs/src/query.cc b/libsefs/src/query.cc
new file mode 100644
index 0000000..64c4e6b
--- /dev/null
+++ b/libsefs/src/query.cc
@@ -0,0 +1,431 @@
+/**
+ * @file
+ * Implementation of the sefs_query class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sefs_internal.hh"
+
+#include <sefs/query.hh>
+#include <apol/util.h>
+#include <qpol/genfscon_query.h>
+
+#include <assert.h>
+#include <errno.h>
+
+/******************** public functions below ********************/
+
+sefs_query::sefs_query()
+{
+ _user = _role = _type = _range = NULL;
+ _path = _dev = NULL;
+ _objclass = QPOL_CLASS_ALL;
+ _indirect = _regex = _recursive = false;
+ _inode = 0;
+ _recompiled = false;
+ _reuser = _rerole = _retype = _rerange = _repath = _redev = NULL;
+}
+
+sefs_query::~sefs_query()
+{
+ free(_user);
+ free(_role);
+ free(_type);
+ free(_range);
+ free(_path);
+ free(_dev);
+ if (_recompiled)
+ {
+ regfree(_reuser);
+ free(_reuser);
+ regfree(_rerole);
+ free(_rerole);
+ regfree(_retype);
+ free(_retype);
+ regfree(_rerange);
+ free(_rerange);
+ regfree(_repath);
+ free(_repath);
+ regfree(_redev);
+ free(_redev);
+ }
+}
+
+void sefs_query::user(const char *name) throw(std::bad_alloc)
+{
+ if (name != _user)
+ {
+ free(_user);
+ _user = NULL;
+ if (name != NULL && *name != '\0' && (_user = strdup(name)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ }
+}
+
+void sefs_query::role(const char *name) throw(std::bad_alloc)
+{
+ if (name != _role)
+ {
+ free(_role);
+ _role = NULL;
+ if (name != NULL && *name != '\0' && (_role = strdup(name)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ }
+}
+
+void sefs_query::type(const char *name, bool indirect) throw(std::bad_alloc)
+{
+ if (name != _type)
+ {
+ free(_type);
+ _type = NULL;
+ if (name != NULL && *name != '\0')
+ {
+ if ((_type = strdup(name)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ _indirect = indirect;
+ }
+ }
+}
+
+void sefs_query::range(const char *name, int match) throw(std::bad_alloc)
+{
+ if (name != _range)
+ {
+ free(_range);
+ _range = NULL;
+ if (name != NULL && *name != '\0')
+ {
+ if ((_range = strdup(name)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ _rangeMatch = match;
+ }
+ }
+}
+
+void sefs_query::objectClass(uint32_t objclass)
+{
+ _objclass = objclass;
+}
+
+void sefs_query::objectClass(const char *name)
+{
+ if (name == NULL || *name == '\0' || strcmp(name, "any") == 0)
+ {
+ _objclass = QPOL_CLASS_ALL;
+ }
+ else
+ {
+ uint32_t o = apol_str_to_objclass(name);
+ if (o != QPOL_CLASS_ALL)
+ {
+ _objclass = o;
+ }
+ }
+}
+
+void sefs_query::path(const char *str) throw(std::bad_alloc)
+{
+ if (str != _path)
+ {
+ free(_path);
+ _path = NULL;
+ if (str != NULL && *str != '\0' && (_path = strdup(str)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ }
+}
+
+void sefs_query::inode(ino64_t ino)
+{
+ _inode = ino;
+}
+
+void sefs_query::dev(const char *str) throw(std::bad_alloc)
+{
+ if (str != _dev)
+ {
+ free(_dev);
+ _dev = NULL;
+ if (str != NULL && *str != '\0' && (_dev = strdup(str)) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ }
+}
+
+void sefs_query::regex(bool r)
+{
+ _regex = r;
+}
+
+/******************** private functions below ********************/
+
+void sefs_query::compile() throw(std::bad_alloc, std::invalid_argument)
+{
+ if (_recompiled)
+ {
+ regfree(_reuser);
+ regfree(_rerole);
+ regfree(_retype);
+ regfree(_rerange);
+ regfree(_repath);
+ regfree(_redev);
+ }
+ else
+ {
+ if ((_reuser = static_cast < regex_t * >(malloc(sizeof(*_reuser)))) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((_rerole = static_cast < regex_t * >(malloc(sizeof(*_rerole)))) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((_retype = static_cast < regex_t * >(malloc(sizeof(*_retype)))) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((_rerange = static_cast < regex_t * >(malloc(sizeof(*_rerange)))) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((_repath = static_cast < regex_t * >(malloc(sizeof(*_repath)))) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ if ((_redev = static_cast < regex_t * >(malloc(sizeof(*_redev)))) == NULL)
+ {
+ throw std::bad_alloc();
+ }
+ }
+ char errbuf[1024] = { '\0' };
+ int regretv;
+ const char *s = (_user == NULL ? "" : _user);
+ if ((regretv = regcomp(_reuser, s, REG_EXTENDED | REG_NOSUB)))
+ {
+ regerror(regretv, _reuser, errbuf, 1024);
+ throw std::invalid_argument(errbuf);
+ }
+ s = (_role == NULL ? "" : _role);
+ if ((regretv = regcomp(_rerole, s, REG_EXTENDED | REG_NOSUB)))
+ {
+ regerror(regretv, _reuser, errbuf, 1024);
+ throw std::invalid_argument(errbuf);
+ }
+ s = (_type == NULL ? "" : _type);
+ if ((regretv = regcomp(_retype, s, REG_EXTENDED | REG_NOSUB)))
+ {
+ regerror(regretv, _reuser, errbuf, 1024);
+ throw std::invalid_argument(errbuf);
+ }
+ s = (_range == NULL ? "" : _range);
+ if ((regretv = regcomp(_rerange, s, REG_EXTENDED | REG_NOSUB)))
+ {
+ regerror(regretv, _reuser, errbuf, 1024);
+ throw std::invalid_argument(errbuf);
+ }
+ s = (_path == NULL ? "" : _path);
+ if ((regretv = regcomp(_repath, s, REG_EXTENDED | REG_NOSUB)))
+ {
+ regerror(regretv, _reuser, errbuf, 1024);
+ throw std::invalid_argument(errbuf);
+ }
+ s = (_dev == NULL ? "" : _dev);
+ if ((regretv = regcomp(_redev, s, REG_EXTENDED | REG_NOSUB)))
+ {
+ regerror(regretv, _reuser, errbuf, 1024);
+ throw std::invalid_argument(errbuf);
+ }
+ _recompiled = true;
+}
+
+/******************** C functions below ********************/
+
+sefs_query_t *sefs_query_create()
+{
+ return new sefs_query();
+}
+
+void sefs_query_destroy(sefs_query_t ** query)
+{
+ if (query != NULL && *query != NULL)
+ {
+ delete(*query);
+ *query = NULL;
+ }
+}
+
+int sefs_query_set_user(sefs_query_t * query, const char *name)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ query->user(name);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int sefs_query_set_role(sefs_query_t * query, const char *name)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ query->role(name);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int sefs_query_set_type(sefs_query_t * query, const char *name, bool indirect)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ query->type(name, indirect);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int sefs_query_set_range(sefs_query_t * query, const char *range, int match)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ query->range(range, match);
+ return 0;
+}
+
+int sefs_query_set_object_class(sefs_query_t * query, uint32_t objclass)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ query->objectClass(objclass);
+ return 0;
+}
+
+int sefs_query_set_object_class_str(sefs_query_t * query, const char *name)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ query->objectClass(name);
+ return 0;
+}
+
+int sefs_query_set_path(sefs_query_t * query, const char *path)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ query->path(path);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int sefs_query_set_inode(sefs_query_t * query, ino64_t inode)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ query->inode(inode);
+ return 0;
+}
+
+int sefs_query_set_dev(sefs_query_t * query, const char *dev)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ try
+ {
+ query->dev(dev);
+ }
+ catch(...)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+int sefs_query_set_regex(sefs_query_t * query, bool regex)
+{
+ if (query == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ query->regex(regex);
+ return 0;
+}
diff --git a/libsefs/src/sefs_internal.hh b/libsefs/src/sefs_internal.hh
new file mode 100644
index 0000000..20a2775
--- /dev/null
+++ b/libsefs/src/sefs_internal.hh
@@ -0,0 +1,78 @@
+/**
+ * @file
+ * Additional declarations for use solely by libsefs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEFS_INTERNAL_HH
+#define SEFS_INTERNAL_HH
+
+#include <apol/bst.h>
+#include <sefs/fclist.hh>
+#include <regex.h>
+
+/**
+ * Given a policy containing types, generate and return a vector of
+ * names (char *) that match the given criteria.
+ *
+ * @param policy Policy associated with types.
+ * @param str Type name to find.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use.
+ * @param regex_flag If true, use the compiled regular expression
+ * instead of str.
+ * @param indirect If true, do indirect type matching.
+ *
+ * @return Vector of strings. The caller is responsible for calling
+ * apol_vector_destroy() upon the returned value afterwards.
+ */
+apol_vector_t *query_create_candidate_type(apol_policy_t * policy, const char *str, const regex_t * regex, const bool regex_flag,
+ const bool indirect);
+
+/**
+ * Determines if a string matches a target symbol name. If \a
+ * regex_flag is true, use the compiled regular expression instead of
+ * \a str. Otherwise do a straight string comparison between \a str
+ * and \a target. If \a str is NULL and/or empty then the comparison
+ * always succeeds regardless of \a regex and \a target. Next, if \a
+ * target is NULL or empty then comparison fails.
+ *
+ * @param target Name of target symbol to compare.
+ * @param str Source string from which to compare.
+ * @param regex If using regexp comparison, the compiled regular
+ * expression to use.
+ * @param regex_flag If true, use the compiled regular expression
+ * instead.
+ *
+ * @return true if comparison succeeds, false if not.
+ */
+bool query_str_compare(const char *target, const char *str, const regex_t * regex, const bool regex_flag);
+
+// rather than having each sefs_entry having its own apol_context_t
+// object, build a cache of nodes to save space
+struct sefs_context_node
+{
+ apol_context_t *context; // each node owns its apol context
+ const char *user, *role, *type, *range; // these are pointers into fclists's BSTs
+ char *context_str; // each node owns the string
+};
+
+#endif
diff --git a/libsefs/src/util.c b/libsefs/src/util.c
new file mode 100644
index 0000000..6c27297
--- /dev/null
+++ b/libsefs/src/util.c
@@ -0,0 +1,46 @@
+/**
+ * @file
+ *
+ * Implementation of utility functions for libsefs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#ifdef LIBSELINUX
+#include <selinux/selinux.h>
+#endif
+
+const char *libsefs_get_version(void)
+{
+ return LIBSEFS_VERSION_STRING;
+}
+
+char *sefs_default_file_contexts_get_path(void)
+{
+#ifdef LIBSELINUX
+ return strdup(selinux_file_context_path());
+#else
+ return strdup("");
+#endif
+}
diff --git a/libsefs/swig/Makefile.am b/libsefs/swig/Makefile.am
new file mode 100644
index 0000000..6af1d63
--- /dev/null
+++ b/libsefs/swig/Makefile.am
@@ -0,0 +1,15 @@
+if DO_SWIGIFY_PYTHON
+ MAYBE_PYSWIG = python
+endif
+
+if DO_SWIGIFY_JAVA
+ MAYBE_JSWIG = java
+endif
+
+if DO_SWIGIFY_TCL
+ MAYBE_TCLSWIG = tcl
+endif
+
+SUBDIRS = $(MAYBE_PYSWIG) $(MAYBE_JSWIG) $(MAYBE_TCLSWIG)
+
+dist_noinst_DATA = sefs.i
diff --git a/libsefs/swig/java/MANIFEST.MF.in b/libsefs/swig/java/MANIFEST.MF.in
new file mode 100644
index 0000000..0ac9628
--- /dev/null
+++ b/libsefs/swig/java/MANIFEST.MF.in
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+
+Name: com/tresys/setools/
+Specification-Title: "SETools Java Libraries"
+Specification-Version: "@VERSION@"
+Specification-Vendor: "Tresys Technology"
+Implementation-Title: "com.tresys.setools.sefs"
+Implementation-Version: "@libsefs_version@"
+Implementation-Vendor: "Tresys Technology"
+Extension-List: qpol apol
+qpol-Extension-Name: com.tresys.setools.qpol
+qpol-Implementation-Version: @libqpol_version@
+apol-Extension-Name: com.tresys.setools.apol
+apol-Implementation-Version: @libapol_version@
diff --git a/libsefs/swig/java/Makefile.am b/libsefs/swig/java/Makefile.am
new file mode 100644
index 0000000..088b61b
--- /dev/null
+++ b/libsefs/swig/java/Makefile.am
@@ -0,0 +1,85 @@
+wrappedso_DATA = libjsefs.so.@libsefs_version@
+wrappedso_SONAME = @libsefs_jswig_soname@
+short_name = libjsefs.so
+wrappedsodir = $(libdir)
+
+package_name = com.tresys.setools.sefs
+package_dir = $(dir $(subst .,/,$(package_name)))sefs
+
+wrappedjar_DATA = sefs.jar
+wrappedjardir = $(setoolsdir)
+
+dist_noinst_DATA = $(srcdir)/../sefs.i
+BUILT_SOURCES = sefs_wrap.cc \
+ sefsConstants.java \
+ sefs_db.java \
+ sefs_entry.java \
+ sefs_fcfile.java \
+ sefs_fclist.java \
+ sefs_fclist_type_e.java \
+ sefs_filesystem.java \
+ sefs.java \
+ sefsJNI.java \
+ sefs_query.java \
+ SWIGTYPE_p_dev_t.java \
+ SWIGTYPE_p_f_p_sefs_fclist_p_q_const__sefs_entry_p_void__int.java \
+ SWIGTYPE_p_f_p_void_p_q_const__sefs_fclist_int_p_q_const__char_va_list__void.java \
+ SWIGTYPE_p_ino64_t.java \
+ SWIGTYPE_p_void.java
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libsefs/include
+AM_JFLAGS = @DEBUGJFLAGS@ @WARNJFLAGS@ \
+ -classpath $(top_builddir)/libqpol/swig/java/qpol.jar:$(top_builddir)/libapol/swig/java/apol.jar
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @SEFS_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so
+
+$(firstword $(BUILT_SOURCES)): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_JAVA_OPT) -package $(package_name) -o $@ \
+ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wordlist 2,$(words $(BUILT_SOURCES)), $(BUILT_SOURCES)): $(firstword $(BUILT_SOURCES))
+
+$(wrappedso_DATA): $(filter %.cc, $(BUILT_SOURCES))
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_JAVA_CFLAGS) -DSWIGJAVA=1 $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+# Intentionally do not include SWIGTYPE_p_void.java below so that the
+# Java compiler uses the one created in package
+# com.tresys.setools.qpol instead of the one from package
+# com.tresys.setools.sefs.
+java_files = $(filter-out SWIGTYPE_p_void.java, $(filter %.java, $(BUILT_SOURCES)))
+
+classes = $(patsubst %.java, $(package_dir)/%.class, $(java_files))
+
+# Because the Java compiler can generate multiple class files from the
+# same source .java file, putting all of the classes below will result
+# in repeated invocations of javac. Therefore, an alternative is to
+# just depend upon the first class file, and let the Java compiler
+# create the rest of them.
+$(firstword $(classes)): $(java_files)
+ $(JAVAC) $(AM_JFLAGS) $(JAVAFLAGS) -d . $^
+
+$(wordlist 2,$(words $(classes)),$(classes)): $(firstword $(classes))
+
+$(wrappedjar_DATA): MANIFEST.MF
+
+$(wrappedjar_DATA): $(classes)
+ $(JAR) cfm $@ MANIFEST.MF $^
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(short_name)
+ $(mkdir_p) $(DESTDIR)$(javadir) && cd $(DESTDIR)$(javadir) && $(LN_S) -f $(wrappedjardir)/$(wrappedjar_DATA)
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/$(short_name)
+ -rm -f $(DESTDIR)$(javadir)/$(wrappedjar_DATA)
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(classes) $(wrappedso_DATA) $(wrappedjar_DATA) $(wrappedso_SONAME) $(short_name)
diff --git a/libsefs/swig/python/Makefile.am b/libsefs/swig/python/Makefile.am
new file mode 100644
index 0000000..f7fb7b3
--- /dev/null
+++ b/libsefs/swig/python/Makefile.am
@@ -0,0 +1,39 @@
+wrappedso_DATA = _sefs.so.@libsefs_version@
+wrappedso_SONAME = @libsefs_pyswig_soname@
+wrappedsodir = $(pkgpyexecdir)
+
+wrappedpy_DATA = sefs.py
+wrappedpydir = $(pkgpyexecdir)
+
+dist_noinst_DATA = $(srcdir)/../sefs.i
+BUILT_SOURCES = sefs_wrap.cc
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libsefs/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @PYTHON_LDFLAGS@ \
+ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @SEFS_LIB_FLAG@ @XML_LIBS@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_PYTHON_OPT) -o $@ \
+ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libqpol/include \
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _sefs.so
+
+$(wrappedpy_DATA): $(BUILT_SOURCES)
+
+install-data-hook:
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) $(wrappedso_SONAME)
+ cd $(DESTDIR)$(wrappedsodir) && $(LN_S) -f $(wrappedso_DATA) _sefs.so
+
+uninstall-local:
+ -rm -rf $(DESTDIR)$(wrappedsodir)/$(wrappedso_SONAME) $(DESTDIR)$(wrappedsodir)/_sefs.so
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedpy_DATA) $(wrappedso_SONAME) _sefs.so sefs.pyc
diff --git a/libsefs/swig/sefs.i b/libsefs/swig/sefs.i
new file mode 100644
index 0000000..adc5d7e
--- /dev/null
+++ b/libsefs/swig/sefs.i
@@ -0,0 +1,162 @@
+/**
+ * @file
+ * SWIG declarations for libsefs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%module sefs
+
+%{
+#include <sefs/db.hh>
+#include <sefs/entry.hh>
+#include <sefs/fcfile.hh>
+#include <sefs/fclist.hh>
+#include <sefs/filesystem.hh>
+#include <sefs/query.hh>
+#include <sefs/util.h>
+%}
+
+%import apol.i
+
+%exception;
+
+%include std_except.i
+
+%{
+#undef BEGIN_EXCEPTION
+#undef END_EXCEPTION
+%}
+
+%inline %{
+ typedef struct apol_string_vector apol_string_vector_t;
+%}
+
+#ifdef SWIGPYTHON
+
+%typemap(out) time_t {
+ $result = PyInt_FromLong((long) $1);
+}
+
+#endif // end of python specific code
+
+#ifdef SWIGJAVA
+
+/* handle size_t correctly in java as architecture independent */
+%typemap(jni) size_t "jlong"
+%typemap(jtype) size_t "long"
+%typemap(jstype) size_t "long"
+%typemap("javaimports") SWIGTYPE, FILE* %{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+/* the following handles the dependencies on qpol and apol */
+%pragma(java) jniclassimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+%pragma(java) jniclasscode=%{
+ static {
+ try
+ {
+ libsefs_get_version ();
+ }
+ catch (UnsatisfiedLinkError ule)
+ {
+ System.loadLibrary("jsefs");
+ }
+ }
+%}
+%pragma(java) moduleimports=%{
+import com.tresys.setools.qpol.*;
+import com.tresys.setools.apol.*;
+%}
+
+%apply long { time_t }
+
+%javaconst(1);
+
+#else
+/* not in java so handle size_t as architecture dependent */
+#ifdef SWIGWORDSIZE64
+typedef uint64_t size_t;
+#else
+typedef uint32_t size_t;
+#endif
+
+#endif // end of Java specific code
+
+#ifdef SWIGTCL
+
+%wrapper %{
+/* Tcl module's initialization routine is expected to be named
+ * Sefs_Init(), but the output file will be called libtsefs.so instead
+ * of libsefs.so. Therefore add an alias from Tsefs_Init() to the
+ * real Sefs_Init().
+ */
+SWIGEXPORT int Tsefs_Init(Tcl_Interp *interp) {
+ return SWIG_init(interp);
+}
+%}
+
+%typemap(out) time_t {
+ Tcl_SetObjResult(interp, Tcl_NewLongObj((long) $1));
+}
+
+#endif // end of Tcl specific code
+
+
+%nodefaultctor;
+
+#define __attribute__(x)
+
+%ignore sefs_fcfile::fileList() const;
+
+// don't wrap private friend functions
+#define SWIG_FRIENDS
+
+%newobject sefs_fclist::runQuery(sefs_query * query);
+%newobject sefs_entry::toString();
+%newobject sefs_default_file_contexts_get_path();
+
+%include <sefs/fclist.hh>
+%include <sefs/db.hh>
+%include <sefs/entry.hh>
+%include <sefs/fcfile.hh>
+%include <sefs/filesystem.hh>
+%include <sefs/query.hh>
+
+const char *libsefs_get_version (void);
+char *sefs_default_file_contexts_get_path(void);
+
+%inline %{
+ // needed to convert from the results of runQuery() to the entry
+ sefs_entry *sefs_entry_from_void(void *v) {
+ return static_cast<sefs_entry *>(v);
+ }
+%}
+
+%extend sefs_fcfile {
+ const apol_string_vector_t *fileListStrs() const
+ {
+ const apol_vector_t *v = self->fileList();
+ return reinterpret_cast<const apol_string_vector_t*>(v);
+ }
+}
diff --git a/libsefs/swig/tcl/Makefile.am b/libsefs/swig/tcl/Makefile.am
new file mode 100644
index 0000000..7964e52
--- /dev/null
+++ b/libsefs/swig/tcl/Makefile.am
@@ -0,0 +1,37 @@
+wrappedso_DATA = libtsefs.so.@libsefs_version@
+wrappedso_SONAME = @libsefs_tswig_soname@
+short_name = libtsefs.so
+wrappedsodir = $(libdir)/setools/sefs
+
+package_SCRIPTS = pkgIndex.tcl
+packagedir = $(wrappedsodir)
+
+dist_noinst_DATA = $(srcdir)/../sefs.i
+BUILT_SOURCES = sefs_wrap.cc
+
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ -I$(top_builddir) -fpic \
+ -I$(top_srcdir)/libsefs/include
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ @TCL_LIB_SPEC@ \
+ @SEFS_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libqpol/src/libqpol.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libsefs/src/libsefs.so
+
+$(BUILT_SOURCES): $(dist_noinst_DATA) $(DEPENDENCIES)
+ $(SWIG) -c++ $(SWIG_TCL_OPT) -pkgversion @libsefs_version@ -o $@ -I$(top_srcdir)/libsefs/include -I$(top_srcdir)/libapol/include -I$(top_srcdir)/libapol/swig -I$(top_srcdir)/libqpol/swig $<
+
+$(wrappedso_DATA): $(BUILT_SOURCES)
+ $(CXX) -shared -o $@ $^ $(AM_CXXFLAGS) $(CXXFLAGS) $(SWIG_TCL_CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ $(short_name)
+
+$(package_SCRIPTS): $(wrappedso_DATA)
+ echo "pkg_mkIndex . $^" | LD_LIBRARY_PATH=$(top_builddir)/libqpol/src:$(top_builddir)/libapol/src:$(top_builddir)/libsefs/src $(TCLSH_PROG)
+ chmod 644 $@
+ $(mkdir_p) sefs
+ cp $(wrappedso_DATA) $@ sefs
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) $(wrappedso_DATA) $(wrappedso_SONAME) $(short_name) $(package_DATA) sefs/$(wrappedso_DATA) sefs/$(package_SCRIPTS)
+
+CLEANFILES = $(package_SCRIPTS)
diff --git a/libsefs/tests/Makefile.am b/libsefs/tests/Makefile.am
new file mode 100644
index 0000000..50f7af0
--- /dev/null
+++ b/libsefs/tests/Makefile.am
@@ -0,0 +1,17 @@
+TESTS = libsefs-tests
+check_PROGRAMS = libsefs-tests
+
+libsefs_tests_SOURCES = \
+ fcfile-tests.cc fcfile-tests.hh \
+ libsefs-tests.cc
+
+EXTRA_DIST = file_contexts.confed file_contexts.union file_contexts.broken
+
+AM_CXXFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEFS_CFLAGS@ -DSRCDIR="\"$(srcdir)\""
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @SEFS_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @CUNIT_LIB_FLAG@
+
+libsefs_tests_DEPENDENCIES = ../src/libsefs.so \ No newline at end of file
diff --git a/libsefs/tests/attic/fuse_non_mls.c b/libsefs/tests/attic/fuse_non_mls.c
new file mode 100644
index 0000000..30b3df4
--- /dev/null
+++ b/libsefs/tests/attic/fuse_non_mls.c
@@ -0,0 +1,195 @@
+/*
+ * The approach described below does not actually work. Apparantly,
+ * SELinux will assign a context based upond the underlying policy
+ * (typically from a fs_use statement); the operating system will not
+ * invoke this file's fuse_getxattr() function at all. Thus it is
+ * not possible to use FUSE to create a virtual filesystem with
+ * arbitrary file contexts.
+ */
+
+/**
+ * @file
+ *
+ * Use the FUSE (filesystem in userspace) to create a virtual
+ * filesystem for libsefs tests. This particular filesystem will not
+ * contain MLS contexts.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#define FUSE_USE_VERSION 25
+
+#define XATTR_NAME_SELINUX "security.selinux"
+
+#include <apol/bst.h>
+#include <fuse.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/*************** definition of the virtual filesystem ***************/
+
+static apol_bst_t *bst = NULL;
+
+struct fuse_entry
+{
+ const char *path;
+ mode_t mode;
+ const char *context;
+};
+
+static struct fuse_entry fs[] = {
+ {"/", S_IFDIR, "user_r:object_r:system_t"},
+ {"/foo", S_IFREG, "user_r:object_r:system_t"},
+ {NULL, 0, NULL}
+};
+
+static int fuse_comp(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ const struct fuse_entry *f1 = (const struct fuse_entry *)a;
+ const struct fuse_entry *f2 = (const struct fuse_entry *)b;
+ return strcmp(f1->path, f2->path);
+}
+
+/*************** required fuse functions ***************/
+
+static int fuse_getattr(const char *path, struct stat *stbuf)
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ memset(stbuf, 0, sizeof(*stbuf));
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+ if (e->mode == S_IFDIR) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else {
+ stbuf->st_mode = e->mode | 0444;
+ stbuf->st_nlink = 1;
+ }
+ return 0;
+}
+
+static int fuse_getxattr(const char *path, const char *attrib_name, char *buf, size_t buflen)
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+ if (strcmp(attrib_name, XATTR_NAME_SELINUX) != 0) {
+ return -ENOSYS;
+ }
+ strncpy(buf, e->context, buflen);
+ return strlen(e->context) + 1;
+}
+
+static int fuse_open(const char *path, struct fuse_file_info *fi __attribute__ ((unused)))
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+
+ return -EACCES;
+}
+
+static int fuse_read(const char *path, char *buf __attribute__ ((unused)), size_t size, off_t offset __attribute__ ((unused)),
+ struct fuse_file_info *fi __attribute__ ((unused)))
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+ return size;
+}
+
+struct stem_data
+{
+ const char *stem;
+ void *buf;
+ fuse_fill_dir_t filler;
+};
+
+static int fuse_stem_match(void *a, void *data)
+{
+ const struct fuse_entry *e = (const struct fuse_entry *)a;
+ struct stem_data *sd = (struct stem_data *)data;
+
+ size_t e_len = strlen(e->path);
+ size_t stem_len = strlen(sd->stem);
+ if (e_len <= stem_len) {
+ /* entry's path is too longer than the requested path */
+ return 0;
+ }
+ if (strncmp(e->path, sd->stem, stem_len) != 0) {
+ /* stem is not the beginning of entry's path */
+ return 0;
+ }
+ const char *file = e->path + 1;
+ if (strchr(file, '/') != NULL) {
+ /* member of a subdirectory of stem */
+ return 0;
+ }
+ return -sd->filler(sd->buf, file, NULL, 0);
+}
+
+static int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset __attribute__ ((unused)), struct fuse_file_info *fi __attribute__ ((unused)))
+{
+ struct fuse_entry key = { path, 0, NULL };
+ struct fuse_entry *e;
+ if (apol_bst_get_element(bst, &key, NULL, (void **)&e) < 0) {
+ return -ENOENT;
+ }
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ struct stem_data sd = { path, buf, filler };
+ return -apol_bst_inorder_map(bst, fuse_stem_match, &sd);
+}
+
+int main(int argc, char *argv[])
+{
+ if ((bst = apol_bst_create(fuse_comp, NULL)) < 0) {
+ return 1;
+ }
+ for (size_t i = 0; fs[i].path != NULL; i++) {
+ if (apol_bst_insert(bst, &fs[i], NULL) < 0) {
+ return 1;
+ }
+ }
+
+ struct fuse_operations non_mls_oper = {
+ .getattr = fuse_getattr,
+ .getxattr = fuse_getxattr,
+ .open = fuse_open,
+ .read = fuse_read,
+ .readdir = fuse_readdir,
+ };
+ return fuse_main(argc, argv, &non_mls_oper);
+}
diff --git a/libsefs/tests/attic/launch-libsefs-tests.sh b/libsefs/tests/attic/launch-libsefs-tests.sh
new file mode 100755
index 0000000..94bc873
--- /dev/null
+++ b/libsefs/tests/attic/launch-libsefs-tests.sh
@@ -0,0 +1,12 @@
+# Mount the virtual filesystems, execute the the real test, then
+# unmount those filesystems.
+
+mkdir -p non-mls
+mkdir -p mls
+./fuse_non_mls non-mls
+./libsefs-tests
+result=$?
+fusermount -u non-mls
+rmdir non-mls
+rmdir mls
+exit ${result}
diff --git a/libsefs/tests/fcfile-tests.cc b/libsefs/tests/fcfile-tests.cc
new file mode 100644
index 0000000..153d111
--- /dev/null
+++ b/libsefs/tests/fcfile-tests.cc
@@ -0,0 +1,333 @@
+/**
+ * @file
+ *
+ * Test the fcfile parsing and querying, introduced in SETools 3.3.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <sefs/fcfile.hh>
+#include <string.h>
+
+#define FC_CONFED SRCDIR "/file_contexts.confed"
+#define FC_UNION SRCDIR "/file_contexts.union"
+#define FC_BROKEN SRCDIR "/file_contexts.broken"
+
+static void fcfile_open()
+{
+ sefs_fcfile *fc1 = NULL, *fc2 = NULL;
+ bool open_failed = false;
+ apol_vector_t *files = apol_vector_create(NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(files);
+ apol_vector_append(files, (void *)(FC_CONFED));
+
+ try
+ {
+ fc1 = new sefs_fcfile(NULL, NULL);
+ fc2 = new sefs_fcfile(files, NULL, NULL);
+ }
+ catch(...)
+ {
+ CU_ASSERT(0);
+ open_failed = true;
+ }
+ delete fc1;
+ fc1 = NULL;
+ if (open_failed)
+ {
+ delete fc2;
+ return;
+ }
+
+ apol_vector_destroy(&files);
+ files = apol_vector_create(NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(files);
+ apol_vector_append(files, (void *)(FC_UNION));
+ apol_vector_append(files, (void *)(FC_BROKEN));
+ size_t num_matches = 0;
+ try
+ {
+ num_matches = fc2->appendFileList(files);
+ CU_ASSERT(num_matches == 1);
+ }
+ catch(...)
+ {
+ CU_ASSERT(0);
+ }
+ apol_vector_destroy(&files);
+
+ CU_ASSERT_FALSE(fc2->isMLS());
+ CU_ASSERT(fc2->fclist_type() == SEFS_FCLIST_TYPE_FCFILE);
+
+ const apol_vector_t *fileList = fc2->fileList();
+ CU_ASSERT(apol_vector_get_size(fileList) == 2);
+ if (apol_vector_get_size(fileList) >= 1)
+ {
+ CU_ASSERT_STRING_EQUAL(apol_vector_get_element(fileList, 0), FC_CONFED);
+ }
+ if (apol_vector_get_size(fileList) >= 2)
+ {
+ CU_ASSERT_STRING_EQUAL(apol_vector_get_element(fileList, 1), FC_UNION);
+ }
+
+ delete fc2;
+}
+
+int fcfile_query_map_user_lee(sefs_fclist * fc __attribute__ ((unused)), const sefs_entry * e, void *data)
+{
+ const apol_context_t *con = e->context();
+ if (strcmp(apol_context_get_user(con), "lee_u") == 0)
+ {
+ CU_ASSERT(1);
+ int *num_matches = static_cast < int *>(data);
+ (*num_matches)++;
+ CU_ASSERT_STRING_EQUAL(e->origin(), FC_CONFED);
+ return 0;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ return -1;
+ }
+}
+
+static void fcfile_query()
+{
+ sefs_fcfile *fc = NULL;
+ int retval;
+ try
+ {
+ fc = new sefs_fcfile(FC_CONFED, NULL, NULL);
+ retval = fc->appendFile(FC_UNION);
+ CU_ASSERT(retval == 0);
+ }
+ catch(...)
+ {
+ CU_ASSERT_FATAL(0);
+ }
+
+ sefs_query *q = new sefs_query();
+ q->user("lee_u");
+ int num_matches = 0;
+ retval = fc->runQueryMap(q, fcfile_query_map_user_lee, &num_matches);
+ CU_ASSERT(retval == 0 && num_matches == 2);
+
+ q->user(NULL);
+ q->role("location_r");
+ apol_vector_t *entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 11);
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ const apol_context_t *con = e->context();
+ const char *t = apol_context_get_type(con);
+ CU_ASSERT(strcmp(t, "city_t") == 0 || strcmp(t, "state_t") == 0 || strcmp(t, "terrain_t") == 0);
+ char *s = e->toString();
+ printf("%s\n", s);
+ CU_ASSERT_PTR_NOT_NULL(s);
+ free(s);
+ }
+ apol_vector_destroy(&entries);
+
+ q->type("city_t", false);
+ entries = fc->runQuery(q); // both role and type are set
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 3);
+ bool found_boonsboro = false, found_sharpsburg = false, found_harpers_ferry = false;
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ const char *p = e->path();
+ if (strcmp(p, "/antietam/boonsboro") == 0)
+ {
+ found_boonsboro = true;
+ }
+ else if (strcmp(p, "/sharpsburg") == 0)
+ {
+ found_sharpsburg = true;
+ }
+ else if (strcmp(p, "/harpers_ferry") == 0)
+ {
+ found_harpers_ferry = true;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_boonsboro && found_sharpsburg && found_harpers_ferry);
+ apol_vector_destroy(&entries);
+
+ q->role(NULL);
+ q->type(NULL, false);
+ q->objectClass(QPOL_CLASS_LNK_FILE);
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 6); // 2 link files, 4 entries without explicit class
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ uint32_t objclass = e->objectClass();
+ CU_ASSERT(objclass == QPOL_CLASS_LNK_FILE || objclass == QPOL_CLASS_ALL);
+ }
+ apol_vector_destroy(&entries);
+
+ q->objectClass("file");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 8); // 4 files, 4 entries without explicit class
+ size_t num_files = 0, num_alls = 0;
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ uint32_t objclass = e->objectClass();
+ if (objclass == QPOL_CLASS_FILE)
+ {
+ num_files++;
+ }
+ else if (objclass == QPOL_CLASS_ALL)
+ {
+ num_alls++;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(num_files == 4 && num_alls == 4);
+ apol_vector_destroy(&entries);
+
+ // setting any of these should have no effect upon results
+ q->range("s0", APOL_QUERY_EXACT);
+ q->inode(42);
+ q->dev("first_maryland_campaign");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 8);
+ num_files = 0, num_alls = 0;
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ uint32_t objclass = e->objectClass();
+ if (objclass == QPOL_CLASS_FILE)
+ {
+ num_files++;
+ }
+ else if (objclass == QPOL_CLASS_ALL)
+ {
+ num_alls++;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(num_files == 4 && num_alls == 4);
+ apol_vector_destroy(&entries);
+
+ q->objectClass((const char *)NULL);
+ q->path("/sharpsburg/main_street");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 0);
+ apol_vector_destroy(&entries);
+
+ q->path("/sharpsburg/east_woods/hooker");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 2);
+ bool found_east_woods = false, found_hooker = false;
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ const char *path = e->path();
+ if (strcmp(path, "/sharpsburg/east_woods(/.*)?") == 0)
+ {
+ found_east_woods = true;
+ }
+ else if (strcmp(path, "/sharpsburg/east_woods/hooker") == 0)
+ {
+ found_hooker = true;
+ }
+ else
+ {
+ CU_ASSERT(0);
+ }
+ }
+ CU_ASSERT(found_east_woods && found_hooker);
+ apol_vector_destroy(&entries);
+
+ q->path(NULL);
+ q->user("hill");
+ q->regex(true);
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 2);
+ apol_vector_destroy(&entries);
+
+ q->user("hilly");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 0);
+ apol_vector_destroy(&entries);
+
+ q->user(NULL);
+ q->path("/sharpsburg/dunker");
+ entries = fc->runQuery(q);
+ CU_ASSERT_PTR_NOT_NULL(entries);
+ CU_ASSERT(apol_vector_get_size(entries) == 1);
+ for (size_t i = 0; i < apol_vector_get_size(entries); i++)
+ {
+ sefs_entry *e = static_cast < sefs_entry * >(apol_vector_get_element(entries, i));
+ const apol_context_t *con = e->context();
+ CU_ASSERT_PTR_NOT_NULL(con);
+ CU_ASSERT_STRING_EQUAL(apol_context_get_user(con), "");
+ CU_ASSERT_STRING_EQUAL(apol_context_get_role(con), "");
+ CU_ASSERT_STRING_EQUAL(apol_context_get_type(con), "");
+ }
+ apol_vector_destroy(&entries);
+
+ delete q;
+ delete fc;
+}
+
+// alse test: load MLS in non-MLS; load non-MLS in MLS; MLS in MLS
+
+CU_TestInfo fcfile_tests[] = {
+ {"fcfile opening files", fcfile_open}
+ ,
+ {"fcfile queries", fcfile_query}
+ ,
+ CU_TEST_INFO_NULL
+};
+
+int fcfile_init()
+{
+ return 0;
+}
+
+int fcfile_cleanup()
+{
+ return 0;
+}
diff --git a/libsefs/tests/fcfile-tests.hh b/libsefs/tests/fcfile-tests.hh
new file mode 100644
index 0000000..e3aa8ed
--- /dev/null
+++ b/libsefs/tests/fcfile-tests.hh
@@ -0,0 +1,35 @@
+/**
+ * @file
+ *
+ * Declarations for libsefs file_contexts file (fcfile) tests.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FCFILE_TESTS_H
+#define FCFILE_TESTS_H
+
+#include <CUnit/CUnit.h>
+
+extern CU_TestInfo fcfile_tests[];
+extern int fcfile_init();
+extern int fcfile_cleanup();
+
+#endif
diff --git a/libsefs/tests/file_contexts.broken b/libsefs/tests/file_contexts.broken
new file mode 100644
index 0000000..b74dd72
--- /dev/null
+++ b/libsefs/tests/file_contexts.broken
@@ -0,0 +1,3 @@
+# An intentionally broken file_contexts file.
+
+/antietam/burnside -? burnside_u:union_r:general_t
diff --git a/libsefs/tests/file_contexts.confed b/libsefs/tests/file_contexts.confed
new file mode 100644
index 0000000..01d48a1
--- /dev/null
+++ b/libsefs/tests/file_contexts.confed
@@ -0,0 +1,24 @@
+# Confederate placement as of morning of September 17, 1861.
+
+/ -d maryland_u:location_r:state_t
+/sharpsburg -d sharpsburg_u:location_r:city_t
+/sharpsburg/lee -l lee_u:confed_r:general_t
+/sharpsburg/nicodemus -d sharpsburg_u:location_r:terrain_t
+/sharpsburg/nicodemus/jackson(/.*)? -- jackson_u:confed_r:infantry_t
+/sharpsburg/nicodemus/stuart -- stuart_u:confed_r:artillery_t
+/sharpsburg/west_woods -d sharpsburg_u:location_r:terrain_t
+/sharpsburg/west_woods/jackson(/.*)? -c jackson_u:confed_r:infantry_t
+/sharpsburg/west_woods/jones -c jones_u:confed_r:infantry_t
+/sharpsburg/west_woods/lee -c lee_u:confed_r:artillery_t
+/sharpsburg/dunker <<none>>
+/sharpsburg/hill -p hill_u:confed_r:infantry_t
+/antietam -d sharpsburg_u:location_r:terrain_t
+/antietam/cemetary_hill -d sharpsburg_u:location_r:terrain_t
+/antietam/cemetary_hill/artillery_ridge -d sharpsburg_u:location_r:terrain_t
+/antietam/cemetary_hill/artillery_ridge/anderson(/.*)? -s anderson_u:confed_r:infantry_t
+/antietam/cemetary_hill/artillery_ridge/jones(/.*)? -s jones_u:confed_r:infantry_t
+/antietam/cemetary_hill/artillery_ridge/walker(/.*)? -s walker_u:confed_r:infantry_t
+/antietam/cemetary_hill/artillery_ridge/toombs(/.*)? -s toombs_u:confed_r:artillery_t
+
+/harpers_ferry virginia_u:location_r:city_t
+/harpers_ferry/.* hill_u:confed_r:infantry_t
diff --git a/libsefs/tests/file_contexts.union b/libsefs/tests/file_contexts.union
new file mode 100644
index 0000000..4e93e9f
--- /dev/null
+++ b/libsefs/tests/file_contexts.union
@@ -0,0 +1,16 @@
+# Union forces as of morning of September 17, 1861.
+
+/antietam/mcclellan -l mcclellan_u:union_r:general_t
+/antietam/boonsboro -- maryland_u:location_r:city_t
+/sharpsburg/north_woods -d sharpsburg_u:location_r:terrain_t
+/sharpsburg/north_woods/doubleday -b doubleday_u:union_r:infantry_t
+/sharpsburg/north_woods/meade.* -b meade_u:union_r:artillery_t
+/sharpsburg/north_woods/ricketts -b ricketts_u:union_r:cavalry_t
+/sharpsburg/east_woods(/.*)? sharpsburg_u:location_r:terrain_t
+/sharpsburg/east_woods/hooker -p hooker_u:union_r:infantry_t
+/sharpsburg/east_woods/mansfield -p mansfield_u:union_r:artillery_t
+/sharpsburg/east_woods/sedgwick -p sedgwick_u:union_r:infantry_t
+/sharpsburg/corn_field -- <<none>>
+/antietam/middle_bridge -b <<none>>
+/antietam/lower_bridge -b <<none>>
+/antietam/snavely -p <<none>>
diff --git a/libsefs/tests/libsefs-tests.cc b/libsefs/tests/libsefs-tests.cc
new file mode 100644
index 0000000..9104d41
--- /dev/null
+++ b/libsefs/tests/libsefs-tests.cc
@@ -0,0 +1,52 @@
+/**
+ * @file
+ *
+ * CUnit testing framework for libsefs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <CUnit/CUnit.h>
+#include <CUnit/Basic.h>
+
+#include "fcfile-tests.hh"
+
+int main(void)
+{
+ if (CU_initialize_registry() != CUE_SUCCESS)
+ {
+ return CU_get_error();
+ }
+
+ CU_SuiteInfo suites[] = {
+ {"fcfile", fcfile_init, fcfile_cleanup, fcfile_tests}
+ ,
+ CU_SUITE_INFO_NULL
+ };
+
+ CU_register_suites(suites);
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ unsigned int num_failures = CU_get_number_of_failure_records();
+ CU_cleanup_registry();
+ return (int)num_failures;
+}
diff --git a/m4/ac_check_classpath.m4 b/m4/ac_check_classpath.m4
new file mode 100644
index 0000000..bfbdda4
--- /dev/null
+++ b/m4/ac_check_classpath.m4
@@ -0,0 +1,63 @@
+##### http://autoconf-archive.cryp.to/ac_check_classpath.html
+#
+# SYNOPSIS
+#
+# AC_CHECK_CLASSPATH
+#
+# DESCRIPTION
+#
+# AC_CHECK_CLASSPATH just displays the CLASSPATH, for the edification
+# of the user.
+#
+# Note: This is part of the set of autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download the whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission. The general documentation, as well as the sample
+# configure.in, is included in the AC_PROG_JAVA macro.
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Stephane Bortzmeyer <bortzmeyer@pasteur.fr>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_CHECK_CLASSPATH],[
+if test "x$CLASSPATH" = x; then
+ echo "You have no CLASSPATH, I hope it is good"
+else
+ echo "You have CLASSPATH $CLASSPATH, hope it is correct"
+fi
+])
diff --git a/m4/ac_java_options.m4 b/m4/ac_java_options.m4
new file mode 100644
index 0000000..290406e
--- /dev/null
+++ b/m4/ac_java_options.m4
@@ -0,0 +1,46 @@
+##### http://autoconf-archive.cryp.to/ac_java_options.html
+#
+# SYNOPSIS
+#
+# AC_JAVA_OPTIONS
+#
+# DESCRIPTION
+#
+# AC_JAVA_OPTIONS adds configure command line options used for Java
+# m4 macros. This Macro is optional.
+#
+# Note: This is part of the set of autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download the whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission. The general documentation, as well as the sample
+# configure.in, is included in the AC_PROG_JAVA macro.
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Devin Weaver <ktohg@tritarget.com>
+#
+# Copying and distribution of this file, with or without
+# modification, are permitted in any medium without royalty provided
+# the copyright notice and this notice are preserved.
+
+AC_DEFUN([AC_JAVA_OPTIONS],[
+AC_ARG_WITH(java-prefix,
+ [ --with-java-prefix=PFX prefix where Java runtime is installed (optional)])
+AC_ARG_WITH(javac-flags,
+ [ --with-javac-flags=FLAGS flags to pass to the Java compiler (optional)])
+AC_ARG_WITH(java-flags,
+ [ --with-java-flags=FLAGS flags to pass to the Java VM (optional)])
+JAVAPREFIX=$with_java_prefix
+JAVACFLAGS=$with_javac_flags
+JAVAFLAGS=$with_java_flags
+AC_SUBST(JAVAPREFIX)dnl
+AC_SUBST(JAVACFLAGS)dnl
+AC_SUBST(JAVAFLAGS)dnl
+AC_SUBST(JAVA)dnl
+AC_SUBST(JAVAC)dnl
+])
diff --git a/m4/ac_pkg_swig.m4 b/m4/ac_pkg_swig.m4
new file mode 100644
index 0000000..040eba8
--- /dev/null
+++ b/m4/ac_pkg_swig.m4
@@ -0,0 +1,125 @@
+##### http://autoconf-archive.cryp.to/ac_pkg_swig.html
+#
+# SYNOPSIS
+#
+# AC_PROG_SWIG([major.minor.micro])
+#
+# DESCRIPTION
+#
+# This macro searches for a SWIG installation on your system. If
+# found you should call SWIG via $(SWIG). You can use the optional
+# first argument to check if the version of the available SWIG is
+# greater than or equal to the value of the argument. It should have
+# the format: N[.N[.N]] (N is a number between 0 and 999. Only the
+# first N is mandatory.)
+#
+# If the version argument is given (e.g. 1.3.17), AC_PROG_SWIG checks
+# that the swig package is this version number or higher.
+#
+# In configure.in, use as:
+#
+# AC_PROG_SWIG(1.3.17)
+# SWIG_ENABLE_CXX
+# SWIG_MULTI_MODULE_SUPPORT
+# SWIG_PYTHON
+#
+# LAST MODIFICATION
+#
+# 2006-10-22
+#
+# COPYLEFT
+#
+# Copyright (c) 2006 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2006 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+# Copyright (c) 2006 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2006 Andrew Collier <colliera@ukzn.ac.za>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_PROG_SWIG],[
+ AC_PATH_PROG([SWIG],[swig])
+ if test -z "$SWIG" ; then
+ AC_MSG_WARN([cannot find 'swig' program. You should look at http://www.swig.org])
+ SWIG='echo "Error: SWIG is not installed. You should look at http://www.swig.org" ; false'
+ elif test -n "$1" ; then
+ AC_MSG_CHECKING([for SWIG version])
+ [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
+ AC_MSG_RESULT([$swig_version])
+ if test -n "$swig_version" ; then
+ # Calculate the required version number components
+ [required=$1]
+ [required_major=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_major" ; then
+ [required_major=0]
+ fi
+ [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+ [required_minor=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_minor" ; then
+ [required_minor=0]
+ fi
+ [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+ [required_patch=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_patch" ; then
+ [required_patch=0]
+ fi
+ # Calculate the available version number components
+ [available=$swig_version]
+ [available_major=`echo $available | sed 's/[^0-9].*//'`]
+ if test -z "$available_major" ; then
+ [available_major=0]
+ fi
+ [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+ [available_minor=`echo $available | sed 's/[^0-9].*//'`]
+ if test -z "$available_minor" ; then
+ [available_minor=0]
+ fi
+ [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+ [available_patch=`echo $available | sed 's/[^0-9].*//'`]
+ if test -z "$available_patch" ; then
+ [available_patch=0]
+ fi
+ if test $available_major -ne $required_major \
+ -o $available_minor -ne $required_minor \
+ -o $available_patch -lt $required_patch ; then
+ AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org])
+ SWIG='echo "Error: SWIG version >= $1 is required. You have '"$swig_version"'. You should look at http://www.swig.org" ; false'
+ else
+ AC_MSG_NOTICE([SWIG executable is '$SWIG'])
+ SWIG_LIB=`$SWIG -swiglib`
+ AC_MSG_NOTICE([SWIG library directory is '$SWIG_LIB'])
+ fi
+ else
+ AC_MSG_WARN([cannot determine SWIG version])
+ SWIG='echo "Error: Cannot determine SWIG version. You should look at http://www.swig.org" ; false'
+ fi
+ fi
+ AC_SUBST([SWIG_LIB])
+])
diff --git a/m4/ac_prog_jar.m4 b/m4/ac_prog_jar.m4
new file mode 100644
index 0000000..18d453f
--- /dev/null
+++ b/m4/ac_prog_jar.m4
@@ -0,0 +1,52 @@
+##### http://autoconf-archive.cryp.to/ac_prog_jar.html
+#
+# SYNOPSIS
+#
+# AC_PROG_JAR
+#
+# DESCRIPTION
+#
+# AC_PROG_JAR tests for an existing jar program. It uses the
+# environment variable JAR then tests in sequence various common jar
+# programs.
+#
+# If you want to force a specific compiler:
+#
+# - at the configure.in level, set JAR=yourcompiler before calling
+# AC_PROG_JAR
+#
+# - at the configure level, setenv JAR
+#
+# You can use the JAR variable in your Makefile.in, with @JAR@.
+#
+# Note: This macro depends on the autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download that whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission.
+#
+# The general documentation of those macros, as well as the sample
+# configure.in, is included in the AC_PROG_JAVA macro.
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Egon Willighagen <e.willighagen@science.ru.nl>
+#
+# Copying and distribution of this file, with or without
+# modification, are permitted in any medium without royalty provided
+# the copyright notice and this notice are preserved.
+
+AC_DEFUN([AC_PROG_JAR],[
+AC_REQUIRE([AC_EXEEXT])dnl
+if test "x$JAVAPREFIX" = x; then
+ test "x$JAR" = x && AC_CHECK_PROGS(JAR, jar$EXEEXT)
+else
+ test "x$JAR" = x && AC_CHECK_PROGS(JAR, jar, $JAVAPREFIX)
+fi
+test "x$JAR" = x && AC_MSG_ERROR([no acceptable jar program found in \$PATH])
+AC_PROVIDE([$0])dnl
+])
diff --git a/m4/ac_prog_java.m4 b/m4/ac_prog_java.m4
new file mode 100644
index 0000000..6c0abf3
--- /dev/null
+++ b/m4/ac_prog_java.m4
@@ -0,0 +1,122 @@
+##### http://autoconf-archive.cryp.to/ac_prog_java.html
+#
+# SYNOPSIS
+#
+# AC_PROG_JAVA
+#
+# DESCRIPTION
+#
+# Here is a summary of the main macros:
+#
+# AC_PROG_JAVAC: finds a Java compiler.
+#
+# AC_PROG_JAVA: finds a Java virtual machine.
+#
+# AC_CHECK_CLASS: finds if we have the given class (beware of
+# CLASSPATH!).
+#
+# AC_CHECK_RQRD_CLASS: finds if we have the given class and stops
+# otherwise.
+#
+# AC_TRY_COMPILE_JAVA: attempt to compile user given source.
+#
+# AC_TRY_RUN_JAVA: attempt to compile and run user given source.
+#
+# AC_JAVA_OPTIONS: adds Java configure options.
+#
+# AC_PROG_JAVA tests an existing Java virtual machine. It uses the
+# environment variable JAVA then tests in sequence various common
+# Java virtual machines. For political reasons, it starts with the
+# free ones. You *must* call [AC_PROG_JAVAC] before.
+#
+# If you want to force a specific VM:
+#
+# - at the configure.in level, set JAVA=yourvm before calling
+# AC_PROG_JAVA
+#
+# (but after AC_INIT)
+#
+# - at the configure level, setenv JAVA
+#
+# You can use the JAVA variable in your Makefile.in, with @JAVA@.
+#
+# *Warning*: its success or failure can depend on a proper setting of
+# the CLASSPATH env. variable.
+#
+# TODO: allow to exclude virtual machines (rationale: most Java
+# programs cannot run with some VM like kaffe).
+#
+# Note: This is part of the set of autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download the whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission.
+#
+# A Web page, with a link to the latest CVS snapshot is at
+# <http://www.internatif.org/bortzmeyer/autoconf-Java/>.
+#
+# This is a sample configure.in Process this file with autoconf to
+# produce a configure script.
+#
+# AC_INIT(UnTag.java)
+#
+# dnl Checks for programs.
+# AC_CHECK_CLASSPATH
+# AC_PROG_JAVAC
+# AC_PROG_JAVA
+#
+# dnl Checks for classes
+# AC_CHECK_RQRD_CLASS(org.xml.sax.Parser)
+# AC_CHECK_RQRD_CLASS(com.jclark.xml.sax.Driver)
+#
+# AC_OUTPUT(Makefile)
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Stephane Bortzmeyer <bortzmeyer@pasteur.fr>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_PROG_JAVA],[
+AC_REQUIRE([AC_EXEEXT])dnl
+if test x$JAVAPREFIX = x; then
+ test x$JAVA = x && AC_CHECK_PROGS(JAVA, kaffe$EXEEXT java$EXEEXT)
+else
+ test x$JAVA = x && AC_CHECK_PROGS(JAVA, kaffe$EXEEXT java$EXEEXT, $JAVAPREFIX)
+fi
+test x$JAVA = x && AC_MSG_ERROR([no acceptable Java virtual machine found in \$PATH])
+AC_PROG_JAVA_WORKS
+AC_PROVIDE([$0])dnl
+])
diff --git a/m4/ac_prog_java_works.m4 b/m4/ac_prog_java_works.m4
new file mode 100644
index 0000000..aa9c2ef
--- /dev/null
+++ b/m4/ac_prog_java_works.m4
@@ -0,0 +1,137 @@
+##### http://autoconf-archive.cryp.to/ac_prog_java_works.html
+#
+# SYNOPSIS
+#
+# AC_PROG_JAVA_WORKS
+#
+# DESCRIPTION
+#
+# Internal use ONLY.
+#
+# Note: This is part of the set of autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download the whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission. The general documentation, as well as the sample
+# configure.in, is included in the AC_PROG_JAVA macro.
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Stephane Bortzmeyer <bortzmeyer@pasteur.fr>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_PROG_JAVA_WORKS], [
+AC_CHECK_PROG(uudecode, uudecode$EXEEXT, yes)
+if test x$uudecode = xyes; then
+AC_CACHE_CHECK([if uudecode can decode base 64 file], ac_cv_prog_uudecode_base64, [
+dnl /**
+dnl * Test.java: used to test if java compiler works.
+dnl */
+dnl public class Test
+dnl {
+dnl
+dnl public static void
+dnl main( String[] argv )
+dnl {
+dnl System.exit (0);
+dnl }
+dnl
+dnl }
+cat << \EOF > Test.uue
+begin-base64 644 Test.class
+yv66vgADAC0AFQcAAgEABFRlc3QHAAQBABBqYXZhL2xhbmcvT2JqZWN0AQAE
+bWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARDb2RlAQAPTGluZU51
+bWJlclRhYmxlDAAKAAsBAARleGl0AQAEKEkpVgoADQAJBwAOAQAQamF2YS9s
+YW5nL1N5c3RlbQEABjxpbml0PgEAAygpVgwADwAQCgADABEBAApTb3VyY2VG
+aWxlAQAJVGVzdC5qYXZhACEAAQADAAAAAAACAAkABQAGAAEABwAAACEAAQAB
+AAAABQO4AAyxAAAAAQAIAAAACgACAAAACgAEAAsAAQAPABAAAQAHAAAAIQAB
+AAEAAAAFKrcAErEAAAABAAgAAAAKAAIAAAAEAAQABAABABMAAAACABQ=
+====
+EOF
+if uudecode$EXEEXT Test.uue; then
+ ac_cv_prog_uudecode_base64=yes
+else
+ echo "configure: __oline__: uudecode had trouble decoding base 64 file 'Test.uue'" >&AC_FD_CC
+ echo "configure: failed file was:" >&AC_FD_CC
+ cat Test.uue >&AC_FD_CC
+ ac_cv_prog_uudecode_base64=no
+fi
+rm -f Test.uue])
+fi
+if test x$ac_cv_prog_uudecode_base64 != xyes; then
+ rm -f Test.class
+ AC_MSG_WARN([I have to compile Test.class from scratch])
+ if test x$ac_cv_prog_javac_works = xno; then
+ AC_MSG_ERROR([Cannot compile java source. $JAVAC does not work properly])
+ fi
+ if test x$ac_cv_prog_javac_works = x; then
+ AC_PROG_JAVAC
+ fi
+fi
+AC_CACHE_CHECK(if $JAVA works, ac_cv_prog_java_works, [
+JAVA_TEST=Test.java
+CLASS_TEST=Test.class
+TEST=Test
+changequote(, )dnl
+cat << \EOF > $JAVA_TEST
+/* [#]line __oline__ "configure" */
+public class Test {
+public static void main (String args[]) {
+ System.exit (0);
+} }
+EOF
+changequote([, ])dnl
+if test x$ac_cv_prog_uudecode_base64 != xyes; then
+ if AC_TRY_COMMAND($JAVAC $JAVACFLAGS $JAVA_TEST) && test -s $CLASS_TEST; then
+ :
+ else
+ echo "configure: failed program was:" >&AC_FD_CC
+ cat $JAVA_TEST >&AC_FD_CC
+ AC_MSG_ERROR(The Java compiler $JAVAC failed (see config.log, check the CLASSPATH?))
+ fi
+fi
+if AC_TRY_COMMAND($JAVA $JAVAFLAGS $TEST) >/dev/null 2>&1; then
+ ac_cv_prog_java_works=yes
+else
+ echo "configure: failed program was:" >&AC_FD_CC
+ cat $JAVA_TEST >&AC_FD_CC
+ AC_MSG_ERROR(The Java VM $JAVA failed (see config.log, check the CLASSPATH?))
+fi
+rm -fr $JAVA_TEST $CLASS_TEST Test.uue
+])
+AC_PROVIDE([$0])dnl
+]
+)
diff --git a/m4/ac_prog_javac.m4 b/m4/ac_prog_javac.m4
new file mode 100644
index 0000000..87c5723
--- /dev/null
+++ b/m4/ac_prog_javac.m4
@@ -0,0 +1,84 @@
+##### http://autoconf-archive.cryp.to/ac_prog_javac.html
+#
+# SYNOPSIS
+#
+# AC_PROG_JAVAC
+#
+# DESCRIPTION
+#
+# AC_PROG_JAVAC tests an existing Java compiler. It uses the
+# environment variable JAVAC then tests in sequence various common
+# Java compilers. For political reasons, it starts with the free
+# ones.
+#
+# If you want to force a specific compiler:
+#
+# - at the configure.in level, set JAVAC=yourcompiler before calling
+# AC_PROG_JAVAC
+#
+# - at the configure level, setenv JAVAC
+#
+# You can use the JAVAC variable in your Makefile.in, with @JAVAC@.
+#
+# *Warning*: its success or failure can depend on a proper setting of
+# the CLASSPATH env. variable.
+#
+# TODO: allow to exclude compilers (rationale: most Java programs
+# cannot compile with some compilers like guavac).
+#
+# Note: This is part of the set of autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download the whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission. The general documentation, as well as the sample
+# configure.in, is included in the AC_PROG_JAVA macro.
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Stephane Bortzmeyer <bortzmeyer@pasteur.fr>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_PROG_JAVAC],[
+AC_REQUIRE([AC_EXEEXT])dnl
+if test "x$JAVAPREFIX" = x; then
+ test "x$JAVAC" = x && AC_CHECK_PROGS(JAVAC, "gcj$EXEEXT -C" guavac$EXEEXT jikes$EXEEXT javac$EXEEXT)
+else
+ test "x$JAVAC" = x && AC_CHECK_PROGS(JAVAC, "gcj$EXEEXT -C" guavac$EXEEXT jikes$EXEEXT javac$EXEEXT, $JAVAPREFIX)
+fi
+test "x$JAVAC" = x && AC_MSG_ERROR([no acceptable Java compiler found in \$PATH])
+AC_PROG_JAVAC_WORKS
+AC_PROVIDE([$0])dnl
+])
diff --git a/m4/ac_prog_javac_works.m4 b/m4/ac_prog_javac_works.m4
new file mode 100644
index 0000000..5a16ca7
--- /dev/null
+++ b/m4/ac_prog_javac_works.m4
@@ -0,0 +1,75 @@
+##### http://autoconf-archive.cryp.to/ac_prog_javac_works.html
+#
+# SYNOPSIS
+#
+# AC_PROG_JAVAC_WORKS
+#
+# DESCRIPTION
+#
+# Internal use ONLY.
+#
+# Note: This is part of the set of autoconf M4 macros for Java
+# programs. It is VERY IMPORTANT that you download the whole set,
+# some macros depend on other. Unfortunately, the autoconf archive
+# does not support the concept of set of macros, so I had to break it
+# for submission. The general documentation, as well as the sample
+# configure.in, is included in the AC_PROG_JAVA macro.
+#
+# LAST MODIFICATION
+#
+# 2000-07-19
+#
+# COPYLEFT
+#
+# Copyright (c) 2000 Stephane Bortzmeyer <bortzmeyer@pasteur.fr>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_PROG_JAVAC_WORKS],[
+AC_CACHE_CHECK([if $JAVAC works], ac_cv_prog_javac_works, [
+JAVA_TEST=Test.java
+CLASS_TEST=Test.class
+cat << \EOF > $JAVA_TEST
+/* [#]line __oline__ "configure" */
+public class Test {
+}
+EOF
+if AC_TRY_COMMAND($JAVAC $JAVACFLAGS $JAVA_TEST) >/dev/null 2>&1; then
+ ac_cv_prog_javac_works=yes
+else
+ AC_MSG_ERROR([The Java compiler $JAVAC failed (see config.log, check the CLASSPATH?)])
+ echo "configure: failed program was:" >&AC_FD_CC
+ cat $JAVA_TEST >&AC_FD_CC
+fi
+rm -f $JAVA_TEST $CLASS_TEST
+])
+AC_PROVIDE([$0])dnl
+])
diff --git a/m4/ac_python_devel.m4 b/m4/ac_python_devel.m4
new file mode 100644
index 0000000..8159391
--- /dev/null
+++ b/m4/ac_python_devel.m4
@@ -0,0 +1,269 @@
+##### http://autoconf-archive.cryp.to/ac_python_devel.html
+#
+# SYNOPSIS
+#
+# AC_PYTHON_DEVEL([version])
+#
+# DESCRIPTION
+#
+# Note: Defines as a precious variable "PYTHON_VERSION". Don't
+# override it in your configure.ac.
+#
+# This macro checks for Python and tries to get the include path to
+# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and
+# $(PYTHON_LDFLAGS) output variables. It also exports
+# $(PYTHON_EXTRA_LIBS) and $(PYTHON_EXTRA_LDFLAGS) for embedding
+# Python in your code.
+#
+# You can search for some particular version of Python by passing a
+# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'".
+# Please note that you *have* to pass also an operator along with the
+# version to match, and pay special attention to the single quotes
+# surrounding the version number. Don't use "PYTHON_VERSION" for
+# this: that environment variable is declared as precious and thus
+# reserved for the end-user.
+#
+# This macro should work for all versions of Python >= 2.1.0. As an
+# end user, you can disable the check for the python version by
+# setting the PYTHON_NOVERSIONCHECK environment variable to something
+# else than the empty string.
+#
+# If you need to use this macro for an older Python version, please
+# contact the authors. We're always open for feedback.
+#
+# LAST MODIFICATION
+#
+# 2006-10-22
+#
+# COPYLEFT
+#
+# Copyright (c) 2006 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2006 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+# Copyright (c) 2006 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2006 Andrew Collier <colliera@ukzn.ac.za>
+# Copyright (c) 2006 Matteo Settenvini <matteo@member.fsf.org>
+# Copyright (c) 2006 Horst Knorr <hk_classes@knoda.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([AC_PYTHON_DEVEL],[
+ #
+ # Allow the use of a (user set) custom python version
+ #
+ AC_ARG_VAR([PYTHON_VERSION],[The installed Python
+ version to use, for example '2.3'. This string
+ will be appended to the Python interpreter
+ canonical name.])
+
+ AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
+ if test -z "$PYTHON"; then
+ AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for a version of Python >= 2.1.0
+ #
+ AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
+ ac_supports_python_ver=`$PYTHON -c "import sys, string; \
+ ver = string.split(sys.version)[[0]]; \
+ print ver >= '2.1.0'"`
+ if test "$ac_supports_python_ver" != "True"; then
+ if test -z "$PYTHON_NOVERSIONCHECK"; then
+ AC_MSG_RESULT([no])
+ AC_MSG_FAILURE([
+This version of the AC@&t@_PYTHON_DEVEL macro
+doesn't work properly with versions of Python before
+2.1.0. You may need to re-run configure, setting the
+variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
+PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
+Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
+to something else than an empty string.
+])
+ else
+ AC_MSG_RESULT([skip at user request])
+ fi
+ else
+ AC_MSG_RESULT([yes])
+ fi
+
+ #
+ # if the macro parameter ``version'' is set, honour it
+ #
+ if test -n "$1"; then
+ AC_MSG_CHECKING([for a version of Python $1])
+ ac_supports_python_ver=`$PYTHON -c "import sys, string; \
+ ver = string.split(sys.version)[[0]]; \
+ print ver $1"`
+ if test "$ac_supports_python_ver" = "True"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([this package requires Python $1.
+If you have it installed, but it isn't the default Python
+interpreter in your system path, please pass the PYTHON_VERSION
+variable to configure. See ``configure --help'' for reference.
+])
+ PYTHON_VERSION=""
+ fi
+ fi
+
+ #
+ # Check if you have distutils, else fail
+ #
+ AC_MSG_CHECKING([for the distutils Python package])
+ ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+ if test -z "$ac_distutils_result"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot import Python module "distutils".
+Please check your Python installation. The error was:
+$ac_distutils_result])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for Python include path
+ #
+ AC_MSG_CHECKING([for Python include path])
+ if test -z "$PYTHON_CPPFLAGS"; then
+ python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print distutils.sysconfig.get_python_inc();"`
+ if test -n "${python_path}"; then
+ python_path="-I$python_path"
+ fi
+ PYTHON_CPPFLAGS=$python_path
+ fi
+ AC_MSG_RESULT([$PYTHON_CPPFLAGS])
+ AC_SUBST([PYTHON_CPPFLAGS])
+
+ #
+ # Check for Python library path
+ #
+ AC_MSG_CHECKING([for Python library path])
+ if test -z "$PYTHON_LDFLAGS"; then
+ # (makes two attempts to ensure we've got a version number
+ # from the interpreter)
+ py_version=`$PYTHON -c "from distutils.sysconfig import *; \
+ from string import join; \
+ print join(get_config_vars('VERSION'))"`
+ if test "$py_version" == "[None]"; then
+ if test -n "$PYTHON_VERSION"; then
+ py_version=$PYTHON_VERSION
+ else
+ py_version=`$PYTHON -c "import sys; \
+ print sys.version[[:3]]"`
+ fi
+ fi
+
+ PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \
+ from string import join; \
+ print '-L' + get_python_lib(1,1), \
+ '-lpython';"`$py_version
+ fi
+ AC_MSG_RESULT([$PYTHON_LDFLAGS])
+ AC_SUBST([PYTHON_LDFLAGS])
+
+ #
+ # Check for site packages
+ #
+ AC_MSG_CHECKING([for Python site-packages path])
+ if test -z "$PYTHON_SITE_PKG"; then
+ PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+ print distutils.sysconfig.get_python_lib(1,0);"`
+ fi
+ AC_MSG_RESULT([$PYTHON_SITE_PKG])
+ AC_SUBST([PYTHON_SITE_PKG])
+
+ #
+ # libraries which must be linked in when embedding
+ #
+ AC_MSG_CHECKING(python extra libraries)
+ if test -z "$PYTHON_EXTRA_LIBS"; then
+ PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print conf('LOCALMODLIBS'), conf('LIBS')"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+ AC_SUBST(PYTHON_EXTRA_LIBS)
+
+ #
+ # linking flags needed when embedding
+ #
+ AC_MSG_CHECKING(python extra linking flags)
+ if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+ PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print conf('LINKFORSHARED')"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+ AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+
+ #
+ # final check to see if everything compiles alright
+ #
+ AC_MSG_CHECKING([consistency of all components of python development environment])
+ AC_LANG_PUSH([C])
+ # save current global flags
+ LIBS="$ac_save_LIBS $PYTHON_LDFLAGS"
+ CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+ AC_TRY_LINK([
+ #include <Python.h>
+ ],[
+ Py_Initialize();
+ ],[pythonexists=yes],[pythonexists=no])
+
+ AC_MSG_RESULT([$pythonexists])
+
+ if test ! "$pythonexists" = "yes"; then
+ AC_MSG_ERROR([
+ Could not link test program to Python. Maybe the main Python library has been
+ installed in some non-standard library path. If so, pass it to configure,
+ via the LDFLAGS environment variable.
+ Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
+ ============================================================================
+ ERROR!
+ You probably have to install the development version of the Python package
+ for your distribution. The exact name of this package varies among them.
+ ============================================================================
+ ])
+ PYTHON_VERSION=""
+ fi
+ AC_LANG_POP
+ # turn back to default flags
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBS="$ac_save_LIBS"
+
+ #
+ # all done!
+ #
+])
diff --git a/m4/c.m4 b/m4/c.m4
new file mode 100644
index 0000000..c0ec5df
--- /dev/null
+++ b/m4/c.m4
@@ -0,0 +1,265 @@
+# This file is part of Autoconf. -*- Autoconf -*-
+# Programming languages support.
+# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software
+# Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception, the Free Software Foundation gives unlimited
+# permission to copy, distribute and modify the configure scripts that
+# are the output of Autoconf. You need not follow the terms of the GNU
+# General Public License when using or distributing such scripts, even
+# though portions of the text of Autoconf appear in them. The GNU
+# General Public License (GPL) does govern all other use of the material
+# that constitutes the Autoconf program.
+#
+# Certain portions of the Autoconf source text are designed to be copied
+# (in certain cases, depending on the input) into the output of
+# Autoconf. We call these the "data" portions. The rest of the Autoconf
+# source text consists of comments plus executable code that decides which
+# of the data portions to output in any given case. We call these
+# comments and executable code the "non-data" portions. Autoconf never
+# copies any of the non-data portions into its output.
+#
+# This special exception to the GPL applies to versions of Autoconf
+# released by the Free Software Foundation. When you make and
+# distribute a modified version of Autoconf, you may extend this special
+# exception to the GPL to apply to your modified version as well, *unless*
+# your modified version has the potential to copy into its output some
+# of the text that was the non-data portion of the version that you started
+# with. (In other words, unless your change moves or copies text from
+# the non-data portions to the data portions.) If your modification has
+# such potential, you must delete any notice of this special exception
+# to the GPL from your modified version.
+#
+# Written by David MacKenzie, with help from
+# Akim Demaille, Paul Eggert,
+# Franc,ois Pinard, Karl Berry, Richard Pixley, Ian Lance Taylor,
+# Roland McGrath, Noah Friedman, david d zuhn, and many others.
+
+
+# SETools Note: Fedora Core 5 ships with autoconf 2.59, which
+# unfortunately is missing the macro AC_PROG_CC_C99. Therefore
+# provide a copy of the one from autoconf 2.61.
+
+
+
+## ------------------------------- ##
+## 4. Compilers' characteristics. ##
+## ------------------------------- ##
+
+
+# -------------------------------- #
+# 4b. C compiler characteristics. #
+# -------------------------------- #
+
+# _AC_C_STD_TRY(STANDARD, TEST-PROLOGUE, TEST-BODY, OPTION-LIST,
+# ACTION-IF-AVAILABLE, ACTION-IF-UNAVAILABLE)
+# --------------------------------------------------------------
+# Check whether the C compiler accepts features of STANDARD (e.g `c89', `c99')
+# by trying to compile a program of TEST-PROLOGUE and TEST-BODY. If this fails,
+# try again with each compiler option in the space-separated OPTION-LIST; if one
+# helps, append it to CC. If eventually successful, run ACTION-IF-AVAILABLE,
+# else ACTION-IF-UNAVAILABLE.
+AC_DEFUN([_AC_C_STD_TRY],
+[AC_MSG_CHECKING([for $CC option to accept ISO ]m4_translit($1, [c], [C]))
+AC_CACHE_VAL(ac_cv_prog_cc_$1,
+[ac_cv_prog_cc_$1=no
+ac_save_CC=$CC
+AC_LANG_CONFTEST([AC_LANG_PROGRAM([$2], [$3])])
+for ac_arg in '' $4
+do
+ CC="$ac_save_CC $ac_arg"
+ _AC_COMPILE_IFELSE([], [ac_cv_prog_cc_$1=$ac_arg])
+ test "x$ac_cv_prog_cc_$1" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+])# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_$1" in
+ x)
+ AC_MSG_RESULT([none needed]) ;;
+ xno)
+ AC_MSG_RESULT([unsupported]) ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_$1"
+ AC_MSG_RESULT([$ac_cv_prog_cc_$1]) ;;
+esac
+AS_IF([test "x$ac_cv_prog_cc_$1" != xno], [$5], [$6])
+])# _AC_C_STD_TRY
+
+
+# _AC_PROG_CC_C99 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE])
+# ----------------------------------------------------------------
+# If the C compiler is not in ISO C99 mode by default, try to add an
+# option to output variable CC to make it so. This macro tries
+# various options that select ISO C99 on some system or another. It
+# considers the compiler to be in ISO C99 mode if it handles _Bool,
+# // comments, flexible array members, inline, long long int, mixed
+# code and declarations, named initialization of structs, restrict,
+# va_copy, varargs macros, variable declarations in for loops and
+# variable length arrays.
+AC_DEFUN([_AC_PROG_CC_C99],
+[_AC_C_STD_TRY([c99],
+[[#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+ your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str;
+ int number;
+ float fnumber;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case 's': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case 'd': // int
+ number = va_arg (args_copy, int);
+ break;
+ case 'f': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+}
+]],
+[[
+ // Check bool.
+ _Bool success = false;
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ test_varargs ("s, d' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+ || dynamic_array[ni.number - 1] != 543);
+]],
+dnl Try
+dnl GCC -std=gnu99 (unused restrictive modes: -std=c99 -std=iso9899:1999)
+dnl AIX -qlanglvl=extc99 (unused restrictive mode: -qlanglvl=stdc99)
+dnl Intel ICC -c99
+dnl IRIX -c99
+dnl Solaris (unused because it causes the compiler to assume C99 semantics for
+dnl library functions, and this is invalid before Solaris 10: -xc99)
+dnl Tru64 -c99
+dnl with extended modes being tried first.
+[[-std=gnu99 -c99 -qlanglvl=extc99]], [$1], [$2])[]dnl
+])# _AC_PROG_CC_C99
+
+
+# AC_PROG_CC_C99
+# --------------
+AC_DEFUN([AC_PROG_CC_C99],
+[ AC_REQUIRE([AC_PROG_CC])dnl
+ _AC_PROG_CC_C99
+])
diff --git a/m4/swig_python.m4 b/m4/swig_python.m4
new file mode 100644
index 0000000..0eccd59
--- /dev/null
+++ b/m4/swig_python.m4
@@ -0,0 +1,67 @@
+##### http://autoconf-archive.cryp.to/swig_python.html
+#
+# SYNOPSIS
+#
+# SWIG_PYTHON([use-shadow-classes = {no, yes}])
+#
+# DESCRIPTION
+#
+# Checks for Python and provides the $(SWIG_PYTHON_CPPFLAGS), and
+# $(SWIG_PYTHON_OPT) output variables.
+#
+# $(SWIG_PYTHON_OPT) contains all necessary SWIG options to generate
+# code for Python. Shadow classes are enabled unless the value of the
+# optional first argument is exactly 'no'. If you need multi module
+# support (provided by the SWIG_MULTI_MODULE_SUPPORT macro) use
+# $(SWIG_PYTHON_LIBS) to link against the appropriate library. It
+# contains the SWIG Python runtime library that is needed by the type
+# check system for example.
+#
+# LAST MODIFICATION
+#
+# 2006-10-22
+#
+# COPYLEFT
+#
+# Copyright (c) 2006 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2006 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+# Copyright (c) 2006 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2006 Andrew Collier <colliera@ukzn.ac.za>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# As a special exception, the respective Autoconf Macro's copyright
+# owner gives unlimited permission to copy, distribute and modify the
+# configure scripts that are the output of Autoconf when processing
+# the Macro. You need not follow the terms of the GNU General Public
+# License when using or distributing such scripts, even though
+# portions of the text of the Macro appear in them. The GNU General
+# Public License (GPL) does govern all other use of the material that
+# constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the
+# Autoconf Macro released by the Autoconf Macro Archive. When you
+# make and distribute a modified version of the Autoconf Macro, you
+# may extend this special exception to the GPL to apply to your
+# modified version as well.
+
+AC_DEFUN([SWIG_PYTHON],[
+ AC_REQUIRE([AC_PROG_SWIG])
+ AC_REQUIRE([AC_PYTHON_DEVEL])
+ test "x$1" != "xno" || swig_shadow=" -noproxy"
+ AC_SUBST([SWIG_PYTHON_OPT],[-python$swig_shadow])
+ AC_SUBST([SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS])
+])
diff --git a/m4/tcl.m4 b/m4/tcl.m4
new file mode 100644
index 0000000..20e97ed
--- /dev/null
+++ b/m4/tcl.m4
@@ -0,0 +1,3960 @@
+# tcl.m4 --
+#
+# This file provides a set of autoconf macros to help TEA-enable
+# a Tcl extension.
+#
+# Copyright (c) 1999-2000 Ajuba Solutions.
+# Copyright (c) 2002-2005 ActiveState Corporation.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: tcl.m4,v 1.92 2006/05/26 19:15:38 das Exp $
+
+AC_PREREQ(2.59)
+
+dnl TEA extensions pass this us the version of TEA they think they
+dnl are compatible with (must be set in TEA_INIT below)
+dnl TEA_VERSION="3.5"
+
+# Possible values for key variables defined:
+#
+# TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem')
+# TEA_PLATFORM - windows unix
+#
+
+#------------------------------------------------------------------------
+# TEA_PATH_TCLCONFIG --
+#
+# Locate the tclConfig.sh file and perform a sanity check on
+# the Tcl compile flags
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-tcl=...
+#
+# Defines the following vars:
+# TCL_BIN_DIR Full path to the directory containing
+# the tclConfig.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_TCLCONFIG], [
+ dnl Make sure we are initialized
+ AC_REQUIRE([TEA_INIT])
+ #
+ # Ok, lets find the tcl configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tcl
+ #
+
+ if test x"${no_tcl}" = x ; then
+ # we reset no_tcl in case something fails here
+ no_tcl=true
+ AC_ARG_WITH(tcl,
+ AC_HELP_STRING([--with-tcl],
+ [directory containing tcl configuration (tclConfig.sh)]),
+ with_tclconfig=${withval})
+ AC_MSG_CHECKING([for Tcl configuration])
+ AC_CACHE_VAL(ac_cv_c_tclconfig,[
+
+ # First check to see if --with-tcl was specified.
+ if test x"${with_tclconfig}" != x ; then
+ case ${with_tclconfig} in
+ */tclConfig.sh )
+ if test -f ${with_tclconfig}; then
+ AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself])
+ with_tclconfig=`echo ${with_tclconfig} | sed 's!/tclConfig\.sh$!!'`
+ fi ;;
+ esac
+ if test -f "${with_tclconfig}/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd ${with_tclconfig}; pwd)`
+ else
+ AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh])
+ fi
+ fi
+
+ # then check for a private Tcl installation
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ../tcl \
+ `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tcl \
+ `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tcl \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # on Darwin, check in Framework installation locations
+ if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+ `ls -d /Library/Frameworks 2>/dev/null` \
+ `ls -d /Network/Library/Frameworks 2>/dev/null` \
+ `ls -d /System/Library/Frameworks 2>/dev/null` \
+ ; do
+ if test -f "$i/Tcl.framework/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/Tcl.framework; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # on Windows, check in common installation locations
+ if test "${TEA_PLATFORM}" = "windows" \
+ -a x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 2>/dev/null` \
+ `ls -d /usr/local/lib 2>/dev/null` \
+ `ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/lib64 2>/dev/null` \
+ `ls -d /usr/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tcl \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ TCL_BIN_DIR="# no Tcl configs found"
+ AC_MSG_WARN([Can't find Tcl configuration definitions])
+ exit 0
+ else
+ no_tcl=
+ TCL_BIN_DIR=${ac_cv_c_tclconfig}
+ AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_TKCONFIG --
+#
+# Locate the tkConfig.sh file
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-tk=...
+#
+# Defines the following vars:
+# TK_BIN_DIR Full path to the directory containing
+# the tkConfig.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_TKCONFIG], [
+ #
+ # Ok, lets find the tk configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tk
+ #
+
+ if test x"${no_tk}" = x ; then
+ # we reset no_tk in case something fails here
+ no_tk=true
+ AC_ARG_WITH(tk,
+ AC_HELP_STRING([--with-tk],
+ [directory containing tk configuration (tkConfig.sh)]),
+ with_tkconfig=${withval})
+ AC_MSG_CHECKING([for Tk configuration])
+ AC_CACHE_VAL(ac_cv_c_tkconfig,[
+
+ # First check to see if --with-tkconfig was specified.
+ if test x"${with_tkconfig}" != x ; then
+ case ${with_tkconfig} in
+ */tkConfig.sh )
+ if test -f ${with_tkconfig}; then
+ AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself])
+ with_tkconfig=`echo ${with_tkconfig} | sed 's!/tkConfig\.sh$!!'`
+ fi ;;
+ esac
+ if test -f "${with_tkconfig}/tkConfig.sh" ; then
+ ac_cv_c_tkconfig=`(cd ${with_tkconfig}; pwd)`
+ else
+ AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh])
+ fi
+ fi
+
+ # then check for a private Tk library
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in \
+ ../tk \
+ `ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tk \
+ `ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tk \
+ `ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/unix/tkConfig.sh" ; then
+ ac_cv_c_tkconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # on Darwin, check in Framework installation locations
+ if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+ `ls -d /Library/Frameworks 2>/dev/null` \
+ `ls -d /Network/Library/Frameworks 2>/dev/null` \
+ `ls -d /System/Library/Frameworks 2>/dev/null` \
+ ; do
+ if test -f "$i/Tk.framework/tkConfig.sh" ; then
+ ac_cv_c_tkconfig=`(cd $i/Tk.framework; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 2>/dev/null` \
+ `ls -d /usr/local/lib 2>/dev/null` \
+ `ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/lib64 2>/dev/null` \
+ `ls -d /usr/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tkConfig.sh" ; then
+ ac_cv_c_tkconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # on Windows, check in common installation locations
+ if test "${TEA_PLATFORM}" = "windows" \
+ -a x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tkConfig.sh" ; then
+ ac_cv_c_tkconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tk \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test -f "$i/unix/tkConfig.sh" ; then
+ ac_cv_c_tkconfig=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ TK_BIN_DIR="# no Tk configs found"
+ AC_MSG_WARN([Can't find Tk configuration definitions])
+ exit 0
+ else
+ no_tk=
+ TK_BIN_DIR=${ac_cv_c_tkconfig}
+ AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_TCLCONFIG --
+#
+# Load the tclConfig.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# TCL_BIN_DIR
+#
+# Results:
+#
+# Subst the following vars:
+# TCL_BIN_DIR
+# TCL_SRC_DIR
+# TCL_LIB_FILE
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_TCLCONFIG], [
+ AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh])
+
+ if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then
+ AC_MSG_RESULT([loading])
+ . ${TCL_BIN_DIR}/tclConfig.sh
+ else
+ AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh])
+ fi
+
+ # eval is required to do the TCL_DBGX substitution
+ eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
+ eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
+
+ # If the TCL_BIN_DIR is the build directory (not the install directory),
+ # then set the common variable name to the value of the build variables.
+ # For example, the variable TCL_LIB_SPEC will be set to the value
+ # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC
+ # instead of TCL_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ if test -f ${TCL_BIN_DIR}/Makefile ; then
+ TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC}
+ TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC}
+ TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH}
+ elif test "`uname -s`" = "Darwin"; then
+ # If Tcl was built as a framework, attempt to use the libraries
+ # from the framework at the given location so that linking works
+ # against Tcl.framework installed in an arbitary location.
+ case ${TCL_DEFS} in
+ *TCL_FRAMEWORK*)
+ if test -f ${TCL_BIN_DIR}/${TCL_LIB_FILE}; then
+ for i in "`cd ${TCL_BIN_DIR}; pwd`" \
+ "`cd ${TCL_BIN_DIR}/../..; pwd`"; do
+ if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then
+ TCL_LIB_SPEC="-F`dirname "$i"` -framework ${TCL_LIB_FILE}"
+ break
+ fi
+ done
+ fi
+ if test -f ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}; then
+ TCL_STUB_LIB_SPEC="-L${TCL_BIN_DIR} ${TCL_STUB_LIB_FLAG}"
+ TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"
+ fi
+ ;;
+ esac
+ fi
+
+ # eval is required to do the TCL_DBGX substitution
+ eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
+ eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+ eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\""
+ eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\""
+
+ AC_SUBST(TCL_VERSION)
+ AC_SUBST(TCL_BIN_DIR)
+ AC_SUBST(TCL_SRC_DIR)
+
+ AC_SUBST(TCL_LIB_FILE)
+ AC_SUBST(TCL_LIB_FLAG)
+ AC_SUBST(TCL_LIB_SPEC)
+
+ AC_SUBST(TCL_STUB_LIB_FILE)
+ AC_SUBST(TCL_STUB_LIB_FLAG)
+ AC_SUBST(TCL_STUB_LIB_SPEC)
+
+ AC_SUBST(TCL_LIBS)
+ AC_SUBST(TCL_DEFS)
+ AC_SUBST(TCL_EXTRA_CFLAGS)
+ AC_SUBST(TCL_LD_FLAGS)
+ AC_SUBST(TCL_SHLIB_LD_LIBS)
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_TKCONFIG --
+#
+# Load the tkConfig.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# TK_BIN_DIR
+#
+# Results:
+#
+# Sets the following vars that should be in tkConfig.sh:
+# TK_BIN_DIR
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_TKCONFIG], [
+ AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh])
+
+ if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then
+ AC_MSG_RESULT([loading])
+ . ${TK_BIN_DIR}/tkConfig.sh
+ else
+ AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh])
+ fi
+
+ # eval is required to do the TK_DBGX substitution
+ eval "TK_LIB_FILE=\"${TK_LIB_FILE}\""
+ eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\""
+
+ # If the TK_BIN_DIR is the build directory (not the install directory),
+ # then set the common variable name to the value of the build variables.
+ # For example, the variable TK_LIB_SPEC will be set to the value
+ # of TK_BUILD_LIB_SPEC. An extension should make use of TK_LIB_SPEC
+ # instead of TK_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ if test -f ${TK_BIN_DIR}/Makefile ; then
+ TK_LIB_SPEC=${TK_BUILD_LIB_SPEC}
+ TK_STUB_LIB_SPEC=${TK_BUILD_STUB_LIB_SPEC}
+ TK_STUB_LIB_PATH=${TK_BUILD_STUB_LIB_PATH}
+ elif test "`uname -s`" = "Darwin"; then
+ # If Tk was built as a framework, attempt to use the libraries
+ # from the framework at the given location so that linking works
+ # against Tk.framework installed in an arbitary location.
+ case ${TK_DEFS} in
+ *TK_FRAMEWORK*)
+ if test -f ${TK_BIN_DIR}/${TK_LIB_FILE}; then
+ for i in "`cd ${TK_BIN_DIR}; pwd`" \
+ "`cd ${TK_BIN_DIR}/../..; pwd`"; do
+ if test "`basename "$i"`" = "${TK_LIB_FILE}.framework"; then
+ TK_LIB_SPEC="-F`dirname "$i"` -framework ${TK_LIB_FILE}"
+ break
+ fi
+ done
+ fi
+ if test -f ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}; then
+ TK_STUB_LIB_SPEC="-L${TK_BIN_DIR} ${TK_STUB_LIB_FLAG}"
+ TK_STUB_LIB_PATH="${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"
+ fi
+ ;;
+ esac
+ fi
+
+ # eval is required to do the TK_DBGX substitution
+ eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\""
+ eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\""
+ eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\""
+ eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\""
+
+ # Ensure windowingsystem is defined
+ if test "${TEA_PLATFORM}" = "unix" ; then
+ case ${TK_DEFS} in
+ *MAC_OSX_TK*)
+ AC_DEFINE(MAC_OSX_TK, 1, [Are we building against Mac OS X TkAqua?])
+ TEA_WINDOWINGSYSTEM="aqua"
+ ;;
+ *)
+ TEA_WINDOWINGSYSTEM="x11"
+ ;;
+ esac
+ elif test "${TEA_PLATFORM}" = "windows" ; then
+ TEA_WINDOWINGSYSTEM="win32"
+ fi
+
+ AC_SUBST(TK_VERSION)
+ AC_SUBST(TK_BIN_DIR)
+ AC_SUBST(TK_SRC_DIR)
+
+ AC_SUBST(TK_LIB_FILE)
+ AC_SUBST(TK_LIB_FLAG)
+ AC_SUBST(TK_LIB_SPEC)
+
+ AC_SUBST(TK_STUB_LIB_FILE)
+ AC_SUBST(TK_STUB_LIB_FLAG)
+ AC_SUBST(TK_STUB_LIB_SPEC)
+
+ AC_SUBST(TK_LIBS)
+ AC_SUBST(TK_XINCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_SHARED --
+#
+# Allows the building of shared libraries
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-shared=yes|no
+#
+# Defines the following vars:
+# STATIC_BUILD Used for building import/export libraries
+# on Windows.
+#
+# Sets the following vars:
+# SHARED_BUILD Value of 1 or 0
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_SHARED], [
+ AC_MSG_CHECKING([how to build libraries])
+ AC_ARG_ENABLE(shared,
+ AC_HELP_STRING([--enable-shared],
+ [build and link with shared libraries (default: on)]),
+ [tcl_ok=$enableval], [tcl_ok=yes])
+
+ if test "${enable_shared+set}" = set; then
+ enableval="$enable_shared"
+ tcl_ok=$enableval
+ else
+ tcl_ok=yes
+ fi
+
+ if test "$tcl_ok" = "yes" ; then
+ AC_MSG_RESULT([shared])
+ SHARED_BUILD=1
+ else
+ AC_MSG_RESULT([static])
+ SHARED_BUILD=0
+ AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?])
+ fi
+ AC_SUBST(SHARED_BUILD)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_THREADS --
+#
+# Specify if thread support should be enabled. If "yes" is specified
+# as an arg (optional), threads are enabled by default, "no" means
+# threads are disabled. "yes" is the default.
+#
+# TCL_THREADS is checked so that if you are compiling an extension
+# against a threaded core, your extension must be compiled threaded
+# as well.
+#
+# Note that it is legal to have a thread enabled extension run in a
+# threaded or non-threaded Tcl core, but a non-threaded extension may
+# only run in a non-threaded Tcl core.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-threads
+#
+# Sets the following vars:
+# THREADS_LIBS Thread library(s)
+#
+# Defines the following vars:
+# TCL_THREADS
+# _REENTRANT
+# _THREAD_SAFE
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_THREADS], [
+ AC_ARG_ENABLE(threads,
+ AC_HELP_STRING([--enable-threads],
+ [build with threads]),
+ [tcl_ok=$enableval], [tcl_ok=yes])
+
+ if test "${enable_threads+set}" = set; then
+ enableval="$enable_threads"
+ tcl_ok=$enableval
+ else
+ tcl_ok=yes
+ fi
+
+ if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then
+ TCL_THREADS=1
+
+ if test "${TEA_PLATFORM}" != "windows" ; then
+ # We are always OK on Windows, so check what this platform wants:
+
+ # USE_THREAD_ALLOC tells us to try the special thread-based
+ # allocator that significantly reduces lock contention
+ AC_DEFINE(USE_THREAD_ALLOC, 1,
+ [Do we want to use the threaded memory allocator?])
+ AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+ if test "`uname -s`" = "SunOS" ; then
+ AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+ [Do we really want to follow the standard? Yes we do!])
+ fi
+ AC_DEFINE(_THREAD_SAFE, 1, [Do we want the thread-safe OS API?])
+ AC_CHECK_LIB(pthread,pthread_mutex_init,tcl_ok=yes,tcl_ok=no)
+ if test "$tcl_ok" = "no"; then
+ # Check a little harder for __pthread_mutex_init in the same
+ # library, as some systems hide it there until pthread.h is
+ # defined. We could alternatively do an AC_TRY_COMPILE with
+ # pthread.h, but that will work with libpthread really doesn't
+ # exist, like AIX 4.2. [Bug: 4359]
+ AC_CHECK_LIB(pthread, __pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ fi
+
+ if test "$tcl_ok" = "yes"; then
+ # The space is needed
+ THREADS_LIBS=" -lpthread"
+ else
+ AC_CHECK_LIB(pthreads, pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = "yes"; then
+ # The space is needed
+ THREADS_LIBS=" -lpthreads"
+ else
+ AC_CHECK_LIB(c, pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = "no"; then
+ AC_CHECK_LIB(c_r, pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = "yes"; then
+ # The space is needed
+ THREADS_LIBS=" -pthread"
+ else
+ TCL_THREADS=0
+ AC_MSG_WARN([Do not know how to find pthread lib on your system - thread support disabled])
+ fi
+ fi
+ fi
+ fi
+ fi
+ else
+ TCL_THREADS=0
+ fi
+ # Do checking message here to not mess up interleaved configure output
+ AC_MSG_CHECKING([for building with threads])
+ if test "${TCL_THREADS}" = 1; then
+ AC_DEFINE(TCL_THREADS, 1, [Are we building with threads enabled?])
+ AC_MSG_RESULT([yes (default)])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ # TCL_THREADS sanity checking. See if our request for building with
+ # threads is the same as the way Tcl was built. If not, warn the user.
+ case ${TCL_DEFS} in
+ *THREADS=1*)
+ if test "${TCL_THREADS}" = "0"; then
+ AC_MSG_WARN([
+ Building ${PACKAGE_NAME} without threads enabled, but building against Tcl
+ that IS thread-enabled. It is recommended to use --enable-threads.])
+ fi
+ ;;
+ *)
+ if test "${TCL_THREADS}" = "1"; then
+ AC_MSG_WARN([
+ --enable-threads requested, but building against a Tcl that is NOT
+ thread-enabled. This is an OK configuration that will also run in
+ a thread-enabled core.])
+ fi
+ ;;
+ esac
+ AC_SUBST(TCL_THREADS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_SYMBOLS --
+#
+# Specify if debugging symbols should be used.
+# Memory (TCL_MEM_DEBUG) debugging can also be enabled.
+#
+# Arguments:
+# none
+#
+# TEA varies from core Tcl in that C|LDFLAGS_DEFAULT receives
+# the value of C|LDFLAGS_OPTIMIZE|DEBUG already substituted.
+# Requires the following vars to be set in the Makefile:
+# CFLAGS_DEFAULT
+# LDFLAGS_DEFAULT
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-symbols
+#
+# Defines the following vars:
+# CFLAGS_DEFAULT Sets to $(CFLAGS_DEBUG) if true
+# Sets to $(CFLAGS_OPTIMIZE) if false
+# LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true
+# Sets to $(LDFLAGS_OPTIMIZE) if false
+# DBGX Formerly used as debug library extension;
+# always blank now.
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_SYMBOLS], [
+ dnl Make sure we are initialized
+ AC_REQUIRE([TEA_CONFIG_CFLAGS])
+ AC_MSG_CHECKING([for build with symbols])
+ AC_ARG_ENABLE(symbols,
+ AC_HELP_STRING([--enable-symbols],
+ [build with debugging symbols (default: off)]),
+ [tcl_ok=$enableval], [tcl_ok=no])
+ DBGX=""
+ if test "$tcl_ok" = "no"; then
+ CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE}"
+ LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}"
+ AC_MSG_RESULT([no])
+ else
+ CFLAGS_DEFAULT="${CFLAGS_DEBUG}"
+ LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}"
+ if test "$tcl_ok" = "yes"; then
+ AC_MSG_RESULT([yes (standard debugging)])
+ fi
+ fi
+ if test "${TEA_PLATFORM}" != "windows" ; then
+ LDFLAGS_DEFAULT="${LDFLAGS}"
+ fi
+
+ AC_SUBST(TCL_DBGX)
+ AC_SUBST(CFLAGS_DEFAULT)
+ AC_SUBST(LDFLAGS_DEFAULT)
+
+ if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then
+ AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?])
+ fi
+
+ if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then
+ if test "$tcl_ok" = "all"; then
+ AC_MSG_RESULT([enabled symbols mem debugging])
+ else
+ AC_MSG_RESULT([enabled $tcl_ok debugging])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_LANGINFO --
+#
+# Allows use of modern nl_langinfo check for better l10n.
+# This is only relevant for Unix.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-langinfo=yes|no (default is yes)
+#
+# Defines the following vars:
+# HAVE_LANGINFO Triggers use of nl_langinfo if defined.
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_LANGINFO], [
+ AC_ARG_ENABLE(langinfo,
+ AC_HELP_STRING([--enable-langinfo],
+ [use nl_langinfo if possible to determine encoding at startup, otherwise use old heuristic (default: on)]),
+ [langinfo_ok=$enableval], [langinfo_ok=yes])
+
+ HAVE_LANGINFO=0
+ if test "$langinfo_ok" = "yes"; then
+ AC_CHECK_HEADER(langinfo.h,[langinfo_ok=yes],[langinfo_ok=no])
+ fi
+ AC_MSG_CHECKING([whether to use nl_langinfo])
+ if test "$langinfo_ok" = "yes"; then
+ AC_CACHE_VAL(tcl_cv_langinfo_h,
+ AC_TRY_COMPILE([#include <langinfo.h>], [nl_langinfo(CODESET);],
+ [tcl_cv_langinfo_h=yes],[tcl_cv_langinfo_h=no]))
+ AC_MSG_RESULT([$tcl_cv_langinfo_h])
+ if test $tcl_cv_langinfo_h = yes; then
+ AC_DEFINE(HAVE_LANGINFO, 1, [Do we have nl_langinfo()?])
+ fi
+ else
+ AC_MSG_RESULT([$langinfo_ok])
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_CONFIG_SYSTEM
+#
+# Determine what the system is (some things cannot be easily checked
+# on a feature-driven basis, alas). This can usually be done via the
+# "uname" command, but there are a few systems, like Next, where
+# this doesn't work.
+#
+# Arguments:
+# none
+#
+# Results:
+# Defines the following var:
+#
+# system - System/platform/version identification code.
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_CONFIG_SYSTEM], [
+ AC_CACHE_CHECK([system version], tcl_cv_sys_version, [
+ if test "${TEA_PLATFORM}" = "windows" ; then
+ tcl_cv_sys_version=windows
+ elif test -f /usr/lib/NextStep/software_version; then
+ tcl_cv_sys_version=NEXTSTEP-`awk '/3/,/3/' /usr/lib/NextStep/software_version`
+ else
+ tcl_cv_sys_version=`uname -s`-`uname -r`
+ if test "$?" -ne 0 ; then
+ AC_MSG_WARN([can't find uname command])
+ tcl_cv_sys_version=unknown
+ else
+ # Special check for weird MP-RAS system (uname returns weird
+ # results, and the version is kept in special file).
+
+ if test -r /etc/.relid -a "X`uname -n`" = "X`uname -s`" ; then
+ tcl_cv_sys_version=MP-RAS-`awk '{print [$]3}' /etc/.relid`
+ fi
+ if test "`uname -s`" = "AIX" ; then
+ tcl_cv_sys_version=AIX-`uname -v`.`uname -r`
+ fi
+ fi
+ fi
+ ])
+ system=$tcl_cv_sys_version
+])
+
+#--------------------------------------------------------------------
+# TEA_CONFIG_CFLAGS
+#
+# Try to determine the proper flags to pass to the compiler
+# for building shared libraries and other such nonsense.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines and substitutes the following vars:
+#
+# DL_OBJS - Name of the object file that implements dynamic
+# loading for Tcl on this system.
+# DL_LIBS - Library file(s) to include in tclsh and other base
+# applications in order for the "load" command to work.
+# LDFLAGS - Flags to pass to the compiler when linking object
+# files into an executable application binary such
+# as tclsh.
+# LD_SEARCH_FLAGS-Flags to pass to ld, such as "-R /usr/local/tcl/lib",
+# that tell the run-time dynamic linker where to look
+# for shared libraries such as libtcl.so. Depends on
+# the variable LIB_RUNTIME_DIR in the Makefile. Could
+# be the same as CC_SEARCH_FLAGS if ${CC} is used to link.
+# CC_SEARCH_FLAGS-Flags to pass to ${CC}, such as "-Wl,-rpath,/usr/local/tcl/lib",
+# that tell the run-time dynamic linker where to look
+# for shared libraries such as libtcl.so. Depends on
+# the variable LIB_RUNTIME_DIR in the Makefile.
+# SHLIB_CFLAGS - Flags to pass to cc when compiling the components
+# of a shared library (may request position-independent
+# code, among other things).
+# SHLIB_LD - Base command to use for combining object files
+# into a shared library.
+# SHLIB_LD_LIBS - Dependent libraries for the linker to scan when
+# creating shared libraries. This symbol typically
+# goes at the end of the "ld" commands that build
+# shared libraries. The value of the symbol is
+# "${LIBS}" if all of the dependent libraries should
+# be specified when creating a shared library. If
+# dependent libraries should not be specified (as on
+# SunOS 4.x, where they cause the link to fail, or in
+# general if Tcl and Tk aren't themselves shared
+# libraries), then this symbol has an empty string
+# as its value.
+# SHLIB_SUFFIX - Suffix to use for the names of dynamically loadable
+# extensions. An empty string means we don't know how
+# to use shared libraries on this platform.
+# LIB_SUFFIX - Specifies everything that comes after the "libfoo"
+# in a static or shared library name, using the $VERSION variable
+# to put the version in the right place. This is used
+# by platforms that need non-standard library names.
+# Examples: ${VERSION}.so.1.1 on NetBSD, since it needs
+# to have a version after the .so, and ${VERSION}.a
+# on AIX, since a shared library needs to have
+# a .a extension whereas shared objects for loadable
+# extensions have a .so extension. Defaults to
+# ${VERSION}${SHLIB_SUFFIX}.
+# TCL_NEEDS_EXP_FILE -
+# 1 means that an export file is needed to link to a
+# shared library.
+# TCL_EXP_FILE - The name of the installed export / import file which
+# should be used to link to the Tcl shared library.
+# Empty if Tcl is unshared.
+# TCL_BUILD_EXP_FILE -
+# The name of the built export / import file which
+# should be used to link to the Tcl shared library.
+# Empty if Tcl is unshared.
+# CFLAGS_DEBUG -
+# Flags used when running the compiler in debug mode
+# CFLAGS_OPTIMIZE -
+# Flags used when running the compiler in optimize mode
+# CFLAGS - Additional CFLAGS added as necessary (usually 64-bit)
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_CONFIG_CFLAGS], [
+ dnl Make sure we are initialized
+ AC_REQUIRE([TEA_INIT])
+
+ # Step 0.a: Enable 64 bit support?
+
+ AC_MSG_CHECKING([if 64bit support is requested])
+ AC_ARG_ENABLE(64bit,
+ AC_HELP_STRING([--enable-64bit],
+ [enable 64bit support (default: off)]),
+ [do64bit=$enableval], [do64bit=no])
+ AC_MSG_RESULT([$do64bit])
+
+ # Step 0.b: Enable Solaris 64 bit VIS support?
+
+ AC_MSG_CHECKING([if 64bit Sparc VIS support is requested])
+ AC_ARG_ENABLE(64bit-vis,
+ AC_HELP_STRING([--enable-64bit-vis],
+ [enable 64bit Sparc VIS support (default: off)]),
+ [do64bitVIS=$enableval], [do64bitVIS=no])
+ AC_MSG_RESULT([$do64bitVIS])
+
+ if test "$do64bitVIS" = "yes"; then
+ # Force 64bit on with VIS
+ do64bit=yes
+ fi
+
+ # Step 0.c: Cross-compiling options for Windows/CE builds?
+
+ if test "${TEA_PLATFORM}" = "windows" ; then
+ AC_MSG_CHECKING([if Windows/CE build is requested])
+ AC_ARG_ENABLE(wince,[ --enable-wince enable Win/CE support (where applicable)], [doWince=$enableval], [doWince=no])
+ AC_MSG_RESULT([$doWince])
+ fi
+
+ # Step 1: set the variable "system" to hold the name and version number
+ # for the system.
+
+ TEA_CONFIG_SYSTEM
+
+ # Step 2: check for existence of -ldl library. This is needed because
+ # Linux can use either -ldl or -ldld for dynamic loading.
+
+ AC_CHECK_LIB(dl, dlopen, have_dl=yes, have_dl=no)
+
+ # Require ranlib early so we can override it in special cases below.
+
+ AC_REQUIRE([AC_PROG_LIBTOOL])
+
+ # Step 3: set configuration options based on system name and version.
+ # This is similar to Tcl's unix/tcl.m4 except that we've added a
+ # "windows" case.
+
+ do64bit_ok=no
+ LDFLAGS_ORIG="$LDFLAGS"
+ # When ld needs options to work in 64-bit mode, put them in
+ # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load]
+ # is disabled by the user. [Bug 1016796]
+ LDFLAGS_ARCH=""
+ TCL_EXPORT_FILE_SUFFIX=""
+ UNSHARED_LIB_SUFFIX=""
+ TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`'
+ ECHO_VERSION='`echo ${PACKAGE_VERSION}`'
+ TCL_LIB_VERSIONS_OK=ok
+ CFLAGS_DEBUG=-g
+ CFLAGS_OPTIMIZE=-O
+ if test "$GCC" = "yes" ; then
+ CFLAGS_OPTIMIZE=-O2
+ CFLAGS_WARNING="-Wall -Wno-implicit-int"
+ else
+ CFLAGS_WARNING=""
+ fi
+ TCL_NEEDS_EXP_FILE=0
+ TCL_BUILD_EXP_FILE=""
+ TCL_EXP_FILE=""
+dnl FIXME: Replace AC_CHECK_PROG with AC_CHECK_TOOL once cross compiling is fixed.
+dnl AC_CHECK_TOOL(AR, ar)
+ AC_CHECK_PROG(AR, ar, ar)
+ STLIB_LD='${AR} cr'
+ LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
+ case $system in
+ windows)
+ # This is a 2-stage check to make sure we have the 64-bit SDK
+ # We have to know where the SDK is installed.
+ # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs
+ # MACHINE is IX86 for LINK, but this is used by the manifest,
+ # which requires x86|amd64|ia64.
+ MACHINE="X86"
+ if test "$do64bit" != "no" ; then
+ if test "x${MSSDK}x" = "xx" ; then
+ MSSDK="C:/Progra~1/Microsoft Platform SDK"
+ fi
+ MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'`
+ PATH64=""
+ case "$do64bit" in
+ amd64|x64|yes)
+ MACHINE="AMD64" ; # default to AMD64 64-bit build
+ PATH64="${MSSDK}/Bin/Win64/x86/AMD64"
+ ;;
+ ia64)
+ MACHINE="IA64"
+ PATH64="${MSSDK}/Bin/Win64"
+ ;;
+ esac
+ if test ! -d "${PATH64}" ; then
+ AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode])
+ AC_MSG_WARN([Ensure latest Platform SDK is installed])
+ do64bit="no"
+ else
+ AC_MSG_RESULT([ Using 64-bit $MACHINE mode])
+ do64bit_ok="yes"
+ fi
+ fi
+
+ if test "$doWince" != "no" ; then
+ if test "$do64bit" != "no" ; then
+ AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible])
+ fi
+ if test "$GCC" = "yes" ; then
+ AC_MSG_ERROR([Windows/CE and GCC builds incompatible])
+ fi
+ TEA_PATH_CELIB
+ # Set defaults for common evc4/PPC2003 setup
+ # Currently Tcl requires 300+, possibly 420+ for sockets
+ CEVERSION=420; # could be 211 300 301 400 420 ...
+ TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ...
+ ARCH=ARM; # could be ARM MIPS X86EM ...
+ PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002"
+ if test "$doWince" != "yes"; then
+ # If !yes then the user specified something
+ # Reset ARCH to allow user to skip specifying it
+ ARCH=
+ eval `echo $doWince | awk -F, '{ \
+ if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \
+ if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \
+ if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \
+ if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \
+ if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \
+ }'`
+ if test "x${ARCH}" = "x" ; then
+ ARCH=$TARGETCPU;
+ fi
+ fi
+ OSVERSION=WCE$CEVERSION;
+ if test "x${WCEROOT}" = "x" ; then
+ WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0"
+ if test ! -d "${WCEROOT}" ; then
+ WCEROOT="C:/Program Files/Microsoft eMbedded Tools"
+ fi
+ fi
+ if test "x${SDKROOT}" = "x" ; then
+ SDKROOT="C:/Program Files/Windows CE Tools"
+ if test ! -d "${SDKROOT}" ; then
+ SDKROOT="C:/Windows CE Tools"
+ fi
+ fi
+ WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'`
+ SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'`
+ if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \
+ -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then
+ AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]])
+ doWince="no"
+ else
+ # We could PATH_NOSPACE these, but that's not important,
+ # as long as we quote them when used.
+ CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include"
+ if test -d "${CEINCLUDE}/${TARGETCPU}" ; then
+ CEINCLUDE="${CEINCLUDE}/${TARGETCPU}"
+ fi
+ CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"
+ fi
+ fi
+
+ if test "$GCC" != "yes" ; then
+ if test "${SHARED_BUILD}" = "0" ; then
+ runtime=-MT
+ else
+ runtime=-MD
+ fi
+
+ if test "$do64bit" != "no" ; then
+ # All this magic is necessary for the Win64 SDK RC1 - hobbs
+ CC="\"${PATH64}/cl.exe\""
+ CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\""
+ RC="\"${MSSDK}/bin/rc.exe\""
+ lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
+ LINKBIN="\"${PATH64}/link.exe\""
+ CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d"
+ CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+ # Avoid 'unresolved external symbol __security_cookie'
+ # errors, c.f. http://support.microsoft.com/?id=894573
+ TEA_ADD_LIBS([bufferoverflowU.lib])
+ elif test "$doWince" != "no" ; then
+ CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
+ if test "${TARGETCPU}" = "X86"; then
+ CC="\"${CEBINROOT}/cl.exe\""
+ else
+ CC="\"${CEBINROOT}/cl${ARCH}.exe\""
+ fi
+ CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\""
+ RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\""
+ arch=`echo ${ARCH} | awk '{print tolower([$]0)}'`
+ defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS"
+ if test "${SHARED_BUILD}" = "1" ; then
+ # Static CE builds require static celib as well
+ defs="${defs} _DLL"
+ fi
+ for i in $defs ; do
+ AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i)
+ done
+ AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version])
+ AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version])
+ CFLAGS_DEBUG="-nologo -Zi -Od"
+ CFLAGS_OPTIMIZE="-nologo -Ox"
+ lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'`
+ lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo"
+ LINKBIN="\"${CEBINROOT}/link.exe\""
+ AC_SUBST(CELIB_DIR)
+ else
+ RC="rc"
+ lflags="-nologo"
+ LINKBIN="link"
+ CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
+ CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+ fi
+ fi
+
+ if test "$GCC" = "yes"; then
+ # mingw gcc mode
+ RC="windres"
+ CFLAGS_DEBUG="-g"
+ CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+ SHLIB_LD="$CC -shared"
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
+ LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"
+ else
+ SHLIB_LD="${LINKBIN} -dll ${lflags}"
+ # link -lib only works when -lib is the first arg
+ STLIB_LD="${LINKBIN} -lib ${lflags}"
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib'
+ PATHTYPE=-w
+ # For information on what debugtype is most useful, see:
+ # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp
+ # This essentially turns it all on.
+ LDFLAGS_DEBUG="-debug:full -debugtype:both -warn:2"
+ LDFLAGS_OPTIMIZE="-release"
+ if test "$doWince" != "no" ; then
+ LDFLAGS_CONSOLE="-link ${lflags}"
+ LDFLAGS_WINDOW=${LDFLAGS_CONSOLE}
+ else
+ LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
+ LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
+ fi
+ fi
+
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".dll"
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll'
+
+ TCL_LIB_VERSIONS_OK=nodots
+ # Bogus to avoid getting this turned off
+ DL_OBJS="tclLoadNone.obj"
+ ;;
+ AIX-*)
+ if test "${TCL_THREADS}" = "1" -a "$GCC" != "yes" ; then
+ # AIX requires the _r compiler when gcc isn't being used
+ case "${CC}" in
+ *_r)
+ # ok ...
+ ;;
+ *)
+ CC=${CC}_r
+ ;;
+ esac
+ AC_MSG_RESULT([Using $CC for compiling with threads])
+ fi
+ LIBS="$LIBS -lc"
+ SHLIB_CFLAGS=""
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+
+ DL_OBJS="tclLoadDl.o"
+ LD_LIBRARY_PATH_VAR="LIBPATH"
+
+ # Check to enable 64-bit flags for compiler/linker on AIX 4+
+ if test "$do64bit" = "yes" -a "`uname -v`" -gt "3" ; then
+ if test "$GCC" = "yes" ; then
+ AC_MSG_WARN([64bit mode not supported with GCC on $system])
+ else
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -q64"
+ LDFLAGS_ARCH="-q64"
+ RANLIB="${RANLIB} -X64"
+ AR="${AR} -X64"
+ SHLIB_LD_FLAGS="-b64"
+ fi
+ fi
+
+ if test "`uname -m`" = "ia64" ; then
+ # AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC
+ SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+ # AIX-5 has dl* in libc.so
+ DL_LIBS=""
+ if test "$GCC" = "yes" ; then
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ else
+ CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}'
+ fi
+ LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ else
+ if test "$GCC" = "yes" ; then
+ SHLIB_LD="gcc -shared"
+ else
+ SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bE:lib.exp -H512 -T512 -bnoentry"
+ fi
+ SHLIB_LD="${TCL_SRC_DIR}/unix/ldAix ${SHLIB_LD} ${SHLIB_LD_FLAGS}"
+ DL_LIBS="-ldl"
+ CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ TCL_NEEDS_EXP_FILE=1
+ TCL_EXPORT_FILE_SUFFIX='${PACKAGE_VERSION}.exp'
+ fi
+
+ # AIX v<=4.1 has some different flags than 4.2+
+ if test "$system" = "AIX-4.1" -o "`uname -v`" -lt "4" ; then
+ AC_LIBOBJ([tclLoadAix])
+ DL_LIBS="-lld"
+ fi
+
+ # On AIX <=v4 systems, libbsd.a has to be linked in to support
+ # non-blocking file IO. This library has to be linked in after
+ # the MATH_LIBS or it breaks the pow() function. The way to
+ # insure proper sequencing, is to add it to the tail of MATH_LIBS.
+ # This library also supplies gettimeofday.
+ #
+ # AIX does not have a timezone field in struct tm. When the AIX
+ # bsd library is used, the timezone global and the gettimeofday
+ # methods are to be avoided for timezone deduction instead, we
+ # deduce the timezone by comparing the localtime result on a
+ # known GMT value.
+
+ AC_CHECK_LIB(bsd, gettimeofday, libbsd=yes, libbsd=no)
+ if test $libbsd = yes; then
+ MATH_LIBS="$MATH_LIBS -lbsd"
+ AC_DEFINE(USE_DELTA_FOR_TZ, 1, [Do we need a special AIX hack for timezones?])
+ fi
+ ;;
+ BeOS*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="${CC} -nostart"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+
+ #-----------------------------------------------------------
+ # Check for inet_ntoa in -lbind, for BeOS (which also needs
+ # -lsocket, even if the network functions are in -lnet which
+ # is always linked to, for compatibility.
+ #-----------------------------------------------------------
+ AC_CHECK_LIB(bind, inet_ntoa, [LIBS="$LIBS -lbind -lsocket"])
+ ;;
+ BSD/OS-2.1*|BSD/OS-3*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="shlicc -r"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ BSD/OS-4.*)
+ SHLIB_CFLAGS="-export-dynamic -fPIC"
+ SHLIB_LD="cc -shared"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ LDFLAGS="$LDFLAGS -export-dynamic"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ dgux*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ HP-UX-*.11.*)
+ # Use updated header definitions where possible
+ AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, [Do we want to use the XOPEN network library?])
+ # Needed by Tcl, but not most extensions
+ #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?])
+ #LIBS="$LIBS -lxnet" # Use the XOPEN network library
+
+ SHLIB_SUFFIX=".sl"
+ AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = yes; then
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld -b"
+ SHLIB_LD_LIBS='${LIBS}'
+ DL_OBJS="tclLoadShl.o"
+ DL_LIBS="-ldld"
+ LDFLAGS="$LDFLAGS -Wl,-E"
+ CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
+ LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
+ LD_LIBRARY_PATH_VAR="SHLIB_PATH"
+ fi
+ if test "$GCC" = "yes" ; then
+ SHLIB_LD="gcc -shared"
+ SHLIB_LD_LIBS='${LIBS}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ fi
+
+ # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc
+ #CFLAGS="$CFLAGS +DAportable"
+
+ # Check to enable 64-bit flags for compiler/linker
+ if test "$do64bit" = "yes" ; then
+ if test "$GCC" = "yes" ; then
+ hpux_arch=`${CC} -dumpmachine`
+ case $hpux_arch in
+ hppa64*)
+ # 64-bit gcc in use. Fix flags for GNU ld.
+ do64bit_ok=yes
+ SHLIB_LD="${CC} -shared"
+ SHLIB_LD_LIBS='${LIBS}'
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ ;;
+ *)
+ AC_MSG_WARN([64bit mode not supported with GCC on $system])
+ ;;
+ esac
+ else
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS +DD64"
+ LDFLAGS_ARCH="+DD64"
+ fi
+ fi
+ ;;
+ HP-UX-*.08.*|HP-UX-*.09.*|HP-UX-*.10.*)
+ SHLIB_SUFFIX=".sl"
+ AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = yes; then
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld -b"
+ SHLIB_LD_LIBS=""
+ DL_OBJS="tclLoadShl.o"
+ DL_LIBS="-ldld"
+ LDFLAGS="$LDFLAGS -Wl,-E"
+ CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
+ LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
+ LD_LIBRARY_PATH_VAR="SHLIB_PATH"
+ fi
+ ;;
+ IRIX-5.*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld -shared -rdata_shared"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+ ;;
+ IRIX-6.*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld -n32 -shared -rdata_shared"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+ if test "$GCC" = "yes" ; then
+ CFLAGS="$CFLAGS -mabi=n32"
+ LDFLAGS="$LDFLAGS -mabi=n32"
+ else
+ case $system in
+ IRIX-6.3)
+ # Use to build 6.2 compatible binaries on 6.3.
+ CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS"
+ ;;
+ *)
+ CFLAGS="$CFLAGS -n32"
+ ;;
+ esac
+ LDFLAGS="$LDFLAGS -n32"
+ fi
+ ;;
+ IRIX64-6.*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld -n32 -shared -rdata_shared"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+
+ # Check to enable 64-bit flags for compiler/linker
+
+ if test "$do64bit" = "yes" ; then
+ if test "$GCC" = "yes" ; then
+ AC_MSG_WARN([64bit mode not supported by gcc])
+ else
+ do64bit_ok=yes
+ SHLIB_LD="ld -64 -shared -rdata_shared"
+ CFLAGS="$CFLAGS -64"
+ LDFLAGS_ARCH="-64"
+ fi
+ fi
+ ;;
+ Linux*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+
+ CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+ # egcs-2.91.66 on Redhat Linux 6.0 generates lots of warnings
+ # when you inline the string and math operations. Turn this off to
+ # get rid of the warnings.
+ #CFLAGS_OPTIMIZE="${CFLAGS_OPTIMIZE} -D__NO_STRING_INLINES -D__NO_MATH_INLINES"
+
+ SHLIB_LD="${CC} -shared"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ if test "`uname -m`" = "alpha" ; then
+ CFLAGS="$CFLAGS -mieee"
+ fi
+
+ # The combo of gcc + glibc has a bug related
+ # to inlining of functions like strtod(). The
+ # -fno-builtin flag should address this problem
+ # but it does not work. The -fno-inline flag
+ # is kind of overkill but it works.
+ # Disable inlining only when one of the
+ # files in compat/*.c is being linked in.
+ if test x"${USE_COMPAT}" != x ; then
+ CFLAGS="$CFLAGS -fno-inline"
+ fi
+
+ ;;
+ GNU*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+
+ SHLIB_LD="${CC} -shared"
+ DL_OBJS=""
+ DL_LIBS="-ldl"
+ LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ if test "`uname -m`" = "alpha" ; then
+ CFLAGS="$CFLAGS -mieee"
+ fi
+ ;;
+ Lynx*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ CFLAGS_OPTIMIZE=-02
+ SHLIB_LD="${CC} -shared "
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-mshared -ldl"
+ LD_FLAGS="-Wl,--export-dynamic"
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ ;;
+ MP-RAS-02*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ MP-RAS-*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ LDFLAGS="$LDFLAGS -Wl,-Bexport"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ NetBSD-*|FreeBSD-[[1-2]].*)
+ # NetBSD/SPARC needs -fPIC, -fpic will not do.
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="ld -Bshareable -x"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+ AC_CACHE_CHECK([for ELF], tcl_cv_ld_elf, [
+ AC_EGREP_CPP(yes, [
+#ifdef __ELF__
+ yes
+#endif
+ ], tcl_cv_ld_elf=yes, tcl_cv_ld_elf=no)])
+ if test $tcl_cv_ld_elf = yes; then
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
+ else
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.1.0'
+ fi
+
+ # Ancient FreeBSD doesn't handle version numbers with dots.
+
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ OpenBSD-*)
+ # OpenBSD/SPARC[64] needs -fPIC, -fpic will not do.
+ case `machine` in
+ sparc|sparc64)
+ SHLIB_CFLAGS="-fPIC";;
+ *)
+ SHLIB_CFLAGS="-fpic";;
+ esac
+ SHLIB_LD="${CC} -shared ${SHLIB_CFLAGS}"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.1.0'
+ AC_CACHE_CHECK([for ELF], tcl_cv_ld_elf, [
+ AC_EGREP_CPP(yes, [
+#ifdef __ELF__
+ yes
+#endif
+ ], tcl_cv_ld_elf=yes, tcl_cv_ld_elf=no)])
+ if test $tcl_cv_ld_elf = yes; then
+ LDFLAGS=-Wl,-export-dynamic
+ else
+ LDFLAGS=""
+ fi
+
+ # OpenBSD doesn't do version numbers with dots.
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ FreeBSD-*)
+ # FreeBSD 3.* and greater have ELF.
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="ld -Bshareable -x"
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ LDFLAGS="$LDFLAGS -export-dynamic"
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+ if test "${TCL_THREADS}" = "1" ; then
+ # The -pthread needs to go in the CFLAGS, not LIBS
+ LIBS=`echo $LIBS | sed s/-pthread//`
+ CFLAGS="$CFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread"
+ fi
+ case $system in
+ FreeBSD-3.*)
+ # FreeBSD-3 doesn't handle version numbers with dots.
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so'
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ esac
+ ;;
+ Darwin-*)
+ CFLAGS_OPTIMIZE="-Os"
+ SHLIB_CFLAGS="-fno-common"
+ if test $do64bit = yes; then
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+ fi
+ # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS here:
+ SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}'
+ AC_CACHE_CHECK([if ld accepts -single_module flag], tcl_cv_ld_single_module, [
+ hold_ldflags=$LDFLAGS
+ LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module"
+ AC_TRY_LINK(, [int i;], tcl_cv_ld_single_module=yes, tcl_cv_ld_single_module=no)
+ LDFLAGS=$hold_ldflags])
+ if test $tcl_cv_ld_single_module = yes; then
+ SHLIB_LD="${SHLIB_LD} -Wl,-single_module"
+ fi
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".dylib"
+ DL_OBJS="tclLoadDyld.o"
+ DL_LIBS=""
+ # Don't use -prebind when building for Mac OS X 10.4 or later only:
+ test -z "${MACOSX_DEPLOYMENT_TARGET}" || \
+ test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F. '{print [$]2}'`" -lt 4 && \
+ LDFLAGS="$LDFLAGS -prebind"
+ LDFLAGS="$LDFLAGS -headerpad_max_install_names"
+ AC_CACHE_CHECK([if ld accepts -search_paths_first flag], tcl_cv_ld_search_paths_first, [
+ hold_ldflags=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+ AC_TRY_LINK(, [int i;], tcl_cv_ld_search_paths_first=yes, tcl_cv_ld_search_paths_first=no)
+ LDFLAGS=$hold_ldflags])
+ if test $tcl_cv_ld_search_paths_first = yes; then
+ LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+ fi
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH"
+
+ # TEA specific: for Tk extensions, remove -arch ppc64 from CFLAGS
+ # for fat builds, as neither TkAqua nor TkX11 can be built for 64bit
+ # at present (no 64bit GUI libraries).
+ test $do64bit_ok = no && test -n "${TK_BIN_DIR}" && \
+ CFLAGS="`echo "$CFLAGS" | sed -e 's/-arch ppc64/-arch ppc/g'`"
+ ;;
+ NEXTSTEP-*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="cc -nostdlib -r"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadNext.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ OS/390-*)
+ CFLAGS_OPTIMIZE="" # Optimizer is buggy
+ AC_DEFINE(_OE_SOCKETS, 1, # needed in sys/socket.h
+ [Should OS/390 do the right thing with sockets?])
+ ;;
+ OSF1-1.0|OSF1-1.1|OSF1-1.2)
+ # OSF/1 1.[012] from OSF, and derivatives, including Paragon OSF/1
+ SHLIB_CFLAGS=""
+ # Hack: make package name same as library name
+ SHLIB_LD='ld -R -export $@:'
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadOSF.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ OSF1-1.*)
+ # OSF/1 1.3 from OSF using ELF, and derivatives, including AD2
+ SHLIB_CFLAGS="-fPIC"
+ if test "$SHARED_BUILD" = "1" ; then
+ SHLIB_LD="ld -shared"
+ else
+ SHLIB_LD="ld -non_shared"
+ fi
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ OSF1-V*)
+ # Digital OSF/1
+ SHLIB_CFLAGS=""
+ if test "$SHARED_BUILD" = "1" ; then
+ SHLIB_LD='ld -shared -expect_unresolved "*"'
+ else
+ SHLIB_LD='ld -non_shared -expect_unresolved "*"'
+ fi
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'
+ if test "$GCC" = "yes" ; then
+ CFLAGS="$CFLAGS -mieee"
+ else
+ CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"
+ fi
+ # see pthread_intro(3) for pthread support on osf1, k.furukawa
+ if test "${TCL_THREADS}" = "1" ; then
+ CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE"
+ CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64"
+ LIBS=`echo $LIBS | sed s/-lpthreads//`
+ if test "$GCC" = "yes" ; then
+ LIBS="$LIBS -lpthread -lmach -lexc"
+ else
+ CFLAGS="$CFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread"
+ fi
+ fi
+
+ ;;
+ QNX-6*)
+ # QNX RTP
+ # This may work for all QNX, but it was only reported for v6.
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="ld -Bshareable -x"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ # dlopen is in -lc on QNX
+ DL_LIBS=""
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ SCO_SV-3.2*)
+ # Note, dlopen is available only on SCO 3.2.5 and greater. However,
+ # this test works, since "uname -s" was non-standard in 3.2.4 and
+ # below.
+ if test "$GCC" = "yes" ; then
+ SHLIB_CFLAGS="-fPIC -melf"
+ LDFLAGS="$LDFLAGS -melf -Wl,-Bexport"
+ else
+ SHLIB_CFLAGS="-Kpic -belf"
+ LDFLAGS="$LDFLAGS -belf -Wl,-Bexport"
+ fi
+ SHLIB_LD="ld -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS=""
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ SINIX*5.4*)
+ SHLIB_CFLAGS="-K PIC"
+ SHLIB_LD="cc -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ SunOS-4*)
+ SHLIB_CFLAGS="-PIC"
+ SHLIB_LD="ld"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+
+ # SunOS can't handle version numbers with dots in them in library
+ # specs, like -ltcl7.5, so use -ltcl75 instead. Also, it
+ # requires an extra version number at the end of .so file names.
+ # So, the library has to have a name like libtcl75.so.1.0
+
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.1.0'
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ SunOS-5.[[0-6]])
+ # Careful to not let 5.10+ fall into this case
+
+ # Note: If _REENTRANT isn't defined, then Solaris
+ # won't define thread-safe library routines.
+
+ AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+ AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+ [Do we really want to follow the standard? Yes we do!])
+
+ SHLIB_CFLAGS="-KPIC"
+
+ # Note: need the LIBS below, otherwise Tk won't find Tcl's
+ # symbols when dynamically loaded into tclsh.
+
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ if test "$GCC" = "yes" ; then
+ SHLIB_LD="$CC -shared"
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ else
+ SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+ CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ fi
+ ;;
+ SunOS-5*)
+ # Note: If _REENTRANT isn't defined, then Solaris
+ # won't define thread-safe library routines.
+
+ AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+ AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+ [Do we really want to follow the standard? Yes we do!])
+
+ SHLIB_CFLAGS="-KPIC"
+
+ # Check to enable 64-bit flags for compiler/linker
+ if test "$do64bit" = "yes" ; then
+ arch=`isainfo`
+ if test "$arch" = "sparcv9 sparc" ; then
+ if test "$GCC" = "yes" ; then
+ if test "`gcc -dumpversion | awk -F. '{print [$]1}'`" -lt "3" ; then
+ AC_MSG_WARN([64bit mode not supported with GCC < 3.2 on $system])
+ else
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -m64 -mcpu=v9"
+ LDFLAGS="$LDFLAGS -m64 -mcpu=v9"
+ SHLIB_CFLAGS="-fPIC"
+ fi
+ else
+ do64bit_ok=yes
+ if test "$do64bitVIS" = "yes" ; then
+ CFLAGS="$CFLAGS -xarch=v9a"
+ LDFLAGS_ARCH="-xarch=v9a"
+ else
+ CFLAGS="$CFLAGS -xarch=v9"
+ LDFLAGS_ARCH="-xarch=v9"
+ fi
+ # Solaris 64 uses this as well
+ #LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64"
+ fi
+ elif test "$arch" = "amd64 i386" ; then
+ if test "$GCC" = "yes" ; then
+ AC_MSG_WARN([64bit mode not supported with GCC on $system])
+ else
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -xarch=amd64"
+ LDFLAGS="$LDFLAGS -xarch=amd64"
+ fi
+ else
+ AC_MSG_WARN([64bit mode not supported for $arch])
+ fi
+ fi
+
+ # Note: need the LIBS below, otherwise Tk won't find Tcl's
+ # symbols when dynamically loaded into tclsh.
+
+ SHLIB_LD_LIBS='${LIBS}'
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ if test "$GCC" = "yes" ; then
+ SHLIB_LD="$CC -shared"
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ if test "$do64bit_ok" = "yes" ; then
+ # We need to specify -static-libgcc or we need to
+ # add the path to the sparv9 libgcc.
+ # JH: static-libgcc is necessary for core Tcl, but may
+ # not be necessary for extensions.
+ SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc"
+ # for finding sparcv9 libgcc, get the regular libgcc
+ # path, remove so name and append 'sparcv9'
+ #v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..."
+ #CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir"
+ fi
+ else
+ SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ fi
+ ;;
+ UNIX_SV* | UnixWare-5*)
+ SHLIB_CFLAGS="-KPIC"
+ SHLIB_LD="cc -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ DL_OBJS="tclLoadDl.o"
+ DL_LIBS="-ldl"
+ # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers
+ # that don't grok the -Bexport option. Test that it does.
+ AC_CACHE_CHECK([for ld accepts -Bexport flag], tcl_cv_ld_Bexport, [
+ hold_ldflags=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-Bexport"
+ AC_TRY_LINK(, [int i;], tcl_cv_ld_Bexport=yes, tcl_cv_ld_Bexport=no)
+ LDFLAGS=$hold_ldflags])
+ if test $tcl_cv_ld_Bexport = yes; then
+ LDFLAGS="$LDFLAGS -Wl,-Bexport"
+ fi
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ esac
+
+ if test "$do64bit" = "yes" -a "$do64bit_ok" = "no" ; then
+ AC_MSG_WARN([64bit support being disabled -- don't know magic for this platform])
+ fi
+
+ # Step 4: disable dynamic loading if requested via a command-line switch.
+
+ AC_ARG_ENABLE(load,
+ AC_HELP_STRING([--disable-load],
+ [disallow dynamic loading and "load" command (default: enabled)]),
+ [tcl_ok=$enableval], [tcl_ok=yes])
+ if test "$tcl_ok" = "no"; then
+ DL_OBJS=""
+ fi
+
+ if test "x$DL_OBJS" != "x" ; then
+ BUILD_DLTEST="\$(DLTEST_TARGETS)"
+ else
+ echo "Can't figure out how to do dynamic loading or shared libraries"
+ echo "on this system."
+ SHLIB_CFLAGS=""
+ SHLIB_LD=""
+ SHLIB_SUFFIX=""
+ DL_OBJS="tclLoadNone.o"
+ DL_LIBS=""
+ LDFLAGS="$LDFLAGS_ORIG"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ BUILD_DLTEST=""
+ fi
+ LDFLAGS="$LDFLAGS $LDFLAGS_ARCH"
+
+ # If we're running gcc, then change the C flags for compiling shared
+ # libraries to the right flags for gcc, instead of those for the
+ # standard manufacturer compiler.
+
+ if test "$DL_OBJS" != "tclLoadNone.o" ; then
+ if test "$GCC" = "yes" ; then
+ case $system in
+ AIX-*)
+ ;;
+ BSD/OS*)
+ ;;
+ IRIX*)
+ ;;
+ NetBSD-*|FreeBSD-*)
+ ;;
+ Darwin-*)
+ ;;
+ SCO_SV-3.2*)
+ ;;
+ windows)
+ ;;
+ *)
+ SHLIB_CFLAGS="-fPIC"
+ ;;
+ esac
+ fi
+ fi
+
+ if test "$SHARED_LIB_SUFFIX" = "" ; then
+ SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'
+ fi
+ if test "$UNSHARED_LIB_SUFFIX" = "" ; then
+ UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'
+ fi
+
+ AC_SUBST(DL_LIBS)
+
+ AC_SUBST(CFLAGS_DEBUG)
+ AC_SUBST(CFLAGS_OPTIMIZE)
+ AC_SUBST(CFLAGS_WARNING)
+
+ AC_SUBST(STLIB_LD)
+ AC_SUBST(SHLIB_LD)
+
+ AC_SUBST(SHLIB_LD_LIBS)
+ AC_SUBST(SHLIB_CFLAGS)
+
+ AC_SUBST(LD_LIBRARY_PATH_VAR)
+
+ # These must be called after we do the basic CFLAGS checks and
+ # verify any possible 64-bit or similar switches are necessary
+ TEA_TCL_EARLY_FLAGS
+ TEA_TCL_64BIT_FLAGS
+])
+
+#--------------------------------------------------------------------
+# TEA_SERIAL_PORT
+#
+# Determine which interface to use to talk to the serial port.
+# Note that #include lines must begin in leftmost column for
+# some compilers to recognize them as preprocessor directives,
+# and some build environments have stdin not pointing at a
+# pseudo-terminal (usually /dev/null instead.)
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines only one of the following vars:
+# HAVE_SYS_MODEM_H
+# USE_TERMIOS
+# USE_TERMIO
+# USE_SGTTY
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_SERIAL_PORT], [
+ AC_CHECK_HEADERS(sys/modem.h)
+ AC_CACHE_CHECK([termios vs. termio vs. sgtty], tcl_cv_api_serial, [
+ AC_TRY_RUN([
+#include <termios.h>
+
+int main() {
+ struct termios t;
+ if (tcgetattr(0, &t) == 0) {
+ cfsetospeed(&t, 0);
+ t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ if test $tcl_cv_api_serial = no ; then
+ AC_TRY_RUN([
+#include <termio.h>
+
+int main() {
+ struct termio t;
+ if (ioctl(0, TCGETA, &t) == 0) {
+ t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no ; then
+ AC_TRY_RUN([
+#include <sgtty.h>
+
+int main() {
+ struct sgttyb t;
+ if (ioctl(0, TIOCGETP, &t) == 0) {
+ t.sg_ospeed = 0;
+ t.sg_flags |= ODDP | EVENP | RAW;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no ; then
+ AC_TRY_RUN([
+#include <termios.h>
+#include <errno.h>
+
+int main() {
+ struct termios t;
+ if (tcgetattr(0, &t) == 0
+ || errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+ cfsetospeed(&t, 0);
+ t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no; then
+ AC_TRY_RUN([
+#include <termio.h>
+#include <errno.h>
+
+int main() {
+ struct termio t;
+ if (ioctl(0, TCGETA, &t) == 0
+ || errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+ t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+ }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no; then
+ AC_TRY_RUN([
+#include <sgtty.h>
+#include <errno.h>
+
+int main() {
+ struct sgttyb t;
+ if (ioctl(0, TIOCGETP, &t) == 0
+ || errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+ t.sg_ospeed = 0;
+ t.sg_flags |= ODDP | EVENP | RAW;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=none, tcl_cv_api_serial=none)
+ fi])
+ case $tcl_cv_api_serial in
+ termios) AC_DEFINE(USE_TERMIOS, 1, [Use the termios API for serial lines]);;
+ termio) AC_DEFINE(USE_TERMIO, 1, [Use the termio API for serial lines]);;
+ sgtty) AC_DEFINE(USE_SGTTY, 1, [Use the sgtty API for serial lines]);;
+ esac
+])
+
+#--------------------------------------------------------------------
+# TEA_MISSING_POSIX_HEADERS
+#
+# Supply substitutes for missing POSIX header files. Special
+# notes:
+# - stdlib.h doesn't define strtol, strtoul, or
+# strtod insome versions of SunOS
+# - some versions of string.h don't declare procedures such
+# as strstr
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines some of the following vars:
+# NO_DIRENT_H
+# NO_ERRNO_H
+# NO_VALUES_H
+# HAVE_LIMITS_H or NO_LIMITS_H
+# NO_STDLIB_H
+# NO_STRING_H
+# NO_SYS_WAIT_H
+# NO_DLFCN_H
+# HAVE_SYS_PARAM_H
+#
+# HAVE_STRING_H ?
+#
+# tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and
+# CHECK on limits.h
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [
+ AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h,
+ AC_TRY_LINK([#include <sys/types.h>
+#include <dirent.h>], [
+#ifndef _POSIX_SOURCE
+# ifdef __Lynx__
+ /*
+ * Generate compilation error to make the test fail: Lynx headers
+ * are only valid if really in the POSIX environment.
+ */
+
+ missing_procedure();
+# endif
+#endif
+DIR *d;
+struct dirent *entryPtr;
+char *p;
+d = opendir("foobar");
+entryPtr = readdir(d);
+p = entryPtr->d_name;
+closedir(d);
+], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no))
+
+ if test $tcl_cv_dirent_h = no; then
+ AC_DEFINE(NO_DIRENT_H, 1, [Do we have <dirent.h>?])
+ fi
+
+ AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have <errno.h>?])])
+ AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have <float.h>?])])
+ AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have <values.h>?])])
+ AC_CHECK_HEADER(limits.h,
+ [AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have <limits.h>?])],
+ [AC_DEFINE(NO_LIMITS_H, 1, [Do we have <limits.h>?])])
+ AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0)
+ AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0)
+ AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0)
+ AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0)
+ if test $tcl_ok = 0; then
+ AC_DEFINE(NO_STDLIB_H, 1, [Do we have <stdlib.h>?])
+ fi
+ AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0)
+ AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0)
+ AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0)
+
+ # See also memmove check below for a place where NO_STRING_H can be
+ # set and why.
+
+ if test $tcl_ok = 0; then
+ AC_DEFINE(NO_STRING_H, 1, [Do we have <string.h>?])
+ fi
+
+ AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have <sys/wait.h>?])])
+ AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have <dlfcn.h>?])])
+
+ # OS/390 lacks sys/param.h (and doesn't need it, by chance).
+ AC_HAVE_HEADERS(sys/param.h)
+])
+
+#--------------------------------------------------------------------
+# TEA_PATH_X
+#
+# Locate the X11 header files and the X11 library archive. Try
+# the ac_path_x macro first, but if it doesn't find the X stuff
+# (e.g. because there's no xmkmf program) then check through
+# a list of possible directories. Under some conditions the
+# autoconf macro will return an include directory that contains
+# no include files, so double-check its result just to be safe.
+#
+# This should be called after TEA_CONFIG_CFLAGS as setting the
+# LIBS line can confuse some configure macro magic.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Sets the following vars:
+# XINCLUDES
+# XLIBSW
+# PKG_LIBS (appends to)
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_X], [
+ if test "${TEA_WINDOWINGSYSTEM}" = "x11" ; then
+ TEA_PATH_UNIX_X
+ fi
+])
+
+AC_DEFUN([TEA_PATH_UNIX_X], [
+ AC_PATH_X
+ not_really_there=""
+ if test "$no_x" = ""; then
+ if test "$x_includes" = ""; then
+ AC_TRY_CPP([#include <X11/XIntrinsic.h>], , not_really_there="yes")
+ else
+ if test ! -r $x_includes/X11/Intrinsic.h; then
+ not_really_there="yes"
+ fi
+ fi
+ fi
+ if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then
+ AC_MSG_CHECKING([for X11 header files])
+ found_xincludes="no"
+ AC_TRY_CPP([#include <X11/Intrinsic.h>], found_xincludes="yes", found_xincludes="no")
+ if test "$found_xincludes" = "no"; then
+ dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include"
+ for i in $dirs ; do
+ if test -r $i/X11/Intrinsic.h; then
+ AC_MSG_RESULT([$i])
+ XINCLUDES=" -I$i"
+ found_xincludes="yes"
+ break
+ fi
+ done
+ fi
+ else
+ if test "$x_includes" != ""; then
+ XINCLUDES="-I$x_includes"
+ found_xincludes="yes"
+ fi
+ fi
+ if test found_xincludes = "no"; then
+ AC_MSG_RESULT([couldn't find any!])
+ fi
+
+ if test "$no_x" = yes; then
+ AC_MSG_CHECKING([for X11 libraries])
+ XLIBSW=nope
+ dirs="/usr/unsupported/lib /usr/local/lib /usr/X386/lib /usr/X11R6/lib /usr/X11R5/lib /usr/lib/X11R5 /usr/lib/X11R4 /usr/openwin/lib /usr/X11/lib /usr/sww/X11/lib"
+ for i in $dirs ; do
+ if test -r $i/libX11.a -o -r $i/libX11.so -o -r $i/libX11.sl; then
+ AC_MSG_RESULT([$i])
+ XLIBSW="-L$i -lX11"
+ x_libraries="$i"
+ break
+ fi
+ done
+ else
+ if test "$x_libraries" = ""; then
+ XLIBSW=-lX11
+ else
+ XLIBSW="-L$x_libraries -lX11"
+ fi
+ fi
+ if test "$XLIBSW" = nope ; then
+ AC_CHECK_LIB(Xwindow, XCreateWindow, XLIBSW=-lXwindow)
+ fi
+ if test "$XLIBSW" = nope ; then
+ AC_MSG_RESULT([could not find any! Using -lX11.])
+ XLIBSW=-lX11
+ fi
+ if test x"${XLIBSW}" != x ; then
+ PKG_LIBS="${PKG_LIBS} ${XLIBSW}"
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_BLOCKING_STYLE
+#
+# The statements below check for systems where POSIX-style
+# non-blocking I/O (O_NONBLOCK) doesn't work or is unimplemented.
+# On these systems (mostly older ones), use the old BSD-style
+# FIONBIO approach instead.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines some of the following vars:
+# HAVE_SYS_IOCTL_H
+# HAVE_SYS_FILIO_H
+# USE_FIONBIO
+# O_NONBLOCK
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_BLOCKING_STYLE], [
+ AC_CHECK_HEADERS(sys/ioctl.h)
+ AC_CHECK_HEADERS(sys/filio.h)
+ TEA_CONFIG_SYSTEM
+ AC_MSG_CHECKING([FIONBIO vs. O_NONBLOCK for nonblocking I/O])
+ case $system in
+ # There used to be code here to use FIONBIO under AIX. However, it
+ # was reported that FIONBIO doesn't work under AIX 3.2.5. Since
+ # using O_NONBLOCK seems fine under AIX 4.*, I removed the FIONBIO
+ # code (JO, 5/31/97).
+
+ OSF*)
+ AC_DEFINE(USE_FIONBIO, 1, [Should we use FIONBIO?])
+ AC_MSG_RESULT([FIONBIO])
+ ;;
+ SunOS-4*)
+ AC_DEFINE(USE_FIONBIO, 1, [Should we use FIONBIO?])
+ AC_MSG_RESULT([FIONBIO])
+ ;;
+ *)
+ AC_MSG_RESULT([O_NONBLOCK])
+ ;;
+ esac
+])
+
+#--------------------------------------------------------------------
+# TEA_TIME_HANLDER
+#
+# Checks how the system deals with time.h, what time structures
+# are used on the system, and what fields the structures have.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines some of the following vars:
+# USE_DELTA_FOR_TZ
+# HAVE_TM_GMTOFF
+# HAVE_TM_TZADJ
+# HAVE_TIMEZONE_VAR
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TIME_HANDLER], [
+ AC_CHECK_HEADERS(sys/time.h)
+ AC_HEADER_TIME
+ AC_STRUCT_TIMEZONE
+
+ AC_CHECK_FUNCS(gmtime_r localtime_r)
+
+ AC_CACHE_CHECK([tm_tzadj in struct tm], tcl_cv_member_tm_tzadj,
+ AC_TRY_COMPILE([#include <time.h>], [struct tm tm; tm.tm_tzadj;],
+ tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no))
+ if test $tcl_cv_member_tm_tzadj = yes ; then
+ AC_DEFINE(HAVE_TM_TZADJ, 1, [Should we use the tm_tzadj field of struct tm?])
+ fi
+
+ AC_CACHE_CHECK([tm_gmtoff in struct tm], tcl_cv_member_tm_gmtoff,
+ AC_TRY_COMPILE([#include <time.h>], [struct tm tm; tm.tm_gmtoff;],
+ tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no))
+ if test $tcl_cv_member_tm_gmtoff = yes ; then
+ AC_DEFINE(HAVE_TM_GMTOFF, 1, [Should we use the tm_gmtoff field of struct tm?])
+ fi
+
+ #
+ # Its important to include time.h in this check, as some systems
+ # (like convex) have timezone functions, etc.
+ #
+ AC_CACHE_CHECK([long timezone variable], tcl_cv_timezone_long,
+ AC_TRY_COMPILE([#include <time.h>],
+ [extern long timezone;
+ timezone += 1;
+ exit (0);],
+ tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no))
+ if test $tcl_cv_timezone_long = yes ; then
+ AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
+ else
+ #
+ # On some systems (eg IRIX 6.2), timezone is a time_t and not a long.
+ #
+ AC_CACHE_CHECK([time_t timezone variable], tcl_cv_timezone_time,
+ AC_TRY_COMPILE([#include <time.h>],
+ [extern time_t timezone;
+ timezone += 1;
+ exit (0);],
+ tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no))
+ if test $tcl_cv_timezone_time = yes ; then
+ AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
+ fi
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_BUGGY_STRTOD
+#
+# Under Solaris 2.4, strtod returns the wrong value for the
+# terminating character under some conditions. Check for this
+# and if the problem exists use a substitute procedure
+# "fixstrtod" (provided by Tcl) that corrects the error.
+# Also, on Compaq's Tru64 Unix 5.0,
+# strtod(" ") returns 0.0 instead of a failure to convert.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Might defines some of the following vars:
+# strtod (=fixstrtod)
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_BUGGY_STRTOD], [
+ AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0)
+ if test "$tcl_strtod" = 1; then
+ AC_CACHE_CHECK([for Solaris2.4/Tru64 strtod bugs], tcl_cv_strtod_buggy,[
+ AC_TRY_RUN([
+ extern double strtod();
+ int main() {
+ char *infString="Inf", *nanString="NaN", *spaceString=" ";
+ char *term;
+ double value;
+ value = strtod(infString, &term);
+ if ((term != infString) && (term[-1] == 0)) {
+ exit(1);
+ }
+ value = strtod(nanString, &term);
+ if ((term != nanString) && (term[-1] == 0)) {
+ exit(1);
+ }
+ value = strtod(spaceString, &term);
+ if (term == (spaceString+1)) {
+ exit(1);
+ }
+ exit(0);
+ }], tcl_cv_strtod_buggy=ok, tcl_cv_strtod_buggy=buggy,
+ tcl_cv_strtod_buggy=buggy)])
+ if test "$tcl_cv_strtod_buggy" = buggy; then
+ AC_LIBOBJ([fixstrtod])
+ USE_COMPAT=1
+ AC_DEFINE(strtod, fixstrtod, [Do we want to use the strtod() in compat?])
+ fi
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_LINK_LIBS
+#
+# Search for the libraries needed to link the Tcl shell.
+# Things like the math library (-lm) and socket stuff (-lsocket vs.
+# -lnsl) are dealt with here.
+#
+# Arguments:
+# Requires the following vars to be set in the Makefile:
+# DL_LIBS
+# LIBS
+# MATH_LIBS
+#
+# Results:
+#
+# Subst's the following var:
+# TCL_LIBS
+# MATH_LIBS
+#
+# Might append to the following vars:
+# LIBS
+#
+# Might define the following vars:
+# HAVE_NET_ERRNO_H
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_LINK_LIBS], [
+ #--------------------------------------------------------------------
+ # On a few very rare systems, all of the libm.a stuff is
+ # already in libc.a. Set compiler flags accordingly.
+ # Also, Linux requires the "ieee" library for math to work
+ # right (and it must appear before "-lm").
+ #--------------------------------------------------------------------
+
+ AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm")
+ AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
+
+ #--------------------------------------------------------------------
+ # Interactive UNIX requires -linet instead of -lsocket, plus it
+ # needs net/errno.h to define the socket-related error codes.
+ #--------------------------------------------------------------------
+
+ AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"])
+ AC_CHECK_HEADER(net/errno.h, [
+ AC_DEFINE(HAVE_NET_ERRNO_H, 1, [Do we have <net/errno.h>?])])
+
+ #--------------------------------------------------------------------
+ # Check for the existence of the -lsocket and -lnsl libraries.
+ # The order here is important, so that they end up in the right
+ # order in the command line generated by make. Here are some
+ # special considerations:
+ # 1. Use "connect" and "accept" to check for -lsocket, and
+ # "gethostbyname" to check for -lnsl.
+ # 2. Use each function name only once: can't redo a check because
+ # autoconf caches the results of the last check and won't redo it.
+ # 3. Use -lnsl and -lsocket only if they supply procedures that
+ # aren't already present in the normal libraries. This is because
+ # IRIX 5.2 has libraries, but they aren't needed and they're
+ # bogus: they goof up name resolution if used.
+ # 4. On some SVR4 systems, can't use -lsocket without -lnsl too.
+ # To get around this problem, check for both libraries together
+ # if -lsocket doesn't work by itself.
+ #--------------------------------------------------------------------
+
+ tcl_checkBoth=0
+ AC_CHECK_FUNC(connect, tcl_checkSocket=0, tcl_checkSocket=1)
+ if test "$tcl_checkSocket" = 1; then
+ AC_CHECK_FUNC(setsockopt, , [AC_CHECK_LIB(socket, setsockopt,
+ LIBS="$LIBS -lsocket", tcl_checkBoth=1)])
+ fi
+ if test "$tcl_checkBoth" = 1; then
+ tk_oldLibs=$LIBS
+ LIBS="$LIBS -lsocket -lnsl"
+ AC_CHECK_FUNC(accept, tcl_checkNsl=0, [LIBS=$tk_oldLibs])
+ fi
+ AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname,
+ [LIBS="$LIBS -lnsl"])])
+
+ # Don't perform the eval of the libraries here because DL_LIBS
+ # won't be set until we call TEA_CONFIG_CFLAGS
+
+ TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'
+ AC_SUBST(TCL_LIBS)
+ AC_SUBST(MATH_LIBS)
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_EARLY_FLAGS
+#
+# Check for what flags are needed to be passed so the correct OS
+# features are available.
+#
+# Arguments:
+# None
+#
+# Results:
+#
+# Might define the following vars:
+# _ISOC99_SOURCE
+# _LARGEFILE64_SOURCE
+# _LARGEFILE_SOURCE64
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_EARLY_FLAG],[
+ AC_CACHE_VAL([tcl_cv_flag_]translit($1,[A-Z],[a-z]),
+ AC_TRY_COMPILE([$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no,
+ AC_TRY_COMPILE([[#define ]$1[ 1
+]$2], $3,
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes,
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no)))
+ if test ["x${tcl_cv_flag_]translit($1,[A-Z],[a-z])[}" = "xyes"] ; then
+ AC_DEFINE($1, 1, [Add the ]$1[ flag when building])
+ tcl_flags="$tcl_flags $1"
+ fi
+])
+
+AC_DEFUN([TEA_TCL_EARLY_FLAGS],[
+ AC_MSG_CHECKING([for required early compiler flags])
+ tcl_flags=""
+ TEA_TCL_EARLY_FLAG(_ISOC99_SOURCE,[#include <stdlib.h>],
+ [char *p = (char *)strtoll; char *q = (char *)strtoull;])
+ TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include <sys/stat.h>],
+ [struct stat64 buf; int i = stat64("/", &buf);])
+ TEA_TCL_EARLY_FLAG(_LARGEFILE_SOURCE64,[#include <sys/stat.h>],
+ [char *p = (char *)open64;])
+ if test "x${tcl_flags}" = "x" ; then
+ AC_MSG_RESULT([none])
+ else
+ AC_MSG_RESULT([${tcl_flags}])
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_64BIT_FLAGS
+#
+# Check for what is defined in the way of 64-bit features.
+#
+# Arguments:
+# None
+#
+# Results:
+#
+# Might define the following vars:
+# TCL_WIDE_INT_IS_LONG
+# TCL_WIDE_INT_TYPE
+# HAVE_STRUCT_DIRENT64
+# HAVE_STRUCT_STAT64
+# HAVE_TYPE_OFF64_T
+#
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
+ AC_MSG_CHECKING([for 64-bit integer type])
+ AC_CACHE_VAL(tcl_cv_type_64bit,[
+ tcl_cv_type_64bit=none
+ # See if the compiler knows natively about __int64
+ AC_TRY_COMPILE(,[__int64 value = (__int64) 0;],
+ tcl_type_64bit=__int64, tcl_type_64bit="long long")
+ # See if we should use long anyway Note that we substitute in the
+ # type that is our current guess for a 64-bit type inside this check
+ # program, so it should be modified only carefully...
+ AC_TRY_COMPILE(,[switch (0) {
+ case 1: case (sizeof(]${tcl_type_64bit}[)==sizeof(long)): ;
+ }],tcl_cv_type_64bit=${tcl_type_64bit})])
+ if test "${tcl_cv_type_64bit}" = none ; then
+ AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Are wide integers to be implemented with C 'long's?])
+ AC_MSG_RESULT([using long])
+ elif test "${tcl_cv_type_64bit}" = "__int64" \
+ -a "${TEA_PLATFORM}" = "windows" ; then
+ # We actually want to use the default tcl.h checks in this
+ # case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER*
+ AC_MSG_RESULT([using Tcl header defaults])
+ else
+ AC_DEFINE_UNQUOTED(TCL_WIDE_INT_TYPE,${tcl_cv_type_64bit},
+ [What type should be used to define wide integers?])
+ AC_MSG_RESULT([${tcl_cv_type_64bit}])
+
+ # Now check for auxiliary declarations
+ AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[
+ AC_TRY_COMPILE([#include <sys/types.h>
+#include <sys/dirent.h>],[struct dirent64 p;],
+ tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)])
+ if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in <sys/types.h>?])
+ fi
+
+ AC_CACHE_CHECK([for struct stat64], tcl_cv_struct_stat64,[
+ AC_TRY_COMPILE([#include <sys/stat.h>],[struct stat64 p;
+],
+ tcl_cv_struct_stat64=yes,tcl_cv_struct_stat64=no)])
+ if test "x${tcl_cv_struct_stat64}" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_STAT64, 1, [Is 'struct stat64' in <sys/stat.h>?])
+ fi
+
+ AC_CHECK_FUNCS(open64 lseek64)
+ AC_MSG_CHECKING([for off64_t])
+ AC_CACHE_VAL(tcl_cv_type_off64_t,[
+ AC_TRY_COMPILE([#include <sys/types.h>],[off64_t offset;
+],
+ tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)])
+ dnl Define HAVE_TYPE_OFF64_T only when the off64_t type and the
+ dnl functions lseek64 and open64 are defined.
+ if test "x${tcl_cv_type_off64_t}" = "xyes" && \
+ test "x${ac_cv_func_lseek64}" = "xyes" && \
+ test "x${ac_cv_func_open64}" = "xyes" ; then
+ AC_DEFINE(HAVE_TYPE_OFF64_T, 1, [Is off64_t in <sys/types.h>?])
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ fi
+])
+
+##
+## Here ends the standard Tcl configuration bits and starts the
+## TEA specific functions
+##
+
+#------------------------------------------------------------------------
+# TEA_INIT --
+#
+# Init various Tcl Extension Architecture (TEA) variables.
+# This should be the first called TEA_* macro.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines and substs the following vars:
+# CYGPATH
+# EXEEXT
+# Defines only:
+# TEA_VERSION
+# TEA_INITED
+# TEA_PLATFORM (windows or unix)
+#
+# "cygpath" is used on windows to generate native path names for include
+# files. These variables should only be used with the compiler and linker
+# since they generate native path names.
+#
+# EXEEXT
+# Select the executable extension based on the host type. This
+# is a lightweight replacement for AC_EXEEXT that doesn't require
+# a compiler.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_INIT], [
+ # TEA extensions pass this us the version of TEA they think they
+ # are compatible with.
+ TEA_VERSION="3.5"
+
+ AC_MSG_CHECKING([for correct TEA configuration])
+ if test x"${PACKAGE_NAME}" = x ; then
+ AC_MSG_ERROR([
+The PACKAGE_NAME variable must be defined by your TEA configure.in])
+ fi
+ if test x"$1" = x ; then
+ AC_MSG_ERROR([
+TEA version not specified.])
+ elif test "$1" != "${TEA_VERSION}" ; then
+ AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"])
+ else
+ AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
+ fi
+ case "`uname -s`" in
+ *win32*|*WIN32*|*CYGWIN_NT*|*CYGWIN_9*|*CYGWIN_ME*|*MINGW32_*)
+ AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo)
+ EXEEXT=".exe"
+ TEA_PLATFORM="windows"
+ ;;
+ *)
+ CYGPATH=echo
+ EXEEXT=""
+ TEA_PLATFORM="unix"
+ ;;
+ esac
+
+ # Check if exec_prefix is set. If not use fall back to prefix.
+ # Note when adjusted, so that TEA_PREFIX can correct for this.
+ # This is needed for recursive configures, since autoconf propagates
+ # $prefix, but not $exec_prefix (doh!).
+ if test x$exec_prefix = xNONE ; then
+ exec_prefix_default=yes
+ exec_prefix=$prefix
+ fi
+
+ AC_SUBST(EXEEXT)
+ AC_SUBST(CYGPATH)
+
+ # This package name must be replaced statically for AC_SUBST to work
+ AC_SUBST(PKG_LIB_FILE)
+ # Substitute STUB_LIB_FILE in case package creates a stub library too.
+ AC_SUBST(PKG_STUB_LIB_FILE)
+
+ # We AC_SUBST these here to ensure they are subst'ed,
+ # in case the user doesn't call TEA_ADD_...
+ AC_SUBST(PKG_STUB_SOURCES)
+ AC_SUBST(PKG_STUB_OBJECTS)
+ AC_SUBST(PKG_TCL_SOURCES)
+ AC_SUBST(PKG_HEADERS)
+ AC_SUBST(PKG_INCLUDES)
+ AC_SUBST(PKG_LIBS)
+ AC_SUBST(PKG_CFLAGS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_SOURCES --
+#
+# Specify one or more source files. Users should check for
+# the right platform before adding to their list.
+# It is not important to specify the directory, as long as it is
+# in the generic, win or unix subdirectory of $(srcdir).
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_SOURCES
+# PKG_OBJECTS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_SOURCES], [
+ vars="$@"
+ for i in $vars; do
+ case $i in
+ [\$]*)
+ # allow $-var names
+ PKG_SOURCES="$PKG_SOURCES $i"
+ PKG_OBJECTS="$PKG_OBJECTS $i"
+ ;;
+ *)
+ # check for existence - allows for generic/win/unix VPATH
+ if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+ ; then
+ AC_MSG_ERROR([could not find source file '$i'])
+ fi
+ PKG_SOURCES="$PKG_SOURCES $i"
+ # this assumes it is in a VPATH dir
+ i=`basename $i`
+ # handle user calling this before or after TEA_SETUP_COMPILER
+ if test x"${OBJEXT}" != x ; then
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}"
+ else
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}"
+ fi
+ PKG_OBJECTS="$PKG_OBJECTS $j"
+ ;;
+ esac
+ done
+ AC_SUBST(PKG_SOURCES)
+ AC_SUBST(PKG_OBJECTS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_STUB_SOURCES --
+#
+# Specify one or more source files. Users should check for
+# the right platform before adding to their list.
+# It is not important to specify the directory, as long as it is
+# in the generic, win or unix subdirectory of $(srcdir).
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_STUB_SOURCES
+# PKG_STUB_OBJECTS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_STUB_SOURCES], [
+ vars="$@"
+ for i in $vars; do
+ # check for existence - allows for generic/win/unix VPATH
+ if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+ ; then
+ AC_MSG_ERROR([could not find stub source file '$i'])
+ fi
+ PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i"
+ # this assumes it is in a VPATH dir
+ i=`basename $i`
+ # handle user calling this before or after TEA_SETUP_COMPILER
+ if test x"${OBJEXT}" != x ; then
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}"
+ else
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}"
+ fi
+ PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j"
+ done
+ AC_SUBST(PKG_STUB_SOURCES)
+ AC_SUBST(PKG_STUB_OBJECTS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_TCL_SOURCES --
+#
+# Specify one or more Tcl source files. These should be platform
+# independent runtime files.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_TCL_SOURCES
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_TCL_SOURCES], [
+ vars="$@"
+ for i in $vars; do
+ # check for existence, be strict because it is installed
+ if test ! -f "${srcdir}/$i" ; then
+ AC_MSG_ERROR([could not find tcl source file '${srcdir}/$i'])
+ fi
+ PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i"
+ done
+ AC_SUBST(PKG_TCL_SOURCES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_HEADERS --
+#
+# Specify one or more source headers. Users should check for
+# the right platform before adding to their list.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_HEADERS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_HEADERS], [
+ vars="$@"
+ for i in $vars; do
+ # check for existence, be strict because it is installed
+ if test ! -f "${srcdir}/$i" ; then
+ AC_MSG_ERROR([could not find header file '${srcdir}/$i'])
+ fi
+ PKG_HEADERS="$PKG_HEADERS $i"
+ done
+ AC_SUBST(PKG_HEADERS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_INCLUDES --
+#
+# Specify one or more include dirs. Users should check for
+# the right platform before adding to their list.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_INCLUDES
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_INCLUDES], [
+ vars="$@"
+ for i in $vars; do
+ PKG_INCLUDES="$PKG_INCLUDES $i"
+ done
+ AC_SUBST(PKG_INCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_LIBS --
+#
+# Specify one or more libraries. Users should check for
+# the right platform before adding to their list. For Windows,
+# libraries provided in "foo.lib" format will be converted to
+# "-lfoo" when using GCC (mingw).
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_LIBS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_LIBS], [
+ vars="$@"
+ for i in $vars; do
+ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
+ # Convert foo.lib to -lfoo for GCC. No-op if not *.lib
+ i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.lib[$]/-l\1/i'`
+ fi
+ PKG_LIBS="$PKG_LIBS $i"
+ done
+ AC_SUBST(PKG_LIBS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_CFLAGS --
+#
+# Specify one or more CFLAGS. Users should check for
+# the right platform before adding to their list.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_CFLAGS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_CFLAGS], [
+ PKG_CFLAGS="$PKG_CFLAGS $@"
+ AC_SUBST(PKG_CFLAGS)
+])
+
+#------------------------------------------------------------------------
+# TEA_PREFIX --
+#
+# Handle the --prefix=... option by defaulting to what Tcl gave
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# If --prefix or --exec-prefix was not specified, $prefix and
+# $exec_prefix will be set to the values given to Tcl when it was
+# configured.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_PREFIX], [
+ if test "${prefix}" = "NONE"; then
+ prefix_default=yes
+ if test x"${TCL_PREFIX}" != x; then
+ AC_MSG_NOTICE([--prefix defaulting to TCL_PREFIX ${TCL_PREFIX}])
+ prefix=${TCL_PREFIX}
+ else
+ AC_MSG_NOTICE([--prefix defaulting to /usr/local])
+ prefix=/usr/local
+ fi
+ fi
+ if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \
+ -o x"${exec_prefix_default}" = x"yes" ; then
+ if test x"${TCL_EXEC_PREFIX}" != x; then
+ AC_MSG_NOTICE([--exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}])
+ exec_prefix=${TCL_EXEC_PREFIX}
+ else
+ AC_MSG_NOTICE([--exec-prefix defaulting to ${prefix}])
+ exec_prefix=$prefix
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_SETUP_COMPILER_CC --
+#
+# Do compiler checks the way we want. This is just a replacement
+# for AC_PROG_CC in TEA configure.in files to make them cleaner.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Sets up CC var and other standard bits we need to make executables.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_SETUP_COMPILER_CC], [
+ # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE)
+ # in this macro, they need to go into TEA_SETUP_COMPILER instead.
+
+ # If the user did not set CFLAGS, set it now to keep
+ # the AC_PROG_CC macro from adding "-g -O2".
+ if test "${CFLAGS+set}" != "set" ; then
+ CFLAGS=""
+ fi
+
+ AC_PROG_CC
+ AC_PROG_CPP
+
+ AC_PROG_INSTALL
+
+ #--------------------------------------------------------------------
+ # Checks to see if the make program sets the $MAKE variable.
+ #--------------------------------------------------------------------
+
+ AC_PROG_MAKE_SET
+
+ #--------------------------------------------------------------------
+ # Find ranlib
+ #--------------------------------------------------------------------
+
+ AC_PROG_LIBTOOL
+
+ #--------------------------------------------------------------------
+ # Determines the correct binary file extension (.o, .obj, .exe etc.)
+ #--------------------------------------------------------------------
+
+ AC_OBJEXT
+ AC_EXEEXT
+])
+
+#------------------------------------------------------------------------
+# TEA_SETUP_COMPILER --
+#
+# Do compiler checks that use the compiler. This must go after
+# TEA_SETUP_COMPILER_CC, which does the actual compiler check.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Sets up CC var and other standard bits we need to make executables.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_SETUP_COMPILER], [
+ # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here.
+ AC_REQUIRE([TEA_SETUP_COMPILER_CC])
+
+ #------------------------------------------------------------------------
+ # If we're using GCC, see if the compiler understands -pipe. If so, use it.
+ # It makes compiling go faster. (This is only a performance feature.)
+ #------------------------------------------------------------------------
+
+ if test -z "$no_pipe" -a -n "$GCC"; then
+ AC_MSG_CHECKING([if the compiler understands -pipe])
+ OLDCC="$CC"
+ CC="$CC -pipe"
+ AC_TRY_COMPILE(,, AC_MSG_RESULT([yes]), CC="$OLDCC"
+ AC_MSG_RESULT([no]))
+ fi
+
+ #--------------------------------------------------------------------
+ # Common compiler flag setup
+ #--------------------------------------------------------------------
+
+ AC_C_BIGENDIAN
+ if test "${TEA_PLATFORM}" = "unix" ; then
+ TEA_TCL_LINK_LIBS
+ TEA_MISSING_POSIX_HEADERS
+ # Let the user call this, because if it triggers, they will
+ # need a compat/strtod.c that is correct. Users can also
+ # use Tcl_GetDouble(FromObj) instead.
+ #TEA_BUGGY_STRTOD
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_MAKE_LIB --
+#
+# Generate a line that can be used to build a shared/unshared library
+# in a platform independent manner.
+#
+# Arguments:
+# none
+#
+# Requires:
+#
+# Results:
+#
+# Defines the following vars:
+# CFLAGS - Done late here to note disturb other AC macros
+# MAKE_LIB - Command to execute to build the Tcl library;
+# differs depending on whether or not Tcl is being
+# compiled as a shared library.
+# MAKE_SHARED_LIB Makefile rule for building a shared library
+# MAKE_STATIC_LIB Makefile rule for building a static library
+# MAKE_STUB_LIB Makefile rule for building a stub library
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_MAKE_LIB], [
+ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then
+ MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)"
+ MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)"
+ MAKE_STUB_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_STUB_OBJECTS)"
+ else
+ MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)"
+ MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
+ MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(PKG_STUB_OBJECTS)"
+ fi
+
+ if test "${SHARED_BUILD}" = "1" ; then
+ MAKE_LIB="${MAKE_SHARED_LIB} "
+ else
+ MAKE_LIB="${MAKE_STATIC_LIB} "
+ fi
+
+ #--------------------------------------------------------------------
+ # Shared libraries and static libraries have different names.
+ # Use the double eval to make sure any variables in the suffix is
+ # substituted. (@@@ Might not be necessary anymore)
+ #--------------------------------------------------------------------
+
+ if test "${TEA_PLATFORM}" = "windows" ; then
+ if test "${SHARED_BUILD}" = "1" ; then
+ # We force the unresolved linking of symbols that are really in
+ # the private libraries of Tcl and Tk.
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\""
+ if test x"${TK_BIN_DIR}" != x ; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\""
+ fi
+ eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ else
+ eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ fi
+ # Some packages build their own stubs libraries
+ eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ if test "$GCC" = "yes"; then
+ PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE}
+ fi
+ # These aren't needed on Windows (either MSVC or gcc)
+ RANLIB=:
+ RANLIB_STUB=:
+ else
+ RANLIB_STUB="${RANLIB}"
+ if test "${SHARED_BUILD}" = "1" ; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}"
+ if test x"${TK_BIN_DIR}" != x ; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
+ fi
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ RANLIB=:
+ else
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ fi
+ # Some packages build their own stubs libraries
+ eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ fi
+
+ # These are escaped so that only CFLAGS is picked up at configure time.
+ # The other values will be substituted at make time.
+ CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}"
+ if test "${SHARED_BUILD}" = "1" ; then
+ CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}"
+ fi
+
+ AC_SUBST(MAKE_LIB)
+ AC_SUBST(MAKE_SHARED_LIB)
+ AC_SUBST(MAKE_STATIC_LIB)
+ AC_SUBST(MAKE_STUB_LIB)
+ AC_SUBST(RANLIB_STUB)
+])
+
+#------------------------------------------------------------------------
+# TEA_LIB_SPEC --
+#
+# Compute the name of an existing object library located in libdir
+# from the given base name and produce the appropriate linker flags.
+#
+# Arguments:
+# basename The base name of the library without version
+# numbers, extensions, or "lib" prefixes.
+# extra_dir Extra directory in which to search for the
+# library. This location is used first, then
+# $prefix/$exec-prefix, then some defaults.
+#
+# Requires:
+# TEA_INIT and TEA_PREFIX must be called first.
+#
+# Results:
+#
+# Defines the following vars:
+# ${basename}_LIB_NAME The computed library name.
+# ${basename}_LIB_SPEC The computed linker flags.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LIB_SPEC], [
+ AC_MSG_CHECKING([for $1 library])
+
+ # Look in exec-prefix for the library (defined by TEA_PREFIX).
+
+ tea_lib_name_dir="${exec_prefix}/lib"
+
+ # Or in a user-specified location.
+
+ if test x"$2" != x ; then
+ tea_extra_lib_dir=$2
+ else
+ tea_extra_lib_dir=NONE
+ fi
+
+ for i in \
+ `ls -dr ${tea_extra_lib_dir}/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr ${tea_extra_lib_dir}/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr ${tea_lib_name_dir}/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr ${tea_lib_name_dir}/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr /usr/lib/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr /usr/lib/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr /usr/local/lib/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr /usr/local/lib/lib$1[[0-9]]* 2>/dev/null ` ; do
+ if test -f "$i" ; then
+ tea_lib_name_dir=`dirname $i`
+ $1_LIB_NAME=`basename $i`
+ $1_LIB_PATH_NAME=$i
+ break
+ fi
+ done
+
+ if test "${TEA_PLATFORM}" = "windows"; then
+ $1_LIB_SPEC=\"`${CYGPATH} ${$1_LIB_PATH_NAME} 2>/dev/null`\"
+ else
+ # Strip off the leading "lib" and trailing ".a" or ".so"
+
+ tea_lib_name_lib=`echo ${$1_LIB_NAME}|sed -e 's/^lib//' -e 's/\.[[^.]]*$//' -e 's/\.so.*//'`
+ $1_LIB_SPEC="-L${tea_lib_name_dir} -l${tea_lib_name_lib}"
+ fi
+
+ if test "x${$1_LIB_NAME}" = x ; then
+ AC_MSG_ERROR([not found])
+ else
+ AC_MSG_RESULT([${$1_LIB_SPEC}])
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PRIVATE_TCL_HEADERS --
+#
+# Locate the private Tcl include files
+#
+# Arguments:
+#
+# Requires:
+# TCL_SRC_DIR Assumes that TEA_LOAD_TCLCONFIG has
+# already been called.
+#
+# Results:
+#
+# Substs the following vars:
+# TCL_TOP_DIR_NATIVE
+# TCL_GENERIC_DIR_NATIVE
+# TCL_UNIX_DIR_NATIVE
+# TCL_WIN_DIR_NATIVE
+# TCL_BMAP_DIR_NATIVE
+# TCL_TOOL_DIR_NATIVE
+# TCL_PLATFORM_DIR_NATIVE
+# TCL_BIN_DIR_NATIVE
+# TCL_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [
+ AC_MSG_CHECKING([for Tcl private include files])
+
+ TCL_SRC_DIR_NATIVE=`${CYGPATH} ${TCL_SRC_DIR}`
+ TCL_TOP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}\"
+ TCL_GENERIC_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/generic\"
+ TCL_UNIX_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/unix\"
+ TCL_WIN_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/win\"
+ TCL_BMAP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/bitmaps\"
+ TCL_TOOL_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/tools\"
+ TCL_COMPAT_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/compat\"
+
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCL_PLATFORM_DIR_NATIVE=${TCL_WIN_DIR_NATIVE}
+ else
+ TCL_PLATFORM_DIR_NATIVE=${TCL_UNIX_DIR_NATIVE}
+ fi
+ # We want to ensure these are substituted so as not to require
+ # any *_NATIVE vars be defined in the Makefile
+ TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}"
+ if test "`uname -s`" = "Darwin"; then
+ # If Tcl was built as a framework, attempt to use
+ # the framework's Headers and PrivateHeaders directories
+ case ${TCL_DEFS} in
+ *TCL_FRAMEWORK*)
+ if test -d "${TCL_BIN_DIR}/Headers" -a -d "${TCL_BIN_DIR}/PrivateHeaders"; then
+ TCL_INCLUDES="-I\"${TCL_BIN_DIR}/Headers\" -I\"${TCL_BIN_DIR}/PrivateHeaders\" ${TCL_INCLUDES}"; else
+ TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`"; fi
+ ;;
+ esac
+ fi
+
+ AC_SUBST(TCL_TOP_DIR_NATIVE)
+ AC_SUBST(TCL_GENERIC_DIR_NATIVE)
+ AC_SUBST(TCL_UNIX_DIR_NATIVE)
+ AC_SUBST(TCL_WIN_DIR_NATIVE)
+ AC_SUBST(TCL_BMAP_DIR_NATIVE)
+ AC_SUBST(TCL_TOOL_DIR_NATIVE)
+ AC_SUBST(TCL_PLATFORM_DIR_NATIVE)
+
+ AC_SUBST(TCL_INCLUDES)
+ AC_MSG_RESULT([Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}])
+])
+
+#------------------------------------------------------------------------
+# TEA_PUBLIC_TCL_HEADERS --
+#
+# Locate the installed public Tcl header files
+#
+# Arguments:
+# None.
+#
+# Requires:
+# CYGPATH must be set
+#
+# Results:
+#
+# Adds a --with-tclinclude switch to configure.
+# Result is cached.
+#
+# Substs the following vars:
+# TCL_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PUBLIC_TCL_HEADERS], [
+ AC_MSG_CHECKING([for Tcl public headers])
+
+ AC_ARG_WITH(tclinclude, [ --with-tclinclude directory containing the public Tcl header files], with_tclinclude=${withval})
+
+ AC_CACHE_VAL(ac_cv_c_tclh, [
+ # Use the value from --with-tclinclude, if it was given
+
+ if test x"${with_tclinclude}" != x ; then
+ if test -f "${with_tclinclude}/tcl.h" ; then
+ ac_cv_c_tclh=${with_tclinclude}
+ else
+ AC_MSG_ERROR([${with_tclinclude} directory does not contain tcl.h])
+ fi
+ else
+ if test "`uname -s`" = "Darwin"; then
+ # If Tcl was built as a framework, attempt to use
+ # the framework's Headers directory
+ case ${TCL_DEFS} in
+ *TCL_FRAMEWORK*)
+ list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`"
+ ;;
+ esac
+ fi
+
+ # Look in the source dir only if Tcl is not installed,
+ # and in that situation, look there before installed locations.
+ if test -f "${TCL_BIN_DIR}/Makefile" ; then
+ list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`"
+ fi
+
+ # Check order: pkg --prefix location, Tcl's --prefix location,
+ # relative to directory of tclConfig.sh.
+
+ eval "temp_includedir=${includedir}"
+ list="$list \
+ `ls -d ${temp_includedir} 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+ if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+ list="$list /usr/local/include /usr/include"
+ if test x"${TCL_INCLUDE_SPEC}" != x ; then
+ d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'`
+ list="$list `ls -d ${d} 2>/dev/null`"
+ fi
+ fi
+ for i in $list ; do
+ if test -f "$i/tcl.h" ; then
+ ac_cv_c_tclh=$i
+ break
+ fi
+ done
+ fi
+ ])
+
+ # Print a message based on how we determined the include path
+
+ if test x"${ac_cv_c_tclh}" = x ; then
+ AC_MSG_ERROR([tcl.h not found. Please specify its location with --with-tclinclude])
+ else
+ AC_MSG_RESULT([${ac_cv_c_tclh}])
+ fi
+
+ # Convert to a native path and substitute into the output files.
+
+ INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}`
+
+ TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+ AC_SUBST(TCL_INCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_PRIVATE_TK_HEADERS --
+#
+# Locate the private Tk include files
+#
+# Arguments:
+#
+# Requires:
+# TK_SRC_DIR Assumes that TEA_LOAD_TKCONFIG has
+# already been called.
+#
+# Results:
+#
+# Substs the following vars:
+# TK_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PRIVATE_TK_HEADERS], [
+ AC_MSG_CHECKING([for Tk private include files])
+
+ TK_SRC_DIR_NATIVE=`${CYGPATH} ${TK_SRC_DIR}`
+ TK_TOP_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}\"
+ TK_UNIX_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/unix\"
+ TK_WIN_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/win\"
+ TK_GENERIC_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/generic\"
+ TK_XLIB_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/xlib\"
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TK_PLATFORM_DIR_NATIVE=${TK_WIN_DIR_NATIVE}
+ else
+ TK_PLATFORM_DIR_NATIVE=${TK_UNIX_DIR_NATIVE}
+ fi
+ # We want to ensure these are substituted so as not to require
+ # any *_NATIVE vars be defined in the Makefile
+ TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}"
+ if test "${TEA_WINDOWINGSYSTEM}" = "win32" \
+ -o "${TEA_WINDOWINGSYSTEM}" = "aqua"; then
+ TK_INCLUDES="${TK_INCLUDES} -I${TK_XLIB_DIR_NATIVE}"
+ fi
+ if test "${TEA_WINDOWINGSYSTEM}" = "aqua"; then
+ TK_INCLUDES="${TK_INCLUDES} -I${TK_SRC_DIR_NATIVE}/macosx"
+ fi
+ if test "`uname -s`" = "Darwin"; then
+ # If Tk was built as a framework, attempt to use
+ # the framework's Headers and PrivateHeaders directories
+ case ${TK_DEFS} in
+ *TK_FRAMEWORK*)
+ if test -d "${TK_BIN_DIR}/Headers" -a -d "${TK_BIN_DIR}/PrivateHeaders"; then
+ TK_INCLUDES="-I\"${TK_BIN_DIR}/Headers\" -I\"${TK_BIN_DIR}/PrivateHeaders\" ${TK_INCLUDES}"; fi
+ ;;
+ esac
+ fi
+
+ AC_SUBST(TK_TOP_DIR_NATIVE)
+ AC_SUBST(TK_UNIX_DIR_NATIVE)
+ AC_SUBST(TK_WIN_DIR_NATIVE)
+ AC_SUBST(TK_GENERIC_DIR_NATIVE)
+ AC_SUBST(TK_XLIB_DIR_NATIVE)
+ AC_SUBST(TK_PLATFORM_DIR_NATIVE)
+
+ AC_SUBST(TK_INCLUDES)
+ AC_MSG_RESULT([Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}])
+])
+
+#------------------------------------------------------------------------
+# TEA_PUBLIC_TK_HEADERS --
+#
+# Locate the installed public Tk header files
+#
+# Arguments:
+# None.
+#
+# Requires:
+# CYGPATH must be set
+#
+# Results:
+#
+# Adds a --with-tkinclude switch to configure.
+# Result is cached.
+#
+# Substs the following vars:
+# TK_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PUBLIC_TK_HEADERS], [
+ AC_MSG_CHECKING([for Tk public headers])
+
+ AC_ARG_WITH(tkinclude, [ --with-tkinclude directory containing the public Tk header files], with_tkinclude=${withval})
+
+ AC_CACHE_VAL(ac_cv_c_tkh, [
+ # Use the value from --with-tkinclude, if it was given
+
+ if test x"${with_tkinclude}" != x ; then
+ if test -f "${with_tkinclude}/tk.h" ; then
+ ac_cv_c_tkh=${with_tkinclude}
+ else
+ AC_MSG_ERROR([${with_tkinclude} directory does not contain tk.h])
+ fi
+ else
+ if test "`uname -s`" = "Darwin"; then
+ # If Tk was built as a framework, attempt to use
+ # the framework's Headers directory.
+ case ${TK_DEFS} in
+ *TK_FRAMEWORK*)
+ list="`ls -d ${TK_BIN_DIR}/Headers 2>/dev/null`"
+ ;;
+ esac
+ fi
+
+ # Look in the source dir only if Tk is not installed,
+ # and in that situation, look there before installed locations.
+ if test -f "${TK_BIN_DIR}/Makefile" ; then
+ list="$list `ls -d ${TK_SRC_DIR}/generic 2>/dev/null`"
+ fi
+
+ # Check order: pkg --prefix location, Tk's --prefix location,
+ # relative to directory of tkConfig.sh, Tcl's --prefix location,
+ # relative to directory of tclConfig.sh.
+
+ eval "temp_includedir=${includedir}"
+ list="$list \
+ `ls -d ${temp_includedir} 2>/dev/null` \
+ `ls -d ${TK_PREFIX}/include 2>/dev/null` \
+ `ls -d ${TK_BIN_DIR}/../include 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+ if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+ list="$list /usr/local/include /usr/include"
+ fi
+ for i in $list ; do
+ if test -f "$i/tk.h" ; then
+ ac_cv_c_tkh=$i
+ break
+ fi
+ done
+ fi
+ ])
+
+ # Print a message based on how we determined the include path
+
+ if test x"${ac_cv_c_tkh}" = x ; then
+ AC_MSG_ERROR([tk.h not found. Please specify its location with --with-tkinclude])
+ else
+ AC_MSG_RESULT([${ac_cv_c_tkh}])
+ fi
+
+ # Convert to a native path and substitute into the output files.
+
+ INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tkh}`
+
+ TK_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+ AC_SUBST(TK_INCLUDES)
+
+ if test "${TEA_WINDOWINGSYSTEM}" = "win32" \
+ -o "${TEA_WINDOWINGSYSTEM}" = "aqua"; then
+ # On Windows and Aqua, we need the X compat headers
+ AC_MSG_CHECKING([for X11 header files])
+ if test ! -r "${INCLUDE_DIR_NATIVE}/X11/Xlib.h"; then
+ INCLUDE_DIR_NATIVE="`${CYGPATH} ${TK_SRC_DIR}/xlib`"
+ TK_XINCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+ AC_SUBST(TK_XINCLUDES)
+ fi
+ AC_MSG_RESULT([${INCLUDE_DIR_NATIVE}])
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PROG_TCLSH
+# Determine the fully qualified path name of the tclsh executable
+# in the Tcl build directory or the tclsh installed in a bin
+# directory. This macro will correctly determine the name
+# of the tclsh executable even if tclsh has not yet been
+# built in the build directory. The tclsh found is always
+# associated with a tclConfig.sh file. This tclsh should be used
+# only for running extension test cases. It should never be
+# or generation of files (like pkgIndex.tcl) at build time.
+#
+# Arguments
+# none
+#
+# Results
+# Subst's the following values:
+# TCLSH_PROG
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PROG_TCLSH], [
+ AC_MSG_CHECKING([for tclsh])
+ if test -f "${TCL_BIN_DIR}/Makefile" ; then
+ # tclConfig.sh is in Tcl build directory
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+ else
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
+ fi
+ else
+ # tclConfig.sh is in install location
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+ else
+ TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}"
+ fi
+ list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`"
+ for i in $list ; do
+ if test -f "$i/${TCLSH_PROG}" ; then
+ REAL_TCL_BIN_DIR="`cd "$i"; pwd`"
+ break
+ fi
+ done
+ TCLSH_PROG="${REAL_TCL_BIN_DIR}/${TCLSH_PROG}"
+ fi
+ AC_MSG_RESULT([${TCLSH_PROG}])
+ AC_SUBST(TCLSH_PROG)
+])
+
+#------------------------------------------------------------------------
+# TEA_PROG_WISH
+# Determine the fully qualified path name of the wish executable
+# in the Tk build directory or the wish installed in a bin
+# directory. This macro will correctly determine the name
+# of the wish executable even if wish has not yet been
+# built in the build directory. The wish found is always
+# associated with a tkConfig.sh file. This wish should be used
+# only for running extension test cases. It should never be
+# or generation of files (like pkgIndex.tcl) at build time.
+#
+# Arguments
+# none
+#
+# Results
+# Subst's the following values:
+# WISH_PROG
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PROG_WISH], [
+ AC_MSG_CHECKING([for wish])
+ if test -f "${TK_BIN_DIR}/Makefile" ; then
+ # tkConfig.sh is in Tk build directory
+ if test "${TEA_PLATFORM}" = "windows"; then
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
+ else
+ WISH_PROG="${TK_BIN_DIR}/wish"
+ fi
+ else
+ # tkConfig.sh is in install location
+ if test "${TEA_PLATFORM}" = "windows"; then
+ WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
+ else
+ WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}${TK_DBGX}"
+ fi
+ list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \
+ `ls -d ${TK_BIN_DIR}/.. 2>/dev/null` \
+ `ls -d ${TK_PREFIX}/bin 2>/dev/null`"
+ for i in $list ; do
+ if test -f "$i/${WISH_PROG}" ; then
+ REAL_TK_BIN_DIR="`cd "$i"; pwd`"
+ break
+ fi
+ done
+ WISH_PROG="${REAL_TK_BIN_DIR}/${WISH_PROG}"
+ fi
+ AC_MSG_RESULT([${WISH_PROG}])
+ AC_SUBST(WISH_PROG)
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_CONFIG --
+#
+# Locate the ${1}Config.sh file and perform a sanity check on
+# the ${1} compile flags. These are used by packages like
+# [incr Tk] that load *Config.sh files from more than Tcl and Tk.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-$1=...
+#
+# Defines the following vars:
+# $1_BIN_DIR Full path to the directory containing
+# the $1Config.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_CONFIG], [
+ #
+ # Ok, lets find the $1 configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-$1
+ #
+
+ if test x"${no_$1}" = x ; then
+ # we reset no_$1 in case something fails here
+ no_$1=true
+ AC_ARG_WITH($1, [ --with-$1 directory containing $1 configuration ($1Config.sh)], with_$1config=${withval})
+ AC_MSG_CHECKING([for $1 configuration])
+ AC_CACHE_VAL(ac_cv_c_$1config,[
+
+ # First check to see if --with-$1 was specified.
+ if test x"${with_$1config}" != x ; then
+ case ${with_$1config} in
+ */$1Config.sh )
+ if test -f ${with_$1config}; then
+ AC_MSG_WARN([--with-$1 argument should refer to directory containing $1Config.sh, not to $1Config.sh itself])
+ with_$1config=`echo ${with_$1config} | sed 's!/$1Config\.sh$!!'`
+ fi;;
+ esac
+ if test -f "${with_$1config}/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd ${with_$1config}; pwd)`
+ else
+ AC_MSG_ERROR([${with_$1config} directory doesn't contain $1Config.sh])
+ fi
+ fi
+
+ # then check for a private $1 installation
+ if test x"${ac_cv_c_$1config}" = x ; then
+ for i in \
+ ../$1 \
+ `ls -dr ../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ../../$1 \
+ `ls -dr ../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ../../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ../../../$1 \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ${srcdir}/../$1 \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ; do
+ if test -f "$i/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd $i; pwd)`
+ break
+ fi
+ if test -f "$i/unix/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_$1config}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 2>/dev/null` \
+ `ls -d /usr/local/lib 2>/dev/null` \
+ `ls -d /usr/contrib/lib 2>/dev/null` \
+ `ls -d /usr/lib64 2>/dev/null` \
+ `ls -d /usr/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_$1config}" = x ; then
+ $1_BIN_DIR="# no $1 configs found"
+ AC_MSG_WARN([Cannot find $1 configuration definitions])
+ exit 0
+ else
+ no_$1=
+ $1_BIN_DIR=${ac_cv_c_$1config}
+ AC_MSG_RESULT([found $$1_BIN_DIR/$1Config.sh])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_CONFIG --
+#
+# Load the $1Config.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# $1_BIN_DIR
+#
+# Results:
+#
+# Subst the following vars:
+# $1_SRC_DIR
+# $1_LIB_FILE
+# $1_LIB_SPEC
+#
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_CONFIG], [
+ AC_MSG_CHECKING([for existence of ${$1_BIN_DIR}/$1Config.sh])
+
+ if test -f "${$1_BIN_DIR}/$1Config.sh" ; then
+ AC_MSG_RESULT([loading])
+ . ${$1_BIN_DIR}/$1Config.sh
+ else
+ AC_MSG_RESULT([file not found])
+ fi
+
+ #
+ # If the $1_BIN_DIR is the build directory (not the install directory),
+ # then set the common variable name to the value of the build variables.
+ # For example, the variable $1_LIB_SPEC will be set to the value
+ # of $1_BUILD_LIB_SPEC. An extension should make use of $1_LIB_SPEC
+ # instead of $1_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ #
+
+ if test -f ${$1_BIN_DIR}/Makefile ; then
+ AC_MSG_WARN([Found Makefile - using build library specs for $1])
+ $1_LIB_SPEC=${$1_BUILD_LIB_SPEC}
+ $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC}
+ $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH}
+ fi
+
+ AC_SUBST($1_VERSION)
+ AC_SUBST($1_BIN_DIR)
+ AC_SUBST($1_SRC_DIR)
+
+ AC_SUBST($1_LIB_FILE)
+ AC_SUBST($1_LIB_SPEC)
+
+ AC_SUBST($1_STUB_LIB_FILE)
+ AC_SUBST($1_STUB_LIB_SPEC)
+ AC_SUBST($1_STUB_LIB_PATH)
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_CELIB --
+#
+# Locate Keuchel's celib emulation layer for targeting Win/CE
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-celib=...
+#
+# Defines the following vars:
+# CELIB_DIR Full path to the directory containing
+# the include and platform lib files
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_CELIB], [
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-celib
+
+ if test x"${no_celib}" = x ; then
+ # we reset no_celib in case something fails here
+ no_celib=true
+ AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], with_celibconfig=${withval})
+ AC_MSG_CHECKING([for Windows/CE celib directory])
+ AC_CACHE_VAL(ac_cv_c_celibconfig,[
+ # First check to see if --with-celibconfig was specified.
+ if test x"${with_celibconfig}" != x ; then
+ if test -d "${with_celibconfig}/inc" ; then
+ ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)`
+ else
+ AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory])
+ fi
+ fi
+
+ # then check for a celib library
+ if test x"${ac_cv_c_celibconfig}" = x ; then
+ for i in \
+ ../celib-palm-3.0 \
+ ../celib \
+ ../../celib-palm-3.0 \
+ ../../celib \
+ `ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \
+ ${srcdir}/../celib-palm-3.0 \
+ ${srcdir}/../celib \
+ `ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \
+ ; do
+ if test -d "$i/inc" ; then
+ ac_cv_c_celibconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+ if test x"${ac_cv_c_celibconfig}" = x ; then
+ AC_MSG_ERROR([Cannot find celib support library directory])
+ else
+ no_celib=
+ CELIB_DIR=${ac_cv_c_celibconfig}
+ CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'`
+ AC_MSG_RESULT([found $CELIB_DIR])
+ fi
+ fi
+])
+
+
+# Local Variables:
+# mode: autoconf
+# End:
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..0fafccb
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,19 @@
+if BUILD_GUI
+ MAYBEMANS = apol.1 \
+ seaudit.8 seaudit-report.8 \
+ sediffx.1
+endif
+
+EXTRA_DIST=$(man_MANS) apol.1 \
+ seaudit.8 seaudit-report.8.in \
+ sediffx.1
+
+man_MANS = findcon.1 indexcon.1 replcon.1 \
+ sechecker.1 \
+ sediff.1 \
+ seinfo.1 sesearch.1 $(MAYBEMANS)
+
+seaudit-report.8: seaudit-report.8.in Makefile
+ sed -e 's|\@setoolsdir\@|$(setoolsdir)|g' $< > $@
+
+CLEANFILES = seaudit-report.8
diff --git a/man/apol.1 b/man/apol.1
new file mode 100644
index 0000000..c8cdb91
--- /dev/null
+++ b/man/apol.1
@@ -0,0 +1,43 @@
+.TH apol 1
+.SH NAME
+apol \- SELinux policy analysis tool
+.SH SYNOPSIS
+.B apol
+[OPTIONS] [POLICY ...]
+.SH DESCRIPTION
+.PP
+.B apol
+is a graphical tool that allows the user to inspect aspects of a SELinux policy.
+The tool allows the user to browse policy components (types, classes, roles, users, etc.), rules (TE, RBAC, MLS), and file system contexts.
+The tool also provides in depth analyses of domain transitions, information flows, and relabeling permissions.
+.SH POLICY
+.PP
+.B
+apol
+supports loading a SELinux policy in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+If a policy is not given on the command line then
+.B
+apol
+will begin with none loaded.
+.SH OPTIONS
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2001-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+seinfo(1), sesearch(1), sechecker(1), indexcon(1)
diff --git a/man/findcon.1 b/man/findcon.1
new file mode 100644
index 0000000..e7bf825
--- /dev/null
+++ b/man/findcon.1
@@ -0,0 +1,119 @@
+.TH findcon 1
+.SH NAME
+findcon \- SELinux file context search tool
+.SH SYNOPSIS
+.B findcon
+FCLIST [OPTIONS] [EXPRESSION]
+.SH DESCRIPTION
+.PP
+.B findcon
+allows the user to search for files with a specified context.
+Results can be filtered by object class as described below.
+.SH FCLIST
+The
+.B findcon
+tool operates upon a file context list source. There are three valid
+file context lists.
+.IP directory
+If
+.B FCLIST
+is a name of a directory then begin the search at that directory and
+recurse within it. Be sure there are no circular mounts within it.
+.IP file_contexts
+If
+.B FCLIST
+is the name of a file_contexts file (e.g.,
+/etc/selinux/strict/contexts/files/file_contexts) then open that file
+and find matching entries.
+.IP database
+If
+.B FCLIST
+is the name of a database as created by a previous run of
+.B indexcon
+or
+.B apol
+then open the database and execute queries into it.
+.SH EXPRESSION
+.P
+The following options allow the user to specify which files to print.
+A file must meet all specified criteria.
+If no expression is provided, all files are printed.
+.IP "-t TYPE, --type=TYPE"
+Search for files with a context containing the type TYPE.
+.IP "-u USER, --user=USER"
+Search for files with a context containing the user USER.
+.IP "-r ROLE, --role=ROLE"
+Search for files with a context containing the role ROLE.
+.IP "-m RANGE, --mls-range=RANGE"
+Search for files with a context with the MLS range of RANGE. Note
+that
+.B findcon
+ignores the SELinux translation library, if present. In addition,
+this flag is ignored if the
+.B FCLIST
+has no MLS information.
+.IP "--context=CONTEXT"
+Search for files matching this partial context. This flag overrides
+-t, -u, -r, and -m.
+.IP "-p PATH, --path=PATH"
+Search for files which include PATH.
+.IP "-c CLASS, --class=CLASS"
+Search only files of object class CLASS.
+.SH OPTIONS
+The following additional options are available.
+.IP "-R, --regex"
+Search using regular expressions instead of exact string matching.
+This option does not affect the --class flag.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH PARTIAL CONTEXT
+The
+.B --context
+flag specifies a partial context, which is a a colon separated list of
+user, role, and type. If the system supports MLS, the context may
+have a fourth field that gives the range. If a field is not specified
+or is the literal asterisk, then the query will always match the field.
+.SH OBJECT CLASSES
+Valid object class strings are
+.PP
+block,
+char,
+dir,
+fifo,
+file,
+link, or
+sock.
+.SH NOTE
+The findcon utility always operates on "raw" SELinux file contexts.
+If the system has an installed translation library (i.e., libsetrans),
+those translations are ignored in favor of reading the original
+contexts from the filesystem (if FCFILE is a directory).
+.SH EXAMPLES
+.TP
+.B findcon .
+Find every context in the current directory and all of its
+subdirectories.
+.TP
+.B findcon -u user_u .
+Find every context whose user is user_u in the current directory and
+all subdirectories.
+.TP
+.B findcon -u system_u -t bin_t file_contexts
+Find entries user system_u and type bin_t within a file_contexts file,
+assuming that file_contexts is a file contexts file.
+.TP
+.B findcon --context=system_u::bin_t file_contexts
+This is equivalent to the previous example.
+.TP
+.B findcon --context=system_u:*:bin_t:* file_contexts
+This is also equivalent to the above example.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2003-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+replcon(1), indexcon(1)
diff --git a/man/indexcon.1 b/man/indexcon.1
new file mode 100644
index 0000000..6ecbbf4
--- /dev/null
+++ b/man/indexcon.1
@@ -0,0 +1,36 @@
+.TH indexcon 1
+.SH NAME
+indexcon \- SELinux file context indexing tool
+.SH SYNOPSIS
+.B indexcon
+FILE [OPTIONS]
+.SH DESCRIPTION
+.PP
+.B indexcon
+allows the user to index the file contexts on a SELinux system,
+beginning with the root directory (
+.B
+/
+) and recursing into subdirectories.
+The index will be written to FILE.
+The index can be searched using apol or findcon.
+.SH OPTIONS
+.IP "-d DIR, --directory=DIR"
+Start scanning at directory DIR, and recurse through its subdirectories.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH NOTE
+The indexcon utility always operates on "raw" SELinux file contexts.
+If the system has an installed translation library (i.e., libsetrans),
+those translations are ignored in favor of reading the original
+contexts from the filesystem.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2003-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+apol(1), findcon(1)
diff --git a/man/replcon.1 b/man/replcon.1
new file mode 100644
index 0000000..8aca08a
--- /dev/null
+++ b/man/replcon.1
@@ -0,0 +1,102 @@
+.TH replcon 1
+.SH NAME
+replcon \- SELinux file context replacement tool
+.SH SYNOPSIS
+.B replcon
+NEW_CONTEXT DIR [OPTIONS] [EXPRESSION]
+.SH DESCRIPTION
+.PP
+.B replcon
+allows the user to find and replace file contexts.
+Replacements can be filtered by object class as described below.
+.SH REQUIRED ARGUMENTS
+.IP NEW_CONTEXT
+The replacement context as expressed as a partial context, described
+below.
+.IP DIR
+Initial directory to begin searching. The tool will recurse into any
+subdirectories, so be sure there are no circular mounts within it.
+.SH EXPRESSION
+.P
+The following options allow the user to specify which files to find.
+A file must meet all specified criteria for its context to be
+replaced. If no expression is provided, all files' contexts are
+replaced.
+.IP "-t TYPE, --type=TYPE"
+Search for files with a context containing the type TYPE.
+.IP "-u USER, --user=USER"
+Search for files with a context containing the user USER.
+.IP "-r ROLE, --role=ROLE"
+Search for files with a context containing the role ROLE.
+.IP "-m RANGE, --mls-range=RANGE"
+Search for files with a context with the MLS range of RANGE. Note
+that
+.B replcon
+ignores the SELinux translation library, if present. In addition,
+this flag is ignored if
+.B DIR
+has no MLS information.
+.IP "--context=CONTEXT"
+Search for files matching this partial context. This flag overrides
+-t, -u, -r, and -m.
+.IP "-p PATH, --path=PATH"
+Search for files which include PATH.
+.IP "-c CLASS, --class=CLASS"
+Search only files of object class CLASS.
+.SH OPTIONS
+.IP "-v, --verbose"
+Display context info during replacement.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH PARTIAL CONTEXT
+The
+.B --context
+flag and
+.B NEW_CONTEXT
+argument specify a partial context, which is a a colon separated list
+of user, role, and type. If the system supports MLS, the context may
+have a fourth field that gives the range. With
+.B --context
+if a field is not specified or is the literal asterisk, then the query
+will always match the field. With
+.B NEW_CONTEXT
+if a field is not specified or is the literal asterisk, then that
+portion of the context will not be modified.
+.SH OBJECT CLASSES
+Valid object class strings are
+.PP
+block,
+char,
+dir,
+fifo,
+file,
+link, or
+sock.
+.SH NOTE
+The replcon utility always operates on "raw" SELinux file contexts.
+If the system has an installed translation library (i.e., libsetrans),
+those translations are ignored in favor of reading the original
+contexts from the filesystem.
+.SH EXAMPLES
+.TP
+.B replcon ::type_t: .
+Replace every context's type in the current directory with type_t.
+The user and role portion remain unchanged.
+.TP
+.B replcon -u user_u *:role_r:* .
+Replace every context's role with user user_u in the current directory
+with role_r. The user and type portion remain unchanged.
+.TP
+.B replcon --context ::type_t:s0 :::s0:c0 /tmp
+Replace every context with type type_t and MLS range s0 in /tmp
+with MLS range s0:c0.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2003-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+findcon(1), indexcon(1)
diff --git a/man/seaudit-report.8.in b/man/seaudit-report.8.in
new file mode 100644
index 0000000..f4c5f4f
--- /dev/null
+++ b/man/seaudit-report.8.in
@@ -0,0 +1,38 @@
+.TH seaudit-report 8
+.SH NAME
+seaudit-report \- SELinux audit log reporting tool
+.SH SYNOPSIS
+.B seaudit-report
+[OPTIONS] LOGFILE ...
+.SH DESCRIPTION
+.PP
+.B seaudit-report
+allows the user to generate custom audit log reports from the command line or by integration with the Logwatch tool.
+.SH OPTIONS
+.IP "-s, --stdin"
+Read log data from standard input instead of from a file.
+File(s) specified on the command line will be ignored.
+.IP "-m, --malformed"
+Include malformed log messages in generated report.
+.IP "-o FILE, --output=FILE"
+Write output to FILE instead of standard output.
+.IP "-c FILE, --config=FILE"
+Read configuration options from FILE instead of the default config file.
+.IP "--html"
+Set output format to HTML instead of plain text.
+.IP "--stylesheet=FILE"
+Specify the HTML stylesheet to use for formatting the HTML report.
+This option is ignored if --html is not given.
+See the default styesheet for an example (installed at @setoolsdir@/seaudit-report.css).
+.IP "-V, --version"
+Print version information and exit.
+.IP "-h, --help"
+Print help information and exit.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2004-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+seaudit(8)
diff --git a/man/seaudit.8 b/man/seaudit.8
new file mode 100644
index 0000000..1c3eb2e
--- /dev/null
+++ b/man/seaudit.8
@@ -0,0 +1,49 @@
+.TH seaudit 8
+.SH NAME
+seaudit \- SELinux graphical audit log analysis tool
+.SH SYNOPSIS
+.B seaudit
+[OPTIONS] [POLICY ...]
+.SH DESCRIPTION
+.PP
+.B seaudit
+allows the user to view and filter the contents of a log file.
+.B seaudit
+supports the syslog and auditd log formats and provides queries to inspect the SELinux policy based on log messages.
+.SH POLICY
+.PP
+.B
+seaudit
+supports loading a SELinux policy in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+If no policy file is provided,
+.B
+seaudit
+will search for the system default policy: checking first for a source policy, next for a binary policy matching the running kernel's preferred version, and finally for the highest version that can be found.
+If no policy can be found,
+.B
+seaudit
+will begin with no policy loaded.
+.SH OPTIONS
+.IP "-l FILE, --log=FILE"
+Upon startup, open the log FILE instead of the system log file.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2006-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+seaudit-report(8)
diff --git a/man/sechecker.1 b/man/sechecker.1
new file mode 100644
index 0000000..ecc2a07
--- /dev/null
+++ b/man/sechecker.1
@@ -0,0 +1,136 @@
+.TH sechecker 1
+.SH NAME
+sechecker \- SELinux policy checking tool
+.SH SYNOPSIS
+.B sechecker
+[OPTIONS] -p profile [POLICY ...]
+.br
+.B sechecker
+[OPTIONS] -m module [POLICY ...]
+.br
+.B sechecker
+[OPTIONS] -p profile -m module [POLICY ...]
+.SH DESCRIPTION
+.PP
+.B sechecker
+allows the user to perform predefined modular checks on a SELinux policy.
+Profiles exist to group modules together and allow modification of module settings (see below).
+.SH POLICY
+.PP
+.B
+sechecker
+supports loading a SELinux policy in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+If no policy file is provided,
+.B
+sechecker
+will search for the system default policy: checking first for a source policy, next for a binary policy matching the running kernel's preferred version, and finally for the highest version that can be found.
+In the latter case, the policy will be downgraded to match the running system.
+If no policy can be found,
+.B
+sechecker
+will print an error message and exit.
+.SH OPTIONS
+.IP "-p PROFILE, --profile=PROFILE"
+Load module settings from a module profile.
+The settings in the profile will override the default settings for all specified modules.
+If specified without -m, run all modules in the profile.
+PROFILE may either be the name of a known profile (see --list) or the path to a user created profile.
+see PROFILE OPTIONS below for more information about creating profiles.
+.IP "-m MODULE, --module=MODULE"
+Run only the module named MODULE (see --list).
+.IP "--min-sev=SEVERITY"
+Report only results with the minimum severity of SEVERITY.
+SEVERITY must have one of the following values:
+.RS
+.IP "low"
+The module's results indicate a flaw in the policy that does not affect the manner in which the policy is enforced, but is considered to be improper.
+.IP "med"
+The module's results indicate a flaw in the policy that changes the manner in which the policy is enforced; however, it does not present an identifiable security risk.
+.IP "high"
+The module's results indicate a flaw in the policy that presents an identifiable security risk.
+.RE
+.IP "--fcfile=FILE"
+Use FILE for the file_contexts file instead of the system default.
+This flag is only applicable if sechecker was configured with the
+.B
+--enable-sefs
+flag.
+.IP "-l, --list"
+Print a list of the name and a brief description of all known profiles and modules and exit.
+.IP "-h[MODULE], --help[=MODULE]"
+Print general help information and exit.
+If MODULE is provided, print help information for the module named MODULE and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SS REPORT GENERATION OPTIONS
+.P
+Only one of the following may be provided to specify the length of the report for all modules.
+If provided, this option overrides both profile and module default output settings.
+.IP "-q, --quiet"
+suppress output
+.IP "-s, --short"
+print short output
+.IP "-v, --verbose"
+print verbose output
+.SH PROFILE OPTIONS
+Profiles are used to group modules together, to specify the output format for each module in the report, and to provide the ability to override the modules' default options. Each profile is a well-formed XML document, as specified by the DTD installed with sechecker. An example profile follows:
+.PP
+<sechecker version="1.1">
+.br
+ <profile>
+.br
+ <module name="find_domains">
+.br
+ <output value="quiet"/>
+.br
+ <option name="domain_attribute">
+.br
+ <item value="domain"/>
+.br
+ <item value="user_domain"/>
+.br
+ ...
+.br
+ </option>
+.br
+ </module>
+.br
+ ...
+.br
+ </profile>
+.br
+</sechecker>
+.PP
+The example profile specifies the output property for the find_domains module.
+The example profile also overrides the default value for the "domain_attribute" option in the find_domains module.
+.SS PROFILE OUTPUT OPTIONS
+The valid output values for each module are specified below:
+.IP "verbose"
+Print each result in the report with accompanying proof(s).
+.IP "short"
+Print a list of results with no accompanying proof.
+.IP "none"
+Do not print output from this module in the report; however, module errors will still be printed.
+.IP "quiet"
+Do not print output from this module in the report and do not print errors. This is useful for utility modules for which the calling module handles any errors.
+.SS PROFILE MODULE OPTIONS
+Several modules provide one or more options that can be set from a profile.
+Each option has one or more items.
+To check what options are available for a module use --help=MODULE, where MODULE is the name of the module as printed by --list.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2005-2008 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+apol(1)
diff --git a/man/sediff.1 b/man/sediff.1
new file mode 100644
index 0000000..ef4f3a9
--- /dev/null
+++ b/man/sediff.1
@@ -0,0 +1,118 @@
+.TH sediff 1
+.SH NAME
+sediff \- SELinux policy difference tool
+.SH SYNOPSIS
+.B sediff
+[OPTIONS] [EXPRESSION] ORIGINAL_POLICY ; MODIFIED_POLICY
+.SH DESCRIPTION
+.PP
+.B sediff
+allows the user to inspect the semantic differences between two SELinux policies.
+.SH POLICY
+.PP
+.B
+sediff
+supports loading SELinux policies in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+Policies do not need to be the same format. If not provided
+.B
+sediff
+will print an error message and exit.
+.SH EXPRESSIONS
+.P
+The user may specify an expression listing the policy elements to differentiate.
+If not provided, all supported policy elements sans neverallows are examined.
+.IP "-c, --class"
+Find differences in permissions assigned to object classes and common permission sets.
+.IP "--level"
+Find differences in categories authorized for MLS levels.
+.IP "--category"
+Find differences in category definitions.
+.IP "-t, --type"
+Find differences in attributes associated with types.
+.IP "-a, --attribute"
+Find differences in types assigned to attributes.
+.IP "-r, --role"
+Find differences in types authorized for roles.
+.IP "-u, --user"
+Find differences in roles authorized for users.
+.IP "-b, --bool"
+Find differences in the default values of booleans.
+.IP "-A, --allow"
+Find differences in allow rules.
+.IP "--auditallow"
+Find differences in auditallow rules.
+.IP "--dontaudit"
+Find differences in dontaudit rules.
+.IP "--neverallow"
+Find differences in neverallow rules.
+.IP "--type_trans"
+Find differences in type_transition rules.
+.IP "--type_member"
+Find differences in type_member rules.
+.IP "--type_change"
+Find differences in type_change rules.
+.IP "--role_trans"
+Find differences in role_transition rules.
+This includes differences in the default role.
+.IP "--role_allow"
+Find differences in role allow rules.
+.IP "--range_trans"
+Find differences in range_transition rules. This includes differences
+in the target MLS range.
+.SH OPTIONS
+.IP "-q, --quiet"
+If there are no differences for elements of a given kind,
+suppress status output for that kind of element.
+.IP "--stats"
+Print difference statistics only.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH DIFFERENCES
+.PP
+.B
+sediff
+categorizes differences in policy elements into one of three forms.
+.RS
+.IP "added"
+The element exists only in the modified policy.
+.IP "removed"
+The element exists only in the original policy.
+.IP "modified"
+The element exists in both policies but its semantic meaning has changed.
+For example, a class is modified if one or more permissions are added or removed.
+.RE
+.PP
+For all rules with types as their source or target, two additional forms of difference are recognized.
+This helps distinguish differences due to new types from differences in rules for existing types.
+.RS
+.IP "added, new type"
+The rule exists only in the modified policy;
+furthermore, one or more of the types in the rule do not exist in the original policy.
+.IP "removed, missing type"
+The rule exists only in the original policy;
+furthermore, one or more of the types in the rule do not exist in the modified policy.
+.RE
+.SH NOTE
+Most shells interpret the semicolon as a metacharacter, thus requiring
+a backslash like so:
+.B
+sediff original.policy \\; modified.policy
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2004-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+sediffx(1)
diff --git a/man/sediffx.1 b/man/sediffx.1
new file mode 100644
index 0000000..d2ab486
--- /dev/null
+++ b/man/sediffx.1
@@ -0,0 +1,75 @@
+.TH sediffx 1
+.SH NAME
+sediffx \- graphical SELinux policy difference tool
+.SH SYNOPSIS
+.B sediffx
+[\-d] [ORIGINAL_POLICY ; MODIFIED_POLICY]
+.SH DESCRIPTION
+.PP
+.B sediffx
+allows the user to graphically inspect the semantic differences between two SELinux policies.
+All supported policy elements are examined.
+.SH POLICY
+.PP
+.B
+sediffx
+supports loading SELinux policies in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+Policies do not need to be the same format. If not provided
+.B
+sediffx
+will begin with no policies loaded.
+.SH OPTIONS
+.IP "-d, --diff-now"
+Load the policies and differentiate them immediately.
+This option requires the user to specify the policies on the command line.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH DIFFERENCES
+.PP
+.B
+sediffx
+categorizes differences in policy elements into one of three forms.
+.RS
+.IP "added"
+The element exists only in the modified policy.
+.IP "removed"
+The element exists only in the original policy.
+.IP "modified"
+The element exists in both policies but its semantic meaning has changed.
+For example, a class is modified if one or more permissions are added or removed.
+.RE
+.PP
+For all rules with types as their source or target, two additional forms of difference are recognized.
+This helps distinguish differences due to new types from differences in rules for existing types.
+.RS
+.IP "added, new type"
+The rule exists only in the modified policy;
+furthermore, one or more of the types in the rule do not exist in the original policy.
+.IP "removed, missing type"
+The rule exists only in the original policy;
+furthermore, one or more of the types in the rule do not exist in the modified policy.
+.RE
+.SH NOTE
+Most shells interpret the semicolon as a metacharacter, thus requiring
+a backslash like so:
+.B
+sediffx original.policy \\; modified.policy
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2005-2007 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+sediff(1)
diff --git a/man/seinfo.1 b/man/seinfo.1
new file mode 100644
index 0000000..8612119
--- /dev/null
+++ b/man/seinfo.1
@@ -0,0 +1,109 @@
+.TH seinfo 1
+.SH NAME
+seinfo \- SELinux policy query tool
+.SH SYNOPSIS
+.B seinfo
+[OPTIONS] [EXPRESSION] [POLICY ...]
+.SH DESCRIPTION
+.PP
+.B seinfo
+allows the user to query the components of a SELinux policy.
+.SH POLICY
+.PP
+.B
+seinfo
+supports loading a SELinux policy in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+If no policy file is provided,
+.B
+seinfo
+will search for the system default policy: checking first for a source policy, next for a binary policy matching the running kernel's preferred version, and finally for the highest version that can be found.
+In the latter case, the policy will be downgraded to match the running system.
+If no policy can be found,
+.B
+seinfo
+will print an error message and exit.
+.SH EXPRESSIONS
+.P
+One or more of the following component types can be queried. Each option may only be specified once.
+If an option is provided multiple times, the last instance will be used. Some components support the -x flag to print expanded information
+about that component; if a particular component specified does not support expanded information,
+the flag will be ignored for that component (see -x below). If no expressions are provided, policy statistics will be printed (see --stats below).
+.IP "-c[NAME], --class[=NAME]"
+Print a list of object classes or, if NAME is provided, print the object class NAME.
+With -x, print a list of permissions for each displayed object class.
+.IP "--sensitivity[=NAME]"
+Print a list of sensitivities or, if NAME is provided, print the sensitivity NAME.
+With -x, print the corresponding level statement for each displayed sensitivity.
+.IP "--category[=NAME]"
+Print a list of categories or, if NAME is provided, print the category NAME.
+With -x, print a list of sensitivities with which each displayed category may be associated.
+.IP "-t[NAME], --type[=NAME]"
+Print a list of types (not including aliases or attributes) or, if NAME is provided, print the type NAME.
+With -x, print a list of attributes which include each displayed type.
+.IP "-a[NAME], --attribute[=NAME]"
+Print a list of type attributes or, if NAME is provided, print the attribute NAME.
+With -x, print a list of types assigned to each displayed attribute.
+.IP "-r[NAME], --role[=NAME]"
+Print a list of roles or, if NAME is provided, print the role NAME.
+With -x, print a list of types assigned to each displayed role.
+.IP "-u[NAME], --user[=NAME]"
+Print a list of users or, if NAME is provided, print the user NAME.
+With -x, print a list of roles assigned to each displayed user.
+.IP "-b[NAME], --bool[=NAME]"
+Print a list of conditional booleans or, if NAME is provided, print the boolean NAME.
+With -x, print the default state of each displayed conditional boolean.
+.IP "--initialsid[=NAME]"
+Print a list of initial SIDs or, if NAME is provided, print the initial SID NAME.
+With -x, print the context assigned to each displayed SID.
+.IP "--fs_use[=TYPE]"
+Print a list of fs_use statements or, if TYPE is provided, print the statement for filesystem TYPE.
+There is no expanded information for this component.
+.IP "--genfscon[=TYPE]"
+Print a list of genfscon statements or, if TYPE is provided, print the statement for the filesystem TYPE.
+There is no expanded information for this component.
+.IP "--netifcon[=NAME]"
+Print a list of netif contexts or, if NAME is provided, print the statement for interface NAME.
+There is no expanded information for this component.
+.IP "--nodecon[=ADDR]"
+Print a list of node contexts or, if ADDR is provided, print the statement for the node with address ADDR.
+There is no expanded information for this component.
+.IP "--portcon[=PORT]"
+Print a list of port contexts or, if PORT is provided, print the statement for port PORT.
+There is no expanded information for this component.
+.IP "--protocol=PROTO"
+Print only portcon statements for the protocol PROTO. This option is ignored if portcon statements are not printed or if no statement exists for the requested port.
+.IP "--constrain"
+Print a list of constraints.
+There is no expanded information for this component.
+.IP "--all"
+Print all components.
+.SH OPTIONS
+.IP "-x, --expand"
+Print additional details for each component matching the expression.
+These details include the types assigned to an attribute or role and the permissions for an object class.
+This option is not available for all component types; see the description of each component for the details this option will provide.
+.IP "--stats"
+Print policy statistics including policy type and version information and counts of all components and rules.
+.IP "-l"
+Print line breaks when displaying constraint statements.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2003-2010 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+sesearch(1), apol(1)
diff --git a/man/sesearch.1 b/man/sesearch.1
new file mode 100644
index 0000000..d002faf
--- /dev/null
+++ b/man/sesearch.1
@@ -0,0 +1,113 @@
+.TH sesearch 1
+.SH NAME
+sesearch \- SELinux policy query tool
+.SH SYNOPSIS
+.B sesearch
+[OPTIONS] RULE_TYPE [RULE_TYPE ...] [EXPRESSION] [POLICY ...]
+.SH DESCRIPTION
+.PP
+.B sesearch
+allows the user to search the rules in a SELinux policy.
+.SH POLICY
+.PP
+.B
+sesearch
+supports loading a SELinux policy in one of four formats.
+.IP "source"
+A single text file containing policy source for versions 12 through 21. This file is usually named policy.conf.
+.IP "binary"
+A single file containing a monolithic kernel binary policy for versions 15 through 21. This file is usually named by version - for example, policy.20.
+.IP "modular"
+A list of policy packages each containing a loadable policy module. The first module listed must be a base module.
+.IP "policy list"
+A single text file containing all the information needed to load a policy, usually exported by SETools graphical utilities.
+.PP
+If no policy file is provided,
+.B
+sesearch
+will search for the system default policy: checking first for a source policy, next for a binary policy matching the running kernel's preferred version, and finally for the highest version that can be found.
+In the latter case, the policy will be downgraded to match the running system.
+If no policy can be found,
+.B
+sesearch
+will print an error message and exit.
+.SH RULE TYPE OPTIONS
+.P
+.B
+sesearch
+is capable of searching multiple types of rules. At least one of the following
+must be provided to specify the desired type(s) of rules to search.
+.IP "-A, --allow"
+Search for allow rules.
+.IP "--neverallow"
+Search for neverallow rules.
+.IP "--auditallow"
+Search for auditallow rules.
+.IP "--dontaudit"
+Search for dontaudit rules.
+.IP "-T, --type"
+Search for type_transition, type_member, and type_change rules.
+.IP "--role_allow"
+Search for role allow rules.
+.IP "--role_trans"
+Search for role_transition rules.
+.IP "--range_trans"
+Search for range_transition rules.
+.IP "--all"
+Search all rule types.
+.SH EXPRESSIONS
+.P
+The user may specify an expression containing values for a given field(s) in a rule.
+Only those fields applicable to a given rule type will be used; all other fields will be ignored.
+(For example, type_transition rules will ignore the permissions field.)
+If no expression is specified or if none of the specified fields apply to a given rule type,
+all rules of that type are considered to match the expression.
+.IP "-s NAME, --source=NAME"
+Find rules with type/attribute NAME as their source.
+.IP "-t NAME, --target=NAME"
+Find rules with type/attribute NAME as their target.
+.IP "--role_source=NAME"
+Find rules with role NAME as their source.
+.IP "--role_target=NAME"
+Find rules with role NAME as their target.
+.IP "-c NAME, --class=NAME"
+Find rules with class NAME as their object class.
+.IP "-p P1[,P2,...] --perm=P1[,P2...]"
+Find rules with at least one of the specified permissions.
+Multiple permissions may be specified as a comma separated list;
+it is recommended that this list be quoted for shells that interpret comma as a special character.
+.IP "-b NAME, --bool=NAME"
+Find conditional rules with NAME in their conditional expression.
+This option will include rules in both the true and false lists of the conditional.
+.SH OPTIONS
+.P
+The following additional options exist to modify how the search is performed and the amount of information printed for each result.
+.IP "-d, --direct"
+Normally rules are matched using the type given or any of that type's
+attributes (or an attribute's types). This "indirect" matching also
+considers types used in complemented sets, the special set "*", and
+the special target "self". When the direct flag is given, matching is
+done literally. The rule must explicitly contain the given type (or
+attribute) for it to be returned.
+.IP "-R, --regex"
+Use regular expressions to match symbol names. By default only exact
+string matches will be considered.
+.IP "-n, --linenum"
+Print the line number for each rule. This option is ignored if using the --semantic option or if line numbers are not available for the given policy.
+.IP "-S, --semantic"
+Search rules semantically instead of syntactically. This option is implied for policies for which syntactic rules are not available.
+.IP "-C, --show_cond"
+Print the conditional expression and state for all conditional rules found.
+This option has no effect on unconditional rules.
+.IP "-h, --help"
+Print help information and exit.
+.IP "-V, --version"
+Print version information and exit.
+.SH AUTHOR
+This manual page was written by Jeremy A. Mowery <jmowery@tresys.com>.
+.SH COPYRIGHT
+Copyright(C) 2003-2008 Tresys Technology, LLC
+.SH BUGS
+Please report bugs via an email to setools-bugs@tresys.com.
+.SH SEE ALSO
+seinfo(1), apol(1)
diff --git a/packages/BWidget-1.8.0.tar.bz2 b/packages/BWidget-1.8.0.tar.bz2
new file mode 100644
index 0000000..26400c0
--- /dev/null
+++ b/packages/BWidget-1.8.0.tar.bz2
Binary files differ
diff --git a/packages/Doxyfile b/packages/Doxyfile
new file mode 100644
index 0000000..70e2edf
--- /dev/null
+++ b/packages/Doxyfile
@@ -0,0 +1,1237 @@
+# Doxyfile 1.4.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME =
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = /tmp/setools
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = YES
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish,
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese,
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish,
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = NO
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = NO
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = libsefs/src/sqlite
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = __cplusplus:=1
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = YES
diff --git a/packages/Makefile.am b/packages/Makefile.am
new file mode 100644
index 0000000..82c9947
--- /dev/null
+++ b/packages/Makefile.am
@@ -0,0 +1,38 @@
+bwidget_destdir = @BWIDGET_DESTDIR@
+dist_noinst_DATA = BWidget-1.8.0.tar.bz2 combobox.tcl mainframe.tcl notebook.tcl \
+ Doxyfile
+
+SUBDIRS = rpm
+
+if COPY_BWIDGET
+ copy_bwidget = yes
+else
+ copy_bwidget = no
+endif
+
+pkgconfig_DATA = libqpol.pc libapol.pc libpoldiff.pc libseaudit.pc libsefs.pc
+pkgconfigdir = @libdir@/pkgconfig
+
+BUILT_SOURCES = $(pkgconfig_DATA)
+
+$(pkgconfig_DATA): $(top_builddir)/config.status
+
+install-data-local:
+ if test $(copy_bwidget) = "yes"; then \
+ tar jxf BWidget-1.8.0.tar.bz2; \
+ test -z "$(bwidget_destdir)" || $(mkdir_p) "$(bwidget_destdir)" ; \
+ cd BWidget-1.8.0 ; \
+ find . -type d -exec $(mkdir_p) "$(bwidget_destdir)/{}" \; ; \
+ find . -type f -exec $(INSTALL_DATA) '{}' $(bwidget_destdir)/'{}' \; ; \
+ fi
+
+%.pc: %.pc.in Makefile
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+uninstall-local:
+ if test $(copy_bwidget) = "yes"; then \
+ rm -rf $(bwidget_destdir) ; \
+ fi
+
+clean-local:
+ -rm -rf BWidget-1.8.0
diff --git a/packages/combobox.tcl b/packages/combobox.tcl
new file mode 100644
index 0000000..247b55f
--- /dev/null
+++ b/packages/combobox.tcl
@@ -0,0 +1,764 @@
+# this file was taken from BWidget 1.8 release
+
+namespace eval ComboBox {
+ Widget::declare ComboBox {
+ {-height TkResource 0 0 listbox}
+ {-values String "" 0}
+ {-images String "" 0}
+ {-indents String "" 0}
+ {-modifycmd String "" 0}
+ {-postcommand String "" 0}
+ {-expand Enum none 0 {none tab}}
+ {-autocomplete Boolean 0 0}
+ {-autopost Boolean 0 0}
+ {-bwlistbox Boolean 0 0}
+ {-listboxwidth Int 0 0}
+ {-hottrack Boolean 0 0}
+ }
+}
+
+
+# ComboBox::create --
+#
+# Create a combobox widget with the given options.
+#
+# Arguments:
+# path name of the new widget.
+# args optional arguments to the widget.
+#
+# Results:
+# path name of the new widget.
+
+proc ComboBox::create { path args } {
+ array set maps [list ComboBox {} :cmd {} .e {} .a {}]
+ array set maps [Widget::parseArgs ComboBox $args]
+
+ eval [list frame $path] $maps(:cmd) \
+ [list -highlightthickness 0 -takefocus 0 -class ComboBox]
+ Widget::initFromODB ComboBox $path $maps(ComboBox)
+
+ bindtags $path [list $path BwComboBox [winfo toplevel $path] all]
+
+ set entry [eval [list Entry::create $path.e] $maps(.e) \
+ [list -relief flat -borderwidth 0 -takefocus 1]]
+
+ ::bind $path.e <FocusOut> [list $path _focus_out]
+ ::bind $path <<TraverseIn>> [list $path _traverse_in]
+
+ if {[Widget::cget $path -autocomplete]} {
+ ::bind $path.e <KeyRelease> [list $path _auto_complete %K]
+ }
+
+ if {[Widget::cget $path -autopost]} {
+ ::bind $path.e <KeyRelease> +[list $path _auto_post %K]
+ } else {
+ ::bind $entry <Key-Up> [list ComboBox::_unmapliste $path]
+ ::bind $entry <Key-Down> [list ComboBox::_mapliste $path]
+ }
+
+ if {[string equal $::tcl_platform(platform) "unix"]} {
+ set ipadx 0
+ set width 11
+ } else {
+ set ipadx 2
+ set width 15
+ }
+ set height [winfo reqheight $entry]
+ set arrow [eval [list ArrowButton::create $path.a] $maps(.a) \
+ [list -width $width -height $height \
+ -highlightthickness 0 -borderwidth 1 -takefocus 0 \
+ -dir bottom -type button -ipadx $ipadx \
+ -command [list ComboBox::_mapliste $path] \
+ ]]
+
+ pack $arrow -side right -fill y
+ pack $entry -side left -fill both -expand yes
+
+ set editable [Widget::cget $path -editable]
+ Entry::configure $path.e -editable $editable
+ if {$editable} {
+ ::bind $entry <ButtonPress-1> [list ComboBox::_unmapliste $path]
+ } else {
+ ::bind $entry <ButtonPress-1> [list ArrowButton::invoke $path.a]
+ if { ![string equal [Widget::cget $path -state] "disabled"] } {
+ Entry::configure $path.e -takefocus 1
+ }
+ }
+
+ ::bind $path <ButtonPress-1> [list ComboBox::_unmapliste $path]
+ ::bind $entry <Control-Up> [list ComboBox::_modify_value $path previous]
+ ::bind $entry <Control-Down> [list ComboBox::_modify_value $path next]
+ ::bind $entry <Control-Prior> [list ComboBox::_modify_value $path first]
+ ::bind $entry <Control-Next> [list ComboBox::_modify_value $path last]
+
+ if {$editable} {
+ set expand [Widget::cget $path -expand]
+ if {[string equal "tab" $expand]} {
+ # Expand entry value on Tab (from -values)
+ ::bind $entry <Tab> "[list ComboBox::_expand $path]; break"
+ } elseif {[string equal "auto" $expand]} {
+ # Expand entry value anytime (from -values)
+ #::bind $entry <Key> "[list ComboBox::_expand $path]; break"
+ }
+ }
+
+ ## If we have images, we have to use a BWidget ListBox.
+ set bw [Widget::cget $path -bwlistbox]
+ if {[llength [Widget::cget $path -images]]} {
+ Widget::configure $path [list -bwlistbox 1]
+ } else {
+ Widget::configure $path [list -bwlistbox $bw]
+ }
+
+ return [Widget::create ComboBox $path]
+}
+
+
+# ComboBox::configure --
+#
+# Configure subcommand for ComboBox widgets. Works like regular
+# widget configure command.
+#
+# Arguments:
+# path Name of the ComboBox widget.
+# args Additional optional arguments:
+# ?-option?
+# ?-option value ...?
+#
+# Results:
+# Depends on arguments. If no arguments are given, returns a complete
+# list of configuration information. If one argument is given, returns
+# the configuration information for that option. If more than one
+# argument is given, returns nothing.
+
+proc ComboBox::configure { path args } {
+ set res [Widget::configure $path $args]
+ set entry $path.e
+
+
+ set list [list -images -values -bwlistbox -hottrack]
+ foreach {ci cv cb ch} [eval [linsert $list 0 Widget::hasChangedX $path]] { break }
+
+ if { $ci } {
+ set images [Widget::cget $path -images]
+ if {[llength $images]} {
+ Widget::configure $path [list -bwlistbox 1]
+ } else {
+ Widget::configure $path [list -bwlistbox 0]
+ }
+ }
+
+ set bw [Widget::cget $path -bwlistbox]
+
+ ## If the images, bwlistbox, hottrack or values have changed,
+ ## destroy the shell so that it will re-create itself the next
+ ## time around.
+ if { $ci || $cb || $ch || ($bw && $cv) } {
+ destroy $path.shell
+ }
+
+ set chgedit [Widget::hasChangedX $path -editable]
+ if {$chgedit} {
+ if {[Widget::cget $path -editable]} {
+ ::bind $entry <ButtonPress-1> [list ComboBox::_unmapliste $path]
+ Entry::configure $entry -editable true
+ } else {
+ ::bind $entry <ButtonPress-1> [list ArrowButton::invoke $path.a]
+ Entry::configure $entry -editable false
+
+ # Make sure that non-editable comboboxes can still be tabbed to.
+
+ if { ![string equal [Widget::cget $path -state] "disabled"] } {
+ Entry::configure $entry -takefocus 1
+ }
+ }
+ }
+
+ if {$chgedit || [Widget::hasChangedX $path -expand]} {
+ # Unset what we may have created.
+ ::bind $entry <Tab> {}
+ if {[Widget::cget $path -editable]} {
+ set expand [Widget::cget $path -expand]
+ if {[string equal "tab" $expand]} {
+ # Expand entry value on Tab (from -values)
+ ::bind $entry <Tab> "[list ComboBox::_expand $path]; break"
+ } elseif {[string equal "auto" $expand]} {
+ # Expand entry value anytime (from -values)
+ #::bind $entry <Key> "[list ComboBox::_expand $path]; break"
+ }
+ }
+ }
+
+ # if the dropdown listbox is shown, simply force the actual entry
+ # colors into it. If it is not shown, the next time the dropdown
+ # is shown it'll get the actual colors anyway
+ if {[winfo exists $path.shell.listb]} {
+ $path.shell.listb configure \
+ -bg [Widget::cget $path -entrybg] \
+ -fg [Widget::cget $path -foreground] \
+ -selectbackground [Widget::cget $path -selectbackground] \
+ -selectforeground [Widget::cget $path -selectforeground]
+ }
+
+ return $res
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::cget
+# ----------------------------------------------------------------------------
+proc ComboBox::cget { path option } {
+ return [Widget::cget $path $option]
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::setvalue
+# ----------------------------------------------------------------------------
+proc ComboBox::setvalue { path index } {
+ set values [Widget::getMegawidgetOption $path -values]
+ set value [Entry::cget $path.e -text]
+ switch -- $index {
+ next {
+ if { [set idx [lsearch -exact $values $value]] != -1 } {
+ incr idx
+ } else {
+ set idx [lsearch -exact $values "$value*"]
+ }
+ }
+ previous {
+ if { [set idx [lsearch -exact $values $value]] != -1 } {
+ incr idx -1
+ } else {
+ set idx [lsearch -exact $values "$value*"]
+ }
+ }
+ first {
+ set idx 0
+ }
+ last {
+ set idx [expr {[llength $values]-1}]
+ }
+ default {
+ if { [string index $index 0] == "@" } {
+ set idx [string range $index 1 end]
+ if { ![string is integer -strict $idx] } {
+ return -code error "bad index \"$index\""
+ }
+ } else {
+ return -code error "bad index \"$index\""
+ }
+ }
+ }
+ if { $idx >= 0 && $idx < [llength $values] } {
+ set newval [lindex $values $idx]
+ Entry::configure $path.e -text $newval
+ return 1
+ }
+ return 0
+}
+
+
+proc ComboBox::icursor { path idx } {
+ return [$path.e icursor $idx]
+}
+
+
+proc ComboBox::get { path } {
+ return [$path.e get]
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::getvalue
+# ----------------------------------------------------------------------------
+proc ComboBox::getvalue { path } {
+ set values [Widget::getMegawidgetOption $path -values]
+ set value [Entry::cget $path.e -text]
+
+ return [lsearch -exact $values $value]
+}
+
+
+proc ComboBox::getlistbox { path } {
+ _create_popup $path
+ return $path.shell.listb
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::post
+# ----------------------------------------------------------------------------
+proc ComboBox::post { path } {
+ _mapliste $path
+ return
+}
+
+
+proc ComboBox::unpost { path } {
+ _unmapliste $path
+ return
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::bind
+# ----------------------------------------------------------------------------
+proc ComboBox::bind { path args } {
+ return [eval [list ::bind $path.e] $args]
+}
+
+
+proc ComboBox::insert { path idx args } {
+ upvar #0 [Widget::varForOption $path -values] values
+
+ if {[Widget::cget $path -bwlistbox]} {
+ set l [$path getlistbox]
+ set i [eval [linsert $args 0 $l insert $idx #auto]]
+ set text [$l itemcget $i -text]
+ if {$idx == "end"} {
+ lappend values $text
+ } else {
+ set values [linsert $values $idx $text]
+ }
+ } else {
+ set values [eval [list linsert $values $idx] $args]
+ }
+}
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::clearvalue
+# ----------------------------------------------------------------------------
+proc ComboBox::clearvalue { path } {
+ Entry::configure $path.e -text ""
+}
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_create_popup
+# ----------------------------------------------------------------------------
+proc ComboBox::_create_popup { path } {
+ set shell $path.shell
+
+ if {[winfo exists $shell]} { return }
+
+ set lval [Widget::cget $path -values]
+ set h [Widget::cget $path -height]
+ set bw [Widget::cget $path -bwlistbox]
+
+ if { $h <= 0 } {
+ set len [llength $lval]
+ if { $len < 3 } {
+ set h 3
+ } elseif { $len > 10 } {
+ set h 10
+ } else {
+ set h $len
+ }
+ }
+
+ if { $::tcl_platform(platform) == "unix" } {
+ set sbwidth 11
+ } else {
+ set sbwidth 15
+ }
+
+ toplevel $shell -relief solid -bd 1
+ wm withdraw $shell
+ update idle
+ wm overrideredirect $shell 1
+ wm transient $shell [winfo toplevel $path]
+ catch { wm attributes $shell -topmost 1 }
+
+ set sw [ScrolledWindow $shell.sw -managed 0 -size $sbwidth -ipad 0]
+
+ if {$bw} {
+ set listb [ListBox $shell.listb \
+ -relief flat -borderwidth 0 -highlightthickness 0 \
+ -selectmode single -selectfill 1 -autofocus 0 -height $h \
+ -font [Widget::cget $path -font] \
+ -bg [Widget::cget $path -entrybg] \
+ -fg [Widget::cget $path -foreground] \
+ -selectbackground [Widget::cget $path -selectbackground] \
+ -selectforeground [Widget::cget $path -selectforeground]]
+
+ set values [Widget::cget $path -values]
+ set images [Widget::cget $path -images]
+ foreach value $values image $images {
+ $listb insert end #auto -text $value -image $image
+ }
+ $listb bindText <1> [list ComboBox::_select $path]
+ $listb bindImage <1> [list ComboBox::_select $path]
+ if {[Widget::cget $path -hottrack]} {
+ $listb bindText <Enter> [list $listb selection set]
+ $listb bindImage <Enter> [list $listb selection set]
+ }
+ } else {
+ set listb [listbox $shell.listb \
+ -relief flat -borderwidth 0 -highlightthickness 0 \
+ -exportselection false \
+ -font [Widget::cget $path -font] \
+ -height $h \
+ -bg [Widget::cget $path -entrybg] \
+ -fg [Widget::cget $path -foreground] \
+ -selectbackground [Widget::cget $path -selectbackground] \
+ -selectforeground [Widget::cget $path -selectforeground] \
+ -listvariable [Widget::varForOption $path -values]]
+ ::bind $listb <ButtonRelease-1> [list ComboBox::_select $path @%x,%y]
+
+ if {[Widget::cget $path -hottrack]} {
+ bindtags $listb [concat [bindtags $listb] ListBoxHotTrack]
+ }
+ }
+ pack $sw -fill both -expand yes
+ $sw setwidget $listb
+
+ ::bind $listb <Return> "ComboBox::_select [list $path] \[%W curselection\]"
+ ::bind $listb <Escape> [list ComboBox::_unmapliste $path]
+ ::bind $listb <FocusOut> [list ComboBox::_focus_out $path]
+}
+
+
+proc ComboBox::_recreate_popup { path } {
+ variable background
+ variable foreground
+
+ set shell $path.shell
+ set lval [Widget::cget $path -values]
+ set h [Widget::cget $path -height]
+ set bw [Widget::cget $path -bwlistbox]
+
+ if { $h <= 0 } {
+ set len [llength $lval]
+ if { $len < 3 } {
+ set h 3
+ } elseif { $len > 10 } {
+ set h 10
+ } else {
+ set h $len
+ }
+ }
+
+ if { $::tcl_platform(platform) == "unix" } {
+ set sbwidth 11
+ } else {
+ set sbwidth 15
+ }
+
+ _create_popup $path
+
+ if {![Widget::cget $path -editable]} {
+ if {[info exists background]} {
+ $path.e configure -bg $background
+ $path.e configure -fg $foreground
+ unset background
+ unset foreground
+ }
+ }
+
+ set listb $shell.listb
+ destroy $shell.sw
+ set sw [ScrolledWindow $shell.sw -managed 0 -size $sbwidth -ipad 0]
+ $listb configure \
+ -height $h \
+ -font [Widget::cget $path -font] \
+ -bg [Widget::cget $path -entrybg] \
+ -fg [Widget::cget $path -foreground] \
+ -selectbackground [Widget::cget $path -selectbackground] \
+ -selectforeground [Widget::cget $path -selectforeground]
+ pack $sw -fill both -expand yes
+ $sw setwidget $listb
+ raise $listb
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_mapliste
+# ----------------------------------------------------------------------------
+proc ComboBox::_mapliste { path } {
+ set listb $path.shell.listb
+ if {[winfo exists $path.shell] &&
+ [string equal [wm state $path.shell] "normal"]} {
+ _unmapliste $path
+ return
+ }
+
+ if { [Widget::cget $path -state] == "disabled" } {
+ return
+ }
+ if {[llength [set cmd [Widget::getMegawidgetOption $path -postcommand]]]} {
+ uplevel \#0 $cmd
+ }
+ if { ![llength [Widget::getMegawidgetOption $path -values]] } {
+ return
+ }
+
+ _recreate_popup $path
+
+ ArrowButton::configure $path.a -relief sunken
+ update
+
+ set bw [Widget::cget $path -bwlistbox]
+
+ $listb selection clear 0 end
+ set values [Widget::getMegawidgetOption $path -values]
+ set curval [Entry::cget $path.e -text]
+ if { [set idx [lsearch -exact $values $curval]] != -1 ||
+ [set idx [lsearch -exact $values "$curval*"]] != -1 } {
+ if {$bw} {
+ set idx [$listb items $idx]
+ } else {
+ $listb activate $idx
+ }
+ $listb selection set $idx
+ $listb see $idx
+ } else {
+ set idx 0
+ if {$bw} {
+ set idx [$listb items 0]
+ } else {
+ $listb activate $idx
+ }
+ $listb selection set $idx
+ $listb see $idx
+ }
+
+ set width [Widget::cget $path -listboxwidth]
+ if {!$width} { set width [winfo width $path] }
+ BWidget::place $path.shell $width 0 below $path
+ wm deiconify $path.shell
+ raise $path.shell
+ BWidget::focus set $listb
+ BWidget::grab global $path
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_unmapliste
+# ----------------------------------------------------------------------------
+proc ComboBox::_unmapliste { path {refocus 1} } {
+ if {[winfo exists $path.shell] && \
+ [string equal [wm state $path.shell] "normal"]} {
+ BWidget::grab release $path
+ BWidget::focus release $path.shell.listb $refocus
+ # Update now because otherwise [focus -force...] makes the app hang!
+ if {$refocus} {
+ update
+ focus -force $path.e
+ }
+ wm withdraw $path.shell
+ ArrowButton::configure $path.a -relief raised
+ }
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_select
+# ----------------------------------------------------------------------------
+proc ComboBox::_select { path index } {
+ set index [$path.shell.listb index $index]
+ _unmapliste $path
+ if { $index != -1 } {
+ if { [setvalue $path @$index] } {
+ set cmd [Widget::getMegawidgetOption $path -modifycmd]
+ if {[llength $cmd]} {
+ uplevel \#0 $cmd
+ }
+ }
+ }
+ $path.e selection clear
+ $path.e selection range 0 end
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_modify_value
+# ----------------------------------------------------------------------------
+proc ComboBox::_modify_value { path direction } {
+ if {[setvalue $path $direction]
+ && [llength [set cmd [Widget::getMegawidgetOption $path -modifycmd]]]} {
+ uplevel \#0 $cmd
+ }
+}
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_expand
+# ----------------------------------------------------------------------------
+proc ComboBox::_expand {path} {
+ set values [Widget::getMegawidgetOption $path -values]
+ if {![llength $values]} {
+ bell
+ return 0
+ }
+
+ set found {}
+ set curval [Entry::cget $path.e -text]
+ set curlen [$path.e index insert]
+ if {$curlen < [string length $curval]} {
+ # we are somewhere in the middle of a string.
+ # if the full value matches some string in the listbox,
+ # reorder values to start matching after that string.
+ set idx [lsearch -exact $values $curval]
+ if {$idx >= 0} {
+ set values [concat [lrange $values [expr {$idx+1}] end] \
+ [lrange $values 0 $idx]]
+ }
+ }
+ if {$curlen == 0} {
+ set found $values
+ } else {
+ foreach val $values {
+ if {[string equal -length $curlen $curval $val]} {
+ lappend found $val
+ }
+ }
+ }
+ if {[llength $found]} {
+ Entry::configure $path.e -text [lindex $found 0]
+ if {[llength $found] > 1} {
+ set best [_best_match $found [string range $curval 0 $curlen]]
+ set blen [string length $best]
+ $path.e icursor $blen
+ $path.e selection range $blen end
+ }
+ } else {
+ bell
+ }
+ return [llength $found]
+}
+
+# best_match --
+# finds the best unique match in a list of names
+# The extra $e in this argument allows us to limit the innermost loop a
+# little further.
+# Arguments:
+# l list to find best unique match in
+# e currently best known unique match
+# Returns:
+# longest unique match in the list
+#
+proc ComboBox::_best_match {l {e {}}} {
+ set ec [lindex $l 0]
+ if {[llength $l]>1} {
+ set e [string length $e]; incr e -1
+ set ei [string length $ec]; incr ei -1
+ foreach l $l {
+ while {$ei>=$e && [string first $ec $l]} {
+ set ec [string range $ec 0 [incr ei -1]]
+ }
+ }
+ }
+ return $ec
+}
+# possibly faster
+#proc match {string1 string2} {
+# set i 1
+# while {[string equal -length $i $string1 $string2]} { incr i }
+# return [string range $string1 0 [expr {$i-2}]]
+#}
+#proc matchlist {list} {
+# set list [lsort $list]
+# return [match [lindex $list 0] [lindex $list end]]
+#}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_traverse_in
+# Called when widget receives keyboard focus due to keyboard traversal.
+# ----------------------------------------------------------------------------
+proc ComboBox::_traverse_in { path } {
+ if {[$path.e selection present] != 1} {
+ # Autohighlight the selection, but not if one existed
+ $path.e selection range 0 end
+ }
+}
+
+
+# ----------------------------------------------------------------------------
+# Command ComboBox::_focus_out
+# ----------------------------------------------------------------------------
+proc ComboBox::_focus_out { path } {
+ if {[string first $path [focus]] != 0} {
+ # we lost focus to some other app or window, so remove the listbox
+ return [_unmapliste $path 0]
+ }
+}
+
+proc ComboBox::_auto_complete { path key } {
+ ## Any key string with more than one character and is not entirely
+ ## lower-case is considered a function key and is thus ignored.
+ if {[string length $key] > 1 && [string tolower $key] != $key} { return }
+
+ set text [string map [list {[} {\[} {]} {\]}] [$path.e get]]
+ if {[string equal $text ""]} { return }
+ set values [Widget::cget $path -values]
+ set x [lsearch $values $text*]
+ if {$x < 0} { return }
+
+ set idx [$path.e index insert]
+ $path.e configure -text [lindex $values $x]
+ $path.e icursor $idx
+ $path.e select range insert end
+}
+
+proc ComboBox::_auto_post { path key } {
+ if {[string equal $key "Escape"] || [string equal $key "Return"]} {
+ _unmapliste $path
+ return
+ }
+ if {[catch {$path.shell.listb curselection} x] || $x == ""} {
+ if {[string equal $key "Up"]} {
+ _unmapliste $path
+ return
+ }
+ set x -1
+ }
+ if {([string length $key] > 1 && [string tolower $key] != $key) && \
+ [string equal $key "Backspace"] != 0 && \
+ [string equal $key "Up"] != 0 && \
+ [string equal $key "Down"] != 0} {
+ return
+ }
+
+ # post the listbox
+ _create_popup $path
+ set width [Widget::cget $path -listboxwidth]
+ if {!$width} { set width [winfo width $path] }
+ BWidget::place $path.shell $width 0 below $path
+ wm deiconify $path.shell
+ BWidget::grab release $path
+ BWidget::focus release $path.shell.listb 1
+ focus -force $path.e
+
+ set values [Widget::cget $path -values]
+ switch -- $key {
+ Up {
+ if {[incr x -1] < 0} {
+ set x 0
+ } else {
+ Entry::configure $path.e -text [lindex $values $x]
+ }
+ }
+ Down {
+ if {[incr x] >= [llength $values]} {
+ set x [expr {[llength $values] - 1}]
+ } else {
+ Entry::configure $path.e -text [lindex $values $x]
+ }
+ }
+ default {
+ # auto-select within the listbox the item closest to the entry's value
+ set text [string map [list {[} {\[} {]} {\]}] [$path.e get]]
+ if {[string equal $text ""]} {
+ set x 0
+ } else {
+ set x [lsearch $values $text*]
+ }
+ }
+ }
+
+ if {$x >= 0} {
+ $path.shell.listb selection clear 0 end
+ $path.shell.listb selection set $x
+ $path.shell.listb see $x
+ }
+}
diff --git a/packages/libapol.pc.in b/packages/libapol.pc.in
new file mode 100644
index 0000000..da177db
--- /dev/null
+++ b/packages/libapol.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libapol
+Description: SETools policy analysis library
+Version: @libapol_version@
+Requires: libqpol >= 1.3
+Conflicts:
+Libs: -L${libdir} -lapol
+Cflags: -I${includedir}/apol
diff --git a/packages/libpoldiff.pc.in b/packages/libpoldiff.pc.in
new file mode 100644
index 0000000..0722f5a
--- /dev/null
+++ b/packages/libpoldiff.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpoldiff
+Description: SETools policy difference library
+Version: @libpoldiff_version@
+Requires: libqpol >= 1.3, libapol >= 4.0
+Conflicts:
+Libs: -L${libdir} -lpoldiff
+Cflags: -I${includedir}/poldiff
diff --git a/packages/libqpol.pc.in b/packages/libqpol.pc.in
new file mode 100644
index 0000000..c898a6b
--- /dev/null
+++ b/packages/libqpol.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libqpol
+Description: SETools policy internals library
+Version: @libqpol_version@
+Requires:
+Conflicts:
+Libs: -L${libdir} -lqpol
+Cflags: -I${includedir}/qpol
diff --git a/packages/libseaudit.pc.in b/packages/libseaudit.pc.in
new file mode 100644
index 0000000..9d0450e
--- /dev/null
+++ b/packages/libseaudit.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libseaudit
+Description: SETools audit messages library
+Version: @libseaudit_version@
+Requires: libapol >= 4.0
+Conflicts:
+Libs: -L${libdir} -lseaudit
+Cflags: -I${includedir}/seaudit
diff --git a/packages/libsefs.pc.in b/packages/libsefs.pc.in
new file mode 100644
index 0000000..1c95182
--- /dev/null
+++ b/packages/libsefs.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsefs
+Description: SETools file contexts library
+Version: @libsefs_version@
+Requires: libapol >= 4.1, sqlite3 >= 3.2
+Conflicts:
+Libs: -L${libdir} -lsefs
+Cflags: -I${includedir}/sefs
diff --git a/packages/mainframe.tcl b/packages/mainframe.tcl
new file mode 100644
index 0000000..bb55db9
--- /dev/null
+++ b/packages/mainframe.tcl
@@ -0,0 +1,133 @@
+# this fragment was taken from BWidget 1.8 release, slightly modified
+proc MainFrame::_create_menubar { path descmenu } {
+ variable _widget
+ global tcl_platform
+
+ set top $_widget($path,top)
+
+ foreach {v x} {mbfnt -menubarfont mefnt -menuentryfont} {
+ if {[string length [Widget::getoption $path $x]]} {
+ set $v [list -font [Widget::getoption $path $x]]
+ } else {
+ set $v ""
+ }
+ }
+
+ if {$tcl_platform(platform) == "unix"} {
+ set menuopts [list -background [Widget::getoption $path -background] \
+ -borderwidth 1]
+ } else {
+ set menuopts [list]
+ }
+ set menubar [eval [list menu $top.menubar -tearoff 0] $menuopts $mbfnt]
+ $top configure -menu $menubar
+
+ set count 0
+ foreach {name tags menuid tearoff entries} $descmenu {
+ set opt [_parse_name $name]
+ if {[string length $menuid]
+ && ![info exists _widget($path,menuid,$menuid)] } {
+ # menu has identifier
+ # we use it for its pathname, to enable special menu entries
+ # (help, system, ...)
+ set menu $menubar.$menuid
+ } else {
+ set menu $menubar.menu$count
+ }
+ eval [list $menubar add cascade] $opt [list -menu $menu]
+ eval [list menu $menu -tearoff $tearoff] $menuopts $mefnt
+ foreach tag $tags {
+ lappend _widget($path,tags,$tag) $menubar $count
+ # ericm@scriptics: Add a tagstate tracker
+ if { ![info exists _widget($path,tagstate,$tag)] } {
+ set _widget($path,tagstate,$tag) 1
+ }
+ }
+ # ericm@scriptics: Add mapping from menu items to tags
+ set _widget($path,menutags,[list $menubar $count]) $tags
+
+ if { [string length $menuid] } {
+ # menu has identifier
+ set _widget($path,menuid,$menuid) $menu
+ }
+ _create_entries $path $menu $menuopts $entries
+ incr count
+ }
+}
+
+
+# ----------------------------------------------------------------------------
+# Command MainFrame::_create_entries
+# ----------------------------------------------------------------------------
+proc MainFrame::_create_entries { path menu menuopts entries } {
+ variable _widget
+
+ set count [$menu cget -tearoff]
+ set registered 0
+ foreach entry $entries {
+ set len [llength $entry]
+ set type [lindex $entry 0]
+
+ if { [string equal $type "separator"] } {
+ $menu add separator
+ incr count
+ continue
+ }
+
+ # entry name and tags
+ set opt [_parse_name [lindex $entry 1]]
+ set tags [lindex $entry 2]
+ foreach tag $tags {
+ lappend _widget($path,tags,$tag) $menu $count
+ # ericm@scriptics: Add a tagstate tracker
+ if { ![info exists _widget($path,tagstate,$tag)] } {
+ set _widget($path,tagstate,$tag) 1
+ }
+ }
+ # ericm@scriptics: Add mapping from menu items to tags
+ set _widget($path,menutags,[list $menu $count]) $tags
+
+ if {[string equal $type "cascade"] || [string equal $type "cascad"]} {
+ set menuid [lindex $entry 3]
+ set tearoff [lindex $entry 4]
+ set submenu $menu.menu$count
+ eval [list $menu add cascade] $opt [list -menu $submenu]
+ eval [list menu $submenu -tearoff $tearoff] $menuopts
+ if { [string length $menuid] } {
+ # menu has identifier
+ set _widget($path,menuid,$menuid) $submenu
+ }
+ _create_entries $path $submenu $menuopts [lindex $entry 5]
+ incr count
+ continue
+ }
+
+ # entry help description
+ set desc [lindex $entry 3]
+ if { [string length $desc] } {
+ if { !$registered } {
+ DynamicHelp::register $menu menu [Widget::getoption $path -textvariable]
+ set registered 1
+ }
+ DynamicHelp::register $menu menuentry $count $desc
+ }
+
+ # entry accelerator
+ set accel [_parse_accelerator [lindex $entry 4]]
+ if { [llength $accel] } {
+ lappend opt -accelerator [lindex $accel 0]
+ bind $_widget($path,top) [lindex $accel 1] [list $menu invoke $count]
+ }
+
+ # user options
+ set useropt [lrange $entry 5 end]
+ if { [string equal $type "command"] ||
+ [string equal $type "radiobutton"] ||
+ [string equal $type "checkbutton"] } {
+ eval [list $menu add $type] $opt $useropt
+ } else {
+ return -code error "invalid menu type \"$type\""
+ }
+ incr count
+ }
+}
diff --git a/packages/notebook.tcl b/packages/notebook.tcl
new file mode 100644
index 0000000..c0cc159
--- /dev/null
+++ b/packages/notebook.tcl
@@ -0,0 +1,9 @@
+# this fragment taken from BWidget 1.8 release
+proc NoteBook::bindtabs { path event script } {
+ if { $script != "" } {
+ append script " \[NoteBook::_get_page_name [list $path] current 1\]"
+ $path.c bind "page" $event $script
+ } else {
+ $path.c bind "page" $event {}
+ }
+}
diff --git a/packages/rpm/Makefile.am b/packages/rpm/Makefile.am
new file mode 100644
index 0000000..997377c
--- /dev/null
+++ b/packages/rpm/Makefile.am
@@ -0,0 +1,11 @@
+dist_noinst_DATA = setools.spec \
+ apol.desktop.in seaudit.desktop.in \
+ sediffx.desktop.in seaudit.pam seaudit.console.in
+
+BUILT_SOURCES = apol.desktop seaudit.desktop sediffx.desktop seaudit.console
+
+%: %.in Makefile
+ sed -e "s|\@bindir\@|$(bindir)|" -e "s|\@sbindir\@|$(sbindir)|" $< > $@
+
+clean-local:
+ -rm -f $(BUILT_SOURCES)
diff --git a/packages/rpm/apol.desktop.in b/packages/rpm/apol.desktop.in
new file mode 100644
index 0000000..341cc25
--- /dev/null
+++ b/packages/rpm/apol.desktop.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=SELinux Policy Analysis
+GenericName=SELinux Policy Analysis Tool
+Comment=This tool can examine, search, and relate policy components and policy rules
+Icon=apol.png
+Exec=@bindir@/apol
+Type=Application
+Terminal=false
+Encoding=UTF-8
+Categories=System;
+X-Desktop-File-Install-Version=0.2
+StartupNotify=true
diff --git a/packages/rpm/fc9-compile.patch b/packages/rpm/fc9-compile.patch
new file mode 100644
index 0000000..e645db9
--- /dev/null
+++ b/packages/rpm/fc9-compile.patch
@@ -0,0 +1,13 @@
+Index: libseaudit/swig/python/Makefile.am
+===================================================================
+--- libseaudit/swig/python/Makefile.am (revision 4788)
++++ libseaudit/swig/python/Makefile.am (working copy)
+@@ -23,7 +23,7 @@
+ -I$(top_srcdir)/libqpol/swig -I$(top_srcdir)/libapol/swig $<
+
+ $(wrappedso_DATA): $(BUILT_SOURCES)
+- $(CC) -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
++ $(CC) -std=gnu89 -shared -o $@ $^ $(AM_CFLAGS) $(CFLAGS) $(SWIG_PYTHON_CPPFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -Wl,-soname,$(wrappedso_SONAME)
+ $(LN_S) -f $@ $(wrappedso_SONAME)
+ $(LN_S) -f $@ _seaudit.so
+
diff --git a/packages/rpm/seaudit.console.in b/packages/rpm/seaudit.console.in
new file mode 100644
index 0000000..6a94e56
--- /dev/null
+++ b/packages/rpm/seaudit.console.in
@@ -0,0 +1,3 @@
+USER=root
+PROGRAM=@sbindir@/seaudit
+SESSION=true
diff --git a/packages/rpm/seaudit.desktop.in b/packages/rpm/seaudit.desktop.in
new file mode 100644
index 0000000..b7873d3
--- /dev/null
+++ b/packages/rpm/seaudit.desktop.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=SELinux Audit Log Analysis
+GenericName=SELinux Audit Log Analysis Tool
+Comment=This tool parses syslog files and extracts all policy, AVC, and change of boolean messages
+Icon=seaudit.png
+Exec=@bindir@/seaudit
+Type=Application
+Terminal=false
+Encoding=UTF-8
+Categories=System;
+X-Desktop-File-Install-Version=0.2
+StartupNotify=true
diff --git a/packages/rpm/seaudit.pam b/packages/rpm/seaudit.pam
new file mode 100644
index 0000000..c7d67e3
--- /dev/null
+++ b/packages/rpm/seaudit.pam
@@ -0,0 +1,4 @@
+#%PAM-1.0
+auth include config-util
+account include config-util
+session include config-util
diff --git a/packages/rpm/sediffx.desktop.in b/packages/rpm/sediffx.desktop.in
new file mode 100644
index 0000000..47fcae3
--- /dev/null
+++ b/packages/rpm/sediffx.desktop.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=SELinux Policy Difference
+GenericName=SELinux Policy Difference tool
+Comment=This tool compares two policy files
+Exec=@bindir@/sediffx
+Type=Application
+Terminal=false
+Encoding=UTF-8
+Categories=System;
+X-Desktop-File-Install-Version=0.2
+StartupNotify=true
+Icon=sediffx.png
diff --git a/packages/rpm/setools.spec b/packages/rpm/setools.spec
new file mode 100644
index 0000000..5e0ad15
--- /dev/null
+++ b/packages/rpm/setools.spec
@@ -0,0 +1,661 @@
+%define setools_maj_ver 3.3
+%define setools_min_ver 7
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
+
+Name: setools
+Version: %{setools_maj_ver}.%{setools_min_ver}
+Release: 1%{?dist}
+License: GPLv2
+URL: http://oss.tresys.com/projects/setools
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+Source: http://oss.tresys.com/projects/setools/chrome/site/dists/setools-%{version}/setools-%{version}.tar.bz2
+Source1: setools.pam
+Source2: apol.desktop
+Source3: seaudit.desktop
+Source4: sediffx.desktop
+Patch1: setools-python.patch
+Summary: Policy analysis tools for SELinux
+Group: System Environment/Base
+Requires: setools-libs = %{version}-%{release} setools-libs-tcl = %{version}-%{release} setools-gui = %{version}-%{release} setools-console = %{version}-%{release}
+
+# external requirements
+%define autoconf_ver 2.59
+%define bwidget_ver 1.8
+%define java_ver 1.2
+%define gtk_ver 2.8
+%define python_ver 2.3
+%define sepol_ver 1.12.27
+%define selinux_ver 1.30
+%define sqlite_ver 3.2.0
+%define swig_ver 1.3.28
+%define tcltk_ver 8.4.9
+
+%description
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This meta-package depends upon the main packages necessary to run
+SETools.
+
+%package libs
+License: LGPLv2
+Summary: Policy analysis support libraries for SELinux
+Group: System Environment/Libraries
+Requires: libselinux >= %{selinux_ver} libsepol >= %{sepol_ver} sqlite >= %{sqlite_ver}
+BuildRequires: flex bison pkgconfig
+BuildRequires: glibc-devel libstdc++-devel gcc gcc-c++
+BuildRequires: libselinux-devel >= %{selinux_ver} libsepol-devel >= %{sepol_ver}
+BuildRequires: libsepol-static >= %{sepol_ver}
+BuildRequires: sqlite-devel >= %{sqlite_ver} libxml2-devel
+BuildRequires: tcl-devel >= %{tcltk_ver}
+BuildRequires: autoconf >= %{autoconf_ver} automake
+
+%description libs
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes the following run-time libraries:
+
+ libapol policy analysis library
+ libpoldiff semantic policy difference library
+ libqpol library that abstracts policy internals
+ libseaudit parse and filter SELinux audit messages in log files
+ libsefs SELinux file contexts library
+
+%package libs-python
+License: LGPLv2
+Summary: Python bindings for SELinux policy analysis
+Group: Development/Languages
+Requires: setools-libs = %{version}-%{release} python2 >= %{python_ver}
+BuildRequires: python2-devel >= %{python_ver} swig >= %{swig_ver}
+
+%description libs-python
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes Python bindings for the following libraries:
+
+ libapol policy analysis library
+ libpoldiff semantic policy difference library
+ libqpol library that abstracts policy internals
+ libseaudit parse and filter SELinux audit messages in log files
+ libsefs SELinux file contexts library
+
+%package libs-java
+License: LGPLv2
+Summary: Java bindings for SELinux policy analysis
+Group: Development/Languages
+Requires: setools-libs = %{version}-%{release} java >= %{java_ver}
+BuildRequires: java-devel >= %{java_ver} swig >= %{swig_ver}
+
+%description libs-java
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes Java bindings for the following libraries:
+
+ libapol policy analysis library
+ libpoldiff semantic policy difference library
+ libqpol library that abstracts policy internals
+ libseaudit parse and filter SELinux audit messages in log files
+ libsefs SELinux file contexts library
+
+%package libs-tcl
+License: LGPLv2
+Summary: Tcl bindings for SELinux policy analysis
+Group: Development/Languages
+Requires: setools-libs = %{version}-%{release} tcl >= %{tcltk_ver}
+BuildRequires: tcl-devel >= %{tcltk_ver} swig >= %{swig_ver}
+
+%description libs-tcl
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes Tcl bindings for the following libraries:
+
+ libapol policy analysis library
+ libpoldiff semantic policy difference library
+ libqpol library that abstracts policy internals
+ libseaudit parse and filter SELinux audit messages in log files
+ libsefs SELinux file contexts library
+
+%package devel
+License: LGPLv2
+Summary: Policy analysis development files for SELinux
+Group: Development/Libraries
+Requires: libselinux-devel >= %{selinux_ver} libsepol-devel >= %{sepol_ver} setools-libs = %{version}-%{release}
+BuildRequires: sqlite-devel >= %{sqlite_ver} libxml2-devel
+
+%description devel
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes header files and archives for the following
+libraries:
+
+ libapol policy analysis library
+ libpoldiff semantic policy difference library
+ libqpol library that abstracts policy internals
+ libseaudit parse and filter SELinux audit messages in log files
+ libsefs SELinux file contexts library
+
+%package console
+Summary: Policy analysis command-line tools for SELinux
+Group: System Environment/Base
+License: GPLv2
+Requires: setools-libs = %{version}-%{release}
+Requires: libselinux >= %{selinux_ver}
+
+%description console
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes the following console tools:
+
+ seaudit-report audit log analysis tool
+ sechecker SELinux policy checking tool
+ secmds command line tools: seinfo, sesearch, findcon,
+ replcon, and indexcon
+ sediff semantic policy difference tool
+
+%package gui
+Summary: Policy analysis graphical tools for SELinux
+Group: System Environment/Base
+Requires: tcl >= %{tcltk_ver} tk >= %{tcltk_ver} bwidget >= %{bwidget_ver}
+Requires: setools-libs = %{version}-%{release} setools-libs-tcl = %{version}-%{release}
+Requires: glib2 gtk2 >= %{gtk_ver} usermode
+BuildRequires: gtk2-devel >= %{gtk_ver} libglade2-devel libxml2-devel tk-devel >= %{tcltk_ver}
+BuildRequires: desktop-file-utils
+
+%description gui
+SETools is a collection of graphical tools, command-line tools, and
+libraries designed to facilitate SELinux policy analysis.
+
+This package includes the following graphical tools:
+
+ apol policy analysis tool
+ seaudit audit log analysis tool
+ sediffx semantic policy difference tool
+
+%define setoolsdir %{_datadir}/setools-%{setools_maj_ver}
+%define pkg_py_lib %{python_sitelib}/setools
+%define pkg_py_arch %{python_sitearch}/setools
+%define javajardir %{_datadir}/java
+%define tcllibdir %{_libdir}/setools
+
+%prep
+%setup -q
+%patch1 -p 1 -b .python
+
+%build
+%configure --libdir=%{_libdir} --disable-bwidget-check --disable-selinux-check \
+ --enable-swig-python --enable-swig-java --enable-swig-tcl --with-java-prefix=/usr/lib/jvm/java
+# work around issue with gcc 4.3 + gnu99 + swig-generated code:
+sed -i -e 's:$(CC):gcc -std=gnu89:' libseaudit/swig/python/Makefile
+%ifarch sparc sparcv9 sparc64 s390 s390x
+ for file in `find . -name Makefile`; do
+ sed -i -e 's:-fpic:-fPIC:' $file;
+ done
+%endif
+make %{?_smp_mflags}
+
+%install
+rm -rf ${RPM_BUILD_ROOT}
+make DESTDIR=${RPM_BUILD_ROOT} INSTALL="install -p" install
+mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/applications
+mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/pixmaps
+install -d -m 755 ${RPM_BUILD_ROOT}%{_sysconfdir}/pam.d
+install -p -m 644 %{SOURCE1} ${RPM_BUILD_ROOT}%{_sysconfdir}/pam.d/seaudit
+install -d -m 755 ${RPM_BUILD_ROOT}%{_sysconfdir}/security/console.apps
+install -p -m 644 packages/rpm/seaudit.console ${RPM_BUILD_ROOT}%{_sysconfdir}/security/console.apps/seaudit
+install -d -m 755 ${RPM_BUILD_ROOT}%{_datadir}/applications
+install -p -m 644 apol/apol.png ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/apol.png
+install -p -m 644 seaudit/seaudit.png ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/seaudit.png
+install -p -m 644 sediff/sediffx.png ${RPM_BUILD_ROOT}%{_datadir}/pixmaps/sediffx.png
+desktop-file-install --dir ${RPM_BUILD_ROOT}%{_datadir}/applications %{SOURCE2} %{SOURCE3} %{SOURCE4}
+ln -sf consolehelper ${RPM_BUILD_ROOT}/%{_bindir}/seaudit
+# replace absolute symlinks with relative symlinks
+ln -sf ../setools-%{setools_maj_ver}/qpol.jar ${RPM_BUILD_ROOT}/%{javajardir}/qpol.jar
+ln -sf ../setools-%{setools_maj_ver}/apol.jar ${RPM_BUILD_ROOT}/%{javajardir}/apol.jar
+ln -sf ../setools-%{setools_maj_ver}/poldiff.jar ${RPM_BUILD_ROOT}/%{javajardir}/poldiff.jar
+ln -sf ../setools-%{setools_maj_ver}/seaudit.jar ${RPM_BUILD_ROOT}/%{javajardir}/seaudit.jar
+ln -sf ../setools-%{setools_maj_ver}/sefs.jar ${RPM_BUILD_ROOT}/%{javajardir}/sefs.jar
+# remove static libs
+rm -f ${RPM_BUILD_ROOT}/%{_libdir}/*.a
+# ensure permissions are correct
+chmod 0755 ${RPM_BUILD_ROOT}/%{_libdir}/*.so.*
+chmod 0755 ${RPM_BUILD_ROOT}/%{_libdir}/%{name}/*/*.so.*
+chmod 0755 ${RPM_BUILD_ROOT}/%{pkg_py_arch}/*.so.*
+chmod 0755 ${RPM_BUILD_ROOT}/%{setoolsdir}/seaudit-report-service
+chmod 0644 ${RPM_BUILD_ROOT}/%{tcllibdir}/*/pkgIndex.tcl
+
+%clean
+rm -rf ${RPM_BUILD_ROOT}
+
+%files
+%defattr(-,root,root,-)
+
+%files libs
+%defattr(-,root,root,-)
+%doc AUTHORS ChangeLog COPYING COPYING.GPL COPYING.LGPL KNOWN-BUGS NEWS README
+%{_libdir}/libqpol.so.*
+%{_libdir}/libapol.so.*
+%{_libdir}/libpoldiff.so.*
+%{_libdir}/libsefs.so.*
+%{_libdir}/libseaudit.so.*
+%dir %{setoolsdir}
+
+%files libs-python
+%defattr(-,root,root,-)
+%{pkg_py_lib}/
+%ifarch x86_64 ppc64 sparc64 s390x
+%{pkg_py_arch}/
+%endif
+%{python_sitearch}/setools*.egg-info
+
+%files libs-java
+%defattr(-,root,root,-)
+%{_libdir}/libjqpol.so.*
+%{_libdir}/libjapol.so.*
+%{_libdir}/libjpoldiff.so.*
+%{_libdir}/libjseaudit.so.*
+%{_libdir}/libjsefs.so.*
+%{setoolsdir}/*.jar
+%{javajardir}/*.jar
+
+%files libs-tcl
+%defattr(-,root,root,-)
+%dir %{tcllibdir}
+%{tcllibdir}/qpol/
+%{tcllibdir}/apol/
+%{tcllibdir}/poldiff/
+%{tcllibdir}/seaudit/
+%{tcllibdir}/sefs/
+
+%files devel
+%defattr(-,root,root,-)
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/*
+%{_includedir}/qpol/
+%{_includedir}/apol/
+%{_includedir}/poldiff/
+%{_includedir}/seaudit/
+%{_includedir}/sefs/
+
+%files console
+%defattr(-,root,root,-)
+%{_bindir}/seinfo
+%{_bindir}/sesearch
+%{_bindir}/indexcon
+%{_bindir}/findcon
+%{_bindir}/replcon
+%{_bindir}/sechecker
+%{_bindir}/sediff
+%{_bindir}/seaudit-report
+%{setoolsdir}/sechecker-profiles/
+%{setoolsdir}/sechecker_help.txt
+%{setoolsdir}/seaudit-report-service
+%{setoolsdir}/seaudit-report.conf
+%{setoolsdir}/seaudit-report.css
+%{_mandir}/man1/findcon.1.gz
+%{_mandir}/man1/indexcon.1.gz
+%{_mandir}/man1/replcon.1.gz
+%{_mandir}/man1/sechecker.1.gz
+%{_mandir}/man1/sediff.1.gz
+%{_mandir}/man1/seinfo.1.gz
+%{_mandir}/man1/sesearch.1.gz
+%{_mandir}/man8/seaudit-report.8.gz
+
+%files gui
+%defattr(-,root,root,-)
+%{_bindir}/seaudit
+%{_bindir}/sediffx
+%{_bindir}/apol
+%{tcllibdir}/apol_tcl/
+%{setoolsdir}/sediff_help.txt
+%{setoolsdir}/apol_help.txt
+%{setoolsdir}/domaintrans_help.txt
+%{setoolsdir}/file_relabel_help.txt
+%{setoolsdir}/infoflow_help.txt
+%{setoolsdir}/types_relation_help.txt
+%{setoolsdir}/apol_perm_mapping_*
+%{setoolsdir}/seaudit_help.txt
+%{setoolsdir}/*.glade
+%{setoolsdir}/*.png
+%{setoolsdir}/apol.gif
+%{setoolsdir}/dot_seaudit
+%{_mandir}/man1/apol.1.gz
+%{_mandir}/man1/sediffx.1.gz
+%{_mandir}/man8/seaudit.8.gz
+%{_sbindir}/seaudit
+%config(noreplace) %{_sysconfdir}/pam.d/seaudit
+%config(noreplace) %{_sysconfdir}/security/console.apps/seaudit
+%{_datadir}/applications/*
+%attr(0644,root,root) %{_datadir}/pixmaps/*.png
+
+%post libs -p /sbin/ldconfig
+
+%postun libs -p /sbin/ldconfig
+
+%post libs-java -p /sbin/ldconfig
+
+%postun libs-java -p /sbin/ldconfig
+
+%post libs-tcl -p /sbin/ldconfig
+
+%postun libs-tcl -p /sbin/ldconfig
+
+%changelog
+* Fri Apr 30 2010 Spencer Shimko <sshimko@tresys.com> 3.3.7-1
+- New release w/ constraint support and module loading fixes.
+- Dropping qpol patch since it was fixed upstream.
+
+* Tue Aug 11 2009 Dan Walsh <dwalsh@redhat.com> 3.3.6-4
+- Add python bindings for sesearch and seinfo
+
+* Tue Jul 28 2009 Dan Walsh <dwalsh@redhat.com> 3.3.6-3
+- Fix qpol install of include files
+
+* Sun Jul 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.3.6-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Wed Jul 22 2009 Chris PeBenito <cpebenito@tresys.com> 3.3.6-1
+- New upstream release.
+
+* Sun Apr 5 2009 Dan Horák <dan[at]danny.cz> - 3.3.5-8
+- don't expect that java-devel resolves as gcj
+
+* Sun Apr 5 2009 Dan Horák <dan[at]danny.cz> - 3.3.5-7
+- add support for s390x
+
+* Wed Feb 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.3.5-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Thu Dec 04 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 3.3.5-5
+- Rebuild for Python 2.6
+
+* Mon Dec 1 2008 Michael Schwendt <mschwendt@fedoraproject.org> - 3.3.5-4
+- Include %%tcllibdir directory in -libs-tcl package.
+
+* Sat Nov 29 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 3.3.5-3
+- Rebuild for Python 2.6
+
+* Wed Sep 17 2008 Dennis Gilmore <dennis@ausil.us> 3.3.5-2
+- fix building in sparc and s390 arches
+
+* Tue Aug 26 2008 Chris PeBenito <cpebenito@tresys.com> 3.3.5-1
+- Update to upstream version 3.3.5.
+
+* Wed Feb 27 2008 Chris PeBenito <cpebenito@tresys.com> 3.3.4-1
+- Fixes gcc 4.3, glibc 2.7, tcl 8.5, and libsepol 2.0.20 issues.
+- Fix policy loading when policy on disk is higher version than the kernel.
+
+* Tue Feb 19 2008 Fedora Release Engineering <rel-eng@fedoraproject.org> - 3.3.2-3
+- Autorebuild for GCC 4.3
+
+* Tue Jan 29 2008 Chris Pebenito <cpebenito@tresys.com> 3.3.2-2.fc9
+- Bump to pick up new libsepol and policy 22.
+
+* Wed Nov 28 2007 Chris Pebenito <cpebenito@tresys.com> 3.3.2-1.fc9
+- Update for 3.3.2.
+
+* Thu Oct 18 2007 Chris PeBenito <cpebenito@tresys.com> 3.3.1-7.fc8
+- Rebuild to fix ppc64 issue.
+
+* Wed Oct 17 2007 Chris PeBenito <cpebenito@tresys.com> 3.3.1-6.fc8
+- Update for 3.3.1.
+
+* Tue Aug 28 2007 Fedora Release Engineering <rel-eng at fedoraproject dot org> - 3.2-4
+- Rebuild for selinux ppc32 issue.
+
+* Fri Jul 20 2007 Dan Walsh <dwalsh@redhat.com> 3.2-3
+- Move to Tresys spec file
+
+* Wed Jun 13 2007 Dan Walsh <dwalsh@redhat.com> 3.2-2
+- Bump for rebuild
+
+* Mon Apr 30 2007 Dan Walsh <dwalsh@redhat.com> 3.2-1
+- Start shipping the rest of the setools command line apps
+
+* Wed Apr 25 2007 Jason Tang <jtang@tresys.com> 3.2-0
+- update to SETools 3.2 release
+
+* Mon Feb 02 2007 Jason Tang <jtang@tresys.com> 3.1-1
+- update to SETools 3.1 release
+
+* Mon Oct 30 2006 Dan Walsh <dwalsh@redhat.com> 3.0-2.fc6
+- bump for fc6
+
+* Thu Oct 26 2006 Dan Walsh <dwalsh@redhat.com> 3.0-2
+- Build on rawhide
+
+* Sun Oct 15 2006 Dan Walsh <dwalsh@redhat.com> 3.0-1
+- Update to upstream
+
+* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - sh: line 0: fg: no job control
+- rebuild
+
+* Tue May 23 2006 Dan Walsh <dwalsh@redhat.com> 2.4-2
+- Remove sqlite include directory
+
+* Wed May 3 2006 Dan Walsh <dwalsh@redhat.com> 2.4-1
+- Update from upstream
+
+* Mon Apr 10 2006 Dan Walsh <dwalsh@redhat.com> 2.3-3
+- Fix help
+- Add icons
+
+* Tue Mar 21 2006 Dan Walsh <dwalsh@redhat.com> 2.3-2
+- Remove console apps for sediff, sediffx and apol
+
+* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 2.3-1.2
+- bump again for double-long bug on ppc(64)
+
+* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 2.3-1.1
+- rebuilt for new gcc4.1 snapshot and glibc changes
+
+* Tue Jan 31 2006 Dan Walsh <dwalsh@redhat.com> 2.3-1
+- Update from upstream
+ * apol:
+ added new MLS components tab for sensitivities,
+ levels, and categories.
+ Changed users tab to support ranges and default
+ levels.
+ added range transition tab for searching range
+ Transition rules.
+ added new tab for network context components.
+ added new tab for file system context components.
+ * libapol:
+ added binpol support for MLS, network contexts,
+ and file system contexts.
+ * seinfo:
+ added command line options for MLS components.
+ added command line options for network contexts
+ and file system contexts.
+ * sesearch:
+ added command line option for searching for rules
+ by conditional boolean name.
+ * seaudit:
+ added new column in the log view for the 'comm'
+ field found in auditd log files.
+ added filters for the 'comm' field and 'message'
+ field.
+ * manpages:
+ added manpages for all tools.
+
+
+
+* Fri Dec 16 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt for new gcj
+
+* Wed Dec 14 2005 Dan Walsh <dwalsh@redhat.com> 2.2-4
+- Fix dessktop files
+- Apply fixes from bkyoung
+
+* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt
+
+* Thu Nov 3 2005 Dan Walsh <dwalsh@redhat.com> 2.2-3
+- Move more gui files out of base into gui
+
+* Thu Nov 3 2005 Dan Walsh <dwalsh@redhat.com> 2.2-2
+- Move sediff from gui to main package
+
+* Thu Nov 3 2005 Dan Walsh <dwalsh@redhat.com> 2.2-1
+- Upgrade to upstream version
+
+* Thu Oct 13 2005 Dan Walsh <dwalsh@redhat.com> 2.1.3-1
+- Upgrade to upstream version
+
+* Mon Oct 10 2005 Tomas Mraz <tmraz@redhat.com> 2.1.2-3
+- use include instead of pam_stack in pam config
+
+* Thu Sep 1 2005 Dan Walsh <dwalsh@redhat.com> 2.1.2-2
+- Fix spec file
+
+* Thu Sep 1 2005 Dan Walsh <dwalsh@redhat.com> 2.1.2-1
+- Upgrade to upstream version
+
+* Thu Aug 18 2005 Florian La Roche <laroche@redhat.com>
+- do not package debug files into the -devel package
+
+* Wed Aug 17 2005 Jeremy Katz <katzj@redhat.com> - 2.1.1-3
+- rebuild against new cairo
+
+* Wed May 25 2005 Dan Walsh <dwalsh@redhat.com> 2.1.1-0
+- Upgrade to upstream version
+
+* Mon May 23 2005 Bill Nottingham <notting@redhat.com> 2.1.0-5
+- put libraries in the right place (also puts debuginfo in the right
+ package)
+- add %%defattr for -devel too
+
+* Thu May 12 2005 Dan Walsh <dwalsh@redhat.com> 2.1.0-4
+- Move sepcut to gui apps.
+
+* Fri May 6 2005 Dan Walsh <dwalsh@redhat.com> 2.1.0-3
+- Fix Missing return code.
+
+* Wed Apr 20 2005 Dan Walsh <dwalsh@redhat.com> 2.1.0-2
+- Fix requires line
+
+* Tue Apr 19 2005 Dan Walsh <dwalsh@redhat.com> 2.1.0-1
+- Update to latest from tresys
+
+* Tue Apr 5 2005 Dan Walsh <dwalsh@redhat.com> 2.0.0-2
+- Fix buildrequires lines in spec file
+
+* Tue Mar 2 2005 Dan Walsh <dwalsh@redhat.com> 2.0.0-1
+- Update to latest from tresys
+
+* Mon Nov 29 2004 Dan Walsh <dwalsh@redhat.com> 1.5.1-6
+- add FALLBACK=true to /etc/security/console.apps/apol
+
+* Wed Nov 10 2004 Dan Walsh <dwalsh@redhat.com> 1.5.1-3
+- Add badtcl patch from Tresys.
+
+* Mon Nov 8 2004 Dan Walsh <dwalsh@redhat.com> 1.5.1-2
+- Apply malloc problem patch provided by Sami Farin
+
+* Mon Nov 1 2004 Dan Walsh <dwalsh@redhat.com> 1.5.1-1
+- Update to latest from Upstream
+
+* Wed Oct 6 2004 Dan Walsh <dwalsh@redhat.com> 1.4.1-5
+- Update tresys patch
+
+* Mon Oct 4 2004 Dan Walsh <dwalsh@redhat.com> 1.4.1-4
+- Fix directory ownership
+
+* Thu Jul 8 2004 Dan Walsh <dwalsh@redhat.com> 1.4.1-1
+- Latest from Tresys
+
+* Wed Jun 23 2004 Dan Walsh <dwalsh@redhat.com> 1.4-5
+- Add build requires libselinux
+
+* Tue Jun 22 2004 Dan Walsh <dwalsh@redhat.com> 1.4-4
+- Add support for policy.18
+
+* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Thu Jun 10 2004 Dan Walsh <dwalsh@redhat.com> 1.4-2
+- Fix install locations of policy_src_dir
+
+* Wed Jun 2 2004 Dan Walsh <dwalsh@redhat.com> 1.4-1
+- Update to latest from TRESYS.
+
+* Tue Jun 1 2004 Dan Walsh <dwalsh@redhat.com> 1.3-3
+- Make changes to work with targeted/strict policy
+* Fri Apr 16 2004 Dan Walsh <dwalsh@redhat.com> 1.3-2
+- Take out requirement for policy file
+
+* Fri Apr 16 2004 Dan Walsh <dwalsh@redhat.com> 1.3-1
+- Fix doc location
+
+* Fri Apr 16 2004 Dan Walsh <dwalsh@redhat.com> 1.3-1
+- Latest from TRESYS
+
+* Tue Apr 13 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-8
+- fix location of policy.conf file
+
+* Tue Apr 6 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-7
+- Obsolete setools-devel
+* Tue Apr 6 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-6
+- Fix location of
+* Tue Apr 6 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-5
+- Remove devel libraries
+- Fix installdir for lib64
+
+* Sat Apr 3 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-4
+- Add usr_t file read to policy
+
+* Thu Mar 25 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-3
+- Use tcl8.4
+
+* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Fri Feb 6 2004 Dan Walsh <dwalsh@redhat.com> 1.2.1-1
+- New patch
+
+* Fri Feb 6 2004 Dan Walsh <dwalsh@redhat.com> 1.2-1
+- Latest upstream version
+
+* Tue Dec 30 2003 Dan Walsh <dwalsh@redhat.com> 1.1.1-1
+- New version from upstream
+- Remove seuser.te. Now in policy file.
+
+* Tue Dec 30 2003 Dan Walsh <dwalsh@redhat.com> 1.1-2
+- Add Defattr to devel
+- move libs to base kit
+
+* Fri Dec 19 2003 Dan Walsh <dwalsh@redhat.com> 1.1-1
+- Update to latest code from tresys
+- Break into three separate packages for cmdline, devel and gui
+- Incorporate the tcl patch
+
+* Mon Dec 15 2003 Jens Petersen <petersen@redhat.com> - 1.0.1-3
+- apply setools-1.0.1-tcltk.patch to build against tcl/tk 8.4
+- buildrequire tk-devel
+
+* Thu Nov 20 2003 Dan Walsh <dwalsh@redhat.com> 1.0.1-2
+- Add Bwidgets to this RPM
+
+* Tue Nov 4 2003 Dan Walsh <dwalsh@redhat.com> 1.0.1-1
+- Upgrade to 1.0.1
+
+* Wed Oct 15 2003 Dan Walsh <dwalsh@redhat.com> 1.0-6
+- Clean up build
+
+* Tue Oct 14 2003 Dan Walsh <dwalsh@redhat.com> 1.0-5
+- Update with correct seuser.te
+
+* Wed Oct 1 2003 Dan Walsh <dwalsh@redhat.com> 1.0-4
+- Update with final release from Tresys
+
+* Mon Jun 2 2003 Dan Walsh <dwalsh@redhat.com> 1.0-1
+- Initial version
diff --git a/seaudit/Makefile.am b/seaudit/Makefile.am
new file mode 100644
index 0000000..1987c99
--- /dev/null
+++ b/seaudit/Makefile.am
@@ -0,0 +1,90 @@
+setoolsdir = @setoolsdir@
+bin_PROGRAMS = seaudit-report
+sbin_PROGRAMS = seaudit
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEAUDIT_CFLAGS@
+
+seaudit_CFLAGS = $(AM_CFLAGS) \
+ @GTK_CFLAGS@ @PIXBUF_CFLAGS@ @GLADE_CFLAGS@ @GTHREAD_CFLAGS@
+seaudit_report_CFLAGS = $(AM_CFLAGS) -DAPOL_INSTALL_DIR='"${setoolsdir}"'
+
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+# need the -rdynamic flag below - glade uses dlopen() upon seaudit callbacks
+seaudit_LDFLAGS = $(AM_LDFLAGS) \
+ @GTK_LIBS@ @PIXBUF_LIBS@ @GLADE_LIBS@ @GTHREAD_LIBS@ -rdynamic
+
+LDADD = @SELINUX_LIB_FLAG@ @SEAUDIT_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+
+dist_setools_DATA = \
+ seaudit.glade \
+ seaudit_help.txt \
+ seaudit-report.conf \
+ seaudit-report.css \
+ seaudit.png seaudit-small.png
+
+nodist_setools_DATA = \
+ dot_seaudit \
+ seaudit-report-service
+
+seaudit_SOURCES = \
+ filter_view.c filter_view.h \
+ message_view.c message_view.h \
+ modify_view.c modify_view.h \
+ open_policy_window.c open_policy_window.h \
+ policy_components_view.c policy_components_view.h \
+ policy_view.c policy_view.h \
+ preferences.c preferences.h \
+ preferences_view.c preferences_view.h \
+ progress.c progress.h \
+ report_window.c report_window.h \
+ seaudit.c seaudit.h \
+ toplevel.c toplevel.h \
+ utilgui.c utilgui.h
+
+seaudit_DEPENDENCIES = $(top_builddir)/libseaudit/src/libseaudit.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libqpol/src/libqpol.so
+
+dot_seaudit: dot_seaudit.in Makefile
+ sed -e 's|\@setoolsdir\@|$(setoolsdir)|g' $< > $@
+
+seaudit_report_SOURCES = seaudit-report.c
+seaudit_report_DEPENDENCIES = $(top_builddir)/libseaudit/src/libseaudit.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libqpol/src/libqpol.so
+
+logwatch = $(DESTDIR)/etc/logwatch
+LOGWATCH_GROUP = $(logwatch)/conf/logfiles
+LOGWATCH_SERVICE = $(logwatch)/conf/services
+LOGWATCH_FILTER = $(logwatch)/scripts/services
+
+dist_noinst_DATA = dot_seaudit.in \
+ seaudit-report-group.conf \
+ seaudit-report-service.conf \
+ seaudit-report-service.in
+
+seaudit-report-service: seaudit-report-service.in Makefile
+ sed -e 's|\@bindir\@|$(bindir)|g' $< > $@
+
+install-logwatch: $(dist_noinst_DATA) seaudit-report-service
+ mkdir -p -- $(LOGWATCH_GROUP)
+ install -m 644 seaudit-report-group.conf $(LOGWATCH_GROUP)
+ mkdir -p -- $(LOGWATCH_SERVICE)
+ install -m 644 seaudit-report-service.conf $(LOGWATCH_SERVICE)
+ mkdir -p -- $(LOGWATCH_FILTER)
+ install -m 755 seaudit-report-service $(LOGWATCH_FILTER)
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+$(top_builddir)/libsefs/src/libsefs.so:
+ $(MAKE) -C $(top_builddir)/libsefs/src $(notdir $@)
+
+.PHONY: install-logwatch
+
+CLEANFILES = dot_seaudit seaudit-report-service
diff --git a/seaudit/dot_seaudit.in b/seaudit/dot_seaudit.in
new file mode 100644
index 0000000..2852819
--- /dev/null
+++ b/seaudit/dot_seaudit.in
@@ -0,0 +1,12 @@
+# Configuration file for seaudit - an audit log tool for Security
+# Enhanced Linux. This file is auto-generated by the build system.
+
+DEFAULT_LOG_FILE /var/log/audit/audit.log
+DEFAULT_POLICY_FILE
+DEFAULT_REPORT_CONFIG_FILE @setoolsdir@/seaudit-report.conf
+DEFAULT_REPORT_CSS_FILE @setoolsdir@/seaudit-report.css
+RECENT_LOG_FILES
+RECENT_POLICY_FILES
+LOG_COLUMNS_HIDDEN path_field:src_usr_field:src_role_field:tgt_usr_field:tgt_role_field:inode_field:pid_field:
+REAL_TIME_LOG_MONITORING 0
+REAL_TIME_UPDATE_INTERVAL 1000
diff --git a/seaudit/filter_view.c b/seaudit/filter_view.c
new file mode 100644
index 0000000..e0d8745
--- /dev/null
+++ b/seaudit/filter_view.c
@@ -0,0 +1,1127 @@
+/**
+ * @file
+ * Run the dialog to modify a particular filter.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "filter_view.h"
+#include "policy_components_view.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/policy-query.h>
+#include <apol/mls-query.h>
+#include <apol/util.h>
+#include <glade/glade.h>
+
+struct context_item
+{
+ GtkButton *button;
+ GtkEntry *entry;
+ apol_vector_t *items;
+};
+
+struct date_item
+{
+ GtkComboBox *month;
+ GtkSpinButton *day, *hour, *minute, *second;
+ GtkFrame *frame;
+};
+
+struct filter_view
+{
+ toplevel_t *top;
+ seaudit_filter_t *filter;
+ GladeXML *xml;
+
+ GtkDialog *dialog;
+
+ GtkEntry *name_entry;
+ GtkComboBox *match_combo;
+
+ struct context_item suser, srole, stype, smls_lvl, smls_clr, tuser, trole, ttype, tmls_lvl, tmls_clr, obj_class;
+ GtkButton *context_clear_button;
+
+ GtkEntry *ipaddr_entry, *port_entry, *netif_entry, *exe_entry, *path_entry, *host_entry, *comm_entry;
+ GtkComboBox *message_combo;
+ GtkButton *other_clear_button;
+
+ GtkRadioButton *date_none_radio, *date_before_radio, *date_after_radio, *date_between_radio;
+ struct date_item dates[2];
+ GtkTextBuffer *description_buffer;
+};
+
+/**
+ * Initialize pointers to widgets on the context tab.
+ */
+static void filter_view_init_widgets_context(struct filter_view *fv)
+{
+ fv->suser.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSUserButton"));
+ fv->srole.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSRoleButton"));
+ fv->stype.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSTypeButton"));
+ fv->smls_lvl.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSMLSLVLButton"));
+ fv->smls_clr.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewSMLSCLRButton"));
+ fv->tuser.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTUserButton"));
+ fv->trole.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTRoleButton"));
+ fv->ttype.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTTypeButton"));
+ fv->tmls_lvl.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTMLSLVLButton"));
+ fv->tmls_clr.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewTMLSCLRButton"));
+ fv->obj_class.button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewClassButton"));
+ assert(fv->suser.button != NULL && fv->srole.button != NULL && fv->stype.button != NULL && fv->smls_lvl.button != NULL && fv->smls_clr.button != NULL &&
+ fv->tuser.button != NULL && fv->trole.button != NULL && fv->ttype.button != NULL && fv->tmls_lvl.button != NULL && fv->tmls_clr.button != NULL && fv->obj_class.button != NULL);
+
+ fv->suser.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSUserEntry"));
+ fv->srole.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSRoleEntry"));
+ fv->stype.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSTypeEntry"));
+ fv->smls_lvl.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSMLSLVLEntry"));
+ fv->smls_clr.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewSMLSCLREntry"));
+ fv->tuser.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTUserEntry"));
+ fv->trole.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTRoleEntry"));
+ fv->ttype.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTTypeEntry"));
+ fv->tmls_lvl.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTMLSLVLEntry"));
+ fv->tmls_clr.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewTMLSCLREntry"));
+ fv->obj_class.entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewClassEntry"));
+ assert(fv->suser.entry != NULL && fv->srole.entry != NULL && fv->stype.entry != NULL && fv->smls_lvl.entry != NULL && fv->smls_clr.entry != NULL &&
+ fv->tuser.entry != NULL && fv->trole.entry != NULL && fv->ttype.entry != NULL && fv->tmls_lvl.entry != NULL && fv->tmls_clr.entry != NULL && fv->obj_class.entry != NULL);
+ g_object_set_data(G_OBJECT(fv->suser.entry), "data", &fv->suser);
+ g_object_set_data(G_OBJECT(fv->srole.entry), "data", &fv->srole);
+ g_object_set_data(G_OBJECT(fv->stype.entry), "data", &fv->stype);
+ g_object_set_data(G_OBJECT(fv->smls_lvl.entry), "data", &fv->smls_lvl);
+ g_object_set_data(G_OBJECT(fv->smls_clr.entry), "data", &fv->smls_clr);
+ g_object_set_data(G_OBJECT(fv->tuser.entry), "data", &fv->tuser);
+ g_object_set_data(G_OBJECT(fv->trole.entry), "data", &fv->trole);
+ g_object_set_data(G_OBJECT(fv->ttype.entry), "data", &fv->ttype);
+ g_object_set_data(G_OBJECT(fv->tmls_lvl.entry), "data", &fv->tmls_lvl);
+ g_object_set_data(G_OBJECT(fv->tmls_clr.entry), "data", &fv->tmls_clr);
+ g_object_set_data(G_OBJECT(fv->obj_class.entry), "data", &fv->obj_class);
+
+ fv->context_clear_button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewContextClearButton"));
+ assert(fv->context_clear_button != NULL);
+}
+
+/**
+ * Initialize pointers to widgets on the other tab.
+ */
+static void filter_view_init_widgets_other(struct filter_view *fv)
+{
+ fv->ipaddr_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewIPAddrEntry"));
+ fv->port_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewPortEntry"));
+ fv->netif_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewNetIfEntry"));
+ assert(fv->ipaddr_entry != NULL && fv->port_entry != NULL && fv->netif_entry != NULL);
+
+ fv->exe_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewExeEntry"));
+ fv->path_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewPathEntry"));
+ fv->host_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewHostEntry"));
+ fv->comm_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewCommEntry"));
+ assert(fv->exe_entry != NULL && fv->path_entry != NULL && fv->host_entry != NULL && fv->comm_entry != NULL);
+
+ fv->message_combo = GTK_COMBO_BOX(glade_xml_get_widget(fv->xml, "FilterViewMessageCombo"));
+ assert(fv->message_combo != NULL);
+
+ fv->other_clear_button = GTK_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewOtherClearButton"));
+ assert(fv->other_clear_button != NULL);
+}
+
+/**
+ * Initialize pointers to widgets on the date tab.
+ */
+static void filter_view_init_widgets_date(struct filter_view *fv)
+{
+ static const char *widgets[2][6] = {
+ {"FilterViewDateStartFrame", "FilterViewDateStartMonthCombo", "FilterViewDateStartDaySpin",
+ "FilterViewDateStartHourSpin", "FilterViewDateStartMinuteSpin", "FilterViewDateStartSecondSpin"},
+ {"FilterViewDateEndFrame", "FilterViewDateEndMonthCombo", "FilterViewDateEndDaySpin",
+ "FilterViewDateEndHourSpin", "FilterViewDateEndMinuteSpin", "FilterViewDateEndSecondSpin"}
+ };
+ size_t i;
+ fv->date_none_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateNoneRadio"));
+ fv->date_before_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateBeforeRadio"));
+ fv->date_after_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateAfterRadio"));
+ fv->date_between_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(fv->xml, "FilterViewDateBetweenRadio"));
+ assert(fv->date_none_radio != NULL && fv->date_before_radio != NULL && fv->date_after_radio != NULL
+ && fv->date_between_radio != NULL);
+
+ for (i = 0; i < 2; i++) {
+ fv->dates[i].frame = GTK_FRAME(glade_xml_get_widget(fv->xml, widgets[i][0]));
+ fv->dates[i].month = GTK_COMBO_BOX(glade_xml_get_widget(fv->xml, widgets[i][1]));
+ fv->dates[i].day = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][2]));
+ fv->dates[i].hour = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][3]));
+ fv->dates[i].minute = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][4]));
+ fv->dates[i].second = GTK_SPIN_BUTTON(glade_xml_get_widget(fv->xml, widgets[i][5]));
+ assert(fv->dates[i].frame != NULL && fv->dates[i].month != NULL && fv->dates[i].day != NULL &&
+ fv->dates[i].hour != NULL && fv->dates[i].minute != NULL && fv->dates[i].second != NULL);
+ }
+}
+
+static void filter_view_init_widgets(struct filter_view *fv, GtkWindow * parent)
+{
+ GtkTextView *description_view;
+
+ fv->dialog = GTK_DIALOG(glade_xml_get_widget(fv->xml, "FilterWindow"));
+ assert(fv->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(fv->dialog), parent);
+
+ fv->name_entry = GTK_ENTRY(glade_xml_get_widget(fv->xml, "FilterViewNameEntry"));
+ fv->match_combo = GTK_COMBO_BOX(glade_xml_get_widget(fv->xml, "FilterViewMatchCombo"));
+ assert(fv->name_entry != NULL && fv->match_combo);
+
+ filter_view_init_widgets_context(fv);
+ filter_view_init_widgets_other(fv);
+ filter_view_init_widgets_date(fv);
+
+ fv->description_buffer = gtk_text_buffer_new(NULL);
+#ifdef GTK_2_8
+ g_object_ref_sink(fv->description_buffer);
+#endif
+ description_view = GTK_TEXT_VIEW(glade_xml_get_widget(fv->xml, "FilterViewDescView"));
+ assert(description_view != NULL);
+ gtk_text_view_set_buffer(description_view, fv->description_buffer);
+}
+
+/********** functions that copies filter object values to widget **********/
+
+/**
+ * Get the vector of strings from the accessor function. If the
+ * vector is NULL then clear the entry's contents; otherwies set the
+ * entry to the vector of strings, comma delimited.
+ */
+static void filter_view_context_item_to_entry(struct filter_view *fv, struct context_item *item)
+{
+ if (item->items == NULL) {
+ gtk_entry_set_text(item->entry, "");
+ } else {
+ GString *s = g_string_new("");
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(item->items); i++) {
+ char *t = apol_vector_get_element(item->items, i);
+ if (i > 0) {
+ g_string_append(s, ", ");
+ }
+ g_string_append(s, t);
+ }
+ gtk_entry_set_text(item->entry, s->str);
+ g_string_free(s, TRUE);
+ }
+}
+
+static void filter_view_context_items_to_entries(struct filter_view *fv)
+{
+ filter_view_context_item_to_entry(fv, &fv->suser);
+ filter_view_context_item_to_entry(fv, &fv->srole);
+ filter_view_context_item_to_entry(fv, &fv->stype);
+ filter_view_context_item_to_entry(fv, &fv->smls_lvl);
+ filter_view_context_item_to_entry(fv, &fv->smls_clr);
+ filter_view_context_item_to_entry(fv, &fv->tuser);
+ filter_view_context_item_to_entry(fv, &fv->trole);
+ filter_view_context_item_to_entry(fv, &fv->ttype);
+ filter_view_context_item_to_entry(fv, &fv->tmls_lvl);
+ filter_view_context_item_to_entry(fv, &fv->tmls_clr);
+ filter_view_context_item_to_entry(fv, &fv->obj_class);
+}
+
+static void filter_view_init_context(struct filter_view *fv)
+{
+ const apol_vector_t *v;
+ v = seaudit_filter_get_source_user(fv->filter);
+ if (v != NULL && (fv->suser.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_source_role(fv->filter);
+ if (v != NULL && (fv->srole.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_source_type(fv->filter);
+ if (v != NULL && (fv->stype.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_source_mls_lvl(fv->filter);
+ if (v != NULL && (fv->smls_lvl.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_source_mls_clr(fv->filter);
+ if (v != NULL && (fv->smls_clr.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+
+ v = seaudit_filter_get_target_user(fv->filter);
+ if (v != NULL && (fv->tuser.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_target_role(fv->filter);
+ if (v != NULL && (fv->trole.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_target_type(fv->filter);
+ if (v != NULL && (fv->ttype.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_target_mls_lvl(fv->filter);
+ if (v != NULL && (fv->tmls_lvl.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ v = seaudit_filter_get_target_mls_clr(fv->filter);
+ if (v != NULL && (fv->tmls_clr.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+
+ v = seaudit_filter_get_target_class(fv->filter);
+ if (v != NULL && (fv->obj_class.items = apol_vector_create_from_vector(v, apol_str_strdup, NULL, free)) == NULL) {
+ toplevel_ERR(fv->top, "Error initializing context tab: %s", strerror(errno));
+ return;
+ }
+ filter_view_context_items_to_entries(fv);
+}
+
+/**
+ * Get the string from the accessor function. If the returned string
+ * is NULL then clear the entry's contents; otherwise set the entry to
+ * the returned string.
+ */
+static void filter_view_init_entry(struct filter_view *fv, const char *(*accessor) (const seaudit_filter_t *), GtkEntry * entry)
+{
+ const char *s = accessor(fv->filter);
+ if (s == NULL) {
+ s = "";
+ }
+ gtk_entry_set_text(entry, s);
+}
+
+static void filter_view_init_other(struct filter_view *fv)
+{
+ char s[32];
+ filter_view_init_entry(fv, seaudit_filter_get_anyaddr, fv->ipaddr_entry);
+ if (seaudit_filter_get_anyport(fv->filter) <= 0) {
+ s[0] = '\0';
+ } else {
+ snprintf(s, 32, "%d", seaudit_filter_get_anyport(fv->filter));
+ }
+ gtk_entry_set_text(fv->port_entry, s);
+ filter_view_init_entry(fv, seaudit_filter_get_netif, fv->netif_entry);
+ filter_view_init_entry(fv, seaudit_filter_get_executable, fv->exe_entry);
+ filter_view_init_entry(fv, seaudit_filter_get_path, fv->path_entry);
+ filter_view_init_entry(fv, seaudit_filter_get_host, fv->host_entry);
+ filter_view_init_entry(fv, seaudit_filter_get_command, fv->comm_entry);
+ switch (seaudit_filter_get_message_type(fv->filter)) {
+ case SEAUDIT_AVC_DENIED:
+ gtk_combo_box_set_active(fv->message_combo, 1);
+ break;
+ case SEAUDIT_AVC_GRANTED:
+ gtk_combo_box_set_active(fv->message_combo, 2);
+ break;
+ default:
+ gtk_combo_box_set_active(fv->message_combo, 0);
+ }
+}
+
+static void filter_view_init_date(struct filter_view *fv)
+{
+ const struct tm *start, *end;
+ struct tm values[2];
+ int has_value[2] = { 0, 0 };
+ seaudit_filter_date_match_e match;
+ size_t i;
+
+ seaudit_filter_get_date(fv->filter, &start, &end, &match);
+ if (start == NULL && end == NULL) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_none_radio), TRUE);
+ } else {
+ if (match == SEAUDIT_FILTER_DATE_MATCH_BEFORE) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_before_radio), TRUE);
+ } else if (match == SEAUDIT_FILTER_DATE_MATCH_AFTER) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_after_radio), TRUE);
+ }
+ memcpy(values + 0, start, sizeof(values[0]));
+ has_value[0] = 1;
+ }
+ if (match == SEAUDIT_FILTER_DATE_MATCH_BETWEEN) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fv->date_between_radio), TRUE);
+ memcpy(values + 1, end, sizeof(values[1]));
+ has_value[1] = 1;
+ }
+ for (i = 0; i < 2; i++) {
+ if (has_value[i]) {
+ gtk_combo_box_set_active(fv->dates[i].month, values[i].tm_mon);
+ gtk_spin_button_set_value(fv->dates[i].day, values[i].tm_mday);
+ gtk_spin_button_set_value(fv->dates[i].hour, values[i].tm_hour);
+ gtk_spin_button_set_value(fv->dates[i].minute, values[i].tm_min);
+ gtk_spin_button_set_value(fv->dates[i].second, values[i].tm_sec);
+ } else {
+ gtk_combo_box_set_active(fv->dates[i].month, 0);
+ }
+ }
+}
+
+/**
+ * Copy values from seaudit filter object to GTK+ widgets.
+ */
+static void filter_view_init_dialog(struct filter_view *fv)
+{
+ const char *name = seaudit_filter_get_name(fv->filter);
+ const char *desc = seaudit_filter_get_description(fv->filter);;
+ if (name == NULL) {
+ name = "Untitled";
+ }
+ gtk_entry_set_text(fv->name_entry, name);
+ gtk_combo_box_set_active(fv->match_combo, seaudit_filter_get_match(fv->filter));
+
+ filter_view_init_context(fv);
+ filter_view_init_other(fv);
+ filter_view_init_date(fv);
+
+ if (desc == NULL) {
+ desc = "";
+ }
+ gtk_text_buffer_set_text(fv->description_buffer, desc, -1);
+}
+
+/********** functions that copies widget values to filter object **********/
+
+static void filter_view_apply_context(struct filter_view *fv)
+{
+ if (seaudit_filter_set_source_user(fv->filter, fv->suser.items) < 0 ||
+ seaudit_filter_set_source_role(fv->filter, fv->srole.items) < 0 ||
+ seaudit_filter_set_source_type(fv->filter, fv->stype.items) < 0 ||
+ seaudit_filter_set_source_mls_lvl(fv->filter, fv->smls_lvl.items) < 0 ||
+ seaudit_filter_set_source_mls_clr(fv->filter, fv->smls_clr.items) < 0 ||
+ seaudit_filter_set_target_user(fv->filter, fv->tuser.items) < 0 ||
+ seaudit_filter_set_target_role(fv->filter, fv->trole.items) < 0 ||
+ seaudit_filter_set_target_type(fv->filter, fv->ttype.items) < 0 ||
+ seaudit_filter_set_target_mls_lvl(fv->filter, fv->tmls_lvl.items) < 0 ||
+ seaudit_filter_set_target_mls_clr(fv->filter, fv->tmls_clr.items) < 0 ||
+ seaudit_filter_set_target_class(fv->filter, fv->obj_class.items) < 0) {
+ toplevel_ERR(fv->top, "Error applying context: %s", strerror(errno));
+ }
+}
+
+/**
+ * If the entry is empty, then call the modifier function passing NULL
+ * as the second parameter. Else call the function with the entry's
+ * contents.
+ */
+static void filter_view_apply_entry(struct filter_view *fv, GtkEntry * entry, int (*modifier) (seaudit_filter_t *, const char *))
+{
+ const char *s = gtk_entry_get_text(entry);
+ if (strcmp(s, "") == 0) {
+ s = NULL;
+ }
+ if (modifier(fv->filter, s) < 0) {
+ toplevel_ERR(fv->top, "Error apply settings: %s", strerror(errno));
+ }
+}
+
+/**
+ * Copy values from the other tab to filter object.
+ */
+static void filter_view_apply_other(struct filter_view *fv)
+{
+ const char *s;
+ int port = 0;
+ seaudit_avc_message_type_e message_type;
+
+ filter_view_apply_entry(fv, fv->ipaddr_entry, seaudit_filter_set_anyaddr);
+ s = gtk_entry_get_text(fv->port_entry);
+ if (strcmp(s, "") != 0) {
+ port = atoi(s);
+ }
+ if (seaudit_filter_set_anyport(fv->filter, port) < 0) {
+ toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno));
+ return;
+ }
+ filter_view_apply_entry(fv, fv->netif_entry, seaudit_filter_set_netif);
+ filter_view_apply_entry(fv, fv->exe_entry, seaudit_filter_set_executable);
+ filter_view_apply_entry(fv, fv->path_entry, seaudit_filter_set_path);
+ filter_view_apply_entry(fv, fv->host_entry, seaudit_filter_set_host);
+ filter_view_apply_entry(fv, fv->comm_entry, seaudit_filter_set_command);
+ switch (gtk_combo_box_get_active(fv->message_combo)) {
+ case 1:
+ message_type = SEAUDIT_AVC_DENIED;
+ break;
+ case 2:
+ message_type = SEAUDIT_AVC_GRANTED;
+ break;
+ default:
+ message_type = SEAUDIT_AVC_UNKNOWN;
+ }
+ if (seaudit_filter_set_message_type(fv->filter, message_type) < 0) {
+ toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno));
+ return;
+ }
+}
+
+/**
+ * Returns which date radio button is active:
+ *
+ * -1 if date_none_radio,
+ * else something that can be casted to seaudit_filter_date_match
+ */
+static int filter_view_get_date_match(struct filter_view *fv)
+{
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fv->date_before_radio))) {
+ return SEAUDIT_FILTER_DATE_MATCH_BEFORE;
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fv->date_after_radio))) {
+ return SEAUDIT_FILTER_DATE_MATCH_AFTER;
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fv->date_between_radio))) {
+ return SEAUDIT_FILTER_DATE_MATCH_BETWEEN;
+ }
+ return -1;
+}
+
+/**
+ * Copy values from date tab to the seaudit filter object.
+ */
+static void filter_view_apply_date(struct filter_view *fv)
+{
+ struct tm tm[2];
+ size_t i;
+ int date_match = filter_view_get_date_match(fv);
+ memset(&tm, 0, sizeof(tm));
+ for (i = 0; i < 2; i++) {
+ tm[i].tm_mon = gtk_combo_box_get_active(fv->dates[i].month);
+ tm[i].tm_mday = gtk_spin_button_get_value_as_int(fv->dates[i].day);
+ tm[i].tm_year = 0;
+ tm[i].tm_hour = gtk_spin_button_get_value_as_int(fv->dates[i].hour);
+ tm[i].tm_min = gtk_spin_button_get_value_as_int(fv->dates[i].minute);
+ tm[i].tm_sec = gtk_spin_button_get_value_as_int(fv->dates[i].second);
+ }
+ if (date_match < 0) {
+ seaudit_filter_set_date(fv->filter, NULL, NULL, 0);
+ } else {
+ seaudit_filter_set_date(fv->filter, tm + 0, tm + 1, (seaudit_filter_date_match_e) date_match);
+ }
+}
+
+/**
+ * Copy values from GTK+ widgets to the seaudit filter object.
+ */
+static void filter_view_apply(struct filter_view *fv)
+{
+ GtkTextIter start, end;
+ char *s;
+ seaudit_filter_match_e match = SEAUDIT_FILTER_MATCH_ALL;
+
+ filter_view_apply_entry(fv, fv->name_entry, seaudit_filter_set_name);
+ if (gtk_combo_box_get_active(fv->match_combo) == 1) {
+ match = SEAUDIT_FILTER_MATCH_ANY;
+ }
+ if (seaudit_filter_set_match(fv->filter, match) < 0) {
+ toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno));
+ }
+
+ filter_view_apply_context(fv);
+ filter_view_apply_other(fv);
+ filter_view_apply_date(fv);
+
+ gtk_text_buffer_get_bounds(fv->description_buffer, &start, &end);
+ s = gtk_text_buffer_get_text(fv->description_buffer, &start, &end, FALSE);
+ if (strcmp(s, "") == 0) {
+ free(s);
+ s = NULL;
+ }
+ if (seaudit_filter_set_description(fv->filter, s) < 0) {
+ toplevel_ERR(fv->top, "Error setting filter: %s", strerror(errno));
+ }
+ free(s);
+}
+
+/******************** signal handlers for dialog ********************/
+
+/**
+ * Return a list of users within the currently loaded policy, sorted
+ * alphabetically. If there is no policy loaded then return NULL.
+ */
+static apol_vector_t *filter_view_get_policy_users(struct filter_view *fv)
+{
+ apol_vector_t *policy_items = NULL, *v = NULL;
+ apol_policy_t *p = toplevel_get_policy(fv->top);
+ size_t i;
+ if (p == NULL) {
+ return NULL;
+ }
+ if (apol_user_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) {
+ toplevel_ERR(fv->top, "Error getting a list of policy users: %s", strerror(errno));
+ apol_vector_destroy(&policy_items);
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_user_t *e = apol_vector_get_element(v, i);
+ const char *name;
+ qpol_user_get_name(apol_policy_get_qpol(p), e, &name);
+ if (apol_vector_append(policy_items, (void *)name) < 0) {
+ toplevel_ERR(fv->top, "Error getting a list of policy users: %s", strerror(errno));
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&policy_items);
+ }
+ }
+ apol_vector_destroy(&v);
+ apol_vector_sort(policy_items, apol_str_strcmp, NULL);
+ return policy_items;
+}
+
+/**
+ * Return a list of roles within the currently loaded policy, sorted
+ * alphabetically. If there is no policy loaded then return NULL.
+ */
+static apol_vector_t *filter_view_get_policy_roles(struct filter_view *fv)
+{
+ apol_vector_t *policy_items = NULL, *v = NULL;
+ apol_policy_t *p = toplevel_get_policy(fv->top);
+ size_t i;
+ if (p == NULL) {
+ return NULL;
+ }
+ if (apol_role_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) {
+ toplevel_ERR(fv->top, "Error getting a list of policy roles: %s", strerror(errno));
+ apol_vector_destroy(&policy_items);
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_role_t *e = apol_vector_get_element(v, i);
+ const char *name;
+ qpol_role_get_name(apol_policy_get_qpol(p), e, &name);
+ if (apol_vector_append(policy_items, (void *)name) < 0) {
+ toplevel_ERR(fv->top, "Error getting a list of policy roles: %s", strerror(errno));
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&policy_items);
+ }
+ }
+ apol_vector_destroy(&v);
+ apol_vector_sort(policy_items, apol_str_strcmp, NULL);
+ return policy_items;
+}
+
+/**
+ * Return a list of types (not attributes nor aliases) within the
+ * currently loaded policy, sorted alphabetically. If there is no
+ * policy loaded then return NULL.
+ */
+static apol_vector_t *filter_view_get_policy_types(struct filter_view *fv)
+{
+ apol_vector_t *policy_items = NULL, *v = NULL;
+ apol_policy_t *p = toplevel_get_policy(fv->top);
+ size_t i;
+ if (p == NULL) {
+ return NULL;
+ }
+ if (apol_type_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) {
+ toplevel_ERR(fv->top, "Error getting a list of policy types: %s", strerror(errno));
+ apol_vector_destroy(&policy_items);
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_type_t *e = apol_vector_get_element(v, i);
+ const char *name;
+ qpol_type_get_name(apol_policy_get_qpol(p), e, &name);
+ if (apol_vector_append(policy_items, (void *)name) < 0) {
+ toplevel_ERR(fv->top, "Error getting a list of policy types: %s", strerror(errno));
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&policy_items);
+ }
+ }
+ apol_vector_destroy(&v);
+ apol_vector_sort(policy_items, apol_str_strcmp, NULL);
+ return policy_items;
+}
+
+/**
+ * Return a list of mls levels/clearance (not aliases) within the
+ * currently loaded policy, sorted alphabetically. If there is no
+ * policy loaded then return NULL.
+ */
+static apol_vector_t *filter_view_get_policy_mls_lvl(struct filter_view *fv)
+{
+ apol_vector_t *policy_items = NULL, *v = NULL;
+ apol_policy_t *p = toplevel_get_policy(fv->top);
+ const qpol_iterator_t **cats = NULL;
+
+ size_t i;
+
+ if (p == NULL) {
+ return NULL;
+ }
+
+ if (apol_level_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(&free)) == NULL) {
+ toplevel_ERR(fv->top, "Error getting a list of policy mls levels: %s", strerror(errno));
+ apol_vector_destroy(&policy_items);
+ return NULL;
+ }
+
+ for (i = 0; i < apol_vector_get_size(v); i++)
+ {
+ const char *name = NULL;
+ const char *mls = malloc(100*sizeof(mls));
+
+ const char *cat_name1 = NULL, *cat_name2 = NULL;
+ uint32_t *cat_val1, *cat_val2;
+ const char *cat_low = NULL;
+ const char *cat_high = NULL;
+ bool isrange = false, isrange_end = false, isempty = true;
+ size_t k;
+ qpol_cat_t *c = NULL;
+ const qpol_level_t *e = apol_vector_get_element(v, i);
+
+ qpol_level_get_name(apol_policy_get_qpol(p), e, &name);
+ strcpy(mls, name);
+ if (qpol_level_get_cat_iter(apol_policy_get_qpol(p), e, &cats) < 0){
+ toplevel_ERR(fv->top, "Error getting categories for level: %s", strerror(errno));
+ qpol_iterator_destroy(&cats);
+ }
+ qpol_iterator_get_size(cats, &k);
+
+ if (k > 0){
+ strcat(mls, ":");
+ isempty = true;
+ }
+ if (qpol_iterator_get_item(cats, &c) < 0){
+ toplevel_ERR(fv->top, "Error getting category: %s", strerror(errno));
+ qpol_iterator_destroy(&cats);
+ }
+ qpol_cat_get_value(apol_policy_get_qpol(p), c, &cat_val1);
+ qpol_cat_get_name(apol_policy_get_qpol(p), c, &cat_name1);
+ isrange = false;
+ isrange_end = false;
+
+ for (; !qpol_iterator_end(cats); qpol_iterator_next(cats))
+ {
+ if (qpol_iterator_get_item(cats, &c) < 0){
+ toplevel_ERR(fv->top, "Error getting category: %s", strerror(errno));
+ qpol_iterator_destroy(&cats);
+ }
+ qpol_cat_get_value(apol_policy_get_qpol(p), c, &cat_val2);
+ qpol_cat_get_name(apol_policy_get_qpol(p), c, &cat_name2);
+ if (((int)cat_val2 == ((int)cat_val1 + 1)) && (isrange == false))
+ {
+ cat_low = cat_name1;
+ strcat(mls, cat_low);
+ strcat(mls, ".");
+ isrange = true;
+ isempty = false;
+ }
+ if ((isrange == true) && ((int)cat_val2 == ((int)cat_val1 + 1)))
+ {
+ cat_high = cat_name2;
+ }
+ else if ((isrange == true) && ((int)cat_val2 != ((int)cat_val1 + 1)))
+ {
+ cat_high = cat_name1;
+ isrange_end = true;
+ strcat(mls, cat_high);
+ isempty=false;
+ }
+ if ((isrange == false) && (isempty == false) && ((int)cat_val2 != ((int)cat_val1 + 1)))
+ {
+ strcat(mls, ",");
+ strcat(mls, cat_name2);
+ }
+ cat_val1 = cat_val2;
+ cat_name1 = cat_name2;
+
+ }
+ if ((isrange == true) && (isrange_end == false))
+ {
+ strcat(mls, cat_high);
+ }
+ if (apol_vector_append(policy_items, (void *)mls) < 0) {
+ toplevel_ERR(fv->top, "Error getting a list of policy mls levels: %s", strerror(errno));
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&policy_items);
+ }
+ }
+
+ apol_vector_destroy(&v);
+ qpol_iterator_destroy(&cats);
+ apol_vector_sort(policy_items, apol_str_strcmp, NULL);
+ return policy_items;
+}
+
+/**
+ * Return a list of object classeswithin the currently loaded policy,
+ * sorted alphabetically. If there is no policy loaded then return
+ * NULL.
+ */
+static apol_vector_t *filter_view_get_policy_classes(struct filter_view *fv)
+{
+ apol_vector_t *policy_items = NULL, *v = NULL;
+ apol_policy_t *p = toplevel_get_policy(fv->top);
+ size_t i;
+ if (p == NULL) {
+ return NULL;
+ }
+ if (apol_class_get_by_query(p, NULL, &v) < 0 || (policy_items = apol_vector_create(NULL)) == NULL) {
+ toplevel_ERR(fv->top, "Error getting a list of policy classes: %s", strerror(errno));
+ apol_vector_destroy(&policy_items);
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ const qpol_class_t *e = apol_vector_get_element(v, i);
+ const char *name;
+ qpol_class_get_name(apol_policy_get_qpol(p), e, &name);
+ if (apol_vector_append(policy_items, (void *)name) < 0) {
+ toplevel_ERR(fv->top, "Error getting a list of policy classes: %s", strerror(errno));
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&policy_items);
+ }
+ }
+ apol_vector_destroy(&v);
+ apol_vector_sort(policy_items, apol_str_strcmp, NULL);
+ return policy_items;
+}
+
+static void filter_view_on_suser_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_users(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_users(fv);
+ fv->suser.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source User Items", log_items, policy_items,
+ fv->suser.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->suser);
+}
+
+static void filter_view_on_srole_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_roles(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_roles(fv);
+ fv->srole.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source Role Items", log_items, policy_items,
+ fv->srole.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->srole);
+}
+
+static void filter_view_on_stype_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_types(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_types(fv);
+ fv->stype.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source Type Items", log_items, policy_items,
+ fv->stype.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->stype);
+}
+
+static void filter_view_on_smls_lvl_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+
+ apol_vector_t *log_items = toplevel_get_log_mls_lvl(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv);
+ fv->smls_lvl.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source MLS Level Items", log_items, policy_items,
+ fv->smls_lvl.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->smls_lvl);
+}
+
+static void filter_view_on_smls_clr_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_mls_clr(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv);
+ fv->smls_clr.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Source MLS Clearance Items", log_items, policy_items,
+ fv->smls_clr.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->smls_clr);
+}
+
+static void filter_view_on_tuser_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_users(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_users(fv);
+ fv->tuser.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target User Items", log_items, policy_items,
+ fv->tuser.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->tuser);
+}
+
+static void filter_view_on_trole_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_roles(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_roles(fv);
+ fv->trole.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target Role Items", log_items, policy_items,
+ fv->trole.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->trole);
+}
+
+static void filter_view_on_ttype_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_types(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_types(fv);
+ fv->ttype.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target Type Items", log_items, policy_items,
+ fv->ttype.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->ttype);
+}
+
+static void filter_view_on_tmls_lvl_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_mls_lvl(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv);
+ fv->tmls_lvl.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target MLS Level Items", log_items, policy_items,
+ fv->tmls_lvl.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->tmls_lvl);
+}
+
+static void filter_view_on_tmls_clr_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_mls_clr(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_mls_lvl(fv);
+ fv->tmls_clr.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Target MLS Clearance Items", log_items, policy_items,
+ fv->tmls_clr.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->tmls_clr);
+}
+
+static void filter_view_on_class_context_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ apol_vector_t *log_items = toplevel_get_log_classes(fv->top);
+ apol_vector_t *policy_items = filter_view_get_policy_classes(fv);
+ fv->obj_class.items =
+ policy_components_view_run(fv->top, GTK_WINDOW(fv->dialog), "Object Class Items", log_items, policy_items,
+ fv->obj_class.items);
+ apol_vector_destroy(&log_items);
+ apol_vector_destroy(&policy_items);
+ filter_view_context_item_to_entry(fv, &fv->obj_class);
+}
+
+/**
+ * Whenever the user finished manually editing a context entry,
+ * convert the entry's string into the underlying vector.
+ */
+static gboolean filter_view_on_entry_focus_out(GtkWidget * widget, GdkEventFocus * event
+ __attribute__ ((unused)), gpointer user_data)
+{
+ struct context_item *item = g_object_get_data(G_OBJECT(widget), "data");
+ struct filter_view *fv = (struct filter_view *)user_data;
+ gchar **strs = g_strsplit(gtk_entry_get_text(GTK_ENTRY(widget)), ",", -1);
+ gchar *s;
+ size_t i = 0;
+ char *t;
+ apol_vector_t *new_v = NULL;
+ while (1) {
+ s = strs[i++];
+ if (s == NULL) {
+ break;
+ }
+ if (new_v == NULL && (new_v = apol_vector_create(free)) == NULL) {
+ toplevel_ERR(fv->top, "Could not interpret entry contents: %s", strerror(errno));
+ break;
+ }
+ if ((t = strdup(s)) == NULL) {
+ toplevel_ERR(fv->top, "Could not interpret entry contents: %s", strerror(errno));
+ free(t);
+ break;
+ }
+ apol_str_trim(t);
+ if (apol_vector_append(new_v, t) < 0) {
+ toplevel_ERR(fv->top, "Could not interpret entry contents: %s", strerror(errno));
+ free(t);
+ break;
+ }
+ }
+ g_strfreev(strs);
+ apol_vector_destroy(&item->items);
+ item->items = new_v;
+ filter_view_context_item_to_entry(fv, item);
+ return FALSE;
+}
+
+static void filter_view_destroy_context_vectors(struct filter_view *fv)
+{
+ apol_vector_destroy(&fv->suser.items);
+ apol_vector_destroy(&fv->srole.items);
+ apol_vector_destroy(&fv->stype.items);
+ apol_vector_destroy(&fv->smls_lvl.items);
+ apol_vector_destroy(&fv->smls_clr.items);
+ apol_vector_destroy(&fv->tuser.items);
+ apol_vector_destroy(&fv->trole.items);
+ apol_vector_destroy(&fv->ttype.items);
+ apol_vector_destroy(&fv->tmls_lvl.items);
+ apol_vector_destroy(&fv->tmls_clr.items);
+ apol_vector_destroy(&fv->obj_class.items);
+}
+
+static void filter_view_on_context_clear_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ filter_view_destroy_context_vectors(fv);
+ filter_view_context_items_to_entries(fv);
+}
+
+static void filter_view_init_context_signals(struct filter_view *fv)
+{
+ g_signal_connect(fv->suser.button, "clicked", G_CALLBACK(filter_view_on_suser_context_click), fv);
+ g_signal_connect(fv->suser.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->srole.button, "clicked", G_CALLBACK(filter_view_on_srole_context_click), fv);
+ g_signal_connect(fv->srole.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->stype.button, "clicked", G_CALLBACK(filter_view_on_stype_context_click), fv);
+ g_signal_connect(fv->stype.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->smls_lvl.button, "clicked", G_CALLBACK(filter_view_on_smls_lvl_context_click), fv);
+ g_signal_connect(fv->smls_lvl.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->smls_clr.button, "clicked", G_CALLBACK(filter_view_on_smls_clr_context_click), fv);
+ g_signal_connect(fv->smls_clr.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->tuser.button, "clicked", G_CALLBACK(filter_view_on_tuser_context_click), fv);
+ g_signal_connect(fv->tuser.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->trole.button, "clicked", G_CALLBACK(filter_view_on_trole_context_click), fv);
+ g_signal_connect(fv->trole.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->ttype.button, "clicked", G_CALLBACK(filter_view_on_ttype_context_click), fv);
+ g_signal_connect(fv->ttype.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->tmls_lvl.button, "clicked", G_CALLBACK(filter_view_on_tmls_lvl_context_click), fv);
+ g_signal_connect(fv->tmls_lvl.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->tmls_clr.button, "clicked", G_CALLBACK(filter_view_on_tmls_clr_context_click), fv);
+ g_signal_connect(fv->tmls_clr.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->obj_class.button, "clicked", G_CALLBACK(filter_view_on_class_context_click), fv);
+ g_signal_connect(fv->obj_class.entry, "focus_out_event", G_CALLBACK(filter_view_on_entry_focus_out), fv);
+ g_signal_connect(fv->context_clear_button, "clicked", G_CALLBACK(filter_view_on_context_clear_click), fv);
+}
+
+static void filter_view_on_other_clear_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ gtk_entry_set_text(fv->ipaddr_entry, "");
+ gtk_entry_set_text(fv->port_entry, "");
+ gtk_entry_set_text(fv->netif_entry, "");
+ gtk_entry_set_text(fv->exe_entry, "");
+ gtk_entry_set_text(fv->path_entry, "");
+ gtk_entry_set_text(fv->host_entry, "");
+ gtk_entry_set_text(fv->comm_entry, "");
+ gtk_combo_box_set_active(fv->message_combo, 0);
+}
+
+static void filter_view_on_date_toggle(GtkToggleButton * widget, gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ int match;
+ /* clicking on the radio buttons emit two toggle signals, one for
+ * the original button and one for the new one. thus only need to
+ * handle half of all signals */
+ if (!gtk_toggle_button_get_active(widget)) {
+ return;
+ }
+ match = filter_view_get_date_match(fv);
+ gtk_widget_set_sensitive(GTK_WIDGET(fv->dates[0].frame), (match != -1));
+ gtk_widget_set_sensitive(GTK_WIDGET(fv->dates[1].frame), (match == SEAUDIT_FILTER_DATE_MATCH_BETWEEN));
+}
+
+/* Given the year and the month set the spin button to have the
+ correct number of days for that month */
+static void filter_view_date_set_number_days(int month, GtkSpinButton * button)
+{
+ static const int days[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int cur_day;
+ /* get the current day because set_range moves the current day
+ * by the difference minus 1 day. e.g., going from jan 20 to
+ * february the day would automatically become 18 */
+ cur_day = gtk_spin_button_get_value_as_int(button);
+ gtk_spin_button_set_range(button, 1, days[month]);
+ /* return to current day, or to the max value allowed in range */
+ gtk_spin_button_set_value(button, cur_day);
+}
+
+static void filter_view_on_month0_change(GtkComboBox * widget, gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ filter_view_date_set_number_days(gtk_combo_box_get_active(widget), fv->dates[0].day);
+}
+
+static void filter_view_on_month1_change(GtkComboBox * widget, gpointer user_data)
+{
+ struct filter_view *fv = (struct filter_view *)user_data;
+ filter_view_date_set_number_days(gtk_combo_box_get_active(widget), fv->dates[1].day);
+}
+
+static void filter_view_init_signals(struct filter_view *fv)
+{
+ filter_view_init_context_signals(fv);
+ g_signal_connect(fv->other_clear_button, "clicked", G_CALLBACK(filter_view_on_other_clear_click), fv);
+ g_signal_connect(fv->date_none_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv);
+ g_signal_connect(fv->date_before_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv);
+ g_signal_connect(fv->date_after_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv);
+ g_signal_connect(fv->date_between_radio, "toggled", G_CALLBACK(filter_view_on_date_toggle), fv);
+ g_signal_connect(fv->dates[0].month, "changed", G_CALLBACK(filter_view_on_month0_change), fv);
+ g_signal_connect(fv->dates[1].month, "changed", G_CALLBACK(filter_view_on_month1_change), fv);
+}
+
+/******************** public function below ********************/
+
+void filter_view_run(seaudit_filter_t * filter, toplevel_t * top, GtkWindow * parent)
+{
+ struct filter_view fv;
+ gint response;
+
+ memset(&fv, 0, sizeof(fv));
+ fv.top = top;
+ fv.filter = filter;
+ fv.xml = glade_xml_new(toplevel_get_glade_xml(top), "FilterWindow", NULL);
+ filter_view_init_widgets(&fv, parent);
+ filter_view_init_signals(&fv);
+ filter_view_init_dialog(&fv);
+ do {
+ response = gtk_dialog_run(fv.dialog);
+ } while (response != GTK_RESPONSE_CLOSE);
+
+ filter_view_apply(&fv);
+ g_object_unref(fv.description_buffer);
+ gtk_widget_destroy(GTK_WIDGET(fv.dialog));
+ filter_view_destroy_context_vectors(&fv);
+}
diff --git a/seaudit/filter_view.h b/seaudit/filter_view.h
new file mode 100644
index 0000000..d94ece3
--- /dev/null
+++ b/seaudit/filter_view.h
@@ -0,0 +1,42 @@
+/**
+ * @file
+ * Dialog that allows the user to modify a particular filter.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FILTER_VIEW_H
+#define FILTER_VIEW_H
+
+#include "toplevel.h"
+#include <seaudit/filter.h>
+#include <gtk/gtk.h>
+
+/**
+ * Display and run a dialog that allows the user to modify a single
+ * filter.
+ *
+ * @param top Toplevel containing message view.
+ * @param view Message view to modify.
+ * @param parent Parent window upon which to center this dialog.
+ */
+void filter_view_run(seaudit_filter_t * filter, toplevel_t * top, GtkWindow * parent);
+
+#endif
diff --git a/seaudit/message_view.c b/seaudit/message_view.c
new file mode 100644
index 0000000..64d625f
--- /dev/null
+++ b/seaudit/message_view.c
@@ -0,0 +1,1341 @@
+/**
+ * @file
+ * Implementation of the view for a libseaudit model.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "message_view.h"
+#include "modify_view.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <apol/util.h>
+
+/**
+ * A custom model that implements the interfaces GtkTreeModel and
+ * GtkTreeSortable.
+ */
+typedef struct message_view_store
+{
+ /** this must be the first field, to satisfy glib */
+ GObject parent;
+ /** pointer to the store's controller */
+ message_view_t *view;
+ /** vector of seaudit_message_t, as returned by
+ * seaudit_model_get_messages() */
+ apol_vector_t *messages;
+ /** column that is currently being sorted; use OTHER_FIELD to
+ * indicate no sorting */
+ gint sort_field;
+ /** current sort direction, either 1 or ascending or -1 for
+ * descending */
+ int sort_dir;
+ /** unique integer for each instance of a model */
+ gint stamp;
+} message_view_store_t;
+
+typedef struct message_view_store_class
+{
+ GObjectClass parent_class;
+} message_view_store_class_t;
+
+static GType message_view_store_get_type(void);
+#define SEAUDIT_TYPE_MESSAGE_VIEW_STORE (message_view_store_get_type())
+#define SEAUDIT_IS_MESSAGE_VIEW_STORE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SEAUDIT_TYPE_MESSAGE_VIEW_STORE))
+
+struct message_view
+{
+ seaudit_model_t *model;
+ toplevel_t *top;
+ /** toplevel of the view, currently a scrolled_window */
+ GtkWidget *w;
+ /** actual GTK+ tree view widget that displays the rows and
+ * columns of message data */
+ GtkTreeView *view;
+ /** GTK+ store that models messages within the tree */
+ message_view_store_t *store;
+ /** filename for when this view was saved (could be NULL) */
+ char *filename;
+ /** most recent filename for exported messages (could be NULL) */
+ char *export_filename;
+};
+
+typedef seaudit_sort_t *(*sort_generator_fn_t) (int direction);
+
+struct view_column_record
+{
+ preference_field_e id;
+ const char *name;
+ const char *sample_text;
+ sort_generator_fn_t sort;
+};
+
+static const struct view_column_record column_data[] = {
+ {HOST_FIELD, "Hostname", "Hostname", seaudit_sort_by_host},
+ {MESSAGE_FIELD, "Message", "Message", seaudit_sort_by_message_type},
+ {DATE_FIELD, "Date", "Jan 01 00:00:00", seaudit_sort_by_date},
+ {SUSER_FIELD, "Source\nUser", "Source", seaudit_sort_by_source_user},
+ {SROLE_FIELD, "Source\nRole", "Source", seaudit_sort_by_source_role},
+ {STYPE_FIELD, "Source\nType", "unlabeled_t", seaudit_sort_by_source_type},
+ {SMLS_LVL_FIELD, "Source\nMLS Level", "MLS Level", seaudit_sort_by_source_mls_lvl},
+ {SMLS_CLR_FIELD, "Source\nMLS Clearance", "MLS Clearance", seaudit_sort_by_source_mls_clr},
+ {TUSER_FIELD, "Target\nUser", "Target", seaudit_sort_by_target_user},
+ {TROLE_FIELD, "Target\nRole", "Target", seaudit_sort_by_target_role},
+ {TTYPE_FIELD, "Target\nType", "unlabeled_t", seaudit_sort_by_target_type},
+ {TMLS_LVL_FIELD, "Target\nMLS Level", "MLS Level", seaudit_sort_by_target_mls_lvl},
+ {TMLS_CLR_FIELD, "Target\nMLS Clearance", "MLS Clearance", seaudit_sort_by_target_mls_clr},
+ {OBJCLASS_FIELD, "Object\nClass", "Object", seaudit_sort_by_object_class},
+ {PERM_FIELD, "Permission", "Permission", seaudit_sort_by_permission},
+ {EXECUTABLE_FIELD, "Executable", "/usr/bin/cat", seaudit_sort_by_executable},
+ {COMMAND_FIELD, "Command", "/usr/bin/cat", seaudit_sort_by_command},
+ {NAME_FIELD, "Name", "iceweasel", seaudit_sort_by_name},
+ {PID_FIELD, "PID", "12345", seaudit_sort_by_pid},
+ {INODE_FIELD, "Inode", "123456", seaudit_sort_by_inode},
+ {PATH_FIELD, "Path", "/home/gburdell/foo", seaudit_sort_by_path},
+ {OTHER_FIELD, "Other", "Lorem ipsum dolor sit amet, consectetur", NULL}
+};
+
+static const size_t num_columns = sizeof(column_data) / sizeof(column_data[0]);
+
+/**
+ * (Re)sort the view based upon which column is clicked. If already
+ * sorting on this column, then reverse the sort direction. Also
+ * update the sort indicator for this column.
+ */
+static gboolean message_view_on_column_click(GtkTreeViewColumn * column, gpointer user_data)
+{
+ gint column_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column id"));
+ message_view_t *view = (message_view_t *) user_data;
+ int dir = 0;
+ seaudit_sort_t *sort;
+ GtkTreeViewColumn *prev_column;
+ if (column_id == view->store->sort_field) {
+ dir = view->store->sort_dir * -1;
+ } else {
+ dir = 1;
+ }
+
+ if ((sort = column_data[(preference_field_e) column_id].sort(dir)) == NULL) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ return TRUE;
+ }
+ seaudit_model_clear_sorts(view->model);
+ if (seaudit_model_append_sort(view->model, sort) < 0) {
+ seaudit_sort_destroy(&sort);
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ }
+ prev_column = gtk_tree_view_get_column(view->view, view->store->sort_field);
+ if (prev_column != NULL) {
+ gtk_tree_view_column_set_sort_indicator(prev_column, FALSE);
+ }
+ gtk_tree_view_column_set_sort_indicator(column, TRUE);
+ if (dir > 0) {
+ gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING);
+ } else {
+ gtk_tree_view_column_set_sort_order(column, GTK_SORT_DESCENDING);
+ }
+
+ view->store->sort_field = column_id;
+ view->store->sort_dir = dir;
+ message_view_update_rows(view);
+ return TRUE;
+}
+
+/*************** implementation of a custom GtkTreeModel ***************/
+
+static GObjectClass *parent_class = NULL;
+
+static void message_view_store_init(message_view_store_t * m);
+static void message_view_store_class_init(message_view_store_class_t * c);
+static void message_view_store_tree_init(GtkTreeModelIface * iface);
+static void message_view_store_finalize(GObject * object);
+static GtkTreeModelFlags message_view_store_get_flags(GtkTreeModel * tree_model);
+static gint message_view_store_get_n_columns(GtkTreeModel * tree_model);
+static GType message_view_store_get_column_type(GtkTreeModel * tree_model, gint index);
+static gboolean message_view_store_get_iter(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreePath * path);
+static GtkTreePath *message_view_store_get_path(GtkTreeModel * tree_model, GtkTreeIter * iter);
+static void message_view_store_get_value(GtkTreeModel * tree_model, GtkTreeIter * iter, gint column, GValue * value);
+static gboolean message_view_store_iter_next(GtkTreeModel * tree_model, GtkTreeIter * iter);
+static gboolean message_view_store_iter_children(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent);
+static gboolean message_view_store_iter_has_child(GtkTreeModel * tree_model, GtkTreeIter * iter);
+static gint message_view_store_iter_n_children(GtkTreeModel * tree_model, GtkTreeIter * iter);
+static gboolean message_view_store_iter_nth_child(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent, gint n);
+static gboolean message_view_store_iter_parent(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * child);
+
+static GType message_view_store_get_type(void)
+{
+ static GType store_type = 0;
+ static const GTypeInfo store_info = {
+ sizeof(message_view_store_class_t),
+ NULL,
+ NULL,
+ (GClassInitFunc) message_view_store_class_init,
+ NULL,
+ NULL,
+ sizeof(message_view_store_t),
+ 0,
+ (GInstanceInitFunc) message_view_store_init
+ };
+ static const GInterfaceInfo tree_model_info = {
+ (GInterfaceInitFunc) message_view_store_tree_init,
+ NULL,
+ NULL
+ };
+
+ if (store_type)
+ return store_type;
+
+ store_type = g_type_register_static(G_TYPE_OBJECT, "message_view_store", &store_info, (GTypeFlags) 0);
+ g_type_add_interface_static(store_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
+ return store_type;
+}
+
+static void message_view_store_init(message_view_store_t * m)
+{
+ static int next_stamp = 0;
+ m->messages = NULL;
+ m->sort_field = OTHER_FIELD;
+ m->sort_dir = 1;
+ m->stamp = next_stamp++;
+}
+
+static void message_view_store_class_init(message_view_store_class_t * c)
+{
+ GObjectClass *object_class;
+ parent_class = g_type_class_peek_parent(c);
+ object_class = (GObjectClass *) c;
+ object_class->finalize = message_view_store_finalize;
+}
+
+static void message_view_store_tree_init(GtkTreeModelIface * iface)
+{
+ iface->get_flags = message_view_store_get_flags;
+ iface->get_n_columns = message_view_store_get_n_columns;
+ iface->get_column_type = message_view_store_get_column_type;
+ iface->get_iter = message_view_store_get_iter;
+ iface->get_path = message_view_store_get_path;
+ iface->get_value = message_view_store_get_value;
+ iface->iter_next = message_view_store_iter_next;
+ iface->iter_children = message_view_store_iter_children;
+ iface->iter_has_child = message_view_store_iter_has_child;
+ iface->iter_n_children = message_view_store_iter_n_children;
+ iface->iter_nth_child = message_view_store_iter_nth_child;
+ iface->iter_parent = message_view_store_iter_parent;
+}
+
+static void message_view_store_finalize(GObject * object)
+{
+ (*parent_class->finalize) (object);
+}
+
+static GtkTreeModelFlags message_view_store_get_flags(GtkTreeModel * tree_model)
+{
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), 0);
+ return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+}
+
+static gint message_view_store_get_n_columns(GtkTreeModel * tree_model __attribute__ ((unused)))
+{
+ return OTHER_FIELD + 1;
+}
+
+static GType message_view_store_get_column_type(GtkTreeModel * tree_model, gint idx __attribute__ ((unused)))
+{
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), G_TYPE_INVALID);
+ /* everything is a string for now */
+ return G_TYPE_STRING;
+}
+
+static gboolean message_view_store_get_iter(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreePath * path)
+{
+ gint i;
+ message_view_store_t *store = (message_view_store_t *) tree_model;
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE);
+ g_return_val_if_fail(gtk_tree_path_get_depth(path) > 0, FALSE);
+ i = gtk_tree_path_get_indices(path)[0];
+ if (i >= apol_vector_get_size(store->messages))
+ return FALSE;
+
+ iter->stamp = store->stamp;
+ iter->user_data = apol_vector_get_element(store->messages, i);
+ iter->user_data2 = GINT_TO_POINTER(i);
+ iter->user_data3 = store->view;
+ return TRUE;
+}
+
+static GtkTreePath *message_view_store_get_path(GtkTreeModel * tree_model, GtkTreeIter * iter)
+{
+ GtkTreePath *retval;
+ message_view_store_t *store = (message_view_store_t *) tree_model;
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), NULL);
+ g_return_val_if_fail(iter->stamp == store->stamp, NULL);
+ retval = gtk_tree_path_new();
+ gtk_tree_path_append_index(retval, GPOINTER_TO_INT(iter->user_data2));
+ return retval;
+}
+
+/**
+ * Given a string, check that it is UTF8 legal. If not, or if the
+ * string is NULL, then return an empty string. Otherwise return the
+ * original string.
+ */
+static void message_view_to_utf8(GValue * value, const char *s)
+{
+ if (s == NULL || !g_utf8_validate(s, -1, NULL)) {
+ g_value_set_string(value, "");
+ }
+ g_value_set_string(value, s);
+}
+
+static void message_view_store_get_value(GtkTreeModel * tree_model, GtkTreeIter * iter, gint column, GValue * value)
+{
+ message_view_store_t *store;
+ message_view_t *view;
+ seaudit_message_t *m;
+ seaudit_message_type_e type;
+ void *data;
+ seaudit_avc_message_t *avc;
+ g_return_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(column <= OTHER_FIELD);
+ g_value_init(value, G_TYPE_STRING);
+ store = (message_view_store_t *) tree_model;
+ view = store->view;
+ m = (seaudit_message_t *) iter->user_data;
+ data = seaudit_message_get_data(m, &type);
+ preference_field_e field = column;
+
+ switch (field) {
+ case HOST_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_message_get_host(m));
+ return;
+ }
+ case MESSAGE_FIELD:
+ {
+ char *message = "Invalid";
+ switch (type) {
+ case SEAUDIT_MESSAGE_TYPE_BOOL:
+ {
+ message = "Boolean";
+ break;
+ }
+ case SEAUDIT_MESSAGE_TYPE_LOAD:
+ {
+ message = "Load";
+ break;
+ }
+ case SEAUDIT_MESSAGE_TYPE_AVC:
+ {
+ avc = (seaudit_avc_message_t *) data;
+ seaudit_avc_message_type_e avc_type;
+ avc_type = seaudit_avc_message_get_message_type(avc);
+ switch (avc_type) {
+ case SEAUDIT_AVC_DENIED:
+ {
+ message = "Denied";
+ break;
+ }
+ case SEAUDIT_AVC_GRANTED:
+ {
+ message = "Granted";
+ break;
+ }
+ default:
+ {
+ /* should never get here */
+ toplevel_ERR(view->top, "Got an invalid AVC message type %d!", avc_type);
+ assert(0);
+ return;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ /* should never get here */
+ toplevel_ERR(view->top, "Got an invalid message type %d!", type);
+ assert(0);
+ return;
+ }
+ }
+ message_view_to_utf8(value, message);
+ return;
+ }
+ case DATE_FIELD:
+ {
+ const struct tm *tm = seaudit_message_get_time(m);
+ char date[256];
+ /* check to see if we have been given a valid year, if
+ * so display, otherwise no year displayed */
+ if (tm->tm_year == 0) {
+ strftime(date, 256, "%b %d %H:%M:%S", tm);
+ } else {
+ strftime(date, 256, "%b %d %H:%M:%S %Y", tm);
+ }
+ message_view_to_utf8(value, date);
+ return;
+ }
+ case OTHER_FIELD:
+ {
+ char *other = seaudit_message_to_misc_string(m);;
+ if (other == NULL) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ return;
+ }
+ message_view_to_utf8(value, other);
+ free(other);
+ return;
+ }
+ default: /* FALLTHROUGH */
+ break;
+ }
+
+ if (type != SEAUDIT_MESSAGE_TYPE_AVC) {
+ /* the rest of the columns are blank for non-AVC
+ * messages */
+ message_view_to_utf8(value, "");
+ return;
+ }
+ avc = (seaudit_avc_message_t *) data;
+
+ switch (field) {
+ case SUSER_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_source_user(avc));
+ return;
+ }
+ case SROLE_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_source_role(avc));
+ return;
+ }
+ case STYPE_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_source_type(avc));
+ return;
+ }
+ case SMLS_LVL_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_source_mls_lvl(avc));
+ return;
+ }
+ case SMLS_CLR_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_source_mls_clr(avc));
+ return;
+ }
+ case TUSER_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_target_user(avc));
+ return;
+ }
+ case TROLE_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_target_role(avc));
+ return;
+ }
+ case TTYPE_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_target_type(avc));
+ return;
+ }
+ case TMLS_LVL_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_target_mls_lvl(avc));
+ return;
+ }
+ case TMLS_CLR_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_target_mls_clr(avc));
+ return;
+ }
+ case OBJCLASS_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_object_class(avc));
+ return;
+ }
+ case PERM_FIELD:
+ {
+ const apol_vector_t *perms = seaudit_avc_message_get_perm(avc);
+ char *perm = NULL;
+ size_t i, len = 0;
+ for (i = 0; perms != NULL && i < apol_vector_get_size(perms); i++) {
+ char *p = apol_vector_get_element(perms, i);
+ if (apol_str_appendf(&perm, &len, "%s%s", (i > 0 ? "," : ""), p) < 0) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ return;
+ }
+ }
+ message_view_to_utf8(value, perm);
+ free(perm);
+ return;
+ }
+ case EXECUTABLE_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_exe(avc));
+ return;
+ }
+ case COMMAND_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_comm(avc));
+ return;
+ }
+ case NAME_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_name(avc));
+ return;
+ }
+ case PID_FIELD:
+ {
+ char *s;
+ if (asprintf(&s, "%u", seaudit_avc_message_get_pid(avc)) < 0) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ return;
+ }
+ message_view_to_utf8(value, s);
+ free(s);
+ return;
+ }
+ case INODE_FIELD:
+ {
+ char *s;
+ if (asprintf(&s, "%lu", seaudit_avc_message_get_inode(avc)) < 0) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ return;
+ }
+ message_view_to_utf8(value, s);
+ free(s);
+ return;
+ }
+ case PATH_FIELD:
+ {
+ message_view_to_utf8(value, seaudit_avc_message_get_path(avc));
+ return;
+ }
+ default: /* FALLTHROUGH */
+ break;
+ }
+ /* should never get here */
+ toplevel_ERR(view->top, "Got an invalid column %d!", field);
+ assert(0);
+}
+
+static gboolean message_view_store_iter_next(GtkTreeModel * tree_model, GtkTreeIter * iter)
+{
+ gint i;
+ message_view_store_t *store = (message_view_store_t *) tree_model;
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE);
+ g_return_val_if_fail(iter->stamp == store->stamp, FALSE);
+ if (iter == NULL || iter->user_data == NULL)
+ return FALSE;
+ i = GPOINTER_TO_INT(iter->user_data2) + 1;
+ if (i >= apol_vector_get_size(store->messages)) {
+ return FALSE;
+ }
+ iter->user_data = apol_vector_get_element(store->messages, i);
+ iter->user_data2 = GINT_TO_POINTER(i);
+ iter->user_data3 = store->view;
+ return TRUE;
+}
+
+static gboolean message_view_store_iter_children(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent)
+{
+ message_view_store_t *store;
+ g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
+ if (parent)
+ return FALSE;
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE);
+
+ /* set iterator to first row, if possible */
+ store = (message_view_store_t *) tree_model;
+ if (store->messages == NULL || apol_vector_get_size(store->messages) == 0)
+ return FALSE;
+
+ iter->stamp = store->stamp;
+ iter->user_data = apol_vector_get_element(store->messages, 0);
+ iter->user_data2 = GINT_TO_POINTER(0);
+ iter->user_data3 = store->view;
+ return TRUE;
+}
+
+static gboolean message_view_store_iter_has_child(GtkTreeModel * tree_model __attribute__ ((unused)), GtkTreeIter * iter
+ __attribute__ ((unused)))
+{
+ return FALSE;
+}
+
+static gint message_view_store_iter_n_children(GtkTreeModel * tree_model, GtkTreeIter * iter)
+{
+ message_view_store_t *store;
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), -1);
+ g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
+ store = (message_view_store_t *) tree_model;
+ /* return the number of rows, if iterator is at the top;
+ * otherwise return 0 because this store is just a list */
+ if (iter != NULL || store->messages == NULL) {
+ return 0;
+ }
+ return apol_vector_get_size(store->messages);
+}
+
+static gboolean message_view_store_iter_nth_child(GtkTreeModel * tree_model, GtkTreeIter * iter, GtkTreeIter * parent, gint n)
+{
+ message_view_store_t *store;
+ g_return_val_if_fail(SEAUDIT_IS_MESSAGE_VIEW_STORE(tree_model), FALSE);
+ store = (message_view_store_t *) tree_model;
+ if (store->messages == NULL || parent != NULL) {
+ return FALSE;
+ }
+ if (n >= apol_vector_get_size(store->messages)) {
+ return FALSE;
+ }
+ iter->stamp = store->stamp;
+ iter->user_data = apol_vector_get_element(store->messages, n);
+ iter->user_data2 = GINT_TO_POINTER(n);
+ iter->user_data3 = store->view;
+ return TRUE;
+}
+
+static gboolean message_view_store_iter_parent(GtkTreeModel * tree_model __attribute__ ((unused)), GtkTreeIter * iter
+ __attribute__ ((unused)), GtkTreeIter * child __attribute__ ((unused)))
+{
+ return FALSE;
+}
+
+/*************** end of custom GtkTreeModel implementation ***************/
+
+/*************** message_view_messages_vector() callbacks ******************/
+#define LBACK 1
+#define LFORWARD 2
+#define LNOOP 255
+
+typedef struct _msg_user_data
+{
+ message_view_t *view;
+ apol_vector_t *messages;
+ GtkDialog *dialog;
+ GtkTextBuffer *buffer;
+ gint handle_id;
+} _msg_user_data_t;
+
+static void message_view_dialog_change(GtkTreeModel * tree_model,
+ GtkTreePath * path __attribute__ ((unused)),
+ GtkTreeIter * iter __attribute__ ((unused)), gpointer user_data)
+{
+ _msg_user_data_t *d = (_msg_user_data_t *) user_data;
+ /* Disconnect this signal handler after it fires. It's one
+ * shot, nothing should be able to bring the next and previous
+ * buttons back from the dead if the view ever changes.
+ */
+ g_signal_handler_disconnect(tree_model, d->handle_id);
+ d->view = NULL;
+ gtk_dialog_set_response_sensitive(d->dialog, LBACK, FALSE);
+ gtk_dialog_set_response_sensitive(d->dialog, LFORWARD, FALSE);
+}
+
+static void message_view_dialog_response(GtkDialog * dialog, gint response, gpointer user_data)
+{
+ _msg_user_data_t *d = (_msg_user_data_t *) user_data;
+ GtkTreePath *p;
+ GtkTreeIter tree_iter;
+ GtkTextIter text_iter;
+ size_t i;
+ gboolean go_back = FALSE;
+ gboolean go_forward = FALSE;
+
+ gtk_text_buffer_set_text(d->buffer, "", -1);
+ p = apol_vector_get_element(d->messages, 0);
+ assert(p != NULL);
+ switch (response) {
+ case LNOOP:
+ break; /* no-op response, display and test only */
+ case LBACK:
+ gtk_tree_path_prev(p);
+ break;
+ case LFORWARD:
+ gtk_tree_path_next(p);
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ apol_vector_destroy(&d->messages);
+ if (d->view != NULL) {
+ g_signal_handler_disconnect(d->view->store, d->handle_id);
+ }
+ free(d);
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ return;
+ default:
+ /* should never get here */
+ toplevel_ERR(d->view->top, "Unhandled response type (%d).\n", response);
+ assert(0);
+ return;
+ }
+ assert(d->view != NULL);
+
+ /* determine if the forward and backward buttons should be
+ enabled or not */
+ if (apol_vector_get_size(d->messages) == 1) {
+ GtkTreePath *dupe_p = gtk_tree_path_copy(p);
+ if (dupe_p == NULL) {
+ toplevel_ERR(d->view->top, "%s", strerror(errno));
+ return;
+ }
+ go_back = gtk_tree_path_prev(dupe_p);
+ gtk_tree_path_free(dupe_p);
+
+ message_view_store_get_iter(GTK_TREE_MODEL(d->view->store), &tree_iter, p);
+ go_forward = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->view->store), &tree_iter);
+ }
+ gtk_dialog_set_response_sensitive(dialog, LBACK, go_back);
+ gtk_dialog_set_response_sensitive(dialog, LFORWARD, go_forward);
+
+ gtk_text_buffer_get_start_iter(d->buffer, &text_iter);
+ for (i = 0; i < apol_vector_get_size(d->messages); i++) {
+ char *s;
+ p = apol_vector_get_element(d->messages, i);
+ message_view_store_get_iter(GTK_TREE_MODEL(d->view->store), &tree_iter, p);
+ if ((s = seaudit_message_to_string(tree_iter.user_data)) == NULL) {
+ toplevel_ERR(d->view->top, "%s", strerror(errno));
+ continue;
+ }
+
+ gtk_text_buffer_insert(d->buffer, &text_iter, s, -1);
+ gtk_text_buffer_insert(d->buffer, &text_iter, "\n", -1);
+ free(s);
+ }
+}
+
+/**
+ * Show all messages within the messages vector (vector of
+ * GtkTreePaths into the view's store).
+ *
+ * Callback function cb_view_message takes ownership of messages
+ * vector and state.
+ */
+static void message_view_messages_vector(message_view_t * view, apol_vector_t * messages)
+{
+ GtkWidget *window = NULL;
+ GtkWidget *scroll;
+ GtkWidget *text_view;
+ GtkTextBuffer *buffer;
+ _msg_user_data_t *state;
+
+ state = malloc(sizeof(_msg_user_data_t));
+ if (state == NULL) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ apol_vector_destroy(&messages);
+ return;
+ }
+
+ window = gtk_dialog_new_with_buttons("View Messages",
+ toplevel_get_window(view->top),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_GO_BACK, LBACK,
+ GTK_STOCK_GO_FORWARD, LFORWARD, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE);
+ gtk_window_set_modal(GTK_WINDOW(window), FALSE);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ text_view = gtk_text_view_new();
+ gtk_window_set_default_size(GTK_WINDOW(window), 480, 300);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), scroll);
+ gtk_container_add(GTK_CONTAINER(scroll), text_view);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD);
+ gtk_widget_show(text_view);
+ gtk_widget_show(scroll);
+
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
+
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
+ state->view = view;
+ state->messages = messages;
+ state->dialog = GTK_DIALOG(window);
+ state->buffer = buffer;
+ g_signal_connect(window, "response", G_CALLBACK(message_view_dialog_response), state);
+ state->handle_id = g_signal_connect(view->store, "row-changed", G_CALLBACK(message_view_dialog_change), state);
+ message_view_dialog_response(GTK_DIALOG(window), LNOOP, state);
+
+ gtk_widget_show_all(GTK_WIDGET(window));
+}
+
+/******************** handlers for right click menu ********************/
+
+static void message_view_popup_on_view_message_activate(GtkMenuItem * menuitem, gpointer user_data __attribute__ ((unused)))
+{
+ message_view_t *v = g_object_get_data(G_OBJECT(menuitem), "view-object");
+ message_view_entire_message(v);
+}
+
+static void message_view_popup_on_find_terules_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ message_view_t *v = g_object_get_data(G_OBJECT(menuitem), "view-object");
+ toplevel_find_terules(v->top, (seaudit_message_t *) user_data);
+}
+
+static void message_view_popup_on_export_selected_messages_activate(GtkMenuItem * menuitem, gpointer user_data
+ __attribute__ ((unused)))
+{
+ message_view_t *v = g_object_get_data(G_OBJECT(menuitem), "view-object");
+ message_view_export_selected_messages(v);
+}
+
+static void message_view_popup_menu(GtkWidget * treeview, GdkEventButton * event, message_view_t * view,
+ seaudit_message_t * message)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gint num_selected_rows = gtk_tree_selection_count_selected_rows(selection);
+ GtkWidget *menu, *menuitem, *menuitem2, *menuitem3;
+ int button, event_time;
+
+ menu = gtk_menu_new();
+ if (num_selected_rows == 1) {
+ menuitem = gtk_menu_item_new_with_label("View Selected Message");
+ menuitem3 = gtk_menu_item_new_with_label("Export Selected Message...");
+ } else {
+ menuitem = gtk_menu_item_new_with_label("View Selected Messages");
+ menuitem3 = gtk_menu_item_new_with_label("Export Selected Messages...");
+ }
+ menuitem2 = gtk_menu_item_new_with_label("Find TERules using Message...");
+ g_signal_connect(menuitem, "activate", (GCallback) message_view_popup_on_view_message_activate, message);
+ g_signal_connect(menuitem2, "activate", (GCallback) message_view_popup_on_find_terules_activate, message);
+ g_signal_connect(menuitem3, "activate", (GCallback) message_view_popup_on_export_selected_messages_activate, NULL);
+ g_object_set_data(G_OBJECT(menuitem), "view-object", view);
+ g_object_set_data(G_OBJECT(menuitem2), "view-object", view);
+ g_object_set_data(G_OBJECT(menuitem3), "view-object", view);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem2);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem3);
+ gtk_widget_show_all(menu);
+ if (toplevel_get_policy(view->top) == NULL) {
+ gtk_widget_set_sensitive(menuitem2, FALSE);
+ }
+
+ if (event) {
+ button = event->button;
+ event_time = event->time;
+ } else {
+ button = 0;
+ event_time = gtk_get_current_event_time();
+ }
+ gtk_menu_attach_to_widget(GTK_MENU(menu), treeview, NULL);
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
+}
+
+static gboolean message_view_delayed_selection_menu_item(gpointer data)
+{
+ message_view_t *view = (message_view_t *) data;
+ toplevel_update_selection_menu_item(view->top);
+ return FALSE;
+}
+
+static gboolean message_view_on_button_press(GtkWidget * treeview, GdkEventButton * event, gpointer user_data)
+{
+ message_view_t *view = (message_view_t *) user_data;
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), event->x, event->y, &path, NULL, NULL, NULL)) {
+ return FALSE;
+ }
+ /* if the right click occurred on an unselected row, remove
+ * all selections and select the item under the pointer */
+ if (!gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_path(selection, path);
+ }
+ message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, path);
+ /* popup a menu for the row that was clicked */
+ message_view_popup_menu(treeview, event, view, (seaudit_message_t *) iter.user_data);
+ return TRUE;
+ } else if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ /* n.b.: rows can be selected but never deselected.
+ * delay updating the menu, for upon the first click
+ * there is not a selection yet */
+ g_idle_add(&message_view_delayed_selection_menu_item, view);
+ return FALSE;
+ } else if (event->type == GDK_2BUTTON_PRESS && event->button == 1){
+ /* Show message on double click */
+ message_view_entire_message(view);
+ }
+ return FALSE;
+}
+
+static void message_view_gtk_tree_path_free(gpointer data, gpointer user_data __attribute__ ((unused)))
+{
+ gtk_tree_path_free((GtkTreePath *) data);
+}
+
+static gboolean message_view_on_popup_menu(GtkWidget * treeview, gpointer user_data)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ message_view_t *view = (message_view_t *) user_data;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ if (glist == NULL) {
+ return FALSE;
+ }
+ path = g_list_nth_data(glist, 0);
+ message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, path);
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ message_view_popup_menu(treeview, NULL, view, (seaudit_message_t *) iter.user_data);
+ return TRUE;
+}
+
+static void message_view_on_row_activate(GtkTreeView * tree_view __attribute__ ((unused)), GtkTreePath * path
+ __attribute__ ((unused)), GtkTreeViewColumn * column
+ __attribute__ ((unused)), gpointer user_data)
+{
+ message_view_t *view = (message_view_t *) user_data;
+ toplevel_update_selection_menu_item(view->top);
+}
+
+/******************** other public functions below ********************/
+
+message_view_t *message_view_create(toplevel_t * top, seaudit_model_t * model, const char *filename)
+{
+ message_view_t *view;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ size_t i;
+
+ if ((view = calloc(1, sizeof(*view))) == NULL || (filename != NULL && (view->filename = strdup(filename)) == NULL)) {
+ int error = errno;
+ toplevel_ERR(top, "%s", strerror(error));
+ message_view_destroy(&view);
+ errno = error;
+ return NULL;
+ }
+ view->top = top;
+ view->model = model;
+ view->store = (message_view_store_t *) g_object_new(SEAUDIT_TYPE_MESSAGE_VIEW_STORE, NULL);
+ view->store->view = view;
+ view->store->sort_field = OTHER_FIELD;
+ view->store->sort_dir = 1;
+ view->w = gtk_scrolled_window_new(NULL, NULL);
+ view->view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(view->store)));
+ selection = gtk_tree_view_get_selection(view->view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+ gtk_container_add(GTK_CONTAINER(view->w), GTK_WIDGET(view->view));
+ gtk_widget_show(GTK_WIDGET(view->view));
+ gtk_widget_show(view->w);
+
+ renderer = gtk_cell_renderer_text_new();
+ for (i = 0; i < num_columns; i++) {
+ struct view_column_record r = column_data[i];
+ PangoLayout *layout = gtk_widget_create_pango_layout(GTK_WIDGET(view->view), r.sample_text);
+ gint width;
+ GtkTreeViewColumn *column;
+ pango_layout_get_pixel_size(layout, &width, NULL);
+ g_object_unref(G_OBJECT(layout));
+ width += 12;
+ column = gtk_tree_view_column_new_with_attributes(r.name, renderer, "text", r.id, NULL);
+ gtk_tree_view_column_set_clickable(column, TRUE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ if (r.sort != NULL) {
+ g_object_set_data(G_OBJECT(column), "column id", GINT_TO_POINTER(r.id));
+ g_signal_connect_after(G_OBJECT(column), "clicked", G_CALLBACK(message_view_on_column_click), view);
+ }
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_fixed_width(column, width);
+ gtk_tree_view_append_column(view->view, column);
+ }
+
+ g_signal_connect(G_OBJECT(view->view), "button-press-event", G_CALLBACK(message_view_on_button_press), view);
+ g_signal_connect(G_OBJECT(view->view), "popup-menu", G_CALLBACK(message_view_on_popup_menu), view);
+ g_signal_connect(G_OBJECT(view->view), "row-activated", G_CALLBACK(message_view_on_row_activate), view);
+ message_view_update_visible_columns(view);
+ message_view_update_rows(view);
+ return view;
+}
+
+void message_view_destroy(message_view_t ** view)
+{
+ if (view != NULL && *view != NULL) {
+ /* emit a signal to force all message view dialogs to
+ disable their scroll buttons. need to pass a
+ non-NULL path in the signal handler to make GTK
+ shut up */
+ GtkTreePath *path = gtk_tree_path_new_first();
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter(GTK_TREE_MODEL((*view)->store), &iter, path);
+ g_signal_emit_by_name((*view)->store, "row-changed", path, &iter);
+ gtk_tree_path_free(path);
+ seaudit_model_destroy(&(*view)->model);
+ apol_vector_destroy(&((*view)->store->messages));
+ g_free((*view)->filename);
+ g_free((*view)->export_filename);
+ /* let glib handle destruction of object */
+ g_object_unref((*view)->store);
+ free(*view);
+ *view = NULL;
+ }
+}
+
+seaudit_model_t *message_view_get_model(message_view_t * view)
+{
+ return view->model;
+}
+
+void message_view_set_model(message_view_t * view, seaudit_model_t * model)
+{
+ seaudit_model_destroy(&view->model);
+ view->model = model;
+ toplevel_update_tabs(view->top);
+ message_view_update_rows(view);
+}
+
+GtkWidget *message_view_get_view(message_view_t * view)
+{
+ return view->w;
+}
+
+size_t message_view_get_num_log_messages(message_view_t * view)
+{
+ if (view->store->messages == NULL) {
+ return 0;
+ }
+ return apol_vector_get_size(view->store->messages);
+}
+
+gboolean message_view_is_message_selected(message_view_t * view)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(view->view);
+ GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ if (glist == NULL) {
+ return FALSE;
+ }
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ return TRUE;
+}
+
+void message_view_vector_gtk_tree_path_free(void *elem)
+{
+ GtkTreePath *path = (GtkTreePath *) elem;
+ gtk_tree_path_free(path);
+}
+
+void message_view_entire_message(message_view_t * view)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(view->view);
+ GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ GList *l;
+ apol_vector_t *messages;
+ if (glist == NULL) {
+ return;
+ }
+ if ((messages = apol_vector_create(message_view_vector_gtk_tree_path_free)) == NULL) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ return;
+ }
+ for (l = glist; l != NULL; l = l->next) {
+ GtkTreePath *path = gtk_tree_path_copy((GtkTreePath *) l->data);
+ if (path == NULL || apol_vector_append(messages, path) < 0) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ gtk_tree_path_free(path);
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ apol_vector_destroy(&messages);
+ return;
+ }
+ }
+ /* the following function takes ownership of messages vector */
+ message_view_messages_vector(view, messages);
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+}
+
+void message_view_save(message_view_t * view)
+{
+ if (view->filename == NULL) {
+ GtkWindow *parent = toplevel_get_window(view->top);
+ char *path = util_save_file(parent, "Save View", NULL);
+ if (path == NULL) {
+ return;
+ }
+ view->filename = path;
+ }
+ if (seaudit_model_save_to_file(view->model, view->filename) < 0) {
+ toplevel_ERR(view->top, "Error saving view: %s", strerror(errno));
+ }
+}
+
+void message_view_saveas(message_view_t * view)
+{
+ GtkWindow *parent = toplevel_get_window(view->top);
+ char *path = util_save_file(parent, "Save View As", view->filename);
+ if (path == NULL) {
+ return;
+ }
+ g_free(view->filename);
+ view->filename = path;
+ if (seaudit_model_save_to_file(view->model, view->filename) < 0) {
+ toplevel_ERR(view->top, "Error saving view: %s", strerror(errno));
+ }
+}
+
+void message_view_modify(message_view_t * view)
+{
+ if (modify_view_run(view->top, view)) {
+ toplevel_update_status_bar(view->top);
+ }
+}
+
+void message_view_clear(message_view_t * view)
+{
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(view->store->messages); i++) {
+ seaudit_message_t *m = apol_vector_get_element(view->store->messages, i);
+ seaudit_model_hide_message(view->model, m);
+ }
+ message_view_update_rows(view);
+}
+
+/**
+ * Write to a file all messages in the given vector. Upon success,
+ * update the view object's export filename.
+ *
+ * @param view View containing messages to write.
+ * @param path Destination to write file, overwriting existing files
+ * as necessary.
+ * @param messages Vector of seaudit_message_t.
+ */
+static void message_view_export_messages_vector(message_view_t * view, char *path, apol_vector_t * messages)
+{
+ FILE *f = NULL;
+ size_t i;
+ g_free(view->export_filename);
+ view->export_filename = path;
+ if ((f = fopen(path, "w")) == NULL) {
+ toplevel_ERR(view->top, "Could not open %s for writing.", path);
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(messages); i++) {
+ seaudit_message_t *m = apol_vector_get_element(messages, i);
+ char *s = seaudit_message_to_string(m);
+ if (s == NULL || fprintf(f, "%s\n", s) < 0) {
+ toplevel_ERR(view->top, "Error writing string: %s", strerror(errno));
+ goto cleanup;
+ }
+ free(s);
+ }
+ cleanup:
+ if (f != NULL) {
+ fclose(f);
+ }
+}
+
+void message_view_export_all_messages(message_view_t * view)
+{
+ GtkWindow *parent = toplevel_get_window(view->top);
+ char *path = util_save_file(parent, "Export Messages", view->export_filename);
+ apol_vector_t *messages = view->store->messages;
+ if (path == NULL) {
+ return;
+ }
+ message_view_export_messages_vector(view, path, messages);
+}
+
+void message_view_export_selected_messages(message_view_t * view)
+{
+ GtkWindow *parent = toplevel_get_window(view->top);
+ char *path;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(view->view);
+ GList *glist = gtk_tree_selection_get_selected_rows(selection, NULL);
+ GList *l;
+ apol_vector_t *messages;
+ if (glist == NULL) {
+ return;
+ }
+ path = util_save_file(parent, "Export Selected Messages", view->export_filename);
+ if (path == NULL) {
+ return;
+ }
+ if ((messages = apol_vector_create(NULL)) == NULL) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ return;
+ }
+ for (l = glist; l != NULL; l = l->next) {
+ GtkTreePath *tree_path = (GtkTreePath *) l->data;
+ GtkTreeIter iter;
+ message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, tree_path);
+ if (apol_vector_append(messages, iter.user_data) < 0) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ apol_vector_destroy(&messages);
+ return;
+ }
+ }
+ message_view_export_messages_vector(view, path, messages);
+ g_list_foreach(glist, message_view_gtk_tree_path_free, NULL);
+ g_list_free(glist);
+ apol_vector_destroy(&messages);
+}
+
+/**
+ * Given the name of a column, return its column record data.
+ */
+static const struct view_column_record *get_record(const char *name)
+{
+ size_t i;
+ for (i = 0; i < num_columns; i++) {
+ const struct view_column_record *r = column_data + i;
+ if (strcmp(r->name, name) == 0) {
+ return r;
+ }
+ }
+ return NULL;
+}
+
+void message_view_update_visible_columns(message_view_t * view)
+{
+ GList *columns, *c;
+ preferences_t *prefs = toplevel_get_prefs(view->top);
+ columns = gtk_tree_view_get_columns(view->view);
+ c = columns;
+ while (c != NULL) {
+ GtkTreeViewColumn *vc = GTK_TREE_VIEW_COLUMN(c->data);
+ const gchar *title = gtk_tree_view_column_get_title(vc);
+ const struct view_column_record *r = get_record(title);
+ if (preferences_is_column_visible(prefs, r->id)) {
+ gtk_tree_view_column_set_visible(vc, TRUE);
+ } else {
+ gtk_tree_view_column_set_visible(vc, FALSE);
+ }
+ c = g_list_next(c);
+ }
+ g_list_free(columns);
+}
+
+void message_view_update_rows(message_view_t * view)
+{
+ /* remove all existing rows, then insert them back into the
+ * view according to the model. automatically scroll to the
+ * same seleceted row(s). */
+ GtkTreeSelection *selection;
+ GList *rows, *r, *selected = NULL;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ seaudit_log_t *log;
+ size_t i, num_old_messages = 0, num_new_messages = 0, num_changed;
+ int first_scroll = 0;
+
+ if (!seaudit_model_is_changed(view->model)) {
+ return;
+ }
+
+ /* convert the current selection into a GList of message
+ * pointers */
+ selection = gtk_tree_view_get_selection(view->view);
+ rows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ for (r = rows; r != NULL; r = r->next) {
+ path = (GtkTreePath *) r->data;
+ message_view_store_get_iter(GTK_TREE_MODEL(view->store), &iter, path);
+ selected = g_list_prepend(selected, iter.user_data);
+ }
+ g_list_foreach(rows, message_view_gtk_tree_path_free, NULL);
+ g_list_free(rows);
+
+ log = toplevel_get_log(view->top);
+ if (view->store->messages != NULL) {
+ num_old_messages = apol_vector_get_size(view->store->messages);
+ }
+ apol_vector_destroy(&view->store->messages);
+ if (log != NULL) {
+ view->store->messages = seaudit_model_get_messages(log, view->model);
+ num_new_messages = apol_vector_get_size(view->store->messages);
+ }
+ gtk_tree_selection_unselect_all(selection);
+
+ /* mark which rows have been changed/removed/inserted. do
+ * this as a single pass, rather than a two pass
+ * mark-and-sweep, for GTK+ tree views can be somewhat slow */
+ num_changed = num_old_messages;
+ if (num_new_messages < num_changed) {
+ num_changed = num_new_messages;
+ }
+ for (i = 0; i < num_changed; i++) {
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, i);
+ iter.user_data = apol_vector_get_element(view->store->messages, i);
+ iter.user_data2 = GINT_TO_POINTER(i);
+ iter.user_data3 = view;
+ gtk_tree_model_row_changed(GTK_TREE_MODEL(view->store), path, &iter);
+ for (r = selected; r != NULL; r = r->next) {
+ if (r->data == iter.user_data) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ if (!first_scroll) {
+ gtk_tree_view_scroll_to_cell(view->view, path, NULL, FALSE, 0.0, 0.0);
+ first_scroll = 1;
+ }
+ break;
+ }
+ }
+ gtk_tree_path_free(path);
+ }
+ if (num_old_messages > num_changed) {
+ /* delete in reverse order, else indices get renumbered */
+ for (i = num_old_messages; i > num_changed; i--) {
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, i - 1);
+ gtk_tree_model_row_deleted(GTK_TREE_MODEL(view->store), path);
+ gtk_tree_path_free(path);
+ }
+ } else {
+ for (; i < num_new_messages; i++) {
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, i);
+ iter.user_data = apol_vector_get_element(view->store->messages, i);
+ iter.user_data2 = GINT_TO_POINTER(i);
+ iter.user_data3 = view;
+ gtk_tree_model_row_inserted(GTK_TREE_MODEL(view->store), path, &iter);
+ for (r = selected; r != NULL; r = r->next) {
+ if (r->data == iter.user_data) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ if (!first_scroll) {
+ gtk_tree_view_scroll_to_cell(view->view, path, NULL, FALSE, 0.0, 0.0);
+ first_scroll = 1;
+ }
+ break;
+ }
+ }
+ gtk_tree_path_free(path);
+ }
+ }
+ g_list_free(selected);
+}
diff --git a/seaudit/message_view.h b/seaudit/message_view.h
new file mode 100644
index 0000000..5b9bdd6
--- /dev/null
+++ b/seaudit/message_view.h
@@ -0,0 +1,178 @@
+/**
+ * @file
+ * Declaration of a single tab within the main notebook, showing
+ * all messages within a libseaudit model.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MESSAGE_VIEW_H
+#define MESSAGE_VIEW_H
+
+#include "toplevel.h"
+
+#include <gtk/gtk.h>
+#include <seaudit/model.h>
+
+typedef struct message_view message_view_t;
+
+/**
+ * Allocate a new view for a particular model.
+ *
+ * @param top Handle to the controlling toplevel widget.
+ * @param model libseaudit model to display. The view takes ownership
+ * of the model afterwards.
+ * @param filename Initial filename for the view, or NULL if none.
+ * This function will duplicate the string.
+ *
+ * @return A newly allocated view, or NULL upon error. The caller is
+ * responsible for calling message_view_destroy() afterwards.
+ */
+message_view_t *message_view_create(toplevel_t * top, seaudit_model_t * model, const char *filename);
+
+/**
+ * Destroy a view and free its memory. This does nothing if the
+ * pointer is set to NULL.
+ *
+ * @param view Reference to a toplevel object. Afterwards the pointer
+ * will be set to NULL.
+ */
+void message_view_destroy(message_view_t ** view);
+
+/**
+ * Get the message view's model. If the caller changes the model then
+ * he is responsible for calling message_view_update_rows() to update
+ * the view.
+ *
+ * @param view View whose model to obtain.
+ *
+ * @return View's model.
+ */
+seaudit_model_t *message_view_get_model(message_view_t * view);
+
+/**
+ * Replace a message view's model with a different model. The
+ * previous model will be destroyed. Afterwards the view will update
+ * its rows.
+ *
+ * @param view View to modify.
+ * @param libseaudit model to display. The view takes ownership
+ * of the model afterwards.
+ */
+void message_view_set_model(message_view_t * view, seaudit_model_t * model);
+
+/**
+ * Get the message view's widget display. This widget will be placed
+ * in a container for the user to see.
+ *
+ * @param view View whose widget to obtain.
+ *
+ * @return View's widget.
+ */
+GtkWidget *message_view_get_view(message_view_t * view);
+
+/**
+ * Return the number of messages currently in this view.
+ *
+ * @param view View object to query.
+ *
+ * @return Number of log messages, or 0 if no model is associated with
+ * the view.
+ */
+size_t message_view_get_num_log_messages(message_view_t * view);
+
+/**
+ * Return TRUE if the message view has one or more messages selected,
+ * FALSE if not.
+ *
+ * @param view View object to query.
+ *
+ * @return TRUE if any messages are selected.
+ */
+gboolean message_view_is_message_selected(message_view_t * view);
+
+/**
+ * Save the current view to disk.
+ *
+ * @param view View to save.
+ */
+void message_view_save(message_view_t * view);
+
+/**
+ * Save the current view to disk under a new filename.
+ *
+ * @param view View to save.
+ */
+void message_view_saveas(message_view_t * view);
+
+/**
+ * Modify the settings for this view.
+ *
+ * @param view View to modify.
+ */
+void message_view_modify(message_view_t * view);
+
+/**
+ * Clear all messages from this view.
+ *
+ * @param view View to clear.
+ */
+void message_view_clear(message_view_t * view);
+
+/**
+ * Export to file all messages in a particular view.
+ *
+ * @param view View whose messages to export.
+ */
+void message_view_export_all_messages(message_view_t * view);
+
+/**
+ * Export to file all messages selected in a particular view.
+ *
+ * @param view View whose messages to export.
+ */
+void message_view_export_selected_messages(message_view_t * view);
+
+/**
+ * Open a dialog that shows an approximation of the message(s)
+ * currently selected.
+ *
+ * @param view View whose messages to show.
+ */
+void message_view_entire_message(message_view_t * view);
+
+/**
+ * Show/hide columns in a view based upon the user's current
+ * preferences.
+ *
+ * @param view View's columns to update.
+ */
+void message_view_update_visible_columns(message_view_t * view);
+
+/**
+ * (Re)synchronize the messages displayed in a view with its
+ * underlying model. This needs to be called when a model's filter
+ * changes or if new messages are found within the model's log.
+ *
+ * @param view View's rows to update.
+ */
+void message_view_update_rows(message_view_t * view);
+
+#endif
diff --git a/seaudit/modify_view.c b/seaudit/modify_view.c
new file mode 100644
index 0000000..abfbcc4
--- /dev/null
+++ b/seaudit/modify_view.c
@@ -0,0 +1,327 @@
+/**
+ * @file
+ * Run the dialog to modify a view.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "filter_view.h"
+#include "modify_view.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <glade/glade.h>
+
+struct modify_view
+{
+ toplevel_t *top;
+ message_view_t *view;
+ GladeXML *xml;
+ /** the model currently being modified -- note that this is a
+ * deep copy of the message_view's model */
+ seaudit_model_t *model;
+
+ /** model containing a list of filter names, needs to be
+ * destroyed afterwords */
+ GtkListStore *filter_store;
+ GtkDialog *dialog;
+ GtkEntry *name_entry;
+ GtkComboBox *visible_combo, *match_combo;
+ GtkTreeView *filter_view;
+ GtkButton *add_button, *edit_button, *remove_button, *import_button, *export_button;
+
+ /** keep track of most recent filter filename */
+ char *filter_filename;
+};
+
+/**
+ * Return the currently selected filter, or NULL if no filter is
+ * selected.
+ */
+static seaudit_filter_t *modify_view_get_current_filter(struct modify_view *mv)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view);
+ GtkTreeIter iter;
+ seaudit_filter_t *filter;
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return NULL;
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(mv->filter_store), &iter, 0, &filter, -1);
+ return filter;
+}
+
+/**
+ * Rebuild the filter store, preserving the current selection if
+ * possible.
+ */
+static void modify_view_update_filter_store(struct modify_view *mv)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view);
+ GtkTreeIter old_iter, iter;
+ gboolean selection_existed = gtk_tree_selection_get_selected(selection, NULL, &old_iter);
+ const apol_vector_t *filters = seaudit_model_get_filters(mv->model);
+ size_t i;
+ gtk_list_store_clear(mv->filter_store);
+ for (i = 0; i < apol_vector_get_size(filters); i++) {
+ seaudit_filter_t *filter = apol_vector_get_element(filters, i);
+ gtk_list_store_append(mv->filter_store, &iter);
+ gtk_list_store_set(mv->filter_store, &iter, 0, filter, 1, seaudit_filter_get_name(filter), -1);
+ }
+ /* initially select the last thing, then reset selection */
+ if (i > 0) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ if (selection_existed && gtk_list_store_iter_is_valid(mv->filter_store, &old_iter)) {
+ gtk_tree_selection_select_iter(selection, &old_iter);
+ }
+}
+
+static void modify_view_on_selection_change(GtkTreeSelection * selection, gpointer user_data)
+{
+ struct modify_view *mv = (struct modify_view *)user_data;
+ gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL);
+ gtk_widget_set_sensitive(GTK_WIDGET(mv->edit_button), sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(mv->remove_button), sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(mv->export_button), sens);
+}
+
+static void modify_view_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct modify_view *mv = (struct modify_view *)user_data;
+ seaudit_filter_t *filter = NULL;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view);
+
+ if ((filter = seaudit_filter_create("Untitled")) == NULL || (seaudit_model_append_filter(mv->model, filter) < 0)) {
+ toplevel_ERR(mv->top, "Error adding new filter: %s", strerror(errno));
+ seaudit_filter_destroy(&filter);
+ return;
+ }
+ /* select the filter that was just created, by removing the
+ * selection and letting the following function select it */
+ gtk_tree_selection_unselect_all(selection);
+ modify_view_update_filter_store(mv);
+ filter_view_run(filter, mv->top, GTK_WINDOW(mv->dialog));
+ modify_view_update_filter_store(mv);
+}
+
+static void modify_view_on_edit_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct modify_view *mv = (struct modify_view *)user_data;
+ seaudit_filter_t *filter = modify_view_get_current_filter(mv);
+ assert(filter != NULL);
+ filter_view_run(filter, mv->top, GTK_WINDOW(mv->dialog));
+ modify_view_update_filter_store(mv);
+}
+
+static void modify_view_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct modify_view *mv = (struct modify_view *)user_data;
+ seaudit_filter_t *filter = modify_view_get_current_filter(mv);
+ assert(filter != NULL);
+ seaudit_model_remove_filter(mv->model, filter);
+ modify_view_update_filter_store(mv);
+}
+
+static void modify_view_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct modify_view *mv = (struct modify_view *)user_data;
+ apol_vector_t *paths = util_open_file(GTK_WINDOW(mv->dialog), "Import Filter", mv->filter_filename, 0);
+ apol_vector_t *filters;
+ size_t i;
+ if (paths == NULL) {
+ return;
+ }
+ free(mv->filter_filename);
+ if ((mv->filter_filename = strdup(apol_vector_get_element(paths, 0))) == NULL) {
+ toplevel_ERR(mv->top, "Error importing filter: %s", strerror(errno));
+ apol_vector_destroy(&paths);
+ return;
+ }
+ apol_vector_destroy(&paths);
+ if ((filters = seaudit_filter_create_from_file(mv->filter_filename)) == NULL) {
+ toplevel_ERR(mv->top, "Error importing filter: %s", strerror(errno));
+ apol_vector_destroy(&paths);
+ return;
+ }
+ for (i = 0; i < apol_vector_get_size(filters); i++) {
+ seaudit_filter_t *filter = apol_vector_get_element(filters, i);
+ seaudit_filter_t *new_f = NULL;
+ if ((new_f = seaudit_filter_create_from_filter(filter)) == NULL ||
+ seaudit_model_append_filter(mv->model, new_f) < 0) {
+ toplevel_ERR(mv->top, "Error importing filter: %s", strerror(errno));
+ seaudit_filter_destroy(&new_f);
+ apol_vector_destroy(&filters);
+ return;
+ }
+ modify_view_update_filter_store(mv);
+ }
+ apol_vector_destroy(&filters);
+}
+
+static void modify_view_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct modify_view *mv = (struct modify_view *)user_data;
+ char *path = util_save_file(GTK_WINDOW(mv->dialog), "Export Filter", mv->filter_filename);
+ seaudit_filter_t *filter = modify_view_get_current_filter(mv);
+ assert(filter != NULL);
+ if (path == NULL) {
+ return;
+ }
+ g_free(mv->filter_filename);
+ mv->filter_filename = path;
+ if (seaudit_filter_save_to_file(filter, mv->filter_filename) < 0) {
+ toplevel_ERR(mv->top, "Error exporting filter: %s", strerror(errno));
+ }
+}
+
+/**
+ * Make libglade calls to fill in struct modify_view widget
+ * references.
+ */
+static void modify_view_init_widgets(struct modify_view *mv)
+{
+ mv->dialog = GTK_DIALOG(glade_xml_get_widget(mv->xml, "ModifyViewWindow"));
+ assert(mv->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(mv->dialog), toplevel_get_window(mv->top));
+
+ mv->name_entry = GTK_ENTRY(glade_xml_get_widget(mv->xml, "ModifyViewNameEntry"));
+ assert(mv->name_entry != NULL);
+
+ mv->visible_combo = GTK_COMBO_BOX(glade_xml_get_widget(mv->xml, "ModifyViewVisibleCombo"));
+ mv->match_combo = GTK_COMBO_BOX(glade_xml_get_widget(mv->xml, "ModifyViewMatchCombo"));
+ assert(mv->visible_combo != NULL && mv->match_combo != NULL);
+
+ mv->filter_view = GTK_TREE_VIEW(glade_xml_get_widget(mv->xml, "ModifyViewFilterView"));
+ assert(mv->filter_view != NULL);
+ mv->filter_store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
+ gtk_tree_view_set_model(mv->filter_view, GTK_TREE_MODEL(mv->filter_store));
+
+ mv->add_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewAddButton"));
+ mv->edit_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewEditButton"));
+ mv->remove_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewRemoveButton"));
+ mv->import_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewImportButton"));
+ mv->export_button = GTK_BUTTON(glade_xml_get_widget(mv->xml, "ModifyViewExportButton"));
+ assert(mv->add_button != NULL && mv->edit_button != NULL && mv->remove_button != NULL &&
+ mv->import_button != NULL && mv->export_button != NULL);
+}
+
+static void modify_view_init_signals(struct modify_view *mv)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Filter names", renderer, "text", 1, NULL);
+ gtk_tree_view_column_set_clickable(column, FALSE);
+ gtk_tree_view_column_set_resizable(column, FALSE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_visible(column, TRUE);
+ gtk_tree_view_append_column(mv->filter_view, column);
+
+ selection = gtk_tree_view_get_selection(mv->filter_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ g_signal_connect(selection, "changed", G_CALLBACK(modify_view_on_selection_change), mv);
+
+ g_signal_connect(mv->add_button, "clicked", G_CALLBACK(modify_view_on_add_click), mv);
+ g_signal_connect(mv->edit_button, "clicked", G_CALLBACK(modify_view_on_edit_click), mv);
+ g_signal_connect(mv->remove_button, "clicked", G_CALLBACK(modify_view_on_remove_click), mv);
+ g_signal_connect(mv->import_button, "clicked", G_CALLBACK(modify_view_on_import_click), mv);
+ g_signal_connect(mv->export_button, "clicked", G_CALLBACK(modify_view_on_export_click), mv);
+}
+
+/**
+ * Set up the window to reflect the current view's values.
+ */
+static void modify_view_init_dialog(struct modify_view *mv)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(mv->filter_view);
+ GtkTreeIter iter;
+
+ gtk_entry_set_text(mv->name_entry, seaudit_model_get_name(mv->model));
+
+ gtk_combo_box_set_active(mv->visible_combo, seaudit_model_get_filter_visible(mv->model));
+ gtk_combo_box_set_active(mv->match_combo, seaudit_model_get_filter_match(mv->model));
+
+ gtk_tree_selection_unselect_all(selection);
+ modify_view_update_filter_store(mv);
+ /* automatically select the first filter upon dialog
+ * startup */
+
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mv->filter_store), &iter)) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+}
+
+int modify_view_run(toplevel_t * top, message_view_t * view)
+{
+ struct modify_view mv;
+ seaudit_model_t *orig_model = message_view_get_model(view);
+ gint response;
+ int retval;
+
+ memset(&mv, 0, sizeof(mv));
+ mv.top = top;
+ mv.view = view;
+ mv.xml = glade_xml_new(toplevel_get_glade_xml(top), "ModifyViewWindow", NULL);
+ mv.filter_filename = NULL;
+ if ((mv.model = seaudit_model_create_from_model(orig_model)) == NULL) {
+ toplevel_ERR(mv.top, "Error duplicating model: %s", strerror(errno));
+ return 0;
+ }
+ modify_view_init_widgets(&mv);
+ modify_view_init_signals(&mv);
+ modify_view_init_dialog(&mv);
+
+ do {
+ response = gtk_dialog_run(mv.dialog);
+ const gchar *text = gtk_entry_get_text(mv.name_entry);
+
+ if (seaudit_model_set_name(mv.model, text) < 0) {
+ toplevel_ERR(mv.top, "Could not set name: %s", strerror(errno));
+ }
+ seaudit_model_set_filter_visible(mv.model, gtk_combo_box_get_active(mv.visible_combo));
+ seaudit_model_set_filter_match(mv.model, gtk_combo_box_get_active(mv.match_combo));
+ if (response == GTK_RESPONSE_APPLY) {
+ seaudit_model_t *new_model;
+ if ((new_model = seaudit_model_create_from_model(mv.model)) == NULL) {
+ toplevel_ERR(mv.top, "Error applying model: %s", strerror(errno));
+ break;
+ }
+ message_view_set_model(mv.view, new_model);
+ toplevel_update_status_bar(mv.top);
+ }
+ } while (response == GTK_RESPONSE_APPLY);
+
+ if (response == GTK_RESPONSE_OK) {
+ message_view_set_model(mv.view, mv.model);
+ retval = 1;
+ } else {
+ seaudit_model_destroy(&mv.model);
+ retval = 0;
+ }
+ gtk_widget_destroy(GTK_WIDGET(mv.dialog));
+ g_object_unref(mv.filter_store);
+ return retval;
+}
diff --git a/seaudit/modify_view.h b/seaudit/modify_view.h
new file mode 100644
index 0000000..06993f3
--- /dev/null
+++ b/seaudit/modify_view.h
@@ -0,0 +1,41 @@
+/**
+ * @file
+ * Dialog that allows the user to modify the current view.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MODIFY_VIEW_H
+#define MODIFY_VIEW_H
+
+#include "toplevel.h"
+#include "message_view.h"
+
+/**
+ * Display and run a dialog that allows the user to modify a view.
+ *
+ * @param top Toplevel containing message view.
+ * @param view Message view to modify.
+ *
+ * @return Non-zero if the view changed, zero if not.
+ */
+int modify_view_run(toplevel_t * top, message_view_t * view);
+
+#endif
diff --git a/seaudit/open_policy_window.c b/seaudit/open_policy_window.c
new file mode 100644
index 0000000..ca8a511
--- /dev/null
+++ b/seaudit/open_policy_window.c
@@ -0,0 +1,469 @@
+/**
+ * @file
+ * Run the dialog to allow the user to open a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "open_policy_window.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+
+struct open_policy
+{
+ GladeXML *xml;
+ toplevel_t *top;
+ char *last_module_path;
+ GtkDialog *dialog;
+
+ GtkRadioButton *monolithic_radio, *modular_radio;
+ GtkLabel *main_label;
+
+ GtkHBox *bottom_hbox;
+ GtkListStore *module_store;
+
+ GtkEntry *base_entry;
+ GtkButton *base_browse_button;
+
+ GtkTreeView *module_view;
+ GtkButton *add_button, *remove_button, *import_button, *export_button;
+ GtkButton *ok_button;
+};
+
+enum module_columns
+{
+ PATH_COLUMN = 0, NAME_COLUMN, VERSION_COLUMN, NUM_COLUMNS
+};
+
+/**
+ * Sort columns in alphabetical order.
+ */
+static gint open_policy_sort(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data)
+{
+ GValue value_a = { 0 }, value_b = {
+ 0};
+ const char *name_a, *name_b;
+ int retval, column_id = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get_value(model, a, column_id, &value_a);
+ gtk_tree_model_get_value(model, b, column_id, &value_b);
+ name_a = g_value_get_string(&value_a);
+ name_b = g_value_get_string(&value_b);
+ retval = strcmp(name_a, name_b);
+ g_value_unset(&value_a);
+ g_value_unset(&value_b);
+ return retval;
+}
+
+static void open_policy_init_widgets(struct open_policy *op)
+{
+ GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+
+ op->dialog = GTK_DIALOG(glade_xml_get_widget(op->xml, "PolicyOpenWindow"));
+ assert(op->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(op->dialog), toplevel_get_window(op->top));
+
+ op->monolithic_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, "monolithic radio"));
+ op->modular_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, "modular radio"));
+ op->main_label = GTK_LABEL(glade_xml_get_widget(op->xml, "main filename label"));
+ assert(op->monolithic_radio != NULL && op->modular_radio != NULL && op->main_label != NULL);
+
+ op->base_entry = GTK_ENTRY(glade_xml_get_widget(op->xml, "base entry"));
+ op->base_browse_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "base browse"));
+ assert(op->base_entry != NULL && op->base_browse_button != NULL);
+
+ op->bottom_hbox = GTK_HBOX(glade_xml_get_widget(op->xml, "hbox.3"));
+ assert(op->bottom_hbox != NULL);
+
+ op->module_view = GTK_TREE_VIEW(glade_xml_get_widget(op->xml, "module view"));
+ assert(op->module_view != NULL);
+ op->module_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model(op->module_view, GTK_TREE_MODEL(op->module_store));
+
+ op->add_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module add button"));
+ op->remove_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module remove button"));
+ op->import_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module list import button"));
+ op->export_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "module list export button"));
+ op->ok_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "ok button"));
+ assert(op->add_button != NULL && op->remove_button != NULL &&
+ op->import_button != NULL && op->export_button != NULL && op->ok_button != NULL);
+
+ selection = gtk_tree_view_get_selection(op->module_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+
+ column = gtk_tree_view_column_new_with_attributes("Module", renderer, "text", NAME_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NAME_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(op->module_view, column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(op->module_store), NAME_COLUMN, open_policy_sort, (gpointer) NAME_COLUMN,
+ NULL);
+
+ column = gtk_tree_view_column_new_with_attributes("Version", renderer, "text", VERSION_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, VERSION_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(op->module_view, column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(op->module_store), VERSION_COLUMN, open_policy_sort,
+ (gpointer) VERSION_COLUMN, NULL);
+
+ column = gtk_tree_view_column_new_with_attributes("Path", renderer, "text", PATH_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PATH_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(op->module_view, column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(op->module_store), PATH_COLUMN, open_policy_sort, (gpointer) PATH_COLUMN,
+ NULL);
+
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(op->module_store), NAME_COLUMN, GTK_SORT_ASCENDING);
+}
+
+static void open_policy_on_policy_type_toggle(GtkToggleButton * widget, gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ /* clicking on the radio buttons emit two toggle signals, one for
+ * the original button and one for the new one. thus only need to
+ * handle half of all signals */
+ if (!gtk_toggle_button_get_active(widget)) {
+ return;
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(op->monolithic_radio))) {
+ gtk_widget_set_sensitive(GTK_WIDGET(op->bottom_hbox), FALSE);
+ gtk_label_set_markup(op->main_label, "<b>Policy Filename:</b>");
+ } else {
+ gtk_widget_set_sensitive(GTK_WIDGET(op->bottom_hbox), TRUE);
+ gtk_label_set_markup(op->main_label, "<b>Base Filename:</b>");
+ }
+}
+
+static void open_policy_on_entry_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ gboolean sens = FALSE;
+ if (strcmp(gtk_entry_get_text(op->base_entry), "") != 0) {
+ sens = TRUE;
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(op->export_button), sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(op->ok_button), sens);
+}
+
+static void open_policy_on_base_browse_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ const char *current_path = gtk_entry_get_text(op->base_entry);
+ char *title;
+ apol_vector_t *paths;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(op->monolithic_radio))) {
+ title = "Open Monolithic Policy";
+ } else {
+ title = "Open Modular Policy";
+ }
+ if (strcmp(current_path, "") == 0) {
+ current_path = NULL;
+ }
+ if ((paths = util_open_file(GTK_WINDOW(op->dialog), title, current_path, 0)) == NULL) {
+ return;
+ }
+ gtk_entry_set_text(op->base_entry, apol_vector_get_element(paths, 0));
+ apol_vector_destroy(&paths);
+}
+
+/**
+ * Attempt to load a module and retrieve its name and version. Upon
+ * success add an entry to the list store.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int open_policy_load_module(struct open_policy *op, const char *path)
+{
+ const char *module_name, *version_string;
+ int module_type;
+ qpol_module_t *module = NULL;
+ GtkTreeIter iter;
+
+ /* check if modulue was already loaded */
+ gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(op->module_store), &iter);
+ while (iter_valid) {
+ char *s;
+ gtk_tree_model_get(GTK_TREE_MODEL(op->module_store), &iter, PATH_COLUMN, &s, -1);
+ if (strcmp(s, path) == 0) {
+ toplevel_ERR(op->top, "Module %s was already added.", path);
+ return -1;
+ }
+ iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(op->module_store), &iter);
+ }
+ if ((qpol_module_create_from_file(path, &module)) < 0) {
+ toplevel_ERR(op->top, "Error opening module %s: %s", path, strerror(errno));
+ return -1;
+ }
+ if (qpol_module_get_name(module, &module_name) < 0 ||
+ qpol_module_get_version(module, &version_string) < 0 || qpol_module_get_type(module, &module_type) < 0) {
+ toplevel_ERR(op->top, "Error reading module %s: %s", path, strerror(errno));
+ qpol_module_destroy(&module);
+ return -1;
+ }
+ if (module_type != QPOL_MODULE_OTHER) {
+ toplevel_ERR(op->top, "%s is not a loadable module.", path);
+ qpol_module_destroy(&module);
+ return -1;
+ }
+ gtk_list_store_append(op->module_store, &iter);
+ gtk_list_store_set(op->module_store, &iter, PATH_COLUMN, path, NAME_COLUMN, module_name, VERSION_COLUMN, version_string,
+ -1);
+ qpol_module_destroy(&module);
+ return 0;
+}
+
+static void open_policy_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ apol_vector_t *paths;
+ const char *path = NULL, *prev_path;
+ size_t i;
+ if ((prev_path = op->last_module_path) == NULL) {
+ prev_path = gtk_entry_get_text(op->base_entry);
+ if (strcmp(prev_path, "") == 0) {
+ prev_path = NULL;
+ }
+ }
+ paths = util_open_file(GTK_WINDOW(op->dialog), "Open Module", prev_path, 1);
+ if (paths == NULL) {
+ return;
+ }
+ int all_succeed = 1;
+ for (i = 0; i < apol_vector_get_size(paths); i++) {
+ path = apol_vector_get_element(paths, i);
+ if (open_policy_load_module(op, path) < 0) {
+ all_succeed = 0;
+ }
+ }
+ if (all_succeed) {
+ assert(path != NULL);
+ free(op->last_module_path);
+ op->last_module_path = strdup(path);
+ }
+ apol_vector_destroy(&paths);
+}
+
+static void open_policy_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(op->module_view);
+ GtkTreeIter iter;
+ char *path;
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return;
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(op->module_store), &iter, 0, &path, -1);
+ gtk_list_store_remove(op->module_store, &iter);
+}
+
+static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data);
+static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data);
+
+static void open_policy_on_selection_change(GtkTreeSelection * selection, gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL);
+ gtk_widget_set_sensitive(GTK_WIDGET(op->remove_button), sens);
+}
+
+static void open_policy_init_signals(struct open_policy *op)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(op->module_view);
+ g_signal_connect(op->monolithic_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), op);
+ g_signal_connect(op->modular_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), op);
+ g_signal_connect(op->base_entry, "event-after", G_CALLBACK(open_policy_on_entry_event_after), op);
+ g_signal_connect(op->base_browse_button, "clicked", G_CALLBACK(open_policy_on_base_browse_click), op);
+ g_signal_connect(selection, "changed", G_CALLBACK(open_policy_on_selection_change), op);
+ g_signal_connect(op->add_button, "clicked", G_CALLBACK(open_policy_on_add_click), op);
+ g_signal_connect(op->remove_button, "clicked", G_CALLBACK(open_policy_on_remove_click), op);
+ g_signal_connect(op->import_button, "clicked", G_CALLBACK(open_policy_on_import_click), op);
+ g_signal_connect(op->export_button, "clicked", G_CALLBACK(open_policy_on_export_click), op);
+}
+
+static void open_policy_init_values(struct open_policy *op, const apol_policy_path_t * path)
+{
+ if (path != NULL) {
+ apol_policy_path_type_e path_type = apol_policy_path_get_type(path);
+ const char *primary_path = apol_policy_path_get_primary(path);
+ gtk_entry_set_text(op->base_entry, primary_path);
+ gtk_list_store_clear(op->module_store);
+ if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(op->monolithic_radio), TRUE);
+ } else if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t i;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(op->modular_radio), TRUE);
+ for (i = 0; i < apol_vector_get_size(modules); i++) {
+ char *module_path = apol_vector_get_element(modules, i);
+ if (open_policy_load_module(op, module_path) < 0) {
+ break;
+ }
+ }
+ } else {
+ /* should never get here */
+ toplevel_ERR(op->top, "Unknown policy path type %d.", path_type);
+ }
+ }
+}
+
+/**
+ * Build the policy path corresponding to the user's inputs on this
+ * dialog.
+ *
+ * @return path for the dialog, or NULL upon error. The caller must
+ * call apol_policy_path_destroy() afterwards.
+ */
+static apol_policy_path_t *open_policy_build_path(struct open_policy *op)
+{
+ const char *primary_path = gtk_entry_get_text(op->base_entry);
+ apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ apol_vector_t *modules = NULL;
+ apol_policy_path_t *path = NULL;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(op->modular_radio))) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ GtkTreeIter iter;
+ if ((modules = apol_vector_create(free)) == NULL) {
+ toplevel_ERR(op->top, "%s", strerror(errno));
+ return NULL;
+ }
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(op->module_store), &iter)) {
+ do {
+ GValue value = { 0 };
+ char *module_path;
+ gtk_tree_model_get_value(GTK_TREE_MODEL(op->module_store), &iter, PATH_COLUMN, &value);
+ module_path = g_value_dup_string(&value);
+ g_value_unset(&value);
+ if (apol_vector_append(modules, module_path) < 0) {
+ toplevel_ERR(op->top, "%s", strerror(errno));
+ free(module_path);
+ apol_vector_destroy(&modules);
+ return NULL;
+ }
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(op->module_store), &iter));
+ }
+ }
+ path = apol_policy_path_create(path_type, primary_path, modules);
+ apol_vector_destroy(&modules);
+ if (path == NULL) {
+ toplevel_ERR(op->top, "%s", strerror(errno));
+ return NULL;
+ }
+ return path;
+}
+
+static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ apol_vector_t *paths = NULL;
+ apol_policy_path_t *ppath = NULL;
+ const char *path = NULL, *prev_path;
+ if ((prev_path = op->last_module_path) == NULL) {
+ prev_path = gtk_entry_get_text(op->base_entry);
+ if (strcmp(prev_path, "") == 0) {
+ prev_path = NULL;
+ }
+ }
+ paths = util_open_file(GTK_WINDOW(op->dialog), "Import Policy List", prev_path, 1);
+ if (paths == NULL) {
+ return;
+ }
+ path = apol_vector_get_element(paths, 0);
+ ppath = apol_policy_path_create_from_file(path);
+ if (ppath == NULL) {
+ toplevel_ERR(op->top, "Error importing policy list %s: %s", path, strerror(errno));
+ goto cleanup;
+ }
+ open_policy_init_values(op, ppath);
+ free(op->last_module_path);
+ op->last_module_path = strdup(path);
+ cleanup:
+ apol_vector_destroy(&paths);
+ apol_policy_path_destroy(&ppath);
+}
+
+static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ char *path = util_save_file(GTK_WINDOW(op->dialog), "Export Policy List", NULL);
+ apol_policy_path_t *ppath = NULL;
+ if (path == NULL) {
+ return;
+ }
+ ppath = open_policy_build_path(op);
+ if (ppath == NULL) {
+ goto cleanup;
+ }
+ if (apol_policy_path_to_file(ppath, path) < 0) {
+ toplevel_ERR(op->top, "Error exporting policy list %s: %s", path, strerror(errno));
+ }
+ cleanup:
+ g_free(path);
+ apol_policy_path_destroy(&ppath);
+}
+
+void open_policy_window_run(toplevel_t * top, const apol_policy_path_t * path, apol_policy_path_t ** selection)
+{
+ struct open_policy op;
+ gint response;
+ apol_policy_path_t *input;
+
+ memset(&op, 0, sizeof(op));
+ op.top = top;
+ op.xml = glade_xml_new(toplevel_get_glade_xml(top), "PolicyOpenWindow", NULL);
+
+ open_policy_init_widgets(&op);
+ open_policy_init_signals(&op);
+ open_policy_init_values(&op, path);
+ if (selection != NULL) {
+ *selection = NULL;
+ }
+
+ while (1) {
+ response = gtk_dialog_run(op.dialog);
+ if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) {
+ break;
+ }
+ if ((input = open_policy_build_path(&op)) == NULL) {
+ continue;
+ }
+ if (selection == NULL) {
+ if (toplevel_open_policy(op.top, input) == 0) {
+ break;
+ }
+ } else {
+ *selection = input;
+ break;
+ }
+ }
+ gtk_widget_destroy(GTK_WIDGET(op.dialog));
+ free(op.last_module_path);
+ g_object_unref(op.module_store);
+}
diff --git a/seaudit/open_policy_window.h b/seaudit/open_policy_window.h
new file mode 100644
index 0000000..2b57e9d
--- /dev/null
+++ b/seaudit/open_policy_window.h
@@ -0,0 +1,46 @@
+/**
+ * @file
+ * Dialog that allows the user to select either a monolithic policy
+ * or a base policy + list of modules. The dialog may also actually
+ * open the policy or it may be used simply as a file chooser.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OPEN_POLICY_WINDOW_H
+#define OPEN_POLICY_WINDOW_H
+
+#include "toplevel.h"
+#include <apol/policy-path.h>
+
+/**
+ * Display and run a dialog that allows the user open a policy, either
+ * a monolithic or a modular policy.
+ *
+ * @param top Toplevel for the application.
+ * @param path If not NULL, the default path for the policy.
+ * @param selection If non-NULL, then allocate and set this reference
+ * pointer to the path selected; caller must call
+ * apol_policy_path_destroy() afterwards. Otherwise if NULL then
+ * actually load the policy and set the path tloplvel_open_policy().
+ */
+void open_policy_window_run(toplevel_t * top, const apol_policy_path_t * path, apol_policy_path_t ** selection);
+
+#endif
diff --git a/seaudit/policy_components_view.c b/seaudit/policy_components_view.c
new file mode 100644
index 0000000..c2f1d02
--- /dev/null
+++ b/seaudit/policy_components_view.c
@@ -0,0 +1,373 @@
+/**
+ * @file
+ * Run the dialog to select from lists of strings.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "policy_components_view.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/util.h>
+#include <glade/glade.h>
+
+struct polcomp_view
+{
+ GladeXML *xml;
+ toplevel_t *top;
+ apol_vector_t *log_items, *policy_items, *both_items, *included_items;
+ GtkDialog *dialog;
+
+ GtkRadioButton *log_radio, *policy_radio, *both_radio;
+
+ GtkListStore *master_store;
+ GtkTreeModel *inc_store, *exc_store;
+ GtkTreeView *inc_view, *exc_view;
+ GtkButton *inc_select_button, *inc_unselect_button, *exc_select_button, *exc_unselect_button;
+
+ GtkButton *to_exc_button, *to_inc_button;
+};
+
+enum polcom_columns
+{
+ POINTER_COLUMN = 0, NAME_COLUMN, ISLOG_COLUMN, ISPOLICY_COLUMN, ISINC_COLUMN
+};
+
+static void policy_components_view_init_widgets(struct polcomp_view *pv)
+{
+ pv->dialog = GTK_DIALOG(glade_xml_get_widget(pv->xml, "PolicyComponentListsWindow"));
+ assert(pv->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(pv->dialog), toplevel_get_window(pv->top));
+
+ pv->log_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompLogRadio"));
+ pv->policy_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompPolicyRadio"));
+ pv->both_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompBothRadio"));
+ assert(pv->log_radio != NULL && pv->policy_radio != NULL && pv->both_radio != NULL);
+
+ pv->inc_view = GTK_TREE_VIEW(glade_xml_get_widget(pv->xml, "PolicyCompIncView"));
+ pv->exc_view = GTK_TREE_VIEW(glade_xml_get_widget(pv->xml, "PolicyCompExcView"));
+ assert(pv->inc_view != NULL && pv->exc_view != NULL);
+
+ pv->inc_select_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompIncSelectButton"));
+ pv->inc_unselect_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompIncUnselectButton"));
+ pv->exc_select_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompExcSelectButton"));
+ pv->exc_unselect_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompExcUnselectButton"));
+ assert(pv->inc_select_button != NULL && pv->inc_unselect_button &&
+ pv->exc_select_button != NULL && pv->exc_unselect_button);
+
+ pv->to_exc_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompToExcButton"));
+ pv->to_inc_button = GTK_BUTTON(glade_xml_get_widget(pv->xml, "PolicyCompToIncButton"));
+ assert(pv->to_exc_button != NULL && pv->to_inc_button);
+}
+
+/******************** functions to build the lists ********************/
+
+/**
+ * Determine if a particular row should be visible based upon the
+ * current radio button selection. If the current item source is
+ * 'Log' then return TRUE if the islog attribute is enabled. If the
+ * source is 'Policy' then return TRUE if ispolicy is enabled.
+ * Otherwise the item is visible by default.
+ */
+static gboolean policy_components_view_is_visible_radio(struct polcomp_view *pv, gboolean islog, gboolean ispolicy)
+{
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->log_radio))) {
+ return islog;
+ } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->policy_radio))) {
+ return ispolicy;
+ }
+ return TRUE;
+}
+
+/**
+ * Callback invoked to determine if a row should be visible to the
+ * included view.
+ */
+static gboolean policy_components_view_is_visible_included(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
+{
+ struct polcomp_view *pv = (struct polcomp_view *)data;
+ gboolean islog, ispolicy, isinc;
+ gtk_tree_model_get(model, iter, ISLOG_COLUMN, &islog, ISPOLICY_COLUMN, &ispolicy, ISINC_COLUMN, &isinc, -1);
+ if (!isinc) {
+ return FALSE;
+ }
+ return policy_components_view_is_visible_radio(pv, islog, ispolicy);
+}
+
+/**
+ * Callback invoked to determine if a row should be visible in the
+ * included view.
+ */
+static gboolean policy_components_view_is_visible_excluded(GtkTreeModel * model, GtkTreeIter * iter, gpointer data)
+{
+ struct polcomp_view *pv = (struct polcomp_view *)data;
+ gboolean islog, ispolicy, isinc;
+ gtk_tree_model_get(model, iter, ISLOG_COLUMN, &islog, ISPOLICY_COLUMN, &ispolicy, ISINC_COLUMN, &isinc, -1);
+ if (isinc) {
+ return FALSE;
+ }
+ return policy_components_view_is_visible_radio(pv, islog, ispolicy);
+}
+
+static void policy_components_view_init_lists(struct polcomp_view *pv)
+{
+ GtkTreeIter iter;
+ size_t i, j;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+
+ if ((pv->both_items = apol_vector_create_from_vector(pv->log_items, NULL, NULL, NULL)) == NULL) {
+ toplevel_ERR(pv->top, "Error generating union list: %s", strerror(errno));
+ return;
+ }
+ if (pv->policy_items == NULL) {
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->policy_radio), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->both_radio), FALSE);
+ } else {
+ if (apol_vector_cat(pv->both_items, pv->policy_items) < 0) {
+ toplevel_ERR(pv->top, "Error generating union list: %s", strerror(errno));
+ return;
+ }
+ apol_vector_sort_uniquify(pv->both_items, apol_str_strcmp, NULL);
+ }
+ pv->master_store =
+ gtk_list_store_new(ISINC_COLUMN + 1, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ for (i = 0; i < apol_vector_get_size(pv->both_items); i++) {
+ char *s = apol_vector_get_element(pv->both_items, i);
+ gboolean is_log = FALSE, is_policy = FALSE, is_included = FALSE;
+ if (apol_vector_get_index(pv->log_items, s, NULL, NULL, &j) == 0) {
+ is_log = TRUE;
+ }
+ if (pv->policy_items != NULL && apol_vector_get_index(pv->policy_items, s, NULL, NULL, &j) == 0) {
+ is_policy = TRUE;
+ }
+ if (apol_vector_get_index(pv->included_items, s, apol_str_strcmp, NULL, &j) == 0) {
+ is_included = TRUE;
+ }
+ gtk_list_store_append(pv->master_store, &iter);
+ gtk_list_store_set(pv->master_store, &iter,
+ POINTER_COLUMN, s,
+ NAME_COLUMN, s, ISLOG_COLUMN, is_log, ISPOLICY_COLUMN, is_policy, ISINC_COLUMN, is_included, -1);
+ }
+
+ pv->inc_store = gtk_tree_model_filter_new(GTK_TREE_MODEL(pv->master_store), NULL);
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(pv->inc_store), policy_components_view_is_visible_included, pv,
+ NULL);
+ pv->exc_store = gtk_tree_model_filter_new(GTK_TREE_MODEL(pv->master_store), NULL);
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(pv->exc_store), policy_components_view_is_visible_excluded, pv,
+ NULL);
+ gtk_tree_view_set_model(pv->inc_view, pv->inc_store);
+ gtk_tree_view_set_model(pv->exc_view, pv->exc_store);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Item names", renderer, "text", NAME_COLUMN, NULL);
+ gtk_tree_view_column_set_clickable(column, FALSE);
+ gtk_tree_view_column_set_resizable(column, FALSE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_visible(column, TRUE);
+ gtk_tree_view_append_column(pv->inc_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Item names", renderer, "text", NAME_COLUMN, NULL);
+ gtk_tree_view_column_set_clickable(column, FALSE);
+ gtk_tree_view_column_set_resizable(column, FALSE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_visible(column, TRUE);
+ gtk_tree_view_append_column(pv->exc_view, column);
+
+ selection = gtk_tree_view_get_selection(pv->inc_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+ selection = gtk_tree_view_get_selection(pv->exc_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+}
+
+/******************** signal handlers ********************/
+
+static void policy_components_view_on_source_toggle(GtkToggleButton * widget, gpointer user_data)
+{
+ struct polcomp_view *pv = (struct polcomp_view *)user_data;
+ /* clicking on the radio buttons emit two toggle signals, one for
+ * the original button and one for the new one. thus only need to
+ * handle half of all signals */
+ if (!gtk_toggle_button_get_active(widget)) {
+ return;
+ }
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->inc_store));
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->exc_store));
+}
+
+static void policy_components_gtk_tree_path_free(gpointer data, gpointer user_data __attribute__ ((unused)))
+{
+ gtk_tree_path_free((GtkTreePath *) data);
+}
+
+static void policy_components_gtk_tree_row_reference_free(gpointer data, gpointer user_data __attribute__ ((unused)))
+{
+ gtk_tree_row_reference_free((GtkTreeRowReference *) data);
+}
+
+static void policy_components_view_on_to_exc_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct polcomp_view *pv = (struct polcomp_view *)user_data;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(pv->inc_view);
+ GList *r, *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
+ GList *refs = NULL;
+
+ /* use references because the filtered store will be changing
+ * as master_store's ISINC_COLUMN value changes */
+ for (r = rows; r != NULL; r = r->next) {
+ GtkTreePath *path = (GtkTreePath *) r->data;
+ GtkTreeRowReference *ref = gtk_tree_row_reference_new(pv->inc_store, path);
+ refs = g_list_prepend(refs, ref);
+ }
+
+ for (r = refs; r != NULL; r = r->next) {
+ GtkTreeRowReference *ref = (GtkTreeRowReference *) r->data;
+ GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
+ GtkTreeIter iter, child_iter;
+ char *name;
+ size_t i = 0;
+ int retval;
+ gtk_tree_model_get_iter(pv->inc_store, &iter, path);
+ gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(pv->inc_store), &child_iter, &iter);
+ gtk_tree_model_get(GTK_TREE_MODEL(pv->master_store), &child_iter, NAME_COLUMN, &name, -1);
+ gtk_list_store_set(pv->master_store, &child_iter, ISINC_COLUMN, FALSE, -1);
+ retval = apol_vector_get_index(pv->included_items, name, apol_str_strcmp, NULL, &i);
+ assert(retval == 0);
+ name = apol_vector_get_element(pv->included_items, i);
+ free(name);
+ apol_vector_remove(pv->included_items, i);
+ }
+
+ g_list_foreach(refs, policy_components_gtk_tree_row_reference_free, NULL);
+ g_list_free(refs);
+ g_list_foreach(rows, policy_components_gtk_tree_path_free, NULL);
+ g_list_free(rows);
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->inc_store));
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->exc_store));
+}
+
+static void policy_components_view_on_to_inc_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct polcomp_view *pv = (struct polcomp_view *)user_data;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(pv->exc_view);
+ GList *r, *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ /* use references because the filtered store will be changing
+ * as master_store's ISINC_COLUMN value changes */
+ GList *refs = NULL;
+ for (r = rows; r != NULL; r = r->next) {
+ GtkTreePath *path = (GtkTreePath *) r->data;
+ GtkTreeRowReference *ref = gtk_tree_row_reference_new(pv->exc_store, path);
+ refs = g_list_prepend(refs, ref);
+ }
+
+ for (r = refs; r != NULL; r = r->next) {
+ GtkTreeRowReference *ref = (GtkTreeRowReference *) r->data;
+ GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
+ GtkTreeIter iter, child_iter;
+ char *name;
+ gtk_tree_model_get_iter(pv->exc_store, &iter, path);
+ gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(pv->exc_store), &child_iter, &iter);
+ gtk_tree_model_get(GTK_TREE_MODEL(pv->master_store), &child_iter, NAME_COLUMN, &name, -1);
+ gtk_list_store_set(pv->master_store, &child_iter, ISINC_COLUMN, TRUE, -1);
+ apol_vector_append(pv->included_items, strdup(name));
+ }
+ apol_vector_sort_uniquify(pv->included_items, apol_str_strcmp, NULL);
+ g_list_foreach(refs, policy_components_gtk_tree_row_reference_free, NULL);
+ g_list_free(refs);
+ g_list_foreach(rows, policy_components_gtk_tree_path_free, NULL);
+ g_list_free(rows);
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->inc_store));
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(pv->exc_store));
+}
+
+static void policy_components_view_on_select_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ GtkTreeView *tv = GTK_TREE_VIEW(user_data);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(tv);
+ gtk_tree_selection_select_all(selection);
+}
+
+static void policy_components_view_on_unselect_click(GtkButton * widget __attribute__ ((unused)), gpointer user_data)
+{
+ GtkTreeView *tv = GTK_TREE_VIEW(user_data);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(tv);
+ gtk_tree_selection_unselect_all(selection);
+}
+
+static void policy_components_view_init_signals(struct polcomp_view *pv)
+{
+ g_signal_connect(pv->log_radio, "toggled", G_CALLBACK(policy_components_view_on_source_toggle), pv);
+ g_signal_connect(pv->policy_radio, "toggled", G_CALLBACK(policy_components_view_on_source_toggle), pv);
+ g_signal_connect(pv->both_radio, "toggled", G_CALLBACK(policy_components_view_on_source_toggle), pv);
+ g_signal_connect(pv->to_exc_button, "clicked", G_CALLBACK(policy_components_view_on_to_exc_click), pv);
+ g_signal_connect(pv->to_inc_button, "clicked", G_CALLBACK(policy_components_view_on_to_inc_click), pv);
+ g_signal_connect(pv->inc_select_button, "clicked", G_CALLBACK(policy_components_view_on_select_click), pv->inc_view);
+ g_signal_connect(pv->inc_unselect_button, "clicked", G_CALLBACK(policy_components_view_on_unselect_click), pv->inc_view);
+ g_signal_connect(pv->exc_select_button, "clicked", G_CALLBACK(policy_components_view_on_select_click), pv->exc_view);
+ g_signal_connect(pv->exc_unselect_button, "clicked", G_CALLBACK(policy_components_view_on_unselect_click), pv->exc_view);
+}
+
+apol_vector_t *policy_components_view_run(toplevel_t * top, GtkWindow * parent __attribute__ ((unused)), const char *title,
+ apol_vector_t * log_items, apol_vector_t * policy_items, apol_vector_t * included)
+{
+ struct polcomp_view pv;
+ gint response;
+
+ memset(&pv, 0, sizeof(pv));
+ pv.top = top;
+ pv.xml = glade_xml_new(toplevel_get_glade_xml(top), "PolicyComponentListsWindow", NULL);
+ pv.log_items = log_items;
+ pv.policy_items = policy_items;
+ if (included == NULL) {
+ pv.included_items = apol_vector_create(free);
+ } else {
+ pv.included_items = apol_vector_create_from_vector(included, apol_str_strdup, NULL, free);
+ }
+ apol_vector_destroy(&included);
+ if (pv.included_items == NULL) {
+ toplevel_ERR(pv.top, "Error creating dialog: %s", strerror(errno));
+ return NULL;
+ }
+
+ policy_components_view_init_widgets(&pv);
+ policy_components_view_init_lists(&pv);
+ policy_components_view_init_signals(&pv);
+ gtk_window_set_title(GTK_WINDOW(pv.dialog), title);
+ do {
+ response = gtk_dialog_run(pv.dialog);
+ } while (response != GTK_RESPONSE_CLOSE);
+
+ gtk_widget_destroy(GTK_WIDGET(pv.dialog));
+ g_object_unref(pv.master_store);
+ g_object_unref(pv.inc_store);
+ g_object_unref(pv.exc_store);
+ apol_vector_destroy(&pv.both_items);
+ if (apol_vector_get_size(pv.included_items) == 0) {
+ apol_vector_destroy(&pv.included_items);
+ return NULL;
+ }
+ return pv.included_items;
+}
diff --git a/seaudit/policy_components_view.h b/seaudit/policy_components_view.h
new file mode 100644
index 0000000..eece365
--- /dev/null
+++ b/seaudit/policy_components_view.h
@@ -0,0 +1,56 @@
+/**
+ * @file
+ * Dialog that shows two columns of strings, an included list and an
+ * excluded list. The user then moves items between the two lists.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLICY_COMPONENTS_VIEW_H
+#define POLICY_COMPONENTS_VIEW_H
+
+#include "toplevel.h"
+#include <apol/vector.h>
+#include <gtk/gtk.h>
+
+/**
+ * Display and run a dialog that allows the user to select items from
+ * arrays of strings.
+ *
+ * @param top Toplevel containing message view.
+ * @param parent Parent window upon which to center this dialog.
+ * @param title Title for the dialog window.
+ * @param log_items Vector of strings that the log has. The function
+ * will not modify this vector.
+ * @param policy_items Vector of strings that the policy has. If
+ * NULL, then no policy is loaded. The function will not modify this
+ * vector.
+ * @param included List of strings to be included. The strings are
+ * assumed to have been strdup()ped from some other source. This
+ * function takes ownership of the vector and its contents.
+ *
+ * @return A vector newly allocated strings corresponding to the items
+ * to be included, or NULL upon error. The caller must call
+ * apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *policy_components_view_run(toplevel_t * top, GtkWindow * parent, const char *title,
+ apol_vector_t * log_items, apol_vector_t * policy_items, apol_vector_t * included);
+
+#endif
diff --git a/seaudit/policy_view.c b/seaudit/policy_view.c
new file mode 100644
index 0000000..5fdc9c0
--- /dev/null
+++ b/seaudit/policy_view.c
@@ -0,0 +1,573 @@
+/**
+ * @file
+ * Implementation of policy viewer.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "policy_view.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/policy-query.h>
+#include <apol/util.h>
+#include <glade/glade.h>
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include <seaudit/avc_message.h>
+
+/* these are for mmaping the policy file */
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+struct policy_view
+{
+ toplevel_t *top;
+ GladeXML *xml;
+ GtkWindow *window;
+ GtkNotebook *notebook;
+ GtkToggleButton *stype_check, *ttype_check, *class_check;
+ GtkComboBoxEntry *stype_combo, *ttype_combo, *class_combo;
+ GtkToggleButton *stype_direct, *ttype_direct;
+ GtkListStore *type_model, *class_model;
+ apol_vector_t *type_list, *class_list;
+ GtkTextBuffer *rules_text, *policy_text;
+ char *policy_text_mmap;
+ size_t policy_text_len;
+};
+
+/**
+ * Display a vector of rules (either qpol_avrule_t or
+ * qpol_syn_avrule_t) in the rules text buffer.
+ */
+static void policy_view_display_avrule_results(policy_view_t * pv, apol_vector_t * results, int is_syn_rules)
+{
+ apol_policy_t *policy = toplevel_get_policy(pv->top);
+ qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ GtkTextIter start, end;
+ char *string, buf[64];
+ size_t i;
+
+ gtk_text_buffer_get_start_iter(pv->rules_text, &start);
+ gtk_text_buffer_get_end_iter(pv->rules_text, &end);
+ gtk_text_buffer_delete(pv->rules_text, &start, &end);
+
+ snprintf(buf, 64, "%zd rule(s) match the search criteria.\n\n", apol_vector_get_size(results));
+ gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, buf, -1, "summary", NULL);
+ for (i = 0; i < apol_vector_get_size(results); i++) {
+ if (!is_syn_rules) {
+ qpol_avrule_t *rule = apol_vector_get_element(results, i);
+ string = apol_avrule_render(policy, rule);
+ } else {
+ qpol_syn_avrule_t *rule = apol_vector_get_element(results, i);
+ string = apol_syn_avrule_render(policy, rule);
+ unsigned long lineno;
+ if (qpol_policy_has_capability(qp, QPOL_CAP_LINE_NUMBERS)) {
+ qpol_syn_avrule_get_lineno(qp, rule, &lineno);
+ sprintf(buf, "%ld", lineno);
+ gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, "[", -1, "rule", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, buf, -1, "line-number", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, "] ", -1, "rule", NULL);
+ }
+ }
+ if (string == NULL) {
+ toplevel_ERR(pv->top, "Error displaying rule: %s", strerror(errno));
+ return;
+ }
+ gtk_text_buffer_insert_with_tags_by_name(pv->rules_text, &end, string, -1, "rule", NULL);
+ free(string);
+ gtk_text_buffer_insert(pv->rules_text, &end, "\n", -1);
+ }
+}
+
+struct find_terules_datum
+{
+ apol_policy_t *policy;
+ apol_avrule_query_t *query;
+ apol_vector_t *results;
+ int is_syn_rules, retval;
+ progress_t *progress;
+};
+
+/**
+ * Perform the rule query within a thread.
+ */
+static gpointer policy_view_find_terules_runner(gpointer data)
+{
+ struct find_terules_datum *run = (struct find_terules_datum *)data;
+ run->results = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(run->policy);
+ if (!qpol_policy_has_capability(q, QPOL_CAP_SYN_RULES)) {
+ progress_update(run->progress, "Searching AV rules");
+ run->retval = apol_avrule_get_by_query(run->policy, run->query, &run->results);
+ run->is_syn_rules = 0;
+ } else {
+ qpol_policy_build_syn_rule_table(q);
+ progress_update(run->progress, "Searching syntactic AV rules");
+ run->retval = apol_syn_avrule_get_by_query(run->policy, run->query, &run->results);
+ run->is_syn_rules = 1;
+ }
+ if (run->retval == 0) {
+ progress_done(run->progress);
+ } else {
+ progress_abort(run->progress, NULL);
+ }
+ return NULL;
+}
+
+/**
+ * Collect the rule search criteria into an avrule_query_t object.
+ * Actually execute the query in a progress thread.
+ */
+static void policy_view_on_find_terules_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ policy_view_t *pv = (policy_view_t *) user_data;
+ apol_policy_t *policy = toplevel_get_policy(pv->top);
+ apol_avrule_query_t *query = apol_avrule_query_create();
+ apol_avrule_query_set_regex(policy, query, 1);
+ struct find_terules_datum run;
+ const char *s;
+ gboolean only_direct;
+ apol_avrule_query_set_rules(policy, query, QPOL_RULE_ALLOW);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->stype_check))) {
+ s = util_combo_box_get_active_text(GTK_COMBO_BOX(pv->stype_combo));
+ only_direct = gtk_toggle_button_get_active(pv->stype_direct);
+ if (strcmp(s, "") == 0) {
+ toplevel_ERR(pv->top, "No source type was selected.");
+ return;
+ }
+ apol_avrule_query_set_source(policy, query, s, only_direct == FALSE);
+ apol_avrule_query_set_source_component(policy, query, APOL_QUERY_SYMBOL_IS_TYPE);
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->ttype_check))) {
+ s = util_combo_box_get_active_text(GTK_COMBO_BOX(pv->ttype_combo));
+ only_direct = gtk_toggle_button_get_active(pv->ttype_direct);
+ if (strcmp(s, "") == 0) {
+ toplevel_ERR(pv->top, "No target type was selected.");
+ return;
+ }
+ apol_avrule_query_set_target(policy, query, s, only_direct == FALSE);
+ apol_avrule_query_set_source_component(policy, query, APOL_QUERY_SYMBOL_IS_TYPE);
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pv->class_check))) {
+ s = util_combo_box_get_active_text(GTK_COMBO_BOX(pv->class_combo));
+ if (strcmp(s, "") == 0) {
+ toplevel_ERR(pv->top, "No object class was selected.");
+ return;
+ }
+ apol_avrule_query_append_class(policy, query, s);
+ }
+
+ util_cursor_wait(GTK_WIDGET(pv->window));
+ run.policy = policy;
+ run.query = query;
+ run.progress = toplevel_get_progress(pv->top);
+ progress_show(run.progress, "Finding TE Rules");
+ g_thread_create(policy_view_find_terules_runner, &run, FALSE, NULL);
+ progress_wait(run.progress);
+ progress_hide(run.progress);
+ util_cursor_clear(GTK_WIDGET(pv->window));
+ apol_avrule_query_destroy(&query);
+ if (run.retval == 0) {
+ policy_view_display_avrule_results(pv, run.results, run.is_syn_rules);
+ }
+ apol_vector_destroy(&run.results);
+}
+
+static void policy_view_close(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ policy_view_t *pv = (policy_view_t *) user_data;
+ gtk_widget_hide(GTK_WIDGET(pv->window));
+}
+
+static gboolean policy_view_on_delete_event(GtkWidget * widget, GdkEvent * event __attribute__ ((unused)), gpointer user_data
+ __attribute__ ((unused)))
+{
+ gtk_widget_hide(widget);
+ return TRUE;
+}
+
+static void policy_view_on_stype_toggle(GtkToggleButton * toggle, gpointer user_data)
+{
+ gboolean sens = gtk_toggle_button_get_active(toggle);
+ policy_view_t *pv = (policy_view_t *) user_data;
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->stype_combo), sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->stype_direct), sens);
+}
+
+static void policy_view_on_ttype_toggle(GtkToggleButton * toggle, gpointer user_data)
+{
+ gboolean sens = gtk_toggle_button_get_active(toggle);
+ policy_view_t *pv = (policy_view_t *) user_data;
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->ttype_combo), sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->ttype_direct), sens);
+}
+
+static void policy_view_on_class_toggle(GtkToggleButton * toggle, gpointer user_data)
+{
+ gboolean sens = gtk_toggle_button_get_active(toggle);
+ policy_view_t *pv = (policy_view_t *) user_data;
+ gtk_widget_set_sensitive(GTK_WIDGET(pv->class_combo), sens);
+
+}
+
+static gboolean policy_view_on_line_event(GtkTextTag * tag
+ __attribute__ ((unused)), GObject * event_object
+ __attribute__ ((unused)), GdkEvent * event, const GtkTextIter * iter, gpointer user_data)
+{
+ policy_view_t *pv = (policy_view_t *) user_data;
+ GtkTextIter start, end;
+ int line;
+ GtkTextView *view;
+ if (event->type != GDK_BUTTON_PRESS) {
+ return FALSE;
+ }
+ start = *iter;
+ while (!gtk_text_iter_starts_word(&start))
+ gtk_text_iter_backward_char(&start);
+ end = start;
+ while (!gtk_text_iter_ends_word(&end))
+ gtk_text_iter_forward_char(&end);
+ /* subtract 1 because text buffers are indexed from 0 */
+ line = atoi(gtk_text_iter_get_slice(&start, &end)) - 1;
+ view = GTK_TEXT_VIEW(glade_xml_get_widget(pv->xml, "PolicyWindowPolicyText"));
+ assert(view != NULL);
+ gtk_notebook_set_current_page(pv->notebook, 1);
+ gtk_text_buffer_get_start_iter(pv->policy_text, &start);
+ gtk_text_iter_set_line(&start, line);
+ gtk_text_view_scroll_to_iter(view, &start, 0.0001, TRUE, 0.0, 0.5);
+ gtk_text_buffer_place_cursor(pv->policy_text, &start);
+ gtk_text_view_set_cursor_visible(view, TRUE);
+ return TRUE;
+}
+
+static gboolean policy_view_on_rules_motion(GtkWidget * widget, GdkEventMotion * event, gpointer user_data __attribute__ ((unused)))
+{
+ GtkTextView *textview = GTK_TEXT_VIEW(widget);
+ gint x, ex, y, ey;
+ GtkTextIter iter;
+ GSList *tags, *tagp;
+ int hovering = 0;
+ if (event->is_hint) {
+ gdk_window_get_pointer(event->window, &ex, &ey, NULL);
+ } else {
+ ex = event->x;
+ ey = event->y;
+ }
+ gtk_text_view_window_to_buffer_coords(textview, GTK_TEXT_WINDOW_WIDGET, ex, ey, &x, &y);
+ gtk_text_view_get_iter_at_location(textview, &iter, x, y);
+ tags = gtk_text_iter_get_tags(&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ if (strcmp(GTK_TEXT_TAG(tagp->data)->name, "line-number") == 0) {
+ hovering = TRUE;
+ break;
+ }
+ }
+ if (hovering) {
+ GdkCursor *cursor = gdk_cursor_new(GDK_HAND2);
+ gdk_window_set_cursor(event->window, cursor);
+ gdk_cursor_unref(cursor);
+ gdk_flush();
+ } else {
+ gdk_window_set_cursor(event->window, NULL);
+ }
+ g_slist_free(tags);
+ return FALSE;
+}
+
+/**
+ * Create a text buffer to hold the results of running the TE rules
+ * search. Initialize its tags and add event handlers to the
+ * "line-number" tag, such that clicking on the tag jumps to the
+ * policy's line and hovering over the tag changes the cursor.
+ */
+static void policy_view_create_rules_buffer(policy_view_t * pv)
+{
+ GtkTextView *rules_textview;
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+ rules_textview = GTK_TEXT_VIEW(glade_xml_get_widget(pv->xml, "PolicyWindowTERulesResults"));
+ assert(rules_textview != NULL);
+ pv->rules_text = gtk_text_buffer_new(NULL);
+ gtk_text_view_set_buffer(rules_textview, pv->rules_text);
+ table = gtk_text_buffer_get_tag_table(pv->rules_text);
+ gtk_text_buffer_create_tag(pv->rules_text, "summary", "family", "monospace", "weight", "bold", NULL);
+ tag = gtk_text_buffer_create_tag(pv->rules_text, "line-number",
+ "family", "monospace", "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
+ g_signal_connect(G_OBJECT(tag), "event", G_CALLBACK(policy_view_on_line_event), pv);
+ g_signal_connect(rules_textview, "motion-notify-event", G_CALLBACK(policy_view_on_rules_motion), NULL);
+ gtk_text_buffer_create_tag(pv->rules_text, "rule", "family", "monospace", NULL);
+}
+
+policy_view_t *policy_view_create(toplevel_t * top)
+{
+ GtkWidget *w;
+ GtkTextView *policy_textview;
+ policy_view_t *pv;
+ if ((pv = calloc(1, sizeof(*pv))) == NULL) {
+ return NULL;
+ }
+ pv->top = top;
+ pv->xml = glade_xml_new(toplevel_get_glade_xml(top), "PolicyWindow", NULL);
+ pv->window = GTK_WINDOW(glade_xml_get_widget(pv->xml, "PolicyWindow"));
+ assert(pv->window != NULL);
+ gtk_window_set_transient_for(pv->window, toplevel_get_window(top));
+ pv->notebook = GTK_NOTEBOOK(glade_xml_get_widget(pv->xml, "PolicyWindowNotebook"));
+ assert(pv->notebook != NULL);
+
+ pv->stype_check = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowSTypeCheck"));
+ pv->ttype_check = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowTTypeCheck"));
+ pv->class_check = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowClassCheck"));
+ assert(pv->stype_check != NULL && pv->ttype_check != NULL && pv->class_check != NULL);
+ g_signal_connect(pv->stype_check, "toggled", G_CALLBACK(policy_view_on_stype_toggle), pv);
+ g_signal_connect(pv->ttype_check, "toggled", G_CALLBACK(policy_view_on_ttype_toggle), pv);
+ g_signal_connect(pv->class_check, "toggled", G_CALLBACK(policy_view_on_class_toggle), pv);
+
+ pv->stype_combo = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(pv->xml, "PolicyWindowSTypeCombo"));
+ pv->ttype_combo = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(pv->xml, "PolicyWindowTTypeCombo"));
+ pv->class_combo = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(pv->xml, "PolicyWindowClassCombo"));
+ assert(pv->stype_combo != NULL && pv->ttype_combo != NULL && pv->class_combo != NULL);
+ pv->type_model = gtk_list_store_new(1, G_TYPE_STRING);
+ pv->class_model = gtk_list_store_new(1, G_TYPE_STRING);
+ gtk_combo_box_set_model(GTK_COMBO_BOX(pv->stype_combo), GTK_TREE_MODEL(pv->type_model));
+ gtk_combo_box_set_model(GTK_COMBO_BOX(pv->ttype_combo), GTK_TREE_MODEL(pv->type_model));
+ gtk_combo_box_set_model(GTK_COMBO_BOX(pv->class_combo), GTK_TREE_MODEL(pv->class_model));
+ gtk_combo_box_entry_set_text_column(pv->stype_combo, 0);
+ gtk_combo_box_entry_set_text_column(pv->ttype_combo, 0);
+ gtk_combo_box_entry_set_text_column(pv->class_combo, 0);
+
+ pv->stype_direct = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowSTypeDirectCheck"));
+ pv->ttype_direct = GTK_TOGGLE_BUTTON(glade_xml_get_widget(pv->xml, "PolicyWindowTTypeDirectCheck"));
+ assert(pv->stype_direct != NULL && pv->ttype_direct != NULL);
+
+ policy_view_create_rules_buffer(pv);
+
+ policy_textview = GTK_TEXT_VIEW(glade_xml_get_widget(pv->xml, "PolicyWindowPolicyText"));
+ assert(policy_textview != NULL);
+ pv->policy_text = gtk_text_buffer_new(NULL);
+ gtk_text_view_set_buffer(policy_textview, pv->policy_text);
+
+ /* set up signal handlers for the widgets */
+
+ w = glade_xml_get_widget(pv->xml, "PolicyWindowFindTERulesButton");
+ assert(w != NULL);
+ g_signal_connect(w, "clicked", G_CALLBACK(policy_view_on_find_terules_click), pv);
+
+ w = glade_xml_get_widget(pv->xml, "PolicyWindowCloseButton");
+ assert(w != NULL);
+ g_signal_connect(w, "clicked", G_CALLBACK(policy_view_close), pv);
+ g_signal_connect(pv->window, "delete_event", G_CALLBACK(policy_view_on_delete_event), NULL);
+
+ policy_view_update(pv, NULL);
+ return pv;
+}
+
+void policy_view_destroy(policy_view_t ** pv)
+{
+ if (pv != NULL && *pv != NULL) {
+ apol_vector_destroy(&(*pv)->type_list);
+ apol_vector_destroy(&(*pv)->class_list);
+ free(*pv);
+ *pv = NULL;
+ }
+}
+
+/**
+ * If the currently loaded policy is a source policy, then load its
+ * contents into the policy text buffer. Otherwise let the user know
+ * that the policy is binary.
+ *
+ * @param pv Policy view's policy source tab to update.
+ * @param path Path to the currently loaded policy, or NULL if no
+ * policy is loaded.
+ */
+static void policy_view_load_policy_source(policy_view_t * pv, apol_policy_path_t * path)
+{
+ apol_policy_t *policy = toplevel_get_policy(pv->top);
+ const char *primary_path;
+ if (path == NULL) {
+ gtk_text_buffer_set_text(pv->policy_text, "No policy has been loaded.", -1);
+ return;
+ }
+ primary_path = apol_policy_path_get_primary(path);
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SOURCE)) {
+ GString *string = g_string_new("");
+ g_string_printf(string, "Policy file %s is not a source policy.", primary_path);
+ gtk_text_buffer_set_text(pv->policy_text, string->str, -1);
+ g_string_free(string, TRUE);
+ } else {
+ /* load the policy by mmap()ing the file */
+ struct stat statbuf;
+ int fd;
+ if (pv->policy_text_mmap != NULL) {
+ munmap(pv->policy_text_mmap, pv->policy_text_len);
+ }
+
+ pv->policy_text_mmap = NULL;
+ pv->policy_text_len = 0;
+
+ if ((fd = open(primary_path, O_RDONLY)) < 0) {
+ toplevel_ERR(pv->top, "Could not open %s for reading.", primary_path);
+ return;
+ }
+ if (fstat(fd, &statbuf) < 0) {
+ close(fd);
+ toplevel_ERR(pv->top, "Could not stat %s.", primary_path);
+ return;
+ }
+
+ pv->policy_text_len = statbuf.st_size;
+ if ((pv->policy_text_mmap = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ pv->policy_text_mmap = NULL;
+ toplevel_ERR(pv->top, "Could not mmap %s.", primary_path);
+ return;
+ }
+ close(fd);
+ gtk_text_buffer_set_text(pv->policy_text, pv->policy_text_mmap, pv->policy_text_len);
+ }
+}
+
+/**
+ * If a policy is currently loaded then set the rule search combo box
+ * menus to that policy's components. Otherwies clear the combo box
+ * menus.
+ *
+ * @param pv Policy view's rule search boxes to modify.
+ */
+static void policy_view_populate_combo_boxes(policy_view_t * pv)
+{
+ apol_policy_t *policy = toplevel_get_policy(pv->top);
+ gtk_list_store_clear(pv->type_model);
+ gtk_list_store_clear(pv->class_model);
+ apol_vector_destroy(&pv->type_list);
+ apol_vector_destroy(&pv->class_list);
+ pv->type_list = apol_vector_create(NULL);
+ pv->class_list = apol_vector_create(NULL);
+ if (policy != NULL) {
+ qpol_policy_t *qp = apol_policy_get_qpol(policy);
+ size_t i;
+ const qpol_type_t *type;
+ const qpol_class_t *obj_class;
+ const char *s;
+ GtkTreeIter iter;
+ apol_vector_t *v;
+ apol_type_get_by_query(policy, NULL, &v);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ type = apol_vector_get_element(v, i);
+ qpol_type_get_name(qp, type, &s);
+ apol_vector_append(pv->type_list, (void *)s);
+ }
+ apol_vector_destroy(&v);
+ apol_vector_sort(pv->type_list, apol_str_strcmp, NULL);
+ for (i = 0; i < apol_vector_get_size(pv->type_list); i++) {
+ s = apol_vector_get_element(pv->type_list, i);
+#ifdef GTK_2_8
+ gtk_list_store_insert_with_values(pv->type_model, &iter, i, 0, s, -1);
+#else
+ gtk_list_store_insert(pv->type_model, &iter, i);
+ gtk_list_store_set(pv->type_model, &iter, 0, s, -1);
+#endif
+ }
+ apol_class_get_by_query(policy, NULL, &v);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ obj_class = apol_vector_get_element(v, i);
+ qpol_class_get_name(qp, obj_class, &s);
+ apol_vector_append(pv->class_list, (void *)s);
+ }
+ apol_vector_destroy(&v);
+ apol_vector_sort(pv->class_list, apol_str_strcmp, NULL);
+ for (i = 0; i < apol_vector_get_size(pv->class_list); i++) {
+ s = apol_vector_get_element(pv->class_list, i);
+#ifdef GTK_2_8
+ gtk_list_store_insert_with_values(pv->class_model, &iter, i, 0, s, -1);
+#else
+ gtk_list_store_insert(pv->class_model, &iter, i);
+ gtk_list_store_set(pv->class_model, &iter, 0, s, -1);
+#endif
+ }
+ }
+}
+
+void policy_view_update(policy_view_t * pv, apol_policy_path_t * path)
+{
+ GtkTextIter start, end;
+ policy_view_load_policy_source(pv, path);
+ policy_view_populate_combo_boxes(pv);
+ gtk_text_buffer_get_start_iter(pv->rules_text, &start);
+ gtk_text_buffer_get_end_iter(pv->rules_text, &end);
+ gtk_text_buffer_delete(pv->rules_text, &start, &end);
+
+}
+
+void policy_view_find_terules(policy_view_t * pv, seaudit_message_t * message)
+{
+ seaudit_message_type_e type = SEAUDIT_MESSAGE_TYPE_INVALID;
+ void *data = NULL;
+ const char *stype = "", *ttype = "", *obj_class = "";
+ size_t i;
+ assert(pv->type_list != NULL);
+ assert(pv->class_list != NULL);
+ if (message != NULL) {
+ data = seaudit_message_get_data(message, &type);
+ }
+ if (type == SEAUDIT_MESSAGE_TYPE_AVC) {
+ seaudit_avc_message_t *avc = data;
+ if ((stype = seaudit_avc_message_get_source_type(avc)) == NULL) {
+ stype = "";
+ }
+ if ((ttype = seaudit_avc_message_get_target_type(avc)) == NULL) {
+ ttype = "";
+ }
+ if ((obj_class = seaudit_avc_message_get_object_class(avc)) == NULL) {
+ obj_class = "";
+ }
+ }
+ if (strcmp(stype, "") == 0 || apol_vector_get_index(pv->type_list, stype, apol_str_strcmp, NULL, &i) < 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(pv->stype_combo), -1);
+ gtk_toggle_button_set_active(pv->stype_check, FALSE);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(pv->stype_combo), i);
+ gtk_toggle_button_set_active(pv->stype_check, TRUE);
+ }
+ if (strcmp(ttype, "") == 0 || apol_vector_get_index(pv->type_list, ttype, apol_str_strcmp, NULL, &i) < 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(pv->ttype_combo), -1);
+ gtk_toggle_button_set_active(pv->ttype_check, FALSE);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(pv->ttype_combo), i);
+ gtk_toggle_button_set_active(pv->ttype_check, TRUE);
+ }
+ if (strcmp(obj_class, "") == 0 || apol_vector_get_index(pv->class_list, obj_class, apol_str_strcmp, NULL, &i) < 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(pv->class_combo), -1);
+ gtk_toggle_button_set_active(pv->class_check, FALSE);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(pv->class_combo), i);
+ gtk_toggle_button_set_active(pv->class_check, TRUE);
+ }
+ gtk_notebook_set_current_page(pv->notebook, 0);
+ gtk_widget_show(GTK_WIDGET(pv->window));
+}
diff --git a/seaudit/policy_view.h b/seaudit/policy_view.h
new file mode 100644
index 0000000..17b23ed
--- /dev/null
+++ b/seaudit/policy_view.h
@@ -0,0 +1,77 @@
+/**
+ * @file
+ * Declaration of viewer for the currently loaded policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef POLICY_VIEW_H
+#define POLICY_VIEW_H
+
+#include "toplevel.h"
+
+#include <seaudit/message.h>
+
+typedef struct policy_view policy_view_t;
+
+/**
+ * Create a new policy view object. This is used to display the
+ * policy's content and to search for TE rules.
+ *
+ * @param top Toplevel object that will control the newly opened
+ * policy view.
+ *
+ * @return An initialized policy view object, or NULL upon error. The
+ * caller must call policy_view_destroy() upon the returned value.
+ */
+policy_view_t *policy_view_create(toplevel_t * top);
+
+/**
+ * Destroy the policy view object. This does nothing if the pointer
+ * is set to NULL.
+ *
+ * @param pv Reference to a policy view object. Afterwards the
+ * pointer will be set to NULL.
+ */
+void policy_view_destroy(policy_view_t ** pv);
+
+/**
+ * (Re)synchronize the policy displayed in the viewer with the one
+ * actually loaded. If there is no policy loaded then clear the
+ * viewer's contents.
+ *
+ * @param pv Policy view to update.
+ * @param path Path to the policy, or NULL if no policy is loaded.
+ */
+void policy_view_update(policy_view_t * pv, apol_policy_path_t * path);
+
+/**
+ * (Re)open the policy view window to allow the user to search for TE
+ * rules in the currently opened policy. If message is not NULL then
+ * set the query's initial parameters to the message's source type,
+ * target type, and object class.
+ *
+ * @param pv Policy view object. Note that a policy must already
+ * exist and policy_view_update() must be first called.
+ * @param message If non-NULL, the initial parameters for query.
+ */
+void policy_view_find_terules(policy_view_t * pv, seaudit_message_t * message);
+
+#endif
diff --git a/seaudit/preferences.c b/seaudit/preferences.c
new file mode 100644
index 0000000..5e65385
--- /dev/null
+++ b/seaudit/preferences.c
@@ -0,0 +1,585 @@
+/**
+ * @file
+ * Implementation of the storage class preferences_t.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "preferences.h"
+
+#include <apol/util.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** default frequency, in milliseconds, to poll log file for changes */
+#define DEFAULT_LOG_UPDATE_INTERVAL 1000
+
+/** maximum number of recent log files and recent policy files to remember */
+#define MAX_RECENT_ENTRIES 5
+
+/** name of the user's seaudit personal preferences file */
+#define USER_SEAUDIT_CONF ".seaudit"
+
+/** name of the system seaudit preference file */
+#define SYSTEM_SEAUDIT_CONF "dot_seaudit"
+
+struct visible_field
+{
+ preference_field_e id;
+ const char *field;
+ int visible;
+};
+
+static const struct visible_field default_visible_fields[] = {
+ {HOST_FIELD, "host_field", 1},
+ {MESSAGE_FIELD, "msg_field", 1},
+ {DATE_FIELD, "date_field", 1},
+ {SUSER_FIELD, "src_usr_field", 0},
+ {SROLE_FIELD, "src_role_field", 0},
+ {STYPE_FIELD, "src_type_field", 1},
+ {SMLS_LVL_FIELD, "src_mls_lvl_field", 0},
+ {SMLS_CLR_FIELD, "src_mls_clr_field", 0},
+ {TUSER_FIELD, "tgt_usr_field", 0},
+ {TROLE_FIELD, "tgt_role_field", 0},
+ {TTYPE_FIELD, "tgt_type_field", 1},
+ {TMLS_LVL_FIELD, "tgt_mls_lvl_field", 0},
+ {TMLS_CLR_FIELD, "tgt_mls_clr_field", 0},
+ {OBJCLASS_FIELD, "obj_class_field", 1},
+ {PERM_FIELD, "perm_field", 1},
+ {EXECUTABLE_FIELD, "exe_field", 1},
+ {COMMAND_FIELD, "comm_field", 1},
+ {NAME_FIELD, "name_field", 0},
+ {PID_FIELD, "pid_field", 0},
+ {INODE_FIELD, "inode_field", 0},
+ {PATH_FIELD, "path_field", 0},
+ {OTHER_FIELD, "other_field", 1}
+};
+
+static const size_t num_visible_fields = sizeof(default_visible_fields) / sizeof(default_visible_fields[0]);
+
+struct preferences
+{
+ /** path to default system log file */
+ char *log;
+ /** path to default policy */
+ apol_policy_path_t *policy;
+ /** default path when writing reports */
+ char *report;
+ /** default path to the stylesheet, used during report writing */
+ char *stylesheet;
+ /** vector of paths (strings) to recently opened log files */
+ apol_vector_t *recent_log_files;
+ /** vector of apol_policy_path_t objects to recently opened
+ policies */
+ apol_vector_t *recent_policy_files;
+ /** non-zero if seaudit should poll the log file for changes */
+ int real_time_log;
+ /** frequency, in milliesconds, to poll log file */
+ int real_time_interval;
+ struct visible_field *fields;
+};
+
+void preferences_apol_policy_path_free(void *elem)
+{
+ apol_policy_path_t *path = elem;
+ apol_policy_path_destroy(&path);
+}
+
+/**
+ * Parse the old-style recent policies list (a ':' separated list of
+ * paths) into the recent_policy_files field.
+ */
+static int preferences_parse_old_recent_files(preferences_t * prefs, const char *s)
+{
+ apol_vector_t *v = NULL;
+ size_t i;
+ char *base;
+ apol_policy_path_t *path;
+ int error = 0;
+
+ if ((v = apol_str_split(s, ":")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ base = apol_vector_get_element(v, i);
+ if ((path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, base, NULL)) == NULL ||
+ apol_vector_append(prefs->recent_policy_files, path) < 0) {
+ error = errno;
+ apol_policy_path_destroy(&path);
+ goto cleanup;
+ }
+ }
+
+ cleanup:
+ apol_vector_destroy(&v);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Parse the new recent policy files, which now spans across multiple
+ * lines.
+ */
+static int preferences_parse_new_recent_files(preferences_t * prefs, FILE * f, int num_files)
+{
+ int count;
+ for (count = 0; count < num_files; count++) {
+ char *var_name, *value = NULL;
+ apol_policy_path_t *p = NULL;
+ if (asprintf(&var_name, "RECENT_POLICY_PATH_%d", count) < 0) {
+ return -1;
+ }
+ value = apol_config_get_var(var_name, f);
+ free(var_name);
+ if (value == NULL ||
+ (p = apol_policy_path_create_from_string(value)) == NULL ||
+ apol_vector_append(prefs->recent_policy_files, p) < 0) {
+ free(value);
+ apol_policy_path_destroy(&p);
+ return -1;
+ }
+ free(value);
+ }
+ return 0;
+}
+
+preferences_t *preferences_create(void)
+{
+ preferences_t *prefs = NULL;
+ FILE *file = NULL;
+ char *path = NULL, *value;
+ apol_vector_t *v = NULL;
+ size_t i, j;
+ int error = 0;
+
+ if ((prefs = calloc(1, sizeof(*prefs))) == NULL ||
+ (prefs->log = strdup("")) == NULL ||
+ (prefs->report = strdup("")) == NULL ||
+ (prefs->stylesheet = strdup("")) == NULL ||
+ (prefs->recent_log_files = apol_vector_create(free)) == NULL ||
+ (prefs->recent_policy_files = apol_vector_create(preferences_apol_policy_path_free)) == NULL ||
+ (prefs->fields = calloc(num_visible_fields, sizeof(struct visible_field))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ prefs->real_time_interval = DEFAULT_LOG_UPDATE_INTERVAL;
+ memcpy(prefs->fields, default_visible_fields, num_visible_fields * sizeof(struct visible_field));
+ path = apol_file_find_user_config(USER_SEAUDIT_CONF);
+ if (!path) {
+ if ((path = apol_file_find_path(SYSTEM_SEAUDIT_CONF)) == NULL) {
+ return prefs;
+ }
+ }
+ if ((file = fopen(path, "r")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ if ((value = apol_config_get_var("DEFAULT_LOG_FILE", file)) != NULL) {
+ free(prefs->log);
+ prefs->log = value;
+ }
+ if ((value = apol_config_get_var("DEFAULT_POLICY_FILE", file)) != NULL) {
+ apol_policy_path_destroy(&prefs->policy);
+ if (apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, value, NULL) == NULL) {
+ error = errno;
+ free(value);
+ goto cleanup;
+ }
+ free(value);
+ }
+ if ((value = apol_config_get_var("DEFAULT_POLICY_PATH", file)) != NULL) {
+ apol_policy_path_destroy(&prefs->policy);
+ if ((prefs->policy = apol_policy_path_create_from_string(value)) == NULL) {
+ error = errno;
+ free(value);
+ goto cleanup;
+ }
+ free(value);
+ }
+ if ((value = apol_config_get_var("DEFAULT_REPORT_CONFIG_FILE", file)) != NULL) {
+ free(prefs->report);
+ prefs->report = value;
+ }
+ if ((value = apol_config_get_var("DEFAULT_REPORT_CSS_FILE", file)) != NULL) {
+ free(prefs->stylesheet);
+ prefs->stylesheet = value;
+ }
+ if ((value = apol_config_get_var("RECENT_LOG_FILES", file)) == NULL || (v = apol_str_split(value, ":")) == NULL) {
+ error = errno;
+ free(value);
+ goto cleanup;
+ }
+ free(value);
+ apol_vector_destroy(&prefs->recent_log_files);
+ prefs->recent_log_files = v;
+
+ /* test if there exists the new recent list that contains
+ * module filenames */
+ if ((value = apol_config_get_var("RECENT_POLICY_PATH_FILES", file)) != NULL) {
+ if (preferences_parse_new_recent_files(prefs, file, atoi(value)) < 0) {
+ error = errno;
+ free(value);
+ goto cleanup;
+ }
+ } else {
+ /* use older style that could only handle monolithic policies */
+ if ((value = apol_config_get_var("RECENT_POLICY_FILES", file)) == NULL
+ || preferences_parse_old_recent_files(prefs, value) < 0) {
+ error = errno;
+ free(value);
+ goto cleanup;
+ }
+ }
+ free(value);
+
+ if ((value = apol_config_get_var("LOG_COLUMNS_HIDDEN", file)) == NULL || (v = apol_str_split(value, ":")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (j = 0; j < num_visible_fields; j++) {
+ prefs->fields[j].visible = 1;
+ }
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ char *s = apol_vector_get_element(v, i);
+ for (j = 0; j < num_visible_fields; j++) {
+ if (strcmp(s, prefs->fields[j].field) == 0) {
+ prefs->fields[j].visible = 0;
+ break;
+ }
+ }
+ }
+ free(value);
+ apol_vector_destroy(&v);
+ value = apol_config_get_var("REAL_TIME_LOG_MONITORING", file);
+ if (value != NULL && value[0] != '0') {
+ prefs->real_time_log = 1;
+ }
+ free(value);
+ value = apol_config_get_var("REAL_TIME_LOG_UPDATE_INTERVAL", file);
+ if (value != NULL) {
+ prefs->real_time_interval = atoi(value);
+ }
+ free(value);
+ cleanup:
+ free(path);
+ if (file != NULL) {
+ fclose(file);
+ }
+ if (error != 0) {
+ preferences_destroy(&prefs);
+ errno = error;
+ return NULL;
+ }
+ return prefs;
+}
+
+void preferences_destroy(preferences_t ** prefs)
+{
+ if (prefs != NULL && *prefs != NULL) {
+ free((*prefs)->log);
+ apol_policy_path_destroy(&(*prefs)->policy);
+ free((*prefs)->report);
+ free((*prefs)->stylesheet);
+ apol_vector_destroy(&(*prefs)->recent_log_files);
+ apol_vector_destroy(&(*prefs)->recent_policy_files);
+ free((*prefs)->fields);
+ free(*prefs);
+ *prefs = NULL;
+ }
+}
+
+int preferences_write_to_conf_file(preferences_t * prefs)
+{
+ FILE *file = NULL;
+ char *home, *conf_file = NULL, *value;
+ apol_vector_t *hidden_fields = NULL;
+ size_t i;
+ int retval = 0, error = 0;
+
+ /* we need to open ~/.seaudit */
+ home = getenv("HOME");
+ if (!home) {
+ error = EBADRQC;
+ goto cleanup;
+ }
+ if (asprintf(&conf_file, "%s/%s", home, USER_SEAUDIT_CONF) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+
+ if ((file = fopen(conf_file, "w")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+
+ fprintf(file, "# configuration file for seaudit - an audit log tool for Security Enhanced Linux.\n");
+ fprintf(file, "# this file is auto-generated\n\n");
+
+ if (strcmp(prefs->log, "") != 0) {
+ fprintf(file, "DEFAULT_LOG_FILE %s\n", prefs->log);
+ }
+ if (prefs->policy != NULL) {
+ value = apol_policy_path_to_string(prefs->policy);
+ if (value == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ fprintf(file, "DEFAULT_POLICY_PATH %s\n", value);
+ free(value);
+ }
+ if (strcmp(prefs->report, "") != 0) {
+ fprintf(file, "DEFAULT_REPORT_CONFIG_FILE %s\n", prefs->report);
+ }
+ if (strcmp(prefs->stylesheet, "") != 0) {
+ fprintf(file, "DEFAULT_REPORT_CSS_FILE %s\n", prefs->stylesheet);
+ }
+ if ((value = apol_str_join(prefs->recent_log_files, ":")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ fprintf(file, "RECENT_LOG_FILES %s\n", value);
+ free(value);
+
+ fprintf(file, "RECENT_POLICY_PATH_FILES %zd\n", apol_vector_get_size(prefs->recent_policy_files));
+ for (i = 0; i < apol_vector_get_size(prefs->recent_policy_files); i++) {
+ apol_policy_path_t *p = apol_vector_get_element(prefs->recent_policy_files, i);
+ if ((value = apol_policy_path_to_string(p)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ fprintf(file, "RECENT_POLICY_PATH_%zd %s\n", i, value);
+ free(value);
+ }
+
+ if ((hidden_fields = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = 0; i < num_visible_fields; i++) {
+ if (!prefs->fields[i].visible && apol_vector_append(hidden_fields, (char *)prefs->fields[i].field) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ }
+ if ((value = apol_str_join(hidden_fields, ":")) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ fprintf(file, "LOG_COLUMNS_HIDDEN %s\n", value);
+ free(value);
+ fprintf(file, "REAL_TIME_LOG_MONITORING %d\n", prefs->real_time_log);
+ fprintf(file, "REAL_TIME_LOG_UPDATE_INTERVAL %d\n", prefs->real_time_interval);
+ retval = 0;
+ cleanup:
+ free(conf_file);
+ apol_vector_destroy(&hidden_fields);
+ if (file != NULL) {
+ fclose(file);
+ }
+ errno = error;
+ return retval;
+}
+
+int preferences_is_column_visible(preferences_t * prefs, preference_field_e id)
+{
+ size_t i;
+ for (i = 0; i < num_visible_fields; i++) {
+ if (prefs->fields[i].id == id) {
+ return prefs->fields[i].visible;
+ }
+ }
+ assert(0);
+ return -1;
+}
+
+void preferences_set_column_visible(preferences_t * prefs, preference_field_e id, int visible)
+{
+ size_t i;
+ for (i = 0; i < num_visible_fields; i++) {
+ if (prefs->fields[i].id == id) {
+ prefs->fields[i].visible = visible;
+ return;
+ }
+ }
+ assert(0);
+}
+
+int preferences_set_log(preferences_t * prefs, const char *log)
+{
+ char *s;
+ if ((s = strdup(log)) == NULL) {
+ return -1;
+ }
+ free(prefs->log);
+ prefs->log = s;
+ return 0;
+}
+
+const char *preferences_get_log(preferences_t * prefs)
+{
+ return prefs->log;
+}
+
+int preferences_set_policy(preferences_t * prefs, const apol_policy_path_t * policy)
+{
+ apol_policy_path_t *new_policy;
+ if ((new_policy = apol_policy_path_create_from_policy_path(policy)) == NULL) {
+ return -1;
+ }
+ apol_policy_path_destroy(&prefs->policy);
+ prefs->policy = new_policy;
+ return 0;
+}
+
+const apol_policy_path_t *preferences_get_policy(preferences_t * prefs)
+{
+ return prefs->policy;
+}
+
+int preferences_set_report(preferences_t * prefs, const char *report)
+{
+ char *s;
+ if ((s = strdup(report)) == NULL) {
+ return -1;
+ }
+ free(prefs->report);
+ prefs->report = s;
+ return 0;
+}
+
+const char *preferences_get_report(preferences_t * prefs)
+{
+ return prefs->report;
+}
+
+int preferences_set_stylesheet(preferences_t * prefs, const char *stylesheet)
+{
+ char *s;
+ if ((s = strdup(stylesheet)) == NULL) {
+ return -1;
+ }
+ free(prefs->stylesheet);
+ prefs->stylesheet = s;
+ return 0;
+}
+
+const char *preferences_get_stylesheet(preferences_t * prefs)
+{
+ return prefs->stylesheet;
+}
+
+void preferences_set_real_time_at_startup(preferences_t * prefs, int startup)
+{
+ prefs->real_time_log = startup;
+}
+
+int preferences_get_real_time_at_startup(preferences_t * prefs)
+{
+ return prefs->real_time_log;
+}
+
+void preferences_set_real_time_interval(preferences_t * prefs, int interval)
+{
+ if (interval <= 0) {
+ prefs->real_time_interval = 0;
+ } else {
+ prefs->real_time_interval = interval;
+ }
+}
+
+int preferences_get_real_time_interval(preferences_t * prefs)
+{
+ return prefs->real_time_interval;
+}
+
+/**
+ * Add an entry to a vector, discarding the oldest entry if the vector
+ * size is too large.
+ */
+static int prefs_add_recent_vector(apol_vector_t * v, const char *entry)
+{
+ size_t i;
+ char *s;
+ if (apol_vector_get_index(v, (void *)entry, apol_str_strcmp, NULL, &i) == 0) {
+ return 0;
+ }
+ if ((s = strdup(entry)) == NULL || apol_vector_append(v, s) < 0) {
+ int error = errno;
+ free(s);
+ errno = error;
+ return -1;
+ }
+ if (apol_vector_get_size(v) >= MAX_RECENT_ENTRIES) {
+ s = apol_vector_get_element(v, 0);
+ free(s);
+ return apol_vector_remove(v, 0);
+ }
+ return 0;
+}
+
+int preferences_add_recent_log(preferences_t * prefs, const char *log)
+{
+ return prefs_add_recent_vector(prefs->recent_log_files, log);
+}
+
+apol_vector_t *preferences_get_recent_logs(preferences_t * prefs)
+{
+ return prefs->recent_log_files;
+}
+
+static int preferences_policy_path_compare(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ return apol_policy_path_compare((const apol_policy_path_t *)a, (const apol_policy_path_t *)b);
+}
+
+int preferences_add_recent_policy(preferences_t * prefs, const apol_policy_path_t * policy)
+{
+ size_t i;
+ apol_policy_path_t *p = NULL;
+ if (apol_vector_get_index(prefs->recent_policy_files, policy, preferences_policy_path_compare, NULL, &i) == 0) {
+ return 0;
+ }
+ if ((p = apol_policy_path_create_from_policy_path(policy)) == NULL || apol_vector_append(prefs->recent_policy_files, p) < 0) {
+ int error = errno;
+ apol_policy_path_destroy(&p);
+ errno = error;
+ return -1;
+ }
+ if (apol_vector_get_size(prefs->recent_policy_files) >= MAX_RECENT_ENTRIES) {
+ p = apol_vector_get_element(prefs->recent_policy_files, 0);
+ apol_policy_path_destroy(&p);
+ return apol_vector_remove(prefs->recent_policy_files, 0);
+ }
+ return 0;
+}
+
+apol_vector_t *preferences_get_recent_policies(preferences_t * prefs)
+{
+ return prefs->recent_policy_files;
+}
diff --git a/seaudit/preferences.h b/seaudit/preferences.h
new file mode 100644
index 0000000..8891943
--- /dev/null
+++ b/seaudit/preferences.h
@@ -0,0 +1,274 @@
+/**
+ * @file
+ * Declaration of the current user's preferences for the seaudit
+ * application.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PREFERENCES_H
+#define PREFERENCES_H
+
+#include <apol/policy-path.h>
+#include <apol/vector.h>
+
+typedef struct preferences preferences_t;
+
+/* n.b.: OTHER_FIELD must be the last entry in this enumeration, for
+ message_view stops processing after that token */
+typedef enum preference_field
+{
+ HOST_FIELD, MESSAGE_FIELD, DATE_FIELD,
+ SUSER_FIELD, SROLE_FIELD, STYPE_FIELD, SMLS_LVL_FIELD, SMLS_CLR_FIELD,
+ TUSER_FIELD, TROLE_FIELD, TTYPE_FIELD, TMLS_LVL_FIELD, TMLS_CLR_FIELD,
+ OBJCLASS_FIELD, PERM_FIELD,
+ EXECUTABLE_FIELD, COMMAND_FIELD, NAME_FIELD,
+ PID_FIELD, INODE_FIELD, PATH_FIELD, OTHER_FIELD
+} preference_field_e;
+
+/**
+ * Allocate and return a preferences object. This function will first
+ * initialize the object using the user's configuration file. If that
+ * is not readable then the system-wide configuration is attempted.
+ * It is not an error if both files are not available.
+ *
+ * @return An initialized preferences object, or NULL upon error. The
+ * caller must call preferences_destroy() afterwards.
+ */
+preferences_t *preferences_create(void);
+
+/**
+ * Destroy a preferences object, and all memory associated with it.
+ * Does nothing if the pointer is already NULL.
+ *
+ * @param prefs Reference to a preferences object to destroy. This
+ * will be set to NULL afterwards.
+ */
+void preferences_destroy(preferences_t ** prefs);
+
+/**
+ * Write the preferences object to the user's configuration file,
+ * overwriting any existing file.
+ *
+ * @param prefs Preference object to write.
+ *
+ * @return 0 if successfully written, < 0 upon error.
+ */
+int preferences_write_to_conf_file(preferences_t * prefs);
+
+/**
+ * Return the visibility of the column with the given preference id.
+ *
+ * @param prefs Preference object to query.
+ * @param id Preferences column identifier.
+ *
+ * @return Non-zero if the column is set to be visible, zero if not.
+ */
+int preferences_is_column_visible(preferences_t * prefs, preference_field_e id);
+
+/**
+ * Set the visibility of a column with the given preference id. Note
+ * that this will <b>not</b> update any message_view_t.
+ *
+ * @param prefs Preference object to query.
+ * @param id Preferences column identifier.
+ * @param visible If non-zero then set column visible, zero to hide.
+ *
+ * @see message_view_update_visible_columns needs to be called if
+ * column visibilities are changed.
+ */
+void preferences_set_column_visible(preferences_t * prefs, preference_field_e id, int visible);
+
+/**
+ * Set the filename for the preferred audit log file. Unless
+ * overridden by the command line, this log file will be opened when
+ * seaudit is launched.
+ *
+ * @param prefs Preference object to modify.
+ * @param log Path to the log file. The string will be duplicated.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int preferences_set_log(preferences_t * prefs, const char *log);
+
+/**
+ * Get the filename for the preferred log file from the preferences
+ * object.
+ *
+ * @param prefs Preference object to query.
+ *
+ * @return Filename for the log file, or an empty string if none set.
+ * Do not modify this string.
+ */
+const char *preferences_get_log(preferences_t * prefs);
+
+/**
+ * Set the path for the preferred policy. Unless overridden by the
+ * command line, this policy will be opened when seaudit is launched.
+ *
+ * @param prefs Preference object to modify.
+ * @param policy Path to the policy file. The policy path object will
+ * be duplicated.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int preferences_set_policy(preferences_t * prefs, const apol_policy_path_t * policy);
+
+/**
+ * Get the policy path object for the preferred policy from the
+ * preferences object.
+ *
+ * @param prefs Preference object to query.
+ *
+ * @return Policy path object for the policy, or NULL if none set. Do
+ * not modify this object.
+ */
+const apol_policy_path_t *preferences_get_policy(preferences_t * prefs);
+
+/**
+ * Set the default report filename.
+ *
+ * @param prefs Preference object to modify.
+ * @param report Path to the report. The string will be duplicated.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int preferences_set_report(preferences_t * prefs, const char *report);
+
+/**
+ * Get the default report filename.
+ *
+ * @param prefs Preference object to query.
+ *
+ * @return Filename for the report, or an empty string if none set.
+ * Do not modify this string.
+ */
+const char *preferences_get_report(preferences_t * prefs);
+
+/**
+ * Set the default stylesheet filename.
+ *
+ * @param prefs Preference object to modify.
+ * @param stylesheet Path to the stylesheet. The string will be
+ * duplicated.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int preferences_set_stylesheet(preferences_t * prefs, const char *stylesheet);
+
+/**
+ * Get the default stylesheet filename.
+ *
+ * @param prefs Preference object to query.
+ *
+ * @return Filename for the stylesheet, or an empty string if none
+ * set. Do not modify this string.
+ */
+const char *preferences_get_stylesheet(preferences_t * prefs);
+
+/**
+ * Set the default real-time setting for opened log files. If startup
+ * is non-zero, then the real-time monitor will be enabled for new log
+ * files.
+ *
+ * @param prefs Preferences object to modify.
+ * @param startup If non-zero, then enable real-time by default.
+ */
+void preferences_set_real_time_at_startup(preferences_t * prefs, int startup);
+
+/**
+ * Get the default value for real-time monitoring.
+ *
+ * @param prefs Preference object to query.
+ *
+ * @return Non-zero if opened logs should be monitored.
+ */
+int preferences_get_real_time_at_startup(preferences_t * prefs);
+
+/**
+ * Set the time interval (in milliseconds) for polling the log file
+ * during real-time monitoring.
+ *
+ * @param prefs Preferences object to modify.
+ * @param interval Polling interval in milliseconds.
+ */
+void preferences_set_real_time_interval(preferences_t * prefs, int interval);
+
+/**
+ * Get the time interval (in milliseconds) when performing real-time
+ * monitoring.
+ *
+ * @param prefs Preference object to query.
+ *
+ * @return Time interval in milliseconds.
+ */
+int preferences_get_real_time_interval(preferences_t * prefs);
+
+/**
+ * Add a filename to the recently opened log files list. If the name
+ * is already in the list then do nothing. Otherwise append the name
+ * to the end of the list. If the list grows too large then remove
+ * the oldest entry.
+ *
+ * @param prefs Preference object to modify.
+ * @param log Path to the most recently opened log. The string will
+ * be duplicated.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int preferences_add_recent_log(preferences_t * prefs, const char *log);
+
+/**
+ * Return a vector of recently loaded log files (type char *), with
+ * the oldest file first. Note that the vector may be empty.
+ *
+ * @param prefs Preferences object to query.
+ *
+ * @return Vector of paths. Treat this vector as const.
+ */
+apol_vector_t *preferences_get_recent_logs(preferences_t * prefs);
+
+/**
+ * Add a policy path to the recently opened policy files list. If the
+ * name is already in the list then do nothing. Otherwise append the
+ * name to the end of the list. If the list grows too large then
+ * remove the oldest entry.
+ *
+ * @param prefs Preference object to modify.
+ * @param policy Path to the most recently opened policy. The path
+ * will be duplicated.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int preferences_add_recent_policy(preferences_t * prefs, const apol_policy_path_t * policy);
+
+/**
+ * Return a vector of recently loaded policy files (type
+ * apol_policy_path_t *), with the oldest file first. Note that the
+ * vector may be empty.
+ *
+ * @param prefs Preferences object to query.
+ *
+ * @return Vector of paths. Treat this vector as const.
+ */
+apol_vector_t *preferences_get_recent_policies(preferences_t * prefs);
+
+#endif
diff --git a/seaudit/preferences_view.c b/seaudit/preferences_view.c
new file mode 100644
index 0000000..7ac5ed3
--- /dev/null
+++ b/seaudit/preferences_view.c
@@ -0,0 +1,310 @@
+/**
+ * @file
+ * Implementation of preferences editor.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "open_policy_window.h"
+#include "preferences_view.h"
+#include "utilgui.h"
+#include <assert.h>
+#include <string.h>
+#include <glade/glade.h>
+
+struct pref_view
+{
+ GladeXML *xml;
+ toplevel_t *top;
+ preferences_t *prefs;
+ GtkDialog *dialog;
+ const char *current_log;
+ const apol_policy_path_t *current_policy;
+ apol_policy_path_t *policy_path;
+};
+
+struct pref_entry
+{
+ const char *entry_name, *browse_name;
+ const char *(*accessor) (preferences_t *);
+ int (*modifier) (preferences_t *, const char *);
+ const char *title;
+ /* next field is for callbacks to the browse button */
+ struct pref_view *pv;
+};
+
+static struct pref_entry pref_entry_data[] = {
+ {"PrefsViewLogEntry", "PrefsViewLogBrowseButton", preferences_get_log, preferences_set_log, "Select Default Log"},
+ {"PrefsViewConfigEntry", "PrefsViewConfigBrowseButton", preferences_get_report, preferences_set_report,
+ "Select Report Configuration File"},
+ {"PrefsViewStylesheetEntry", "PrefsViewStylesheetBrowseButton", preferences_get_stylesheet, preferences_set_stylesheet,
+ "Select HTML Report Style File"}
+};
+static const size_t num_entries = sizeof(pref_entry_data) / sizeof(pref_entry_data[0]);
+
+struct pref_toggle
+{
+ const char *widget_name;
+ preference_field_e preference_field;
+};
+
+static const struct pref_toggle pref_toggle_map[] = {
+ {"HostCheck", HOST_FIELD},
+ {"MessageCheck", MESSAGE_FIELD},
+ {"DateCheck", DATE_FIELD},
+ {"SourceUserCheck", SUSER_FIELD},
+ {"SourceRoleCheck", SROLE_FIELD},
+ {"SourceTypeCheck", STYPE_FIELD},
+ {"SourceMLSLVLCheck", SMLS_LVL_FIELD},
+ {"SourceMLSCLRCheck", SMLS_CLR_FIELD},
+ {"TargetUserCheck", TUSER_FIELD},
+ {"TargetRoleCheck", TROLE_FIELD},
+ {"TargetTypeCheck", TTYPE_FIELD},
+ {"TargetMLSLVLCheck", TMLS_LVL_FIELD},
+ {"TargetMLSCLRCheck", TMLS_CLR_FIELD},
+ {"ObjectClassCheck", OBJCLASS_FIELD},
+ {"PermissionCheck", PERM_FIELD},
+ {"ExecutableCheck", EXECUTABLE_FIELD},
+ {"CommandCheck", COMMAND_FIELD},
+ {"NameCheck", NAME_FIELD},
+ {"PIDCheck", PID_FIELD},
+ {"InodeCheck", INODE_FIELD},
+ {"PathCheck", PATH_FIELD},
+ {"OtherCheck", OTHER_FIELD}
+};
+static const size_t num_toggles = sizeof(pref_toggle_map) / sizeof(pref_toggle_map[0]);
+
+static void preferences_view_on_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data)
+{
+ const struct pref_entry *pe = (const struct pref_entry *)user_data;
+ struct pref_view *pv = pe->pv;
+ GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, pe->entry_name));
+ const char *current_path = gtk_entry_get_text(entry);
+ GtkWindow *parent = GTK_WINDOW(pv->dialog);
+ const char *title = pe->title;
+ apol_vector_t *new_paths = util_open_file(parent, title, current_path, 0);
+ if (new_paths != NULL) {
+ gtk_entry_set_text(entry, apol_vector_get_element(new_paths, 0));
+ apol_vector_destroy(&new_paths);
+ }
+}
+
+static void preferences_view_on_log_current_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct pref_view *pv = (struct pref_view *)user_data;
+ GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, "PrefsViewLogEntry"));
+ assert(entry != NULL);
+ if (pv->current_log == NULL) {
+ gtk_entry_set_text(entry, "");
+ } else {
+ gtk_entry_set_text(entry, pv->current_log);
+ }
+}
+
+static void preferences_view_on_policy_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct pref_view *pv = (struct pref_view *)user_data;
+ GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, "PrefsViewPolicyEntry"));
+ assert(entry != NULL);
+ apol_policy_path_t *new_path;
+
+ open_policy_window_run(pv->top, pv->policy_path, &new_path);
+ if (new_path != NULL) {
+ apol_policy_path_destroy(&pv->policy_path);
+ pv->policy_path = new_path;
+ char *path_string = util_policy_path_to_string(pv->policy_path);
+ gtk_entry_set_text(entry, path_string);
+ free(path_string);
+ }
+}
+
+static void preferences_view_on_policy_current_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct pref_view *pv = (struct pref_view *)user_data;
+ GtkEntry *entry = GTK_ENTRY(glade_xml_get_widget(pv->xml, "PrefsViewPolicyEntry"));
+ assert(entry != NULL);
+ apol_policy_path_destroy(&pv->policy_path);
+ if (pv->current_policy != NULL) {
+ pv->policy_path = apol_policy_path_create_from_policy_path(pv->current_policy);
+ char *path_string = util_policy_path_to_string(pv->policy_path);
+ gtk_entry_set_text(entry, path_string);
+ free(path_string);
+ } else {
+ gtk_entry_set_text(entry, "");
+ }
+}
+
+static void preferences_view_init_widgets(struct pref_view *pv)
+{
+ GtkWidget *w;
+ size_t i;
+
+ w = glade_xml_get_widget(pv->xml, "PreferencesWindow");
+ assert(w != NULL);
+ pv->dialog = GTK_DIALOG(w);
+ gtk_window_set_transient_for(GTK_WINDOW(pv->dialog), toplevel_get_window(pv->top));
+
+ for (i = 0; i < num_entries; i++) {
+ struct pref_entry *pe = pref_entry_data + i;
+ w = glade_xml_get_widget(pv->xml, pe->browse_name);
+ assert(w != NULL);
+ pe->pv = pv;
+ g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_browse_click), pe);
+ }
+
+ w = glade_xml_get_widget(pv->xml, "PrefsViewLogCurrentButton");
+ assert(w != NULL);
+ if (pv->current_log == NULL) {
+ gtk_widget_set_sensitive(w, FALSE);
+ }
+ g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_log_current_click), pv);
+
+ w = glade_xml_get_widget(pv->xml, "PrefsViewPolicyModifyButton");
+ assert(w != NULL);
+ g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_policy_browse_click), pv);
+ w = glade_xml_get_widget(pv->xml, "PrefsViewPolicyCurrentButton");
+ assert(w != NULL);
+ if (pv->current_policy == NULL) {
+ gtk_widget_set_sensitive(w, FALSE);
+ }
+ g_signal_connect(w, "clicked", G_CALLBACK(preferences_view_on_policy_current_click), pv);
+}
+
+/**
+ * Copy values from preferences object to dialog widgets.
+ */
+static void preferences_view_init_values(struct pref_view *pv)
+{
+ GtkWidget *w;
+ const char *current_value;
+ const apol_policy_path_t *current_path;
+ char *s;
+ size_t i;
+
+ for (i = 0; i < num_entries; i++) {
+ const struct pref_entry *pe = pref_entry_data + i;
+ w = glade_xml_get_widget(pv->xml, pe->entry_name);
+ assert(w != NULL);
+ current_value = pe->accessor(pv->prefs);
+ gtk_entry_set_text(GTK_ENTRY(w), current_value);
+ }
+ if ((current_path = preferences_get_policy(pv->prefs)) != NULL) {
+ pv->policy_path = apol_policy_path_create_from_policy_path(current_path);
+ w = glade_xml_get_widget(pv->xml, "PrefsViewPolicyEntry");
+ char *path_string = util_policy_path_to_string(pv->policy_path);
+ assert(w != NULL);
+ gtk_entry_set_text(GTK_ENTRY(w), path_string);
+ free(path_string);
+ }
+ for (i = 0; i < num_toggles; i++) {
+ int visible;
+ w = glade_xml_get_widget(pv->xml, pref_toggle_map[i].widget_name);
+ assert(w != NULL);
+ visible = preferences_is_column_visible(pv->prefs, pref_toggle_map[i].preference_field);
+ if (visible) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
+ } else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), FALSE);
+ }
+ }
+ w = glade_xml_get_widget(pv->xml, "PrefsViewIntervalEntry");
+ assert(w != NULL);
+ if (asprintf(&s, "%d", preferences_get_real_time_interval(pv->prefs)) >= 0) {
+ gtk_entry_set_text(GTK_ENTRY(w), s);
+ free(s);
+ } else {
+ gtk_entry_set_text(GTK_ENTRY(w), "");
+ }
+ w = glade_xml_get_widget(pv->xml, "RealTimeCheck");
+ assert(w != NULL);
+ if (preferences_get_real_time_at_startup(pv->prefs)) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
+ } else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), FALSE);
+ }
+}
+
+/**
+ * Copy values from dialog widget to the preferences object.
+ */
+static void preferences_view_get_from_dialog(struct pref_view *pv)
+{
+ GtkWidget *w;
+ const gchar *entry;
+ size_t i;
+
+ for (i = 0; i < num_entries; i++) {
+ const struct pref_entry *pe = pref_entry_data + i;
+ w = glade_xml_get_widget(pv->xml, pe->entry_name);
+ entry = gtk_entry_get_text(GTK_ENTRY(w));
+ pe->modifier(pv->prefs, entry);
+ }
+ preferences_set_policy(pv->prefs, pv->policy_path);
+ for (i = 0; i < num_toggles; i++) {
+ gboolean active;
+ w = glade_xml_get_widget(pv->xml, pref_toggle_map[i].widget_name);
+ active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+ if (active) {
+ preferences_set_column_visible(pv->prefs, pref_toggle_map[i].preference_field, 1);
+ } else {
+ preferences_set_column_visible(pv->prefs, pref_toggle_map[i].preference_field, 0);
+ }
+ }
+ w = glade_xml_get_widget(pv->xml, "PrefsViewIntervalEntry");
+ entry = gtk_entry_get_text(GTK_ENTRY(w));
+ if (strcmp(entry, "") == 0) {
+ entry = "0";
+ }
+ preferences_set_real_time_interval(pv->prefs, atoi(entry));
+ w = glade_xml_get_widget(pv->xml, "RealTimeCheck");
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
+ preferences_set_real_time_at_startup(pv->prefs, 1);
+ } else {
+ preferences_set_real_time_at_startup(pv->prefs, 0);
+ }
+}
+
+int preferences_view_run(toplevel_t * top, const char *current_log, const apol_policy_path_t * current_policy)
+{
+ struct pref_view pv;
+ gint response;
+
+ memset(&pv, 0, sizeof(pv));
+ pv.top = top;
+ pv.xml = glade_xml_new(toplevel_get_glade_xml(top), "PreferencesWindow", NULL);
+ pv.prefs = toplevel_get_prefs(top);
+ pv.current_log = current_log;
+ pv.current_policy = current_policy;
+
+ preferences_view_init_widgets(&pv);
+ preferences_view_init_values(&pv);
+
+ response = gtk_dialog_run(pv.dialog);
+ if (response != GTK_RESPONSE_OK) {
+ gtk_widget_destroy(GTK_WIDGET(pv.dialog));
+ return 0;
+ }
+ preferences_view_get_from_dialog(&pv);
+ gtk_widget_destroy(GTK_WIDGET(pv.dialog));
+ return 1;
+}
diff --git a/seaudit/preferences_view.h b/seaudit/preferences_view.h
new file mode 100644
index 0000000..d36aa3c
--- /dev/null
+++ b/seaudit/preferences_view.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ * Declaration of preferences editor.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PREFERENCES_VIEW_H
+#define PREFERENCES_VIEW_H
+
+#include "toplevel.h"
+#include <gtk/gtk.h>
+
+/**
+ * Display a dialog from which the user may edit his preferences.
+ *
+ * @param top Toplevel object containing preferences to modify
+ * @param current_log Path to the currently loaded log file, or NULL
+ * if none loaded.
+ * @param current_policy Path to the currently loaded policy, or NULL
+ * if none loaded.
+ *
+ * @return Non-zero if preferences changed, zero if not.
+ */
+int preferences_view_run(toplevel_t * top, const char *current_log, const apol_policy_path_t * current_policy);
+
+#endif
diff --git a/seaudit/progress.c b/seaudit/progress.c
new file mode 100644
index 0000000..01b01fb
--- /dev/null
+++ b/seaudit/progress.c
@@ -0,0 +1,200 @@
+/**
+ * @file
+ * Routines to show a progress dialog, indicating that the
+ * application is doing something.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "progress.h"
+#include "utilgui.h"
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+struct progress
+{
+ toplevel_t *top;
+ GtkWidget *progress;
+ GtkWidget *label1, *label2;
+ char *s;
+ int done;
+ GCond *cond;
+ GMutex *mutex;
+};
+
+progress_t *progress_create(toplevel_t * top)
+{
+ progress_t *p;
+ GtkWidget *vbox;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL) {
+ return NULL;
+ }
+ p->top = top;
+ p->progress = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_modal(GTK_WINDOW(p->progress), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(p->progress), toplevel_get_window(top));
+ gtk_window_set_default_size(GTK_WINDOW(p->progress), 300, 100);
+ vbox = gtk_vbox_new(FALSE, 2);
+ gtk_container_add(GTK_CONTAINER(p->progress), vbox);
+ p->label1 = gtk_label_new(NULL);
+ gtk_container_add(GTK_CONTAINER(vbox), p->label1);
+ p->label2 = gtk_label_new(NULL);
+ gtk_container_add(GTK_CONTAINER(vbox), p->label2);
+ gtk_widget_show(p->label1);
+ gtk_widget_show(p->label2);
+ gtk_widget_show(vbox);
+ util_cursor_wait(p->progress);
+ p->cond = g_cond_new();
+ p->mutex = g_mutex_new();
+ return p;
+}
+
+void progress_destroy(progress_t ** progress)
+{
+ if (progress != NULL && *progress != NULL) {
+ free((*progress)->s);
+ g_cond_free((*progress)->cond);
+ g_mutex_free((*progress)->mutex);
+ free(*progress);
+ *progress = NULL;
+ }
+}
+
+void progress_show(progress_t * progress, const char *title)
+{
+ gtk_label_set_text(GTK_LABEL(progress->label1), title);
+ gtk_label_set_text(GTK_LABEL(progress->label2), "");
+ gtk_widget_show(progress->progress);
+ gtk_window_deiconify(GTK_WINDOW(progress->progress));
+ gtk_window_set_title(GTK_WINDOW(progress->progress), title);
+ progress->done = 0;
+}
+
+void progress_hide(progress_t * progress)
+{
+ gtk_widget_hide(progress->progress);
+}
+
+int progress_wait(progress_t * progress)
+{
+ GTimeVal wait_time = { 0, 50000 };
+ g_mutex_lock(progress->mutex);
+ while (!progress->done) {
+ g_cond_timed_wait(progress->cond, progress->mutex, &wait_time);
+ if (progress->s != NULL) {
+ gtk_label_set_text(GTK_LABEL(progress->label2), progress->s);
+ free(progress->s);
+ progress->s = NULL;
+ }
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ }
+ g_mutex_unlock(progress->mutex);
+ if (progress->done < 0) {
+ toplevel_ERR(progress->top, GTK_LABEL(progress->label2)->label);
+ return progress->done;
+ } else if (progress->done > 1) {
+ toplevel_WARN(progress->top, GTK_LABEL(progress->label2)->label);
+ return progress->done - 1;
+ } else {
+ progress->done = 0;
+ return 0;
+ }
+}
+
+void progress_done(progress_t * progress)
+{
+ g_mutex_lock(progress->mutex);
+ progress->done = 1;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+void progress_warn(progress_t * progress, char *reason, ...)
+{
+ gchar *s;
+ va_list ap;
+ g_mutex_lock(progress->mutex);
+ if (reason != NULL) {
+ va_start(ap, reason);
+ g_vasprintf(&s, reason, ap);
+ free(progress->s);
+ progress->s = s;
+ va_end(ap);
+ }
+ progress->done = 2;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+void progress_abort(progress_t * progress, char *reason, ...)
+{
+ gchar *s;
+ va_list ap;
+ g_mutex_lock(progress->mutex);
+ if (reason != NULL) {
+ va_start(ap, reason);
+ g_vasprintf(&s, reason, ap);
+ free(progress->s);
+ progress->s = s;
+ va_end(ap);
+ }
+ progress->done = -1;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+static void progress_update_label(progress_t * progress, const char *fmt, va_list va_args)
+{
+ gchar *s = NULL;
+ g_vasprintf(&s, fmt, va_args);
+ g_mutex_lock(progress->mutex);
+ free(progress->s);
+ progress->s = s;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+void progress_update(progress_t * progress, char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ progress_update_label(progress, fmt, ap);
+ va_end(ap);
+}
+
+void progress_seaudit_handle_func(void *arg, const seaudit_log_t * log __attribute__ ((unused)), int level
+ __attribute__ ((unused)), const char *fmt, va_list va_args)
+{
+ progress_t *progress = arg;
+ progress_update_label(progress, fmt, va_args);
+}
+
+void progress_apol_handle_func(void *varg, const apol_policy_t * p __attribute__ ((unused)), int level
+ __attribute__ ((unused)), const char *fmt, va_list argp)
+{
+ progress_t *progress = varg;
+ progress_update_label(progress, fmt, argp);
+}
diff --git a/seaudit/progress.h b/seaudit/progress.h
new file mode 100644
index 0000000..5c6646f
--- /dev/null
+++ b/seaudit/progress.h
@@ -0,0 +1,139 @@
+/**
+ * @file
+ * Header for showing progress dialogs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef PROGRESS_H
+#define PROGRESS_H
+
+typedef struct progress progress_t;
+
+#include "toplevel.h"
+
+#include <apol/policy.h>
+#include <seaudit/log.h>
+
+/**
+ * Allocate and return a new progress dialog object.
+ *
+ * @param top Toplevel object that will control the progress object.
+ *
+ * @return An initialized progress object, or NULL upon error. The
+ * caller is responsible for calling progress_destroy() afterwards.
+ */
+progress_t *progress_create(toplevel_t * top);
+
+/**
+ * Destroy a progress dialog. Does nothing if the pointer is already
+ * NULL.
+ *
+ * @param prefs Reference to a progress object to destroy. This will
+ * be set to NULL afterwards.
+ */
+void progress_destroy(progress_t ** progress);
+
+/**
+ * Display a progress dialog.
+ *
+ * @param progress Progress dialog to show.
+ * @param title Title for the progress window.
+ */
+void progress_show(progress_t * progress, const char *title);
+
+/**
+ * Hide the progress dialog. Note that this does not actually destroy
+ * the object.
+ *
+ * @param progress Progress dialog to hide.
+ */
+void progress_hide(progress_t * progress);
+
+/* the rest of these are for multi-threaded progress dialog */
+
+/**
+ * Block the current thread until the progress dialog receives a done
+ * signal via progress_done() or progress_abort(). The dialog will
+ * periodically awake and update the user interface, based upon
+ * message received by its handle implementations.
+ *
+ * @param progress Progress object to wait against.
+ *
+ * @return 0 if the progress object got a progress_done(), < 0 if
+ * progress_abort().
+ */
+int progress_wait(progress_t * progress);
+
+/**
+ * Signal to a progress object that this thread is ending
+ * successfully. This will cause all threads waiting upon the
+ * progress object to resume.
+ *
+ * @param progress Progress object to signal completion.
+ */
+void progress_done(progress_t * progress);
+
+/**
+ * Signal to a progress object that this thread completed with
+ * warnings. This will cause all threads waiting upon the progress
+ * object to resume.
+ *
+ * @param progress Progress object to signal completion.
+ * @param reason Explanation for warning, or NULL to use most recently
+ * written message as the reason.
+ */
+void progress_warn(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Signal to a progress object that this thread is aborting. This
+ * will cause all threads waiting upon the progress object to resume.
+ *
+ * @param progress Progress object to signal completion.
+ * @param reason Explanation for abort, or NULL to abort for no
+ * reason. The most recently written message will be used as the
+ * reason.
+ */
+void progress_abort(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Have the progress dialog show a message upon its next refresh.
+ *
+ * @param progress Progress object to update.
+ * @param fmt Format for string to display.
+ */
+void progress_update(progress_t * progress, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Implementation of libseaudit's message callback function. This
+ * will route messages generated by libseaudit to the progress
+ * dialog's display. To use this, pass the progress_t object as
+ * seaudit_log_create()'s callback_arg parameter.
+ */
+void progress_seaudit_handle_func(void *arg, const seaudit_log_t * log, int level, const char *fmt, va_list va_args);
+
+/**
+ * Implementation of a libapol message callback function. This will
+ * route messages generated by libapol to the progress dialog's
+ * display. To use this, pass the progress_t object as
+ * apol_policy_open()'s varg parameter.
+ */
+void progress_apol_handle_func(void *varg, const apol_policy_t * p, int level, const char *fmt, va_list argp);
+
+#endif
diff --git a/seaudit/report_window.c b/seaudit/report_window.c
new file mode 100644
index 0000000..d7213b5
--- /dev/null
+++ b/seaudit/report_window.c
@@ -0,0 +1,261 @@
+/**
+ * @file
+ * Run the dialog that generates reports.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "report_window.h"
+#include "utilgui.h"
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <glade/glade.h>
+#include <seaudit/report.h>
+
+struct report_window
+{
+ GladeXML *xml;
+ GtkDialog *dialog;
+ GtkRadioButton *all_messages_radio, *text_radio;
+ GtkToggleButton *malformed_toggle, *use_stylesheet_toggle;
+ GtkWidget *stylesheet_label, *stylesheet_browse, *config_browse;
+ GtkEntry *stylesheet_entry, *config_entry;
+ char *filename;
+ message_view_t *current_view;
+ seaudit_log_t *log;
+ int result;
+ progress_t *progress;
+};
+
+static void report_window_on_all_messages_toggle(GtkToggleButton * toggle, gpointer user_data)
+{
+ gboolean sens = gtk_toggle_button_get_active(toggle);
+ struct report_window *rw = (struct report_window *)user_data;
+ gtk_widget_set_sensitive(GTK_WIDGET(rw->malformed_toggle), sens);
+}
+
+static void report_window_on_use_stylesheet_toggle(GtkToggleButton * toggle, gpointer user_data)
+{
+ gboolean sens = gtk_toggle_button_get_active(toggle);
+ struct report_window *rw = (struct report_window *)user_data;
+ gtk_widget_set_sensitive(rw->stylesheet_label, sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(rw->stylesheet_entry), sens);
+ gtk_widget_set_sensitive(rw->stylesheet_browse, sens);
+}
+
+static void report_window_on_output_format_toggle(GtkToggleButton * toggle, gpointer user_data)
+{
+ gboolean sens = gtk_toggle_button_get_active(toggle);
+ struct report_window *rw = (struct report_window *)user_data;
+ gtk_widget_set_sensitive(GTK_WIDGET(rw->use_stylesheet_toggle), !sens);
+ if (sens == TRUE) {
+ gtk_widget_set_sensitive(rw->stylesheet_label, FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(rw->stylesheet_entry), FALSE);
+ gtk_widget_set_sensitive(rw->stylesheet_browse, FALSE);
+ } else {
+ report_window_on_use_stylesheet_toggle(rw->use_stylesheet_toggle, rw);
+ }
+}
+
+static void report_window_browse(GtkEntry * entry, GtkWindow * parent, const char *title)
+{
+ const char *current_path = gtk_entry_get_text(entry);
+ apol_vector_t *new_paths = util_open_file(parent, title, current_path, 0);
+ if (new_paths != NULL) {
+ gtk_entry_set_text(entry, apol_vector_get_element(new_paths, 0));
+ apol_vector_destroy(&new_paths);
+ }
+}
+
+static void report_window_on_stylesheet_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct report_window *rw = (struct report_window *)user_data;
+ report_window_browse(rw->stylesheet_entry, GTK_WINDOW(rw->dialog), "Select Style Sheet");
+}
+
+static void report_window_on_config_browse_click(GtkWidget * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct report_window *rw = (struct report_window *)user_data;
+ report_window_browse(rw->config_entry, GTK_WINDOW(rw->dialog), "Select Report Configuration");
+}
+
+/**
+ * Set up report window struct's widget pointers.
+ */
+static void report_window_init_dialog(struct report_window *rw, toplevel_t * top)
+{
+ rw->dialog = GTK_DIALOG(glade_xml_get_widget(rw->xml, "ReportWindow"));
+ assert(rw->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(rw->dialog), toplevel_get_window(top));
+
+ rw->all_messages_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowAllMessagesRadio"));
+ rw->malformed_toggle = GTK_TOGGLE_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowMalformedCheck"));
+ assert(rw->all_messages_radio != NULL && rw->malformed_toggle != NULL);
+
+ rw->text_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowTextRadio"));
+ rw->use_stylesheet_toggle = GTK_TOGGLE_BUTTON(glade_xml_get_widget(rw->xml, "ReportWindowUseStylesheetCheck"));
+ assert(rw->text_radio != NULL && rw->use_stylesheet_toggle != NULL);
+
+ rw->stylesheet_label = glade_xml_get_widget(rw->xml, "ReportWindowStylesheetLabel");
+ rw->stylesheet_entry = GTK_ENTRY(glade_xml_get_widget(rw->xml, "ReportWindowStylesheetEntry"));
+ rw->stylesheet_browse = glade_xml_get_widget(rw->xml, "ReportWindowStylesheetBrowse");
+ assert(rw->stylesheet_label != NULL && rw->stylesheet_entry && rw->stylesheet_browse);
+
+ rw->config_entry = GTK_ENTRY(glade_xml_get_widget(rw->xml, "ReportWindowConfigEntry"));
+ rw->config_browse = glade_xml_get_widget(rw->xml, "ReportWindowConfigBrowse");
+ assert(rw->config_entry != NULL && rw->config_browse != NULL);
+
+ /* set up signal handlers */
+ g_signal_connect(rw->all_messages_radio, "toggled", G_CALLBACK(report_window_on_all_messages_toggle), rw);
+ g_signal_connect(rw->text_radio, "toggled", G_CALLBACK(report_window_on_output_format_toggle), rw);
+ g_signal_connect(rw->use_stylesheet_toggle, "toggled", G_CALLBACK(report_window_on_use_stylesheet_toggle), rw);
+ g_signal_connect(rw->stylesheet_browse, "clicked", G_CALLBACK(report_window_on_stylesheet_browse_click), rw);
+ g_signal_connect(rw->config_browse, "clicked", G_CALLBACK(report_window_on_config_browse_click), rw);
+
+}
+
+/**
+ * The first time the report window is shown, populate its entry boxes
+ * with values from the user's preferences. On subsequent times
+ * remember the user's entries.
+ */
+static void report_window_copy_prefs(struct report_window *rw, toplevel_t * top)
+{
+ static int report_window_initialized = 0;
+ if (!report_window_initialized) {
+ preferences_t *prefs = toplevel_get_prefs(top);
+ gtk_entry_set_text(rw->stylesheet_entry, preferences_get_stylesheet(prefs));
+ gtk_entry_set_text(rw->config_entry, preferences_get_report(prefs));
+ }
+ report_window_initialized = 1;
+}
+
+static gpointer report_window_create_report_runner(gpointer data)
+{
+ struct report_window *rw = (struct report_window *)data;
+ seaudit_model_t *model = NULL;
+ seaudit_report_t *report = NULL;
+ seaudit_report_format_e format = SEAUDIT_REPORT_FORMAT_TEXT;
+ int do_malformed = 0, do_stylesheet = 0;
+ const char *config_name = NULL, *stylesheet_name = NULL;
+
+ rw->result = -1;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rw->all_messages_radio))) {
+ model = seaudit_model_create("All Messages", rw->log);
+ if (gtk_toggle_button_get_active(rw->malformed_toggle)) {
+ do_malformed = 1;
+ }
+ } else {
+ seaudit_model_t *view_model = message_view_get_model(rw->current_view);
+ model = seaudit_model_create_from_model(view_model);
+ }
+ if (model == NULL) {
+ progress_abort(rw->progress, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rw->text_radio))) {
+ format = SEAUDIT_REPORT_FORMAT_HTML;
+ }
+ if (gtk_toggle_button_get_active(rw->use_stylesheet_toggle)) {
+ do_stylesheet = 1;
+ }
+ stylesheet_name = gtk_entry_get_text(rw->stylesheet_entry);
+ if (strcmp(stylesheet_name, "") == 0) {
+ stylesheet_name = NULL;
+ }
+ config_name = gtk_entry_get_text(rw->config_entry);
+ if (strcmp(config_name, "") == 0) {
+ config_name = NULL;
+ }
+
+ if ((report = seaudit_report_create(model)) == NULL) {
+ progress_abort(rw->progress, "%s", strerror(errno));
+ goto cleanup;
+ }
+ if (seaudit_report_set_format(rw->log, report, format) < 0 ||
+ seaudit_report_set_configuration(rw->log, report, config_name) < 0 ||
+ seaudit_report_set_stylesheet(rw->log, report, stylesheet_name, do_stylesheet) < 0 ||
+ seaudit_report_set_malformed(rw->log, report, do_malformed) < 0) {
+ goto cleanup;
+ }
+ progress_update(rw->progress, "Writing");
+ if (seaudit_report_write(rw->log, report, rw->filename) < 0) {
+ goto cleanup;
+ }
+ rw->result = 0;
+ cleanup:
+ seaudit_report_destroy(&report);
+ seaudit_model_destroy(&model);
+ if (rw->result == 0) {
+ progress_done(rw->progress);
+ } else {
+ progress_abort(rw->progress, NULL);
+ }
+ return NULL;
+}
+
+void report_window_run(toplevel_t * top, message_view_t * view)
+{
+ struct report_window rw;
+ /** keey track of most recently used report filename */
+ static char *filename = NULL;
+
+ memset(&rw, 0, sizeof(rw));
+ rw.xml = glade_xml_new(toplevel_get_glade_xml(top), "ReportWindow", NULL);
+ report_window_init_dialog(&rw, top);
+ report_window_copy_prefs(&rw, top);
+
+ rw.current_view = view;
+ rw.log = toplevel_get_log(top);
+ rw.progress = toplevel_get_progress(top);
+ rw.filename = filename;
+ do {
+ gint response = gtk_dialog_run(rw.dialog);
+ if (response != GTK_RESPONSE_OK) {
+ break;
+ }
+ if ((filename = util_save_file(GTK_WINDOW(rw.dialog), "Save Report to File", rw.filename)) == NULL) {
+ continue;
+ }
+ g_free(rw.filename);
+ rw.filename = filename;
+ util_cursor_wait(GTK_WIDGET(rw.dialog));
+ progress_show(rw.progress, "Creating Report");
+ g_thread_create(report_window_create_report_runner, &rw, FALSE, NULL);
+ progress_wait(rw.progress);
+ progress_hide(rw.progress);
+ /* Reset the cursor if the save failed. upon success,
+ * the window will be destroyed anyways, so don't
+ * bother resetting the cursor.
+ *
+ * (Real reason: util_cursor_clear() resets the cursor
+ * as an idle callback -- but by the time it triggers,
+ * the window will be destroyed by then.)
+ */
+ if (rw.result == 0) {
+ break;
+ }
+ util_cursor_clear(GTK_WIDGET(rw.dialog));
+ } while (1);
+ gtk_widget_destroy(GTK_WIDGET(rw.dialog));
+}
diff --git a/seaudit/report_window.h b/seaudit/report_window.h
new file mode 100644
index 0000000..ff9fcb7
--- /dev/null
+++ b/seaudit/report_window.h
@@ -0,0 +1,41 @@
+/**
+ * @file
+ * Dialog that generates reports from all messages or only those in
+ * the current view.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef REPORT_WINDOW_H
+#define REPORT_WINDOW_H
+
+#include "toplevel.h"
+#include "message_view.h"
+
+/**
+ * Display and run a dialog that allows the user to generate a report.
+ *
+ * @param top Toplevel containing preferences and log file for report
+ * writer.
+ * @param view Current message view.
+ */
+void report_window_run(toplevel_t * top, message_view_t * view);
+
+#endif
diff --git a/seaudit/seaudit-report-group.conf b/seaudit/seaudit-report-group.conf
new file mode 100644
index 0000000..3b0664c
--- /dev/null
+++ b/seaudit/seaudit-report-group.conf
@@ -0,0 +1,3 @@
+# This is the logfile group configuration file for Logwatch
+LogFile = messages
+
diff --git a/seaudit/seaudit-report-service.conf b/seaudit/seaudit-report-service.conf
new file mode 100644
index 0000000..30efcde
--- /dev/null
+++ b/seaudit/seaudit-report-service.conf
@@ -0,0 +1,5 @@
+# This is the service filter configuration file for Logwatch
+Title = "seaudit-report"
+
+# Which logfile group...
+LogFile = seaudit-report-group
diff --git a/seaudit/seaudit-report-service.in b/seaudit/seaudit-report-service.in
new file mode 100644
index 0000000..d923589
--- /dev/null
+++ b/seaudit/seaudit-report-service.in
@@ -0,0 +1,24 @@
+#!/bin/sh
+# shell script to run seaudit-report on STDIN
+#
+
+SEAUDITREPORT=@bindir@
+OPTS="--stdin --malformed"
+
+echo "Date Range: $LOGWATCH_DATE_RANGE"
+echo "Detail Level: $LOGWATCH_DETAIL_LEVEL"
+echo "Temp Dir: $LOGWATCH_TEMP_DIR"
+echo "Debug Level: $LOGWATCH_DEBUG"
+
+# execute the program with the specified options
+${SEAUDITREPORT} ${OPTS}
+
+# program failed
+if [ $? -ne 0 ]; then
+ RC=$?
+ echo >&2 "Failed while executing seaudit-report.\n"
+ exit $RC
+fi
+
+# All done, exit ok
+exit 0
diff --git a/seaudit/seaudit-report.c b/seaudit/seaudit-report.c
new file mode 100644
index 0000000..af3c6fb
--- /dev/null
+++ b/seaudit/seaudit-report.c
@@ -0,0 +1,246 @@
+/**
+ * @file
+ * Command line tool for processing SELinux audit logs and generating
+ * a concise report containing standard information as well as
+ * customized information using seaudit views. Reports are rendered
+ * in either HTML or plain text. Future support will provide
+ * rendering into XML. The HTML report can be formatted by providing
+ * an alternate stylesheet file or by configuring the default
+ * stylesheet. This tool also provides the option for including
+ * malformed strings within the report.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <seaudit/log.h>
+#include <seaudit/parse.h>
+#include <seaudit/report.h>
+
+#include <apol/vector.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC"
+
+enum opts
+{
+ OPT_HTML = 256, OPT_STYLESHEET
+};
+
+static struct option const longopts[] = {
+ {"html", no_argument, NULL, OPT_HTML},
+ {"malformed", no_argument, NULL, 'm'},
+ {"output", required_argument, NULL, 'o'},
+ {"stylesheet", required_argument, NULL, OPT_STYLESHEET},
+ {"stdin", no_argument, NULL, 's'},
+ {"config", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+/**
+ * Vector of seaudit_log_t, corresponding to each of the log files to
+ * process.
+ */
+static apol_vector_t *logs = NULL;
+
+/**
+ * Error reporting log handler.
+ */
+static seaudit_log_t *first_log = NULL;
+
+/**
+ * Model that incorporates all of the logs within the logs vector.
+ */
+static seaudit_model_t *model = NULL;
+
+/**
+ * Report object for the above model.
+ */
+static seaudit_report_t *report = NULL;
+
+/**
+ * Destination file for the seaudit report, or NULL to write to
+ * standard output.
+ */
+static char *outfile = NULL;
+
+static void seaudit_report_info_usage(const char *program_name, int brief)
+{
+ printf("Usage: %s [OPTIONS] LOGFILE ...\n\n", program_name);
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", program_name);
+ return;
+ }
+ printf("Generate a customized SELinux log report.\n\n");
+ printf(" -s, --stdin read log data from standard input\n");
+ printf(" -m, --malformed include malformed log messages\n");
+ printf(" -o FILE, --output=FILE output to FILE\n");
+ printf(" --config=FILE read configuration from FILE\n");
+ printf(" --html set output format to HTML\n");
+ printf(" --stylesheet=FILE HTML style sheet for formatting HTML report\n");
+ printf(" (ignored if --html is not given)\n");
+ printf(" -h, --help print this help text and exit\n");
+ printf(" -V, --version print version information and exit\n");
+ printf("\n");
+ printf("Default style sheet is at %s.\n", APOL_INSTALL_DIR);
+}
+
+static void parse_command_line_args(int argc, char **argv)
+{
+ int optc, i;
+ int do_malformed = 0, do_style = 0, read_stdin = 0;
+ seaudit_report_format_e format = SEAUDIT_REPORT_FORMAT_TEXT;
+ char *configfile = NULL, *stylesheet = NULL;
+
+ /* get option arguments */
+ while ((optc = getopt_long(argc, argv, "smo:c:hV", longopts, NULL)) != -1) {
+ switch (optc) {
+ case 's': /* read LOGFILES from standard input */
+ read_stdin = 1;
+ break;
+ case 'm': /* include malformed messages */
+ do_malformed = 1;
+ break;
+ case 'o': /* output file name */
+ outfile = optarg;
+ break;
+ case 'c': /* Alternate config file path */
+ configfile = optarg;
+ break;
+ case OPT_HTML: /* Set the output to format to html */
+ format = SEAUDIT_REPORT_FORMAT_HTML;
+ do_style = 1;
+ break;
+ case OPT_STYLESHEET: /* HTML stylesheet file path */
+ stylesheet = optarg;
+ do_style = 1;
+ break;
+ case 'h':
+ /* display help */
+ seaudit_report_info_usage(argv[0], 0);
+ exit(0);
+ case 'V':
+ /* display version */
+ printf("seaudit-report %s\n%s\n", VERSION, COPYRIGHT_INFO);
+ exit(0);
+ default:
+ /* display usage and handle error */
+ seaudit_report_info_usage(argv[0], 1);
+ exit(-1);
+ }
+ }
+
+ /* Throw warning if a stylesheet was specified, but the --html
+ * option was not. */
+ if (stylesheet != NULL && format != SEAUDIT_REPORT_FORMAT_HTML) {
+ fprintf(stderr, "Warning: The --html option was not specified.\n");
+ exit(-1);
+ }
+
+ if (!read_stdin && optind >= argc) {
+ /* display usage and handle error */
+ seaudit_report_info_usage(argv[0], 1);
+ exit(-1);
+ }
+
+ if ((model = seaudit_model_create("seaudit-report", NULL)) == NULL) {
+ exit(-1);
+ }
+ if ((first_log = seaudit_log_create(NULL, NULL)) == NULL || seaudit_model_append_log(model, first_log) < 0) {
+ fprintf(stderr, "ERROR: %s\n", strerror(errno));
+ exit(-1);
+ }
+ if ((logs = apol_vector_create(NULL)) == NULL || apol_vector_append(logs, first_log) < 0) {
+ fprintf(stderr, "ERROR: %s\n", strerror(errno));
+ exit(-1);
+ }
+ if (read_stdin) {
+ /* Ensure that logfiles were not specified in addition
+ * to the standard-in option */
+ if (optind < argc) {
+ fprintf(stderr, "WARNING: %s\n", "Command line filename(s) will be ignored. Reading from stdin.");
+ }
+ if (seaudit_log_parse(first_log, stdin) < 0) {
+ exit(-1);
+ }
+ } else {
+ /* Parse given filenames */
+ FILE *f;
+ seaudit_log_t *l;
+ if ((f = fopen(argv[optind], "r")) == NULL) {
+ fprintf(stderr, "ERROR: %s\n", strerror(errno));
+ exit(-1);
+ }
+ if (seaudit_log_parse(first_log, f) < 0) {
+ exit(-1);
+ }
+ fclose(f);
+ for (i = optind + 1; i < argc; i++) {
+ if ((l = seaudit_log_create(NULL, NULL)) == NULL || seaudit_model_append_log(model, l) < 0) {
+ exit(-1);
+ }
+ if (apol_vector_append(logs, l) < 0) {
+ fprintf(stderr, "ERROR: %s\n", strerror(errno));
+ exit(-1);
+ }
+ if ((f = fopen(argv[i], "r")) == NULL) {
+ fprintf(stderr, "ERROR: %s\n", strerror(errno));
+ exit(-1);
+ }
+ if (seaudit_log_parse(l, f) < 0) {
+ exit(-1);
+ }
+ fclose(f);
+ }
+ }
+
+ if ((report = seaudit_report_create(model)) == NULL ||
+ seaudit_report_set_format(first_log, report, format) < 0 ||
+ seaudit_report_set_configuration(first_log, report, configfile) < 0 ||
+ seaudit_report_set_stylesheet(first_log, report, stylesheet, do_style) < 0 ||
+ seaudit_report_set_malformed(first_log, report, do_malformed) < 0) {
+ exit(-1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ size_t i;
+ parse_command_line_args(argc, argv);
+ if (seaudit_report_write(first_log, report, outfile) < 0) {
+ return -1;
+ }
+ seaudit_report_destroy(&report);
+ seaudit_model_destroy(&model);
+ for (i = 0; i < apol_vector_get_size(logs); i++) {
+ seaudit_log_t *l = apol_vector_get_element(logs, i);
+ seaudit_log_destroy(&l);
+ }
+ apol_vector_destroy(&logs);
+ return 0;
+}
diff --git a/seaudit/seaudit-report.conf b/seaudit/seaudit-report.conf
new file mode 100644
index 0000000..fb36c24
--- /dev/null
+++ b/seaudit/seaudit-report.conf
@@ -0,0 +1,62 @@
+<?xml version="1.0" ?>
+<!-- comment
+ seaudit-report.conf
+
+ This conf file is used to configure information presented in
+ reports generated by the seaudit-report tool.
+
+ The XML-based configuration file is laid out as follows:
+ Report Configuration
+ Standard Section Definitions
+ Custom Section Definitions
+ SEAudit View Definitions
+
+ The STANDARD SECTION definitions define a set of predefined tags
+ that the tool will look for when creating the report. The tool
+ will consider each of these tags as a standard section for the
+ report. To exclude a standard section within the report, simply
+ remove the appropriate standard-section tag. The predefined
+ standard-section tags may use any of the following identifiers:
+
+ 'Statistics': displays general statistics for the log.
+ 'PolicyLoads': displays all load policy messages.
+ 'EnforcementToggles': displays all enforcement toggle messages.
+ 'PolicyBooleans': displays all policy boolean messages.
+ 'AllowListing': displays all allow messages.
+ 'DenyListing': displays all denied messages.
+
+ All of the above predefined tags require the attribute 'id' to
+ be specified or an error will be returned. The 'title' attribute
+ is optional.
+
+ The CUSTOM SECTION definitions are used to create customized
+ sections within the report through the use of saved seaudit
+ view files. SEAudit views files can be created using the seaudit
+ GUI tool or by manually creating the file(s) with the correct
+ format. View tags should be provided as children within the
+ custom-section tag and a valid pathname is required for the
+ tags' 'file' attribute. An example of a custom-section tag can
+ be:
+ <custom-section title="Critical Errors">
+ <view file="/home/sec_admin/seaudit-report/views/critical.vw">
+ </view>
+ </custom-section>
+
+ The 'title' attribute is optional for the custom-section tag.
+
+ NOTE: The tool will display the reports' sections in the order
+ in which the tags are defined within the config file. So for
+ instance, inserting the custom-section tag to be the first defined
+ tag within the config file, will display the custom-section results
+ before all other results in the report. See the default settings
+ below for further information.
+-->
+
+<seaudit-report title="SEAudit Log Report">
+ <standard-section id ="Statistics" title="Log Statistics"></standard-section>
+ <standard-section id ="PolicyLoads" title="Policy Loads"></standard-section>
+ <standard-section id ="EnforcementToggles" title="Enforcement mode toggles"></standard-section>
+ <standard-section id ="PolicyBooleans" title="Policy boolean changes"></standard-section>
+ <standard-section id ="AllowListing" title="Allow Listing"></standard-section>
+ <standard-section id ="DenyListing" title="Deny Listing"></standard-section>
+</seaudit-report>
diff --git a/seaudit/seaudit-report.css b/seaudit/seaudit-report.css
new file mode 100644
index 0000000..4780a2d
--- /dev/null
+++ b/seaudit/seaudit-report.css
@@ -0,0 +1,178 @@
+# seaudit-report.css
+#
+# This is a default stylesheet template used to format an HTML report
+# generated by the seaudit-report tool.
+#
+# There are two ways of configuring this style sheet:
+#
+# 1. External Style Sheet:
+# Specify an external stylesheet to link to the HTML report using a
+# <link> tag, as follows:
+# <link rel="stylesheet" type="text/css" href="mystyle.css">
+# The seaudit-report tool will insert the <link> tag you define
+# within the <head> section of the generated HTML report.
+#
+# 2. Internal Style Sheet:
+# Specify internal style tags for the HTML report using the <style>
+# tag. An example can be:
+# <style type="text/css">
+# body {background-color: red}
+# p {margin-left: 20px}
+# </style>
+# The seaudit-report tool will insert the <style> tag you define
+# within the head section of the generated HTML report.
+#
+# The following is a list of defined HTML tags and their class selectors
+# that can be used:
+#
+# HTML Tag Class Selector(s)
+# -------- -----------------
+# <h1> report_title
+#
+# <h2> standard_section_title, custom_section_title
+#
+# <font> message_count_label, stats_label, message_date,
+# host_name, syscall_timestamp, avc_type,
+# src_context, tgt_context, obj_class
+#
+# <b> report_date, message_count, stats_count
+#
+# See the default (internal) style sheet format below for an example.s
+
+body
+{
+ font-size: 82%;
+ color:#000000;
+ background-color:white;
+ margin:0px;
+}
+
+h1.report_title
+{
+ margin-top:0px;
+ margin-bottom:5px;
+ font-size:110%;
+ padding-top:0px;
+ padding-bottom:1px;
+ padding-left:4px;
+ color:#ffffff;
+ text-align:left;
+ background-color:#808080;
+}
+
+h2.standard_section_title
+{
+ margin-top:0px;
+ margin-bottom:5px;
+ font-size:100%;
+ padding-top:0px;
+ padding-bottom:1px;
+ padding-left:0px;
+ color:black;
+ background-color:transparent;
+}
+
+h2.custom_section_title
+{
+ margin-top:0px;
+ margin-bottom:5px;
+ font-size:100%;
+ padding-top:0px;
+ padding-bottom:1px;
+ padding-left:0px;
+ color:black;
+ background-color:transparent;
+}
+
+font.message_count_label
+{
+color: #000000;
+background-color:transparent;
+}
+
+b.message_count
+{
+color: #000000;
+background-color:transparent;
+}
+
+font.stats_label
+{
+color: #000000;
+background-color:transparent;
+}
+
+b.stats_count
+{
+color: #000000;
+background-color:transparent;
+}
+
+b.report_date
+{
+color: #000000;
+background-color:transparent;
+}
+
+font.message_date
+{
+color: black;
+background-color:transparent;
+position:relative
+}
+
+font.host_name
+{
+color: #000000;
+background-color:transparent;
+}
+
+font.syscall_timestamp
+{
+color: #000000;
+background-color:transparent;
+}
+
+font.avc_deny
+{
+color:red;
+background-color:transparent;
+}
+
+font.avc_grant
+{
+color:green;
+background-color:transparent;
+}
+
+font.exe
+{
+color: #000000;
+background-color:transparent;
+}
+
+font.path
+{
+color: blue;
+background-color:transparent;
+}
+
+font.src_context
+{
+color:black;
+background-color:transparent;
+font-weight: bold
+}
+
+font.tgt_context
+{
+color:black;
+background-color:transparent;
+font-weight: bold
+}
+
+font.obj_class
+{
+color: #000000;
+background-color:transparent;
+} \ No newline at end of file
diff --git a/seaudit/seaudit-small.png b/seaudit/seaudit-small.png
new file mode 100644
index 0000000..4cced9b
--- /dev/null
+++ b/seaudit/seaudit-small.png
Binary files differ
diff --git a/seaudit/seaudit.c b/seaudit/seaudit.c
new file mode 100644
index 0000000..ff542a9
--- /dev/null
+++ b/seaudit/seaudit.c
@@ -0,0 +1,418 @@
+/**
+ * @file
+ * Main driver for the seaudit application. This file also
+ * implements the main class seaudit_t.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "seaudit.h"
+#include "toplevel.h"
+
+#include <apol/util.h>
+#include <seaudit/model.h>
+#include <seaudit/parse.h>
+#include <seaudit/util.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glade/glade.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+struct seaudit
+{
+ preferences_t *prefs;
+ apol_policy_t *policy;
+ apol_policy_path_t *policy_path;
+ seaudit_log_t *log;
+ FILE *file;
+ char *log_path;
+ size_t num_log_messages;
+ const struct tm *first, *last;
+ toplevel_t *top;
+};
+
+static struct option const opts[] = {
+ {"log", required_argument, NULL, 'l'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+preferences_t *seaudit_get_prefs(seaudit_t * s)
+{
+ return s->prefs;
+}
+
+void seaudit_set_policy(seaudit_t * s, apol_policy_t * policy, apol_policy_path_t * path)
+{
+ if (policy != NULL) {
+ if (preferences_add_recent_policy(s->prefs, path) < 0) {
+ toplevel_ERR(s->top, "%s", strerror(errno));
+ apol_policy_destroy(&policy);
+ return;
+ }
+ apol_policy_destroy(&s->policy);
+ s->policy = policy;
+ if (path != s->policy_path) {
+ apol_policy_path_destroy(&s->policy_path);
+ }
+ s->policy_path = path;
+ } else {
+ apol_policy_destroy(&s->policy);
+ apol_policy_path_destroy(&s->policy_path);
+ }
+}
+
+apol_policy_t *seaudit_get_policy(seaudit_t * s)
+{
+ return s->policy;
+}
+
+apol_policy_path_t *seaudit_get_policy_path(seaudit_t * s)
+{
+ return s->policy_path;
+}
+
+void seaudit_set_log(seaudit_t * s, seaudit_log_t * log, FILE * f, const char *filename)
+{
+ if (s->file != NULL) {
+ fclose(s->file);
+ s->file = NULL;
+ }
+ if (log != NULL) {
+ seaudit_model_t *model = NULL;
+ apol_vector_t *messages = NULL;
+ char *t = NULL;
+ if ((model = seaudit_model_create(NULL, log)) == NULL ||
+ (messages = seaudit_model_get_messages(log, model)) == NULL ||
+ (t = strdup(filename)) == NULL || preferences_add_recent_log(s->prefs, filename) < 0) {
+ toplevel_ERR(s->top, "%s", strerror(errno));
+ seaudit_log_destroy(&log);
+ seaudit_model_destroy(&model);
+ apol_vector_destroy(&messages);
+ free(t);
+ return;
+ }
+ /* do it in this order, for filename could be pointing to
+ * s->log_path */
+ seaudit_log_destroy(&s->log);
+ s->log = log;
+ s->file = f;
+ free(s->log_path);
+ s->log_path = t;
+ s->num_log_messages = apol_vector_get_size(messages);
+ if (s->num_log_messages == 0) {
+ s->first = s->last = NULL;
+ } else {
+ seaudit_message_t *message = apol_vector_get_element(messages, 0);
+ s->first = seaudit_message_get_time(message);
+ message = apol_vector_get_element(messages, s->num_log_messages - 1);
+ s->last = seaudit_message_get_time(message);
+ }
+ seaudit_model_destroy(&model);
+ apol_vector_destroy(&messages);
+ } else {
+ seaudit_log_destroy(&s->log);
+ free(s->log_path);
+ s->log_path = NULL;
+ s->num_log_messages = 0;
+ s->first = s->last = NULL;
+ }
+}
+
+int seaudit_parse_log(seaudit_t * s)
+{
+ return seaudit_log_parse(s->log, s->file);
+}
+
+seaudit_log_t *seaudit_get_log(seaudit_t * s)
+{
+ return s->log;
+}
+
+char *seaudit_get_log_path(seaudit_t * s)
+{
+ return s->log_path;
+}
+
+apol_vector_t *seaudit_get_log_users(seaudit_t * s)
+{
+ if (s->log == NULL) {
+ return NULL;
+ } else {
+ return seaudit_log_get_users(s->log);
+ }
+}
+
+apol_vector_t *seaudit_get_log_roles(seaudit_t * s)
+{
+ if (s->log == NULL) {
+ return NULL;
+ } else {
+ return seaudit_log_get_roles(s->log);
+ }
+}
+
+apol_vector_t *seaudit_get_log_types(seaudit_t * s)
+{
+ if (s->log == NULL) {
+ return NULL;
+ } else {
+ return seaudit_log_get_types(s->log);
+ }
+}
+
+apol_vector_t *seaudit_get_log_mls_lvl(seaudit_t * s)
+{
+ if (s->log == NULL) {
+ return NULL;
+ } else {
+ return seaudit_log_get_mls_lvl(s->log);
+ }
+}
+
+apol_vector_t *seaudit_get_log_mls_clr(seaudit_t * s)
+{
+ if (s->log == NULL) {
+ return NULL;
+ } else {
+ return seaudit_log_get_mls_clr(s->log);
+ }
+}
+
+apol_vector_t *seaudit_get_log_classes(seaudit_t * s)
+{
+ if (s->log == NULL) {
+ return NULL;
+ } else {
+ return seaudit_log_get_classes(s->log);
+ }
+}
+
+size_t seaudit_get_num_log_messages(seaudit_t * s)
+{
+ return s->num_log_messages;
+}
+
+const struct tm *seaudit_get_log_first(seaudit_t * s)
+{
+ return s->first;
+}
+
+const struct tm *seaudit_get_log_last(seaudit_t * s)
+{
+ return s->last;
+}
+
+static seaudit_t *seaudit_create(preferences_t * prefs)
+{
+ seaudit_t *s = calloc(1, sizeof(*s));
+ if (s != NULL) {
+ s->prefs = prefs;
+ }
+ return s;
+}
+
+static void seaudit_destroy(seaudit_t ** s)
+{
+ if (s != NULL && *s != NULL) {
+ apol_policy_destroy(&(*s)->policy);
+ seaudit_log_destroy(&(*s)->log);
+ if ((*s)->file != NULL) {
+ fclose((*s)->file);
+ }
+ preferences_destroy(&(*s)->prefs);
+ toplevel_destroy(&(*s)->top);
+ free((*s)->policy_path);
+ free((*s)->log_path);
+ free(*s);
+ *s = NULL;
+ }
+}
+
+static void print_version_info(void)
+{
+ printf("seaudit %s\n%s\n", VERSION, COPYRIGHT_INFO);
+}
+
+static void print_usage_info(const char *program_name, int brief)
+{
+ printf("Usage: %s [OPTIONS] [POLICY ...]\n\n", program_name);
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", program_name);
+ return;
+ }
+ printf("Audit Log analysis tool for Security Enhanced Linux.\n\n");
+ printf(" -l FILE, --log=FILE open the log FILE\n");
+ printf(" -h, --help print this help text and exit\n");
+ printf(" -V, --version print version information and exit\n\n");
+}
+
+static void seaudit_parse_command_line(seaudit_t * seaudit, int argc, char **argv, const char **log, apol_policy_path_t ** policy)
+{
+ int optc;
+ *log = NULL;
+ *policy = NULL;
+ apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ char *primary_path = NULL;
+ apol_vector_t *modules = NULL;
+ while ((optc = getopt_long(argc, argv, "l:hV", opts, NULL)) != -1) {
+ switch (optc) {
+ case 'l':
+ {
+ *log = optarg;
+ break;
+ }
+ case 'h':
+ {
+ print_usage_info(argv[0], 0);
+ seaudit_destroy(&seaudit);
+ exit(EXIT_SUCCESS);
+ }
+ case 'V':
+ {
+ print_version_info();
+ seaudit_destroy(&seaudit);
+ exit(EXIT_SUCCESS);
+ }
+ default:
+ {
+ /* unrecognized argument give full usage */
+ print_usage_info(argv[0], 1);
+ seaudit_destroy(&seaudit);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ if (optind < argc) { /* modules */
+ if ((modules = apol_vector_create(NULL)) == NULL) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ seaudit_destroy(&seaudit);
+ exit(EXIT_FAILURE);
+ }
+ path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ primary_path = argv[optind++];
+ while (argc - optind) {
+ if (apol_vector_append(modules, argv[optind])) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ seaudit_destroy(&seaudit);
+ exit(EXIT_FAILURE);
+ }
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ optind++;
+ }
+ }
+ if (*log == NULL) {
+ *log = preferences_get_log(seaudit->prefs);
+ }
+ if (primary_path != NULL && strcmp(primary_path, "") != 0) {
+ if (apol_file_is_policy_path_list(primary_path)) {
+ if ((*policy = apol_policy_path_create_from_file(primary_path)) == NULL) {
+ ERR(NULL, "%s", "invalid policy list");
+ seaudit_destroy(&seaudit);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if ((*policy = apol_policy_path_create(path_type, primary_path, modules)) == NULL) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ seaudit_destroy(&seaudit);
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else {
+ const apol_policy_path_t *path = preferences_get_policy(seaudit->prefs);
+ if (path != NULL && (*policy = apol_policy_path_create_from_policy_path(path)) == NULL) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ seaudit_destroy(&seaudit);
+ exit(EXIT_FAILURE);
+ }
+ }
+ apol_vector_destroy(&modules);
+}
+
+/*
+ * We don't want to do the heavy work of loading and displaying the
+ * log and policy before the main loop has started because it will
+ * freeze the gui for too long. To solve this, the function is called
+ * from an idle callback set-up in main.
+ */
+struct delay_file_data
+{
+ toplevel_t *top;
+ const char *log_filename;
+ apol_policy_path_t *policy_path;
+};
+
+static gboolean delayed_main(gpointer data)
+{
+ struct delay_file_data *dfd = (struct delay_file_data *)data;
+ if (dfd->log_filename != NULL && strcmp(dfd->log_filename, "") != 0) {
+ toplevel_open_log(dfd->top, dfd->log_filename);
+ }
+ if (dfd->policy_path != NULL) {
+ toplevel_open_policy(dfd->top, dfd->policy_path);
+ }
+ return FALSE;
+}
+
+int main(int argc, char **argv)
+{
+ preferences_t *prefs;
+ seaudit_t *app;
+ const char *log;
+ apol_policy_path_t *policy;
+ struct delay_file_data file_data;
+
+ gtk_init(&argc, &argv);
+ glade_init();
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+ if ((prefs = preferences_create()) == NULL) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ exit(EXIT_FAILURE);
+ }
+ if ((app = seaudit_create(prefs)) == NULL) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ exit(EXIT_FAILURE);
+ }
+ seaudit_parse_command_line(app, argc, argv, &log, &policy);
+ if ((app->top = toplevel_create(app)) == NULL) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ seaudit_destroy(&app);
+ exit(EXIT_FAILURE);
+ }
+ file_data.top = app->top;
+ file_data.log_filename = log;
+ file_data.policy_path = policy;
+ g_idle_add(&delayed_main, &file_data);
+ gtk_main();
+ if (preferences_write_to_conf_file(app->prefs) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ }
+ seaudit_destroy(&app);
+ exit(EXIT_SUCCESS);
+}
diff --git a/seaudit/seaudit.glade b/seaudit/seaudit.glade
new file mode 100644
index 0000000..95da87e
--- /dev/null
+++ b/seaudit/seaudit.glade
@@ -0,0 +1,7713 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkDialog" id="PreferencesWindow">
+ <property name="title" translatable="yes">seaudit Preferences</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewCancelButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ <accessibility>
+ <atkproperty name="AtkObject::accessible_name" translatable="yes">Close</atkproperty>
+ </accessibility>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewOKButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox39">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkTable" id="table2">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">10</property>
+
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Policy:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewLogBrowseButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="PrefsViewLogEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewLogCurrentButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Use Current</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Log:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewPolicyModifyButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-edit</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Choose...</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="PrefsViewPolicyEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewPolicyCurrentButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Use Current</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table31">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">10</property>
+
+ <child>
+ <widget class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Report Config File:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Report Style Sheet:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="PrefsViewStylesheetEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewConfigBrowseButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PrefsViewStylesheetBrowseButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="PrefsViewConfigEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Default Files&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkTable" id="table3">
+ <property name="border_width">10</property>
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">5</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">2</property>
+ <property name="column_spacing">18</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="SourceTypeCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source Type</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="SourceRoleCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source Role</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="SourceUserCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source User</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="SourceMLSLVLCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source MLS Level</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="SourceMLSCLRCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source MLS Clearance</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="DateCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Date</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="MessageCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Message</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="HostCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Hostname</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="TargetUserCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Target User</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="TargetRoleCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Target Role</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="TargetTypeCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Target Type</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="TargetMLSLVLCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Target MLS Level</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="TargetMLSCLRCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Target MLS Clearance</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="ObjectClassCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Object Class</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="PermissionCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Permission</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="ExecutableCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Executable</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="PIDCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">PID</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="InodeCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Inode</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="PathCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Path</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="OtherCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Other</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="NameCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Name</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="CommandCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Command</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Columns to Display&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame3">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkTable" id="table4">
+ <property name="border_width">10</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">1</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">2</property>
+ <property name="column_spacing">18</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Log update interval (in milliseconds):</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="PrefsViewIntervalEntry">
+ <property name="width_request">53</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Time interval for updating the log in real-time mode</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="RealTimeCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Enable real-time monitoring of newly opened log files</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Real-time Monitoring&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="PolicyWindow">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK</property>
+ <property name="extension_events">GDK_EXTENSION_EVENTS_ALL</property>
+ <property name="title" translatable="yes">Find TE Rules</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="default_width">800</property>
+ <property name="default_height">600</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkNotebook" id="PolicyWindowNotebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="border_width">10</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">41</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="PolicyWindowSTypeCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source type regular expression</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="PolicyWindowSTypeCombo">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="PolicyWindowSTypeDirectCheck">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Only show direct matches</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="PolicyWindowTTypeCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Target type regular expression</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="PolicyWindowTTypeCombo">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="PolicyWindowTTypeDirectCheck">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Only show direct matches</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="PolicyWindowClassCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Object class</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="PolicyWindowClassCombo">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="PolicyWindowFindTERulesButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <accelerator key="Return" modifiers="0" signal="clicked"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Find TE Rules</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PolicyWindowCloseButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="PolicyWindowTERulesResults">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK</property>
+ <property name="extension_events">GDK_EXTENSION_EVENTS_ALL</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="PolicyWindowFindTERulesTab">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Find TE Rules</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="PolicyWindowPolicyText">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="PolicyWindowPolicyTextTab">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Policy Source</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkStatusbar" id="statusbar1">
+ <property name="visible">True</property>
+ <property name="has_resize_grip">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkWindow" id="TopLevel">
+ <property name="title" translatable="yes">seaudit</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">950</property>
+ <property name="default_height">650</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <signal name="destroy" handler="toplevel_on_destroy" object="TopLevel" last_modification_time="Mon, 27 Nov 2006 16:53:05 GMT"/>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+ <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="FileMenu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="FileMenu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="OpenLog">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Open a log file</property>
+ <property name="label" translatable="yes">Open _Log</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_open_log_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:23:31 GMT"/>
+ <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1458">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="OpenRecentLog">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Open a log from the recent files list</property>
+ <property name="label" translatable="yes">Open Recent Log</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator3">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="OpenPolicy">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Open a policy</property>
+ <property name="label" translatable="yes">Open _Policy</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_open_policy_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:23:18 GMT"/>
+ <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1459">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="OpenRecentPolicy">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Open a policy from the recent files list</property>
+ <property name="label" translatable="yes">Open Recent Policy</property>
+ <property name="use_underline">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Preferences">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Edit startup and display preferences</property>
+ <property name="label" translatable="yes">Preferences</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_preferences_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:22:57 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1460">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator4">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Quit">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Quit seaudit</property>
+ <property name="label" translatable="yes">_Quit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_quit_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:24:49 GMT"/>
+ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1461">
+ <property name="visible">True</property>
+ <property name="stock">gtk-quit</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="ViewMenu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="ViewMenu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="NewView">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Create a new default view</property>
+ <property name="label" translatable="yes">_New View</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_new_view_activate" object="TopLevel" last_modification_time="Tue, 05 Dec 2006 20:38:59 GMT"/>
+ <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1462">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="OpenView">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Open an existing view</property>
+ <property name="label" translatable="yes">_Open View...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_open_view_activate" object="TopLevel" last_modification_time="Mon, 27 Nov 2006 15:48:22 GMT"/>
+ <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1463">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="SaveView">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Save the current view</property>
+ <property name="label" translatable="yes">_Save View</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_save_view_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:34:20 GMT"/>
+ <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1464">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="SaveViewAs">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Save the current view as a new file</property>
+ <property name="label" translatable="yes">S_ave View As...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_save_viewas_activate" object="TopLevel" last_modification_time="Tue, 05 Dec 2006 20:40:24 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1465">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save-as</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator5">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="ModifyView">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Modify the current view</property>
+ <property name="label" translatable="yes">_Modify View</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_modify_view_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:35:27 GMT"/>
+ <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1466">
+ <property name="visible">True</property>
+ <property name="stock">gtk-convert</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator7">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="ExportAll">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Export to a file all messages in the current view</property>
+ <property name="label" translatable="yes">_Export Messages...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_export_all_messages_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:37:54 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="ExportSelected">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Export to a file selected messages</property>
+ <property name="label" translatable="yes">E_xport Selected Messages...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_export_selected_messages_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:37:16 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator8">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="ViewMessage">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">View all fields of the selected message</property>
+ <property name="label" translatable="yes">_View Selected Message</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_view_entire_message_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 15:39:51 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="SearchMenu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Search</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="SearchMenu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="FindTERules">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Find TE rules in the loaded policy</property>
+ <property name="label" translatable="yes">_Find TE Rules...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_find_terules_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 16:30:21 GMT"/>
+ <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1467">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="ToolsMenu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Tools</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="ToolsMenu_menu">
+
+ <child>
+ <widget class="GtkMenuItem" id="CreateReport">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Create a report from audit messages</property>
+ <property name="label" translatable="yes">_Create Report...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_create_report_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 16:30:21 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator10">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkCheckMenuItem" id="MonitorLog">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Enable or disable real-time monitoring of log file</property>
+ <property name="label" translatable="yes">_Monitor Log</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <signal name="activate" handler="toplevel_on_monitor_log_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 16:35:09 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="ClearView">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Clear all messages from the current view</property>
+ <property name="label" translatable="yes">C_lear View</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_clear_view_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 16:30:21 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="HelpMenu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="HelpMenu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Help">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_help_activate" object="TopLevel" last_modification_time="Mon, 27 Nov 2006 15:44:08 GMT"/>
+ <accelerator key="H" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1468">
+ <property name="visible">True</property>
+ <property name="stock">gtk-help</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator9">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="About">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_About seaudit</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_about_seaudit_activate" object="TopLevel" last_modification_time="Wed, 22 Nov 2006 16:02:03 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image1469">
+ <property name="visible">True</property>
+ <property name="stock">gtk-about</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="FindTERulesButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Find TE rules in the loaded policy</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="toplevel_on_find_terules_click" object="TopLevel" last_modification_time="Mon, 27 Nov 2006 15:48:56 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment54">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox69">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1488">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label207">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Find TE Rules</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ModifyViewButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Modify the current view</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="toplevel_on_modify_view_click" object="TopLevel" last_modification_time="Mon, 27 Nov 2006 15:48:51 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment55">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox70">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1489">
+ <property name="visible">True</property>
+ <property name="stock">gtk-convert</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label208">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Modify View</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="MonitorLogButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Toggle the monitor log feature</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="toplevel_on_monitor_log_click" object="TopLevel" last_modification_time="Mon, 27 Nov 2006 15:48:44 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment56">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox71">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1490">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label209">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Monitor Log</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ClearViewButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip" translatable="yes">Clear messages in the current view</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal name="clicked" handler="toplevel_on_clear_view_click" object="TopLevel" last_modification_time="Thu, 05 Jul 2007 21:07:02 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment57">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox72">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1491">
+ <property name="visible">True</property>
+ <property name="stock">gtk-clear</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label210">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Clear View</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="NotebookVBox">
+ <property name="border_width">1</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="PolicyVersionLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Policy: No Policy</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">28</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">20</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="LogNumLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Log Messages: No log</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.519999980927</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">24</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="LogDateLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Dates: No log</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="MonitorLogLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Monitor Status: &lt;span foreground=&quot;red&quot;&gt;OFF&lt;/span&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">18</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">20</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="ReportWindow">
+ <property name="title" translatable="yes">Create Report</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="default_width">760</property>
+ <property name="default_height">240</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="ReportWindowClose">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ReportWindowCreate">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment26">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox44">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1474">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label129">
+ <property name="visible">True</property>
+ <property name="label">Create Report</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox36">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame27">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment21">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">4</property>
+ <property name="right_padding">4</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox14">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox18">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="ReportWindowAllMessagesRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Messages from entire audit log</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="ReportWindowViewMessagesRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Messages from current view
+(Does not preserve sort order.)</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">ReportWindowAllMessagesRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="ReportWindowMalformedCheck">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Include malformed messages</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label122">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Input&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame28">
+ <property name="border_width">8</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment22">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">4</property>
+ <property name="right_padding">4</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="ReportWindowTextRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Plain Text</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="ReportWindowHTMLRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">HTML</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">ReportWindowTextRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox17">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="ReportWindowUseStylesheetCheck">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Use HTML style sheet</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox42">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">7</property>
+
+ <child>
+ <widget class="GtkLabel" id="ReportWindowStylesheetLabel">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes"> Style sheet file:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="ReportWindowStylesheetEntry">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ReportWindowStylesheetBrowse">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment25">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox43">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1473">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label128">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox39">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">7</property>
+
+ <child>
+ <widget class="GtkLabel" id="label123">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Report config file:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="ReportWindowConfigEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ReportWindowConfigBrowse">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment24">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox41">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1472">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label126">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label124">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Output&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="ModifyViewWindow">
+ <property name="title" translatable="yes">Modify View</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="button48">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button49">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-10</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button56">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox19">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox46">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label131">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">View name:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="ModifyViewNameEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame29">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox47">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkComboBox" id="ModifyViewVisibleCombo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Show
+Hide</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label132">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">messages that match</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="ModifyViewMatchCombo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">all filters
+any filter</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label133">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Match&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame30">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox48">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow4">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="ModifyViewFilterView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox2">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkButton" id="ModifyViewAddButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ModifyViewEditButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-edit</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ModifyViewRemoveButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ModifyViewImportButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment28">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox49">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1476">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label134">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Import</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ModifyViewExportButton">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment29">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox50">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1477">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label135">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">E_xport</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label136">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Filters&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="FilterWindow">
+ <property name="title" translatable="yes">Edit Filter</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="closebutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox20">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame31">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox21">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox51">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label138">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Filter name:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewNameEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"> </property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox52">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label139">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Filter match:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="FilterViewMatchCombo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">All Criteria
+Any Criterion</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="notebook1">
+ <property name="border_width">4</property>
+ <property name="width_request">349</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox22">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame32">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkTable" id="table17">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewSUserEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Source user(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewSRoleEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Source role(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewSTypeEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Source type(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewSMLSLVLEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Source MLS Level(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewSMLSCLREntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Source MLS Clearance(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewSRoleButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select source role(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> Role: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewSUserButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select source user(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> User: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewSTypeButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select source type(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> Type: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewSMLSLVLButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select source MLS Level(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> MLS Level: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewSMLSCLRButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select source MLS Clearance(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> MLS Clearance: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label140">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Source Context&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame33">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkTable" id="table18">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewTTypeButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select target type(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> Type: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewTUserButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select target user(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> User: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewTRoleButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select target role(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> Role: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewTMLSLVLButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select target MLS Level(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> MLS Level: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewTMLSCLRButton">
+ <property name="border_width">2</property>
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select target MLS Clearance(s)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> MLS Clearance: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewTTypeEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Target type(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewTUserEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Target user(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewTRoleEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Target role(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewTMLSLVLEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Target MLS Level(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewTMLSCLREntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Target MLS Clearance(s) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label141">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Target Context&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame34">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox53">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewClassButton">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Select object class(es)</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes"> Class: </property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewClassEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Object class(es) to filter, comma separated</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label142">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Object Class&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox6">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewContextClearButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment38">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox58">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1480">
+ <property name="visible">True</property>
+ <property name="stock">gtk-clear</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label175">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Clear Tab</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label144">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Context</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox23">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame35">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkTable" id="table19">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label145">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">IP Address:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label146">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Port:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label147">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Interface:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewIPAddrEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">IP Address to filter (match as a glob expression)</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewPortEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Port number to match (any of port, source, dest, fport or lport)</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewNetIfEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Network interface to match (exact string match)</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label148">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Networking&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame36">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkTable" id="table20">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label149">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Executable:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewExeEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Name of executable (match as a glob expression)</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewPathEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Path of target file (match as a glob expression)</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label150">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Path:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label151">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Hostname:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewHostEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Host system that generated audit message (match as a glob expression)</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label152">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Command:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="FilterViewCommEntry">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Name of command (match as a glob expression)</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label153">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Message Type:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="FilterViewMessageCombo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">All Messages
+Only AVC Denied
+Only AVC Granted</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label154">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Other&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox7">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">5</property>
+
+ <child>
+ <widget class="GtkButton" id="FilterViewOtherClearButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment31">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox55">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1479">
+ <property name="visible">True</property>
+ <property name="stock">gtk-clear</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label155">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Clear Tab</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label156">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Other</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox24">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="FilterViewDateNoneRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Do not match date and time</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="FilterViewDateBeforeRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Match messages before Start</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">FilterViewDateNoneRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="FilterViewDateAfterRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Match messages after Start</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">FilterViewDateNoneRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment41">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">20</property>
+ <property name="right_padding">20</property>
+
+ <child>
+ <widget class="GtkFrame" id="FilterViewDateStartFrame">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment42">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">4</property>
+ <property name="right_padding">4</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox29">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkTable" id="table26">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">True</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label183">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Month:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label184">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Day:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateStartDaySpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 1 31 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="FilterViewDateStartMonthCombo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Jan
+Feb
+Mar
+Apr
+May
+Jun
+Jul
+Aug
+Sep
+Oct
+Nov
+Dec</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table27">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label186">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Hour:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label187">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Minute:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label188">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Second:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateStartHourSpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 23 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateStartMinuteSpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 59 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateStartSecondSpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 59 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label190">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Start&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="FilterViewDateBetweenRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Match messages between Start and End</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">FilterViewDateNoneRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment44">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">20</property>
+ <property name="right_padding">20</property>
+
+ <child>
+ <widget class="GtkFrame" id="FilterViewDateEndFrame">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment45">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">4</property>
+ <property name="right_padding">4</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox30">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkTable" id="table28">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">True</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label191">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Month:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label192">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Day:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateEndDaySpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 1 31 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="FilterViewDateEndMonthCombo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">Jan
+Feb
+Mar
+Apr
+May
+Jun
+Jul
+Aug
+Sep
+Oct
+Nov
+Dec</property>
+ <property name="add_tearoffs">False</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="table29">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">0</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label193">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Hour:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label194">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Minute:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label195">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Second:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateEndHourSpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 23 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateEndMinuteSpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 59 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="FilterViewDateEndSecondSpin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 59 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label196">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;End&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label173">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Date</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox27">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="FilterViewDescView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_WORD</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label174">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Notes</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="PolicyComponentListsWindow">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Policy Components</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="default_width">600</property>
+ <property name="default_height">450</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area5">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="closebutton2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox31">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox37">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label200">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Select items from:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment49">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">10</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox38">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="PolicyCompLogRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Log</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="PolicyCompPolicyRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">PolicyCompLogRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="PolicyCompBothRadio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Log and Policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">PolicyCompLogRadio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox59">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame50">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox35">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow7">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="PolicyCompIncView">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Unselected Items</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_ENTER_NOTIFY_MASK</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox9">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="PolicyCompIncSelectButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Select All</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PolicyCompIncUnselectButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Unselect All</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label199">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Included Items&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment47">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">0.20000000298</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">50</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox3">
+ <property name="width_request">85</property>
+ <property name="height_request">56</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="PolicyCompToExcButton">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Add to selected items list</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment48">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox64">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1482">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-forward</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label202">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PolicyCompToIncButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkImage" id="image1483">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-back</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame52">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox36">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow8">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="PolicyCompExcView">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Unselected Items</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_ENTER_NOTIFY_MASK</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox10">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="PolicyCompExcSelectButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Select All</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="PolicyCompExcUnselectButton">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Unselect All</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label201">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Excluded Items&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="PolicyOpenWindow">
+ <property name="title" translatable="yes">Open Policy</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="default_width">340</property>
+ <property name="default_height">360</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area6">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancel button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ok button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox.1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Policy Type:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment.1.2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">10</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox.1.2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="monolithic radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Monolithic policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="modular radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Modular policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">monolithic radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox.2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="main filename label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Policy Filename:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.2.2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkEntry" id="base entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="base browse">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment.2.2.1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.2.2.1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image.2.2.1.1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label.2.2.1.1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.3">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow.3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="module view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox.3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkButton" id="module add button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module remove button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module list import button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment50">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox65">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1484">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label203">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Import</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module list export button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment51">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox66">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1485">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label204">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">E_xport</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/seaudit/seaudit.gladep b/seaudit/seaudit.gladep
new file mode 100644
index 0000000..5b8f438
--- /dev/null
+++ b/seaudit/seaudit.gladep
@@ -0,0 +1,7 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>seaudit</name>
+ <program_name>seaudit</program_name>
+</glade-project>
diff --git a/seaudit/seaudit.h b/seaudit/seaudit.h
new file mode 100644
index 0000000..34be02b
--- /dev/null
+++ b/seaudit/seaudit.h
@@ -0,0 +1,231 @@
+/**
+ * @file
+ * Declaration of the main driver class for seaudit.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEAUDIT_H
+#define SEAUDIT_H
+
+#include "preferences.h"
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <seaudit/log.h>
+#include <stdio.h>
+#include <time.h>
+
+typedef struct seaudit seaudit_t;
+
+#define COPYRIGHT_INFO "Copyright (c) 2003-2007 Tresys Technology, LLC"
+
+/**
+ * Retrieve the preferences object associated with the seaudit object.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Pointer to a preferences object. Do not free() this pointer.
+ */
+preferences_t *seaudit_get_prefs(seaudit_t * s);
+
+/**
+ * Set the currently loaded policy for seaudit. This will also update
+ * the preferences object's recently loaded policies.
+ *
+ * @param s seaudit object to modify.
+ * @param policy New policy file for seaudit. If NULL then seaudit
+ * has no policy opened. Afterwards seaudit takes ownership of the
+ * policy.
+ * @param path If policy is not NULL, then add this path to the most
+ * recently used policy files. This function takes ownership of the
+ * path.
+ */
+void seaudit_set_policy(seaudit_t * s, apol_policy_t * policy, apol_policy_path_t * path);
+
+/**
+ * Retrieve the currently loaded policy.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Pointer to an apol policy, or NULL if none loaded. Treat
+ * this as a const pointer.
+ */
+apol_policy_t *seaudit_get_policy(seaudit_t * s);
+
+/**
+ * Return the path to the currently loaded policy. If the current
+ * policy is modular then this returns the base policy's path.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Path of policy, or NULL if none loaded. Treat this as a
+ * const pointer.
+ */
+apol_policy_path_t *seaudit_get_policy_path(seaudit_t * s);
+
+/**
+ * Set the currently loaded log for seaudit. This will also update
+ * the preferences object's recently loaded files.
+ *
+ * @param s seaudit object to modify.
+ * @param log New log file for seaudit. If NULL then seaudit has no
+ * log files opened. Afterwards seaudit takes ownership of the log.
+ * @param f File handler that was used to open the log. Afterwards
+ * seaudit takes ownership of this handler.
+ * @param filename If log is not NULL, then add this filename to the
+ * most recently used files.
+ */
+void seaudit_set_log(seaudit_t * s, seaudit_log_t * log, FILE * f, const char *filename);
+
+/**
+ * Command seaudit to (re)parse its log file.
+ *
+ * @param s seaudit object containing the log.
+ *
+ * @return 0 if log parsed cleanly, < 0 upon errors, or > 0 if there
+ * were warnings.
+ */
+int seaudit_parse_log(seaudit_t * s);
+
+/**
+ * Retrieve the currently loaded log file.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Pointer to a libseaudit log, or NULL if none loaded. Treat
+ * this as a const pointer.
+ */
+seaudit_log_t *seaudit_get_log(seaudit_t * s);
+
+/**
+ * Return the path to the currently loaded log file.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Path of log file, or NULL if none loaded. Treat this as a
+ * const pointer.
+ */
+char *seaudit_get_log_path(seaudit_t * s);
+
+/**
+ * Return a vector of strings corresponding to all users found within
+ * currently opened log files. The vector will be sorted
+ * alphabetically.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Vector of sorted users, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *seaudit_get_log_users(seaudit_t * s);
+
+/**
+ * Return a vector of strings corresponding to all roles found within
+ * currently opened log files. The vector will be sorted
+ * alphabetically.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Vector of sorted roles, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *seaudit_get_log_roles(seaudit_t * s);
+
+/**
+ * Return a vector of strings corresponding to all types found within
+ * currently opened log files. The vector will be sorted
+ * alphabetically.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Vector of sorted types, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *seaudit_get_log_types(seaudit_t * s);
+
+/**
+ * Return a vector of strings corresponding to all mls levels
+ * found within currently opened log files. The vector will be sorted
+ * alphabetically.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Vector of sorted types, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *seaudit_get_log_mls_lvl(seaudit_t * s);
+
+/**
+ * Return a vector of strings corresponding to all mls clearance
+ * found within currently opened log files. The vector will be sorted
+ * alphabetically.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Vector of sorted types, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *seaudit_get_log_mls_clr(seaudit_t * s);
+
+/**
+ * Return a vector of strings corresponding to all object classes
+ * found within currently opened log file. The vector will be sorted
+ * alphabetically.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Vector of sorted classes, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *seaudit_get_log_classes(seaudit_t * s);
+
+/**
+ * Return the number of messages in the current log.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Number of log messages, or 0 if no log is opened.
+ */
+size_t seaudit_get_num_log_messages(seaudit_t * s);
+
+/**
+ * Return the time stamp for the first message in the currently opened
+ * log.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Time of the first log message, or NULL if no log is opened.
+ * Treat this as a const pointer.
+ */
+const struct tm *seaudit_get_log_first(seaudit_t * s);
+
+/**
+ * Return the time stamp for the last message in the currently opened
+ * log.
+ *
+ * @param s seaudit object to query.
+ *
+ * @return Time of the last log message, or NULL if no log is opened.
+ * Treat this as a const pointer.
+ */
+const struct tm *seaudit_get_log_last(seaudit_t * s);
+
+#endif
diff --git a/seaudit/seaudit.png b/seaudit/seaudit.png
new file mode 100644
index 0000000..8ed09e2
--- /dev/null
+++ b/seaudit/seaudit.png
Binary files differ
diff --git a/seaudit/seaudit.xcf b/seaudit/seaudit.xcf
new file mode 100644
index 0000000..ada98bb
--- /dev/null
+++ b/seaudit/seaudit.xcf
Binary files differ
diff --git a/seaudit/seaudit_help.txt b/seaudit/seaudit_help.txt
new file mode 100644
index 0000000..3959f02
--- /dev/null
+++ b/seaudit/seaudit_help.txt
@@ -0,0 +1,293 @@
+Audit Log Analysis Tool for Security Enhanced Linux
+
+
+Overview:
+---------
+This file contains basic help information for using seaudit, an audit
+log analysis tool for Security Enhanced Linux (SELinux) audit
+messages.
+
+The tool does not need to be installed on an SELinux system; it will
+work on any Linux machine. The tool parses a given syslog and
+extracts all load policy messages, AVC messages, and change of Boolean
+messages from conditional policies.
+
+The tool has the following main functions:
+ 1) Browse and sort SELinux audit messages.
+ 2) Filter an audit log based on fields in the messages.
+ 3) Search the policy based on data from a given audit message.
+ 4) Export SELinux audit messages to a file.
+ 5) Generate reports in HTML or plain text format from an entire log
+ or an seaudit view.
+
+
+Log and Policy Files:
+---------------------
+The program provides you with the option of opening either a source,
+monolithic binary, or modular policy file. If a policy is not
+specified at the command line, seaudit will attempt to use the default
+policy location, as specified during configuration time (e.g.,
+./configure --with-default-policy).
+
+Note that seaudit does not require an opened policy; in this case the
+user will not be able to use the search policy features of the tool.
+Only one policy and one audit log can be open at a time, so if another
+one is opened the current one will be closed.
+
+When opening a log file the user may get the warning "Warning! One or
+more invalid messages found in audit log." This means that one or
+more of the SELinux audit messages either was missing a standard
+message field (e.g., time, hostname, or access type) or:
+
+ 1) A message had an unrecognized time stamp,
+ 2) An AVC message did not contain permissions,
+ 3) An AVC message was not labeled as "denied" or "granted",
+ 4) A load policy message was not in the correct form, such as
+ missing a line or a data field, or
+ 5) A Boolean message did not contain a list of Booleans.
+
+The seaudit program will still attempt to display the remaining data
+from the SELinux audit message in question along with all the other
+SELinux messages in the log, but only if one of the following
+sub-strings is found within the message:
+
+ "avc:" - an access denied or granted message,
+ "security:" - a load policy message, or
+ "committed booleans" - a change in one or more Boolean states.
+
+All other messages will be ignored.
+
+
+Menus:
+------
+Use the FILE menu to load a different audit log or a policy. The file
+menu also allows the user to change preferences including default log,
+default policy, which columns to present when viewing audit logs, and
+whether seaudit should enable real-time log monitoring upon start-up.
+All of these settings will be saved and reloaded each time seaudit is
+started.
+
+The VIEW menu allows the user to display multiple views of a log. A
+default view is created automatically when an audit log is first
+opened. Additional views can be created by selecting View->New View.
+A view has its own set of filters that limits which messages are
+shown. Use 'Save View' and 'Save View As...' menu items to save to
+file the current view's settings. 'Export Messages' writes to a file
+the messages within the current view; 'Export Selected Messages'
+writes only those that are currently selected. 'View Selected
+Message' will open a new window that shows all of the fields for the
+selected log message or messages.
+
+Use the SEARCH menu to find type enforcement rules within the policy.
+
+The TOOLS menu presents seaudit's advanced features. The first
+option, 'Create Report...', is used to create report files in HTML or
+plain text format using an entire audit log or an seaudit view.
+'Monitor Log' enables and disables seaudit's real-time monitoring
+feature.
+
+Right-click on an audit message within a view to display a pop-up menu
+that allows the user to:
+ - View the entire message within a separate text box,
+ - Find TE Rules within the policy using the message, or
+ - Export selected messages to a file.
+
+
+Sorting:
+--------
+By default the messages within a view are sorted in the order they
+appear within the log file, typically chronologically. To sort by a
+particular field click on the column heading. The only column that
+cannot be used for sorting is the 'Other' column. Only one level of
+sorting can be performed. The file KNOWN-BUGS describes a particular
+instance where the sort order may be misleading.
+
+
+Log Monitoring:
+---------------
+Selecting 'Monitor Log' from the Tools Menu or clicking on the 'Toggle
+Monitor' button turns on and off the real-time log monitoring feature.
+When this feature is on, seaudit checks for new messages at a regular
+interval, per second by default. This interval can be configured from
+the Preferences dialog. As new messages are added to the currently
+loaded log file, each view will be updated according to its filters
+and sorting criterion.
+
+
+Finding TE Rules:
+-----------------
+The 'Find TE Rules' button opens a new dialog box that contains two
+tabs. In the first tab, the user enters search criteria similar to
+those in apol's TE Rules query. If the user had right-clicked an
+audit message and selected the second option, the search criteria will
+be filled in automatically based on that message. For each entry, the
+user may enter a regular expression; he may also choose a entry from
+the drop-down box.
+
+The 'Only show direct matches' checkbox alters the meaning of the
+search. By default the search returns rules that have either the
+provided type or any of the type's attributes in the appropriate
+field. If this checkbox is enabled then the search will only find
+that type; it ignores the type's attributes.
+
+Click on 'Find TE Rules' button to perform the search and return a
+list of matching rules. If the currently opened policy file is
+capable of showing line numbers, the displayed rules will contain
+hyperlinks to the appropriate line in the Policy Source tab.
+
+The second tab, 'Policy Source', provides a convenient display of the
+text of the policy source file and is only available when opening a
+source policy. If a modular policy was opened, then this tab only
+shows the base policy's source.
+
+The seaudit program provides limited searching. More thorough policy
+searches and analyses may be conducted through the companion tool,
+apol.
+
+
+Log Views:
+----------
+The 'Modify View' button opens a dialog box that lets the user modify
+the list of filters for the current view. Filters are used to select
+either messages to show or to hide; in addition messages can match
+either any filter or all filters.
+
+
+Modifying Filters Within A View:
+--------------------------------
+To add a new filter, first select the view for which the filter is
+needed by clicking on the corresponding tab, then click on the 'Modify
+View' button, and then 'Add'. Within this new dialog, edit the
+various properties of a filter such as its name, description, source
+context, target context, object type, etc.
+
+Use the 'Context' tab to enter values for part or all of the source
+and target context, as well as the object class. Either enter the
+values manually with a comma between entries or click on the button
+(e.g., Types) and to open another dialog that has a list of all valid
+entries. This list can be populated by values from the log, the
+policy, or both the log and policy, by selecting the appropriate radio
+button.
+
+Use the 'Other' tab to filter by networking criteria (i.e., IP
+address, port and/or interface) and other miscellaneous fields. Many
+of these fields accept either an exact match or a glob expression (see
+Globbing Expressions below); the text entries' tool tips specify how
+matching is performed.
+
+The filter criteria are saved automatically when this dialog is
+closed.
+
+
+Globbing Expressions:
+---------------------
+Use glob expressions to construct more flexible search filters by
+allowing for pattern expansion instead of just static strings. There
+are several different methods of glob syntax that are supported by
+seaudit.
+
+(1) Wildcard Matching
+
+String containing the characters '?' and '*' are said to contain
+wildcard characters. While, both are considered wildcards they allow
+for different functionality.
+
+ (a) The '?' character matches any character.
+
+ example: ?at matches the strings aat, bat, cat, etc.
+
+ (b) The '*' matches any string.
+
+ example: sys* matches the strings system, sysadmin, etc.
+
+(2) Character Classes
+
+Character classes are used when one desires to find certain
+characters, at a certain position within a string. The '[' character
+is used to begin a character class and the ']' character is used to
+end the class. The characters in the string contained between the two
+brackets comprise the character class, which can NOT be empty.
+
+ example: e[abz]x matches the strings eax, ebx, ezx
+
+(3) Ranges
+
+Ranges are an extension of character classes which allow one to allow
+for finding a certain sequential set of characters at any point in the
+string. The '-' character is used to indicate a range of characters,
+where the character to the left of the '-' is the beginning and the
+character to the right of the '-' is the end. Multiple ranges can be
+used within the same character class.
+
+ example: a[b-e]f matches the strings abf, acf, adf, aef
+ example: 1[2-36-8]9 matches the strings 129, 139, 169, 179, 189
+
+(4) Complementation
+
+Complementation allows for searching using the complement of any given
+character class or range. The character '!' must be the first
+character after '[' when one desires to use a complementation. When
+using complementations the complement of the string enclosed in the
+brackets after the '!' character is used.
+
+ example: a[!b-y]z matches all three-character strings starting
+ with a followed by any character not occurring between b
+ and y (inclusive), and ending in z
+
+ example: a[!c-ik-y]z matches all three-character string starting
+ with a followed by any character not occurring between c
+ and i (inclusive) or between k and y (inclusive), and
+ ending in z
+
+
+*** CAUTION ***
+
+The seaudit program intersperses the use of regular expressions versus
+glob expressions. For example, 'Edit Filter' uses tool tips to
+specify what type of matching is permitted. The 'Find TE Rules'
+dialog allows regular expressions, not glob expressions.
+Additionally, note that all characters used in glob expressions are
+case sensitive.
+
+
+Status Bar:
+-----------
+At the bottom of seaudit is a status bar. In the left corner it
+displays the approximate version of the policy loaded along with the
+policy type. In the middle it displays the number of log messages in
+the current view and the total number of SELinux messages in the audit
+log. The next label shows the span of the dates in the audit log and
+the right-most label shows the status of the real-time log monitor.
+
+
+Creating Reports:
+-----------------
+From the Tools menu the user can create report files in HTML or plain
+text format using an entire audit log or only those messages present
+in the current view. Select the 'Create Report' menu item to display
+a dialog for making configurations to the report and then save the
+report to a file.
+
+Choose which messages to report using the input frame. Messages may
+come from the entire audit log file or only those in the current view.
+If choosing the entire log, one may also include malformed messages
+within the report. See the previous 'Log and Policy Files' heading
+for what makes up a malformed message in seaudit.
+
+Choose the type to report, either plain text or HTML, in the output
+frame. If selecting an HTML file, an HTML style sheet may also be
+included into the report. A report configuration file specifies the
+type and order of messages to report. If the style sheet or the
+configuration file is not specified, seaudit will use the appropriate
+system default files; the default files may be changed from the
+Preferences dialog.
+
+The seaudit report configuration file may be configured to affect
+information presented in reports; it is required for report
+generation. From this file, one can configure various sections for
+the report, as well as create custom sections in the report through
+the use of saved seaudit view files. Review the default
+seaudit-report.conf file that comes packaged with the SETools
+distribution for more information. This file can be located in the
+shared data directory where seaudit was installed, typically
+/usr/local/share/setools-<version>.
diff --git a/seaudit/toplevel.c b/seaudit/toplevel.c
new file mode 100644
index 0000000..d901a99
--- /dev/null
+++ b/seaudit/toplevel.c
@@ -0,0 +1,1179 @@
+/**
+ * @file
+ * Implementation for the main toplevel window.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "message_view.h"
+#include "open_policy_window.h"
+#include "policy_view.h"
+#include "preferences_view.h"
+#include "report_window.h"
+#include "toplevel.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <seaudit/parse.h>
+
+struct toplevel
+{
+ seaudit_t *s;
+ policy_view_t *pv;
+ progress_t *progress;
+ /** vector of message_view_t that are in the toplevel's notebook */
+ apol_vector_t *views;
+ GladeXML *xml;
+ /** filename for glade file */
+ char *xml_filename;
+ /** toplevel window widget */
+ GtkWindow *w;
+ GtkNotebook *notebook;
+ /** non-zero if the log file should be polled for changes */
+ int do_monitor_log;
+ /** event id for the monitor callback */
+ guint monitor_id;
+ /** serial number for models created, such that new models
+ * will be named Untitled <number> */
+ int next_model_number;
+ /** filename for most recently opened view */
+ char *view_filename;
+};
+
+/**
+ * Given a view, return its index within the toplevel notebook pages.
+ *
+ * @param top Toplevel containing the notebook.
+ * @param view View to look up.
+ *
+ * @return Index of the view (zero-indexed), or -1 if not found.
+ */
+static gint toplevel_notebook_find_view(toplevel_t * top, message_view_t * view)
+{
+ gint num_pages = gtk_notebook_get_n_pages(top->notebook);
+ while (num_pages >= 1) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, num_pages - 1);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object");
+ if (v == view) {
+ return num_pages - 1;
+ }
+ num_pages--;
+ }
+ return -1;
+}
+
+/**
+ * Return the view on the page that is currently raised, or NULL if
+ * there are no views.
+ */
+static message_view_t *toplevel_get_current_view(toplevel_t * top)
+{
+ gint current = gtk_notebook_get_current_page(top->notebook);
+ if (current >= 0) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, current);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ return g_object_get_data(G_OBJECT(tab), "view-object");
+ }
+ return NULL;
+}
+
+static void toplevel_on_notebook_switch_page(GtkNotebook * notebook __attribute__ ((unused)), GtkNotebookPage * page
+ __attribute__ ((unused)), guint pagenum __attribute__ ((unused)), toplevel_t * top)
+{
+ toplevel_update_selection_menu_item(top);
+ toplevel_update_status_bar(top);
+}
+
+/**
+ * Callback invoked when a tab close button is clicked.
+ */
+static void toplevel_on_tab_close(GtkButton * button, toplevel_t * top)
+{
+ /* disallow the close if this is the last tab */
+ if (top->views == NULL || apol_vector_get_size(top->views) <= 1) {
+ return;
+ } else {
+ message_view_t *view = g_object_get_data(G_OBJECT(button), "view-object");
+ gint idx = toplevel_notebook_find_view(top, view);
+ size_t i;
+ assert(idx >= 0);
+ gtk_notebook_remove_page(top->notebook, idx);
+ apol_vector_get_index(top->views, view, NULL, NULL, &i);
+ message_view_destroy(&view);
+ apol_vector_remove(top->views, i);
+ }
+}
+
+/**
+ * Create a new view associated with the given model, then create a
+ * tab to place that view. The newly created tab will then be raised.
+ *
+ * @param top Toplevel containing notebook to which add the view and tab.
+ * @param model Model from which to create a view.
+ * @param filename Initial filename for the view.
+ */
+static void toplevel_add_new_view(toplevel_t * top, seaudit_model_t * model, const char *filename)
+{
+ message_view_t *view;
+ GtkWidget *tab, *button, *label, *image;
+ gint idx;
+ if ((view = message_view_create(top, model, filename)) == NULL) {
+ return;
+ }
+ if (apol_vector_append(top->views, view) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ message_view_destroy(&view);
+ return;
+ }
+ tab = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(tab), "view-object", view);
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "view-object", view);
+ image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+ gtk_container_add(GTK_CONTAINER(button), image);
+ gtk_widget_set_size_request(image, 8, 8);
+ g_signal_connect(G_OBJECT(button), "pressed", G_CALLBACK(toplevel_on_tab_close), top);
+ label = gtk_label_new(seaudit_model_get_name(model));
+ g_object_set_data(G_OBJECT(tab), "label", label);
+ gtk_box_pack_start(GTK_BOX(tab), label, TRUE, TRUE, 5);
+ gtk_box_pack_end(GTK_BOX(tab), button, FALSE, FALSE, 5);
+ gtk_widget_show(label);
+ gtk_widget_show(button);
+ gtk_widget_show(image);
+ idx = gtk_notebook_append_page(top->notebook, message_view_get_view(view), tab);
+ gtk_notebook_set_current_page(top->notebook, idx);
+}
+
+/**
+ * Create a new model for the currently loaded log file (which could
+ * be NULL), then create a view that watches that model.
+ */
+static void toplevel_add_new_model(toplevel_t * top)
+{
+ seaudit_log_t *log = seaudit_get_log(top->s);
+ char *model_name = NULL;
+ seaudit_model_t *model = NULL;
+ if (asprintf(&model_name, "Untitled %d", top->next_model_number) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ model = seaudit_model_create(model_name, log);
+ free(model_name);
+ if (model == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ } else {
+ top->next_model_number++;
+ toplevel_add_new_view(top, model, NULL);
+ }
+}
+
+/**
+ * Callback whenever an item from the recent logs submenu is activated.
+ */
+static void toplevel_on_open_recent_log_activate(GtkWidget * widget, gpointer user_data)
+{
+ GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget));
+ const char *path = gtk_label_get_text(GTK_LABEL(label));
+ toplevel_t *top = (toplevel_t *) user_data;
+ toplevel_open_log(top, path);
+}
+
+/**
+ * Update the entries within recent logs submenu to match those in the
+ * preferences object.
+ */
+static void toplevel_set_recent_logs_submenu(toplevel_t * top)
+{
+ GtkMenuItem *recent = GTK_MENU_ITEM(glade_xml_get_widget(top->xml, "OpenRecentLog"));
+ apol_vector_t *paths = preferences_get_recent_logs(toplevel_get_prefs(top));
+ GtkWidget *submenu, *submenu_item;
+ size_t i;
+
+ gtk_menu_item_remove_submenu(recent);
+ submenu = gtk_menu_new();
+ for (i = 0; i < apol_vector_get_size(paths); i++) {
+ char *path = (char *)apol_vector_get_element(paths, i);
+ submenu_item = gtk_menu_item_new_with_label(path);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(submenu), submenu_item);
+ gtk_widget_show(submenu_item);
+ g_signal_connect(G_OBJECT(submenu_item), "activate", G_CALLBACK(toplevel_on_open_recent_log_activate), top);
+ }
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), submenu);
+}
+
+/**
+ * Callback whenever an item from the recent policies submenu is
+ * activated.
+ */
+static void toplevel_on_open_recent_policy_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ apol_policy_path_t *path = g_object_get_data(G_OBJECT(menuitem), "path");
+ toplevel_t *top = (toplevel_t *) user_data;
+ apol_policy_path_t *dup_path = apol_policy_path_create_from_policy_path(path);
+ if (dup_path == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ toplevel_open_policy(top, dup_path);
+}
+
+/**
+ * Update the entries within recent policies submenu to match those in
+ * the preferences object.
+ */
+static void toplevel_set_recent_policies_submenu(toplevel_t * top)
+{
+ GtkMenuItem *recent = GTK_MENU_ITEM(glade_xml_get_widget(top->xml, "OpenRecentPolicy"));
+ apol_vector_t *paths = preferences_get_recent_policies(toplevel_get_prefs(top));
+ GtkWidget *submenu, *submenu_item;
+ size_t i;
+
+ GtkTooltipsData *tips_data = gtk_tooltips_data_get(GTK_WIDGET(recent));
+ assert(tips_data != NULL);
+ GtkTooltips *tooltips = tips_data->tooltips;
+
+ gtk_menu_item_remove_submenu(recent);
+ submenu = gtk_menu_new();
+ for (i = 0; i < apol_vector_get_size(paths); i++) {
+ apol_policy_path_t *path = apol_vector_get_element(paths, i);
+ char *menu_label = NULL;
+ const char *primary_path = apol_policy_path_get_primary(path);
+ GString *tip = g_string_new(NULL);
+ if ((menu_label = util_policy_path_to_string(path)) == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ g_string_free(tip, TRUE);
+ break;
+ }
+ submenu_item = gtk_menu_item_new_with_label(menu_label);
+ free(menu_label);
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ g_string_append_printf(tip, "monolithic policy: %s", primary_path);
+ } else {
+ char *s = NULL;
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t num_modules = apol_vector_get_size(modules);
+ g_string_append_printf(tip, "base policy: %s", primary_path);
+ if (num_modules > 0) {
+ if ((s = apol_str_join(modules, "\n ")) == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ break;
+ }
+ g_string_append_printf(tip, "\n %s", s);
+ free(s);
+ }
+ }
+ gtk_tooltips_set_tip(tooltips, GTK_WIDGET(submenu_item), tip->str, "");
+ g_string_free(tip, TRUE);
+ g_object_set_data(G_OBJECT(submenu_item), "path", path);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(submenu), submenu_item);
+ gtk_widget_show(submenu_item);
+ g_signal_connect(G_OBJECT(submenu_item), "activate", G_CALLBACK(toplevel_on_open_recent_policy_activate), top);
+ }
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), submenu);
+}
+
+/**
+ * Enable/disable all items (menus and buttons) that depend upon if a
+ * log is loaded.
+ *
+ * @param top Toplevel object containing menu items.
+ * @param TRUE to enable items, FALSE to disable.
+ */
+static void toplevel_enable_log_items(toplevel_t * top, gboolean sens)
+{
+ static const char *items[] = {
+ "NewView", "OpenView", "SaveView", "SaveViewAs", "ModifyView",
+ "ExportAll", "ExportSelected", "ViewMessage",
+ "CreateReport", "MonitorLog", "ClearView",
+ "ModifyViewButton", "MonitorLogButton", "ClearViewButton",
+ NULL
+ };
+ size_t i;
+ const char *s;
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(w != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+/**
+ * Enable/disable all items (menus and buttons) that depend upon if a
+ * policy is loaded.
+ *
+ * @param top Toplevel object containing widgets.
+ * @param TRUE to enable items, FALSE to disable.
+ */
+static void toplevel_enable_policy_items(toplevel_t * top, gboolean sens)
+{
+ static const char *items[] = {
+ "FindTERules", "FindTERulesButton",
+ NULL
+ };
+ size_t i;
+ const char *s;
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(w != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+/**
+ * Update the toplevel's title bar to list the log and policy files
+ * opened.
+ *
+ * @param top Toplevel to modify.
+ */
+static void toplevel_update_title_bar(toplevel_t * top)
+{
+ char *log_path = seaudit_get_log_path(top->s);
+ apol_policy_path_t *policy_path = seaudit_get_policy_path(top->s);
+ char *policy_type_str = "Policy";
+ const char *primary_path;
+ char *s;
+
+ if (log_path == NULL) {
+ log_path = "No Log";
+ }
+ if (policy_path == NULL) {
+ primary_path = "No Policy";
+ } else {
+ if (apol_policy_path_get_type(policy_path) == APOL_POLICY_PATH_TYPE_MODULAR) {
+ policy_type_str = "Base";
+ }
+ primary_path = apol_policy_path_get_primary(policy_path);
+ }
+ if (asprintf(&s, "seaudit - [Log file: %s] [%s file: %s]", log_path, policy_type_str, primary_path) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ gtk_window_set_title(top->w, s);
+ free(s);
+}
+
+/**
+ * Initialize the application icons for the program. These icons are
+ * the ones shown by the window manager within title bars and pagers.
+ * The last icon listed in the array will be displayed in the About
+ * dialog.
+ *
+ * @param top Toplevel whose icon to set. All child windows will
+ * inherit these icons.
+ */
+static void init_icons(toplevel_t * top)
+{
+ static const char *icon_names[] = { "seaudit-small.png", "seaudit.png" };
+ GdkPixbuf *icon;
+ char *path;
+ GList *icon_list = NULL;
+ size_t i;
+ for (i = 0; i < sizeof(icon_names) / sizeof(icon_names[0]); i++) {
+ if ((path = apol_file_find_path(icon_names[i])) == NULL) {
+ continue;
+ }
+ icon = gdk_pixbuf_new_from_file(path, NULL);
+ free(path);
+ if (icon == NULL) {
+ continue;
+ }
+ icon_list = g_list_append(icon_list, icon);
+ }
+ gtk_window_set_default_icon_list(icon_list);
+ gtk_window_set_icon_list(top->w, icon_list);
+}
+
+static void message_view_free(void *elem)
+{
+ message_view_t *view = elem;
+ message_view_destroy(&view);
+}
+
+toplevel_t *toplevel_create(seaudit_t * s)
+{
+ toplevel_t *top;
+ GtkWidget *vbox;
+ int error = 0;
+
+ if ((top = calloc(1, sizeof(*top))) == NULL || (top->views = apol_vector_create(message_view_free)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ top->s = s;
+ top->next_model_number = 1;
+
+ if ((top->xml_filename = apol_file_find_path("seaudit.glade")) == NULL ||
+ (top->xml = glade_xml_new(top->xml_filename, "TopLevel", NULL)) == NULL) {
+ fprintf(stderr, "Could not open seaudit.glade.\n");
+ error = EIO;
+ goto cleanup;
+ }
+ top->w = GTK_WINDOW(glade_xml_get_widget(top->xml, "TopLevel"));
+ g_object_set_data(G_OBJECT(top->w), "toplevel", top);
+ init_icons(top);
+ top->notebook = GTK_NOTEBOOK(gtk_notebook_new());
+ g_signal_connect_after(G_OBJECT(top->notebook), "switch-page", G_CALLBACK(toplevel_on_notebook_switch_page), top);
+ vbox = glade_xml_get_widget(top->xml, "NotebookVBox");
+ gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(top->notebook));
+ gtk_widget_show(GTK_WIDGET(top->notebook));
+ gtk_widget_show(GTK_WIDGET(top->w));
+ toplevel_set_recent_logs_submenu(top);
+ toplevel_set_recent_policies_submenu(top);
+
+ glade_xml_signal_autoconnect(top->xml);
+
+ /* create initial blank tab for the notebook */
+ toplevel_add_new_model(top);
+
+ /* initialize sub-windows, now that glade XML file has been
+ * read */
+ if ((top->pv = policy_view_create(top)) == NULL || (top->progress = progress_create(top)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ cleanup:
+ if (error != 0) {
+ toplevel_destroy(&top);
+ errno = error;
+ return NULL;
+ }
+ return top;
+}
+
+void toplevel_destroy(toplevel_t ** top)
+{
+ if (top != NULL && *top != NULL) {
+ if ((*top)->monitor_id > 0) {
+ g_source_remove((*top)->monitor_id);
+ }
+ policy_view_destroy(&(*top)->pv);
+ apol_vector_destroy(&(*top)->views);
+ free((*top)->xml_filename);
+ g_free((*top)->view_filename);
+ progress_destroy(&(*top)->progress);
+ if ((*top)->w != NULL) {
+ gtk_widget_destroy(GTK_WIDGET((*top)->w));
+ }
+ free(*top);
+ *top = NULL;
+ }
+}
+
+struct log_run_datum
+{
+ toplevel_t *top;
+ FILE *file;
+ const char *filename;
+ seaudit_log_t *log;
+ int result;
+};
+
+/**
+ * Update the seaudit log, then refresh all views as necessary. Note
+ * that this only works in a single-threaded environment; otherwise
+ * there are two possible race conditions:
+ * - monitor is disabled while this function is being executed
+ * - a new log file is loaded while the function is being executed
+ *
+ * But what happens if this function is scheduled and then a new log
+ * is opened? In toplevel_open_log(), do_monitor_log is temporarily
+ * disabled because that function is threaded. It is then re-enabled
+ * afterwards.
+ *
+ * To make this function fully thread-safe requires making this entire
+ * function synchronized, and then employ locking every time
+ * do_monitor_log and monitor_id are set.
+ */
+static gboolean toplevel_monitor_log_timer(gpointer data)
+{
+ toplevel_t *top = (toplevel_t *) data;
+ if (top->do_monitor_log) {
+ int retval;
+ gint i = gtk_notebook_get_n_pages(top->notebook) - 1;
+ uint delay;
+ retval = seaudit_parse_log(top->s);
+ if (retval < 0) {
+ GtkCheckMenuItem *w;
+ toplevel_ERR(top, "Error while monitoring log: %s", strerror(errno));
+ w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog"));
+ top->monitor_id = 0;
+ gtk_check_menu_item_set_active(w, 0);
+ return FALSE;
+ }
+ while (i >= 0) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, i);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object");
+ message_view_update_rows(v);
+ i--;
+ }
+
+ /* reschedule another timer callback */
+ delay = preferences_get_real_time_interval(toplevel_get_prefs(top));
+ top->monitor_id = g_timeout_add(delay, toplevel_monitor_log_timer, top);
+ } else {
+ top->monitor_id = 0;
+ }
+ return FALSE;
+}
+
+/**
+ * Enable or disable the log monitoring feature. While enabled, the
+ * log file will be periodically polled; new lines will be parsed and
+ * inserted into the seaudit log object. All models and their views
+ * will then be notified of the changes.
+ *
+ * @param top Toplevel object whose widgets to update.
+ */
+static void toplevel_monitor_log(toplevel_t * top)
+{
+ GtkLabel *label = GTK_LABEL(glade_xml_get_widget(top->xml, "MonitorLogLabel"));
+ assert(label != NULL);
+ if (top->do_monitor_log) {
+ gtk_label_set_markup(label, "Monitor Status: <span foreground=\"green\">ON</span>");
+ if (top->monitor_id == 0) {
+ uint delay = preferences_get_real_time_interval(toplevel_get_prefs(top));
+ top->monitor_id = g_timeout_add(delay, toplevel_monitor_log_timer, top);
+ }
+ } else {
+ if (top->monitor_id > 0) {
+ g_source_remove(top->monitor_id);
+ top->monitor_id = 0;
+ }
+ gtk_label_set_markup(label, "Monitor Status: <span foreground=\"red\">OFF</span>");
+ }
+}
+
+/**
+ * Thread that loads and parses a log file. It will write to
+ * progress_seaudit_handle_func() its status during the load. Note
+ * that the file handle is not closed upon completion; it is left open
+ * so that subsequent calls to seaudit_log_parse(), such as
+ * forreal-time monitoring.
+ *
+ * @param data Pointer to a struct log_run_datum, for control
+ * information.
+ */
+static gpointer toplevel_open_log_runner(gpointer data)
+{
+ struct log_run_datum *run = (struct log_run_datum *)data;
+ progress_update(run->top->progress, "Parsing %s", run->filename);
+ if ((run->file = fopen(run->filename, "r")) == NULL) {
+ progress_update(run->top->progress, "Could not open %s for reading.", run->filename);
+ run->result = -1;
+ goto cleanup;
+ }
+ if ((run->log = seaudit_log_create(progress_seaudit_handle_func, run->top->progress)) == NULL) {
+ progress_update(run->top->progress, "%s", strerror(errno));
+ run->result = -1;
+ goto cleanup;
+ }
+ run->result = seaudit_log_parse(run->log, run->file);
+ cleanup:
+ if (run->result < 0) {
+ if (run->file != NULL) {
+ fclose(run->file);
+ }
+ run->file = NULL;
+ seaudit_log_destroy(&run->log);
+ progress_abort(run->top->progress, NULL);
+ } else if (run->result > 0) {
+ progress_warn(run->top->progress, NULL);
+ } else {
+ progress_done(run->top->progress);
+ }
+ return NULL;
+}
+
+/**
+ * Destroy all views and their notebook tabs.
+ */
+static void toplevel_destroy_views(toplevel_t * top)
+{
+ gint num_pages = gtk_notebook_get_n_pages(top->notebook);
+ while (num_pages >= 1) {
+ message_view_t *view = apol_vector_get_element(top->views, num_pages - 1);
+ gtk_notebook_remove_page(top->notebook, num_pages - 1);
+ message_view_destroy(&view);
+ apol_vector_remove(top->views, num_pages - 1);
+ num_pages--;
+ }
+}
+
+void toplevel_open_log(toplevel_t * top, const char *filename)
+{
+ struct log_run_datum run = { top, NULL, filename, NULL, 0 };
+ int was_monitor_running;
+ GtkCheckMenuItem *w;
+
+ /* disable monitoring during the threaded part of this code */
+ was_monitor_running = top->do_monitor_log;
+ top->do_monitor_log = 0;
+ toplevel_monitor_log(top);
+
+ util_cursor_wait(GTK_WIDGET(top->w));
+ progress_show(top->progress, "Opening Log");
+ g_thread_create(toplevel_open_log_runner, &run, FALSE, NULL);
+ progress_wait(top->progress);
+ progress_hide(top->progress);
+ util_cursor_clear(GTK_WIDGET(top->w));
+
+ if (run.result < 0) {
+ top->do_monitor_log = was_monitor_running;
+ toplevel_monitor_log(top);
+ return;
+ }
+
+ toplevel_destroy_views(top);
+ top->next_model_number = 1;
+ seaudit_set_log(top->s, run.log, run.file, filename);
+ toplevel_set_recent_logs_submenu(top);
+ toplevel_enable_log_items(top, TRUE);
+ toplevel_add_new_model(top);
+ toplevel_update_title_bar(top);
+ toplevel_update_status_bar(top);
+ toplevel_update_selection_menu_item(top);
+ top->do_monitor_log = preferences_get_real_time_at_startup(toplevel_get_prefs(top));
+
+ w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog"));
+
+ gtk_check_menu_item_set_active(w, top->do_monitor_log);
+ /* call this again because the check item could have already
+ * been active, thus its handler would not run */
+ toplevel_monitor_log(top);
+}
+
+struct policy_run_datum
+{
+ toplevel_t *top;
+ apol_policy_path_t *path;
+ apol_policy_t *policy;
+ int result;
+};
+
+/**
+ * Thread that loads and parses a policy file. It will write to
+ * progress_seaudit_handle_func() its status during the load.
+ *
+ * @param data Pointer to a struct policy_run_datum, for control
+ * information.
+ */
+static gpointer toplevel_open_policy_runner(gpointer data)
+{
+ struct policy_run_datum *run = (struct policy_run_datum *)data;
+ progress_update(run->top->progress, "Opening policy.");
+ run->policy =
+ apol_policy_create_from_policy_path(run->path, QPOL_POLICY_OPTION_NO_NEVERALLOWS, progress_apol_handle_func,
+ run->top->progress);
+ if (run->policy == NULL) {
+ run->result = -1;
+ progress_abort(run->top->progress, NULL);
+ return NULL;
+ }
+ run->result = 0;
+ progress_done(run->top->progress);
+ return NULL;
+}
+
+int toplevel_open_policy(toplevel_t * top, apol_policy_path_t * path)
+{
+ struct policy_run_datum run = { top, path, NULL, 0 };
+
+ util_cursor_wait(GTK_WIDGET(top->w));
+ progress_show(top->progress, apol_policy_path_get_primary(path));
+ g_thread_create(toplevel_open_policy_runner, &run, FALSE, NULL);
+ progress_wait(top->progress);
+ progress_hide(top->progress);
+ util_cursor_clear(GTK_WIDGET(top->w));
+ if (run.result < 0) {
+ apol_policy_path_destroy(&path);
+ return run.result;
+ }
+ seaudit_set_policy(top->s, run.policy, path);
+ toplevel_set_recent_policies_submenu(top);
+ toplevel_enable_policy_items(top, TRUE);
+ toplevel_update_title_bar(top);
+ toplevel_update_status_bar(top);
+ policy_view_update(top->pv, path);
+ return 0;
+}
+
+void toplevel_update_status_bar(toplevel_t * top)
+{
+ apol_policy_t *policy = seaudit_get_policy(top->s);
+ GtkLabel *policy_version = (GtkLabel *) glade_xml_get_widget(top->xml, "PolicyVersionLabel");
+ GtkLabel *log_num = (GtkLabel *) glade_xml_get_widget(top->xml, "LogNumLabel");
+ GtkLabel *log_dates = (GtkLabel *) glade_xml_get_widget(top->xml, "LogDateLabel");
+ seaudit_log_t *log = toplevel_get_log(top);
+
+ if (policy == NULL) {
+ gtk_label_set_text(policy_version, "Policy: No policy");
+ } else {
+ char *policy_str = apol_policy_get_version_type_mls_str(policy);
+ if (policy_str == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ char *s;
+ if (asprintf(&s, "Policy: %s", policy_str) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ gtk_label_set_text(policy_version, s);
+ free(s);
+ }
+ free(policy_str);
+ }
+ }
+
+ if (log == NULL) {
+ gtk_label_set_text(log_num, "Log Messages: No log");
+ gtk_label_set_text(log_dates, "Dates: No log");
+ } else {
+ message_view_t *view = toplevel_get_current_view(top);
+ size_t num_messages = seaudit_get_num_log_messages(top->s);
+ size_t num_view_messages;
+ const struct tm *first = seaudit_get_log_first(top->s);
+ const struct tm *last = seaudit_get_log_last(top->s);
+ assert(view != NULL);
+ num_view_messages = message_view_get_num_log_messages(view);
+ char *s, t1[256], t2[256];
+ if (asprintf(&s, "Log Messages: %zd/%zd", num_view_messages, num_messages) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ gtk_label_set_text(log_num, s);
+ free(s);
+ }
+ if (first == NULL || last == NULL) {
+ gtk_label_set_text(log_dates, "Dates: No messages");
+ } else {
+ strftime(t1, 256, "%b %d %H:%M:%S", first);
+ strftime(t2, 256, "%b %d %H:%M:%S", last);
+ if (asprintf(&s, "Dates: %s - %s", t1, t2) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ gtk_label_set_text(log_dates, s);
+ free(s);
+ }
+ }
+ }
+}
+
+void toplevel_update_tabs(toplevel_t * top)
+{
+ gint i = gtk_notebook_get_n_pages(top->notebook) - 1;
+ while (i >= 0) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, i);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ GtkWidget *label = g_object_get_data(G_OBJECT(tab), "label");
+ message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object");
+ seaudit_model_t *model = message_view_get_model(v);
+ const char *name = seaudit_model_get_name(model);
+ gtk_label_set_text(GTK_LABEL(label), name);
+ i--;
+ }
+}
+
+void toplevel_update_selection_menu_item(toplevel_t * top)
+{
+ static const char *items[] = {
+ "ExportSelected", "ViewMessage",
+ NULL
+ };
+ message_view_t *view = toplevel_get_current_view(top);
+ gboolean sens = FALSE;
+ size_t i;
+ const char *s;
+ if (view != NULL) {
+ sens = message_view_is_message_selected(view);
+ }
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(s != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+preferences_t *toplevel_get_prefs(toplevel_t * top)
+{
+ return seaudit_get_prefs(top->s);
+}
+
+seaudit_log_t *toplevel_get_log(toplevel_t * top)
+{
+ return seaudit_get_log(top->s);
+}
+
+apol_vector_t *toplevel_get_log_users(toplevel_t * top)
+{
+ return seaudit_get_log_users(top->s);
+}
+
+apol_vector_t *toplevel_get_log_roles(toplevel_t * top)
+{
+ return seaudit_get_log_roles(top->s);
+}
+
+apol_vector_t *toplevel_get_log_types(toplevel_t * top)
+{
+ return seaudit_get_log_types(top->s);
+}
+
+apol_vector_t *toplevel_get_log_mls_lvl(toplevel_t * top)
+{
+ return seaudit_get_log_mls_lvl(top->s);
+}
+
+apol_vector_t *toplevel_get_log_mls_clr(toplevel_t * top)
+{
+ return seaudit_get_log_mls_clr(top->s);
+}
+
+apol_vector_t *toplevel_get_log_classes(toplevel_t * top)
+{
+ return seaudit_get_log_classes(top->s);
+}
+
+apol_policy_t *toplevel_get_policy(toplevel_t * top)
+{
+ return seaudit_get_policy(top->s);
+}
+
+char *toplevel_get_glade_xml(toplevel_t * top)
+{
+ return top->xml_filename;
+}
+
+progress_t *toplevel_get_progress(toplevel_t * top)
+{
+ return top->progress;
+}
+
+GtkWindow *toplevel_get_window(toplevel_t * top)
+{
+ return top->w;
+}
+
+void toplevel_find_terules(toplevel_t * top, seaudit_message_t * message)
+{
+ policy_view_find_terules(top->pv, message);
+}
+
+/**
+ * Pop-up a dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param msg_type Type of message being displayed.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+static void toplevel_message(toplevel_t * top, GtkMessageType msg_type, const char *fmt, va_list ap)
+{
+ GtkWidget *dialog;
+ char *msg;
+ if (vasprintf(&msg, fmt, ap) < 0) {
+ ERR(NULL, "%s", strerror(errno));
+ return;
+ }
+ dialog = gtk_message_dialog_new(top->w, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg);
+ free(msg);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+void toplevel_ERR(toplevel_t * top, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ toplevel_message(top, GTK_MESSAGE_ERROR, format, ap);
+ va_end(ap);
+}
+
+void toplevel_WARN(toplevel_t * top, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ toplevel_message(top, GTK_MESSAGE_WARNING, format, ap);
+ va_end(ap);
+}
+
+/************* below are callbacks for the toplevel menu items *************/
+
+void toplevel_on_destroy(gpointer user_data, GtkObject * object __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ top->w = NULL;
+ gtk_main_quit();
+}
+
+void toplevel_on_open_log_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ apol_vector_t *paths = util_open_file(top->w, "Open Log", seaudit_get_log_path(top->s), 0);
+ if (paths != NULL) {
+ toplevel_open_log(top, apol_vector_get_element(paths, 0));
+ apol_vector_destroy(&paths);
+ }
+}
+
+void toplevel_on_open_policy_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ open_policy_window_run(top, seaudit_get_policy_path(top->s), NULL);
+}
+
+void toplevel_on_preferences_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (preferences_view_run(top, seaudit_get_log_path(top->s), seaudit_get_policy_path(top->s))) {
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(top->views); i++) {
+ message_view_t *v = apol_vector_get_element(top->views, i);
+ message_view_update_visible_columns(v);
+ }
+ }
+}
+
+void toplevel_on_quit_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ top->w = NULL;
+ gtk_main_quit();
+}
+
+void toplevel_on_new_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_add_new_model(top);
+}
+
+void toplevel_on_open_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ apol_vector_t *paths = util_open_file(top->w, "Open View", top->view_filename, 0);
+ seaudit_model_t *model = NULL;
+ if (paths == NULL) {
+ return;
+ }
+ free(top->view_filename);
+ top->view_filename = strdup(apol_vector_get_element(paths, 0));
+ apol_vector_destroy(&paths);
+ if (top->view_filename == NULL ||
+ (model = seaudit_model_create_from_file(top->view_filename)) == NULL ||
+ seaudit_model_append_log(model, seaudit_get_log(top->s)) < 0) {
+ toplevel_ERR(top, "Error opening view: %s", strerror(errno));
+ seaudit_model_destroy(&model);
+ } else {
+ toplevel_add_new_view(top, model, top->view_filename);
+ }
+}
+
+void toplevel_on_save_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_save(view);
+}
+
+void toplevel_on_save_viewas_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_saveas(view);
+}
+
+void toplevel_on_modify_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_modify(view);
+}
+
+void toplevel_on_export_all_messages_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_export_all_messages(view);
+}
+
+void toplevel_on_export_selected_messages_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_export_selected_messages(view);
+}
+
+void toplevel_on_view_entire_message_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_entire_message(view);
+}
+
+void toplevel_on_find_terules_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_find_terules(top, NULL);
+}
+
+void toplevel_on_create_report_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ report_window_run(top, view);
+}
+
+void toplevel_on_monitor_log_activate(gpointer user_data, GtkMenuItem * widget)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ top->do_monitor_log = 1;
+ } else {
+ top->do_monitor_log = 0;
+ }
+ toplevel_monitor_log(top);
+}
+
+void toplevel_on_clear_view_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_clear(view);
+}
+
+void toplevel_on_help_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkWidget *window;
+ GtkWidget *scroll;
+ GtkWidget *text_view;
+ GtkTextBuffer *buffer;
+ char *help_text = NULL;
+ size_t len;
+ int rt;
+ char *dir;
+
+ window = gtk_dialog_new_with_buttons("seaudit Help",
+ GTK_WINDOW(top->w),
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE);
+ g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ text_view = gtk_text_view_new();
+ gtk_window_set_default_size(GTK_WINDOW(window), 520, 300);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), scroll);
+ gtk_container_add(GTK_CONTAINER(scroll), text_view);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_NONE);
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
+ dir = apol_file_find_path("seaudit_help.txt");
+ if (!dir) {
+ toplevel_ERR(top, "Cannot find help file.");
+ return;
+ }
+ rt = apol_file_read_to_buffer(dir, &help_text, &len);
+ free(dir);
+ if (rt != 0) {
+ free(help_text);
+ return;
+ }
+ gtk_text_buffer_set_text(buffer, help_text, len);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_widget_show(text_view);
+ gtk_widget_show(scroll);
+ gtk_widget_show(window);
+}
+
+void toplevel_on_about_seaudit_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+#ifdef GTK_2_8
+ gtk_show_about_dialog(top->w,
+ "comments", "Audit Log Analysis Tool for Security Enhanced Linux",
+ "copyright", COPYRIGHT_INFO,
+ "name", "seaudit", "version", VERSION, "website", "http://oss.tresys.com/projects/setools", NULL);
+#else
+ GtkWidget *w = gtk_message_dialog_new(top->w,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "%s %s\n%s\n%s\n%s",
+ "seaudit", VERSION,
+ "Audit Log Analysis Tool for Security Enhanced Linux",
+ COPYRIGHT_INFO,
+ "http://oss.tresys.com/projects/setools");
+ gtk_dialog_run(GTK_DIALOG(w));
+ gtk_widget_destroy(w);
+#endif
+}
+
+void toplevel_on_find_terules_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_find_terules(top, NULL);
+}
+
+void toplevel_on_modify_view_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_modify(view);
+}
+
+void toplevel_on_monitor_log_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkCheckMenuItem *w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog"));
+ gboolean old_state;
+ assert(w != NULL);
+ old_state = gtk_check_menu_item_get_active(w);
+ gtk_check_menu_item_set_active(w, !old_state);
+}
+
+void toplevel_on_clear_view_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_clear(view);
+}
diff --git a/seaudit/toplevel.h b/seaudit/toplevel.h
new file mode 100644
index 0000000..448c8ad
--- /dev/null
+++ b/seaudit/toplevel.h
@@ -0,0 +1,263 @@
+/**
+ * @file
+ * Declaration of the main toplevel window for seaudit.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TOPLEVEL_H
+#define TOPLEVEL_H
+
+typedef struct toplevel toplevel_t;
+
+#include "progress.h"
+#include "seaudit.h"
+#include <apol/policy-path.h>
+#include <gtk/gtk.h>
+#include <seaudit/message.h>
+
+/**
+ * Allocate and return an instance of the toplevel window object.
+ * This will create the window, set up the menus and icons, create an
+ * empty notebook, then display the window.
+ *
+ * @param s Main seaudit object that will control the toplevel.
+ *
+ * @return An initialized toplevel object, or NULL upon error. The
+ * caller must call toplevel_destroy() afterwards.
+ */
+toplevel_t *toplevel_create(seaudit_t * s);
+
+/**
+ * Destroy the toplevel window. This function will recursively
+ * destroy all other windows. This does nothing if the pointer is set
+ * to NULL.
+ *
+ * @param top Reference to a toplevel object. Afterwards the pointer
+ * will be set to NULL.
+ */
+void toplevel_destroy(toplevel_t ** top);
+
+/**
+ * Open a log file, destroying any existing logs and views first.
+ * Afterwards, create a new view for the log.
+ *
+ * @param top Toplevel object, used for UI control.
+ * @param filename Name of the log to open.
+ */
+void toplevel_open_log(toplevel_t * top, const char *filename);
+
+/**
+ * Open a policy file, destroying any existing policies upon success.
+ *
+ * @param top Toplevel object, used for UI control.
+ * @param path Path to the policy to open. This function takes
+ * ownership of this object.
+ *
+ * @return 0 on successful open, < 0 on error.
+ */
+int toplevel_open_policy(toplevel_t * top, apol_policy_path_t * path);
+
+/**
+ * Update the status bar to show the current policy, number of log
+ * messages in the current view, range of messages in current view,
+ * and monitor status.
+ *
+ * @param top Toplevel whose status bar to update.
+ */
+void toplevel_update_status_bar(toplevel_t * top);
+
+/**
+ * Update the menu items whenever a message is selected/deselected.
+ * Certain commands are legal only when one or more messages are
+ * selected.
+ *
+ * @param top Toplevel whose menu to update.
+ */
+void toplevel_update_selection_menu_item(toplevel_t * top);
+
+/**
+ * Update the tab names for all views.
+ *
+ * @param top Toplevel whose notebook tabs to update.
+ */
+void toplevel_update_tabs(toplevel_t * top);
+
+/**
+ * Return the current preferences object for the toplevel object.
+ *
+ * @param top Toplevel containing preferences.
+ *
+ * @return Pointer to a preferences object. Do not free() this pointer.
+ */
+preferences_t *toplevel_get_prefs(toplevel_t * top);
+
+/**
+ * Return a seaudit_log_t object used for error reporting by
+ * libseaudit.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return libseaudit reporting object, or NULL if no log exists yet.
+ * Treat this as a const pointer.
+ */
+seaudit_log_t *toplevel_get_log(toplevel_t * top);
+
+/**
+ * Return a vector of strings corresponding to all users found within
+ * the current log file. The vector will be sorted alphabetically.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return Vector of sorted users, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *toplevel_get_log_users(toplevel_t * top);
+
+/**
+ * Return a vector of strings corresponding to all roles found within
+ * the current log file. The vector will be sorted alphabetically.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return Vector of sorted roles, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *toplevel_get_log_roles(toplevel_t * top);
+
+/**
+ * Return a vector of strings corresponding to all types found within
+ * the current log file. The vector will be sorted alphabetically.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return Vector of sorted types, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *toplevel_get_log_types(toplevel_t * top);
+
+/**
+ * Return a vector of strings corresponding to all mls
+ * levels found within the current log file.
+ * The vector will be sorted alphabetically.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return Vector of sorted types, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *toplevel_get_log_mls_lvl(toplevel_t * top);
+
+/**
+ * Return a vector of strings corresponding to all mls
+ * clearance found within the current log file.
+ * The vector will be sorted alphabetically.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return Vector of sorted types, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *toplevel_get_log_mls_clr(toplevel_t * top);
+
+/**
+ * Return a vector of strings corresponding to all object classes
+ * found within the current log file. The vector will be sorted
+ * alphabetically.
+ *
+ * @param top Toplevel containing seaudit log object.
+ *
+ * @return Vector of sorted classes, or NULL if no log is loaded. The
+ * caller must call apol_vector_destroy() upon the return value.
+ */
+apol_vector_t *toplevel_get_log_classes(toplevel_t * top);
+
+/**
+ * Return the currently loaded policy.
+ *
+ * @param top Toplevel containing policy.
+ *
+ * @return Current policy, or NULL if no policy is loaded yet. Treat
+ * this as a const pointer.
+ */
+apol_policy_t *toplevel_get_policy(toplevel_t * top);
+
+/**
+ * Return the filename containing seaudit's glade file.
+ *
+ * @param top Toplevel containing glade XML declarations.
+ *
+ * @return Name of the glade file. Do not modify this string.
+ */
+char *toplevel_get_glade_xml(toplevel_t * top);
+
+/**
+ * Return the progress object, so that sub-windows may also show the
+ * threaded progress object.
+ *
+ * @param top Toplevel containing progress object.
+ *
+ * @return Progress object. Do not free() this pointer.
+ */
+progress_t *toplevel_get_progress(toplevel_t * top);
+
+/**
+ * Return the main application window. Sub-windows should be set
+ * transient to this window.
+ *
+ * @param top Toplevel containing main window.
+ *
+ * @return Main window.
+ */
+GtkWindow *toplevel_get_window(toplevel_t * top);
+
+/**
+ * (Re)open a dialog that allows the user to search for TE rules in
+ * the currently opened policy. If message is not NULL then set the
+ * query's initial parameters to the message's source type, target
+ * type, and object class.
+ *
+ * @param top Toplevel containing policy.
+ * @param message If non-NULL, the initial parameters for query.
+ */
+void toplevel_find_terules(toplevel_t * top, seaudit_message_t * message);
+
+/**
+ * Pop-up an error dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param format Format string to print, using syntax of printf(3).
+ */
+void toplevel_ERR(toplevel_t * top, const char *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Pop-up a warning dialog with a line of text and wait for the user
+ * to dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param format Format string to print, using syntax of printf(3).
+ */
+void toplevel_WARN(toplevel_t * top, const char *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+#endif
diff --git a/seaudit/utilgui.c b/seaudit/utilgui.c
new file mode 100644
index 0000000..22028e1
--- /dev/null
+++ b/seaudit/utilgui.c
@@ -0,0 +1,134 @@
+/**
+ * @file
+ * Miscellaneous helper functions for GTK+ applications.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "utilgui.h"
+#include <string.h>
+
+void util_message(GtkWindow * parent, GtkMessageType msg_type, const char *msg)
+{
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+void util_cursor_wait(GtkWidget * widget)
+{
+ GdkCursor *cursor;
+ if (widget->window != NULL) {
+ cursor = gdk_cursor_new(GDK_WATCH);
+ gdk_window_set_cursor(widget->window, cursor);
+ gdk_cursor_unref(cursor);
+ }
+}
+
+/**
+ * WARNING: this is sort of a hack
+ *
+ * If we reset the pointer at the end of a callback, it gets reset too
+ * soon (i.e. before all of the pending events have been processed. To
+ * avoid this, this function is put in an idle handler by
+ * util_clear_cursor().
+ */
+static gboolean pointer_reset(gpointer data)
+{
+ gdk_window_set_cursor(GTK_WIDGET(data)->window, NULL);
+ return FALSE;
+}
+
+void util_cursor_clear(GtkWidget * widget)
+{
+ g_idle_add(&pointer_reset, widget);
+}
+
+apol_vector_t *util_open_file(GtkWindow * parent, const char *title, const char *init_path, gboolean multiple)
+{
+ GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+ apol_vector_t *paths = NULL;
+ if (init_path != NULL) {
+ gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), init_path);
+ }
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), multiple);
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ GSList *files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+ GSList *f;
+ paths = apol_vector_create(g_free);
+ for (f = files; f != NULL; f = f->next) {
+ apol_vector_append(paths, f->data);
+ }
+ g_slist_free(files);
+ }
+ gtk_widget_destroy(dialog);
+ return paths;
+}
+
+char *util_save_file(GtkWindow * parent, const char *title, const char *init_path)
+{
+ GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
+ char *path = NULL;
+#ifdef GTK_2_8
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+#endif
+ if (init_path != NULL) {
+ gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), init_path);
+ } else {
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled");
+ }
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+ gtk_widget_destroy(dialog);
+ return path;
+}
+
+char *util_policy_path_to_string(const apol_policy_path_t * path)
+{
+ char *s;
+ const char *primary_path = apol_policy_path_get_primary(path);
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ return strdup(primary_path);
+ } else {
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t num_modules = apol_vector_get_size(modules);
+ if (asprintf(&s, "%s + %zd module%s", primary_path, num_modules, num_modules == 1 ? "" : "s") < 0) {
+ return NULL;
+ }
+ return s;
+ }
+}
+
+const gchar *util_combo_box_get_active_text(GtkComboBox * w)
+{
+#ifdef GTK_2_8
+ return gtk_combo_box_get_active_text(w);
+#else
+ return gtk_entry_get_text(GTK_ENTRY(GTK_BIN(w)->child));
+#endif
+}
diff --git a/seaudit/utilgui.h b/seaudit/utilgui.h
new file mode 100644
index 0000000..52365f4
--- /dev/null
+++ b/seaudit/utilgui.h
@@ -0,0 +1,106 @@
+/**
+ * @file
+ * Miscellaneous helper functions for GTK+ applications.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef UTILGUI_H
+#define UTILGUI_H
+
+#include <apol/policy-path.h>
+#include <gtk/gtk.h>
+
+/**
+ * Pop-up a dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param parent Parent window; this message dialog will be centered
+ * upon the parent.
+ * @param msg_type Type of message being displayed.
+ * @param msg Text of message to display.
+ */
+void util_message(GtkWindow * parent, GtkMessageType msg_type, const char *msg);
+/**
+ * Set the cursor over a widget to the watch cursor.
+ *
+ * @param widget Widget whose cursor to set.
+ */
+void util_cursor_wait(GtkWidget * widget);
+
+/**
+ * Clear the cursor over a widget, setting it to the default arrow.
+ *
+ * @param widget Widget whose cursor to set.
+ */
+void util_cursor_clear(GtkWidget * widget);
+
+/**
+ * Allow the user select one or more existing files. Run the dialog
+ * and return a vector of selected filenames.
+ *
+ * @param parent Parent window; this dialog will be centered upon the
+ * parent.
+ * @param title Name of the dialog.
+ * @param init_path If not NULL, the default filename.
+ * @param multiple If true, allow the user to select multiple files.
+ * Otherwise only one file at a time may be chosen.
+ *
+ * @return Vector of filenames selected, or NULL if none were
+ * selected. The caller must call apol_vector_destroy() upon the
+ * returned value.
+ */
+apol_vector_t *util_open_file(GtkWindow * parent, const char *title, const char *init_path, gboolean multiple);
+
+/**
+ * Allow the user select an existing file or enter a new file for
+ * writing. Run the dialog and return the selected filename.
+ *
+ * @param parent Parent window; this dialog will be centered upon the
+ * parent.
+ * @param title Name of the dialog.
+ * @param init_path If not NULL, the default filename.
+ *
+ * @return Name of the file selected, or NULL if no file was selected.
+ * The caller must free the returned value with g_free().
+ */
+char *util_save_file(GtkWindow * parent, const char *title, const char *init_path);
+
+/**
+ * Given a policy path, return a newly allocated string that briefly
+ * describes the path. This string is suitable for showing to the
+ * user.
+ *
+ * @param path Policy path to describe.
+ *
+ * @return String describing the path, or NULL upon error. The caller
+ * must free the string afterwards.
+ */
+char *util_policy_path_to_string(const apol_policy_path_t * path);
+
+/**
+ * Get the active text from a GtkComboBox.
+ *
+ * Whereas GTK 2.6 has gtk_combo_box_get_active_text(), GTK 2.4
+ * (another supported platform) does not.
+ */
+const gchar *util_combo_box_get_active_text(GtkComboBox * w);
+
+#endif
diff --git a/sechecker/Makefile.am b/sechecker/Makefile.am
new file mode 100644
index 0000000..c7d8e7c
--- /dev/null
+++ b/sechecker/Makefile.am
@@ -0,0 +1,67 @@
+setoolsdir = @setoolsdir@
+bin_PROGRAMS = sechecker
+
+dist_setools_DATA = \
+ sechecker_help.txt
+
+profilesdir = @profile_install_dir@
+dist_profiles_DATA = \
+ profiles/all-checks.sechecker \
+ profiles/all-checks-no-mls.sechecker \
+ profiles/analysis-checks.sechecker \
+ profiles/devel-checks.sechecker \
+ profiles/sechecker.dtd
+
+dist_noinst_DATA = \
+ modules/template/profiles.readme \
+ modules/template/template.howto \
+ modules/template/xx.c \
+ modules/template/xx.h
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEFS_CFLAGS@ @XML_CFLAGS@ \
+ -DPROFILE_INSTALL_DIR='"${profile_install_dir}"'
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @SEFS_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ @XML_LIBS@ -lstdc++
+sechecker_DEPENDENCIES = \
+ $(top_builddir)/libsefs/src/libsefs.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libqpol/src/libqpol.so
+
+sechecker_SOURCES = \
+ sechecker.c sechecker.h \
+ register_list.c register_list.h \
+ sechk_parse.c sechk_parse.h \
+ sechecker_cli.c \
+ modules/attribs_wo_rules.c modules/attribs_wo_rules.h \
+ modules/attribs_wo_types.c modules/attribs_wo_types.h \
+ modules/domain_and_file.c modules/domain_and_file.h \
+ modules/domains_wo_roles.c modules/domains_wo_roles.h \
+ modules/find_assoc_types.c modules/find_assoc_types.h \
+ modules/find_domains.c modules/find_domains.h \
+ modules/find_file_types.c modules/find_file_types.h \
+ modules/find_net_domains.c modules/find_net_domains.h \
+ modules/find_netif_types.c modules/find_netif_types.h \
+ modules/find_node_types.c modules/find_node_types.h \
+ modules/find_port_types.c modules/find_port_types.h \
+ modules/imp_range_trans.c modules/imp_range_trans.h \
+ modules/inc_dom_trans.c modules/inc_dom_trans.h \
+ modules/inc_mount.c modules/inc_mount.h \
+ modules/inc_net_access.c modules/inc_net_access.h \
+ modules/roles_wo_allow.c modules/roles_wo_allow.h \
+ modules/roles_wo_types.c modules/roles_wo_types.h \
+ modules/roles_wo_users.c modules/roles_wo_users.h \
+ modules/spurious_audit.c modules/spurious_audit.h \
+ modules/types_wo_allow.c modules/types_wo_allow.h \
+ modules/unreachable_doms.c modules/unreachable_doms.h \
+ modules/users_wo_roles.c modules/users_wo_roles.h
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+$(top_builddir)/libsefs/src/libsefs.so:
+ $(MAKE) -C $(top_builddir)/libsefs/src $(notdir $@)
diff --git a/sechecker/modules/attribs_wo_rules.c b/sechecker/modules/attribs_wo_rules.c
new file mode 100644
index 0000000..4f2c00e
--- /dev/null
+++ b/sechecker/modules/attribs_wo_rules.c
@@ -0,0 +1,520 @@
+/**
+ * @file
+ * Implementation of the attributes without rules module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "attribs_wo_rules.h"
+#include <apol/type-query.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "attribs_wo_rules";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int attribs_wo_rules_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "attributes not used in any rule";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds attributes in the policy that are not used in any rules; These\n"
+ "attributes will get thrown out by the compiler and have no effect on the \n"
+ "security environment. They are unnecessary and should be removed.\n";
+ mod->opt_description =
+ "Module requirements:\n" " attribute names\n" "Module dependencies:\n" " none\n" "Module options:\n"
+ " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* assign requirements */
+ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES)) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_rules_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_rules_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_rules_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = &attribs_wo_rules_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int attribs_wo_rules_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data. */
+int attribs_wo_rules_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ apol_vector_t *attr_vector;
+ apol_role_query_t *role_query = NULL;
+ apol_avrule_query_t *avrule_query = NULL;
+ apol_terule_query_t *terule_query = NULL;
+ apol_vector_t *avrule_vector;
+ apol_vector_t *terule_vector;
+ apol_vector_t *role_vector;
+ qpol_iterator_t *constraint_iter;
+ qpol_iterator_t *node_iter = NULL;
+ qpol_iterator_t *name_iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int found = 0, error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ res->item_type = SECHK_ITEM_ATTRIB;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+
+ if (!(avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ if (!(terule_query = apol_terule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ if (!(role_query = apol_role_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+
+ apol_attr_get_by_query(policy, NULL, &attr_vector);
+ for (i = 0; i < apol_vector_get_size(attr_vector); i++) {
+ qpol_type_t *attr;
+ const char *attr_name;
+ attr = apol_vector_get_element(attr_vector, i);
+ qpol_type_get_name(q, attr, &attr_name);
+
+ /* access rules */
+ apol_avrule_query_set_source(policy, avrule_query, attr_name, 0);
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ if (apol_vector_get_size(avrule_vector) > 0) {
+ apol_vector_destroy(&avrule_vector);
+ continue;
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ apol_avrule_query_set_source(policy, avrule_query, NULL, 0);
+ apol_avrule_query_set_target(policy, avrule_query, attr_name, 0);
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ if (apol_vector_get_size(avrule_vector) > 0) {
+ apol_vector_destroy(&avrule_vector);
+ continue;
+ }
+ apol_avrule_query_set_target(policy, avrule_query, NULL, 0);
+ apol_vector_destroy(&avrule_vector);
+
+ /* type rules */
+ apol_terule_query_set_source(policy, terule_query, attr_name, 0);
+ apol_terule_get_by_query(policy, terule_query, &terule_vector);
+ if (apol_vector_get_size(terule_vector) > 0) {
+ apol_vector_destroy(&terule_vector);
+ continue;
+ }
+ apol_vector_destroy(&terule_vector);
+
+ apol_terule_query_set_source(policy, terule_query, NULL, 0);
+ apol_terule_query_set_target(policy, terule_query, attr_name, 0);
+ apol_terule_get_by_query(policy, terule_query, &terule_vector);
+ if (apol_vector_get_size(terule_vector) > 0) {
+ apol_vector_destroy(&terule_vector);
+ continue;
+ }
+ apol_terule_query_set_target(policy, terule_query, NULL, 0);
+ apol_vector_destroy(&terule_vector);
+
+ /* role trans */
+ apol_role_query_set_type(policy, role_query, attr_name);
+ apol_role_get_by_query(policy, role_query, &role_vector);
+ if (apol_vector_get_size(role_vector) > 0) {
+ apol_vector_destroy(&role_vector);
+ continue;
+ }
+ apol_vector_destroy(&role_vector);
+
+ /* Check constraints */
+ constraint_iter = NULL;
+ node_iter = NULL;
+ name_iter = NULL;
+ found = 0;
+ qpol_policy_get_constraint_iter(q, &constraint_iter);
+ for (; !qpol_iterator_end(constraint_iter); qpol_iterator_next(constraint_iter)) {
+ qpol_constraint_t *constraint;
+
+ qpol_iterator_get_item(constraint_iter, (void **)&constraint);
+ qpol_constraint_get_expr_iter(q, constraint, &node_iter);
+
+ for (; !qpol_iterator_end(node_iter); qpol_iterator_next(node_iter)) {
+ qpol_constraint_expr_node_t *constraint_node;
+ uint32_t node_type;
+
+ qpol_iterator_get_item(node_iter, (void **)&constraint_node);
+ qpol_constraint_expr_node_get_expr_type(q, constraint_node, &node_type);
+
+ if (node_type == QPOL_CEXPR_TYPE_NAMES) {
+ qpol_constraint_expr_node_get_names_iter(q, constraint_node, &name_iter);
+
+ for (; !qpol_iterator_end(name_iter); qpol_iterator_next(name_iter)) {
+ char *name;
+
+ qpol_iterator_get_item(name_iter, (void **)&name);
+ if (!strcmp(name, attr_name)) {
+ found = 1;
+ free(name);
+ name = NULL;
+ break;
+ }
+ free(name);
+ name = NULL;
+ }
+ qpol_iterator_destroy(&name_iter);
+ if (found)
+ break;
+ }
+ }
+ qpol_iterator_destroy(&node_iter);
+ free(constraint);
+ if (found)
+ break;
+ }
+ qpol_iterator_destroy(&constraint_iter);
+ if (found)
+ continue;
+
+ /* if we get here then the attrib was not found anywhere in a rule so add it */
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)attr;
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ proof->type = SECHK_ITEM_ATTRIB;
+ proof->text = strdup("attribute was not used in any rules.");
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_rules_run_fail;
+ }
+ }
+ apol_avrule_query_destroy(&avrule_query);
+ apol_role_query_destroy(&role_query);
+ apol_terule_query_destroy(&terule_query);
+ apol_vector_destroy(&attr_vector);
+
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ attribs_wo_rules_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int attribs_wo_rules_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd attributes.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following attributes do not appear in any rules.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+ type = NULL;
+ type_name = NULL;
+
+ return 0;
+}
+
+int attribs_wo_rules_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/attribs_wo_rules.h b/sechecker/modules/attribs_wo_rules.h
new file mode 100644
index 0000000..e2869d5
--- /dev/null
+++ b/sechecker/modules/attribs_wo_rules.h
@@ -0,0 +1,63 @@
+/**
+ * @file
+ * Defines the interface for the attributes without rules module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ATTRIBS_WO_RULES
+#define ATTRIBS_WO_RULES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/role-query.h>
+#include <apol/avrule-query.h>
+#include <apol/terule-query.h>
+#include <apol/type-query.h>
+
+/* Module functions:
+ * Do not change any of these prototypes or you will not be
+ * able to run the module in the library
+ * NOTE: while using a modular format SEChecker is built
+ * statically; this means that all modules and their functions
+ * are in the same namespace. */
+ int attribs_wo_rules_register(sechk_lib_t * lib);
+ int attribs_wo_rules_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int attribs_wo_rules_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int attribs_wo_rules_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+/* NOTE: While SEChecker is build statically, it is
+ * intended that no module directly call a function
+ * from another but instead use get_module_function()
+ * to get the desired function from the library. */
+
+ int attribs_wo_rules_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/attribs_wo_types.c b/sechecker/modules/attribs_wo_types.c
new file mode 100644
index 0000000..edac010
--- /dev/null
+++ b/sechecker/modules/attribs_wo_types.c
@@ -0,0 +1,399 @@
+/**
+ * @file
+ * Implementation of the attributes without types module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "attribs_wo_types.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "attribs_wo_types";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int attribs_wo_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "attributes with no types";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds attributes in the policy that are not associated with any\n"
+ "types. Attributes without types can cause type fields in rules to expand to\n"
+ "empty sets and thus become unreachable. This makes for misleading policy source\n" "files.\n";
+ mod->opt_description =
+ "Module requirements:\n" " attribute names\n" "Module dependencies:\n" " none\n" "Module options:\n"
+ " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* assign requirements */
+ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES)) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_types_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_types_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_types_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = attribs_wo_types_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int attribs_wo_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+int attribs_wo_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ apol_vector_t *attr_vector = NULL;
+ qpol_iterator_t *types = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+ res->item_type = SECHK_ITEM_ATTRIB;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+
+ apol_attr_get_by_query(policy, NULL, &attr_vector);
+ for (i = 0; i < apol_vector_get_size(attr_vector); i++) {
+ qpol_type_t *attr;
+ const char *attr_name;
+ int at_end;
+
+ attr = apol_vector_get_element(attr_vector, i);
+ qpol_type_get_name(q, attr, &attr_name);
+ qpol_type_get_type_iter(q, attr, &types);
+ at_end = qpol_iterator_end(types);
+ qpol_iterator_destroy(&types);
+ if (!at_end)
+ continue;
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_ATTRIB;
+ proof->text = (char *)calloc(strlen("attribute has no types") + strlen(attr_name) + 1, sizeof(char));
+ sprintf(proof->text, "attribute %s has no types", attr_name);
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+ }
+ item->item = (void *)attr;
+ item->test_result = 1;
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto attribs_wo_types_run_fail;
+ }
+ }
+ qpol_iterator_destroy(&types);
+ apol_vector_destroy(&attr_vector);
+
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ attribs_wo_types_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int attribs_wo_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ /* display the statistics of the results */
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd attributes.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following attributes are not associated with any types.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+ type = NULL;
+ type_name = NULL;
+
+ return 0;
+}
+
+int attribs_wo_types_get_list(sechk_module_t * mod, apol_policy_t * polciy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/attribs_wo_types.h b/sechecker/modules/attribs_wo_types.h
new file mode 100644
index 0000000..da11801
--- /dev/null
+++ b/sechecker/modules/attribs_wo_types.h
@@ -0,0 +1,48 @@
+/**
+ * @file
+ * Defines the interface for the attributes without types module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ATTRIBS_WO_TYPES
+#define ATTRIBS_WO_TYPES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/type-query.h>
+
+ int attribs_wo_types_register(sechk_lib_t * lib);
+ int attribs_wo_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int attribs_wo_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int attribs_wo_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int attribs_wo_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/domain_and_file.c b/sechecker/modules/domain_and_file.c
new file mode 100644
index 0000000..418e2a5
--- /dev/null
+++ b/sechecker/modules/domain_and_file.c
@@ -0,0 +1,398 @@
+/**
+ * @file
+ * Implementation of the domain and file type module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "domain_and_file.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "domain_and_file";
+
+int domain_and_file_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+ sechk_name_value_t *nv = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign descriptions */
+ mod->brief_description = "types treated as a domain and file type";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all types in the policy treated as both a domain and a file \n"
+ "type. See find_domains and find_file_types modules for details about the \n"
+ "heuristics used to determine these types. It is considered bad security\n"
+ "practice to use the same type for a domain and its data objects because it \n"
+ "requires that less restrictive access be granted to these types.\n";
+ mod->opt_description = "Module requirements:\n" " attribute names\n"
+ " file_contexts\n"
+ "Module dependencies:\n" " find_domains module\n" " find_file_types module\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* assign requirements */
+ nv = sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES);
+ apol_vector_append(mod->requirements, (void *)nv);
+ nv = sechk_name_value_new(SECHK_REQ_FILE_CONTEXTS, NULL);
+ apol_vector_append(mod->requirements, (void *)nv);
+
+ /* assign dependencies */
+ apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_domains"));
+ apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_file_types"));
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = &domain_and_file_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = domain_and_file_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = domain_and_file_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+int domain_and_file_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+int domain_and_file_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ sechk_result_t *domain_res = NULL, *file_type_res = NULL;
+ size_t i, j, k;
+ sechk_mod_fn_t run_fn = NULL;
+ sechk_name_value_t *dep = NULL;
+ apol_vector_t *domain_vector;
+ apol_vector_t *type_vector;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+
+ /* run dependencies */
+ for (i = 0; i < apol_vector_get_size(mod->dependencies); i++) {
+ dep = apol_vector_get_element(mod->dependencies, i);
+ run_fn = sechk_lib_get_module_function(dep->value, SECHK_MOD_FN_RUN, mod->parent_lib);
+ run_fn(sechk_lib_get_module(dep->value, mod->parent_lib), policy, NULL);
+ }
+
+ /* get results */
+ domain_res = sechk_lib_get_module_result("find_domains", mod->parent_lib);
+ if (!domain_res) {
+ error = errno;
+ ERR(policy, "%s", "Unable to get results for module find_domains");
+ goto domain_and_file_run_fail;
+ }
+ file_type_res = sechk_lib_get_module_result("find_file_types", mod->parent_lib);
+ if (!file_type_res) {
+ error = errno;
+ ERR(policy, "%s", "Unable to get results for module find_file_types");
+ goto domain_and_file_run_fail;
+ }
+
+ /* get lists */
+ domain_vector = (apol_vector_t *) domain_res->items;
+ type_vector = (apol_vector_t *) file_type_res->items;
+
+ /* build the both list */
+ for (i = 0; i < apol_vector_get_size(type_vector); i++) {
+ sechk_item_t *type_item;
+ qpol_type_t *type;
+ const char *type_name;
+
+ type_item = apol_vector_get_element(type_vector, i);
+ type = type_item->item;
+ qpol_type_get_name(q, type, &type_name);
+ for (j = 0; j < apol_vector_get_size(domain_vector); j++) {
+ sechk_item_t *domain_item;
+ qpol_type_t *domain;
+ const char *domain_name;
+
+ domain_item = apol_vector_get_element(domain_vector, j);
+ domain = domain_item->item;
+ qpol_type_get_name(q, domain, &domain_name);
+ if (!strcmp(domain_name, type_name)) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ item->item = (void *)domain;
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ for (k = 0; k < apol_vector_get_size(domain_item->proof); k++) {
+ sechk_proof_t *domain_proof;
+
+ domain_proof = apol_vector_get_element(domain_item->proof, k);
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ proof->type = SECHK_ITEM_TYPE;
+ proof->text = strdup(domain_proof->text);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ }
+ for (k = 0; k < apol_vector_get_size(type_item->proof); k++) {
+ sechk_proof_t *type_proof;
+
+ type_proof = apol_vector_get_element(type_item->proof, k);
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ proof->type = SECHK_ITEM_TYPE;
+ proof->text = strdup(type_proof->text);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domain_and_file_run_fail;
+ }
+ }
+ }
+ }
+
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ domain_and_file_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+int domain_and_file_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd types.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/domain_and_file.h b/sechecker/modules/domain_and_file.h
new file mode 100644
index 0000000..81b82c1
--- /dev/null
+++ b/sechecker/modules/domain_and_file.h
@@ -0,0 +1,46 @@
+/**
+ * @file
+ * Defines the interface for the domain and file type module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DOMAIN_AND_FILE
+#define DOMAIN_AND_FILE
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+
+ int domain_and_file_register(sechk_lib_t * lib);
+ int domain_and_file_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int domain_and_file_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int domain_and_file_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/domains_wo_roles.c b/sechecker/modules/domains_wo_roles.c
new file mode 100644
index 0000000..38b42a8
--- /dev/null
+++ b/sechecker/modules/domains_wo_roles.c
@@ -0,0 +1,386 @@
+/**
+ * @file
+ * Implementation of the domains without roles module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "domains_wo_roles.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "domains_wo_roles";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int domains_wo_roles_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "domains with no roles";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all domains in the policy not associated with a role. These \n"
+ "domains cannot have a valid security context. The object_r role is not \n" "considered in this check.\n";
+ mod->opt_description =
+ "Module requirements:\n"
+ " attribute names\n" "Module dependencies:\n" " find_domains\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_MED;
+ /* assign requirements */
+ apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES));
+
+ /* assign dependencies */
+ apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_domains"));
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = domains_wo_roles_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = domains_wo_roles_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = domains_wo_roles_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int domains_wo_roles_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. */
+int domains_wo_roles_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ int retv, error;
+ size_t i;
+ sechk_module_t *mod_ptr = NULL;
+ sechk_mod_fn_t run_fn = NULL;
+ apol_vector_t *domain_vector;
+ apol_vector_t *role_vector;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ apol_role_query_t *role_query = NULL;
+ sechk_result_t *find_domains_res = NULL;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "Error: %s\n", strerror(error));
+ goto domains_wo_roles_run_fail;
+ }
+
+ run_fn = sechk_lib_get_module_function("find_domains", SECHK_MOD_FN_RUN, mod->parent_lib);
+ if (!run_fn) {
+ error = errno;
+ ERR(policy, "%s", "Unable to find run function for module find_domains");
+ goto domains_wo_roles_run_fail;
+ }
+
+ retv = run_fn((mod_ptr = sechk_lib_get_module("find_domains", mod->parent_lib)), policy, NULL);
+ if (retv) {
+ error = errno;
+ ERR(policy, "%s", "Unable to run module find_domains");
+ goto domains_wo_roles_run_fail;
+ }
+
+ if (!(find_domains_res = sechk_lib_get_module_result("find_domains", mod->parent_lib))) {
+ error = errno;
+ ERR(policy, "%s", "Unable to get results for module find_domains");
+ goto domains_wo_roles_run_fail;
+ }
+
+ domain_vector = (apol_vector_t *) find_domains_res->items;
+
+ if (!(role_query = apol_role_query_create())) {
+ error = errno;
+ ERR(policy, "Error: %s\n", strerror(error));
+ goto domains_wo_roles_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(domain_vector); i++) {
+ qpol_type_t *domain;
+ const char *domain_name;
+
+ item = apol_vector_get_element(domain_vector, i);
+ domain = item->item;
+ qpol_type_get_name(q, domain, &domain_name);
+
+ apol_role_query_set_type(policy, role_query, domain_name);
+ apol_role_get_by_query(policy, role_query, &role_vector);
+ if (apol_vector_get_size(role_vector) > 0) {
+ apol_vector_destroy(&role_vector);
+ continue;
+ }
+ apol_vector_destroy(&role_vector);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->text = strdup("Domain has no role.\n");
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+
+ }
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+ }
+ item->item = (void *)domain;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(NULL, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(NULL, "%s", strerror(ENOMEM));
+ goto domains_wo_roles_run_fail;
+ }
+ }
+ apol_role_query_destroy(&role_query);
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ domains_wo_roles_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int domains_wo_roles_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd types.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following types are domains but not associated with any roles.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & (SECHK_OUT_LIST)) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+ type = NULL;
+ type_name = NULL;
+
+ return 0;
+}
diff --git a/sechecker/modules/domains_wo_roles.h b/sechecker/modules/domains_wo_roles.h
new file mode 100644
index 0000000..e46a9b3
--- /dev/null
+++ b/sechecker/modules/domains_wo_roles.h
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Defines the interface for the domains without roles module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DOMAINS_WO_ROLES
+#define DOMAINS_WO_ROLES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/role-query.h>
+
+ int domains_wo_roles_register(sechk_lib_t * lib);
+ int domains_wo_roles_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int domains_wo_roles_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int domains_wo_roles_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_assoc_types.c b/sechecker/modules/find_assoc_types.c
new file mode 100644
index 0000000..0e72801
--- /dev/null
+++ b/sechecker/modules/find_assoc_types.c
@@ -0,0 +1,407 @@
+/**
+ * @file
+ * Implementation of the association types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_assoc_types.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_assoc_types";
+
+int find_assoc_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds types with an unlabeled initial sid. \n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_NONE;
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_assoc_types_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_assoc_types_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_assoc_types_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_assoc_types_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int find_assoc_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)\n", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int find_assoc_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ char *buff = NULL;
+ size_t buff_sz = 0;
+ const qpol_isid_t *isid;
+ const char *type_name = NULL;
+ const qpol_type_t *type;
+ const qpol_context_t *context;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto find_assoc_types_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+
+ /* Initialize vectors */
+
+ qpol_policy_get_isid_by_name(q, "unlabeled", &isid);
+ if (!isid) {
+ error = errno;
+ goto find_assoc_types_run_fail;
+ }
+
+ if (apol_str_append(&buff, &buff_sz, "sid unlabeled ") != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+
+ qpol_isid_get_context(q, isid, &context);
+ qpol_context_get_type(q, context, &type);
+ qpol_type_get_name(q, type, &type_name);
+
+ if (apol_str_append(&buff, &buff_sz, type_name) != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+ }
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+
+ item->test_result = 1;
+ item->item = (void *)type;
+ proof->type = SECHK_ITEM_TYPE;
+ proof->text = buff;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_assoc_types_run_fail;
+ }
+
+ mod->result = res;
+
+ return 0;
+
+ find_assoc_types_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ free(buff);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text and prints the
+ * results to stdout. */
+int find_assoc_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, num_items;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Error: wrong module (%s)\n", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd assoc type(s).\n", num_items);
+ }
+ /* The list reassoc component is a display of all items
+ * found without any supassocing proof. The default method
+ * is to display a comma separated list four items to a line
+ * this may need to be changed for longer items. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (j = 0; j < num_items; j++) {
+ i++;
+ i %= 4;
+ item = apol_vector_get_element(mod->result->items, j);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ if (item)
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+ /* The proof reassoc component is a display of a list of items
+ * with an indented list of proof statements supassocing the result
+ * of the check for that item (e.g. rules with a given type)
+ * this field also lists the computed severity of each item
+ * (see sechk_item_sev in sechecker.c for details on calculation)
+ * items are printed on a line either with (or, if long, such as a
+ * rule, followed by) the severity. Each proof element is then
+ * displayed in an indented list one per line below it. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (j = 0; j < apol_vector_get_size(mod->result->items); j++) {
+ item = apol_vector_get_element(mod->result->items, j);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", type_name);
+ for (k = 0; k < apol_vector_get_size(item->proof); k++) {
+ proof = apol_vector_get_element(item->proof, k);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+ type = NULL;
+ type_name = NULL;
+
+ return 0;
+}
+
+int find_assoc_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/find_assoc_types.h b/sechecker/modules/find_assoc_types.h
new file mode 100644
index 0000000..23cda10
--- /dev/null
+++ b/sechecker/modules/find_assoc_types.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ * Defines the interface for the association types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_ASSOC_TYPES
+#define FIND_ASSOC_TYPES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+
+/* Module functions:
+ * Do not change any of these prototypes or you will not be
+ * able to run the module in the library */
+ int find_assoc_types_register(sechk_lib_t * lib);
+ int find_assoc_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_assoc_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_assoc_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_assoc_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_domains.c b/sechecker/modules/find_domains.c
new file mode 100644
index 0000000..b59abac
--- /dev/null
+++ b/sechecker/modules/find_domains.c
@@ -0,0 +1,633 @@
+/**
+ * @file
+ * Implementation of the find domains utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_domains.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_domains";
+
+int find_domains_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign descriptions */
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This is a utility module which finds types in a policy that are treated as a \n"
+ "domain. A type is considered a domain if any of the following is true:\n"
+ "\n"
+ " 1) it has an attribute associated with domains\n"
+ " 2) it is the source of a TE rule for object class other than filesystem\n"
+ " 3) it is the default type in a type_transition rule for object class process \n"
+ " 4) it is associated with a role other than object_r\n";
+ mod->opt_description =
+ "Module requirements:\n"
+ " attribute names\n"
+ "Module dependencies:\n" " none\n" "Module options:\n" " domain_attributes can be set in a profile\n";
+ mod->severity = SECHK_SEV_NONE;
+ /* assign requirements */
+ apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES));
+
+ /* assign options */
+ apol_vector_append(mod->options, sechk_name_value_new("domain_attribute", "domain"));
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_domains_init;
+ apol_vector_append(mod->functions, fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_domains_run;
+ apol_vector_append(mod->functions, fn_struct);
+
+ mod->data_free = find_domains_data_free;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_domains_print;
+ apol_vector_append(mod->functions, fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_domains_get_list;
+ apol_vector_append(mod->functions, fn_struct);
+
+ return 0;
+}
+
+int find_domains_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_name_value_t *opt = NULL;
+ find_domains_data_t *datum = NULL;
+ size_t i, j;
+ qpol_type_t *attr = NULL;
+ apol_vector_t *attr_vector = NULL;
+ apol_attr_query_t *attr_query = apol_attr_query_create();
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = find_domains_data_new();
+ if (!datum) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!(datum->domain_attribs = apol_vector_create(NULL))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data = datum;
+
+ for (i = 0; i < apol_vector_get_size(mod->options); i++) {
+ opt = apol_vector_get_element(mod->options, i);
+ if (!strcmp(opt->name, "domain_attribute")) {
+ apol_attr_query_set_attr(policy, attr_query, opt->value);
+ apol_attr_get_by_query(policy, attr_query, &attr_vector);
+ for (j = 0; j < apol_vector_get_size(attr_vector); j++) {
+ const char *domain_attrib;
+ attr = apol_vector_get_element(attr_vector, j);
+ qpol_type_get_name(q, attr, &domain_attrib);
+ if (apol_vector_append(datum->domain_attribs, (void *)domain_attrib) < 0) {
+ apol_vector_destroy(&attr_vector);
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+
+ }
+ }
+ apol_vector_destroy(&attr_vector);
+ }
+ }
+ apol_attr_query_destroy(&attr_query);
+ return 0;
+}
+
+int find_domains_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ int error = 0;
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ const char *type_name = NULL;
+ find_domains_data_t *datum = NULL;
+ size_t i, j, proof_idx;
+ apol_vector_t *domain_vector = NULL, *avrule_vector = NULL, *terule_vector = NULL, *role_vector = NULL;
+ apol_terule_query_t *terule_query = NULL;
+ apol_avrule_query_t *avrule_query = NULL;
+ apol_role_query_t *role_query = NULL;
+ qpol_iterator_t *domain_attr_iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ datum = (find_domains_data_t *) mod->data;
+ res = sechk_result_new();
+ if (!res) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = error;
+ return -1;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+
+ if (apol_type_get_by_query(policy, NULL, &domain_vector) < 0) {
+ goto find_domains_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(domain_vector); i++) {
+ qpol_type_t *type = apol_vector_get_element(domain_vector, i);
+ qpol_type_get_name(q, type, &type_name);
+
+ if (qpol_type_get_attr_iter(q, type, &domain_attr_iter) < 0) {
+ error = errno;
+ ERR(policy, "Can't get attributes for type %s", type_name);
+ goto find_domains_run_fail;
+ }
+
+ for (; !qpol_iterator_end(domain_attr_iter); qpol_iterator_next(domain_attr_iter)) {
+ const char *attr_name;
+ const qpol_type_t *attr;
+ size_t nfta;
+
+ qpol_iterator_get_item(domain_attr_iter, (void **)&attr);
+ qpol_type_get_name(q, attr, &attr_name);
+ for (nfta = 0; nfta < apol_vector_get_size(datum->domain_attribs); nfta++) {
+ const char *domain_attrib;
+
+ domain_attrib = apol_vector_get_element(datum->domain_attribs, nfta);
+ if (!strcmp(attr_name, domain_attrib)) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ proof->type = SECHK_ITEM_ATTRIB;
+ proof->elem = (void *)attr;
+ asprintf(&proof->text, "%s has attribute %s", type_name, attr_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ }
+ }
+ qpol_iterator_destroy(&domain_attr_iter);
+
+ /* rule src check !filesystem associate */
+ if (!(avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ apol_avrule_query_set_source(policy, avrule_query, type_name, 0);
+ if (apol_avrule_get_by_query(policy, avrule_query, &avrule_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", "Unable to retrieve AV rules");
+ goto find_domains_run_fail;
+ }
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ const qpol_avrule_t *avrule = NULL;
+ const qpol_class_t *class = NULL;
+ const char *class_name = NULL;
+
+ avrule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_object_class(q, avrule, &class);
+ qpol_class_get_name(q, class, &class_name);
+ if (strcmp("filesystem", class_name)) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ proof->type = SECHK_ITEM_AVRULE;
+ proof->text = apol_avrule_render(policy, avrule);
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ }
+ apol_vector_destroy(&avrule_vector);
+ apol_avrule_query_destroy(&avrule_query);
+
+ /* type rule check file object */
+ if (!(terule_query = apol_terule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ apol_terule_query_set_default(policy, terule_query, type_name);
+ apol_terule_query_append_class(policy, terule_query, "process");
+ if (apol_terule_get_by_query(policy, terule_query, &terule_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", "Unable to retrieve TE rules");
+ goto find_domains_run_fail;
+ }
+ for (j = 0; j < apol_vector_get_size(terule_vector); j++) {
+ qpol_terule_t *terule = NULL;
+ terule = apol_vector_get_element(terule_vector, j);
+
+ if (apol_vector_get_index(item->proof, terule, sechk_proof_with_element_compare, NULL, &proof_idx) == 0)
+ continue;
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ proof->type = SECHK_ITEM_TERULE;
+ proof->elem = terule;
+ proof->text = apol_terule_render(policy, terule);
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ apol_vector_destroy(&terule_vector);
+ apol_terule_query_destroy(&terule_query);
+
+ /* Check Roles */
+ if (!(role_query = apol_role_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ apol_role_query_set_type(policy, role_query, type_name);
+ apol_role_get_by_query(policy, role_query, &role_vector);
+ for (j = 0; j < apol_vector_get_size(role_vector); j++) {
+ const qpol_role_t *role;
+ const char *role_name;
+
+ role = (qpol_role_t *) apol_vector_get_element(role_vector, j);
+ qpol_role_get_name(q, role, &role_name);
+ if (!strcmp("object_r", role_name))
+ continue;
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ proof->type = SECHK_ITEM_ROLE;
+ proof->elem = (void *)role;
+ asprintf(&proof->text, "role %s types %s;", role_name, type_name);
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "Error: %s\n", strerror(error));
+ goto find_domains_run_fail;
+ }
+ }
+ apol_vector_destroy(&role_vector);
+ apol_role_query_destroy(&role_query);
+
+ /* insert any results for this type */
+ if (item) {
+ item->item = type;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_domains_run_fail;
+ }
+ }
+ item = NULL;
+ type = NULL;
+ type_name = NULL;
+ }
+ apol_vector_destroy(&domain_vector);
+
+ /* results are valid at this point */
+ mod->result = res;
+ return 0;
+
+ find_domains_run_fail:
+ qpol_iterator_destroy(&domain_attr_iter);
+ apol_vector_destroy(&domain_vector);
+ apol_vector_destroy(&avrule_vector);
+ apol_vector_destroy(&terule_vector);
+ apol_vector_destroy(&role_vector);
+ apol_avrule_query_destroy(&avrule_query);
+ apol_terule_query_destroy(&terule_query);
+ apol_role_query_destroy(&role_query);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+void find_domains_data_free(void *data)
+{
+ find_domains_data_t *datum = (find_domains_data_t *) data;
+
+ if (datum) {
+ apol_vector_destroy(&datum->domain_attribs);
+ }
+ free(data);
+}
+
+int find_domains_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ find_domains_data_t *datum = NULL;
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ sechk_proof_t *proof = NULL;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp("find_domains", mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = (find_domains_data_t *) mod->data;
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET)) {
+ return 0; /* not an error - no output is requested */
+ }
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i domain types.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following types are domains.\n");
+ }
+
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int find_domains_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
+
+find_domains_data_t *find_domains_data_new(void)
+{
+ find_domains_data_t *datum = NULL;
+
+ datum = (find_domains_data_t *) calloc(1, sizeof(find_domains_data_t));
+
+ return datum;
+}
diff --git a/sechecker/modules/find_domains.h b/sechecker/modules/find_domains.h
new file mode 100644
index 0000000..917cd41
--- /dev/null
+++ b/sechecker/modules/find_domains.h
@@ -0,0 +1,60 @@
+/**
+ * @file
+ * Defines the interface for the find domains utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_DOMAINS
+#define FIND_DOMAINS
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/type-query.h>
+#include <apol/role-query.h>
+#include <apol/terule-query.h>
+#include <apol/avrule-query.h>
+
+ typedef struct find_domains_data
+ {
+ apol_vector_t *domain_attribs;
+ int num_domain_attribs;
+ } find_domains_data_t;
+
+ void find_domains_data_free(void *data);
+ find_domains_data_t *find_domains_data_new(void);
+
+ int find_domains_register(sechk_lib_t * lib);
+ int find_domains_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_domains_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_domains_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_domains_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_file_types.c b/sechecker/modules/find_file_types.c
new file mode 100644
index 0000000..193e3f3
--- /dev/null
+++ b/sechecker/modules/find_file_types.c
@@ -0,0 +1,636 @@
+/**
+ * @file
+ * Implementation of the find file types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_file_types.h"
+
+#include <qpol/genfscon_query.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_file_types";
+
+int find_file_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+ sechk_name_value_t *nv = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all types in the policy treated as a file type. A type is \n"
+ "considered a file type if any of the following is true:\n"
+ "\n"
+ " 1) it has an attribute associated with file types\n"
+ " 2) it is the source of a rule to allow filesystem associate permission\n"
+ " 3) it is the default type of a type transition rule with an object class\n" " other than process\n"
+ " 4) it is specified in a context in the file_contexts file\n";
+ mod->opt_description = "Module requirements:\n" " attribute names\n"
+ " file_contexts\n"
+ "Module dependencies:\n" " none\n" "Module options:\n" " file_type_attribute can be modified in a profile\n";
+ mod->severity = SECHK_SEV_NONE;
+ /* assign requirements */
+ nv = sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES);
+ apol_vector_append(mod->requirements, (void *)nv);
+ nv = sechk_name_value_new(SECHK_REQ_FILE_CONTEXTS, NULL);
+ apol_vector_append(mod->requirements, (void *)nv);
+
+ /* assign options */
+ nv = sechk_name_value_new("file_type_attribute", "file_type");
+ apol_vector_append(mod->options, (void *)nv);
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_file_types_init;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_file_types_run;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ mod->data_free = find_file_types_data_free;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_file_types_print;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_file_types_get_list;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ return 0;
+}
+
+int find_file_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_name_value_t *opt = NULL;
+ find_file_types_data_t *datum = NULL;
+ apol_vector_t *attr_vector = NULL;
+ apol_attr_query_t *attr_query = apol_attr_query_create();
+ qpol_type_t *attr = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i = 0, j = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = find_file_types_data_new();
+ if (!datum) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ if (!(datum->file_type_attribs = apol_vector_create(NULL))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ mod->data = datum;
+
+ for (i = 0; i < apol_vector_get_size(mod->options); i++) {
+ opt = apol_vector_get_element(mod->options, i);
+ if (!strcmp(opt->name, "file_type_attribute")) {
+ apol_attr_query_set_attr(policy, attr_query, opt->value);
+ apol_attr_get_by_query(policy, attr_query, &attr_vector);
+ for (j = 0; j < apol_vector_get_size(attr_vector); j++) {
+ const char *file_attrib;
+ attr = apol_vector_get_element(attr_vector, j);
+ qpol_type_get_name(q, attr, &file_attrib);
+ if (apol_vector_append(datum->file_type_attribs, (void *)file_attrib) < 0) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ apol_vector_destroy(&attr_vector);
+ }
+ }
+ apol_attr_query_destroy(&attr_query);
+ return 0;
+}
+
+int find_file_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ find_file_types_data_t *datum;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ sechk_result_t *res = NULL;
+ const char *type_name = NULL;
+ apol_avrule_query_t *avrule_query = NULL;
+ apol_terule_query_t *terule_query = NULL;
+ apol_vector_t *avrule_vector = NULL;
+ apol_vector_t *terule_vector = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i, j, x;
+ char *buff = NULL;
+ int error = 0;
+
+ /* NEW */
+ size_t num_fc_entries = 0;
+ apol_vector_t *type_vector = NULL;
+ apol_vector_t *fc_entry_vector = NULL;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ datum = (find_file_types_data_t *) mod->data;
+ res->item_type = SECHK_ITEM_TYPE;
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ if (mod->parent_lib->fc_entries) {
+ if (mod->parent_lib->fc_path) {
+ fc_entry_vector = mod->parent_lib->fc_entries;
+ num_fc_entries = apol_vector_get_size(fc_entry_vector);
+ } else {
+ error = ENOENT;
+ ERR(policy, "%s", "Unable to find file contexts file");
+ goto find_file_types_run_fail;
+ }
+ }
+
+ /* Get an iterator for the types */
+ if (apol_type_get_by_query(policy, NULL, &type_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", "Unable to retrieve types");
+ return -1;
+ }
+
+ for (i = 0; i < apol_vector_get_size(type_vector); i++) {
+ qpol_iterator_t *file_attr_iter;
+
+ const qpol_type_t *type = apol_vector_get_element(type_vector, i);
+ qpol_type_get_name(q, type, &type_name);
+
+ if (qpol_type_get_attr_iter(q, type, &file_attr_iter) < 0) {
+ error = errno;
+ ERR(policy, "Could not get attributes for %s\n", type_name);
+ goto find_file_types_run_fail;
+ }
+
+ for (; !qpol_iterator_end(file_attr_iter); qpol_iterator_next(file_attr_iter)) {
+ const char *attr_name;
+ const qpol_type_t *attr;
+ size_t nfta;
+
+ qpol_iterator_get_item(file_attr_iter, (void **)&attr);
+ qpol_type_get_name(q, attr, &attr_name);
+ for (nfta = 0; nfta < apol_vector_get_size(datum->file_type_attribs); nfta++) {
+ const char *file_type_attrib;
+
+ file_type_attrib = apol_vector_get_element(datum->file_type_attribs, nfta);
+ if (!strcmp(attr_name, file_type_attrib)) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_ATTRIB;
+ proof->elem = (void *)attr;
+ asprintf(&proof->text, "has attribute %s", attr_name);
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ }
+ }
+ qpol_iterator_destroy(&file_attr_iter);
+
+ /* rule src check filesystem associate */
+ if (!(avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", "Could not retrieve AV rules");
+ goto find_file_types_run_fail;
+ }
+ apol_avrule_query_set_source(policy, avrule_query, type_name, 0);
+ apol_avrule_query_append_class(policy, avrule_query, "filesystem");
+ apol_avrule_query_append_perm(policy, avrule_query, "associate");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (x = 0; x < apol_vector_get_size(avrule_vector); x++) {
+ qpol_avrule_t *avrule;
+ avrule = apol_vector_get_element(avrule_vector, x);
+ if (avrule) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_AVRULE;
+ proof->elem = avrule;
+ proof->text = apol_avrule_render(policy, avrule);
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ item->test_result = 1;
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ }
+ apol_vector_destroy(&avrule_vector);
+ apol_avrule_query_destroy(&avrule_query);
+
+ /* type rule check file object */
+ if (!(terule_query = apol_terule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", "Could not retrieve TE rules");
+ goto find_file_types_run_fail;
+ }
+ apol_terule_query_set_default(policy, terule_query, type_name);
+ apol_terule_get_by_query(policy, terule_query, &terule_vector);
+ for (x = 0; x < apol_vector_get_size(terule_vector); x++) {
+ const qpol_terule_t *terule;
+ const qpol_class_t *objclass;
+ const char *class_name;
+
+ terule = apol_vector_get_element(terule_vector, x);
+ qpol_terule_get_object_class(q, terule, &objclass);
+ qpol_class_get_name(q, objclass, &class_name);
+ if (strcmp(class_name, "process")) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_TERULE;
+ proof->elem = (void *)terule;
+ proof->text = apol_terule_render(policy, terule);
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ item->test_result = 1;
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ }
+ apol_vector_destroy(&terule_vector);
+ apol_terule_query_destroy(&terule_query);
+
+ /* assigned in fc check */
+ if (fc_entry_vector) {
+ buff = NULL;
+ for (j = 0; j < num_fc_entries; j++) {
+ sefs_entry_t *fc_entry;
+ const char *fc_type_name = NULL;
+ fc_entry = apol_vector_get_element(fc_entry_vector, j);
+ const apol_context_t *context = sefs_entry_get_context(fc_entry);
+ if (!context)
+ continue;
+ fc_type_name = apol_context_get_type(context);
+ if (!strcmp(type_name, fc_type_name)) {
+ buff = sefs_entry_to_string(fc_entry);
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_FCENT;
+ proof->elem = fc_entry;
+ proof->text = buff;
+ buff = NULL;
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ }
+ }
+ /* insert any results for this type */
+ if (item) {
+ item->item = (void *)type;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_file_types_run_fail;
+ }
+ }
+ item = NULL;
+ type = NULL;
+ type_name = NULL;
+ }
+ apol_vector_destroy(&type_vector);
+
+ /* results are valid at this point */
+ mod->result = res;
+
+ return 0;
+
+ find_file_types_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ free(buff);
+ apol_vector_destroy(&type_vector);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+void find_file_types_data_free(void *data)
+{
+ find_file_types_data_t *datum = (find_file_types_data_t *) data;
+
+ if (datum) {
+ apol_vector_destroy(&datum->file_type_attribs);
+ }
+ free(data);
+}
+
+int find_file_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ find_file_types_data_t *datum = NULL;
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items = 0;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = (find_file_types_data_t *) mod->data;
+ outformat = mod->outputformat;
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+ if (outformat & SECHK_OUT_STATS) {
+ num_items = apol_vector_get_size(mod->result->items);
+ printf("Found %zd file types.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following types are file types.\n\n");
+ }
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < apol_vector_get_size(mod->result->items); i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < apol_vector_get_size(mod->result->items); k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int find_file_types_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg
+ __attribute__ ((unused)))
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
+
+find_file_types_data_t *find_file_types_data_new(void)
+{
+ find_file_types_data_t *datum = NULL;
+
+ datum = (find_file_types_data_t *) calloc(1, sizeof(find_file_types_data_t));
+
+ return datum;
+}
diff --git a/sechecker/modules/find_file_types.h b/sechecker/modules/find_file_types.h
new file mode 100644
index 0000000..941862b
--- /dev/null
+++ b/sechecker/modules/find_file_types.h
@@ -0,0 +1,60 @@
+/**
+ * @file
+ * Defines the interface for the find file types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_FILE_TYPES
+#define FIND_FILE_TYPES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/type-query.h>
+#include <apol/avrule-query.h>
+#include <apol/terule-query.h>
+
+ typedef struct find_file_types_data
+ {
+ apol_vector_t *file_type_attribs;
+ int num_file_type_attribs;
+ } find_file_types_data_t;
+
+ void find_file_types_data_free(void *data);
+ find_file_types_data_t *find_file_types_data_new(void);
+
+ int find_file_types_register(sechk_lib_t * lib);
+ int find_file_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_file_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_file_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_file_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_net_domains.c b/sechecker/modules/find_net_domains.c
new file mode 100644
index 0000000..dadd459
--- /dev/null
+++ b/sechecker/modules/find_net_domains.c
@@ -0,0 +1,501 @@
+/**
+ * @file
+ * Implementation of the network domain utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_net_domains.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_net_domains";
+
+int find_net_domains_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all types in a policy considered to be network domains. \n"
+ "A type is considered a network domain if it is the subject of TE rules \n"
+ "involving certain object classes, which are currently defined as:\n"
+ " 1) netif\n"
+ " 2) tcp_socket\n"
+ " 3) udp_socket\n"
+ " 4) node\n" " 5) association\n" "These values can be overridden in this module's profile.";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n"
+ " net_obj\n";
+ mod->severity = SECHK_SEV_NONE;
+
+ /* assign default options */
+ apol_vector_append(mod->options, sechk_name_value_new("net_obj", "netif"));
+ apol_vector_append(mod->options, sechk_name_value_new("net_obj", "tcp_socket"));
+ apol_vector_append(mod->options, sechk_name_value_new("net_obj", "udp_socket"));
+ apol_vector_append(mod->options, sechk_name_value_new("net_obj", "node"));
+ apol_vector_append(mod->options, sechk_name_value_new("net_obj", "association"));
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_net_domains_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_net_domains_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = find_net_domains_data_free;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_net_domains_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_net_domains_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int find_net_domains_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_name_value_t *opt = NULL;
+ find_net_domains_data_t *datum = NULL;
+ size_t i;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = find_net_domains_data_new();
+ if (!datum) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!(datum->net_objs = apol_vector_create(NULL))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data = datum;
+
+ for (i = 0; i < apol_vector_get_size(mod->options); i++) {
+ opt = apol_vector_get_element(mod->options, i);
+ if (!strcmp(opt->name, "net_obj")) {
+ if (apol_vector_append(datum->net_objs, (void *)opt->value) < 0) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int find_net_domains_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ find_net_domains_data_t *datum;
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ int error = 0;
+ size_t i = 0, j = 0, k = 0;
+ apol_vector_t *avrule_vector;
+ apol_avrule_query_t *avrule_query = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ datum = (find_net_domains_data_t *) mod->data;
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+
+ if (!(avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ apol_avrule_query_set_rules(policy, avrule_query, QPOL_RULE_ALLOW);
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (k = 0; k < apol_vector_get_size(avrule_vector); k++) {
+ const qpol_avrule_t *avrule;
+ const qpol_class_t *class;
+ const char *class_name;
+
+ avrule = apol_vector_get_element(avrule_vector, k);
+ qpol_avrule_get_object_class(q, avrule, &class);
+ qpol_class_get_name(q, class, &class_name);
+ for (i = 0; i < apol_vector_get_size(datum->net_objs); i++) {
+ const char *net_obj_name;
+
+ net_obj_name = apol_vector_get_element(datum->net_objs, i);
+ if (!strcmp(class_name, net_obj_name)) {
+ const qpol_type_t *source;
+ const char *source_name;
+
+ qpol_avrule_get_source_type(q, avrule, &source);
+ qpol_type_get_name(q, source, &source_name);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ proof->type = SECHK_ITEM_AVRULE;
+ proof->elem = (void *)avrule;
+ proof->text = apol_avrule_render(policy, avrule);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ item = NULL;
+
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item = NULL;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(res_type_name, source_name))
+ item = res_item;
+ }
+
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)source;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_net_domains_run_fail;
+ }
+ item = NULL;
+ }
+ }
+ }
+ apol_avrule_query_destroy(&avrule_query);
+ apol_vector_destroy(&avrule_vector);
+ mod->result = res;
+
+ return 0;
+
+ find_net_domains_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The free function frees the private data of a module */
+void find_net_domains_data_free(void *data)
+{
+ find_net_domains_data_t *d = data;
+ apol_vector_destroy(&d->net_objs);
+ free(data);
+}
+
+/* The print function generates the text and prints the
+ * results to stdout. */
+int find_net_domains_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ find_net_domains_data_t *datum = NULL;
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = (find_net_domains_data_t *) mod->data;
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("\nFound %i network domains.", num_items);
+ }
+
+ /* The list renode component is a display of all items
+ * found without any supnodeing proof. The default method
+ * is to display a comma separated list four items to a line
+ * this may need to be changed for longer items. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ /* The proof renode component is a display of a list of items
+ * with an indented list of proof statements supnodeing the result
+ * of the check for that item (e.g. rules with a given type)
+ * this field also lists the computed severity of each item
+ * (see sechk_item_sev in sechecker.c for details on calculation)
+ * items are printed on a line either with (or, if long, such as a
+ * rule, followed by) the severity. Each proof element is then
+ * displayed in an indented list one per line below it. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int find_net_domains_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
+
+/* The find_net_domains_data_new function allocates and returns an
+ * initialized private data storage structure for this
+ * module. Initialization expected is as follows:
+ * all arrays (including strings) are initialized to NULL
+ * array sizes are set to 0
+ * any other pointers should be NULL
+ * indices into other arrays (such as type or permission indices)
+ * should be initialized to -1
+ * any other data should be initialized as needed by the check logic */
+find_net_domains_data_t *find_net_domains_data_new(void)
+{
+ find_net_domains_data_t *datum = NULL;
+
+ datum = (find_net_domains_data_t *) calloc(1, sizeof(find_net_domains_data_t));
+
+ return datum;
+}
diff --git a/sechecker/modules/find_net_domains.h b/sechecker/modules/find_net_domains.h
new file mode 100644
index 0000000..68eeb75
--- /dev/null
+++ b/sechecker/modules/find_net_domains.h
@@ -0,0 +1,59 @@
+/**
+ * @file
+ * Defines the interface for the network domain utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_NET_DOMAINS
+#define FIND_NET_DOMAINS
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/avrule-query.h>
+
+/* The find_net_domains_data structure is used to hold the check specific
+ * private data of a module. */
+ typedef struct find_net_domains_data
+ {
+ apol_vector_t *net_objs;
+ } find_net_domains_data_t;
+
+ find_net_domains_data_t *find_net_domains_data_new(void);
+ void find_net_domains_data_free(void *data);
+
+ int find_net_domains_register(sechk_lib_t * lib);
+ int find_net_domains_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_net_domains_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_net_domains_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_net_domains_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_netif_types.c b/sechecker/modules/find_netif_types.c
new file mode 100644
index 0000000..e703687
--- /dev/null
+++ b/sechecker/modules/find_netif_types.c
@@ -0,0 +1,489 @@
+/**
+ * @file
+ * Implementation of the netif types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_netif_types.h"
+#include <apol/netcon-query.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_netif_types";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int find_netif_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all types in a policy treated as a netif type. A type is\n"
+ "a netif type if it is used in the internace context in a netifcon statement or\n"
+ "the context of the netif initial sid.\n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_NONE;
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_netif_types_init;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_netif_types_run;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_netif_types_print;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_netif_types_get_list;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int find_netif_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int find_netif_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ char *buff = NULL;
+ size_t i, buff_sz = 0;
+ apol_vector_t *netifcon_vector;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+
+ /* search initial SIDs */
+ const qpol_isid_t *isid = NULL;
+
+ buff = NULL;
+ qpol_policy_get_isid_by_name(q, "netif", &isid);
+ if (isid) {
+ const qpol_context_t *context;
+ apol_context_t *a_context;
+ const qpol_type_t *context_type;
+ const char *context_type_name;
+ char *tmp;
+
+ proof = NULL;
+ qpol_isid_get_context(q, isid, &context);
+ qpol_context_get_type(q, context, &context_type);
+ qpol_type_get_name(q, context_type, &context_type_name);
+ a_context = apol_context_create_from_qpol_context(policy, context);
+
+ if (apol_str_append(&buff, &buff_sz, "sid netif ") != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ apol_context_destroy(&a_context);
+ goto find_netif_types_run_fail;
+ }
+
+ tmp = apol_context_render(policy, a_context);
+ if (apol_str_append(&buff, &buff_sz, tmp) != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ free(tmp);
+ apol_context_destroy(&a_context);
+ goto find_netif_types_run_fail;
+ }
+ apol_context_destroy(&a_context);
+ free(tmp);
+ tmp = NULL;
+
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ item->test_result = 1;
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+
+ proof->type = SECHK_ITEM_ISID;
+ proof->elem = (void *)isid;
+ proof->text = buff;
+
+ item->item = (void *)context_type;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ }
+
+ if (apol_netifcon_get_by_query(policy, NULL, &netifcon_vector) < 0) {
+ error = errno;
+ goto find_netif_types_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(netifcon_vector); i++) {
+ const char *msg_con_name = NULL;
+ const char *if_con_name = NULL;
+ qpol_netifcon_t *netifcon = NULL;
+ const qpol_context_t *msg_con = NULL;
+ const qpol_context_t *if_con = NULL;
+ const qpol_type_t *msg_type = NULL;
+ const qpol_type_t *if_type = NULL;
+ size_t j = 0;
+
+ netifcon = apol_vector_get_element(netifcon_vector, i);
+ qpol_netifcon_get_msg_con(q, netifcon, &msg_con);
+ qpol_netifcon_get_if_con(q, netifcon, &if_con);
+ qpol_context_get_type(q, msg_con, &msg_type);
+ qpol_context_get_type(q, if_con, &if_type);
+ qpol_type_get_name(q, msg_type, &msg_con_name);
+ qpol_type_get_name(q, if_type, &if_con_name);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_NETIFCON;
+ proof->elem = netifcon;
+ proof->text = apol_netifcon_render(policy, netifcon);
+ item = NULL;
+
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item = NULL;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = (qpol_type_t *) res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(res_type_name, if_con_name))
+ item = res_item;
+ }
+
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)if_type;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_netif_types_run_fail;
+ }
+ item = NULL;
+ }
+ apol_vector_destroy(&netifcon_vector);
+
+ mod->result = res;
+
+ return 0;
+
+ find_netif_types_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ free(buff);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text and prints the
+ * results to stdout. The outline below prints
+ * the standard format of a renetif section. Some modules may
+ * not have results in a format that can be represented by this
+ * outline and will need a different specification. It is
+ * required that each of the flags for output components be
+ * tested in this function (stats, list, proof, detailed, and brief) */
+int find_netif_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, num_items = 0;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i netif types.\n", num_items);
+ }
+
+ /* The list renetif component is a display of all items
+ * found without any supnetifing proof. The default method
+ * is to display a comma separated list four items to a line
+ * this may need to be changed for longer items. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ /* The proof renetif component is a display of a list of items
+ * with an indented list of proof statements supnetifing the result
+ * of the check for that item (e.g. rules with a given type)
+ * this field also lists the computed severity of each item
+ * (see sechk_item_sev in sechecker.c for details on calculation)
+ * items are printed on a line either with (or, if long, such as a
+ * rule, followed by) the severity. Each proof element is then
+ * displayed in an indented list one per line below it. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (j = 0; j < num_items; j++) {
+ item = apol_vector_get_element(mod->result->items, j);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ if (item) {
+ printf("%s\n", type_name);
+ for (k = 0; k < apol_vector_get_size(item->proof); k++) {
+ proof = apol_vector_get_element(item->proof, k);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int find_netif_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/find_netif_types.h b/sechecker/modules/find_netif_types.h
new file mode 100644
index 0000000..859ee8a
--- /dev/null
+++ b/sechecker/modules/find_netif_types.h
@@ -0,0 +1,53 @@
+/**
+ * @file
+ * Defines the interface for the netif types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_NETIF_TYPES_H
+#define FIND_NETIF_TYPES_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/context-query.h>
+#include <apol/netcon-query.h>
+
+/* Module functions:
+ * Do not change any of these prototypes or you will not be
+ * able to run the module in the library */
+ int find_netif_types_register(sechk_lib_t * lib);
+ int find_netif_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_netif_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_netif_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_netif_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_node_types.c b/sechecker/modules/find_node_types.c
new file mode 100644
index 0000000..f10be10
--- /dev/null
+++ b/sechecker/modules/find_node_types.c
@@ -0,0 +1,479 @@
+/**
+ * @file
+ * Implementation of the node types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_node_types.h"
+#include <apol/netcon-query.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_node_types";
+
+/* The register function registers all of a module's functions
+ * with the library. You should not need to edit this function
+ * unless you are adding additional functions you need other modules
+ * to call. See the note at the bottom of this function to do so. */
+int find_node_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Unknown module");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+
+ /* assign the descriptions */
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all types in a policy treated as a node type. A type is\n"
+ "considered a node type if it is used in the context of a nodecon statement or\n"
+ "the context of the node initial sid.\n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_NONE;
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_node_types_init;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_node_types_run;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_node_types_print;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_node_types_get_list;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file.
+ * Add any option processing logic as indicated below. */
+int find_node_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int find_node_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ char *buff = NULL;
+ size_t i, buff_sz = 0;
+ apol_vector_t *nodecon_vector = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+
+ /* search initial SIDs */
+ const qpol_isid_t *isid = NULL;
+
+ buff = NULL;
+ qpol_policy_get_isid_by_name(q, "node", &isid);
+ if (isid) {
+ const qpol_context_t *context;
+ apol_context_t *a_context;
+ const qpol_type_t *context_type;
+ const char *context_type_name;
+ char *tmp;
+
+ proof = NULL;
+ qpol_isid_get_context(q, isid, &context);
+ qpol_context_get_type(q, context, &context_type);
+ qpol_type_get_name(q, context_type, &context_type_name);
+ a_context = apol_context_create_from_qpol_context(policy, context);
+
+ if (apol_str_append(&buff, &buff_sz, "sid node ") != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ apol_context_destroy(&a_context);
+ goto find_node_types_run_fail;
+ }
+
+ tmp = apol_context_render(policy, a_context);
+ if (apol_str_append(&buff, &buff_sz, tmp) != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ apol_context_destroy(&a_context);
+ free(tmp);
+ goto find_node_types_run_fail;
+ }
+ apol_context_destroy(&a_context);
+ free(tmp);
+ tmp = NULL;
+
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ item->test_result = 1;
+ }
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+
+ proof->type = SECHK_ITEM_ISID;
+ proof->elem = (void *)isid;
+ proof->text = buff;
+
+ item->item = (void *)context_type;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ }
+
+ if (apol_nodecon_get_by_query(policy, NULL, &nodecon_vector) < 0) {
+ error = errno;
+ goto find_node_types_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(nodecon_vector); i++) {
+ const char *type_name;
+ size_t j;
+ const qpol_context_t *context;
+ const qpol_type_t *context_type;
+ qpol_nodecon_t *nodecon = apol_vector_get_element(nodecon_vector, i);
+ qpol_nodecon_get_context(q, nodecon, &context);
+ qpol_context_get_type(q, context, &context_type);
+ qpol_type_get_name(q, context_type, &type_name);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->text = apol_nodecon_render(policy, nodecon);
+
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(res_type_name, type_name))
+ item = res_item;
+ }
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)context_type;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_node_types_run_fail;
+ }
+ item = NULL;
+ }
+ apol_vector_destroy(&nodecon_vector);
+
+ mod->result = res;
+
+ return 0;
+
+ find_node_types_run_fail:
+ apol_vector_destroy(&nodecon_vector);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ free(buff);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text and prints the results to stdout. */
+int find_node_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, num_items = 0;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i node types.\n", num_items);
+ }
+
+ /* The list renode component is a display of all items
+ * found without any supnodeing proof. The default method
+ * is to display a comma separated list four items to a line
+ * this may need to be changed for longer items. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ /* The proof renode component is a display of a list of items
+ * with an indented list of proof statements supnodeing the result
+ * of the check for that item (e.g. rules with a given type)
+ * this field also lists the computed severity of each item
+ * (see sechk_item_sev in sechecker.c for details on calculation)
+ * items are printed on a line either with (or, if long, such as a
+ * rule, followed by) the severity. Each proof element is then
+ * displayed in an indented list one per line below it. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (j = 0; j < num_items; j++) {
+ item = apol_vector_get_element(mod->result->items, j);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ if (item) {
+ printf("%s\n", type_name);
+ for (k = 0; k < apol_vector_get_size(item->proof); k++) {
+ proof = apol_vector_get_element(item->proof, k);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int find_node_types_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/find_node_types.h b/sechecker/modules/find_node_types.h
new file mode 100644
index 0000000..44ac749
--- /dev/null
+++ b/sechecker/modules/find_node_types.h
@@ -0,0 +1,53 @@
+/**
+ * @file
+ * Defines the interface for the node types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_NODE_TYPES_H
+#define FINE_NODE_TYPES_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/context-query.h>
+#include <apol/netcon-query.h>
+
+/* Module functions:
+ * Do not change any of these prototypes or you will not be
+ * able to run the module in the library */
+ int find_node_types_register(sechk_lib_t * lib);
+ int find_node_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_node_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_node_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_node_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/find_port_types.c b/sechecker/modules/find_port_types.c
new file mode 100644
index 0000000..8e55bc3
--- /dev/null
+++ b/sechecker/modules/find_port_types.c
@@ -0,0 +1,513 @@
+/**
+ * @file
+ * Implementation of the port types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "find_port_types.h"
+#include <apol/netcon-query.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "find_port_types";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int find_port_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "utility module";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all types in a policy treated as a port type. A type is\n"
+ "considered a port type if it is used in the context of a a portcon statement or\n"
+ "the context of the port initial sid.\n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_NONE;
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_port_types_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_port_types_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = find_port_types_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = &find_port_types_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file.
+ * Add any option processing logic as indicated below. */
+int find_port_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int find_port_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ char *buff = NULL;
+ size_t buff_sz = 0, i, j;
+ apol_vector_t *portcon_vector;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+
+ /* search initial SIDs */
+ const qpol_isid_t *isid = NULL;
+ buff = NULL;
+ qpol_policy_get_isid_by_name(q, "port", &isid);
+ if (isid) {
+ const qpol_context_t *context;
+ apol_context_t *a_context;
+ const qpol_type_t *context_type;
+ const char *context_type_name;
+ char *tmp;
+
+ proof = NULL;
+ qpol_isid_get_context(q, isid, &context);
+ qpol_context_get_type(q, context, &context_type);
+ qpol_type_get_name(q, context_type, &context_type_name);
+ a_context = apol_context_create_from_qpol_context(policy, context);
+
+ if (apol_str_append(&buff, &buff_sz, "sid port ") != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ apol_context_destroy(&a_context);
+ goto find_port_types_run_fail;
+ }
+
+ tmp = apol_context_render(policy, a_context);
+ if (apol_str_append(&buff, &buff_sz, tmp) != 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ apol_context_destroy(&a_context);
+ free(tmp);
+ goto find_port_types_run_fail;
+ }
+ free(tmp);
+ tmp = NULL;
+ apol_context_destroy(&a_context);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+
+ proof->type = SECHK_ITEM_ISID;
+ proof->elem = (void *)isid;
+ proof->text = buff;
+
+ /* Have we encountered this type before? If so, use that type. */
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item = NULL;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(res_type_name, context_type_name))
+ item = res_item;
+ }
+
+ /* We have not encountered this type yet */
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)context_type;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ }
+
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ }
+
+ if (apol_portcon_get_by_query(policy, NULL, &portcon_vector) < 0) {
+ error = errno;
+ goto find_port_types_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(portcon_vector); i++) {
+ const char *portcon_name = NULL;
+ const qpol_portcon_t *portcon = NULL;
+ const qpol_context_t *portcon_context = NULL;
+ const qpol_type_t *portcon_type = NULL;
+
+ portcon = apol_vector_get_element(portcon_vector, i);
+ qpol_portcon_get_context(q, portcon, &portcon_context);
+ qpol_context_get_type(q, portcon_context, &portcon_type);
+ qpol_type_get_name(q, portcon_type, &portcon_name);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_PORTCON;
+ proof->elem = (void *)portcon;
+ proof->text = apol_portcon_render(policy, portcon);
+ item = NULL;
+
+ /* Have we encountered this type before? If so, use that type. */
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item = NULL;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(res_type_name, portcon_name))
+ item = res_item;
+ }
+
+ /* We have not encountered this type yet */
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)portcon_type;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ }
+
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto find_port_types_run_fail;
+ }
+ item = NULL;
+ }
+ apol_vector_destroy(&portcon_vector);
+
+ mod->result = res;
+
+ return 0;
+
+ find_port_types_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ free(buff);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text and prints the
+ * results to stdout. */
+int find_port_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, num_items = 0;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i port types.\n", num_items);
+ }
+
+ /* The list report component is a display of all items
+ * found without any supporting proof. The default method
+ * is to display a comma separated list four items to a line
+ * this may need to be changed for longer items. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ /* The proof report component is a display of a list of items
+ * with an indented list of proof statements supporting the result
+ * of the check for that item (e.g. rules with a given type)
+ * this field also lists the computed severity of each item
+ * (see sechk_item_sev in sechecker.c for details on calculation)
+ * items are printed on a line either with (or, if long, such as a
+ * rule, followed by) the severity. Each proof element is then
+ * displayed in an indented list one per line below it. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (j = 0; j < num_items; j++) {
+ item = apol_vector_get_element(mod->result->items, j);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ if (item) {
+ printf("%s\n", type_name);
+ for (k = 0; k < apol_vector_get_size(item->proof); k++) {
+ proof = apol_vector_get_element(item->proof, k);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int find_port_types_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/find_port_types.h b/sechecker/modules/find_port_types.h
new file mode 100644
index 0000000..adb19bf
--- /dev/null
+++ b/sechecker/modules/find_port_types.h
@@ -0,0 +1,50 @@
+/**
+ * @file
+ * Defines the interface for the port types utility module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_PORT_TYPES
+#define FIND_PORT_TYPES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/context-query.h>
+#include <apol/netcon-query.h>
+
+ int find_port_types_register(sechk_lib_t * lib);
+ int find_port_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_port_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_port_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int find_port_types_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/imp_range_trans.c b/sechecker/modules/imp_range_trans.c
new file mode 100644
index 0000000..8fbf361
--- /dev/null
+++ b/sechecker/modules/imp_range_trans.c
@@ -0,0 +1,513 @@
+/**
+ * @file
+ * Implementation of the impossible range_transition module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author: David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "imp_range_trans.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#define SECHK_NO_ROLES 0x000002
+#define SECHK_BAD_USER_MLS_LOW 0x000040
+#define SECHK_BAD_USER_MLS_HIGH 0x000600
+#define SECHK_NO_USERS 0x008000
+#define SECHK_NO_EXEC_PERMS 0x020000
+
+static const char *const mod_name = "imp_range_trans";
+
+int imp_range_trans_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "finds impossible range transitions";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds impossible range transitions in a policy.\n"
+ "A range transition is possible if and only if all of the following conditions\n"
+ "are satisfied:\n"
+ " 1) there exist TE rules allowing the range transition to occur\n"
+ " 2) there exist RBAC rules allowing the range transition to occur\n"
+ " 3) at least one user must be able to transition to the target MLS range\n";
+ mod->opt_description =
+ " Module requirements:\n" " MLS policy\n" " Module dependencies:\n" " none\n" " Module options:\n"
+ " none\n";
+ mod->severity = SECHK_SEV_MED;
+
+ /* assign requirements */
+ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_MLS)) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = imp_range_trans_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = imp_range_trans_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = imp_range_trans_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int imp_range_trans_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int imp_range_trans_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i, j;
+ apol_vector_t *range_trans_vector = NULL, *role_vector = NULL, *tmp_v = NULL;
+ apol_vector_t *user_vector = NULL, *users_w_roles = NULL, *users_w_range = NULL;
+ apol_vector_t *rule_vector = NULL;
+ const qpol_range_trans_t *rule;
+ const qpol_type_t *source = NULL;
+ const qpol_type_t *target = NULL;
+ const qpol_role_t *role = NULL;
+ const char *source_name = NULL, *target_name = NULL, *role_name = NULL;
+ apol_role_query_t *role_query = NULL;
+ apol_user_query_t *user_query = NULL;
+ apol_avrule_query_t *avrule_query = NULL;
+ apol_mls_range_t *range;
+ const qpol_mls_range_t *qpol_range;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ res->item_type = SECHK_ITEM_RANGETRANS;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+
+ if (apol_range_trans_get_by_query(policy, NULL, &range_trans_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", "Unable to retrieve range transitions");
+ goto imp_range_trans_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(range_trans_vector); i++) {
+ /* collect information about the rule */
+ rule = apol_vector_get_element(range_trans_vector, i);
+ qpol_range_trans_get_source_type(q, rule, &source);
+ qpol_range_trans_get_target_type(q, rule, &target);
+ qpol_type_get_name(q, source, &source_name);
+ qpol_type_get_name(q, target, &target_name);
+ qpol_range_trans_get_range(q, rule, &qpol_range);
+ range = apol_mls_range_create_from_qpol_mls_range(policy, qpol_range);
+
+ /* find roles possible for source */
+ role_query = apol_role_query_create();
+ apol_role_query_set_type(policy, role_query, source_name);
+ apol_role_get_by_query(policy, role_query, &role_vector);
+ apol_role_query_destroy(&role_query);
+
+ /* find users with the possible roles */
+ if ((users_w_roles = apol_vector_create(NULL)) == NULL) {
+ error = errno;
+ goto imp_range_trans_run_fail;
+ }
+ user_query = apol_user_query_create();
+ for (j = 0; j < apol_vector_get_size(role_vector); j++) {
+ role = apol_vector_get_element(role_vector, j);
+ qpol_role_get_name(q, role, &role_name);
+ apol_user_query_set_role(policy, user_query, role_name);
+ apol_user_get_by_query(policy, user_query, &tmp_v);
+ apol_vector_cat(users_w_roles, tmp_v);
+ apol_vector_destroy(&tmp_v);
+ }
+ apol_vector_sort_uniquify(users_w_roles, NULL, NULL);
+ apol_user_query_destroy(&user_query);
+
+ /* find users with the transition range */
+ user_query = apol_user_query_create();
+ apol_user_query_set_range(policy, user_query, range, APOL_QUERY_SUB);
+ apol_user_get_by_query(policy, user_query, &users_w_range);
+ apol_user_query_destroy(&user_query);
+
+ /* find intersection of user sets */
+ user_vector = apol_vector_create_from_intersection(users_w_roles, users_w_range, NULL, NULL);
+
+ /* find avrules for allow <source> <target> : file execute; */
+ avrule_query = apol_avrule_query_create();
+ apol_avrule_query_set_rules(policy, avrule_query, QPOL_RULE_ALLOW);
+ apol_avrule_query_set_source(policy, avrule_query, source_name, 1);
+ apol_avrule_query_set_target(policy, avrule_query, target_name, 1);
+ apol_avrule_query_append_class(policy, avrule_query, "file");
+ apol_avrule_query_append_perm(policy, avrule_query, "execute");
+ apol_avrule_get_by_query(policy, avrule_query, &rule_vector);
+ apol_avrule_query_destroy(&avrule_query);
+
+ /* check avrules */
+ if (!apol_vector_get_size(rule_vector)) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto imp_range_trans_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ asprintf(&proof->text, "Missing: allow %s %s : file execute;", source_name, target_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ item = sechk_item_new(NULL);
+ if (!item) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto imp_range_trans_run_fail;
+ }
+ item->item = (void *)rule;
+ item->test_result = 1;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ proof = NULL;
+ }
+ apol_vector_destroy(&rule_vector);
+
+ /* check RBAC */
+ if (!apol_vector_get_size(role_vector)) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto imp_range_trans_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ asprintf(&proof->text, "No role associated with type %s", source_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto imp_range_trans_run_fail;
+ }
+ item->item = (void *)rule;
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ proof = NULL;
+ }
+
+ /* check users */
+ if (!apol_vector_get_size(user_vector)) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto imp_range_trans_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ if (!apol_vector_get_size(role_vector)) {
+ proof->text = strdup("No role also means no user");
+ } else if (!apol_vector_get_size(users_w_roles)) {
+ asprintf(&proof->text, "No users associated with roles for %s", source_name);
+ } else if (!apol_vector_get_size(users_w_range)) {
+ proof->text = strdup("No user has access to specified MLS range");
+ } else {
+ proof->text = strdup("No user meets MLS and RBAC requirements.");
+ }
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ error = ENOMEM;
+ goto imp_range_trans_run_fail;
+ }
+ item->item = (void *)rule;
+ item->test_result = 1;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto imp_range_trans_run_fail;
+ }
+ }
+ apol_vector_destroy(&role_vector);
+ apol_vector_destroy(&user_vector);
+ apol_vector_destroy(&users_w_roles);
+ apol_vector_destroy(&users_w_range);
+
+ if (item) {
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto imp_range_trans_run_fail;
+ }
+ }
+ item = NULL;
+ }
+ apol_vector_destroy(&range_trans_vector);
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ imp_range_trans_run_fail:
+ apol_vector_destroy(&range_trans_vector);
+ apol_vector_destroy(&role_vector);
+ apol_vector_destroy(&rule_vector);
+ apol_vector_destroy(&user_vector);
+ apol_vector_destroy(&users_w_roles);
+ apol_vector_destroy(&users_w_range);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text and prints the
+ * results to stdout. */
+int imp_range_trans_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ qpol_range_trans_t *rt;
+ char *tmp;
+ size_t i = 0, k = 0, j = 0, num_items;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i impossible range transitions.\n", num_items);
+ }
+
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ item = apol_vector_get_element(mod->result->items, i);
+ rt = item->item;
+ printf("%s\n", (tmp = apol_range_trans_render(policy, rt)));
+ free(tmp);
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ rt = item->item;
+ printf("%s\n", (tmp = apol_range_trans_render(policy, rt)));
+ free(tmp);
+ for (j = 0; j < apol_vector_get_size(item->proof); j++) {
+ proof = apol_vector_get_element(item->proof, j);
+ printf("\t%s\n", proof->text);
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/imp_range_trans.h b/sechecker/modules/imp_range_trans.h
new file mode 100644
index 0000000..04ceba0
--- /dev/null
+++ b/sechecker/modules/imp_range_trans.h
@@ -0,0 +1,52 @@
+/**
+ * @file
+ * Defines the interface for the impossible range_transition module.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author: David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef IMP_RANGE_TRANS
+#define IMP_RANGE_TRANS
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/role-query.h>
+#include <apol/user-query.h>
+#include <apol/range_trans-query.h>
+#include <apol/rbacrule-query.h>
+#include <apol/domain-trans-analysis.h>
+#include <apol/policy-query.h>
+
+ int imp_range_trans_register(sechk_lib_t * lib);
+ int imp_range_trans_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int imp_range_trans_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int imp_range_trans_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IMP_RANGE_TRANS */
diff --git a/sechecker/modules/inc_dom_trans.c b/sechecker/modules/inc_dom_trans.c
new file mode 100644
index 0000000..fe6957d
--- /dev/null
+++ b/sechecker/modules/inc_dom_trans.c
@@ -0,0 +1,913 @@
+/**
+ * @file
+ * Defines the interface for the incomplete domain transition module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "inc_dom_trans.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "inc_dom_trans";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int inc_dom_trans_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "domains with partial transition permissions";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds potential domain transitions missing key permissions. A valid\n"
+ "domain transition requires the following.\n"
+ "\n"
+ " 1) the starting domain can transition to the end domain for class process\n"
+ " 2) the end domain has some type as an entrypoint\n"
+ " 3) the starting domain can execute that extrypoint type\n"
+ " 4) (optional) a type transition rules specifying these three types\n";
+ mod->opt_description =
+ "Module requirements:\n"
+ " attribute names\n" "Module dependencies:\n" " find_domains\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_MED;
+ /* assign requirements */
+ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES)) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ /* Dependencies */
+ apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_domains"));
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_dom_trans_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_dom_trans_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_dom_trans_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int inc_dom_trans_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid paramaters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* wrapper for apol_domain_trans_result_destroy() */
+void dtr_free_wrap(void *x)
+{
+ apol_domain_trans_result_destroy((apol_domain_trans_result_t **) & x);
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. */
+int inc_dom_trans_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL, *tmp_item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i, j, k, retv;
+ sechk_module_t *mod_ptr = NULL;
+ sechk_mod_fn_t run_fn = NULL;
+ sechk_result_t *find_domains_res = NULL;
+ apol_domain_trans_analysis_t *domain_trans = NULL;
+ apol_vector_t *domain_vector = NULL, *role_vector = NULL, *user_vector = NULL, *rbac_vector = NULL;
+ apol_vector_t *domain_trans_vector = NULL, *role_allow_vector = NULL;
+ apol_user_query_t *user_query = NULL;
+ apol_role_query_t *start_role_query = NULL, *end_role_query = NULL;
+ apol_role_trans_query_t *role_trans_query = NULL;
+ apol_role_allow_query_t *role_allow_query = NULL;
+ char *buff = NULL;
+ int buff_sz, error = 0;
+ const qpol_type_t *domain = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *domain_name = NULL;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = EINVAL;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ res->item_type = SECHK_ITEM_DTR;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (apol_policy_build_domain_trans_table(policy) < 0) {
+ error = errno;
+ ERR(policy, "%s", "Unable to build domain transition table");
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(domain_trans = apol_domain_trans_analysis_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(user_query = apol_user_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(role_trans_query = apol_role_trans_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(role_allow_query = apol_role_allow_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(start_role_query = apol_role_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(end_role_query = apol_role_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ run_fn = sechk_lib_get_module_function("find_domains", SECHK_MOD_FN_RUN, mod->parent_lib);
+ if (!run_fn) {
+ error = errno;
+ goto inc_dom_trans_run_fail;
+ }
+
+ retv = run_fn((mod_ptr = sechk_lib_get_module("find_domains", mod->parent_lib)), policy, NULL);
+ if (retv) {
+ error = errno;
+ ERR(policy, "%s", "Unable to find module find_domains");
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (!(find_domains_res = sechk_lib_get_module_result("find_domains", mod->parent_lib))) {
+ error = errno;
+ ERR(policy, "%s", "Unable to get results from module find_domains");
+ goto inc_dom_trans_run_fail;
+ }
+
+ domain_vector = (apol_vector_t *) find_domains_res->items;
+
+ for (i = 0; i < apol_vector_get_size(domain_vector); i++) {
+ tmp_item = apol_vector_get_element(domain_vector, i);
+ domain = tmp_item->item;
+ qpol_type_get_name(q, domain, &domain_name);
+ apol_domain_trans_analysis_set_start_type(policy, domain_trans, domain_name);
+ apol_domain_trans_analysis_set_direction(policy, domain_trans, APOL_DOMAIN_TRANS_DIRECTION_FORWARD);
+ apol_domain_trans_analysis_set_valid(policy, domain_trans, APOL_DOMAIN_TRANS_SEARCH_BOTH);
+ apol_domain_trans_analysis_do(policy, domain_trans, &domain_trans_vector);
+
+ for (j = 0; j < apol_vector_get_size(domain_trans_vector); j++) {
+ apol_domain_trans_result_t *dtr = NULL;
+ const qpol_type_t *start;
+ const qpol_type_t *ep;
+ const qpol_type_t *end;
+ const char *start_name;
+ const char *end_name;
+ const char *ep_name;
+ int result;
+ bool ok;
+
+ ok = false;
+ dtr = apol_vector_get_element(domain_trans_vector, j);
+ start = apol_domain_trans_result_get_start_type(dtr);
+ ep = apol_domain_trans_result_get_entrypoint_type(dtr);
+ end = apol_domain_trans_result_get_end_type(dtr);
+ if (start)
+ qpol_type_get_name(q, start, &start_name);
+ else
+ start_name = "<start_type>";
+ if (end)
+ qpol_type_get_name(q, end, &end_name);
+ else
+ end_name = "<end_type>";
+ if (ep)
+ qpol_type_get_name(q, ep, &ep_name);
+ else
+ ep_name = "<entrypoint_type>";
+
+ result = apol_domain_trans_table_verify_trans(policy, start, ep, end);
+ if (!result) {
+ apol_vector_t *start_role_vector, *end_role_vector, *common_role_vector;
+ apol_role_query_set_type(policy, start_role_query, start_name);
+ apol_role_get_by_query(policy, start_role_query, &start_role_vector);
+ apol_role_query_set_type(policy, end_role_query, end_name);
+ apol_role_get_by_query(policy, end_role_query, &end_role_vector);
+ if (!apol_vector_get_size(start_role_vector) || !apol_vector_get_size(end_role_vector)) {
+ item = sechk_item_new(dtr_free_wrap);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)apol_domain_trans_result_create_from_domain_trans_result(dtr);
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (!apol_vector_get_size(start_role_vector)) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ asprintf(&proof->text, "Need role for %s", start_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ if (!apol_vector_get_size(end_role_vector)) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ asprintf(&proof->text, "Need role for %s", end_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ item = NULL;
+ //if one of the domains has no roles continue; no more can be determined
+ apol_vector_destroy(&start_role_vector);
+ apol_vector_destroy(&end_role_vector);
+ continue;
+ }
+ common_role_vector =
+ apol_vector_create_from_intersection(start_role_vector, end_role_vector, NULL, NULL);
+ const char *role_name = NULL;
+ if (apol_vector_get_size(common_role_vector)) {
+ for (int k = 0; k < apol_vector_get_size(common_role_vector); k++) {
+ const qpol_role_t *common_role;
+
+ common_role = apol_vector_get_element(common_role_vector, k);
+ qpol_role_get_name(q, common_role, &role_name);
+ apol_user_query_set_role(policy, user_query, role_name);
+ apol_user_get_by_query(policy, user_query, &user_vector);
+ if (apol_vector_get_size(user_vector)) {
+ ok = true;
+ apol_vector_destroy(&user_vector);
+ break;
+ }
+ apol_vector_destroy(&user_vector);
+ }
+ if (!ok) {
+ item = sechk_item_new(dtr_free_wrap);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)apol_domain_trans_result_create_from_domain_trans_result(dtr);
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ asprintf(&proof->text, "Need user for role %s", role_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item = NULL;
+ }
+ } else {
+ apol_vector_t *role_trans_vector;
+ bool need_ra = 0, need_user = 0;
+ char *rastring = NULL, *userstring = NULL;
+ apol_role_trans_query_set_target(policy, role_trans_query, ep_name, 1);
+ apol_role_trans_get_by_query(policy, role_trans_query, &role_trans_vector);
+ const char *source_role_name = NULL, *default_role_name = NULL;
+ for (k = 0; k < apol_vector_get_size(role_trans_vector); k++) {
+ qpol_role_trans_t *role_trans;
+ const qpol_role_t *source_role;
+ const qpol_role_t *default_role;
+ int index;
+
+ role_trans = apol_vector_get_element(role_trans_vector, k);
+ qpol_role_trans_get_source_role(q, role_trans, &source_role);
+ qpol_role_trans_get_default_role(q, role_trans, &default_role);
+ if ((apol_vector_get_index(start_role_vector, source_role, NULL, NULL, &index) == 0)
+ && (apol_vector_get_index(end_role_vector, default_role, NULL, NULL, &index) ==
+ 0)) {
+ apol_vector_t *source_role_user, *default_role_user, *common_role_user;
+
+ qpol_role_get_name(q, source_role, &source_role_name);
+ qpol_role_get_name(q, default_role, &default_role_name);
+ apol_role_allow_query_set_source(policy, role_allow_query,
+ source_role_name);
+ apol_role_allow_query_set_target(policy, role_allow_query,
+ default_role_name);
+ apol_role_allow_get_by_query(policy, role_allow_query, &role_allow_vector);
+
+ apol_user_query_set_role(policy, user_query, source_role_name);
+ apol_user_get_by_query(policy, user_query, &source_role_user);
+ apol_user_query_set_role(policy, user_query, default_role_name);
+ apol_user_get_by_query(policy, user_query, &default_role_user);
+
+ common_role_user =
+ apol_vector_create_from_intersection(source_role_user,
+ default_role_user, NULL, NULL);
+ apol_vector_destroy(&source_role_user);
+ apol_vector_destroy(&default_role_user);
+ if (apol_vector_get_size(role_allow_vector) &&
+ apol_vector_get_size(common_role_user)) {
+ ok = true;
+ apol_vector_destroy(&role_allow_vector);
+ apol_vector_destroy(&common_role_user);
+ break;
+ }
+ if (!apol_vector_get_size(role_allow_vector)) {
+ // need role allow;
+ asprintf(&rastring, "allow %s %s;", source_role_name,
+ default_role_name);
+ if (!rastring) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ apol_vector_destroy(&role_allow_vector);
+ if (!apol_vector_get_size(common_role_user)) {
+ // need user;
+ asprintf(&userstring, "need user with roles %s and %s",
+ source_role_name, default_role_name);
+ if (!userstring) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ apol_vector_destroy(&common_role_user);
+ }
+ }
+ apol_vector_destroy(&role_trans_vector);
+ if (!ok) {
+ if (!need_ra && !need_user) {
+ const qpol_role_t *source_role =
+ apol_vector_get_element(start_role_vector, 0);
+ const qpol_role_t *default_role =
+ apol_vector_get_element(end_role_vector, 0);
+ qpol_role_get_name(q, source_role, &source_role_name);
+ qpol_role_get_name(q, default_role, &default_role_name);
+ // need role_transition
+ item = sechk_item_new(dtr_free_wrap);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)
+ apol_domain_trans_result_create_from_domain_trans_result(dtr);
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ asprintf(&proof->text, "role_transition %s %s %s;", source_role_name,
+ ep_name, default_role_name);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item = NULL;
+ } else {
+ //create item
+ item = sechk_item_new(dtr_free_wrap);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)
+ apol_domain_trans_result_create_from_domain_trans_result(dtr);
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (need_ra) {
+ //create proof w/ text rastring
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ proof->text = rastring;
+ rastring = NULL;
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ if (need_user) {
+ //create proof w/ text userstring
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ proof->text = userstring;
+ userstring = NULL;
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ item = NULL;
+ }
+ }
+ }
+ apol_vector_destroy(&start_role_vector);
+ apol_vector_destroy(&end_role_vector);
+ apol_vector_destroy(&common_role_vector);
+ } else {
+ item = sechk_item_new(dtr_free_wrap);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)apol_domain_trans_result_create_from_domain_trans_result(dtr);
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+
+ if (result & APOL_DOMAIN_TRANS_RULE_PROC_TRANS) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ buff = NULL;
+ buff_sz =
+ 10 + strlen("allow : process transition;") + strlen(start_name) + strlen(end_name);
+ buff = (char *)calloc(buff_sz, sizeof(char));
+ if (!buff) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ snprintf(buff, buff_sz, "allow %s %s : process transition;", start_name, end_name);
+ proof->text = strdup(buff);
+ free(buff);
+ buff = NULL;
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+
+ if (result & APOL_DOMAIN_TRANS_RULE_EXEC) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ buff = NULL;
+ buff_sz = 10 + strlen("allow : file execute;") + strlen(start_name) + strlen(ep_name);
+ buff = (char *)calloc(buff_sz, sizeof(char));
+ if (!buff) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ snprintf(buff, buff_sz, "allow %s %s : file execute;", start_name, ep_name);
+ proof->text = strdup(buff);
+ free(buff);
+ buff = NULL;
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+
+ if (result & APOL_DOMAIN_TRANS_RULE_ENTRYPOINT) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ buff = NULL;
+ buff_sz = 10 + strlen("allow : file entrypoint;") + strlen(end_name) + strlen(ep_name);
+ buff = (char *)calloc(buff_sz, sizeof(char));
+ if (!buff) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ snprintf(buff, buff_sz, "allow %s %s : file entrypoint;", end_name, ep_name);
+ proof->text = strdup(buff);
+ free(buff);
+ buff = NULL;
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+
+ if (result & APOL_DOMAIN_TRANS_RULE_TYPE_TRANS) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ buff = NULL;
+ buff_sz =
+ 10 + strlen("type_transition :process ;") + strlen(start_name) + strlen(end_name) +
+ strlen(ep_name);
+ buff = (char *)calloc(buff_sz, sizeof(char));
+ if (!buff) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ snprintf(buff, buff_sz, "type_transition %s %s : process %s;", start_name, ep_name,
+ end_name);
+ proof->text = strdup(buff);
+ free(buff);
+ buff = NULL;
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+
+ if (result & APOL_DOMAIN_TRANS_RULE_SETEXEC) {
+ proof = sechk_proof_new(NULL);
+ proof->type = SECHK_ITEM_OTHER;
+ buff = NULL;
+ buff_sz = 10 + strlen("allow self : process setexec;") + strlen(start_name);
+ buff = (char *)calloc(buff_sz, sizeof(char));
+ if (!buff) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ snprintf(buff, buff_sz, "allow %s self : process setexec;", start_name);
+ proof->text = strdup(buff);
+ free(buff);
+ buff = NULL;
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_dom_trans_run_fail;
+ }
+ }
+ }
+ }
+ apol_vector_destroy(&domain_trans_vector);
+ }
+
+ mod->result = res;
+ apol_domain_trans_analysis_destroy(&domain_trans);
+ apol_user_query_destroy(&user_query);
+ apol_role_trans_query_destroy(&role_trans_query);
+ apol_role_query_destroy(&start_role_query);
+ apol_role_query_destroy(&end_role_query);
+ apol_role_allow_query_destroy(&role_allow_query);
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ inc_dom_trans_run_fail:
+ sechk_item_free(item);
+ apol_vector_destroy(&user_vector);
+ apol_vector_destroy(&domain_trans_vector);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int inc_dom_trans_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i = 0, j = 0, num_items;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i incomplete transitions.\n", num_items);
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & SECHK_OUT_LIST) {
+ /*
+ * printf("\nStart Type\t\tEntrypoint\t\tEnd Type\t\tMissing Rules\n");
+ * printf("----------\t\t----------\t\t--------\t\t-------------\n");
+ */
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ const qpol_type_t *start;
+ const qpol_type_t *end;
+ const qpol_type_t *ep;
+ const char *start_name, *end_name, *ep_name;
+ apol_domain_trans_result_t *dtr;
+
+ item = apol_vector_get_element(mod->result->items, i);
+ dtr = item->item;
+ start = apol_domain_trans_result_get_start_type(dtr);
+ ep = apol_domain_trans_result_get_entrypoint_type(dtr);
+ end = apol_domain_trans_result_get_end_type(dtr);
+ if (start)
+ qpol_type_get_name(q, start, &start_name);
+ else
+ start_name = "<start_type>";
+ if (end)
+ qpol_type_get_name(q, end, &end_name);
+ else
+ end_name = "<end_type>";
+ if (ep)
+ qpol_type_get_name(q, ep, &ep_name);
+ else
+ ep_name = "<entrypoint_type>";
+
+ printf("%s -> %s\tentrypoint: %s\n", start_name, end_name, ep_name);
+ }
+ printf("\n");
+ }
+ /* The proof report component is a display of a list of items
+ * with an indented list of proof statements supporting the result
+ * of the check for that item (e.g. rules with a given type) */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ const qpol_type_t *start;
+ const qpol_type_t *end;
+ const qpol_type_t *ep;
+ const char *start_name, *end_name, *ep_name;
+ apol_domain_trans_result_t *dtr;
+
+ item = apol_vector_get_element(mod->result->items, i);
+ dtr = item->item;
+ start = apol_domain_trans_result_get_start_type(dtr);
+ ep = apol_domain_trans_result_get_entrypoint_type(dtr);
+ end = apol_domain_trans_result_get_end_type(dtr);
+ if (start)
+ qpol_type_get_name(q, start, &start_name);
+ else
+ start_name = "<start_type>";
+ if (end)
+ qpol_type_get_name(q, end, &end_name);
+ else
+ end_name = "<end_type>";
+ if (ep)
+ qpol_type_get_name(q, ep, &ep_name);
+ else
+ ep_name = "<entrypoint_type>";
+
+ printf("%s -> %s\tentrypoint: %s\n\tMissing:\n", start_name, end_name, ep_name);
+ for (j = 0; j < apol_vector_get_size(item->proof); j++) {
+ sechk_proof_t *proof;
+
+ proof = apol_vector_get_element(item->proof, j);
+ if (proof) {
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/inc_dom_trans.h b/sechecker/modules/inc_dom_trans.h
new file mode 100644
index 0000000..02dffbf
--- /dev/null
+++ b/sechecker/modules/inc_dom_trans.h
@@ -0,0 +1,56 @@
+/**
+ * @file
+ * Defines the interface for the incomplete domain transition module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INC_DOM_TRANS
+#define INC_DOM_TRANS
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/domain-trans-analysis.h>
+#include <apol/user-query.h>
+#include <apol/rbacrule-query.h>
+#include <apol/role-query.h>
+
+#define SECHK_INC_DOM_TRANS_HAS_TT 0x08
+#define SECHK_INC_DOM_TRANS_HAS_EXEC 0x04
+#define SECHK_INC_DOM_TRANS_HAS_TRANS 0x02
+#define SECHK_INC_DOM_TRANS_HAS_EP 0x01
+#define SECHK_INC_DOM_TRANS_COMPLETE (SECHK_INC_DOM_TRANS_HAS_EP|SECHK_INC_DOM_TRANS_HAS_TRANS|SECHK_INC_DOM_TRANS_HAS_EXEC)
+
+ int inc_dom_trans_register(sechk_lib_t * lib);
+ int inc_dom_trans_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int inc_dom_trans_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int inc_dom_trans_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/inc_mount.c b/sechecker/modules/inc_mount.c
new file mode 100644
index 0000000..4e9f8b1
--- /dev/null
+++ b/sechecker/modules/inc_mount.c
@@ -0,0 +1,520 @@
+/**
+ * @file
+ * Implementation of the incomplete mount permissions module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "inc_mount.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "inc_mount";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int inc_mount_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "no library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "domains with partial mount permissions";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds domains that have incomplete mount permissions.\n"
+ "In order for a mount operation to be allowed by the policy the following rules\n"
+ "must be present: \n"
+ "\n"
+ " 1) allow somedomain_d sometype_t : filesystem { mount };\n"
+ " 2) allow somedomain_d sometype_t : dir { mounton };\n"
+ "\n" "This module finds domains that have only one of the rules listed above.\n";
+ mod->opt_description =
+ "Module requirements:\n" " none\n" "Module dependencies:\n" " none\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_MED;
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_mount_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_mount_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_mount_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int inc_mount_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+
+int inc_mount_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i, j;
+ bool both = false, add_proof = false;
+ int error = 0;
+ char *tmp = NULL;
+ apol_vector_t *mount_vector;
+ apol_vector_t *mounton_vector;
+ apol_avrule_query_t *mount_avrule_query = NULL;
+ apol_avrule_query_t *mounton_avrule_query = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+
+ if (!(mount_avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+
+ if (!(mounton_avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+
+ /* Get avrules for filesystem mount */
+ apol_avrule_query_set_rules(policy, mount_avrule_query, QPOL_RULE_ALLOW);
+ apol_avrule_query_append_class(policy, mount_avrule_query, "filesystem");
+ apol_avrule_query_append_perm(policy, mount_avrule_query, "mount");
+ apol_avrule_get_by_query(policy, mount_avrule_query, &mount_vector);
+
+ /* Get avrules for dir mounton */
+ apol_avrule_query_set_rules(policy, mounton_avrule_query, QPOL_RULE_ALLOW);
+ apol_avrule_query_append_class(policy, mounton_avrule_query, "dir");
+ apol_avrule_query_append_perm(policy, mounton_avrule_query, "mounton");
+ apol_avrule_get_by_query(policy, mounton_avrule_query, &mounton_vector);
+
+ for (i = 0; i < apol_vector_get_size(mount_vector); i++) {
+ const qpol_avrule_t *mount_rule;
+ const qpol_type_t *mount_source;
+ const qpol_type_t *mount_target;
+ const char *mount_source_name, *mount_target_name;
+
+ both = false;
+ add_proof = true;
+ mount_rule = apol_vector_get_element(mount_vector, i);
+ qpol_avrule_get_source_type(q, mount_rule, &mount_source);
+ qpol_avrule_get_target_type(q, mount_rule, &mount_target);
+ qpol_type_get_name(q, mount_source, &mount_source_name);
+ qpol_type_get_name(q, mount_target, &mount_target_name);
+
+ for (j = 0; j < apol_vector_get_size(mounton_vector); j++) {
+ const qpol_avrule_t *mounton_rule;
+ const qpol_type_t *mounton_source;
+ const qpol_type_t *mounton_target;
+ const char *mounton_source_name, *mounton_target_name;
+
+ mounton_rule = apol_vector_get_element(mounton_vector, j);
+ qpol_avrule_get_source_type(q, mounton_rule, &mounton_source);
+ qpol_avrule_get_target_type(q, mounton_rule, &mounton_target);
+ qpol_type_get_name(q, mounton_source, &mounton_source_name);
+ qpol_type_get_name(q, mounton_target, &mounton_target_name);
+
+ /* Check to see if they match */
+ if (!strcmp(mount_source_name, mounton_source_name) && !strcmp(mount_target_name, mounton_target_name))
+ both = true;
+ }
+ if (!both) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ proof->type = SECHK_ITEM_AVRULE;
+ proof->elem = (void *)mount_rule;
+ tmp = apol_avrule_render(policy, mount_rule);
+ asprintf(&proof->text, "Have Rule:\n\t\t%s\n\tMissing:\n\t\tallow %s %s : dir mounton ;\n",
+ tmp, mount_source_name, mount_target_name);
+ free(tmp);
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(mount_source_name, res_type_name) || !strcmp(mount_target_name, res_type_name))
+ add_proof = false;
+ }
+ if (add_proof) {
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(NULL, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ item->item = (void *)mount_source;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ item = NULL;
+ proof = NULL;
+ }
+ sechk_proof_free(proof);
+ proof = NULL;
+ }
+ }
+
+ for (i = 0; i < apol_vector_get_size(mounton_vector); i++) {
+ const qpol_avrule_t *mounton_rule;
+ const qpol_type_t *mounton_source;
+ const qpol_type_t *mounton_target;
+ const char *mounton_source_name, *mounton_target_name;
+
+ both = false;
+ add_proof = true;
+ mounton_rule = apol_vector_get_element(mounton_vector, i);
+ qpol_avrule_get_source_type(q, mounton_rule, &mounton_source);
+ qpol_avrule_get_target_type(q, mounton_rule, &mounton_target);
+ qpol_type_get_name(q, mounton_source, &mounton_source_name);
+ qpol_type_get_name(q, mounton_target, &mounton_target_name);
+
+ for (j = 0; j < apol_vector_get_size(mount_vector); j++) {
+ const qpol_avrule_t *mount_rule;
+ const qpol_type_t *mount_source;
+ const qpol_type_t *mount_target;
+ const char *mount_source_name, *mount_target_name;
+
+ mount_rule = apol_vector_get_element(mount_vector, j);
+ qpol_avrule_get_source_type(q, mount_rule, &mount_source);
+ qpol_avrule_get_target_type(q, mount_rule, &mount_target);
+ qpol_type_get_name(q, mount_source, &mount_source_name);
+ qpol_type_get_name(q, mount_target, &mount_target_name);
+
+ if (!strcmp(mount_source_name, mounton_source_name) && !strcmp(mount_target_name, mounton_target_name))
+ both = true;
+ }
+ if (!both) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ proof->type = SECHK_ITEM_AVRULE;
+ proof->elem = (void *)mounton_rule;
+ tmp = apol_avrule_render(policy, mounton_rule);
+ asprintf(&proof->text, "Have Rule:\n\t\t%s\n\tMissing:\n\t\tallow %s %s : filesystem mount ;\n",
+ tmp, mounton_source_name, mounton_target_name);
+ free(tmp);
+ for (j = 0; j < apol_vector_get_size(res->items); j++) {
+ sechk_item_t *res_item;
+ const qpol_type_t *res_type;
+ const char *res_type_name;
+
+ res_item = apol_vector_get_element(res->items, j);
+ res_type = res_item->item;
+ qpol_type_get_name(q, res_type, &res_type_name);
+ if (!strcmp(mounton_source_name, res_type_name) || !strcmp(mounton_target_name, res_type_name))
+ add_proof = false;
+ }
+ if (add_proof) {
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(NULL, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ item->item = (void *)mounton_source;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_mount_run_fail;
+ }
+ item = NULL;
+ proof = NULL;
+ }
+ sechk_proof_free(proof);
+ proof = NULL;
+ }
+ }
+ apol_vector_destroy(&mount_vector);
+ apol_vector_destroy(&mounton_vector);
+
+ mod->result = res;
+ apol_avrule_query_destroy(&mount_avrule_query);
+ apol_avrule_query_destroy(&mounton_avrule_query);
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ inc_mount_run_fail:
+ apol_vector_destroy(&mount_vector);
+ apol_vector_destroy(&mounton_vector);
+ apol_avrule_query_destroy(&mount_avrule_query);
+ apol_avrule_query_destroy(&mounton_avrule_query);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ free(tmp);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int inc_mount_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k, l, num_items;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd types.\n", num_items);
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+ /* The proof report component is a display of a list of items
+ * with an indented list of proof statements supporting the result
+ * of the check for that item (e.g. rules with a given type)
+ * this field also lists the computed severity of each item
+ * (see sechk_item_sev in sechecker.c for details on calculation)
+ * items are printed on a line either with the severity.
+ * Each proof element is then displayed in an indented list one per
+ * line below it. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/inc_mount.h b/sechecker/modules/inc_mount.h
new file mode 100644
index 0000000..18d5179
--- /dev/null
+++ b/sechecker/modules/inc_mount.h
@@ -0,0 +1,54 @@
+/**
+ * @file
+ * Defines the interface for the incomplete mount permissions module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INC_MOUNT
+#define INC_MOUNT
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/avrule-query.h>
+
+#define SECHK_MOUNT_ONLY_MOUNT 0x01
+#define SECHK_MOUNT_ONLY_MOUNTON 0x02
+
+/* Module functions:
+ * NOTE: while using a modular format SEChecker is built
+ * statically; this means that all modules and their functions
+ * are in the same namespace. */
+ int inc_mount_register(sechk_lib_t * lib);
+ int inc_mount_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int inc_mount_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int inc_mount_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/inc_net_access.c b/sechecker/modules/inc_net_access.c
new file mode 100644
index 0000000..a8903ea
--- /dev/null
+++ b/sechecker/modules/inc_net_access.c
@@ -0,0 +1,1852 @@
+/**
+ * @file
+ * Defines the interface for the incomplete network access module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "inc_net_access.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+static const char *const mod_name = "inc_net_access";
+
+/* The register function registers all of a module's functions
+ * with the library. You should not need to edit this function
+ * unless you are adding additional functions you need other modules
+ * to call. See the note at the bottom of this function to do so. */
+int inc_net_access_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "finds network domains with inadequate permissions";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all network domains in a policy which do not have the \n"
+ "required permissions needed to facilitate network communication. For network\n"
+ "domains to communicate, the following conditions must be true:\n"
+ " 1) the domain must have read or receive permissions on a socket of the same\n"
+ " type\n"
+ " 2) the domain must have send or receive permissions on an IPsec association\n"
+ " (see find_assoc_types)\n"
+ " 3) the domain must have send or receive permissions on netif objects for a\n"
+ " netif type (see find_netif_types)\n"
+ " 4) the domain must have send or receive permissions on node objects for a\n"
+ " node type (see find_node_types)\n"
+ " 5) the domain must have send or receive permissions on port objects for a\n"
+ " port type (see find_port_types)\n";
+ mod->opt_description =
+ " Module requirements:\n"
+ " none\n" " Module dependencies:\n" " find_net_domains\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_MED;
+ /* assign dependencies */
+ if (apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_net_domains")) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_net_access_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_net_access_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = inc_net_access_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file.
+ * Add any option processing logic as indicated below. */
+int inc_net_access_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* This set of defines represents individual permission bits for
+ * the permissions needed to have complete network access */
+/* allow domain self : sock_file {read write getattr}; */
+#define PERM_SELF_SOCK_FILE_READ 0x00000001
+#define PERM_SELF_SOCK_FILE_WRITE 0x00000002
+#define PERM_SELF_SOCK_FILE_GETATTR 0x00000004
+/* allow domain self : tcp_socket {create read write}; */
+#define PERM_SELF_TCP_SOC_READ 0x00000008
+#define PERM_SELF_TCP_SOC_WRITE 0x00000010
+#define PERM_SELF_TCP_SOC_CREATE 0x00000020
+/* allow domain self : udp_socket {create read write}; */
+#define PERM_SELF_UDP_SOC_READ 0x00000040
+#define PERM_SELF_UDP_SOC_WRITE 0x00000080
+#define PERM_SELF_UDP_SOC_CREATE 0x00000100
+/* allow domain if_type : netif {tcp_send udp_send tcp_recv tcp_send}; */
+#define PERM_NETIF_TCP_SEND 0x00000200
+#define PERM_NETIF_UDP_SEND 0x00000400
+#define PERM_NETIF_TCP_RECV 0x00000800
+#define PERM_NETIF_UDP_RECV 0x00001000
+/* allow domain node_type : node {tcp_send udp_send tcp_recv tcp_send}; */
+#define PERM_NODE_TCP_SEND 0x00002000
+#define PERM_NODE_UDP_SEND 0x00004000
+#define PERM_NODE_TCP_RECV 0x00008000
+#define PERM_NODE_UDP_RECV 0x00010000
+/* allow domain port_type : tcp_socket {send_msg recv_msg}; */
+#define PERM_PORT_TCP_SEND 0x00020000
+#define PERM_PORT_TCP_RECV 0x00040000
+/* allow domain port_type : udp_socket {send_msg recv_msg}; */
+#define PERM_PORT_UDP_SEND 0x00080000
+#define PERM_PORT_UDP_RECV 0x00100000
+/* allow domain assoc_type : association {sendto recvfrom}; */
+#define PERM_ASSOC_SEND 0x00200000
+#define PERM_ASSOC_RECV 0x00400000
+
+/* this set of defines represents masks for individual rules */
+#define RULE_TCP_SOCK_FILE (PERM_SELF_SOCK_FILE_READ|PERM_SELF_SOCK_FILE_WRITE|PERM_SELF_SOCK_FILE_GETATTR)
+#define RULE_UDPs_SOCK_FILE (PERM_SELF_SOCK_FILE_WRITE|PERM_SELF_SOCK_FILE_GETATTR)
+#define RULE_UDPr_SOCK_FILE (PERM_SELF_SOCK_FILE_READ|PERM_SELF_SOCK_FILE_GETATTR)
+#define RULE_TCP_SELF_SOC (PERM_SELF_TCP_SOC_READ|PERM_SELF_TCP_SOC_WRITE|PERM_SELF_TCP_SOC_CREATE)
+#define RULE_UDPs_SELF_SOC (PERM_SELF_UDP_SOC_WRITE|PERM_SELF_UDP_SOC_CREATE)
+#define RULE_UDPr_SELF_SOC (PERM_SELF_UDP_SOC_READ|PERM_SELF_UDP_SOC_CREATE)
+#define RULE_TCP_NETIF (PERM_NETIF_TCP_SEND|PERM_NETIF_TCP_RECV)
+#define RULE_UDPs_NETIF (PERM_NETIF_UDP_SEND)
+#define RULE_UDPr_NETIF (PERM_NETIF_UDP_RECV)
+#define RULE_TCP_NODE (PERM_NODE_TCP_SEND|PERM_NODE_TCP_RECV)
+#define RULE_UDPs_NODE (PERM_NODE_UDP_SEND)
+#define RULE_UDPr_NODE (PERM_NODE_UDP_RECV)
+#define RULE_TCP_PORT (PERM_PORT_TCP_SEND|PERM_PORT_TCP_RECV)
+#define RULE_UDPs_PORT (PERM_PORT_UDP_SEND)
+#define RULE_UDPr_PORT (PERM_PORT_UDP_RECV)
+#define RULE_TCP_ASSOC (PERM_ASSOC_SEND|PERM_ASSOC_RECV)
+#define RULE_UDPs_ASSOC (PERM_ASSOC_SEND)
+#define RULE_UDPr_ASSOC (PERM_ASSOC_RECV)
+
+/* This set of defines represents mask sets to represent access types */
+#define UDP_RECV_PERM_SET (RULE_UDPr_SOCK_FILE|RULE_UDPr_SELF_SOC|RULE_UDPr_NETIF|RULE_UDPr_NODE|RULE_UDPr_PORT|RULE_UDPr_ASSOC)
+#define UDP_SEND_PERM_SET (RULE_UDPs_SOCK_FILE|RULE_UDPs_SELF_SOC|RULE_UDPs_NETIF|RULE_UDPs_NODE|RULE_UDPs_PORT|RULE_UDPs_ASSOC)
+#define TCP_FULL_PERM_SET (RULE_TCP_SOCK_FILE|RULE_TCP_SELF_SOC|RULE_TCP_NETIF|RULE_TCP_NODE|RULE_TCP_PORT|RULE_TCP_ASSOC)
+#define COMMON_ACCESS_SET (PERM_SELF_SOCK_FILE_READ|PERM_SELF_SOCK_FILE_WRITE|PERM_SELF_SOCK_FILE_GETATTR|PERM_ASSOC_SEND|PERM_ASSOC_RECV)
+
+typedef struct net_state
+{
+ uint32_t perms;
+ apol_vector_t *netifs;
+ apol_vector_t *nodes;
+ apol_vector_t *tcpsocs;
+ apol_vector_t *udpsocs;
+ apol_vector_t *assocs;
+} net_state_t;
+
+typedef struct name_perm
+{
+ const char *name; /* will be from policy do not free */
+ uint32_t perms;
+} name_perm_t;
+
+static void net_state_destroy(net_state_t ** n)
+{
+ if (!n || !(*n))
+ return;
+
+ apol_vector_destroy(&((*n)->netifs));
+ apol_vector_destroy(&((*n)->nodes));
+ apol_vector_destroy(&((*n)->tcpsocs));
+ apol_vector_destroy(&((*n)->udpsocs));
+ apol_vector_destroy(&((*n)->assocs));
+ free(*n);
+ *n = NULL;
+}
+
+static net_state_t *net_state_create(void)
+{
+ net_state_t *n = NULL;
+
+ n = calloc(1, sizeof(*n));
+ n->netifs = apol_vector_create(free);
+ n->nodes = apol_vector_create(free);
+ n->tcpsocs = apol_vector_create(free);
+ n->udpsocs = apol_vector_create(free);
+ n->assocs = apol_vector_create(free);
+
+ return n;
+}
+
+static int name_perm_comp(const void *a, const void *b, void *arg __attribute__ ((unused)))
+{
+ const name_perm_t *x = a;
+ const name_perm_t *y = b;
+
+ return strcmp(x->name, y->name);
+}
+
+static name_perm_t *name_perm_create(const char *name)
+{
+ name_perm_t *np = NULL;
+
+ np = calloc(1, sizeof(*np));
+ np->name = name;
+
+ return np;
+}
+
+static int name_perm_vector_has_incomplete_perms(apol_vector_t * v, uint32_t mask)
+{
+ uint32_t tmp;
+ name_perm_t *np = NULL;
+ size_t i = 0;
+
+ if (!apol_vector_get_size(v))
+ return 1;
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ np = apol_vector_get_element(v, i);
+ tmp = np->perms & mask;
+ if (tmp != mask)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void name_perm_vector_add_perm(apol_vector_t * v, const char *name, uint32_t perm)
+{
+ name_perm_t *np = NULL;
+ size_t i = 0;
+ int retv;
+
+ np = name_perm_create(name);
+ retv = apol_vector_get_index(v, np, name_perm_comp, NULL, &i);
+ if (retv) {
+ np->perms = perm;
+ apol_vector_append(v, (void *)np);
+ } else {
+ free(np); /* already exists free temp one */
+ np = apol_vector_get_element(v, i);
+ np->perms |= perm;
+ }
+}
+
+static char *generate_tcp_proof_text(const char *domain, net_state_t * state)
+{
+ char *text = NULL, *tmp = NULL;
+ size_t text_sz = 0, i;
+ uint32_t attempt = 0, missing = 0;
+ name_perm_t *np = NULL;
+
+ if (state->perms == TCP_FULL_PERM_SET) {
+ if (apol_str_append(&text, &text_sz, "Domain has TCP access, but some accesses are incomplete."))
+ goto err;
+ } else {
+ if (apol_str_append(&text, &text_sz, "Domain has incomplete TCP access."))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tsock_file permissions:"))
+ goto err;
+ attempt = state->perms & RULE_TCP_SOCK_FILE;
+ missing = attempt ^ RULE_TCP_SOCK_FILE;
+ asprintf(&tmp, "allow %s self : sock_file { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_SOCK_FILE_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_SOCK_FILE_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_SELF_SOCK_FILE_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (missing & PERM_SELF_SOCK_FILE_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (missing & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tsocket creation permissions:"))
+ goto err;
+ attempt = state->perms & RULE_TCP_SELF_SOC;
+ missing = attempt ^ RULE_TCP_SELF_SOC;
+ asprintf(&tmp, "allow %s self : tcp_socket { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_TCP_SOC_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_TCP_SOC_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_TCP_SOC_CREATE) {
+ if (apol_str_append(&text, &text_sz, "create "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_SELF_TCP_SOC_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (missing & PERM_SELF_TCP_SOC_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (missing & PERM_SELF_TCP_SOC_CREATE) {
+ if (apol_str_append(&text, &text_sz, "create "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tnetif permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->netifs); i++) {
+ np = apol_vector_get_element(state->netifs, i);
+ attempt = state->perms & RULE_TCP_NETIF;
+ missing = attempt ^ RULE_TCP_NETIF;
+ asprintf(&tmp, "allow %s %s : netif { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_NETIF_TCP_SEND) {
+ if (apol_str_append(&text, &text_sz, "tcp_send "))
+ goto err;
+ }
+ if (attempt & PERM_NETIF_TCP_RECV) {
+ if (apol_str_append(&text, &text_sz, "tcp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_NETIF_TCP_SEND) {
+ if (apol_str_append(&text, &text_sz, "tcp_send "))
+ goto err;
+ }
+ if (missing & PERM_NETIF_TCP_RECV) {
+ if (apol_str_append(&text, &text_sz, "tcp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->netifs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <netif_type> : netif { tcp_send tcp_recv };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tnode permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->nodes); i++) {
+ np = apol_vector_get_element(state->nodes, i);
+ attempt = state->perms & RULE_TCP_NODE;
+ missing = attempt ^ RULE_TCP_NODE;
+ asprintf(&tmp, "allow %s %s : node { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_NODE_TCP_SEND) {
+ if (apol_str_append(&text, &text_sz, "tcp_send "))
+ goto err;
+ }
+ if (attempt & PERM_NODE_TCP_RECV) {
+ if (apol_str_append(&text, &text_sz, "tcp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_NODE_TCP_SEND) {
+ if (apol_str_append(&text, &text_sz, "tcp_send "))
+ goto err;
+ }
+ if (missing & PERM_NODE_TCP_RECV) {
+ if (apol_str_append(&text, &text_sz, "tcp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->nodes)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <node_type> : node { tcp_send tcp_recv };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tport socket permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->tcpsocs); i++) {
+ np = apol_vector_get_element(state->tcpsocs, i);
+ attempt = state->perms & RULE_TCP_PORT;
+ missing = attempt ^ RULE_TCP_PORT;
+ asprintf(&tmp, "allow %s %s : tcp_socket { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_PORT_TCP_SEND) {
+ if (apol_str_append(&text, &text_sz, "send_msg "))
+ goto err;
+ }
+ if (attempt & PERM_PORT_TCP_RECV) {
+ if (apol_str_append(&text, &text_sz, "recv_msg "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_PORT_TCP_SEND) {
+ if (apol_str_append(&text, &text_sz, "send_msg "))
+ goto err;
+ }
+ if (missing & PERM_PORT_TCP_RECV) {
+ if (apol_str_append(&text, &text_sz, "recv_msg "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->tcpsocs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <port_type> : tcp_socket { send_msg recv_msg };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tassociation permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->assocs); i++) {
+ np = apol_vector_get_element(state->assocs, i);
+ attempt = state->perms & RULE_TCP_ASSOC;
+ missing = attempt ^ RULE_TCP_ASSOC;
+ asprintf(&tmp, "allow %s %s : association { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_ASSOC_SEND) {
+ if (apol_str_append(&text, &text_sz, "sendto "))
+ goto err;
+ }
+ if (attempt & PERM_ASSOC_RECV) {
+ if (apol_str_append(&text, &text_sz, "recvfrom "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_ASSOC_SEND) {
+ if (apol_str_append(&text, &text_sz, "sendto "))
+ goto err;
+ }
+ if (missing & PERM_ASSOC_RECV) {
+ if (apol_str_append(&text, &text_sz, "recvfrom "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->assocs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <association_type> : association { sendto recvfrom };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n"))
+ goto err;
+
+ return text;
+
+ err:
+ free(text);
+ free(tmp);
+ return NULL;
+}
+
+static char *generate_udp_send_proof_text(const char *domain, net_state_t * state)
+{
+ char *text = NULL, *tmp = NULL;
+ size_t text_sz = 0, i;
+ uint32_t attempt = 0, missing = 0;
+ name_perm_t *np = NULL;
+
+ if (state->perms == UDP_SEND_PERM_SET) {
+ if (apol_str_append(&text, &text_sz, "Domain has UDP send access, but some accesses are incomplete."))
+ goto err;
+ } else {
+ if (apol_str_append(&text, &text_sz, "Domain has incomplete UDP send access."))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tsock_file permissions:"))
+ goto err;
+ attempt = state->perms & RULE_UDPs_SOCK_FILE;
+ missing = attempt ^ RULE_UDPs_SOCK_FILE;
+ asprintf(&tmp, "allow %s self : sock_file { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_SOCK_FILE_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_SELF_SOCK_FILE_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (missing & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tsocket creation permissions:"))
+ goto err;
+ attempt = state->perms & RULE_UDPs_SELF_SOC;
+ missing = attempt ^ RULE_UDPs_SELF_SOC;
+ asprintf(&tmp, "allow %s self : udp_socket { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_UDP_SOC_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_UDP_SOC_CREATE) {
+ if (apol_str_append(&text, &text_sz, "create "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_SELF_UDP_SOC_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (missing & PERM_SELF_UDP_SOC_CREATE) {
+ if (apol_str_append(&text, &text_sz, "create "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tnetif permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->netifs); i++) {
+ np = apol_vector_get_element(state->netifs, i);
+ attempt = state->perms & RULE_UDPs_NETIF;
+ missing = attempt ^ RULE_UDPs_NETIF;
+ asprintf(&tmp, "allow %s %s : netif { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_NETIF_UDP_SEND) {
+ if (apol_str_append(&text, &text_sz, "udp_send "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_NETIF_UDP_SEND) {
+ if (apol_str_append(&text, &text_sz, "udp_send "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->netifs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <netif_type> : netif { udp_send };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tnode permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->nodes); i++) {
+ np = apol_vector_get_element(state->nodes, i);
+ attempt = state->perms & RULE_UDPs_NODE;
+ missing = attempt ^ RULE_UDPs_NODE;
+ asprintf(&tmp, "allow %s %s : node { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_NODE_UDP_SEND) {
+ if (apol_str_append(&text, &text_sz, "udp_send "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_NODE_UDP_SEND) {
+ if (apol_str_append(&text, &text_sz, "udp_send "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->nodes)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <node_type> : node { udp_send };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tport socket permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->udpsocs); i++) {
+ np = apol_vector_get_element(state->udpsocs, i);
+ attempt = state->perms & RULE_UDPs_PORT;
+ missing = attempt ^ RULE_UDPs_PORT;
+ asprintf(&tmp, "allow %s %s : udp_socket { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_PORT_UDP_SEND) {
+ if (apol_str_append(&text, &text_sz, "send_msg "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_PORT_UDP_SEND) {
+ if (apol_str_append(&text, &text_sz, "send_msg "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->udpsocs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <port_type> : udp_socket { send_msg };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tassociation permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->assocs); i++) {
+ np = apol_vector_get_element(state->assocs, i);
+ attempt = state->perms & RULE_UDPs_ASSOC;
+ missing = attempt ^ RULE_UDPs_ASSOC;
+ asprintf(&tmp, "allow %s %s : association { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_ASSOC_SEND) {
+ if (apol_str_append(&text, &text_sz, "sendto "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_ASSOC_SEND) {
+ if (apol_str_append(&text, &text_sz, "sendto "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->assocs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <association_type> : association { sendto };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n"))
+ goto err;
+
+ return text;
+
+ err:
+ free(text);
+ free(tmp);
+ return NULL;
+}
+
+static char *generate_udp_recv_proof_text(const char *domain, net_state_t * state)
+{
+ char *text = NULL, *tmp = NULL;
+ size_t text_sz = 0, i;
+ uint32_t attempt = 0, missing = 0;
+ name_perm_t *np = NULL;
+
+ if (state->perms == UDP_RECV_PERM_SET) {
+ if (apol_str_append(&text, &text_sz, "Domain has UDP receive access, but some accesses are incomplete."))
+ goto err;
+ } else {
+ if (apol_str_append(&text, &text_sz, "Domain has incomplete UDP receive access."))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tsock_file permissions:"))
+ goto err;
+ attempt = state->perms & RULE_UDPr_SOCK_FILE;
+ missing = attempt ^ RULE_UDPr_SOCK_FILE;
+ asprintf(&tmp, "allow %s self : sock_file { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_SOCK_FILE_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_SELF_SOCK_FILE_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (missing & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tsocket creation permissions:"))
+ goto err;
+ attempt = state->perms & RULE_UDPr_SELF_SOC;
+ missing = attempt ^ RULE_UDPr_SELF_SOC;
+ asprintf(&tmp, "allow %s self : udp_socket { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_UDP_SOC_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_UDP_SOC_CREATE) {
+ if (apol_str_append(&text, &text_sz, "create "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_SELF_UDP_SOC_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (missing & PERM_SELF_UDP_SOC_CREATE) {
+ if (apol_str_append(&text, &text_sz, "create "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tnetif permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->netifs); i++) {
+ np = apol_vector_get_element(state->netifs, i);
+ attempt = state->perms & RULE_UDPr_NETIF;
+ missing = attempt ^ RULE_UDPr_NETIF;
+ asprintf(&tmp, "allow %s %s : netif { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_NETIF_UDP_RECV) {
+ if (apol_str_append(&text, &text_sz, "udp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_NETIF_UDP_RECV) {
+ if (apol_str_append(&text, &text_sz, "udp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->netifs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <netif_type> : netif { udp_recv };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tnode permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->nodes); i++) {
+ np = apol_vector_get_element(state->nodes, i);
+ attempt = state->perms & RULE_UDPr_NODE;
+ missing = attempt ^ RULE_UDPr_NODE;
+ asprintf(&tmp, "allow %s %s : node { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_NODE_UDP_RECV) {
+ if (apol_str_append(&text, &text_sz, "udp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_NODE_UDP_RECV) {
+ if (apol_str_append(&text, &text_sz, "udp_recv "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->nodes)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <node_type> : node { udp_recv };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tport socket permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->udpsocs); i++) {
+ np = apol_vector_get_element(state->udpsocs, i);
+ attempt = state->perms & RULE_UDPr_PORT;
+ missing = attempt ^ RULE_UDPr_PORT;
+ asprintf(&tmp, "allow %s %s : udp_socket { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_PORT_UDP_RECV) {
+ if (apol_str_append(&text, &text_sz, "recv_msg "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_PORT_UDP_RECV) {
+ if (apol_str_append(&text, &text_sz, "recv_msg "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->udpsocs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <port_type> : udp_socket { recv_msg };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n\tassociation permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->assocs); i++) {
+ np = apol_vector_get_element(state->assocs, i);
+ attempt = state->perms & RULE_UDPr_ASSOC;
+ missing = attempt ^ RULE_UDPr_ASSOC;
+ asprintf(&tmp, "allow %s %s : association { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_ASSOC_RECV) {
+ if (apol_str_append(&text, &text_sz, "recvfrom "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ if (missing) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (missing & PERM_ASSOC_RECV) {
+ if (apol_str_append(&text, &text_sz, "recvfrom "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+ if (!apol_vector_get_size(state->assocs)) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tMissing: allow "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, domain))
+ goto err;
+ if (apol_str_append(&text, &text_sz, " <association_type> : association { recvfrom };"))
+ goto err;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n"))
+ goto err;
+
+ return text;
+
+ err:
+ free(text);
+ free(tmp);
+ return NULL;
+}
+
+static char *generate_common_only_proof_text(const char *domain, net_state_t * state)
+{
+ char *text = NULL, *tmp = NULL;
+ size_t text_sz = 0, i;
+ uint32_t attempt = 0;
+ name_perm_t *np = NULL;
+
+ if (apol_str_append
+ (&text, &text_sz,
+ "Domain has incomplete network access.\n\tDomain has no protocol specific permissions only the following:"))
+ goto err;
+
+ if (apol_str_append(&text, &text_sz, "\n\tsock_file permissions:"))
+ goto err;
+ attempt = state->perms & RULE_TCP_SOCK_FILE;
+ asprintf(&tmp, "allow %s self : sock_file { ", domain);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_SELF_SOCK_FILE_READ) {
+ if (apol_str_append(&text, &text_sz, "read "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_SOCK_FILE_WRITE) {
+ if (apol_str_append(&text, &text_sz, "write "))
+ goto err;
+ }
+ if (attempt & PERM_SELF_SOCK_FILE_GETATTR) {
+ if (apol_str_append(&text, &text_sz, "getattr "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+
+ if (apol_str_append(&text, &text_sz, "\n\tassociation permissions:"))
+ goto err;
+ for (i = 0; i < apol_vector_get_size(state->assocs); i++) {
+ np = apol_vector_get_element(state->assocs, i);
+ attempt = state->perms & RULE_TCP_ASSOC;
+ asprintf(&tmp, "allow %s %s : association { ", domain, np->name);
+ if (attempt) {
+ if (apol_str_append(&text, &text_sz, "\n\t\tHas: "))
+ goto err;
+ if (apol_str_append(&text, &text_sz, tmp))
+ goto err;
+ if (attempt & PERM_ASSOC_SEND) {
+ if (apol_str_append(&text, &text_sz, "sendto "))
+ goto err;
+ }
+ if (attempt & PERM_ASSOC_RECV) {
+ if (apol_str_append(&text, &text_sz, "recvfrom "))
+ goto err;
+ }
+ if (apol_str_append(&text, &text_sz, "};"))
+ goto err;
+ }
+ free(tmp);
+ tmp = NULL;
+ }
+
+ if (apol_str_append(&text, &text_sz, "\n"))
+ goto err;
+
+ return text;
+
+ err:
+ free(text);
+ free(tmp);
+ return NULL;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int inc_net_access_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL, *tmp_item = NULL;
+ sechk_proof_t *proof = NULL;
+ sechk_result_t *net_domain_res = NULL;
+ sechk_name_value_t *dep = NULL;
+ sechk_mod_fn_t run_fn = NULL;
+ size_t i = 0, j = 0;
+ int error = 0;
+ apol_avrule_query_t *avrule_query = NULL;
+ apol_vector_t *avrule_vector = NULL, *net_domain_vector = NULL;
+ const qpol_type_t *net_domain = NULL, *tmp_type = NULL;
+ const char *net_domain_name = NULL, *tgt_name = NULL;
+ char *perm_name = NULL;
+ const qpol_avrule_t *rule = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ net_state_t *state = NULL;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_net_access_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto inc_net_access_run_fail;
+ }
+
+ /* run dependencies */
+ for (i = 0; i < apol_vector_get_size(mod->dependencies); i++) {
+ dep = apol_vector_get_element(mod->dependencies, i);
+ run_fn = sechk_lib_get_module_function(dep->value, SECHK_MOD_FN_RUN, mod->parent_lib);
+ run_fn(sechk_lib_get_module(dep->value, mod->parent_lib), policy, NULL);
+ }
+
+ net_domain_res = sechk_lib_get_module_result("find_net_domains", mod->parent_lib);
+ if (!net_domain_res) {
+ error = errno;
+ ERR(policy, "%s", "Unable to get results for module find_net_domains");
+ goto inc_net_access_run_fail;
+ }
+ net_domain_vector = (apol_vector_t *) net_domain_res->items;
+
+ avrule_query = apol_avrule_query_create();
+ if (!avrule_query) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ apol_avrule_query_set_rules(policy, avrule_query, QPOL_RULE_ALLOW);
+
+ for (i = 0; i < apol_vector_get_size(net_domain_vector); i++) {
+ tmp_item = apol_vector_get_element(net_domain_vector, i);
+ net_domain = tmp_item->item;
+ qpol_type_get_name(q, net_domain, &net_domain_name);
+ state = net_state_create();
+
+ /* find any self sock_file perms */
+ apol_avrule_query_set_source(policy, avrule_query, net_domain_name, 1);
+ apol_avrule_query_set_target(policy, avrule_query, net_domain_name, 1);
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "sock_file");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "read")) {
+ state->perms |= PERM_SELF_SOCK_FILE_READ;
+ } else if (!strcmp(perm_name, "write")) {
+ state->perms |= PERM_SELF_SOCK_FILE_WRITE;
+ } else if (!strcmp(perm_name, "getattr")) {
+ state->perms |= PERM_SELF_SOCK_FILE_GETATTR;
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any self tcp_socket perms */
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "tcp_socket");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "read")) {
+ state->perms |= PERM_SELF_TCP_SOC_READ;
+ } else if (!strcmp(perm_name, "write")) {
+ state->perms |= PERM_SELF_TCP_SOC_WRITE;
+ } else if (!strcmp(perm_name, "create")) {
+ state->perms |= PERM_SELF_TCP_SOC_CREATE;
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any self udp_socket perms */
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "udp_socket");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "read")) {
+ state->perms |= PERM_SELF_UDP_SOC_READ;
+ } else if (!strcmp(perm_name, "write")) {
+ state->perms |= PERM_SELF_UDP_SOC_WRITE;
+ } else if (!strcmp(perm_name, "create")) {
+ state->perms |= PERM_SELF_UDP_SOC_CREATE;
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any if_t netif perms */
+ apol_avrule_query_set_target(policy, avrule_query, NULL, 0);
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "netif");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_target_type(q, rule, &tmp_type);
+ qpol_type_get_name(q, tmp_type, &tgt_name);
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "tcp_send")) {
+ state->perms |= PERM_NETIF_TCP_SEND;
+ name_perm_vector_add_perm(state->netifs, tgt_name, PERM_NETIF_TCP_SEND);
+ } else if (!strcmp(perm_name, "tcp_recv")) {
+ state->perms |= PERM_NETIF_TCP_RECV;
+ name_perm_vector_add_perm(state->netifs, tgt_name, PERM_NETIF_TCP_RECV);
+ } else if (!strcmp(perm_name, "udp_send")) {
+ state->perms |= PERM_NETIF_UDP_SEND;
+ name_perm_vector_add_perm(state->netifs, tgt_name, PERM_NETIF_UDP_SEND);
+ } else if (!strcmp(perm_name, "udp_recv")) {
+ state->perms |= PERM_NETIF_UDP_RECV;
+ name_perm_vector_add_perm(state->netifs, tgt_name, PERM_NETIF_UDP_RECV);
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any node_t node perms */
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "node");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_target_type(q, rule, &tmp_type);
+ qpol_type_get_name(q, tmp_type, &tgt_name);
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "tcp_send")) {
+ state->perms |= PERM_NODE_TCP_SEND;
+ name_perm_vector_add_perm(state->nodes, tgt_name, PERM_NODE_TCP_SEND);
+ } else if (!strcmp(perm_name, "tcp_recv")) {
+ state->perms |= PERM_NODE_TCP_RECV;
+ name_perm_vector_add_perm(state->nodes, tgt_name, PERM_NODE_TCP_RECV);
+ } else if (!strcmp(perm_name, "udp_send")) {
+ state->perms |= PERM_NODE_UDP_SEND;
+ name_perm_vector_add_perm(state->nodes, tgt_name, PERM_NODE_UDP_SEND);
+ } else if (!strcmp(perm_name, "udp_recv")) {
+ state->perms |= PERM_NODE_UDP_RECV;
+ name_perm_vector_add_perm(state->nodes, tgt_name, PERM_NODE_UDP_RECV);
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any port_t tcp_socket perms */
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "tcp_socket");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_target_type(q, rule, &tmp_type);
+ qpol_type_get_name(q, tmp_type, &tgt_name);
+ /* skip self */
+ if (!strcmp(net_domain_name, tgt_name))
+ continue;
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "send_msg")) {
+ state->perms |= PERM_PORT_TCP_SEND;
+ name_perm_vector_add_perm(state->tcpsocs, tgt_name, PERM_PORT_TCP_SEND);
+ } else if (!strcmp(perm_name, "recv_msg")) {
+ state->perms |= PERM_PORT_TCP_RECV;
+ name_perm_vector_add_perm(state->tcpsocs, tgt_name, PERM_PORT_TCP_RECV);
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any port_t udp_socket perms */
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "udp_socket");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_target_type(q, rule, &tmp_type);
+ qpol_type_get_name(q, tmp_type, &tgt_name);
+ /* skip self */
+ if (!strcmp(net_domain_name, tgt_name))
+ continue;
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "send_msg")) {
+ state->perms |= PERM_PORT_UDP_SEND;
+ name_perm_vector_add_perm(state->udpsocs, tgt_name, PERM_PORT_UDP_SEND);
+ } else if (!strcmp(perm_name, "recv_msg")) {
+ state->perms |= PERM_PORT_UDP_RECV;
+ name_perm_vector_add_perm(state->udpsocs, tgt_name, PERM_PORT_UDP_RECV);
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* find any assoc_t association perms */
+ apol_avrule_query_append_class(policy, avrule_query, NULL);
+ apol_avrule_query_append_class(policy, avrule_query, "association");
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_target_type(q, rule, &tmp_type);
+ qpol_type_get_name(q, tmp_type, &tgt_name);
+ qpol_avrule_get_perm_iter(q, rule, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)(&perm_name));
+ if (!strcmp(perm_name, "sendto")) {
+ state->perms |= PERM_ASSOC_SEND;
+ name_perm_vector_add_perm(state->assocs, tgt_name, PERM_ASSOC_SEND);
+ } else if (!strcmp(perm_name, "recvfrom")) {
+ state->perms |= PERM_ASSOC_RECV;
+ name_perm_vector_add_perm(state->assocs, tgt_name, PERM_ASSOC_RECV);
+ } /* no general case else */
+ free(perm_name);
+ perm_name = NULL;
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&avrule_vector);
+
+ /* if has tcp perms check for missing ones */
+ if ((state->perms & ((~(COMMON_ACCESS_SET)) & (TCP_FULL_PERM_SET))) &&
+ ((state->perms & RULE_TCP_SOCK_FILE) != RULE_TCP_SOCK_FILE ||
+ (state->perms & RULE_TCP_SELF_SOC) != RULE_TCP_SELF_SOC ||
+ name_perm_vector_has_incomplete_perms(state->netifs, RULE_TCP_NETIF) ||
+ name_perm_vector_has_incomplete_perms(state->nodes, RULE_TCP_NODE) ||
+ name_perm_vector_has_incomplete_perms(state->tcpsocs, RULE_TCP_PORT) ||
+ name_perm_vector_has_incomplete_perms(state->assocs, RULE_TCP_ASSOC))) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ item->item = (void *)net_domain;
+ item->test_result = 1;
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->elem = NULL;
+ proof->text = generate_tcp_proof_text(net_domain_name, state);
+ //TODO check that it succeeded
+ if (apol_vector_append(item->proof, (void *)proof)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof = NULL;
+ }
+ /* if has udp send perms check for missing perms */
+ if ((state->perms & ((~(COMMON_ACCESS_SET | PERM_SELF_UDP_SOC_CREATE)) & (UDP_SEND_PERM_SET))) &&
+ ((state->perms & RULE_UDPs_SOCK_FILE) != RULE_UDPs_SOCK_FILE ||
+ (state->perms & RULE_UDPs_SELF_SOC) != RULE_UDPs_SELF_SOC ||
+ name_perm_vector_has_incomplete_perms(state->netifs, RULE_UDPs_NETIF) ||
+ name_perm_vector_has_incomplete_perms(state->nodes, RULE_UDPs_NODE) ||
+ name_perm_vector_has_incomplete_perms(state->udpsocs, RULE_UDPs_PORT) ||
+ name_perm_vector_has_incomplete_perms(state->assocs, RULE_UDPs_ASSOC))) {
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ item->item = (void *)net_domain;
+ item->test_result = 1;
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ }
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->elem = NULL;
+ proof->text = generate_udp_send_proof_text(net_domain_name, state);
+ //TODO check that it succeeded
+ if (apol_vector_append(item->proof, (void *)proof)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof = NULL;
+ }
+ /* if has udp reveive perms check for missing perms */
+ if ((state->perms & ((~(COMMON_ACCESS_SET | PERM_SELF_UDP_SOC_CREATE)) & (UDP_RECV_PERM_SET))) &&
+ ((state->perms & RULE_UDPr_SOCK_FILE) != RULE_UDPr_SOCK_FILE ||
+ (state->perms & RULE_UDPr_SELF_SOC) != RULE_UDPr_SELF_SOC ||
+ name_perm_vector_has_incomplete_perms(state->netifs, RULE_UDPr_NETIF) ||
+ name_perm_vector_has_incomplete_perms(state->nodes, RULE_UDPr_NODE) ||
+ name_perm_vector_has_incomplete_perms(state->udpsocs, RULE_UDPr_PORT) ||
+ name_perm_vector_has_incomplete_perms(state->assocs, RULE_UDPr_ASSOC))) {
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ item->item = (void *)net_domain;
+ item->test_result = 1;
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ }
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->elem = NULL;
+ proof->text = generate_udp_recv_proof_text(net_domain_name, state);
+ //TODO check that it succeeded
+ if (apol_vector_append(item->proof, (void *)proof)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof = NULL;
+ }
+ /* if has only common access perms report that */
+ if (!(state->perms & (~(COMMON_ACCESS_SET)))) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ item->item = (void *)net_domain;
+ item->test_result = 1;
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->elem = NULL;
+ proof->text = generate_common_only_proof_text(net_domain_name, state);
+ //TODO check that it succeeded
+ if (apol_vector_append(item->proof, (void *)proof)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ proof = NULL;
+ }
+
+ if (item) {
+ if (apol_vector_append(res->items, (void *)item)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto inc_net_access_run_fail;
+ }
+ item = NULL;
+ }
+ net_state_destroy(&state);
+ }
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ inc_net_access_run_fail:
+ apol_avrule_query_destroy(&avrule_query);
+ apol_vector_destroy(&avrule_vector);
+ qpol_iterator_destroy(&iter);
+ free(perm_name);
+ net_state_destroy(&state);
+ sechk_item_free(item);
+ sechk_proof_free(proof);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text and prints the
+ * results to stdout. */
+int inc_net_access_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i network domains with insufficient permissions.\n", num_items);
+ }
+
+ /* Print current permissions then the permissions that are missing */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ i = 0;
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/inc_net_access.h b/sechecker/modules/inc_net_access.h
new file mode 100644
index 0000000..746db40
--- /dev/null
+++ b/sechecker/modules/inc_net_access.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ * Defines the interface for the incomplete network access module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INC_NET_ACCESS_H
+#define INC_NET_ACCESS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/avrule-query.h>
+
+/* Module functions:
+ * Do not change any of these prototypes or you will not be
+ * able to run the module in the library */
+ int inc_net_access_register(sechk_lib_t * lib);
+ int inc_net_access_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int inc_net_access_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int inc_net_access_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/roles_wo_allow.c b/sechecker/modules/roles_wo_allow.c
new file mode 100644
index 0000000..3d55bc8
--- /dev/null
+++ b/sechecker/modules/roles_wo_allow.c
@@ -0,0 +1,387 @@
+/**
+ * @file
+ * Implementation of the roles without allow rules module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "roles_wo_allow.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "roles_wo_allow";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int roles_wo_allow_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "roles with no roleallow rules";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds roles defined in the policy that are not used in any role\n"
+ "allow rules. It is not possible to transition to or from any role that does not\n" "have any role allow rules.\n";
+ mod->opt_description =
+ "Module requirements:\n" " none\n" "Module dependencies:\n" " none\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_allow_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_allow_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_allow_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_allow_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int roles_wo_allow_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+int roles_wo_allow_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ apol_vector_t *role_vector;
+ apol_vector_t *role_allow_vector;
+ apol_role_allow_query_t *role_allow_query = NULL;
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ res->item_type = SECHK_ITEM_ROLE;
+
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+
+ if (apol_role_get_by_query(policy, NULL, &role_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ if ((role_allow_query = apol_role_allow_query_create()) == NULL) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(role_vector); i++) {
+ const qpol_role_t *role;
+ const char *role_name;
+
+ role = apol_vector_get_element(role_vector, i);
+ qpol_role_get_name(apol_policy_get_qpol(policy), role, &role_name);
+
+ if (!strcmp(role_name, "object_r"))
+ continue;
+
+ apol_role_allow_query_set_source(policy, role_allow_query, role_name);
+ apol_role_allow_query_set_source_any(policy, role_allow_query, 1);
+ apol_role_allow_get_by_query(policy, role_allow_query, &role_allow_vector);
+ if (apol_vector_get_size(role_allow_vector) > 0) {
+ apol_vector_destroy(&role_allow_vector);
+ continue;
+ }
+ apol_vector_destroy(&role_allow_vector);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ proof->type = SECHK_ITEM_ROLE;
+ proof->text = strdup("Role has no allow.\n");
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ item->item = (void *)role;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_allow_run_fail;
+ }
+ item = NULL;
+ proof = NULL;
+ }
+ apol_vector_destroy(&role_vector);
+ apol_vector_destroy(&role_allow_vector);
+ apol_role_allow_query_destroy(&role_allow_query);
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ roles_wo_allow_run_fail:
+ apol_vector_destroy(&role_vector);
+ apol_vector_destroy(&role_allow_vector);
+ apol_role_allow_query_destroy(&role_allow_query);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int roles_wo_allow_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ size_t i = 0, j = 0, num_items;
+ const qpol_role_t *role;
+ const char *role_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd roles.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following roles do not appear in any allow rules.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & (SECHK_OUT_LIST | SECHK_OUT_PROOF)) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ role = (qpol_role_t *) item->item;
+ qpol_role_get_name(apol_policy_get_qpol(policy), role, &role_name);
+ printf("%s%s", role_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int roles_wo_allow_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/roles_wo_allow.h b/sechecker/modules/roles_wo_allow.h
new file mode 100644
index 0000000..c9dd44e
--- /dev/null
+++ b/sechecker/modules/roles_wo_allow.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ * Defines the interface for the roles without allow rules module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ROLES_WO_ALLOW
+#define ROLES_WO_ALLOW
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/role-query.h>
+#include <apol/rbacrule-query.h>
+
+ int roles_wo_allow_register(sechk_lib_t * lib);
+ int roles_wo_allow_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_allow_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_allow_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_allow_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/roles_wo_types.c b/sechecker/modules/roles_wo_types.c
new file mode 100644
index 0000000..ed38580
--- /dev/null
+++ b/sechecker/modules/roles_wo_types.c
@@ -0,0 +1,330 @@
+/**
+ * @file
+ * Implementation of the roles without types module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "roles_wo_types.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "roles_wo_types";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int roles_wo_types_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign descriptions */
+ mod->brief_description = "roles with no types";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds roles in the policy that have no types. A role with no types \n"
+ "cannot form a valid context.\n";
+ mod->opt_description =
+ "Module requirements:\n" " none\n" "Module dependencies:\n" " none\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_types_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_types_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_types_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int roles_wo_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Ivalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+int roles_wo_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ apol_vector_t *role_vector;
+ qpol_iterator_t *type_iter = NULL;
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Ivalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+ res->item_type = SECHK_ITEM_ROLE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+
+ if (apol_role_get_by_query(policy, NULL, &role_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(role_vector); i++) {
+ const qpol_role_t *role;
+ const char *role_name;
+ int at_end;
+
+ role = apol_vector_get_element(role_vector, i);
+ qpol_role_get_name(apol_policy_get_qpol(policy), role, &role_name);
+
+ if (!strcmp(role_name, "object_r"))
+ continue;
+
+ qpol_role_get_type_iter(apol_policy_get_qpol(policy), role, &type_iter);
+ at_end = qpol_iterator_end(type_iter);
+ qpol_iterator_destroy(&type_iter);
+ if (!at_end)
+ continue;
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+ proof->type = SECHK_ITEM_ROLE;
+ asprintf(&proof->text, "role %s has no types", role_name);
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+ item->item = (void *)role;
+ item->test_result = 1;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_types_run_fail;
+ }
+ }
+ apol_vector_destroy(&role_vector);
+
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ roles_wo_types_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text printed in the
+ * report and prints it to stdout. */
+int roles_wo_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ size_t i = 0, j = 0, num_items;
+ const qpol_role_t *role;
+ const char *role_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* display the statistics of the results */
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd roles.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following roles have no associated types:\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & (SECHK_OUT_LIST | SECHK_OUT_PROOF)) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ role = (qpol_role_t *) item->item;
+ qpol_role_get_name(apol_policy_get_qpol(policy), role, &role_name);
+ printf("%s%s", role_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/roles_wo_types.h b/sechecker/modules/roles_wo_types.h
new file mode 100644
index 0000000..1a241d0
--- /dev/null
+++ b/sechecker/modules/roles_wo_types.h
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Defines the interface for the roles without types module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ROLES_WO_TYPES
+#define ROLES_WO_TYPES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/role-query.h>
+
+ int roles_wo_types_register(sechk_lib_t * lib);
+ int roles_wo_types_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_types_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_types_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/roles_wo_users.c b/sechecker/modules/roles_wo_users.c
new file mode 100644
index 0000000..c590880
--- /dev/null
+++ b/sechecker/modules/roles_wo_users.c
@@ -0,0 +1,392 @@
+/**
+ * @file
+ * Defines the interface for the roles without users module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "roles_wo_users.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "roles_wo_users";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int roles_wo_users_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "roles not assigned to users";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds roles that are not assigned to users. If a role is not \n"
+ "assigned to a user it cannot form a valid context.\n";
+ mod->opt_description =
+ "Module requirements:\n" " none\n" "Module dependencies:\n" " none\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_users_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_users_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_users_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = roles_wo_users_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int roles_wo_users_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+int roles_wo_users_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ apol_vector_t *role_vector;
+ apol_vector_t *user_vector;
+ apol_user_query_t *user_query = NULL;
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ res->item_type = SECHK_ITEM_ROLE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+
+ if (!(user_query = apol_user_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+
+ if (apol_role_get_by_query(policy, NULL, &role_vector) < 0) {
+ error = errno;
+ ERR(policy, "%s", "Unable to retrieve roles");
+ return -1;
+ }
+
+ for (i = 0; i < apol_vector_get_size(role_vector); i++) {
+ const qpol_role_t *role;
+ const char *role_name;
+
+ role = apol_vector_get_element(role_vector, i);
+ qpol_role_get_name(apol_policy_get_qpol(policy), role, &role_name);
+
+ if (!strcmp(role_name, "object_r"))
+ continue;
+
+ apol_user_query_set_role(policy, user_query, role_name);
+ apol_user_get_by_query(policy, user_query, &user_vector);
+ if (apol_vector_get_size(user_vector) > 0) {
+ apol_vector_destroy(&user_vector);
+ continue;
+ }
+ apol_vector_destroy(&user_vector);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ item->item = (void *)role;
+ item->test_result = 1;
+ proof->type = SECHK_ITEM_ROLE;
+ proof->text = strdup("This role is not assigned to any user.");
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto roles_wo_users_run_fail;
+ }
+ item = NULL;
+ proof = NULL;
+ }
+ apol_vector_destroy(&role_vector);
+ apol_vector_destroy(&user_vector);
+ apol_user_query_destroy(&user_query);
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ roles_wo_users_run_fail:
+ apol_vector_destroy(&role_vector);
+ apol_vector_destroy(&user_vector);
+ apol_user_query_destroy(&user_query);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text printed in the
+ * report and prints it to stdout. */
+int roles_wo_users_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ const qpol_role_t *role;
+ const char *role_name;
+ size_t i = 0, j = 0, num_items;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd roles.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following roles are not associated with any users.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & (SECHK_OUT_LIST | SECHK_OUT_PROOF)) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ role = (qpol_role_t *) item->item;
+ qpol_role_get_name(apol_policy_get_qpol(policy), role, &role_name);
+ printf("%s%s", role_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int roles_wo_users_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+
+ return 0;
+}
diff --git a/sechecker/modules/roles_wo_users.h b/sechecker/modules/roles_wo_users.h
new file mode 100644
index 0000000..261bebb
--- /dev/null
+++ b/sechecker/modules/roles_wo_users.h
@@ -0,0 +1,53 @@
+/**
+ * @file
+ * Defines the interface for the roles without users module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ROLES_WO_USERS
+#define ROLES_WO_USERS
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/user-query.h>
+#include <apol/role-query.h>
+
+/* Module functions:
+ * NOTE: while using a modular format SEChecker is built
+ * statically; this means that all modules and their functions
+ * are in the same namespace. */
+ int roles_wo_users_register(sechk_lib_t * lib);
+ int roles_wo_users_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_users_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_users_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int roles_wo_users_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/spurious_audit.c b/sechecker/modules/spurious_audit.c
new file mode 100644
index 0000000..21698df
--- /dev/null
+++ b/sechecker/modules/spurious_audit.c
@@ -0,0 +1,778 @@
+/**
+ * @file
+ * Implementation of the spurious audit rule module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Ryan Jordan rjordan@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "sechecker.h"
+#include "spurious_audit.h"
+#include <apol/policy-query.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "spurious_audit";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int spurious_audit_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "no library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s%s%s", "module unknown, \"", mod_name, "\"");
+ errno = ENOENT;
+ return -1;
+ }
+
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "audit rules with no effect";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds audit rules in the policy which do not affect the auditing of\n"
+ "the policy. This could happen in the following situations:\n"
+ "\n"
+ " 1) there is an allow rule with the same key and permissions for a dontaudit\n"
+ " rule\n"
+ " 2) there is an auditallow rule without an allow rule with the same key or\n"
+ " with permissions that do not appear in an allow rule with the same key.\n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(lib->policy, "%s", strerror(errno));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = spurious_audit_init;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(lib->policy, "%s", strerror(errno));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = spurious_audit_run;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(lib->policy, "%s", strerror(errno));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = spurious_audit_print;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int spurious_audit_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "%s%s%s", "wrong module (", mod->name, ")");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+int spurious_audit_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ int error, rule_found;
+ apol_avrule_query_t *query = 0;
+ apol_vector_t *allow_rules = NULL, *auditallow_rules = NULL;
+ apol_vector_t *dontaudit_rules = NULL, *perm_vector1 = NULL;
+ apol_vector_t *perm_vector2 = NULL, *perm_intersection = NULL;
+ apol_vector_t *tmp_v = NULL;
+ size_t i, j, k, l, tmp_counter;
+ const qpol_avrule_t *rule1, *rule2;
+ const qpol_type_t *source, *target;
+ const qpol_class_t *object;
+ qpol_iterator_t *perm_iter1, *perm_iter2;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ char *string1, *string2, *tmp;
+ const char *src_name, *tgt_name, *obj_name, *perms;
+
+ error = rule_found = 0;
+ allow_rules = auditallow_rules = dontaudit_rules = perm_vector1 = perm_vector2 = perm_intersection = NULL;
+ rule1 = rule2 = 0;
+ source = target = NULL;
+ perm_iter1 = perm_iter2 = NULL;
+ string1 = string2 = tmp = NULL;
+ src_name = tgt_name = obj_name = perms = NULL;
+
+ if (!mod || !policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "%s%s%s", "wrong module (", mod->name, ")");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ res->item_type = SECHK_ITEM_AVRULE;
+
+ query = apol_avrule_query_create();
+ if (!query) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ apol_avrule_query_set_rules(policy, query, QPOL_RULE_AUDITALLOW);
+ if (apol_avrule_get_by_query(policy, query, &auditallow_rules)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ apol_avrule_query_set_rules(policy, query, QPOL_RULE_DONTAUDIT);
+ if (apol_avrule_get_by_query(policy, query, &dontaudit_rules)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* First error case: Allow && Don't Audit */
+ for (i = 0; i < apol_vector_get_size(dontaudit_rules); i++) {
+ /* get first (DONT_AUDIT) rule */
+ rule1 = (qpol_avrule_t *) apol_vector_get_element(dontaudit_rules, i);
+ if (!rule1) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* get source, target, object for Don't Audit rule */
+ if (qpol_avrule_get_source_type(q, rule1, &source)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_avrule_get_target_type(q, rule1, &target)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_avrule_get_object_class(q, rule1, &object)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* extract name strings from source, target, object */
+ if (qpol_type_get_name(q, source, &src_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_type_get_name(q, target, &tgt_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_class_get_name(q, object, &obj_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* configure Allow rule query to match above Don't Audit rule */
+ apol_avrule_query_set_rules(policy, query, QPOL_RULE_ALLOW);
+ apol_avrule_query_set_source(policy, query, src_name, 1);
+ apol_avrule_query_set_target(policy, query, tgt_name, 1);
+ apol_avrule_query_append_class(policy, query, NULL);
+ apol_avrule_query_append_class(policy, query, obj_name);
+
+ /* get vector of matching ALLOW rules */
+ if (apol_avrule_get_by_query(policy, query, &allow_rules)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ if (apol_vector_get_size(allow_rules) != 0) {
+ /* Bad News: Allow && Don't Audit */
+ for (j = 0; j < apol_vector_get_size(allow_rules); j++) {
+ /* get second (Allow) rule */
+ rule2 = (qpol_avrule_t *) apol_vector_get_element(allow_rules, j);
+ if (!rule2) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* get permission iterators for both rules, and make vectors from them */
+ if (qpol_avrule_get_perm_iter(q, rule1, &perm_iter1)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ perm_vector1 = apol_vector_create_from_iter(perm_iter1, free);
+ if (!perm_vector1) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ if (qpol_avrule_get_perm_iter(q, rule2, &perm_iter2)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ perm_vector2 = apol_vector_create_from_iter(perm_iter2, free);
+ if (!perm_vector2) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* create intersection of permissions */
+ perm_intersection = apol_vector_create_from_intersection(perm_vector1,
+ perm_vector2, apol_str_strcmp, NULL);
+ if (!perm_intersection) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ if (apol_vector_get_size(perm_intersection) != 0) {
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ proof->elem = (void *)rule2; /* proof is the allow rule */
+ proof->type = SECHK_ITEM_AVRULE;
+ /* text will show the permissions that conflict */
+ tmp_counter = 0;
+ for (k = 0; k < apol_vector_get_size(perm_intersection); k++) {
+ apol_str_append(&(proof->text), &tmp_counter,
+ (char *)apol_vector_get_element(perm_intersection, k));
+ if (k != (apol_vector_get_size(perm_intersection) - 1))
+ apol_str_append(&(proof->text), &tmp_counter, ", ");
+ }
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ item->item = (void *)rule1; /* item is the dontaudit rule */
+ }
+ if (!item->proof) {
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ apol_vector_append(item->proof, (void *)proof);
+
+ } else {
+ /* these two rules don't overlap, no problem */
+ }
+ /* clean up */
+ rule2 = NULL;
+ apol_vector_destroy(&perm_vector1);
+ apol_vector_destroy(&perm_vector2);
+ apol_vector_destroy(&perm_intersection);
+ qpol_iterator_destroy(&perm_iter1);
+ qpol_iterator_destroy(&perm_iter2);
+ }
+ apol_vector_destroy(&allow_rules);
+ if (!res->items) {
+ res->items = apol_vector_create(sechk_item_free);
+ if (!res->items) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ if (item)
+ apol_vector_append(res->items, (void *)item);
+
+ item = NULL;
+ }
+ apol_vector_destroy(&allow_rules);
+ }
+ apol_vector_destroy(&dontaudit_rules);
+
+ /* Second error case: AuditAllow w/out Allow */
+ for (i = 0; i < apol_vector_get_size(auditallow_rules); i++) {
+ /* get first (AuditAllow) rule */
+ rule1 = (qpol_avrule_t *) apol_vector_get_element(auditallow_rules, i);
+ if (!rule1) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ /* get first rule's source, target, object class */
+ if (qpol_avrule_get_source_type(q, rule1, &source)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_avrule_get_target_type(q, rule1, &target)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_avrule_get_object_class(q, rule1, &object)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ /* extract name strings from source, target, object */
+ if (qpol_type_get_name(q, source, &src_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_type_get_name(q, target, &tgt_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (qpol_class_get_name(q, object, &obj_name)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* configure ALLOW rule query to match above rule */
+ apol_avrule_query_set_rules(policy, query, QPOL_RULE_ALLOW);
+ apol_avrule_query_set_source(policy, query, src_name, 1);
+ apol_avrule_query_set_target(policy, query, tgt_name, 1);
+ apol_avrule_query_append_class(policy, query, obj_name);
+
+ if (apol_avrule_get_by_query(policy, query, &allow_rules)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ if (!apol_vector_get_size(allow_rules)) {
+ /* No ALLOW rule for given AUDIT_ALLOW rule */
+
+ /* Make proof: missing allow rule */
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ proof->elem = NULL;
+ proof->type = SECHK_ITEM_AVRULE;
+
+ /* grab permisisons of auditallow rule, and make text */
+ if (qpol_avrule_get_perm_iter(q, rule1, &perm_iter1)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ perm_vector1 = apol_vector_create_from_iter(perm_iter1, free);
+ if (!perm_vector1) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ tmp_counter = 0;
+ for (j = 0; j < apol_vector_get_size(perm_vector1); j++) {
+ if (apol_str_append(&(proof->text), &tmp_counter, (char *)apol_vector_get_element(perm_vector1, j))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (j != (apol_vector_get_size(perm_vector1) - 1)) {
+ if (apol_str_append(&(proof->text), &tmp_counter, " ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ }
+ apol_vector_destroy(&perm_vector1);
+ qpol_iterator_destroy(&perm_iter1);
+
+ /* Make item: inconsistent auditallow rule */
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ item->item = (void *)rule1;
+ if (!item->proof) {
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ apol_vector_append(item->proof, (void *)proof);
+
+ /* Add item to test result */
+ if (!res->items) {
+ res->items = apol_vector_create(sechk_item_free);
+ if (!res->items) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ if (item)
+ apol_vector_append(res->items, (void *)item);
+ item = NULL;
+ apol_vector_destroy(&allow_rules);
+ continue;
+ }
+
+ /* Here we have AuditAllow rule and Allow rule(s) with same key */
+ /* Checking to make sure they have the same permissions */
+
+ /* Make vector of AuditAllow permissions */
+ if (qpol_avrule_get_perm_iter(q, rule1, &perm_iter1)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ perm_vector1 = apol_vector_create_from_iter(perm_iter1, free);
+ if (!perm_vector1) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ /* Get permissions vector for Allow rule(s) */
+ for (j = 0; j < apol_vector_get_size(allow_rules); j++) {
+ /* get Allow rule */
+ rule2 = (qpol_avrule_t *) apol_vector_get_element(allow_rules, j);
+ if (!rule2) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ if (qpol_avrule_get_perm_iter(q, rule2, &perm_iter2)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+
+ if (!perm_vector2) {
+ perm_vector2 = apol_vector_create(free);
+ if (!perm_vector2) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+
+ /* concatenate permissions from this rule, check for errors, all in one go */
+ if (apol_vector_cat(perm_vector2, (tmp_v = apol_vector_create_from_iter(perm_iter2, NULL)))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ apol_vector_destroy(&tmp_v);
+ goto spurious_audit_run_fail;
+ }
+ apol_vector_destroy(&tmp_v);
+ }
+
+ /* Find intersection of permission, put into a vector */
+ perm_intersection = apol_vector_create_from_intersection(perm_vector1, perm_vector2, apol_str_strcmp, NULL);
+
+ if (apol_vector_get_size(perm_intersection) != apol_vector_get_size(perm_vector1)) {
+ /* Auditallow rule audits things that are not allowed */
+
+ /* item for result is the AuditAllow rule */
+ if (!item) {
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));;
+ goto spurious_audit_run_fail;
+ }
+ }
+ item->item = (void *)rule1;
+ /* proof is the lack of Allow rule */
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ proof->elem = NULL;
+ proof->type = SECHK_ITEM_AVRULE;
+
+ /* the next series of if statements prints the following:
+ * missing: allow <src_name> <tgt_name> : <obj_name> { perms }; */
+ tmp_counter = 0;
+ for (j = 0; j < apol_vector_get_size(perm_vector1); j++) {
+ string1 = (char *)apol_vector_get_element(perm_vector1, j);
+ if (apol_vector_get_index(perm_intersection, (void *)string1, apol_str_strcmp, NULL, &l) < 0) {
+ if (apol_str_append(&(proof->text), &tmp_counter, string1)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ if (apol_str_append(&(proof->text), &tmp_counter, " ")) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ string1 = NULL;
+ }
+
+ if (!item->proof) {
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ apol_vector_append(item->proof, (void *)proof);
+ proof = NULL;
+ if (!res->items) {
+ res->items = apol_vector_create(sechk_item_free);
+ if (!res->items) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto spurious_audit_run_fail;
+ }
+ }
+ apol_vector_append(res->items, (void *)item);
+ item = NULL;
+ }
+
+ /* clean up */
+ apol_vector_destroy(&perm_vector1);
+ apol_vector_destroy(&perm_vector2);
+ apol_vector_destroy(&perm_intersection);
+ apol_vector_destroy(&allow_rules);
+ qpol_iterator_destroy(&perm_iter1);
+ qpol_iterator_destroy(&perm_iter2);
+ }
+
+ apol_vector_destroy(&auditallow_rules);
+ apol_avrule_query_destroy(&query);
+
+ mod->result = res;
+
+ /* If module finds something that would be considered a fail
+ * on the policy return 1 here */
+ if (apol_vector_get_size(res->items) > 0)
+ return 1;
+
+ return 0;
+
+ spurious_audit_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ apol_vector_destroy(&allow_rules);
+ apol_vector_destroy(&auditallow_rules);
+ apol_vector_destroy(&dontaudit_rules);
+ apol_vector_destroy(&perm_vector1);
+ apol_vector_destroy(&perm_vector2);
+ apol_vector_destroy(&perm_intersection);
+ qpol_iterator_destroy(&perm_iter1);
+ qpol_iterator_destroy(&perm_iter2);
+ apol_avrule_query_destroy(&query);
+ free(tmp);
+ tmp = NULL;
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text and prints the
+ * results to stdout. */
+int spurious_audit_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0;
+ uint32_t ruletype;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ char *tmp;
+
+ if (!mod || !policy) {
+ ERR(NULL, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "%s%s%s", "wrong module (", mod->name, ")");
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+
+ if (!mod->result) {
+ ERR(policy, "%s%s%s", "module ", mod->name, "has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ i = apol_vector_get_size(mod->result->items);
+ printf("Found %zd rule%s.\n", i, (i == 1) ? "" : "s");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < apol_vector_get_size(mod->result->items); i++) {
+ item = apol_vector_get_element(mod->result->items, i);
+ printf("%s\n", (tmp = apol_avrule_render(policy, (qpol_avrule_t *) item->item)));
+ free(tmp);
+ }
+ printf("\n");
+ }
+ /* The proof report component is a display of a list of items
+ * with an indented list of proof statements supporting the result
+ * of the check for that item. */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (i = 0; i < apol_vector_get_size(mod->result->items); i++) {
+ item = apol_vector_get_element(mod->result->items, i);
+ printf("%s\n", (tmp = apol_avrule_render(policy, (qpol_avrule_t *) item->item)));
+
+ qpol_avrule_get_rule_type(q, (qpol_avrule_t *) item->item, &ruletype);
+ if (ruletype == QPOL_RULE_DONTAUDIT) {
+ for (j = 0; j < apol_vector_get_size(item->proof); j++) {
+ proof = apol_vector_get_element(item->proof, j);
+ printf("\tinconsistent: ");
+ printf("%s\n", apol_avrule_render(policy, (qpol_avrule_t *) proof->elem));
+ printf("\tinconsistent permissions:\n");
+ printf("\t%s\n", proof->text);
+ }
+ } else if (ruletype == QPOL_RULE_AUDITALLOW) {
+ printf("\tmissing: ");
+ printf("%s\n", strstr(apol_avrule_render(policy, (qpol_avrule_t *) item->item), "allow"));
+ printf("\tmissing permissions:\n");
+ for (j = 0; j < apol_vector_get_size(item->proof); j++) {
+ proof = apol_vector_get_element(item->proof, j);
+ printf("\t%s\n", proof->text);
+ }
+ }
+ printf("\n");
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/sechecker/modules/spurious_audit.h b/sechecker/modules/spurious_audit.h
new file mode 100644
index 0000000..19b93f6
--- /dev/null
+++ b/sechecker/modules/spurious_audit.h
@@ -0,0 +1,53 @@
+/**
+ * @file
+ * Defines the interface for the spurious audit rule module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Ryan Jordan rjordan@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SPURIOUS_AUDIT
+#define SPURIOUS_AUDIT
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+
+#define SECHK_SPUR_AU_AA_MISS 0x01
+#define SECHK_SPUR_AU_AA_PART 0x02
+#define SECHK_SPUR_AU_DA_FULL 0x04
+#define SECHK_SPUR_AU_DA_PART 0x08
+
+/* Module functions: */
+ int spurious_audit_register(sechk_lib_t * lib);
+ int spurious_audit_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int spurious_audit_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int spurious_audit_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/template/profiles.readme b/sechecker/modules/template/profiles.readme
new file mode 100644
index 0000000..f781ee0
--- /dev/null
+++ b/sechecker/modules/template/profiles.readme
@@ -0,0 +1,142 @@
+How to write a profile for SEChecker
+=====================================
+
+Table of Contents
+========================
+1. Use of Profiles
+2. Format of the Profile
+ 2.0 <sechecker>
+ 2.1 <profile>
+ 2.2 <module>
+ 2.3 <output>
+ 2.4 <option>
+3. Example Profile
+
+1. Use of Profiles
+==========================
+SEChecker has a wide variety of modules which perform various tests
+on the policy and/or system. To make the management and running of
+these modules easier, several profiles are defined.
+
+A profile is used to run a set of modules with options set in a way
+that the resulting report reflects a specific security goal.
+
+To write your own profile, create a new XML file named <profile name>.prof.
+The format of the file is detailed below.
+
+2. Format of the Profile
+=========================
+A profile is an XML file loaded by SEChecker to run a specific set of tests.
+The profile recognizes the following tags
+
+2.0 <sechecker>
+===========================
+The sechecker tag should be the first open tag in the file and the final
+tag to be closed. The tag has a single attribute version, which should
+be set to the current version of SEChecker you are using. To find your
+version number, run: "sechecker --version".
+
+<sechecker version="1.0">
+
+Be sure to remember to close this tag at the end of the file.
+
+2.1 <profile>
+===========================
+The profile tag tells the parser that SEChecker should interpret this
+file as a profile. This tag has no attributes.
+
+<profile>
+
+Close this tag just before the sechecker tag is closed at the end of the file
+
+2.2 <module>
+===========================
+The module tag tells SEChecker that a particular module should be run for
+this profile. The only attribute is name.
+
+<module name="mod_name">
+
+This tag is closed after all other tags related to that module
+
+2.3 <output>
+=========================
+The output tag tells SEChecker to use this output format for the module
+in which it appears. This tag is optional and has one attribute, value.
+
+<output value="short"/>
+
+This tag should close itself. The valid values are:
+ none - do not print anything in the report; only run this module
+ as dependency of another module.
+ quiet - print only the stats and header in the report
+ short - print the header, stats, and a list of items found by the
+ module without any accompanying proof
+ long - print the header, stats, and a list of items found by the
+ module with proof of the result following each item
+ verbose - print all possible output including the header, stats,
+ a list of items and a list of items with proof
+
+NOTE: any of the above values other than "none" are overridden by the
+ command line output flags. Setting an output value in a profile
+ overrides the default setting in the configuration file for this
+ profile only.
+
+2.4 <option>
+=======================
+The option tag allows a profile to specify additional options for a module.
+The option tag has two mandatory attributes, "name" and "value".
+The values of these attributes is specific to the module for which the
+option is specified. Options specified in a profile are used in addition
+to those in the configuration file.
+
+<option name="option_name" value="some_value"/>
+
+This tag closes itself. As its name implies this tag is optional.
+
+3. Example Profile
+======================
+The following is a brief example of a profile
+
+<sechecker version="1.0">
+<profile>
+
+ <module name="mod1">
+ </module>
+
+ <module name="mod2">
+ <output value="none"/>
+ <option name="attribute" value="my_attrib"/>
+ </module>
+
+ <module name="mod3">
+ <option name="foo" value="bar"/>
+ </module>
+
+ <module name="mod4">
+ <output value="short"/>
+ </module>
+
+ <module name="mod5">
+ <output value="quiet"/>
+ <option name="type" value="shadow_t"/>
+ </module>
+
+</profile>
+</sechecker>
+
+The result of this profile would be:
+- run mod1 with default configuration;
+ print with default settings
+- run mod2 with the additional attribute my_attrib,
+ but don't print its results
+- run mod3 with option foo set to bar (in addition to any other settings);
+ print with default settings
+- run mod4;
+ print in short output
+- run mod5 with additional type shadow_t;
+ print using quiet output
+
+If there are also modules mod6 and mod7, neither would be run unless
+one of the other modules (mod1-5) had a dependency on them.
+
+
diff --git a/sechecker/modules/template/template.howto b/sechecker/modules/template/template.howto
new file mode 100644
index 0000000..09151e0
--- /dev/null
+++ b/sechecker/modules/template/template.howto
@@ -0,0 +1,13 @@
+Instructions for using the template to add new modules
+========================================================
+1. copy the xx.c and xx.h files to the modules directory
+2. rename the files and replace the text xx with the
+ module name in both files
+3. add the register function for your module xx_register
+ to the register_list.h and register_list.c entries
+4. add options and any requirements or dependencies to
+ the decription in the register function
+5. fill out TODO sections of the template with logic for
+ your module
+6. recompile
+
diff --git a/sechecker/modules/template/xx.c b/sechecker/modules/template/xx.c
new file mode 100644
index 0000000..20f7f5f
--- /dev/null
+++ b/sechecker/modules/template/xx.c
@@ -0,0 +1,393 @@
+/**
+ * @file
+ * Implementation of the xx module.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* NOTE: TODO This is a module template, which includes all the necessary
+ * infrastructure to implement a basic SEChecker module. To use this template
+ * first replace all instances of the string xx with the name of the module,
+ * then edit or complete all sections marked TODO as instructed. */
+
+#include <config.h>
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include "xx.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem of the file
+ * name; it should also match the prefix of all functions defined in this
+ * module and the private data storage structure */
+static const char *const mod_name = "xx";
+
+/* The register function registers all of a module's functions
+ * with the library. TODO: Edit the description fields to include all
+ * options, requirements, and dependencies. Also provide a brief summary
+ * of the steps performed in this module's checks. If you are adding
+ * additional functions you need other modules to call, see the note at
+ * the bottom of this function to do so. */
+int xx_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+ sechk_name_value_t *nv = NULL;
+
+ if (!lib) {
+ ERR(NULL, "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the register list file and their name and options
+ * are stored in the module vector of the library. The name is looked up to
+ * determine where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(lib->policy, "Module unknown \"%s\"", mod_name);
+ errno = ENOENT;
+ return -1;
+ }
+
+ mod->parent_lib = lib;
+
+ /* TODO: assign the descriptions */
+ mod->brief_description = "";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "TODO: detailed description for this module.\n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = "TODO: set proper severity";
+
+ /* TODO: assign default options (remove if none)
+ * fill name and value and repeat as needed */
+ nv = sechk_name_value_new("", "");
+ apol_vector_append(mod->options, (void *)nv);
+
+ /* TODO: assign requirements (remove if none)
+ * fill name and value and repeat as needed */
+ nv = sechk_name_value_new("", "");
+ apol_vector_append(mod->requirements, (void *)nv);
+
+ /* TODO: assign dependencies (remove if not needed)
+ * fill name and value and repeat as needed */
+ nv = sechk_name_value_new("", "");
+ apol_vector_append(mod->dependencies, (void *)nv);
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = xx_init;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = xx_run;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ /* TODO: if the module does not have a private data structure
+ * set this function pointer to NULL */
+ mod->data_free = xx_data_free;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(lib->policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = xx_print;
+ apol_vector_append(mod->functions, (void *)fn_struct);
+
+ /* TODO: (optional) add any other functions needed here,
+ * add a block as above for each additional function */
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object and
+ * initializes its values. Add any option processing logic as indicated below.
+ * TODO: add options processing logic */
+int xx_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_name_value_t *opt = NULL;
+ xx_data_t *datum = NULL;
+ int error = 0;
+ size_t i = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)\n", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* If the module doesnot have a privte data sturcture replace the following
+ * block with "mod->data = NULL" */
+ datum = xx_data_new();
+ if (!datum) {
+ error = errno;
+ ERR(policy, "Error: %s\n", strerror(error));
+ errno = error;
+ return -1;
+ }
+ mod->data = datum;
+
+ for (i = 0; i < apol_vector_get_size(mod->options); i++) {
+ opt = apol_vector_get_element(mod->options, i);
+ /* TODO: check options
+ * check strings opt->name and opt->value of each option
+ * to set the members of the private data storage object
+ * (pointed to by datum).
+ * i.e. if (!strcmp(...)) {} else if (!strcmp((...)) etc.
+ * There should be relatively few options for any one module.
+ * If too many options are needed consider splitting the check
+ * into multiple modules and using dependencies. It is desirable
+ * for all checks to be a simple and granular as is possible */
+ }
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found
+ * TODO: add check logic */
+int xx_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ xx_data_t *datum;
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ int error = 0;
+
+ /* TODO: define any aditional variables needed */
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ datum = (xx_data_t *) mod->data;
+ res = sechk_result_new();
+ if (!res) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto xx_run_fail;
+ }
+ /* TODO: set res->item_type to indicate which array the item_id indexes
+ * use values from the sechk_item_type_e enum (see sechecker.h) */
+
+ /* TODO: check logic here
+ * Perform check here. Create and initialize items and proof as found,
+ * appending to the appropriate vectors.
+ * For examples of the type of code to use here see other modules. */
+
+ mod->result = res;
+
+ /* If module finds something that would be considered a failure
+ * of the policy return 1 here */
+ if (apol_vector_get_size(res->items) > 0)
+ return 1;
+
+ return 0;
+
+ xx_run_fail:
+ /* TODO: free any other memory allocated during check logic */
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The free function frees the private data of a module
+ * TODO: be sure to free any allocated space in the private data */
+void xx_data_free(void *data)
+{
+ xx_data_t *datum = (xx_data_t *) data;
+
+ if (datum) {
+ /* TODO: free any allocated members of the module's
+ * private data structure */
+ }
+
+ free(data);
+}
+
+/* The print function generates the text and prints the results to stdout. The
+ * outline below prints the standard format of a report section. Some modules
+ * may not have results in a format that can be represented by this outline and
+ * will need a different specification. It is required that each of the flags
+ * for output components be tested in this function (stats, list, proof,
+ * detailed, and brief) TODO: fill in the indicated information in the report
+ * fields as indicated below. Some alteration may be necessary for checks that
+ * perform different analyses */
+int xx_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ xx_data_t *datum = NULL;
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = (xx_data_t *) mod->data;
+ outformat = mod->outputformat;
+
+ if (!mod->result) {
+ ERR(policy, "Module %s has not been run", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ /* TODO: display the statistics of the results
+ * typical text is "Found %i <itemtype>.\n"
+ * additional information may be printed here depending upon
+ * the amount of data gathered in the check */
+ if (outformat & SECHK_OUT_STATS) {
+ /* TODO: "Found %i <itemtype>.\n": enter itemtype */
+ printf("Found %zd .\n", apol_vector_get_size(mod->result->items));
+ /* TODO: any additional generated statistics */
+ }
+ /* The list report component is a display of all items found without any
+ * supporting proof. The default method is to display a comma separated list
+ * four items to a line this may need to be changed for longer items.
+ * TODO: you will need to enter the string representation of
+ * each item as the second parameter in the printf statement
+ * in place of the empty string.
+ * NOTE: if the item is a type of rule print only one per line. */
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < apol_vector_get_size(mod->result->items); i++) {
+ item = apol_vector_get_element(mod->result->items, i);
+ i++;
+ /* TODO: (optional) change the number below to
+ * print more or less than 4 items per line */
+ i %= 4;
+ /* TODO: second parameter: item name */
+ printf("%s%s", "", (i ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+ /* The proof report component is a display of a list of items with an
+ * indented list of proof statements supporting the result of the check for
+ * that item (e.g. rules with a given type). Each proof element is then
+ * displayed in an indented list one per line below it.
+ * TODO: the name of the item should be entered below.
+ * NOTE: certain checks may need to further modify this report component if
+ * the results cannot be presented in this format */
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (i = 0; i < apol_vector_get_size(mod->result->items); i++) {
+ item = apol_vector_get_element(mod->result->items, i);
+ printf("%s", ""); /* TODO: item name */
+ printf(" - severity: %s\n", sechk_item_sev(item));
+ for (j = 0; j < apol_vector_get_size(item->proof); j++) {
+ proof = apol_vector_get_element(item->proof, j);
+ printf("\t%s\n", proof->text);
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+/* The xx_data_new function allocates and returns an initialized private data
+ * storage structure for this module.
+ * TODO: initialize any non-zero/non-null data (if needed) below */
+xx_data_t *xx_data_new(void)
+{
+ xx_data_t *datum = NULL;
+
+ datum = (xx_data_t *) calloc(1, sizeof(xx_data_t));
+
+ /* TODO: initialize data */
+
+ return datum;
+}
diff --git a/sechecker/modules/template/xx.h b/sechecker/modules/template/xx.h
new file mode 100644
index 0000000..d8c18b8
--- /dev/null
+++ b/sechecker/modules/template/xx.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Defines the interface for the xx module.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* NOTE: TODO This is a module template, which includes all the necessary
+ * infrastructure to implement a basic SEChecker module. To use this template
+ * first replace all instances of the string xx with the name of the module,
+ * then edit or complete all sections marked TODO as instructed. Do not forget
+ * to add an entry in the register_list files (see these files for further
+ * instruction) */
+
+#include "sechecker.h"
+#include <apol/policy.h>
+
+/* The xx_data structure is used to hold the check specific
+ * private data of a module.
+ * TODO: Add any members you need to perform the check or if the module is not
+ * going to need private data remove this declaration and the data_new() and
+ * data_free() functions */
+typedef struct xx_data
+{
+ /* TODO: define members of this data structure
+ * for module's private data */
+} xx_data_t;
+
+/* The following functions are used to allocate and initialize the private data
+ * storage structure for this module and to free all memory used by it. */
+xx_data_t *xx_data_new(void);
+void xx_data_free(void *data);
+
+/* The register function places the needed information about the module in the
+ * library, including description fields and the functions available. TODO: be
+ * sure to add an entry for this function in the register_list files. */
+int xx_register(sechk_lib_t * lib);
+
+/* Module functions:
+ * The following three functions (init, run, and print) must exist for all
+ * modules. NOTE: while using a modular format SEChecker is built statically;
+ * this means that all modules and their functions are in the same namespace.
+ * Be sure to choose a unique name for each module and to set the module name
+ * prefix xx everywhere */
+int xx_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+int xx_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+int xx_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+/* TODO: (optional) Declare any other functions needed by other modules here.
+ * The prototype of the function must be int xx_fn(sechk_module_t *mod,
+ * apol_policy_t *policy, void *arg). For use by the get_module_function()
+ * function, be sure to add a block in the xx_register function to register
+ * your function.
+ * NOTE: While SEChecker is build statically, it is intended that no module
+ * directly call a function from another but instead use get_module_function()
+ * to get the desired function from the library. */
diff --git a/sechecker/modules/types_wo_allow.c b/sechecker/modules/types_wo_allow.c
new file mode 100644
index 0000000..2d0112a
--- /dev/null
+++ b/sechecker/modules/types_wo_allow.c
@@ -0,0 +1,442 @@
+/**
+ * @file
+ * Implementation of the types without allow rules module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "types_wo_allow.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "types_wo_allow";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int types_wo_allow_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "types with no allow rules";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds types defined in the policy that are not used in any allow\n"
+ "rules. A type that is never granted an allow rule in the policy is a dead type.\n"
+ "This means that all attempted access to the type will be denied including\n"
+ "attempts to relabel to a (usable) type. The type may need to be removed from\n"
+ "the policy or some intended access should be granted to the type.\n";
+ mod->opt_description =
+ "Module requirements:\n" " none\n" "Module dependencies:\n" " none\n" "Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = types_wo_allow_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = types_wo_allow_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = types_wo_allow_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup("get_list");
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = types_wo_allow_get_list;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int types_wo_allow_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. This function allocates the result
+ * structure and fills in all relavant item and proof data. */
+int types_wo_allow_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ bool used = false;
+ apol_vector_t *type_vector;
+ apol_vector_t *avrule_vector;
+ apol_avrule_query_t *avrule_query = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+
+ if (!(avrule_query = apol_avrule_query_create())) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+
+ if (apol_type_get_by_query(policy, NULL, &type_vector) < 0) {
+ error = errno;
+ goto types_wo_allow_run_fail;
+ }
+
+ for (i = 0; i < apol_vector_get_size(type_vector); i++) {
+ const qpol_type_t *type;
+ const char *type_name;
+ size_t j;
+
+ used = false;
+ type = apol_vector_get_element(type_vector, i);
+ qpol_type_get_name(q, type, &type_name);
+
+ /* Check source for allow type */
+ apol_avrule_query_set_source(policy, avrule_query, type_name, 1);
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ uint32_t rule_type;
+ qpol_avrule_t *rule;
+
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_rule_type(q, rule, &rule_type);
+ if (rule_type == QPOL_RULE_ALLOW)
+ used = true;
+ }
+ apol_vector_destroy(&avrule_vector);
+ if (used)
+ continue;
+
+ /* Check target for allow type */
+ apol_avrule_query_set_source(policy, avrule_query, NULL, 0);
+ apol_avrule_query_set_target(policy, avrule_query, type_name, 1);
+ apol_avrule_get_by_query(policy, avrule_query, &avrule_vector);
+ for (j = 0; j < apol_vector_get_size(avrule_vector); j++) {
+ uint32_t rule_type;
+ qpol_avrule_t *rule;
+
+ rule = apol_vector_get_element(avrule_vector, j);
+ qpol_avrule_get_rule_type(q, rule, &rule_type);
+ if (rule_type == QPOL_RULE_ALLOW)
+ used = true;
+ }
+ apol_vector_destroy(&avrule_vector);
+ apol_avrule_query_set_target(policy, avrule_query, NULL, 0);
+ if (used)
+ continue;
+
+ /* not used anywhere */
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ item->test_result = 1;
+ item->item = (void *)type;
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ proof->type = SECHK_ITEM_TYPE;
+ proof->text = strdup("This type does not appear in any allow rules.");
+ if (!proof->text) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto types_wo_allow_run_fail;
+ }
+ }
+ apol_vector_destroy(&type_vector);
+ apol_vector_destroy(&avrule_vector);
+ apol_avrule_query_destroy(&avrule_query);
+
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ types_wo_allow_run_fail:
+ apol_vector_destroy(&type_vector);
+ apol_vector_destroy(&avrule_vector);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print function generates the text printed in the
+ * report and prints it to stdout. */
+int types_wo_allow_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k = 0, l = 0, num_items;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ /* display the statistics of the results */
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %i types.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following types do not appear in any allow rules.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & (SECHK_OUT_LIST)) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ j %= 4;
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ }
+ printf("\n");
+ }
+ type = NULL;
+ type_name = NULL;
+
+ return 0;
+}
+
+int types_wo_allow_get_list(sechk_module_t * mod, apol_policy_t * policy __attribute__ ((unused)), void *arg)
+{
+ apol_vector_t **v = arg;
+
+ if (!mod || !arg) {
+ ERR(NULL, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(NULL, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!mod->result) {
+ ERR(NULL, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ v = &mod->result->items;
+ return 0;
+}
diff --git a/sechecker/modules/types_wo_allow.h b/sechecker/modules/types_wo_allow.h
new file mode 100644
index 0000000..325c786
--- /dev/null
+++ b/sechecker/modules/types_wo_allow.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ * Defines the interface for the types without allow rules module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TYPES_WO_ALLOW
+#define TYPES_WO_ALLOW
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/avrule-query.h>
+#include <apol/type-query.h>
+
+ int types_wo_allow_register(sechk_lib_t * lib);
+ int types_wo_allow_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int types_wo_allow_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int types_wo_allow_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int types_wo_allow_get_list(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/modules/unreachable_doms.c b/sechecker/modules/unreachable_doms.c
new file mode 100644
index 0000000..4653c2d
--- /dev/null
+++ b/sechecker/modules/unreachable_doms.c
@@ -0,0 +1,1053 @@
+/**
+ * @file
+ * Implementation of the unreachable domains module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "unreachable_doms.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+
+static bool parse_default_contexts(const char *ctx_file_path, apol_vector_t * ctx_vector, apol_policy_t * policy);
+static bool in_isid_ctx(const char *type_name, apol_policy_t * policy);
+static bool in_def_ctx(const char *type_name, unreachable_doms_data_t * datum);
+/* for some reason we have to define this here to remove compile warnings */
+extern ssize_t getline(char **lineptr, size_t * n, FILE * stream);
+
+/* This string is the name f the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "unreachable_doms";
+
+int unreachable_doms_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "unreachable domains";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all domains in a policy which are unreachable. A domain is\n"
+ "unreachable if any of the following apply:\n"
+ " 1) There is insufficient type enforcement policy to allow a transition,\n"
+ " 2) There is insufficient RBAC policy to allow a transition,\n"
+ " 3) There are no users with proper roles to allow a transition.\n"
+ "However, if any of the above rules indicate an unreachable domain, yet the\n"
+ "domain appears in the system default contexts file, it is considered reachable.\n";
+ mod->opt_description = " Module requirements:\n" " attribute names\n"
+#ifdef LIBSELINUX
+ " default_contexts file\n"
+#endif
+ " Module dependencies:\n" " find_domains module\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_MED;
+
+ /* assign requirements */
+ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_POLICY_CAP, SECHK_REQ_CAP_ATTRIB_NAMES)) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+#ifdef LIBSELINUX
+ if (apol_vector_append(mod->requirements, sechk_name_value_new(SECHK_REQ_DEFAULT_CONTEXTS, NULL)) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+#endif
+
+ /* assign dependencies */
+ if (apol_vector_append(mod->dependencies, sechk_name_value_new("module", "find_domains")) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = unreachable_doms_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = unreachable_doms_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = unreachable_doms_data_free;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = unreachable_doms_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int unreachable_doms_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unreachable_doms_data_t *datum = NULL;
+ const char *ctx_file_path = NULL;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = unreachable_doms_data_new();
+ if (!datum) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ mod->data = datum;
+
+ /* Parse default contexts file */
+ if (!(datum->ctx_vector = apol_vector_create(free))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+#ifdef LIBSELINUX
+ ctx_file_path = selinux_default_context_path();
+ if (!ctx_file_path) {
+ ERR(policy, "%s", "Unable to find default contexts file");
+ errno = ENOENT;
+ return -1;
+ } else {
+ bool retv = parse_default_contexts(ctx_file_path, datum->ctx_vector, policy);
+ if (!retv) {
+ ERR(policy, "%s", "Unable to parse default contexts file");
+ errno = EIO;
+ return -1;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+typedef enum dom_need
+{
+ KEEP_SEARCHING = 0, /* keep checking way to reach not found yet */
+ USER, /* missing a user for role(s) associated with the type */
+ COMMON_USER, /* missing user for the role in a transition to that type */
+ ROLE_TRANS, /* transition is valid but need a role transition as well */
+ ROLE_ALLOW, /* transition is valid and has a role_transition but not role allow */
+ RBAC, /* there is a transition but insufficient RBAC rules to permit or to determie a user */
+ VALID_TRANS, /* only transitions to the type are invalid ones needs one or more rules to complete */
+ ROLE, /* type has no associated role */
+ TRANSITION, /* no transition exists */
+ DONE /* done searching a valid way to reach the type has been found */
+} dom_need_e;
+
+/**
+ * Finds user witn at least one role from each vector.
+ * @param policy The policy.
+ * @param src_roles The first set of roles.
+ * @param tgt_roles The second set of roles.
+ * @return 1 if a common user can be found 0 other wise.
+ */
+static int exists_common_user(apol_policy_t * policy, apol_vector_t * src_roles, apol_vector_t * tgt_roles,
+ const qpol_role_t ** which_sr, const qpol_role_t ** which_tr, const qpol_user_t ** which_u)
+{
+ int retv = 0;
+ apol_user_query_t *uq;
+ const char *name = NULL;
+ const qpol_role_t *role = NULL;
+ const qpol_user_t *user = NULL;
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *user_v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i, j, k;
+
+ if (!policy || !src_roles || !tgt_roles)
+ return 0;
+
+ if (which_sr)
+ *which_sr = NULL;
+ if (which_tr)
+ *which_tr = NULL;
+ if (which_u)
+ *which_u = NULL;
+
+ if (!(uq = apol_user_query_create()))
+ return 0;
+
+ for (i = 0; i < apol_vector_get_size(src_roles); i++) {
+ role = apol_vector_get_element(src_roles, i);
+ if (which_sr)
+ *which_sr = role;
+ qpol_role_get_name(q, role, &name);
+ apol_user_query_set_role(policy, uq, name);
+ apol_user_get_by_query(policy, uq, &user_v);
+ for (j = 0; j < apol_vector_get_size(user_v); j++) {
+ user = apol_vector_get_element(user_v, j);
+ qpol_user_get_role_iter(q, user, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ qpol_iterator_get_item(iter, (void **)&role);
+ if (!apol_vector_get_index(tgt_roles, role, NULL, NULL, &k)) {
+ retv = 1;
+ if (which_tr)
+ *which_tr = role;
+ if (which_u)
+ *which_u = user;
+ goto exists_done;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ apol_vector_destroy(&user_v);
+ }
+
+ exists_done:
+ qpol_iterator_destroy(&iter);
+ apol_vector_destroy(&user_v);
+ apol_user_query_destroy(&uq);
+ return retv;
+}
+
+/* The run function performs the check. This function runs only once even if
+ * called multiple times. This function allocates the result structure and
+ * fills in all relevant item and proof data.
+ * Return Values:
+ * -1 System error
+ * 0 The module "succeeded" - no negative results found
+ * 1 The module "failed" - some negative results found */
+int unreachable_doms_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unreachable_doms_data_t *datum;
+ sechk_name_value_t *dep = NULL;
+ sechk_result_t *res = NULL, *find_domains_res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i, j, k, l;
+ sechk_mod_fn_t run_fn = NULL;
+ int error = 0, trans_missing = 0;
+ apol_vector_t *dom_vector = NULL, *dom_results = NULL, *dom_roles = NULL;
+ apol_vector_t *valid_rev_trans = NULL, *invalid_rev_trans = NULL;
+ apol_vector_t *start_roles = NULL, *intersect_roles = NULL;
+ apol_vector_t *tmp_users = NULL, *role_users = NULL;
+ apol_vector_t *role_trans_vector = NULL, *role_allow_vector = NULL;
+ apol_domain_trans_analysis_t *dta = NULL;
+ apol_domain_trans_result_t *dtr = NULL;
+ const char *tmp_name = NULL, *cur_dom_name = NULL;
+ const char *tmp2 = NULL, *tmp3 = NULL;
+ const qpol_type_t *cur_dom = NULL, *ep_type = NULL, *start_type = NULL;
+ const qpol_type_t *last_type = NULL;
+ const qpol_role_t *last_role = NULL, *src_role = NULL, *dflt_role = NULL;
+ const qpol_role_t *last_dflt = NULL;
+ const qpol_user_t *last_user = NULL;
+ dom_need_e need = KEEP_SEARCHING;
+ apol_role_query_t *role_q = NULL;
+ apol_user_query_t *user_q = NULL;
+ apol_role_trans_query_t *rtq = NULL;
+ apol_role_allow_query_t *raq = NULL;
+ const qpol_role_trans_t *role_trans = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ datum = (unreachable_doms_data_t *) mod->data;
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto unreachable_doms_run_fail;
+ }
+ res->item_type = SECHK_ITEM_TYPE;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto unreachable_doms_run_fail;
+ }
+
+ /* run dependencies and get results */
+ for (i = 0; i < apol_vector_get_size(mod->dependencies); i++) {
+ dep = apol_vector_get_element(mod->dependencies, i);
+ run_fn = sechk_lib_get_module_function(dep->value, SECHK_MOD_FN_RUN, mod->parent_lib);
+ run_fn(sechk_lib_get_module(dep->value, mod->parent_lib), policy, NULL);
+ }
+
+ find_domains_res = sechk_lib_get_module_result("find_domains", mod->parent_lib);
+ dom_results = find_domains_res->items;
+ if (!(dom_vector = apol_vector_create(NULL))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ for (i = 0; i < apol_vector_get_size(dom_results); i++) {
+ item = apol_vector_get_element(dom_results, i);
+ if (apol_vector_append(dom_vector, (void *)(item->item))) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ }
+ item = NULL;
+ dom_results = NULL; /* no need to destroy, belongs to another module. */
+
+ /* initialize query objects */
+ dta = apol_domain_trans_analysis_create();
+ if (!dta) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ apol_domain_trans_analysis_set_direction(policy, dta, APOL_DOMAIN_TRANS_DIRECTION_REVERSE);
+
+ role_q = apol_role_query_create();
+ if (!role_q) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+
+ user_q = apol_user_query_create();
+ if (!user_q) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+
+ rtq = apol_role_trans_query_create();
+ if (!rtq) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+
+ raq = apol_role_allow_query_create();
+ if (!raq) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+
+ /* dom_vector now contains all types considered domains */
+ for (i = 0; i < apol_vector_get_size(dom_vector); i++) {
+ cur_dom = apol_vector_get_element(dom_vector, i);
+ qpol_type_get_name(q, cur_dom, &cur_dom_name);
+ need = KEEP_SEARCHING;
+
+ if (
+#ifdef LIBSELINUX
+ in_def_ctx(cur_dom_name, datum) ||
+#endif
+ in_isid_ctx(cur_dom_name, policy))
+ continue;
+
+ /* collect information about roles and transitions to this domain */
+ apol_role_query_set_type(policy, role_q, cur_dom_name);
+ apol_role_get_by_query(policy, role_q, &dom_roles);
+ apol_policy_reset_domain_trans_table(policy);
+ apol_domain_trans_analysis_set_start_type(policy, dta, cur_dom_name);
+ apol_domain_trans_analysis_set_valid(policy, dta, APOL_DOMAIN_TRANS_SEARCH_VALID);
+ apol_domain_trans_analysis_do(policy, dta, &valid_rev_trans);
+ apol_policy_reset_domain_trans_table(policy);
+ apol_domain_trans_analysis_set_valid(policy, dta, APOL_DOMAIN_TRANS_SEARCH_INVALID);
+ apol_domain_trans_analysis_do(policy, dta, &invalid_rev_trans);
+
+ /* for valid transitions - validate RBAC, and then users */
+ for (j = 0; j < apol_vector_get_size(valid_rev_trans); j++) {
+ dtr = apol_vector_get_element(valid_rev_trans, j);
+ start_type = apol_domain_trans_result_get_start_type(dtr);
+ ep_type = apol_domain_trans_result_get_entrypoint_type(dtr);
+ qpol_type_get_name(q, start_type, &tmp_name);
+ apol_role_query_set_type(policy, role_q, tmp_name);
+ apol_role_get_by_query(policy, role_q, &start_roles);
+ intersect_roles = apol_vector_create_from_intersection(dom_roles, start_roles, NULL, NULL);
+ if (apol_vector_get_size(intersect_roles) > 0) {
+ /* find user with role in intersect */
+ role_users = apol_vector_create(NULL);
+ for (k = 0; k < apol_vector_get_size(intersect_roles); k++) {
+ last_role = apol_vector_get_element(intersect_roles, k);
+ qpol_role_get_name(q, last_role, &tmp_name);
+ apol_user_query_set_role(policy, user_q, tmp_name);
+ apol_user_get_by_query(policy, user_q, &tmp_users);
+ if (apol_vector_cat(role_users, tmp_users)) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ apol_vector_destroy(&tmp_users);
+ }
+ if (apol_vector_get_size(role_users) > 0)
+ need = DONE;
+ else
+ need = USER;
+ apol_vector_destroy(&role_users);
+ }
+ if (need == DONE)
+ break;
+ /* look for role_transitions */
+ qpol_type_get_name(q, ep_type, &tmp_name);
+ apol_role_trans_query_set_target(policy, rtq, tmp_name, 1);
+ apol_role_trans_get_by_query(policy, rtq, &role_trans_vector);
+ for (k = 0; need != DONE && k < apol_vector_get_size(role_trans_vector); k++) {
+ role_trans = apol_vector_get_element(role_trans_vector, k);
+ qpol_role_trans_get_source_role(q, role_trans, &src_role);
+ qpol_role_trans_get_default_role(q, role_trans, &dflt_role);
+ if (apol_vector_get_index(start_roles, src_role, NULL, NULL, &l)
+ || apol_vector_get_index(dom_roles, dflt_role, NULL, NULL, &l))
+ continue; /* start domain must have the source role and cur_dom must have default role or transition does not apply */
+ if (exists_common_user(policy, start_roles, dom_roles, NULL, NULL, NULL)) {
+ qpol_role_get_name(q, src_role, &tmp_name);
+ apol_role_allow_query_set_source(policy, raq, tmp_name);
+ qpol_role_get_name(q, dflt_role, &tmp_name);
+ apol_role_allow_query_set_target(policy, raq, tmp_name);
+ apol_role_allow_get_by_query(policy, raq, &role_allow_vector);
+ if (apol_vector_get_size(role_allow_vector) > 0) {
+ need = DONE;
+ } else {
+ need = ROLE_ALLOW;
+ last_role = src_role;
+ last_dflt = dflt_role;
+ }
+ apol_vector_destroy(&role_allow_vector);
+ } else {
+ need = COMMON_USER;
+ last_role = src_role;
+ last_dflt = dflt_role;
+ }
+ }
+ /* no roles usable in intersection and no transitions so pick first set with common user */
+ if (apol_vector_get_size(role_trans_vector) == 0) {
+ if (exists_common_user(policy, start_roles, dom_roles, &src_role, &dflt_role, &last_user)) {
+ need = ROLE_TRANS;
+ last_role = src_role;
+ last_dflt = dflt_role;
+ last_type = ep_type;
+ } else {
+ need = RBAC;
+ }
+ }
+ apol_vector_destroy(&role_trans_vector);
+ if (need == DONE)
+ break;
+ }
+ /* if no valid transition found - check what is needed to complete invalid ones */
+ if (need == KEEP_SEARCHING) {
+ for (j = 0; j < apol_vector_get_size(invalid_rev_trans); j++) {
+ dtr = apol_vector_get_element(invalid_rev_trans, j);
+ start_type = apol_domain_trans_result_get_start_type(dtr);
+ ep_type = apol_domain_trans_result_get_entrypoint_type(dtr);
+ trans_missing = apol_domain_trans_table_verify_trans(policy, start_type, ep_type, cur_dom);
+ need = VALID_TRANS;
+ /* since incomplete transitions can be missing types break if we
+ * found one with all three types specified, else keep the last one */
+ if (start_type && ep_type)
+ break;
+ }
+ }
+ /* if no transition exists (valid or otherwise) check that at least one role and user pair is valid */
+ if (need == KEEP_SEARCHING) {
+ role_users = apol_vector_create(NULL);
+ for (j = 0; j < apol_vector_get_size(dom_roles); j++) {
+ last_role = apol_vector_get_element(dom_roles, j);
+ qpol_role_get_name(q, last_role, &tmp_name);
+ apol_user_query_set_role(policy, user_q, tmp_name);
+ apol_user_get_by_query(policy, user_q, &tmp_users);
+ apol_vector_cat(role_users, tmp_users);
+ apol_vector_destroy(&tmp_users);
+ }
+ if (apol_vector_get_size(dom_roles) == 0) {
+ need = ROLE;
+ } else if (apol_vector_get_size(role_users) == 0) {
+ need = USER;
+ } else {
+ need = TRANSITION;
+ }
+ apol_vector_destroy(&role_users);
+ }
+ /* if something needs to be reported do so now */
+ if (need != DONE) {
+ assert(need != KEEP_SEARCHING);
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ item->item = (void *)cur_dom;
+ item->test_result = (unsigned char)need;
+ item->proof = apol_vector_create(sechk_proof_free);
+ if (!item->proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ proof->type = SECHK_ITEM_NONE;
+ proof->elem = NULL;
+ switch (need) {
+ case USER:
+ {
+ qpol_role_get_name(q, last_role, &tmp_name);
+ if (asprintf(&proof->text, "No user associated with role %s for %s", tmp_name, cur_dom_name) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case COMMON_USER:
+ {
+ qpol_role_get_name(q, last_role, &tmp_name);
+ qpol_role_get_name(q, last_dflt, &tmp2);
+ if (asprintf
+ (&proof->text, "Role transition required but no user associated with role %s and %s",
+ tmp_name, tmp2) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case ROLE_TRANS:
+ {
+ qpol_role_get_name(q, last_role, &tmp_name);
+ qpol_role_get_name(q, last_dflt, &tmp2);
+ qpol_type_get_name(q, last_type, &tmp3);
+ if (asprintf(&proof->text, "Missing: role_transition %s %s %s;", tmp_name, tmp3, tmp2) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case ROLE_ALLOW:
+ {
+ qpol_role_get_name(q, last_role, &tmp_name);
+ qpol_role_get_name(q, last_dflt, &tmp2);
+ if (asprintf
+ (&proof->text,
+ "Role transition required but missing role allow rule.\n\tMissing: allow %s %s;",
+ tmp_name, tmp2) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case RBAC:
+ {
+ if (asprintf
+ (&proof->text,
+ "Valid domain transition to %s exists but indufficient RBAC rules to permit it.",
+ cur_dom_name) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case VALID_TRANS:
+ {
+ if (start_type)
+ qpol_type_get_name(q, start_type, &tmp2);
+ else
+ tmp2 = "<start_type>";
+ if (ep_type)
+ qpol_type_get_name(q, ep_type, &tmp3);
+ else
+ tmp3 = "<entrypont>";
+ if (asprintf
+ (&proof->text,
+ "Partial transition to %s found:\n\t%s: allow %s %s : process transition;\n\t%s: allow %s %s : file execute;\n\t%s: allow %s %s : file entrypoint;\n\t%s one of:\n\tallow %s self : process setexec;\n\ttype_transition %s %s : process %s;",
+ cur_dom_name,
+ ((trans_missing & APOL_DOMAIN_TRANS_RULE_PROC_TRANS) ? "Missing" : "Has"), tmp2,
+ cur_dom_name, ((trans_missing & APOL_DOMAIN_TRANS_RULE_EXEC) ? "Missing" : "Has"),
+ tmp2, tmp3, ((trans_missing & APOL_DOMAIN_TRANS_RULE_ENTRYPOINT) ? "Missing" : "Has"),
+ cur_dom_name, tmp3,
+ ((trans_missing & (APOL_DOMAIN_TRANS_RULE_TYPE_TRANS | APOL_DOMAIN_TRANS_RULE_SETEXEC))
+ ? "May need" : "Has"), cur_dom_name, tmp2, tmp3, cur_dom_name) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case ROLE:
+ {
+ if (asprintf(&proof->text, "No role associated with domain %s", cur_dom_name) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case TRANSITION:
+ {
+ if (asprintf(&proof->text, "There are no transitions to domain %s", cur_dom_name) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ break;
+ }
+ case DONE:
+ case KEEP_SEARCHING:
+ default:
+ {
+ assert(0);
+ error = EDOM;
+ goto unreachable_doms_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ proof = NULL;
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(error));
+ goto unreachable_doms_run_fail;
+ }
+ item = NULL;
+ }
+ apol_vector_destroy(&dom_roles);
+ apol_vector_destroy(&valid_rev_trans);
+ apol_vector_destroy(&invalid_rev_trans);
+ }
+
+ apol_vector_destroy(&dom_vector);
+ apol_domain_trans_analysis_destroy(&dta);
+ apol_role_query_destroy(&role_q);
+ apol_user_query_destroy(&user_q);
+ apol_role_trans_query_destroy(&rtq);
+ apol_role_allow_query_destroy(&raq);
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ unreachable_doms_run_fail:
+ apol_vector_destroy(&dom_vector);
+ apol_domain_trans_analysis_destroy(&dta);
+ apol_role_query_destroy(&role_q);
+ apol_user_query_destroy(&user_q);
+ apol_role_trans_query_destroy(&rtq);
+ apol_role_allow_query_destroy(&raq);
+ apol_vector_destroy(&dom_roles);
+ apol_vector_destroy(&valid_rev_trans);
+ apol_vector_destroy(&invalid_rev_trans);
+ apol_vector_destroy(&tmp_users);
+ apol_vector_destroy(&role_users);
+ apol_vector_destroy(&role_trans_vector);
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The free function frees the private data of a module */
+void unreachable_doms_data_free(void *data)
+{
+ unreachable_doms_data_t *d = data;
+ if (!data)
+ return;
+
+ free(d->ctx_file_path);
+ apol_vector_destroy(&d->ctx_vector);
+ free(data);
+}
+
+/* The print function generates the text and prints the results to stdout. */
+int unreachable_doms_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unreachable_doms_data_t *datum = NULL;
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i = 0, j = 0, k, l, num_items;
+ const qpol_type_t *type;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ const char *type_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ datum = (unreachable_doms_data_t *) mod->data;
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd unreachable domains.\n", num_items);
+ }
+
+ if (outformat & SECHK_OUT_LIST) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ type = (qpol_type_t *) item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s%s", type_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+
+ if (outformat & SECHK_OUT_PROOF) {
+ if (apol_vector_get_size(datum->ctx_vector) > 0) {
+ printf("Found %zd domains in %s:\n", apol_vector_get_size(datum->ctx_vector),
+ selinux_default_context_path());
+ for (j = 0; j < apol_vector_get_size(datum->ctx_vector); j++) {
+ type_name = apol_vector_get_element(datum->ctx_vector, j);
+ printf("\t%s\n", type_name);
+ }
+ }
+
+ printf("\n");
+ for (k = 0; k < num_items; k++) {
+ item = apol_vector_get_element(mod->result->items, k);
+ if (item) {
+ type = item->item;
+ qpol_type_get_name(q, type, &type_name);
+ printf("%s\n", (char *)type_name);
+ for (l = 0; l < apol_vector_get_size(item->proof); l++) {
+ proof = apol_vector_get_element(item->proof, l);
+ if (proof)
+ printf("\t%s\n", proof->text);
+ }
+ }
+ printf("\n");
+ }
+ }
+
+ return 0;
+}
+
+/* The unreachable_doms_data_new function allocates and returns an initialized
+ * private data storage structure for this module. */
+unreachable_doms_data_t *unreachable_doms_data_new(void)
+{
+ unreachable_doms_data_t *datum = NULL;
+
+ datum = (unreachable_doms_data_t *) calloc(1, sizeof(unreachable_doms_data_t));
+
+ return datum;
+}
+
+/* Parses default_contexts and adds source domains to datum->ctx_list.
+ * The vector will contain newly allocated strings. */
+static bool parse_default_contexts(const char *ctx_file_path, apol_vector_t * ctx_vector, apol_policy_t * policy)
+{
+ int str_sz, i, charno, error = 0, retv;
+ FILE *ctx_file;
+ char *line = NULL, *src_role = NULL, *src_dom = NULL, *dst_role = NULL, *dst_dom = NULL;
+ size_t line_len = 0;
+ bool uses_mls = false;
+
+ printf("Using default contexts: %s\n", ctx_file_path);
+ ctx_file = fopen(ctx_file_path, "r");
+ if (!ctx_file) {
+ error = errno;
+ ERR(policy, "Opening default contexts file %s", ctx_file_path);
+ goto parse_default_contexts_fail;
+ }
+
+ while (!feof(ctx_file)) {
+ retv = getline(&line, &line_len, ctx_file);
+ if (retv == -1) {
+ if (feof(ctx_file)) {
+ break;
+ } else {
+ error = errno;
+ ERR(policy, "%s", "Reading default contexts file");
+ goto parse_default_contexts_fail;
+ }
+ }
+
+ uses_mls = false;
+ str_sz = APOL_STR_SZ + 128;
+ i = 0;
+
+ /* source role */
+ src_role = malloc(str_sz);
+ if (!src_role) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto parse_default_contexts_fail;
+ }
+
+ memset(src_role, 0x0, str_sz);
+ charno = 0;
+ while (line[i] != ':') {
+ if (!isspace(line[i])) {
+ src_role[i] = line[i];
+ charno++;
+ }
+ i++;
+ }
+ i++; /* skip ':' */
+
+ /* source type */
+ src_dom = malloc(str_sz);
+ if (!src_dom) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto parse_default_contexts_fail;
+ }
+ memset(src_dom, 0x0, str_sz);
+ charno = 0;
+ while (1) {
+ if (isspace(line[i]))
+ break;
+ /* Check for MLS */
+ if (line[i] == ':') {
+ uses_mls = true;
+ i++; /* skip ':' */
+ while (!isspace(line[i]))
+ i++;
+ }
+ if (uses_mls)
+ break;
+
+ src_dom[charno] = line[i];
+ charno++;
+ i++;
+ }
+
+ /* dest role */
+ dst_role = malloc(str_sz);
+ if (!dst_role) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto parse_default_contexts_fail;
+ }
+ memset(dst_role, 0x0, str_sz);
+ charno = 0;
+ while (line[i] != ':') {
+ if (!isspace(line[i])) {
+ dst_role[charno] = line[i];
+ charno++;
+ }
+
+ i++;
+ }
+ i++; /* skip ':' */
+
+ /* dest type */
+ dst_dom = malloc(str_sz);
+ if (!dst_dom) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto parse_default_contexts_fail;
+ }
+ memset(dst_dom, 0x0, str_sz);
+ charno = 0;
+ while (line[i]) {
+ if (uses_mls)
+ if (line[i] == ':')
+ break;
+
+ if (!isspace(line[i]))
+ dst_dom[charno] = line[i];
+
+ charno++;
+ i++;
+ }
+
+ if (apol_vector_append(ctx_vector, (void *)strdup(src_dom)) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto parse_default_contexts_fail;
+ }
+ free(line);
+ line = NULL;
+ free(src_role);
+ free(src_dom);
+ free(dst_role);
+ free(dst_dom);
+ }
+ free(line);
+ fclose(ctx_file);
+ return true;
+ parse_default_contexts_fail:
+ if (ctx_file != NULL) {
+ fclose(ctx_file);
+ }
+ free(line);
+ free(src_role);
+ free(src_dom);
+ free(dst_role);
+ free(dst_dom);
+ errno = error;
+ return false;
+}
+
+/* Returns true if type_idx is in datum->ctx_list */
+static bool in_def_ctx(const char *type_name, unreachable_doms_data_t * datum)
+{
+ size_t i;
+ if (apol_vector_get_index(datum->ctx_vector, type_name, apol_str_strcmp, NULL, &i) < 0) {
+ return false;
+ }
+ return true;
+}
+
+/* Returns true if type is a type assigned to an isid */
+static bool in_isid_ctx(const char *type_name, apol_policy_t * policy)
+{
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ qpol_policy_get_isid_iter(q, &iter);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ const qpol_isid_t *isid;
+ const qpol_context_t *ocon;
+ const qpol_type_t *context_type;
+ const char *context_type_name;
+
+ qpol_iterator_get_item(iter, (void **)&isid);
+ qpol_isid_get_context(q, isid, &ocon);
+ qpol_context_get_type(q, ocon, &context_type);
+ qpol_type_get_name(q, context_type, &context_type_name);
+ if (!strcmp(type_name, context_type_name)) {
+ qpol_iterator_destroy(&iter);
+ return true;
+ }
+ }
+ qpol_iterator_destroy(&iter);
+ return false;
+}
diff --git a/sechecker/modules/unreachable_doms.h b/sechecker/modules/unreachable_doms.h
new file mode 100644
index 0000000..660de76
--- /dev/null
+++ b/sechecker/modules/unreachable_doms.h
@@ -0,0 +1,73 @@
+/**
+ * @file
+ * Defines the interface for the unreachable domains module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef UNREACHABLE_DOMS
+#define UNREACHABLE_DOMS
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/user-query.h>
+#include <apol/role-query.h>
+#include <apol/isid-query.h>
+#include <apol/rbacrule-query.h>
+#include <apol/domain-trans-analysis.h>
+#include <selinux/selinux.h>
+
+#define SECHK_INC_DOM_TRANS_HAS_TT 0x08
+#define SECHK_INC_DOM_TRANS_HAS_EXEC 0x04
+#define SECHK_INC_DOM_TRANS_HAS_TRANS 0x02
+#define SECHK_INC_DOM_TRANS_HAS_EP 0x01
+#define SECHK_INC_DOM_TRANS_COMPLETE (SECHK_INC_DOM_TRANS_HAS_EP|SECHK_INC_DOM_TRANS_HAS_TRANS|SECHK_INC_DOM_TRANS_HAS_EXEC)
+
+#define APOL_STR_SZ 128
+
+/* The unreachable_doms_data structure is used to hold the check specific
+ * private data of a module. */
+ typedef struct unreachable_doms_data
+ {
+ char *ctx_file_path;
+ /* vector of strings, read from default contexts file */
+ apol_vector_t *ctx_vector;
+ } unreachable_doms_data_t;
+
+ unreachable_doms_data_t *unreachable_doms_data_new(void);
+ void unreachable_doms_data_free(void *data);
+
+ int unreachable_doms_register(sechk_lib_t * lib);
+ int unreachable_doms_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int unreachable_doms_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int unreachable_doms_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UNREACHABLE_DOMS */
diff --git a/sechecker/modules/users_wo_roles.c b/sechecker/modules/users_wo_roles.c
new file mode 100644
index 0000000..a98d94d
--- /dev/null
+++ b/sechecker/modules/users_wo_roles.c
@@ -0,0 +1,321 @@
+/**
+ * @file
+ * Implementation of the users without roles module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "users_wo_roles.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/* This string is the name of the module and should match the stem
+ * of the file name; it should also match the prefix of all functions
+ * defined in this module and the private data storage structure */
+static const char *const mod_name = "users_wo_roles";
+
+/* The register function registers all of a module's functions
+ * with the library. */
+int users_wo_roles_register(sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+
+ if (!lib) {
+ ERR(NULL, "%s", "No library");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Modules are declared by the config file and their name and options
+ * are stored in the module array. The name is looked up to determine
+ * where to store the function structures */
+ mod = sechk_lib_get_module(mod_name, lib);
+ if (!mod) {
+ ERR(NULL, "%s", "Module unknown");
+ errno = EINVAL;
+ return -1;
+ }
+ mod->parent_lib = lib;
+
+ /* assign the descriptions */
+ mod->brief_description = "users with no roles";
+ mod->detailed_description =
+ "--------------------------------------------------------------------------------\n"
+ "This module finds all the SELinux users in the policy that have no associated\n"
+ "roles. Users without roles may appear in the label of a file system object;\n"
+ "however, these users cannot login to the system or run any process. Since these\n"
+ "users cannot be used on the system, a policy change is recomended to remove the\n"
+ "users or provide some intended access.\n";
+ mod->opt_description =
+ " Module requirements:\n" " none\n" " Module dependencies:\n" " none\n" " Module options:\n" " none\n";
+ mod->severity = SECHK_SEV_LOW;
+ /* register functions */
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_INIT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = users_wo_roles_init;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_RUN);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = users_wo_roles_run;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mod->data_free = NULL;
+
+ fn_struct = sechk_fn_new();
+ if (!fn_struct) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->name = strdup(SECHK_MOD_FN_PRINT);
+ if (!fn_struct->name) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ fn_struct->fn = users_wo_roles_print;
+ if (apol_vector_append(mod->functions, (void *)fn_struct) < 0) {
+ ERR(NULL, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The init function creates the module's private data storage object
+ * and initializes its values based on the options parsed in the config
+ * file. */
+int users_wo_roles_init(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ mod->data = NULL;
+
+ return 0;
+}
+
+/* The run function performs the check. This function runs only once
+ * even if called multiple times. All test logic should be placed below
+ * as instructed. This function allocates the result structure and fills
+ * in all relavant item and proof data. */
+int users_wo_roles_run(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ sechk_result_t *res = NULL;
+ sechk_item_t *item = NULL;
+ sechk_proof_t *proof = NULL;
+ size_t i;
+ apol_vector_t *user_vector;
+ int error = 0;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if already run return */
+ if (mod->result)
+ return 0;
+
+ res = sechk_result_new();
+ if (!res) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+ res->test_name = strdup(mod_name);
+ if (!res->test_name) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+ res->item_type = SECHK_ITEM_USER;
+ if (!(res->items = apol_vector_create(sechk_item_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+
+ apol_user_get_by_query(policy, NULL, &user_vector);
+ for (i = 0; i < apol_vector_get_size(user_vector); i++) {
+ qpol_user_t *user;
+ qpol_iterator_t *role_iter;
+
+ user = apol_vector_get_element(user_vector, i);
+ qpol_user_get_role_iter(apol_policy_get_qpol(policy), user, &role_iter);
+ if (!qpol_iterator_end(role_iter)) {
+ qpol_iterator_destroy(&role_iter);
+ continue;
+ }
+ qpol_iterator_destroy(&role_iter);
+
+ proof = sechk_proof_new(NULL);
+ if (!proof) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+ proof->type = SECHK_ITEM_USER;
+ proof->text = "User has no roles.\n";
+ item = sechk_item_new(NULL);
+ if (!item) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+ item->item = (void *)user;
+ if (!item->proof) {
+ if (!(item->proof = apol_vector_create(sechk_proof_free))) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+ }
+ if (apol_vector_append(item->proof, (void *)proof) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+ if (apol_vector_append(res->items, (void *)item) < 0) {
+ error = errno;
+ ERR(policy, "%s", strerror(ENOMEM));
+ goto users_wo_roles_run_fail;
+ }
+ }
+ apol_vector_destroy(&user_vector);
+
+ mod->result = res;
+
+ if (apol_vector_get_size(res->items))
+ return 1;
+ return 0;
+
+ users_wo_roles_run_fail:
+ sechk_proof_free(proof);
+ sechk_item_free(item);
+ sechk_result_destroy(&res);
+ errno = error;
+ return -1;
+}
+
+/* The print output function generates the text printed in the
+ * report and prints it to stdout. */
+int users_wo_roles_print(sechk_module_t * mod, apol_policy_t * policy, void *arg __attribute__ ((unused)))
+{
+ unsigned char outformat = 0x00;
+ sechk_item_t *item = NULL;
+ size_t i = 0, j = 0, num_items;
+ const qpol_user_t *user;
+ const char *user_name;
+
+ if (!mod || !policy) {
+ ERR(policy, "%s", "Invalid parameters");
+ errno = EINVAL;
+ return -1;
+ }
+ if (strcmp(mod_name, mod->name)) {
+ ERR(policy, "Wrong module (%s)", mod->name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ outformat = mod->outputformat;
+ num_items = apol_vector_get_size(mod->result->items);
+
+ if (!outformat || (outformat & SECHK_OUT_QUIET))
+ return 0; /* not an error - no output is requested */
+
+ if (!mod->result) {
+ ERR(policy, "%s", "Module has not been run");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* display the statistics of the results */
+ if (outformat & SECHK_OUT_STATS) {
+ printf("Found %zd users.\n", num_items);
+ }
+ if (outformat & SECHK_OUT_PROOF) {
+ printf("\nThe following users have no associated roles.\n");
+ }
+ /* The list report component is a display of all items
+ * found without any supporting proof. */
+ if (outformat & (SECHK_OUT_LIST | SECHK_OUT_PROOF)) {
+ printf("\n");
+ for (i = 0; i < num_items; i++) {
+ j++;
+ j %= 4;
+ item = apol_vector_get_element(mod->result->items, i);
+ user = (qpol_user_t *) item->item;
+ qpol_user_get_name(apol_policy_get_qpol(policy), user, &user_name);
+ printf("%s%s", user_name, (char *)((j && i != num_items - 1) ? ", " : "\n"));
+ }
+ printf("\n");
+ }
+ return 0;
+}
diff --git a/sechecker/modules/users_wo_roles.h b/sechecker/modules/users_wo_roles.h
new file mode 100644
index 0000000..dd1ee42
--- /dev/null
+++ b/sechecker/modules/users_wo_roles.h
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Defines the interface for the users without roles module.
+ *
+ * @author Kevin Carr kcarr@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef USERS_WO_ROLES
+#define USERS_WO_ROLES
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+#include <apol/policy.h>
+#include <apol/user-query.h>
+
+ int users_wo_roles_register(sechk_lib_t * lib);
+ int users_wo_roles_init(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int users_wo_roles_run(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+ int users_wo_roles_print(sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sechecker/profiles/all-checks-no-mls.sechecker b/sechecker/profiles/all-checks-no-mls.sechecker
new file mode 100644
index 0000000..657b5ba
--- /dev/null
+++ b/sechecker/profiles/all-checks-no-mls.sechecker
@@ -0,0 +1,98 @@
+<sechecker version="1.1">
+<profile>
+ <module name="find_domains">
+ <output value="quiet"/>
+ <option name="domain_attribute">
+ <item value="domain"/>
+ </option>
+ </module>
+
+ <module name="find_file_types">
+ <output value="quiet"/>
+ <option name="file_type_attribute">
+ <item value="file_type"/>
+ </option>
+ </module>
+
+ <module name="domain_and_file">
+ <output value="short"/>
+ </module>
+
+ <module name="attribs_wo_types">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_types">
+ <output value="short"/>
+ </module>
+
+ <module name="users_wo_roles">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_allow">
+ <output value="short"/>
+ </module>
+
+ <module name="types_wo_allow">
+ <output value="short"/>
+ </module>
+
+ <module name="attribs_wo_rules">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_users">
+ <output value="short"/>
+ </module>
+
+ <module name="spurious_audit">
+ <output value="short"/>
+ </module>
+
+ <module name="inc_mount">
+ <output value="short"/>
+ </module>
+
+ <module name="domains_wo_roles">
+ <output value="short"/>
+ </module>
+
+ <module name="inc_dom_trans">
+ <output value="short"/>
+ </module>
+
+ <module name="find_net_domains">
+ <output value="quiet"/>
+ <option name="net_obj">
+ <item value="netif"/>
+ <item value="tcp_socket"/>
+ <item value="udp_socket"/>
+ <item value="node"/>
+ <item value="association"/>
+ </option>
+ </module>
+
+ <module name="find_port_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_node_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_netif_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="inc_net_access">
+ <output value="short"/>
+ </module>
+
+ <module name="unreachable_doms">
+ <output value="short"/>
+ </module>
+
+</profile>
+</sechecker>
+
diff --git a/sechecker/profiles/all-checks.sechecker b/sechecker/profiles/all-checks.sechecker
new file mode 100644
index 0000000..498da0b
--- /dev/null
+++ b/sechecker/profiles/all-checks.sechecker
@@ -0,0 +1,102 @@
+<sechecker version="1.1">
+<profile>
+ <module name="find_domains">
+ <output value="quiet"/>
+ <option name="domain_attribute">
+ <item value="domain"/>
+ </option>
+ </module>
+
+ <module name="find_file_types">
+ <output value="quiet"/>
+ <option name="file_type_attribute">
+ <item value="file_type"/>
+ </option>
+ </module>
+
+ <module name="domain_and_file">
+ <output value="short"/>
+ </module>
+
+ <module name="attribs_wo_types">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_types">
+ <output value="short"/>
+ </module>
+
+ <module name="users_wo_roles">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_allow">
+ <output value="short"/>
+ </module>
+
+ <module name="types_wo_allow">
+ <output value="short"/>
+ </module>
+
+ <module name="attribs_wo_rules">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_users">
+ <output value="short"/>
+ </module>
+
+ <module name="spurious_audit">
+ <output value="short"/>
+ </module>
+
+ <module name="inc_mount">
+ <output value="short"/>
+ </module>
+
+ <module name="domains_wo_roles">
+ <output value="short"/>
+ </module>
+
+ <module name="inc_dom_trans">
+ <output value="short"/>
+ </module>
+
+ <module name="find_net_domains">
+ <output value="quiet"/>
+ <option name="net_obj">
+ <item value="netif"/>
+ <item value="tcp_socket"/>
+ <item value="udp_socket"/>
+ <item value="node"/>
+ <item value="association"/>
+ </option>
+ </module>
+
+ <module name="find_port_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_node_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_netif_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="inc_net_access">
+ <output value="short"/>
+ </module>
+
+ <module name="imp_range_trans">
+ <output value="short"/>
+ </module>
+
+ <module name="unreachable_doms">
+ <output value="short"/>
+ </module>
+
+</profile>
+</sechecker>
+
diff --git a/sechecker/profiles/analysis-checks.sechecker b/sechecker/profiles/analysis-checks.sechecker
new file mode 100644
index 0000000..8efaec9
--- /dev/null
+++ b/sechecker/profiles/analysis-checks.sechecker
@@ -0,0 +1,78 @@
+<sechecker version="1.1">
+<profile>
+ <module name="find_domains">
+ <output value="quiet"/>
+ <option name="domain_attribute">
+ <item value="domain"/>
+ </option>
+ </module>
+
+ <module name="find_file_types">
+ <output value="quiet"/>
+ <option name="file_type_attribute">
+ <item value="file_type"/>
+ </option>
+ </module>
+
+ <module name="domain_and_file">
+ <output value="short"/>
+ </module>
+
+ <module name="spurious_audit">
+ <output value="short"/>
+ </module>
+
+ <module name="inc_mount">
+ <output value="short"/>
+ </module>
+
+ <module name="rules_exp_nothing">
+ <output value="short"/>
+ </module>
+
+ <module name="domains_wo_roles">
+ <output value="short"/>
+ </module>
+
+ <module name="inc_dom_trans">
+ <output value="short"/>
+ </module>
+
+ <module name="find_net_domains">
+ <output value="quiet"/>
+ <option name="net_obj">
+ <item value="netif"/>
+ <item value="tcp_socket"/>
+ <item value="udp_socket"/>
+ <item value="node"/>
+ <item value="association"/>
+ </option>
+ </module>
+
+ <module name="find_port_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_node_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_netif_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="inc_net_access">
+ <output value="short"/>
+ </module>
+
+ <module name="imp_range_trans">
+ <output value="short"/>
+ </module>
+
+ <module name="unreachable_doms">
+ <output value="short"/>
+ </module>
+
+</profile>
+</sechecker>
+
diff --git a/sechecker/profiles/devel-checks.sechecker b/sechecker/profiles/devel-checks.sechecker
new file mode 100644
index 0000000..5807044
--- /dev/null
+++ b/sechecker/profiles/devel-checks.sechecker
@@ -0,0 +1,74 @@
+<sechecker version="1.1">
+<profile>
+ <module name="find_domains">
+ <output value="quiet"/>
+ <option name="domain_attribute">
+ <item value="domain"/>
+ </option>
+ </module>
+
+ <module name="find_file_types">
+ <output value="quiet"/>
+ <option name="file_type_attribute">
+ <item value="file_type"/>
+ </option>
+ </module>
+
+ <module name="attribs_wo_types">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_types">
+ <output value="short"/>
+ </module>
+
+ <module name="users_wo_roles">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_allow">
+ <output value="short"/>
+ </module>
+
+ <module name="types_wo_allow">
+ <output value="short"/>
+ </module>
+
+ <module name="attribs_wo_rules">
+ <output value="short"/>
+ </module>
+
+ <module name="roles_wo_users">
+ <output value="short"/>
+ </module>
+
+ <module name="spurious_audit">
+ <output value="short"/>
+ </module>
+
+ <module name="find_net_domains">
+ <output value="quiet"/>
+ <option name="net_obj">
+ <item value="netif"/>
+ <item value="tcp_socket"/>
+ <item value="udp_socket"/>
+ <item value="node"/>
+ <item value="association"/>
+ </option>
+ </module>
+
+ <module name="find_port_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_node_types">
+ <output value="quiet"/>
+ </module>
+
+ <module name="find_netif_types">
+ <output value="quiet"/>
+ </module>
+
+</profile>
+</sechecker>
+
diff --git a/sechecker/profiles/sechecker.dtd b/sechecker/profiles/sechecker.dtd
new file mode 100644
index 0000000..d0aa054
--- /dev/null
+++ b/sechecker/profiles/sechecker.dtd
@@ -0,0 +1,19 @@
+<!ELEMENT item EMPTY>
+<!ATTLIST item value NMTOKEN #REQUIRED>
+
+<!ELEMENT module (output, option?)>
+<!ATTLIST module name ID #REQUIRED>
+
+<!ELEMENT option (item+)>
+<!ATTLIST option name NMTOKEN #REQUIRED>
+
+<!ELEMENT output EMPTY>
+<!ATTLIST output value (quiet|short|verbose|none) #REQUIRED>
+
+<!ELEMENT profile (module+)>
+
+<!ELEMENT sechecker (profile)>
+<!ATTLIST sechecker version NMTOKEN #REQUIRED>
+
+
+
diff --git a/sechecker/register_list.c b/sechecker/register_list.c
new file mode 100644
index 0000000..febc79e
--- /dev/null
+++ b/sechecker/register_list.c
@@ -0,0 +1,102 @@
+/**
+ * @file
+ * Keeps track of the all known sechecker modules.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "register_list.h"
+
+static size_t sechk_register_num_modules = 0;
+static size_t sechk_register_num_profiles = 0;
+
+/* NULL terminated array of module names and register functions */
+static sechk_module_name_reg_t sechk_module_register_list[] = {
+ {"attribs_wo_rules", &attribs_wo_rules_register},
+ {"attribs_wo_types", &attribs_wo_types_register},
+ {"domain_and_file", &domain_and_file_register},
+ {"domains_wo_roles", &domains_wo_roles_register},
+ {"find_assoc_types", &find_assoc_types_register},
+ {"find_domains", &find_domains_register},
+ {"find_file_types", &find_file_types_register},
+ {"find_net_domains", &find_net_domains_register},
+ {"find_node_types", &find_node_types_register},
+ {"find_netif_types", &find_netif_types_register},
+ {"find_port_types", &find_port_types_register},
+ {"imp_range_trans", &imp_range_trans_register},
+ {"inc_dom_trans", &inc_dom_trans_register},
+ {"inc_mount", &inc_mount_register},
+ {"inc_net_access", &inc_net_access_register},
+ {"roles_wo_allow", &roles_wo_allow_register},
+ {"roles_wo_types", &roles_wo_types_register},
+ {"roles_wo_users", &roles_wo_users_register},
+ /* Deprecated *
+ * {"roles_exp_nothing", &roles_exp_nothing_register},
+ */
+ {"spurious_audit", &spurious_audit_register},
+ {"types_wo_allow", &types_wo_allow_register},
+ {"unreachable_doms", &unreachable_doms_register},
+ {"users_wo_roles", &users_wo_roles_register},
+ /* TODO: add additional register addresses here in alphabetical order */
+
+ {NULL, NULL}
+};
+
+/* NULL terminated array of profiles (name, file, description) */
+static sechk_profile_name_reg_t sechk_profile_register_list[] = {
+ {"analysis", "analysis-checks.sechecker", "common analysis checks"},
+ {"development", "devel-checks.sechecker", "common development checks"},
+ {"all", "all-checks.sechecker", "all available checks"},
+ {"all-no-mls", "all-checks-no-mls.sechecker", "all available checks not requiring MLS"},
+ /* TODO: add more profiles */
+
+ {NULL, NULL, NULL}
+};
+
+size_t sechk_register_list_get_num_profiles()
+{
+ size_t i;
+ if (sechk_register_num_profiles != 0)
+ return sechk_register_num_profiles;
+ for (i = 0; sechk_profile_register_list[i].name != NULL; i++) ;
+
+ sechk_register_num_profiles = i;
+ return sechk_register_num_profiles;
+}
+
+const sechk_profile_name_reg_t *sechk_register_list_get_profiles()
+{
+ return sechk_profile_register_list;
+}
+
+size_t sechk_register_list_get_num_modules()
+{
+ size_t i;
+ if (sechk_register_num_modules != 0)
+ return sechk_register_num_modules;
+ for (i = 0; sechk_module_register_list[i].name != NULL; i++) ;
+
+ sechk_register_num_modules = i;
+ return sechk_register_num_modules;
+}
+
+const sechk_module_name_reg_t *sechk_register_list_get_modules()
+{
+ return sechk_module_register_list;
+}
diff --git a/sechecker/register_list.h b/sechecker/register_list.h
new file mode 100644
index 0000000..0a0a6fc
--- /dev/null
+++ b/sechecker/register_list.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2005-2007 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Author: jmowery@tresys.com
+ *
+ */
+
+#ifndef SECHK_REGISTER_LIST_H
+#define SECHK_REGISTER_LIST_H
+
+#include "sechecker.h"
+
+/* TODO: to add a module declare it's register function as
+ * extern int <module_name>_register(sechk_lib_t *lib);
+ * here and add it's address to the array in register_list.c
+ * don't forget to add any necessary options to the config file */
+
+/* extern register functions declarations */
+extern int find_domains_register(sechk_lib_t * lib);
+extern int find_file_types_register(sechk_lib_t * lib);
+extern int domain_and_file_register(sechk_lib_t * lib);
+extern int attribs_wo_types_register(sechk_lib_t * lib);
+extern int roles_wo_types_register(sechk_lib_t * lib);
+extern int users_wo_roles_register(sechk_lib_t * lib);
+extern int roles_wo_allow_register(sechk_lib_t * lib);
+extern int types_wo_allow_register(sechk_lib_t * lib);
+extern int spurious_audit_register(sechk_lib_t * lib);
+extern int attribs_wo_rules_register(sechk_lib_t * lib);
+extern int inc_mount_register(sechk_lib_t * lib);
+extern int roles_wo_users_register(sechk_lib_t * lib);
+/* Deprecated *
+ extern int rules_exp_nothing_register(sechk_lib_t *lib);
+*/
+extern int domains_wo_roles_register(sechk_lib_t * lib);
+extern int inc_dom_trans_register(sechk_lib_t * lib);
+extern int find_port_types_register(sechk_lib_t * lib);
+extern int find_node_types_register(sechk_lib_t * lib);
+extern int find_netif_types_register(sechk_lib_t * lib);
+extern int find_assoc_types_register(sechk_lib_t * lib);
+extern int find_net_domains_register(sechk_lib_t * lib);
+extern int inc_net_access_register(sechk_lib_t * lib);
+extern int unreachable_doms_register(sechk_lib_t * lib);
+extern int imp_range_trans_register(sechk_lib_t * lib);
+/* TODO: additional externs go here ... */
+
+size_t sechk_register_list_get_num_modules(void);
+const sechk_module_name_reg_t *sechk_register_list_get_modules(void);
+size_t sechk_register_list_get_num_profiles(void);
+const sechk_profile_name_reg_t *sechk_register_list_get_profiles(void);
+#endif /* SECHK_REGISTER_LIST_H */
diff --git a/sechecker/sechecker.c b/sechecker/sechecker.c
new file mode 100644
index 0000000..57bb7f1
--- /dev/null
+++ b/sechecker/sechecker.c
@@ -0,0 +1,1230 @@
+/**
+ * @file
+ * Implementation of the public interface for all sechecker modules
+ * and the library.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2008 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "sechecker.h"
+#include "register_list.h"
+#include "sechk_parse.h"
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <apol/policy.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+
+#include <sefs/util.h>
+#include <sefs/fcfile.hh>
+#include <sefs/query.hh>
+
+#ifdef LIBSELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include <qpol/policy.h>
+#include <qpol/util.h>
+
+static int sechk_lib_compare_sev(const char *a, const char *b)
+{
+ int aval, bval;
+
+ if (a == NULL || b == NULL) {
+ assert(false);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strcmp(a, SECHK_SEV_NONE) == 0)
+ aval = 0;
+ else if (strcmp(a, SECHK_SEV_LOW) == 0)
+ aval = 1;
+ else if (strcmp(a, SECHK_SEV_MED) == 0)
+ aval = 2;
+ else if (strcmp(a, SECHK_SEV_HIGH) == 0)
+ aval = 3;
+ else {
+ assert(false);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strcmp(b, SECHK_SEV_NONE) == 0)
+ bval = 0;
+ else if (strcmp(b, SECHK_SEV_LOW) == 0)
+ bval = 1;
+ else if (strcmp(b, SECHK_SEV_MED) == 0)
+ bval = 2;
+ else if (strcmp(b, SECHK_SEV_HIGH) == 0)
+ bval = 3;
+ else {
+ assert(false);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (aval == bval)
+ return 0;
+
+ return aval < bval ? -1 : 1;
+}
+
+int sechk_lib_set_minsev(const char *minsev, sechk_lib_t * lib)
+{
+ if (lib == NULL || lib->policy == NULL || minsev == NULL) {
+ assert(false);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (strcmp(minsev, SECHK_SEV_LOW) == 0)
+ lib->minsev = SECHK_SEV_LOW;
+ else if (strcmp(minsev, SECHK_SEV_MED) == 0)
+ lib->minsev = SECHK_SEV_MED;
+ else if (strcmp(minsev, SECHK_SEV_HIGH) == 0)
+ lib->minsev = SECHK_SEV_HIGH;
+ else {
+ ERR(lib->policy, "%s", "Invalid severity.");
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+sechk_module_t *sechk_module_new(void)
+{
+ sechk_module_t *mod = NULL;
+ int error = 0;
+
+ mod = calloc(1, sizeof(sechk_module_t));
+ if (!mod)
+ return NULL;
+
+ /* create empty vectors */
+ if (!(mod->options = apol_vector_create(sechk_name_value_free)) ||
+ !(mod->requirements = apol_vector_create(sechk_name_value_free)) ||
+ !(mod->dependencies = apol_vector_create(sechk_name_value_free))
+ || !(mod->functions = apol_vector_create(sechk_fn_free))) {
+ error = errno;
+ apol_vector_destroy(&mod->options);
+ apol_vector_destroy(&mod->requirements);
+ apol_vector_destroy(&mod->dependencies);
+ apol_vector_destroy(&mod->functions);
+ free(mod);
+ errno = error;
+ return NULL;
+ }
+
+ return mod;
+}
+
+sechk_lib_t *sechk_lib_new(void)
+{
+ sechk_lib_t *lib = NULL;
+ int retv, error;
+ const sechk_module_name_reg_t *reg_list;
+ size_t num_known_modules = 0;
+ size_t i = 0;
+ sechk_module_t *tmp = NULL;
+
+ /* allocate the new sechk_lib_t structure */
+ lib = (sechk_lib_t *) calloc(1, sizeof(sechk_lib_t));
+ if (!lib) {
+ error = errno;
+ perror("Error creating module library");
+ goto exit_err;
+ }
+
+ /* create the module array from the known modules in register list */
+ num_known_modules = sechk_register_list_get_num_modules();
+ reg_list = sechk_register_list_get_modules();
+ lib->modules = apol_vector_create(sechk_module_free);
+ if (!lib->modules) {
+ error = errno;
+ perror("Error adding modules");
+ goto exit_err;
+
+ }
+ for (i = 0; i < num_known_modules; i++) {
+ tmp = sechk_module_new();
+ if (!tmp) {
+ error = errno;
+ perror("Error adding modules");
+ goto exit_err;
+ }
+ tmp->name = strdup(reg_list[i].name);
+ if (!tmp->name) {
+ error = errno;
+ perror("Error adding modules");
+ goto exit_err;
+ }
+ if (apol_vector_append(lib->modules, tmp)) {
+ error = errno;
+ perror("Error adding modules");
+ goto exit_err;
+ }
+ tmp = NULL;
+ }
+
+ /* set the default output format */
+ lib->outputformat = SECHK_OUT_SHORT;
+ lib->minsev = SECHK_SEV_LOW;
+
+ /* register modules */
+ if ((retv = sechk_lib_register_modules(reg_list, lib)) != 0) {
+ error = errno;
+ perror("Error registering modules");
+ goto exit_err;
+ }
+ exit:
+ return lib;
+
+ exit_err:
+ sechk_lib_destroy(&lib);
+ sechk_module_free(tmp);
+ errno = error;
+ goto exit;
+}
+
+void sechk_lib_destroy(sechk_lib_t ** lib)
+{
+ if (lib == NULL || *lib == NULL)
+ return;
+
+ apol_vector_destroy(&((*lib)->modules));
+ apol_policy_destroy(&((*lib)->policy));
+ apol_vector_destroy(&((*lib)->fc_entries));
+ free((*lib)->fc_path);
+ sefs_fclist_destroy(&((*lib)->fc_file));
+ free((*lib)->selinux_config_path);
+ apol_policy_path_destroy(&((*lib)->policy_path));
+ free(*lib);
+ *lib = NULL;
+}
+
+void sechk_module_free(void *module)
+{
+ sechk_module_t *mod = (sechk_module_t *) module;
+
+ if (!module)
+ return;
+
+ /* do not free describtin fields */
+ sechk_result_destroy(&mod->result);
+ apol_vector_destroy(&mod->options);
+ apol_vector_destroy(&mod->requirements);
+ apol_vector_destroy(&mod->dependencies);
+ /* do not free severity */
+ if (mod->data) {
+ assert(mod->data_free);
+ mod->data_free(mod->data);
+ }
+ free(mod->name);
+ mod->name = NULL;
+ apol_vector_destroy(&mod->functions);
+ free(mod);
+}
+
+void sechk_fn_free(void *fn_struct)
+{
+ sechk_fn_t *fn = (sechk_fn_t *) fn_struct;
+ if (!fn_struct)
+ return;
+
+ free(fn->name);
+ /* NEVER free fn->fn */
+ free(fn);
+}
+
+void sechk_name_value_free(void *nv)
+{
+ sechk_name_value_t *in = (sechk_name_value_t *) nv;
+ if (!nv)
+ return;
+
+ free(in->name);
+ free(in->value);
+ free(nv);
+}
+
+void sechk_result_destroy(sechk_result_t ** res)
+{
+ if (!res || !(*res))
+ return;
+
+ free((*res)->test_name);
+ apol_vector_destroy(&((*res)->items));
+ free(*res);
+ *res = NULL;
+}
+
+void sechk_item_free(void *item)
+{
+ sechk_item_t *it = (sechk_item_t *) item;
+
+ if (!item)
+ return;
+
+ apol_vector_destroy(&it->proof);
+ if (it->item_free_fn)
+ it->item_free_fn(it->item);
+
+ free(item);
+}
+
+void sechk_proof_free(void *proof)
+{
+ sechk_proof_t *p = (sechk_proof_t *) proof;
+
+ if (!proof)
+ return;
+
+ free(p->text);
+ free(p->xml_out);
+
+ if (p->elem_free_fn)
+ p->elem_free_fn(p->elem);
+
+ free(proof);
+}
+
+sechk_fn_t *sechk_fn_new(void)
+{
+ /* no initialization needed here */
+ return (sechk_fn_t *) calloc(1, sizeof(sechk_fn_t));
+}
+
+sechk_name_value_t *sechk_name_value_new(const char *name, const char *value)
+{
+ sechk_name_value_t *nv;
+ int error;
+
+ nv = (sechk_name_value_t *) calloc(1, sizeof(sechk_name_value_t));
+ if (!nv)
+ return NULL;
+ if (name) {
+ nv->name = strdup(name);
+ if (!nv->name) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (value) {
+ nv->value = strdup(value);
+ if (!nv->value) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ return nv;
+
+ err:
+ free(nv->name);
+ free(nv);
+ errno = error;
+ return NULL;
+}
+
+sechk_result_t *sechk_result_new(void)
+{
+ /* initilization to zero is sufficient here */
+ return (sechk_result_t *) calloc(1, sizeof(sechk_result_t));
+}
+
+sechk_item_t *sechk_item_new(free_fn_t fn)
+{
+ sechk_item_t *it = NULL;
+
+ it = (sechk_item_t *) calloc(1, sizeof(sechk_item_t));
+ if (!it)
+ return NULL;
+ it->item_free_fn = fn;
+
+ return it;
+}
+
+sechk_proof_t *sechk_proof_new(free_fn_t fn)
+{
+ sechk_proof_t *proof = NULL;
+ proof = (sechk_proof_t *) calloc(1, sizeof(sechk_proof_t));
+ if (!proof)
+ return NULL;
+ proof->type = SECHK_ITEM_NONE;
+ proof->elem_free_fn = fn;
+ return proof;
+}
+
+int sechk_lib_load_policy(apol_policy_path_t * policy_mods, sechk_lib_t * lib)
+{
+
+ char *default_policy_path = NULL;
+ int retv = -1;
+
+ if (!lib)
+ return -1;
+
+ /* if no policy is given, attempt to find default */
+ if (!policy_mods) {
+ retv = qpol_default_policy_find(&default_policy_path);
+ if (retv < 0) {
+ fprintf(stderr, "Default policy search failed: %s\n", strerror(errno));
+ return -1;
+ } else if (retv != 0) {
+ fprintf(stderr, "No default policy found.\n");
+ return -1;
+ }
+ policy_mods = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, default_policy_path, NULL);
+ lib->policy = apol_policy_create_from_policy_path(policy_mods, QPOL_POLICY_OPTION_MATCH_SYSTEM, NULL, NULL);
+ if (lib->policy == NULL) {
+ fprintf(stderr, "Error: failed opening default policy\n");
+ return -1;
+ }
+ lib->policy_path = policy_mods;
+ if (!(lib->outputformat & SECHK_OUT_QUIET)) {
+ fprintf(stderr, "Using policy: %s\n", apol_policy_path_get_primary(lib->policy_path));
+ }
+ } else {
+ lib->policy_path = policy_mods;
+ lib->policy = apol_policy_create_from_policy_path(policy_mods, 0, NULL, NULL);
+ if (lib->policy == NULL) {
+ fprintf(stderr, "Error: failed opening policy %s\n", apol_policy_path_to_string(lib->policy_path));
+ goto err;
+ }
+ }
+ return 0;
+
+ err:
+ apol_policy_destroy(&lib->policy);
+ return -1;
+}
+
+int sechk_lib_load_fc(const char *fcfilelocation, sechk_lib_t * lib)
+{
+ int error = 0;
+ char *default_fc_path = NULL;
+ sefs_fclist_t *fcfile = NULL;
+ sefs_query_t *q = NULL;
+
+ /* if no policy we can't parse the fc file */
+ if (!lib || !lib->policy) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* if no file_contexts file is given attempt to find the default */
+ if (!fcfilelocation) {
+ default_fc_path = sefs_default_file_contexts_get_path();
+ if (default_fc_path == NULL) {
+ error = errno;
+ WARN(lib->policy, "Unable to find default file_contexts file: %s", strerror(error));
+ errno = error;
+ return 0; /* not fatal error until a module requires this to exist */
+ }
+ if (strcmp(default_fc_path, "") == 0) {
+ WARN(lib->policy, "%s", "The system has no default file_contexts file.");
+ free(default_fc_path);
+ errno = ENOSYS;
+ return 0; /* not fatal error until a module requires this to exist */
+ }
+ fcfile = sefs_fcfile_create_from_file(default_fc_path, NULL, NULL);
+ q = sefs_query_create();
+ lib->fc_entries = sefs_fclist_run_query(fcfile, q);
+ if (!(lib->fc_entries)) {
+ error = errno;
+ WARN(lib->policy, "Unable to process file_contexts file %s.", default_fc_path);
+ free(default_fc_path);
+ errno = error;
+ return -1;
+ } else {
+ lib->fc_path = default_fc_path;
+ lib->fc_file = fcfile;
+ }
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ fprintf(stderr, "Using file contexts: %s\n", lib->fc_path);
+ }
+ } else {
+ fcfile = sefs_fcfile_create_from_file(fcfilelocation, NULL, NULL);
+ q = sefs_query_create();
+ lib->fc_entries = sefs_fclist_run_query(fcfile, q);
+ if (!(lib->fc_entries)) {
+ error = errno;
+ WARN(lib->policy, "Unable to process file_contexts file %s.", fcfilelocation);
+ errno = error;
+ return -1;
+ } else {
+ lib->fc_path = strdup(fcfilelocation);
+ lib->fc_file = fcfile;
+ }
+ }
+ sefs_query_destroy(&q);
+
+ return 0;
+}
+
+int sechk_lib_register_modules(const sechk_module_name_reg_t * register_fns, sechk_lib_t * lib)
+{
+ int retv, error = 0;
+ size_t i;
+ sechk_register_fn_t fn = NULL;
+ if (!register_fns || !lib) {
+ fprintf(stderr, "Error: could not register modules\n");
+ errno = EINVAL;
+ return -1;
+ }
+ if (apol_vector_get_size(lib->modules) != sechk_register_list_get_num_modules()) {
+ fprintf(stderr,
+ "Error: the number of registered modules (%zd) does not match the number of modules in the configuration file (%zd).\n",
+ sechk_register_list_get_num_modules(), apol_vector_get_size(lib->modules));
+ errno = EINVAL;
+ return -1;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ fn = (sechk_register_fn_t) (register_fns[i].fn);
+ retv = fn(lib);
+ if (retv) {
+ error = errno;
+ fprintf(stderr, "Error: could not register module #%zd\n", i);
+ errno = error;
+ return retv;
+ }
+ }
+
+ return 0;
+}
+
+sechk_mod_fn_t sechk_lib_get_module_function(const char *module_name, const char *function_name, const sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+ sechk_fn_t *fn_struct = NULL;
+ size_t i;
+
+ if (!module_name || !function_name || !lib) {
+ fprintf(stderr, "Error: failed to get function from module\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* find the correct module */
+ mod = sechk_lib_get_module(module_name, lib);
+ if (!mod) {
+ fprintf(stderr, "Error: %s: no such module\n", module_name);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /* find function in module */
+ for (i = 0; i < apol_vector_get_size(mod->functions); i++) {
+ fn_struct = apol_vector_get_element(mod->functions, i);
+ if (!strcmp(fn_struct->name, function_name))
+ break;
+ else
+ fn_struct = NULL;
+ }
+ if (!fn_struct) {
+ fprintf(stderr, "Error: %s: no such function in module %s\n", function_name, module_name);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ return fn_struct->fn;
+}
+
+sechk_module_t *sechk_lib_get_module(const char *module_name, const sechk_lib_t * lib)
+{
+ size_t i;
+ sechk_module_t *mod = NULL;
+
+ if (!module_name || !lib) {
+ fprintf(stderr, "Error: failed to get module\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (!(mod->name))
+ continue;
+ if (!strcmp(mod->name, module_name))
+ return mod;
+ }
+ fprintf(stderr, "Error: %s: no such module\n", module_name);
+ errno = ENOENT;
+ return NULL;
+}
+
+sechk_result_t *sechk_lib_get_module_result(const char *module_name, const sechk_lib_t * lib)
+{
+ size_t i;
+ sechk_module_t *mod = NULL;
+ sechk_mod_fn_t run = NULL;
+
+ if (!module_name || !lib) {
+ fprintf(stderr, "Error: failed to get module result\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (!(mod->name))
+ continue;
+ if (strcmp(mod->name, module_name))
+ continue;
+ if (!(mod->result)) {
+ if (!(run = sechk_lib_get_module_function(module_name, SECHK_MOD_FN_RUN, lib)) ||
+ run(mod, lib->policy, NULL) < 0) {
+ return NULL; /* run or get function will set errno */
+ }
+ }
+ return mod->result;
+ }
+ fprintf(stderr, "Error: %s: no such module\n", module_name);
+ errno = ENOENT;
+ return NULL;
+}
+
+int sechk_lib_check_module_dependencies(sechk_lib_t * lib)
+{
+ int idx = 0;
+ size_t i, j;
+ bool test = true, done = false, *processed = NULL;
+ sechk_name_value_t *nv = NULL;
+ sechk_module_t *mod = NULL, *dep = NULL;
+
+ if (!lib) {
+ fprintf(stderr, "Error: invalid module library\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ processed = (bool *) calloc(apol_vector_get_size(lib->modules), sizeof(bool));
+ if (!processed) {
+ perror(NULL);
+ return -1;
+ }
+
+ /* check dependencies and select dependencies to be run */
+ while (!done) {
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ if (processed[i])
+ continue;
+ mod = apol_vector_get_element(lib->modules, i);
+ if (!mod->selected) {
+ processed[i] = true;
+ continue;
+ }
+ for (j = 0; j < apol_vector_get_size(mod->dependencies); j++) {
+ nv = apol_vector_get_element(mod->dependencies, j);
+ test = false;
+ test = sechk_lib_check_dependency(nv, lib);
+ if (!test) {
+ ERR(lib->policy, "Dependency %s not found for %s.", nv->name, mod->name);
+ free(processed);
+ errno = ENOENT;
+ return -1;
+ }
+ idx = sechk_lib_get_module_idx(nv->value, lib);
+ dep = apol_vector_get_element(lib->modules, idx);
+ if (!dep->selected) {
+ processed[idx] = false;
+ dep->selected = true;
+ }
+ }
+ processed[i] = true;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ if (!processed[i])
+ break;
+ }
+ if (i == apol_vector_get_size(lib->modules))
+ done = true;
+ }
+ free(processed);
+
+ return 0;
+}
+
+int sechk_lib_check_module_requirements(sechk_lib_t * lib)
+{
+ int retv = 0;
+ size_t i, j;
+ bool test = true;
+ sechk_name_value_t *nv = NULL;
+ sechk_module_t *mod = NULL;
+
+ /* check requirements for all selected modules */
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (!mod->selected)
+ continue;
+ for (j = 0; j < apol_vector_get_size(mod->requirements); j++) {
+ nv = apol_vector_get_element(mod->requirements, j);
+ test = false;
+ test = sechk_lib_check_requirement(nv, lib);
+ if (!test) {
+ /* if we're in quiet mode then we quit on a failed requirement */
+ if (lib->outputformat & (SECHK_OUT_QUIET)) {
+ errno = ENOTSUP;
+ return -1;
+ } else {
+ /* otherwise we just disable this module and keep testing */
+ ERR(lib->policy, "Requirements not met for %s.", mod->name);
+ mod->selected = false;
+ retv = -1;
+ break;
+ }
+ }
+ }
+ }
+ return retv;
+}
+
+int sechk_lib_init_modules(sechk_lib_t * lib)
+{
+ int retv, error = 0;
+ size_t i;
+ sechk_module_t *mod = NULL;
+ sechk_mod_fn_t init_fn = NULL;
+
+ if (lib == NULL || lib->modules == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (!mod->selected)
+ continue;
+ init_fn = sechk_lib_get_module_function(mod->name, SECHK_MOD_FN_INIT, lib);
+ if (!init_fn) {
+ error = errno;
+ fprintf(stderr, "Error: could not initialize module %s\n", mod->name);
+ errno = error;
+ return -1;
+ }
+ retv = init_fn(mod, lib->policy, NULL);
+ if (retv)
+ return retv;
+ }
+
+ return 0;
+}
+
+int sechk_lib_run_modules(sechk_lib_t * lib)
+{
+ int retv, num_selected = 0, rc = 0;
+ size_t i;
+ sechk_module_t *mod = NULL;
+ sechk_mod_fn_t run_fn = NULL;
+
+ if (!lib) {
+ fprintf(stderr, "Error: invalid library\n");
+ errno = EINVAL;
+ return -1;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (mod->selected)
+ num_selected++;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ /* if module is "off" do not run */
+ if (!mod->selected)
+ continue;
+ /* if module is below the minsev do not run unless its exactly one module */
+ if (lib->minsev && sechk_lib_compare_sev(mod->severity, lib->minsev) < 0 && num_selected != 1)
+ continue;
+ assert(mod->name);
+ run_fn = sechk_lib_get_module_function(mod->name, SECHK_MOD_FN_RUN, lib);
+ if (!run_fn) {
+ ERR(lib->policy, "Could not run module %s.", mod->name);
+ errno = ENOTSUP;
+ return -1;
+ }
+ retv = run_fn(mod, lib->policy, NULL);
+
+ if (retv < 0) {
+ /* module failure */
+ /* only put output failures if we are not in quiet mode */
+ if (lib->outputformat & ~(SECHK_OUT_QUIET))
+ ERR(lib->policy, "Module %s failed.", mod->name);
+ rc = -1;
+ } else if (retv > 0) {
+ /* a module looking for policy errors has found one
+ * if in quiet mode stop since running additional
+ * modules will not change the return code */
+ if (lib->outputformat & (SECHK_OUT_QUIET))
+ return -1;
+ }
+ }
+ return rc;
+}
+
+int sechk_lib_print_modules_report(sechk_lib_t * lib)
+{
+ int retv, num_selected = 0, rc = 0;
+ size_t i;
+ sechk_module_t *mod = NULL;
+ sechk_mod_fn_t print_fn = NULL;
+
+ if (!lib) {
+ fprintf(stderr, "Error: invalid library\n");
+ errno = EINVAL;
+ return -1;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (mod->selected)
+ num_selected++;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ /* if module is "off" or its output format is quiet continue */
+ if (!mod->selected || mod->outputformat & SECHK_OUT_QUIET)
+ continue;
+ /* if module is below the minsev do not print unless exactly one module is selected */
+ if (lib->minsev && sechk_lib_compare_sev(mod->severity, lib->minsev) < 0 && num_selected != 1)
+ continue;
+ /* if module is the only selected one make sure output is generated */
+ if (mod->outputformat == SECHK_OUT_NONE && num_selected == 1)
+ mod->outputformat = SECHK_OUT_SHORT;
+ assert(mod->name);
+ printf("\nModule name: %s\tSeverity: %s\n%s\n", mod->name, mod->severity, mod->detailed_description);
+ print_fn = sechk_lib_get_module_function(mod->name, SECHK_MOD_FN_PRINT, lib);
+ if (!print_fn) {
+ ERR(lib->policy, "Could not get print function for module %s.", mod->name);
+ errno = ENOTSUP;
+ return -1;
+ }
+ retv = print_fn(mod, lib->policy, NULL);
+ if (retv) {
+ fprintf(stderr, "Error: unable to print results for module %s \n", mod->name);
+ rc = -1;
+ }
+ }
+
+ return rc;
+}
+
+bool sechk_lib_check_requirement(sechk_name_value_t * req, sechk_lib_t * lib)
+{
+ struct stat stat_buf;
+
+ if (!req) {
+ fprintf(stderr, "Error: invalid requirement\n");
+ errno = EINVAL;
+ return false;
+ }
+
+ if (!lib || !lib->policy) {
+ fprintf(stderr, "Error: invalid library\n");
+ errno = EINVAL;
+ return false;
+ }
+
+ if (!strcmp(req->name, SECHK_REQ_POLICY_CAP)) {
+ if (!strcmp(req->value, SECHK_REQ_CAP_ATTRIB_NAMES)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_ATTRIB_NAMES)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else if (!strcmp(req->value, SECHK_REQ_CAP_MLS)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_MLS)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else if (!strcmp(req->value, SECHK_REQ_CAP_SYN_RULES)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_SYN_RULES)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else if (!strcmp(req->value, SECHK_REQ_CAP_RULES_LOADED)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_RULES_LOADED)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else if (!strcmp(req->value, SECHK_REQ_CAP_LINE_NOS)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_LINE_NUMBERS)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else if (!strcmp(req->value, SECHK_REQ_CAP_CONDITIONALS)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_CONDITIONALS)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else if (!strcmp(req->value, SECHK_REQ_CAP_MODULES)) {
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(lib->policy), QPOL_CAP_MODULES)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ }
+ return false;
+ }
+ } else {
+ ERR(lib->policy, "Unknown requirement: %s, %s", req->name, req->value);
+ return false;
+ }
+ } else if (!strcmp(req->name, SECHK_REQ_DEFAULT_CONTEXTS)) {
+#ifdef LIBSELINUX
+ if (stat(selinux_default_context_path(), &stat_buf) < 0) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s not met.", req->name);
+ }
+ return false;
+ }
+#else
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Checking requirement %s: %s", req->name, strerror(ENOTSUP));
+ }
+ return false;
+#endif
+ } else if (!strcmp(req->name, SECHK_REQ_FILE_CONTEXTS)) {
+ if (!lib->fc_entries || !apol_vector_get_size(lib->fc_entries)) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET)) {
+ ERR(lib->policy, "Requirement %s not met.", req->name);
+ }
+ }
+ } else if (!strcmp(req->name, SECHK_REQ_SYSTEM)) {
+ if (!strcmp(req->value, SECHK_REQ_SYS_SELINUX)) {
+#ifdef LIBSELINUX
+ if (!is_selinux_mls_enabled() || !is_selinux_enabled()) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET))
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ return false;
+ }
+#else
+ if (lib->outputformat & ~(SECHK_OUT_QUIET))
+ ERR(lib->policy, "Checking requirement %s, %s: %s", req->name, req->value, strerror(ENOTSUP));
+ return false;
+#endif
+ } else if (!strcmp(req->value, SECHK_REQ_SYS_MLS)) {
+#ifdef LIBSELINUX
+ if (!is_selinux_mls_enabled() || !is_selinux_enabled()) {
+ if (lib->outputformat & ~(SECHK_OUT_QUIET))
+ ERR(lib->policy, "Requirement %s, %s not met.", req->name, req->value);
+ return false;
+ }
+#else
+ if (lib->outputformat & ~(SECHK_OUT_QUIET))
+ ERR(lib->policy, "Checking requirement %s, %s: %s", req->name, req->value, strerror(ENOTSUP));
+ return false;
+#endif
+ } else {
+ ERR(lib->policy, "Unknown requirement: %s, %s", req->name, req->value);
+ return false;
+ }
+ } else {
+ ERR(lib->policy, "Unknown requirement: %s, %s", req->name, req->value);
+ return false;
+ }
+
+ return true;
+}
+
+bool sechk_lib_check_dependency(sechk_name_value_t * dep, sechk_lib_t * lib)
+{
+ sechk_module_t *mod = NULL;
+
+ if (!dep || !dep->value) {
+ fprintf(stderr, "Error: invalid dependency\n");
+ errno = EINVAL;
+ return false;
+ }
+
+ if (!lib) {
+ fprintf(stderr, "Error: invalid library\n");
+ errno = EINVAL;
+ return false;
+ }
+
+ mod = sechk_lib_get_module(dep->value, lib);
+ if (!mod) {
+ fprintf(stderr, "Error: could not find dependency %s\n", dep->value);
+ errno = ENOENT;
+ return false;
+ }
+
+ return true;
+}
+
+int sechk_lib_set_outputformat(unsigned char out, sechk_lib_t * lib)
+{
+ size_t i;
+ sechk_module_t *mod = NULL;
+
+ if (!lib || !out) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ lib->outputformat = out;
+
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ mod->outputformat = out;
+ }
+
+ return 0;
+}
+
+sechk_proof_t *sechk_proof_copy(sechk_proof_t * orig)
+{
+ sechk_proof_t *copy = NULL;
+
+ if (!orig)
+ return NULL;
+
+ copy = sechk_proof_new(orig->elem_free_fn);
+ if (!copy) {
+ fprintf(stderr, "Error: out of memory\n");
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ copy->elem = orig->elem;
+ copy->type = orig->type;
+ copy->text = strdup(orig->text);
+ if (!copy->text) {
+ fprintf(stderr, "Error: out of memory\n");
+ errno = ENOMEM;
+ return NULL;
+ }
+ copy->xml_out = NULL; /* TODO: do xml string copy here */
+
+ return copy;
+}
+
+int sechk_lib_load_profile(const char *prof_name, sechk_lib_t * lib)
+{
+ const sechk_profile_name_reg_t *profiles;
+ char *profpath = NULL, *prof_filename = NULL, *path = NULL;
+ int retv = -1, error = 0;
+ size_t num_profiles, i;
+ sechk_module_t *mod = NULL;
+
+ if (!prof_name || !lib) {
+ fprintf(stderr, "Error: invalid parameters to load profile\n");
+ return -1;
+ }
+
+ /* try to find the profile in our known profiles */
+ profiles = sechk_register_list_get_profiles();
+ num_profiles = sechk_register_list_get_num_profiles();
+ for (i = 0; i < num_profiles; i++) {
+ if (strcmp(profiles[i].name, prof_name) == 0) {
+ break;
+ }
+ }
+ /* this is a known installed profile, look for it in that directory */
+ if (i < num_profiles) {
+ /* first look in the local subdir using just PROF_SUBDIR/profile */
+ prof_filename = (char *)calloc(strlen(profiles[i].file) + 4 + strlen(PROF_SUBDIR), sizeof(char));
+ if (!prof_filename) {
+ fprintf(stderr, "Error: out of memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+ sprintf(prof_filename, "%s/%s", PROF_SUBDIR, profiles[i].file);
+ path = apol_file_find(prof_filename);
+ if (!path) {
+ free(prof_filename);
+ prof_filename = NULL;
+ prof_filename = (char *)calloc(strlen(PROFILE_INSTALL_DIR) + strlen(profiles[i].file) + 4, sizeof(char));
+ if (!prof_filename) {
+ fprintf(stderr, "Error: out of memory\n");
+ errno = ENOMEM;
+ return -1;
+ }
+ sprintf(prof_filename, "%s/%s", PROFILE_INSTALL_DIR, profiles[i].file);
+ path = apol_file_find(prof_filename);
+ if (!path) {
+ fprintf(stderr, "Error: Unable to find path\n");
+ error = ENOENT;
+ goto sechk_load_profile_error;
+ }
+ }
+
+ /* concatenate path and filename */
+ profpath = (char *)calloc(3 + strlen(path) + strlen(prof_filename), sizeof(char));
+ if (!profpath) {
+ fprintf(stderr, "Error: out of memory\n");
+ error = ENOMEM;
+ goto sechk_load_profile_error;
+ }
+ sprintf(profpath, "%s/%s", path, prof_filename);
+ free(path);
+ free(prof_filename);
+ path = NULL;
+ prof_filename = NULL;
+ } else {
+ profpath = strdup(prof_name);
+ }
+
+ /* parse the profile */
+ retv = sechk_lib_parse_profile(profpath, lib);
+ if (retv) {
+ error = errno;
+ fprintf(stderr, "Error: could not parse profile\n");
+ goto sechk_load_profile_error;
+ }
+
+ /* turn off output for any unselected modules */
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (!mod->selected)
+ mod->outputformat = SECHK_OUT_NONE;
+ }
+
+ free(profpath);
+ free(prof_filename);
+ free(path);
+ return 0;
+
+ sechk_load_profile_error:
+ free(profpath);
+ free(prof_filename);
+ free(path);
+ errno = error;
+ return -1;
+}
+
+static int sechk_option_name_compare(const void *a, const void *b, void *data __attribute__ ((unused)))
+{
+ sechk_name_value_t *in_a, *in_b;
+
+ in_a = (sechk_name_value_t *) a;
+ in_b = (sechk_name_value_t *) b;
+
+ return strcmp(in_a->name, in_b->name);
+}
+
+int sechk_lib_module_clear_option(sechk_module_t * module, char *option)
+{
+ apol_vector_t *new_opts = NULL;
+ sechk_name_value_t *needle = NULL, *nv = NULL, *tmp = NULL;
+ int error = 0;
+ size_t i = 0;
+
+ if (!module || !option) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(needle = sechk_name_value_new(option, NULL))) {
+ error = errno;
+ ERR(module->parent_lib->policy, "Clearing option %s: %s.", option, strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ /* if not here nothing to do */
+ if (apol_vector_get_index(module->options, needle, sechk_option_name_compare, NULL, &i) < 0) {
+ sechk_name_value_free(needle);
+ return 0;
+ }
+
+ if (!(new_opts = apol_vector_create(sechk_name_value_free))) {
+ error = errno;
+ ERR(module->parent_lib->policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+ }
+
+ /* add all options of a different name to a new vector to replace the old */
+ for (i = 0; i < apol_vector_get_size(module->options); i++) {
+ nv = apol_vector_get_element(module->options, i);
+ if (strcmp(nv->name, needle->name)) {
+ if (!(tmp = sechk_name_value_new(nv->name, nv->value))) {
+ error = errno;
+ WARN(module->parent_lib->policy, "Clearing option %s: %s.", option, strerror(error));
+ goto err;
+ }
+ if (apol_vector_append(new_opts, (void *)tmp)) {
+ error = errno;
+ WARN(module->parent_lib->policy, "Clearing option %s: %s.", option, strerror(error));
+ goto err;
+ }
+ tmp = NULL; /* avoid double free */
+ }
+ }
+
+ sechk_name_value_free(needle);
+ apol_vector_destroy(&module->options);
+ module->options = new_opts;
+
+ return 0;
+
+ err:
+ sechk_name_value_free(tmp);
+ sechk_name_value_free(needle);
+ apol_vector_destroy(&new_opts);
+ errno = error;
+ return -1;
+}
+
+/* get the index of a module in the library by name */
+int sechk_lib_get_module_idx(const char *name, sechk_lib_t * lib)
+{
+ size_t i;
+ sechk_module_t *mod = NULL;
+
+ if (!name || !lib || !lib->modules) {
+ errno = EINVAL;
+ return -1;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ if (mod->name && !strcmp(name, mod->name))
+ return i;
+ }
+ errno = ENOENT;
+ return -1;
+}
+
+int sechk_proof_with_element_compare(const void *in_proof, const void *elem, void *unused __attribute__ ((unused)))
+{
+ const sechk_proof_t *proof = (const sechk_proof_t *)in_proof;
+
+ if (!proof)
+ return 1;
+
+ /* explicit pointer to integer cast */
+ return (int)((char *)proof->elem - (char *)elem);
+}
diff --git a/sechecker/sechecker.h b/sechecker/sechecker.h
new file mode 100644
index 0000000..4f0bd46
--- /dev/null
+++ b/sechecker/sechecker.h
@@ -0,0 +1,695 @@
+/**
+ * @file
+ * Defines the public interface for all sechecker modules and the library.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SECHECKER_H
+#define SECHECKER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <apol/policy-query.h>
+#include <apol/vector.h>
+#include <apol/util.h>
+
+#include <sefs/fcfile.hh>
+#include <libxml/xmlstring.h>
+
+/* These should be defined from the make environment */
+#ifndef PROF_SUBDIR
+#define PROF_SUBDIR "/sechecker-profiles"
+#endif
+#ifndef DEFAULT_PROFILE
+#define DEFAULT_PROFILE ""
+#endif
+
+/* defined flags for outformat */
+/* report components */
+#define SECHK_OUT_STATS 0x01
+#define SECHK_OUT_LIST 0x02
+#define SECHK_OUT_PROOF 0x04
+/* mode flags from command line and test profiles */
+/* NOTE: none is only valid in profiles */
+#define SECHK_OUT_NONE 0x00
+#define SECHK_OUT_QUIET 0x20
+#define SECHK_OUT_SHORT (SECHK_OUT_STATS|SECHK_OUT_LIST)
+#define SECHK_OUT_VERBOSE (SECHK_OUT_STATS|SECHK_OUT_PROOF)
+
+ typedef void (*free_fn_t) (void *x);
+
+/* severity categories */
+#define SECHK_SEV_NONE "none"
+#define SECHK_SEV_LOW "low"
+#define SECHK_SEV_MED "med"
+#define SECHK_SEV_HIGH "high"
+
+/* module requirement name strings */
+/** Require that the loaded policy has a given capability;
+ * values should be set as one of SECHK_REQ_CAP_* from below. */
+#define SECHK_REQ_POLICY_CAP "capability"
+/** Require that the running system supports a given feature;
+ * values should be set as one of SECHK_REQ_SYS_* from below. */
+#define SECHK_REQ_SYSTEM "system"
+/** Require a file_contexts file to be loaded.
+ * This requirement has no associated value text. */
+#define SECHK_REQ_FILE_CONTEXTS "file_contexts"
+/** Require a default_contexts file to be loaded.
+ * This requirement has no associated value text. */
+#define SECHK_REQ_DEFAULT_CONTEXTS "default_contexts"
+
+/* policy capability requirement strings: map to QPOL_CAP_* in policy_query.h */
+/** Require that the loaded policy supports attribute names. */
+#define SECHK_REQ_CAP_ATTRIB_NAMES "attribute names"
+/** Require that the loaded policy supports syntactic rules. */
+#define SECHK_REQ_CAP_SYN_RULES "syntactic rules"
+/** Require that the loaded policy supports line numbers. */
+#define SECHK_REQ_CAP_LINE_NOS "line numbers"
+/** Require that the loaded policy supports booleans and conditional statements. */
+#define SECHK_REQ_CAP_CONDITIONALS "conditionals"
+/** Require that the loaded policy supports MLS. */
+#define SECHK_REQ_CAP_MLS "mls"
+/** Require that the loaded policy supports module loading. */
+#define SECHK_REQ_CAP_MODULES "module loading"
+/** Require that the loaded policy includes av/te rules. */
+#define SECHK_REQ_CAP_RULES_LOADED "rules loaded"
+
+/* system requirement strings */
+/** Require that the running system is a SELinux system. */
+#define SECHK_REQ_SYS_SELINUX "selinux"
+/** Require that the running system supports MLS*/
+#define SECHK_REQ_SYS_MLS "mls"
+
+/** item and proof element types to denote casting of the void pointer */
+ typedef enum sechk_item_type
+ {
+ SECHK_ITEM_CLASS, /*!< qpol_class_t */
+ SECHK_ITEM_COMMON, /*!< qpol_common_t */
+ SECHK_ITEM_PERM, /*!< char * representing the permission name */
+ SECHK_ITEM_CONSTR, /*!< qpol_constraint_t */
+ SECHK_ITEM_VTRANS, /*!< qpol_validatetrans_t */
+ SECHK_ITEM_QLEVEL, /*!< qpol_level_t */
+ SECHK_ITEM_CAT, /*!< qpol_cat_t */
+ SECHK_ITEM_QMLSLEVEL, /*!< qpol_mls_level_t */
+ SECHK_ITEM_QMLSRANGE, /*!< qpol_mls_range_t */
+ SECHK_ITEM_AMLSLEVEL, /*!< apol_mls_level_t */
+ SECHK_ITEM_AMLSRANGE, /*!< apol_mls_range_t */
+ SECHK_ITEM_TYPE, /*!< qpol_type_t */
+ SECHK_ITEM_ATTRIB, /*!< qpol_type_t but is an atribute not a type */
+ SECHK_ITEM_ROLE, /*!< qpol_role_t */
+ SECHK_ITEM_USER, /*!< qpol_user_t */
+ SECHK_ITEM_COND, /*!< qpol_cond_t */
+ SECHK_ITEM_AVRULE, /*!< qpol_avrule_t */
+ SECHK_ITEM_TERULE, /*!< qpol_terule_t */
+ SECHK_ITEM_RALLOW, /*!< qpol_role_allow_t */
+ SECHK_ITEM_RTRAMS, /*!< qpol_role_trans_t */
+ SECHK_ITEM_RANGETRANS, /*!< qpol_range_trans_t */
+ SECHK_ITEM_BOOL, /*!< qpol_bool_t */
+ SECHK_ITEM_FSUSE, /*!< qpol_fs_use_t */
+ SECHK_ITEM_GENFSCON, /*!< qpol_genfscon_t */
+ SECHK_ITEM_ISID, /*!< qpol_isid_t */
+ SECHK_ITEM_NETIFCON, /*!< qpol_netifcon_t */
+ SECHK_ITEM_NODECON, /*!< qpol_nodecon_t */
+ SECHK_ITEM_PORTCON, /*!< qpol_portcon_t */
+ SECHK_ITEM_CONTEXT, /*!< qpol_context_t */
+ /* add more here as needed */
+ SECHK_ITEM_FCENT, /*!< sefs_entry_t */
+ SECHK_ITEM_STR, /*!< char* generic string */
+ SECHK_ITEM_DTR, /*!< apol_domain_trans_result_t */
+ SECHK_ITEM_OTHER, /*!< void* data is something else (module specific) */
+ SECHK_ITEM_NONE /*!< there is no proof element only text */
+ } sechk_item_type_e;
+
+/** Module results proof element: This represents a single reason for the
+ * inclusion of an item in the results. */
+ typedef struct sechk_proof
+ {
+ /** Component, rule, or other object relative to the policy */
+ void *elem;
+ /** The type of element stored by this proof */
+ sechk_item_type_e type;
+ /** Description of proof for prining the report */
+ char *text;
+ xmlChar *xml_out; /* currently unused but retained for future use */
+ /** Function to call if elem should be free()'d or NULL */
+ free_fn_t elem_free_fn;
+ } sechk_proof_t;
+
+/** Module results item:
+ * This represents an item for which results were found. */
+ typedef struct sechk_item
+ {
+ /** The policy item */
+ void *item;
+ /** Test result code for this item. This field is reserved for use
+ * only within the module creating the item. */
+ unsigned char test_result;
+ /** Vector of proof elements (of type sechk_proof_t) indicating
+ * why an item appears in the results. */
+ apol_vector_t *proof;
+ /** Function to call if item should be free()'d or NULL */
+ free_fn_t item_free_fn;
+ } sechk_item_t;
+
+/** Module results: This represents the results generated by a module's
+ * run function. This structure is used both to generate the report and
+ * to comunicate with other modules that depend on the generating module. */
+ typedef struct sechk_result
+ {
+ /** Name of the module that created the results. */
+ char *test_name;
+ /** The type of policy item processed by the module. */
+ sechk_item_type_e item_type;
+ /** Vector of items for which results were found (of type sechk_item_t). */
+ apol_vector_t *items;
+ } sechk_result_t;
+
+/** Generic name value pair:
+ * Used for storing options, dependencies and requirements. */
+ typedef struct sechk_name_value
+ {
+ char *name;
+ char *value;
+ } sechk_name_value_t;
+
+/** Module library:
+ * This structure tracks all modules that SEChecker can run,
+ * the policy, and other policy related data. */
+ typedef struct sechk_lib
+ {
+ /** Vector of the modules (of type sechk_module_t) */
+ apol_vector_t *modules;
+ /** The policy to analyze when running modules */
+ apol_policy_t *policy;
+ /** Vector of file contexts data (of type sefs_entry_t). */
+ apol_vector_t *fc_entries;
+ /** File name of the file_contexts file loaded. */
+ char *fc_path;
+ /** The file_contexts file object. */
+ sefs_fclist_t *fc_file;
+ /** The default output format for the report */
+ unsigned char outputformat;
+ /** The path for the selinux configuration file */
+ char *selinux_config_path;
+ /** File name of the policy loaded.*/
+ apol_policy_path_t *policy_path;
+ /** Minimum severity level specified for the report. */
+ const char *minsev;
+ } sechk_lib_t;
+
+ typedef struct sechk_module
+ {
+ /** Unique module name */
+ char *name;
+ /** Brief description of the module */
+ const char *brief_description;
+ /** Detailed description of the module. This should include a listing of
+ * of all steps performed by the module's checks. */
+ const char *detailed_description;
+ /** Description of options, requirements, and dependencies */
+ const char *opt_description;
+ /** Results generated by this module. */
+ sechk_result_t *result;
+ /** Vector (of type sechk_name_value_t) containing all user specified
+ * options for this module. */
+ apol_vector_t *options;
+ /** Vector (of type sechk_name_value_t) containing all conditions required
+ * by the module such as policy version or type. See profile documentation
+ * for a complete listing of possible requirements. */
+ apol_vector_t *requirements;
+ /** Vector (of type sechk_name_value_t) containing a list of modules
+ * which need to run before this module may access their results. */
+ apol_vector_t *dependencies;
+ /** Vector (of type sechk_fn_t) of all functions registered for this module.
+ * All modules are required to have at least three: init, run, and print. */
+ apol_vector_t *functions;
+ /** Default output format for the report. */
+ unsigned char outputformat;
+ /** This field is used by the library to indicate that the user or another
+ * module has selected this module to be run. */
+ bool selected;
+ /** The severity level of this module's results. One of SECHK_SEV_* above. */
+ const char *severity;
+ /** The module's private data. This includes data generated when processing
+ * options and from reading its dependencies' results. */
+ void *data;
+ /** Function to be used to free the private data. */
+ free_fn_t data_free;
+ /** Pointer to the module's parent library. */
+ const sechk_lib_t *parent_lib;
+ } sechk_module_t;
+
+/* Module function signatures */
+/**
+ * Function signature for the function to register a module with the library.
+ *
+ * @param lib The library with which to register.
+ *
+ * @return 0 on success and < 0 on failure; if the call fails, errno will be
+ * set and the library should be destroyed.
+ */
+ typedef int (*sechk_register_fn_t) (sechk_lib_t * lib);
+
+/**
+ * Function signature for functions registed by a module.
+ *
+ * @param mod The module performing the operation.
+ * @param policy The policy accessed by the module.
+ * @param arg Arbitrary third parameter for use by the function.
+ *
+ * @return 0 on success, or < 0 on fatal error. If the call fails,
+ * it is expected to set errno. Special: a run function is permitted
+ * to return > 0 upon finding results; only the run function may return > 0.
+ */
+ typedef int (*sechk_mod_fn_t) (sechk_module_t * mod, apol_policy_t * policy, void *arg);
+
+/* Module function names */
+#define SECHK_MOD_FN_INIT "init"
+#define SECHK_MOD_FN_RUN "run"
+#define SECHK_MOD_FN_PRINT "print"
+
+/** Registered function container: used to allow the library and modules
+ * to request functions of a specific name. */
+ typedef struct sechk_fn
+ {
+ /** Name of the function without any module prefix */
+ char *name;
+ /** The function. */
+ sechk_mod_fn_t fn;
+ } sechk_fn_t;
+
+/** Module name registration structure: used when the library tries to
+ * discover all known modules. */
+ typedef struct sechk_module_name_reg
+ {
+ /** The name of the module. */
+ char *name;
+ /** The register function for this module. */
+ sechk_register_fn_t fn;
+ } sechk_module_name_reg_t;
+
+/** Profile name registration structure; used when the library tries to
+ * discover all known installed profiles. */
+ typedef struct sechk_profile_name_reg
+ {
+ /** Name of the profile. */
+ char *name;
+ /** Path of the profile file. */
+ char *file;
+ /** Description of the modules run by the profile. */
+ char *desc;
+ } sechk_profile_name_reg_t;
+
+/* alloc methods */
+
+/**
+ * Create a new module library object.
+ *
+ * @return A newly allocated module library or NULL on ENOMEM.
+ * The caller is responsible for calling sechk_lib_destroy()
+ * to free memory used by the library returned.
+ */
+ sechk_lib_t *sechk_lib_new(void);
+
+/**
+ * Create a new module structure.
+ *
+ * @return A newly allocated module or NULL on ENOMEM.
+ * The caller is resbonsible for calling sechk_module_free()
+ * to free memory used by the module returned.
+ */
+ sechk_module_t *sechk_module_new(void);
+
+/**
+ * Create a new module function structre.
+ *
+ * @return A newly allocated module function structure or NULL on ENOMEM.
+ * The caller is responsible for calling sechk_fn_free() to free memory
+ * used by the function structure returned.
+ */
+ sechk_fn_t *sechk_fn_new(void);
+
+/**
+ * Create and initialize a new name value pair.
+ * The incoming strings are duplicated.
+ *
+ * @param name Name to assign.
+ * @param value Value to assign to name.
+ *
+ * @return A newly allocated name value pair of NULL on error; if the
+ * call fails errno will be set.
+ */
+ sechk_name_value_t *sechk_name_value_new(const char *name, const char *value);
+
+/**
+ * Create a new results structure.
+ *
+ * @return A newly allocated results structure or NULL on ENOMEM.
+ * The caller is responsible for calling sechk_result_destroy() to free
+ * the memory used by the returned result structure.
+ */
+ sechk_result_t *sechk_result_new(void);
+
+/**
+ * Create a new result item.
+ *
+ * @param fn Function to be used to free the item stored.
+ *
+ * @return A newly allocated result item or NULL on ENOMEM.
+ * The caller is responsible for calling sechk_item_free() to free
+ * the memory used by the returned item.
+ */
+ sechk_item_t *sechk_item_new(free_fn_t fn);
+
+/**
+ * Create a new result item proof entry.
+ *
+ * @param fn Function to be used to free the element stored.
+ *
+ * @return A newly allocated proof structure or NULL on ENOMEM.
+ * The caller is responsible for calling sechk_proof_free() to free
+ * the memory used by teh returned proof.
+ */
+ sechk_proof_t *sechk_proof_new(free_fn_t fn);
+
+/* free methods */
+
+/**
+ * Free all memory used by a module library and set it to NULL.
+ *
+ * @param The library to destroy.
+ */
+ void sechk_lib_destroy(sechk_lib_t ** lib);
+
+/**
+ * Free all memory used by a module function structure.
+ *
+ * @param fn_struct The function structure to free.
+ */
+ void sechk_fn_free(void *fn_struct);
+
+/**
+ * Free all memory used by a result structure and set it to NULL.
+ *
+ * @param res The result structure to destroy.
+ */
+ void sechk_result_destroy(sechk_result_t ** res);
+
+/**
+ * Free all memory used by a result item.
+ *
+ * @param item The result item to free.
+ */
+ void sechk_item_free(void *item);
+
+/**
+ * Free all memory used by a result item proof element.
+ *
+ * @param proof The proof element to free.
+ */
+ void sechk_proof_free(void *proof);
+
+/**
+ * Free all memory used by a module.
+ *
+ * @param module The module to free.
+ */
+ void sechk_module_free(void *module);
+
+/**
+ * Free all memory used by a name value pair.
+ *
+ * @param nv The name value pair to free.
+ */
+ void sechk_name_value_free(void *nv);
+
+/* register/check_dep/init/run/print - modules */
+/**
+ * Register all known modules with the library.
+ *
+ * @param regiser_fns NULL terminated array of module registration structures.
+ * @param lib The library with which to register the modules in the array.
+ *
+ * @return 0 on success or < 0 on error; if the call fails, errno will be
+ * set and the library should be destroyed.
+ */
+ int sechk_lib_register_modules(const sechk_module_name_reg_t * register_fns, sechk_lib_t * lib);
+
+/**
+ * Check that the dependencies of all selected modules can be met.
+ * This function will select additional modules if needed by those
+ * already selected to be run.
+ *
+ * @param lib The library containing the modules to check.
+ *
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set.
+ */
+ int sechk_lib_check_module_dependencies(sechk_lib_t * lib);
+
+/**
+ * Check that the requirements of all selected modules are met. If the
+ * requirements are not met for a module and the library's default reporting
+ * mode is not SECHK_OUT_QUIET, the module will be deselected so that others
+ * might be checked. If the library is set to quiet, this function exits on
+ * the first module found to not meet its requirements. <b>This function
+ * should only be called after sechk_lib_check_module_dependencies()</b>
+ *
+ * @param lib The library containing the modules to check.
+ *
+ * @return 0 on success and < 0 on error; if the call fails,
+ * errno will be set.
+ */
+ int sechk_lib_check_module_requirements(sechk_lib_t * lib);
+
+/**
+ * Initialize all selected modules. <b>This function should only be called
+ * after both sechk_lib_check_module_dependencies() and
+ * sechk_lib_check_module_requirements() have been called.</b>
+ *
+ * @param lib The library containing the modules to initialize.
+ *
+ * @return 0 on success and < 0 on failure; if the call fails, errno will be
+ * set and the library should be destroyed.
+ */
+ int sechk_lib_init_modules(sechk_lib_t * lib);
+
+/**
+ * Run all selected modules. The modules must have been initialized.
+ *
+ * @param lib The library containing the modules to run.
+ *
+ * @return 0 on success or < 0 on error. Note that in quiet mode this
+ * function is considered to fail if a module finds results.
+ */
+ int sechk_lib_run_modules(sechk_lib_t * lib);
+
+/**
+ * Print a report of all selected modules' results to stdout.
+ * Modules must have been run.
+ *
+ * @param lib The library containing the modules with results to print.
+ *
+ * @return 0 on success and < 0 on error.
+ */
+ int sechk_lib_print_modules_report(sechk_lib_t * lib);
+
+/* module accessors */
+
+/**
+ * Find a module in the library.
+ *
+ * @param module_name The name of the module to find.
+ * @param lib The library to search.
+ *
+ * @return A pointer to the module or NULL if not found.
+ */
+ sechk_module_t *sechk_lib_get_module(const char *module_name, const sechk_lib_t * lib);
+
+/**
+ * Get a pointer to a function registered for a module.
+ *
+ * @param module_name Name of the module containing the function.
+ * @param function_name Name of the function with out any module prefix.
+ * @param lib The library containing the module.
+ *
+ * @return A pointer to the requested function, or NULL if either the module
+ * or the function cannot be found.
+ */
+ sechk_mod_fn_t sechk_lib_get_module_function(const char *module_name, const char *function_name, const sechk_lib_t * lib);
+
+/**
+ * Get the results of a module. If the module has not been run, it will be run
+ * and its results will be returned if it succeeds.
+ *
+ * @param module_name Name of the module containing the results.
+ * @param lib The library containing the module.
+ *
+ * @return The requested module's results or NULL on error. If the module
+ * was not previously run and it fails when run by this function, NULL
+ * will be returned. If the call fails, errno will be set.
+ */
+ sechk_result_t *sechk_lib_get_module_result(const char *module_name, const sechk_lib_t * lib);
+
+/* library utility functions */
+
+/**
+ * Load the policy the library will analyze.
+ *
+ * @param policy_mods Policy path object to use to load the policy.
+ * @param lib The library into which to load the policy.
+ *
+ * @return 0 on success and < 0 on failure.
+ */
+ int sechk_lib_load_policy(apol_policy_path_t * policy_mods, sechk_lib_t * lib);
+
+/**
+ * Load the file contexts file the library will use during analysis.
+ *
+ * @param fcfilelocation Path of the file contexts file to load, or
+ * NULL to search for system default file contexts.
+ * @param lib The library into which to load the file contexts.
+ *
+ * @return 0 on success and < 0 on failure.
+ */
+ int sechk_lib_load_fc(const char *fcfilelocation, sechk_lib_t * lib);
+
+/**
+ * Load a profile containing module options.
+ *
+ * @param prof_name Name of a known installed profile or the absolute path
+ * to a user created profile.
+ * @param lib The library containing the modules specified in the profile.
+ *
+ * @return 0 on success and < 0 on failure; if the call fails errno will be
+ * set and the library should be destroyed.
+ */
+ int sechk_lib_load_profile(const char *prof_name, sechk_lib_t * lib);
+
+/**
+ * Clear an option of all previous values.
+ *
+ * @param module Module containing the option to clear.
+ * @param option Name of the option to clear.
+ *
+ * @return 0 on success or < 0 on failure; if the call fails,
+ * errno will be set, and the module should be freed.
+ */
+ int sechk_lib_module_clear_option(sechk_module_t * module, char *option);
+
+/**
+ * Check that the library can meet a single requirement.
+ *
+ * @param req The requirement to check.
+ * @param lib The library to query.
+ *
+ * @return 1 if the requirement is met, and 0 if it is either unmet or
+ * if the library is unable to determine.
+ */
+ bool sechk_lib_check_requirement(sechk_name_value_t * req, sechk_lib_t * lib);
+
+/**
+ * Check that the library can meet a single module dependency.
+ *
+ * @param dep The dependency to check.
+ * @param lib The library to query for the existence of the dependency.
+ *
+ * @return 1 if the dependency exists, and 0 if it either does not or
+ * if the library is unable to determine.
+ */
+ bool sechk_lib_check_dependency(sechk_name_value_t * dep, sechk_lib_t * lib);
+
+/**
+ * Set the default output format for the library.
+ *
+ * @param out The format to use as a bit-wise or of SECHK_OUT_*.
+ * @param lib The library for which to set the output format.
+ *
+ * @return 0 on success and < 0 on failure; if the call fails,
+ * errno will be set.
+ */
+ int sechk_lib_set_outputformat(unsigned char out, sechk_lib_t * lib);
+
+/**
+ * Set the minimum severity level of the library.
+ *
+ * @param sev Severity level to set as the minimum level for reporting.
+ * Must be one of SECHK_SEV_*.
+ * @param lib The library for which to set the minimum severity level.
+ *
+ * @return 0 on success and < 0 on failure; if the call fials,
+ * errno will be set.
+ */
+ int sechk_lib_set_minsev(const char *sev, sechk_lib_t * lib);
+
+/**
+ * Get the index of a module in the library by name.
+ *
+ * @param name Name of the module for which to get the index.
+ * @param lib The library containing the desired module.
+ *
+ * @return index of the module or -1 if it was not found.
+ * If not found, errno will be set.
+ */
+ int sechk_lib_get_module_idx(const char *name, sechk_lib_t * lib);
+
+/* other utility functions */
+
+/**
+ * Copy a proof element. Note: the element in the proof is a shallow copy.
+ *
+ * @param orig The original proof to copy.
+ *
+ * @return a copy of the proof or NULL on error. If the call fails,
+ * errno will be set.
+ */
+ sechk_proof_t *sechk_proof_copy(sechk_proof_t * orig);
+
+/**
+ * Callback for vector comparison of proof elements.
+ * This callback takes two different type objects both cast to void
+ * it is important that the order of the parameters is correct or the
+ * vector code will fail when using this callback.
+ *
+ * @param in_proof One member of the vector of proofs.
+ * @param elem A policy item to compare to the proof's element.
+ * @param unused Unused. Needed to satisfy vector prototype.
+ *
+ * @return Pointer comparison value of < 0, 0 or > 0 if the element in the
+ * proof is respectively less than, equal to, or greater than that of the
+ * comparison element supplied.
+ */
+ int sechk_proof_with_element_compare(const void *in_proof, const void *elem, void *unused);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECHECKER_H */
diff --git a/sechecker/sechecker_cli.c b/sechecker/sechecker_cli.c
new file mode 100644
index 0000000..9fbbd96
--- /dev/null
+++ b/sechecker/sechecker_cli.c
@@ -0,0 +1,361 @@
+/**
+ * @file
+ * Main function and command line parser for the sechecker program.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sechecker.h"
+#include "register_list.h"
+#include <apol/policy.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2005-2007 Tresys Technology, LLC"
+
+extern sechk_module_name_reg_t sechk_register_list[];
+
+enum opt_values
+{
+ OPT_FCFILE = 256, OPT_MIN_SEV
+};
+
+/* command line options struct */
+static struct option const longopts[] = {
+ {"list", no_argument, NULL, 'l'},
+ {"help", optional_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"short", no_argument, NULL, 's'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"profile", required_argument, NULL, 'p'},
+ {"fcfile", required_argument, NULL, OPT_FCFILE},
+ {"module", required_argument, NULL, 'm'},
+ {"min-sev", required_argument, NULL, OPT_MIN_SEV},
+ {NULL, 0, NULL, 0}
+};
+
+/* display usage help */
+void usage(const char *arg0, bool brief)
+{
+ printf("Usage: sechecker [OPTIONS] -p profile [POLICY ...]\n");
+ printf(" sechecker [OPTIONS] -m module [POLICY ...]\n");
+ printf(" sechecker [OPTIONS] -p profile -m module [POLICY ...]\n");
+ printf("\n");
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", arg0);
+ } else {
+ printf("Perform modular checks on a SELinux policy.\n");
+ printf("\n");
+ printf(" -p PROF, --profile=PROF name or path of profile to load\n");
+ printf(" if used without -m, run all modules in profile\n");
+ printf(" -m MODULE, --module=MODULE MODULE to run\n");
+ printf(" --fcfile=FILE file_contexts file to load\n");
+ printf("\n");
+ printf(" -q, --quiet suppress output\n");
+ printf(" -s, --short print short output\n");
+ printf(" -v, --verbose print verbose output\n");
+ printf(" --min-sev={low|med|high} set the minimum severity to report\n");
+ printf("\n");
+ printf(" -l, --list print a list of profiles and modules and exit\n");
+ printf(" -h[MODULE], --help[=MODULE] print this help text or help for MODULE\n");
+ printf(" -V, --version print version information and exit\n");
+ printf("\n");
+ }
+}
+
+/* print list of modules and installed profiles */
+int sechk_print_list(sechk_lib_t * lib)
+{
+ const sechk_profile_name_reg_t *profiles;
+ size_t num_profiles, i;
+ sechk_module_t *mod = NULL;
+
+ printf("\nProfiles:\n");
+ profiles = sechk_register_list_get_profiles();
+ num_profiles = sechk_register_list_get_num_profiles();
+ for (i = 0; i < num_profiles; i++) {
+ printf("%20s\t%s\n", profiles[i].name, profiles[i].desc);
+ }
+ if (num_profiles == 0)
+ printf("<none>\n");
+
+ printf("Modules:\n");
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ printf("%20s\t%s\n", mod->name, mod->brief_description);
+ }
+ if (apol_vector_get_size(lib->modules) == 0)
+ printf("<none>\n");
+ printf("\n");
+ return 0;
+}
+
+/* main application */
+int main(int argc, char **argv)
+{
+ int optc = 0, retv = 0;
+ size_t i;
+ char *fcpath = NULL;
+ char *modname = NULL;
+ char *prof_name = NULL;
+ char *base_path = NULL;
+ apol_policy_path_t *pol_path = NULL;
+ apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ char *minsev = NULL;
+ unsigned char output_override = 0;
+ sechk_lib_t *lib;
+ sechk_module_t *mod = NULL;
+ bool list_stop = false;
+ bool module_help = false;
+ apol_vector_t *policy_mods = NULL;
+
+ while ((optc = getopt_long(argc, argv, "p:m:qsvlh::V", longopts, NULL)) != -1) {
+ switch (optc) {
+ case 'p':
+ prof_name = strdup(optarg);
+ break;
+ case 'm':
+ if (minsev) {
+ fprintf(stderr, "Error: --min-sev does not work with --module.\n");
+ exit(1);
+ }
+ modname = strdup(optarg);
+ break;
+ case OPT_FCFILE:
+ fcpath = strdup(optarg);
+ break;
+ case 'q':
+ if (output_override) {
+ fprintf(stderr, "Error: multiple output formats specified.\n");
+ usage(argv[0], 1);
+ exit(1);
+ } else {
+ output_override = SECHK_OUT_QUIET;
+ }
+ break;
+ case 's':
+ if (output_override) {
+ fprintf(stderr, "Error: multiple output formats specified.\n");
+ usage(argv[0], 1);
+ exit(1);
+ } else {
+ output_override = SECHK_OUT_SHORT;
+ }
+ break;
+ case 'v':
+ if (output_override) {
+ fprintf(stderr, "Error: multiple output formats specified.\n");
+ usage(argv[0], 1);
+ exit(1);
+ } else {
+ output_override = SECHK_OUT_VERBOSE;
+ }
+ break;
+ case OPT_MIN_SEV:
+ if (modname) {
+ fprintf(stderr, "Error: --min-sev does not work with --module.\n");
+ exit(1);
+ }
+ minsev = strdup(optarg);
+ break;
+ case 'l':
+ list_stop = true;
+ break;
+ case 'h':
+ if (optarg != NULL) {
+ modname = strdup(optarg);
+ module_help = true;
+ break;
+ }
+ usage(argv[0], 0);
+ exit(0);
+ case 'V':
+ printf("sechecker %s\n%s\n", VERSION, COPYRIGHT_INFO);
+ exit(0);
+ default:
+ usage(argv[0], 1);
+ exit(1);
+ }
+ }
+
+ if (!prof_name && !modname && !list_stop) {
+ fprintf(stderr, "Error: no module or profile specified\n\n");
+ usage(argv[0], 1);
+ exit(1);
+ }
+
+ /* create the module library */
+ lib = sechk_lib_new();
+ if (!lib)
+ goto exit_err;
+
+ /* print the list */
+ if (list_stop == true) {
+ sechk_print_list(lib);
+ goto exit;
+ }
+
+ /* print help for a module */
+ if (module_help == true) {
+ retv = sechk_lib_get_module_idx(modname, lib);
+ if (retv < 0) {
+ fprintf(stderr, "Error: Module %s does not exist.\n", modname);
+ goto exit_err;
+ }
+ mod = apol_vector_get_element(lib->modules, retv);
+ printf("\nModule name: %s\n%s\n%s\n", mod->name, mod->detailed_description, mod->opt_description);
+ goto exit;
+ }
+
+ /* load profile if specified */
+ if (prof_name) {
+ retv = sechk_lib_load_profile(prof_name, lib);
+ if (retv) {
+ retv = errno;
+ if (!output_override || !(output_override & ~(SECHK_OUT_QUIET))) {
+ fprintf(stderr, "Error: could not load profile %s\n", prof_name);
+ errno = retv;
+ perror("Error");
+ }
+ goto exit_err;
+ }
+ }
+
+ /* initialize the policy */
+ if (argc - optind) {
+ base_path = argv[optind];
+ optind++;
+ if (argc - optind) {
+ if (!(policy_mods = apol_vector_create(NULL)))
+ goto exit_err;
+ while (argc - optind) {
+ if (apol_vector_append(policy_mods, argv[optind++]))
+ goto exit_err;
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ }
+ } else if (apol_file_is_policy_path_list(base_path) > 0) {
+ pol_path = apol_policy_path_create_from_file(base_path);
+ if (!pol_path) {
+ fprintf(stderr, "Error: invalid policy list\n");
+ goto exit_err;
+ }
+ }
+ if (!pol_path)
+ pol_path = apol_policy_path_create(path_type, base_path, policy_mods);
+ if (!pol_path)
+ goto exit_err;
+ if (sechk_lib_load_policy(pol_path, lib)) {
+ pol_path = NULL;
+ goto exit_err;
+ }
+ } else {
+ if (sechk_lib_load_policy(NULL, lib))
+ goto exit_err;
+ }
+ /* library now owns path object */
+ pol_path = NULL;
+
+ /* set the minimum severity */
+ if (minsev && sechk_lib_set_minsev(minsev, lib) < 0)
+ goto exit_err;
+
+ /* initialize the file contexts */
+ if (sechk_lib_load_fc(fcpath, lib) < 0)
+ goto exit_err;
+ /* initialize the output format */
+ if (output_override) {
+ if (sechk_lib_set_outputformat(output_override, lib) < 0)
+ goto exit_err;
+ }
+
+ /* if running only one module, deselect all others */
+ if (modname) {
+ retv = sechk_lib_get_module_idx(modname, lib);
+ if (retv == -1 || (size_t) retv >= apol_vector_get_size(lib->modules)) {
+ fprintf(stderr, "Error: module %s not found\n", modname);
+ goto exit_err;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ mod->selected = false;
+ }
+ mod = apol_vector_get_element(lib->modules, retv);
+ mod->selected = true;
+ }
+
+ /* process dependencies for selected modules */
+ if (sechk_lib_check_module_dependencies(lib) < 0)
+ goto exit_err;
+
+ /* process requirements for selected modules */
+ if (sechk_lib_check_module_requirements(lib) < 0)
+ goto exit_err;
+
+ /* initialize the modules */
+ if (sechk_lib_init_modules(lib))
+ goto exit_err;
+
+ /* run the modules */
+ if (sechk_lib_run_modules(lib))
+ goto exit_err;
+
+ /* if running only one module, deselect all others again before printing */
+ if (modname) {
+ retv = sechk_lib_get_module_idx(modname, lib);
+ if (retv == -1 || (size_t) retv >= apol_vector_get_size(lib->modules)) {
+ fprintf(stderr, "Error: module %s not found\n", modname);
+ goto exit_err;
+ }
+ for (i = 0; i < apol_vector_get_size(lib->modules); i++) {
+ mod = apol_vector_get_element(lib->modules, i);
+ mod->selected = false;
+ }
+ mod = apol_vector_get_element(lib->modules, retv);
+ mod->selected = true;
+ }
+
+ /* print the report */
+ if (sechk_lib_print_modules_report(lib))
+ goto exit_err;
+
+ exit:
+ free(fcpath);
+ apol_vector_destroy(&policy_mods);
+ free(minsev);
+ free(prof_name);
+ free(modname);
+ sechk_lib_destroy(&lib);
+ return 0;
+
+ exit_err:
+ free(fcpath);
+ apol_vector_destroy(&policy_mods);
+ free(minsev);
+ free(prof_name);
+ free(modname);
+ apol_policy_path_destroy(&pol_path);
+ sechk_lib_destroy(&lib);
+ return 1;
+}
diff --git a/sechecker/sechecker_help.txt b/sechecker/sechecker_help.txt
new file mode 100644
index 0000000..a28496a
--- /dev/null
+++ b/sechecker/sechecker_help.txt
@@ -0,0 +1,86 @@
+SELinux Policy Checker Tool Help File
+
+
+This file contains the basic help information for using sechecker, a
+program that runs a series of policy checks (modules) on a policy.
+Sechecker is designed to be extensible and configurable so that
+developers can easily add new policy checks and configure them to run
+in batches with different options.
+
+Each module analyzes a policy. If a policy is not specified on the
+command line, the tool uses the system policy by default. In
+addition, some checks will require the file_contexts file in order to
+run correctly. If the file_contexts file is not specified, the tool
+will default to using the system file_contexts file by default.
+
+Checks can be run one at a time on the command line (by specifying a
+module) or in a batch (by specifying a profile). The user can create
+a custom profile to specify which modules to run, as well as the
+modules' options.
+
+The return value of sechecker indicates whether a check failed on the
+policy. Therefore sechecker may be used in shell scripts or makefiles
+to do conditional branching.
+
+
+Report Output:
+--------------
+Sechecker generates a report with the output of each module that was
+run. The report includes an explanation of each module, the modules'
+severity, and the modules' results. There are three output options to
+specify what gets included in the report.
+
+1) quiet - do not print the report
+2) short - print the list of results for each module
+3) verbose - print the list of results for each module and the list of
+ proofs for each result
+
+
+Modules:
+--------
+A module encapsulates a single check on the policy. Modules may take
+options from the current profile; if no profile is given then modules
+will use default values. See the help for the specific module(s) to
+determine the parameters that may be overridden in a profile.
+
+Each module has a specified severity (high, med, low). These are
+defined as follows:
+
+1) "high": The module's results indicate an identifiable security
+ risk in the SELinux policy.
+
+2) "med": The module's results indicate a flaw in the SELinux policy
+ that changes the manner in which the policy is enforced; however,
+ it does not present an identifiable security risk.
+
+3) "low": The module's results indicate a flaw in the policy that
+ does not affect the manner in which the policy is enforced, but is
+ considered to be improper.
+
+
+Profiles:
+---------
+Three profiles are installed with the sechecker program:
+
+1) development: This profile includes several policy checks of low
+ and med severity. The checks are common tasks that a policy
+ developer will consider helpful for writing good policy.
+
+2) analysis: This profile includes several policy checks of med and
+ low severity that are of higher computational complexity than the
+ development profile and is not meant to be used frequently by
+ policy developers.
+
+3) all: This profile runs all known modules.
+
+Profiles can be created to run any set of modules with different
+options. The profile can specify the output format for each module.
+
+
+Other Options:
+--------------
+The user may specify a minimum module severity to report. For
+example, if the minimum severity is "med" and the "all" profile is
+used, all modules that are "med" or "high" will be run and the results
+for those modules will be reported by sechecker. The "low" severity
+modules listed in the profile will be ignored.
diff --git a/sechecker/sechk_parse.c b/sechecker/sechk_parse.c
new file mode 100644
index 0000000..ae73af2
--- /dev/null
+++ b/sechecker/sechk_parse.c
@@ -0,0 +1,285 @@
+/* Copyright (C) 2005 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Author: jmowery@tresys.com
+ *
+ */
+
+#include "sechecker.h"
+#include "sechk_parse.h"
+#include <apol/util.h>
+#include <libxml/xmlreader.h>
+#include <errno.h>
+#include <string.h>
+
+/* xml parser keywords */
+#define SECHK_PARSE_SECHECKER_TAG "sechecker"
+#define SECHK_PARSE_PROFILE_TAG "profile"
+#define SECHK_PARSE_MODULE_TAG "module"
+#define SECHK_PARSE_OPTION_TAG "option"
+#define SECHK_PARSE_ITEM_TAG "item"
+#define SECHK_PARSE_OUTPUT_TAG "output"
+#define SECHK_PARSE_VALUE_ATTRIB "value"
+#define SECHK_PARSE_NAME_ATTRIB "name"
+#define SECHK_PARSE_VERSION_ATTRIB "version"
+#define SECHK_PARSE_OUTPUT_NONE "none"
+#define SECHK_PARSE_OUTPUT_QUIET "quiet"
+#define SECHK_PARSE_OUTPUT_SHORT "short"
+#define SECHK_PARSE_OUTPUT_VERBOSE "verbose"
+
+static char *build_dtd_path(void)
+{
+ char *path = NULL;
+ size_t path_sz = 0;
+
+#ifdef PROFILE_INSTALL_DIR
+ if (apol_str_append(&path, &path_sz, "file://localhost/") == -1)
+ return NULL;
+
+ if (apol_str_append(&path, &path_sz, PROFILE_INSTALL_DIR) == -1)
+ return NULL;
+
+ if (apol_str_append(&path, &path_sz, "/sechecker.dtd") == -1)
+ return NULL;
+
+ return path;
+#endif
+
+ return NULL;
+}
+
+ /* Parsing functions */
+
+/* Parse the configuration file. */
+int sechk_lib_parse_xml_file(const char *filename, sechk_lib_t * lib)
+{
+ xmlTextReaderPtr reader = NULL;
+ xmlDtdPtr dtd = NULL;
+ xmlDocPtr xml = NULL;
+ xmlValidCtxtPtr ctxt = NULL;
+ int tmp, ret = 0;
+ char *dtd_path = NULL;
+
+ /* this initializes the XML library and checks potential ABI mismatches
+ * between the version it was compiled for and the actual shared
+ * library used. */
+ LIBXML_TEST_VERSION;
+ reader = xmlReaderForFile(filename, NULL, 0);
+ if (!reader) {
+ ret = errno;
+ if (ret != ENOENT)
+ fprintf(stderr, "Error: Could not create xmlReader.\n");
+ goto exit_err;
+ }
+
+ dtd_path = build_dtd_path();
+ if (!dtd_path) {
+ fprintf(stderr, "Error: getting DTD path\n");
+ goto exit_err;
+ }
+ dtd = xmlParseDTD(NULL, (const xmlChar *)dtd_path);
+ free(dtd_path);
+
+ if (!dtd) {
+ fprintf(stderr, "Error: parsing DTD\n");
+ goto exit_err;
+ }
+
+ xml = xmlParseFile(filename);
+ if (!xml) {
+ fprintf(stderr, "Error: parsing sechecker profile\n");
+ goto exit_err;
+ }
+
+ ctxt = xmlNewValidCtxt();
+ if (!ctxt) {
+ fprintf(stderr, "Error: out of memory\n");
+ goto exit_err;
+ }
+ /* validate profile against the DTD */
+ if (xmlValidateDtd(ctxt, xml, dtd) == 0) {
+ fprintf(stderr, "Error: SEChecker profile contains invalid XML. Aborting.\n");
+ goto exit_err;
+ }
+ xmlFreeValidCtxt(ctxt);
+ xmlFreeDoc(xml);
+ xmlFreeDtd(dtd);
+
+ while (1) {
+ ret = xmlTextReaderRead(reader);
+ if (ret == -1) {
+ ret = errno;
+ fprintf(stderr, "Error: Error reading xml.\n");
+ goto exit_err;
+ }
+ if (ret == 0) /* no more nodes to read */
+ break;
+
+ tmp = sechk_lib_process_xml_node(reader, lib);
+ if (tmp == -1)
+ goto exit_err;
+ if (tmp == 1) {
+ ret = xmlTextReaderNext(reader);
+ if (ret == 0)
+ break;
+ if (ret == -1) {
+ ret = errno;
+ fprintf(stderr, "Error in xmlTextReaderNext()\n");
+ goto exit_err;
+ }
+ }
+ }
+
+ /* cleanup function for the XML library */
+ xmlCleanupParser();
+ xmlFreeTextReader(reader);
+ return 0;
+
+ exit_err:
+ xmlCleanupParser();
+ if (reader)
+ xmlFreeTextReader(reader);
+ if (ret)
+ errno = ret;
+ return -1;
+}
+
+/* process a single node in the xml file */
+int sechk_lib_process_xml_node(xmlTextReaderPtr reader, sechk_lib_t * lib)
+{
+ xmlChar *attrib = NULL;
+ int idx;
+ sechk_name_value_t *nv = NULL;
+ static xmlChar *option = NULL;
+ static xmlChar *value = NULL;
+ static sechk_module_t *current_module = NULL;
+
+ switch (xmlTextReaderNodeType(reader)) {
+
+ case XML_ELEMENT_DECL: /* closing tags */
+ if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_MODULE_TAG) == 1) {
+ current_module = NULL;
+ } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_OPTION_TAG) == 1) {
+ free(option);
+ option = NULL;
+ }
+ break;
+
+ case XML_ELEMENT_NODE: /* opening tags */
+
+ if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_SECHECKER_TAG) == 1) {
+ /* parsing the <sechecker> tag */
+ attrib = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_VERSION_ATTRIB);
+ if (attrib) {
+#ifdef SECHECKER_VERSION
+ if (atof((const char *)attrib) > atof(SECHECKER_VERSION)) {
+ fprintf(stderr,
+ "Error: sechecker version in profile is incorrect: expected %1.1f got %1.1f\n",
+ atof(SECHECKER_VERSION), atof((const char *)attrib));
+ goto exit_err;
+ }
+#endif
+
+ free(attrib);
+ attrib = NULL;
+ } else {
+ fprintf(stderr, "Warning: sechecker version is not specified.\n");
+ }
+ } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_MODULE_TAG) == 1) {
+ /* parsing the <module> tag */
+ attrib = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_NAME_ATTRIB);
+ if (attrib) {
+ if ((idx = sechk_lib_get_module_idx((const char *)attrib, lib)) == -1) {
+ fprintf(stderr, "Warning: module %s not found.\n", (const char *)attrib);
+ return 1; /* not a fatal error */
+ } else {
+ /* set the values on the existing module */
+ current_module = apol_vector_get_element(lib->modules, idx);
+ current_module->selected = true;
+ }
+ free(attrib);
+ attrib = NULL;
+ } else {
+ fprintf(stderr, "Error: module name is not specified.\n");
+ goto exit_err;
+ }
+ } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_OPTION_TAG) == 1) {
+ /* parsing the <option> tag */
+ if (!current_module) {
+ fprintf(stderr, "Error: 'option' specified outside the scope of a module.\n");
+ goto exit_err;
+ }
+ /* read the name of the option */
+ option = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_NAME_ATTRIB);
+ if (!option) {
+ fprintf(stderr, "Error: option name is not specified.\n");
+ goto exit_err;
+ }
+ /* clear the options with this name that were set by defualt for this module */
+ sechk_lib_module_clear_option(current_module, (char *)option);
+
+ } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_ITEM_TAG) == 1) {
+ /* parsing the <item> tag */
+ assert(current_module);
+ nv = sechk_name_value_new((char *)option, NULL);
+ /* read the value for this name value pair */
+ value = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_VALUE_ATTRIB);
+ if (!value) {
+ fprintf(stderr, "Error: item value is not specified.\n");
+ goto exit_err;
+ }
+ nv->value = strdup((char *)value);
+ free(value);
+ value = NULL;
+ /* add the nv pair to the module options */
+ apol_vector_append(current_module->options, (void *)nv);
+ nv = NULL;
+ } else if (xmlStrEqual(xmlTextReaderConstName(reader), (xmlChar *) SECHK_PARSE_OUTPUT_TAG) == 1) {
+ if (!current_module) {
+ fprintf(stderr, "Error: 'output' specified outside the scope of a module.\n");
+ goto exit_err;
+ }
+ attrib = xmlTextReaderGetAttribute(reader, (xmlChar *) SECHK_PARSE_VALUE_ATTRIB);
+ if (attrib) {
+ if (xmlStrEqual(attrib, (xmlChar *) SECHK_PARSE_OUTPUT_QUIET) == 1) {
+ current_module->outputformat = SECHK_OUT_QUIET;
+ } else if (xmlStrEqual(attrib, (xmlChar *) SECHK_PARSE_OUTPUT_VERBOSE) == 1) {
+ current_module->outputformat = SECHK_OUT_VERBOSE;
+ } else if (xmlStrEqual(attrib, (xmlChar *) SECHK_PARSE_OUTPUT_SHORT) == 1) {
+ current_module->outputformat = SECHK_OUT_SHORT;
+ } else if (xmlStrEqual(attrib, (xmlChar *) SECHK_PARSE_OUTPUT_NONE) == 1) {
+ current_module->outputformat = SECHK_OUT_NONE;
+ } else {
+ fprintf(stderr, "Error: invalid output value %s.\n", attrib);
+ goto exit_err;
+ }
+ free(attrib);
+ attrib = NULL;
+ } else {
+ fprintf(stderr, "Error: output value is not specified.\n");
+ goto exit_err;
+ }
+ }
+ break;
+ }
+ return 0;
+
+ exit_err:
+ sechk_name_value_free(nv);
+ errno = EIO;
+ return -1;
+}
diff --git a/sechecker/sechk_parse.h b/sechecker/sechk_parse.h
new file mode 100644
index 0000000..1555e56
--- /dev/null
+++ b/sechecker/sechk_parse.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2005-2007 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Author: jmowery@tresys.com
+ *
+ */
+
+#ifndef SECHK_PARSE_H
+#define SECHK_PARSE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "sechecker.h"
+
+#include <libxml/xmlreader.h>
+#include <assert.h>
+
+#define sechk_lib_parse_profile(path, lib) sechk_lib_parse_xml_file(path, lib)
+
+ int sechk_lib_parse_xml_file(const char *filename, sechk_lib_t * lib);
+ int sechk_lib_process_xml_node(xmlTextReaderPtr reader, sechk_lib_t * lib);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/secmds/Makefile.am b/secmds/Makefile.am
new file mode 100644
index 0000000..ddc88b1
--- /dev/null
+++ b/secmds/Makefile.am
@@ -0,0 +1,40 @@
+# various setools command line tools
+
+bin_PROGRAMS = seinfo sesearch findcon replcon indexcon
+
+# These are for indexcon so that it is usable on machines without setools
+STATICLIBS = ../libsefs/src/libsefs.a ../libapol/src/libapol.a ../libqpol/src/libqpol.a -lsqlite3
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@
+AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEFS_CFLAGS@
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+DEPENDENCIES = $(top_builddir)/libapol/src/libapol.so $(top_builddir)/libqpol/src/libqpol.so
+
+seinfo_SOURCES = seinfo.c
+
+sesearch_SOURCES = sesearch.c
+
+indexcon_SOURCES = indexcon.cc
+indexcon_LDADD = @SELINUX_LIB_FLAG@ $(STATICLIBS)
+indexcon_DEPENDENCIES = $(DEPENDENCIES) $(top_builddir)/libsefs/src/libsefs.so
+
+findcon_SOURCES = findcon.cc
+findcon_LDADD = @SEFS_LIB_FLAG@ $(LDADD)
+findcon_DEPENDENCIES = $(DEPENDENCIES) $(top_builddir)/libsefs/src/libsefs.so
+
+replcon_SOURCES = replcon.cc
+replcon_LDADD = @SEFS_LIB_FLAG@ $(LDADD)
+replcon_DEPENDENCIES = $(DEPENDENCIES) $(top_builddir)/libsefs/src/libsefs.so
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
+
+$(top_builddir)/libsefs/src/libsefs.so:
+ $(MAKE) -C $(top_builddir)/libsefs/src $(notdir $@)
diff --git a/secmds/findcon.cc b/secmds/findcon.cc
new file mode 100644
index 0000000..325c2f0
--- /dev/null
+++ b/secmds/findcon.cc
@@ -0,0 +1,245 @@
+/**
+ * @file
+ *
+ * Search a fclist (either the disk, a database generated by indexcon
+ * or apol, or a file_contexts file for entries that match a given
+ * SELinux file context.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <sefs/db.hh>
+#include <sefs/fcfile.hh>
+#include <sefs/filesystem.hh>
+#include <sefs/entry.hh>
+#include <sefs/query.hh>
+
+using namespace std;
+
+#include <errno.h>
+#include <getopt.h>
+#include <iostream>
+#include <string>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC"
+
+enum OPTIONS
+{
+ OPTION_CONTEXT = 256
+};
+
+static struct option const longopts[] = {
+ {"class", required_argument, NULL, 'c'},
+ {"type", required_argument, NULL, 't'},
+ {"user", required_argument, NULL, 'u'},
+ {"role", required_argument, NULL, 'r'},
+ {"mls-range", required_argument, NULL, 'm'},
+ {"path", required_argument, NULL, 'p'},
+ {"regex", no_argument, NULL, 'R'},
+ {"context", required_argument, NULL, OPTION_CONTEXT},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+static void usage(const char *program_name, bool brief)
+{
+ cout << "Usage: " << program_name << " FCLIST [OPTIONS] [EXPRESSION]" << endl << endl;
+ if (brief)
+ {
+ cout << "\tTry " << program_name << " --help for more help." << endl << endl;
+ return;
+ }
+
+ cout << "Find files matching the given SELinux context." << endl << endl;
+
+ cout << "FCLIST is a directory name, a file_contexts file, or a database" << endl;
+ cout << "created by a previous run of indexcon." << endl;
+ cout << endl;
+
+ cout << "EXPRESSION:" << endl;
+ cout << " -t TYPE, --type=TYPE find contexts with type TYPE" << endl;
+ cout << " -u USER, --user=USER find contexts with user USER" << endl;
+ cout << " -r ROLE, --role=ROLE find contexts with role ROLE" << endl;
+ cout << " -m RANGE, --mls-range=RANGE find contexts with MLS range RANGE" << endl;
+ cout << " --context=CONTEXT partial or full context to find" << endl;
+ cout << " (overrides expression options above)" << endl;
+ cout << " -p PATH, --path=PATH find files in PATH" << endl;
+ cout << " -c CLASS, --class=CLASS find files of object class CLASS" << endl;
+ cout << endl;
+
+ cout << "OPTIONS:" << endl;
+ cout << " -R, --regex enable regular expressions" << endl;
+ cout << " -h, --help print this help text and exit" << endl;
+ cout << " -V, --version print version information and exit" << endl;
+ cout << endl;
+ cout << "If the fclist does not contain MLS ranges and -m was given," << endl;
+ cout << "then the search will return nothing." << endl;
+}
+
+static int print_entry(sefs_fclist * fclist, const sefs_entry * e, void *arg __attribute__ ((unused)))
+{
+ char *str = e->toString();
+ cout << str << endl;
+ free(str);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int optc;
+ sefs_query *query = new sefs_query();
+
+ apol_context_t *context = NULL;
+ try
+ {
+ while ((optc = getopt_long(argc, argv, "t:u:r:m:p:c:RhV", longopts, NULL)) != -1)
+ {
+ switch (optc)
+ {
+ case 't':
+ if (context == NULL)
+ {
+ query->type(optarg, false);
+ }
+ break;
+ case 'u':
+ if (context == NULL)
+ {
+ query->user(optarg);
+ }
+ break;
+ case 'r':
+ if (context == NULL)
+ {
+ query->role(optarg);
+ }
+ break;
+ case 'm':
+ if (context == NULL)
+ {
+ query->range(optarg, APOL_QUERY_EXACT);
+ }
+ break;
+ case OPTION_CONTEXT:
+ if ((context = apol_context_create_from_literal(optarg)) == NULL)
+ {
+ cerr << "Could not create a context." << endl;
+ throw runtime_error(strerror(errno));
+ }
+ break;
+ case 'p':
+ query->path(optarg);
+ break;
+ case 'c':
+ query->objectClass(optarg);
+ break;
+ case 'R':
+ query->regex(true);
+ break;
+ case 'h': // help
+ usage(argv[0], false);
+ exit(0);
+ case 'V': // version
+ cout << "findcon " << VERSION << endl << COPYRIGHT_INFO << endl;
+ exit(0);
+ default:
+ usage(argv[0], true);
+ exit(1);
+ }
+ }
+ if (context != NULL)
+ {
+ query->user(apol_context_get_user(context));
+ query->role(apol_context_get_role(context));
+ query->type(apol_context_get_type(context), false);
+ if (apol_context_get_range(context) != NULL)
+ {
+ char *r = apol_mls_range_render(NULL, apol_context_get_range(context));
+ query->range(r, APOL_QUERY_EXACT);
+ free(r);
+ }
+ else
+ {
+ query->range(NULL, APOL_QUERY_EXACT);
+ }
+ }
+ }
+ catch(...)
+ {
+ cerr << strerror(errno) << endl;
+ apol_context_destroy(&context);
+ delete query;
+ exit(-1);
+ }
+ apol_context_destroy(&context);
+
+ if (optind + 1 != argc)
+ {
+ usage(argv[0], 1);
+ delete query;
+ exit(-1);
+ }
+
+ // try to autodetect the type of thing being searched
+ struct stat sb;
+ if (stat(argv[optind], &sb) != 0)
+ {
+ cerr << "Could not open " << argv[optind] << ": " << strerror(errno) << endl;
+ delete query;
+ exit(-1);
+ }
+
+ sefs_fclist *fclist = NULL;
+ try
+ {
+ if (S_ISDIR(sb.st_mode))
+ {
+ fclist = new sefs_filesystem(argv[optind], NULL, NULL);
+ }
+ else if (sefs_db::isDB(argv[optind]))
+ {
+ fclist = new sefs_db(argv[optind], NULL, NULL);
+ }
+ else
+ {
+ fclist = new sefs_fcfile(argv[optind], NULL, NULL);
+ }
+
+ if (fclist->runQueryMap(query, print_entry, NULL) < 0)
+ {
+ throw runtime_error(strerror(errno));
+ }
+ }
+ catch(...)
+ {
+ delete query;
+ delete fclist;
+ exit(-1);
+ }
+
+ delete query;
+ delete fclist;
+ return 0;
+}
diff --git a/secmds/indexcon.cc b/secmds/indexcon.cc
new file mode 100644
index 0000000..dbbb4af
--- /dev/null
+++ b/secmds/indexcon.cc
@@ -0,0 +1,121 @@
+/**
+ * @file
+ *
+ * Command-line program that builds a libsefs database of file
+ * contexts.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * indexcon: a tool for indexing the security contexts of filesystem entities
+ */
+
+#include <config.h>
+
+#include <sefs/db.hh>
+#include <sefs/filesystem.hh>
+
+using namespace std;
+
+#include <iostream>
+#include <getopt.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC"
+
+static struct option const longopts[] = {
+ {"directory", required_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+static void usage(const char *program_name, bool brief)
+{
+ cout << "Usage: " << program_name << " FILE [OPTIONS]" << endl << endl;
+ if (brief)
+ {
+ cout << "\tTry " << program_name << " --help for more help." << endl << endl;
+ return;
+ }
+ cout << "Index SELinux contexts on the filesystem." << endl;
+ cout << endl;
+ cout << " -d DIR, --directory=DIR start scanning at directory DIR (default \"/\")" << endl;
+ cout << " -h, --help print this help text and exit" << endl;
+ cout << " -V, --version print version information and exit" << endl;
+}
+
+int main(int argc, char *argv[])
+{
+ int optc;
+
+ char *outfilename = NULL, *dir = "/";
+
+ while ((optc = getopt_long(argc, argv, "d:hV", longopts, NULL)) != -1)
+ {
+ switch (optc)
+ {
+ case 'd': // starting directory
+ dir = optarg;
+ break;
+ case 'h':
+ usage(argv[0], false);
+ exit(0);
+ case 'V':
+ cout << "indexcon " << VERSION << endl << COPYRIGHT_INFO << endl;
+ exit(0);
+ default:
+ usage(argv[0], true);
+ exit(1);
+ }
+ }
+ if (argc - optind > 1 || argc - optind < 1)
+ {
+ usage(argv[0], true);
+ exit(1);
+ }
+ else
+ {
+ outfilename = argv[optind];
+ }
+
+ if (outfilename == NULL)
+ {
+ usage(argv[0], true);
+ exit(1);
+ }
+
+ sefs_filesystem *fs = NULL;
+ sefs_db *db = NULL;
+ try
+ {
+ fs = new sefs_filesystem(dir, NULL, NULL);
+ db = new sefs_db(fs, NULL, NULL);
+ db->save(outfilename);
+ }
+ catch(...)
+ {
+ delete fs;
+ delete db;
+ exit(2);
+ }
+
+ delete fs;
+ delete db;
+
+ return 0;
+}
diff --git a/secmds/replcon.cc b/secmds/replcon.cc
new file mode 100644
index 0000000..34f7c1a
--- /dev/null
+++ b/secmds/replcon.cc
@@ -0,0 +1,358 @@
+/**
+ * @file
+ *
+ * A tool for replacing file contexts in SELinux.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <sefs/filesystem.hh>
+#include <sefs/query.hh>
+#include <selinux/selinux.h>
+#include <apol/util.h>
+
+using namespace std;
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <iostream>
+#include <stdlib.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC"
+
+enum OPTIONS
+{
+ OPTION_CONTEXT = 256
+};
+
+static struct option const longopts[] = {
+ {"class", required_argument, NULL, 'c'},
+ {"type", required_argument, NULL, 't'},
+ {"user", required_argument, NULL, 'u'},
+ {"role", required_argument, NULL, 'r'},
+ {"mls-range", required_argument, NULL, 'm'},
+ {"path", required_argument, NULL, 'p'},
+ {"regex", no_argument, NULL, 'R'},
+ {"context", required_argument, NULL, OPTION_CONTEXT},
+ {"verbose", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+extern int lsetfilecon_raw(const char *, security_context_t) __attribute__ ((weak));
+
+/**
+ * As that setools must work with older libselinux versions that may
+ * not have the _raw() functions, declare them as weak. If libselinux
+ * does indeed have the new functions then use them; otherwise
+ * fallback to the originals.
+ */
+static int replcon_lsetfilecon(const char *path, security_context_t context)
+{
+ if (lsetfilecon_raw != NULL)
+ {
+ return lsetfilecon_raw(path, context);
+ }
+ else
+ {
+ return lsetfilecon(path, context);
+ }
+}
+
+struct replcon_info
+{
+ bool verbose, mls;
+ apol_context_t *replcon;
+};
+
+static void usage(const char *program_name, bool brief)
+{
+ cout << "Usage: " << program_name << " NEW_CONTEXT DIR [OPTIONS] [EXPRESSION]" << endl << endl;
+ if (brief)
+ {
+ cout << "\tTry " << program_name << " --help for more help." << endl << endl;
+ return;
+ }
+
+ cout << "Replace SELinux file contexts for files matching a given context." << endl << endl;
+
+ cout << "REQUIRED ARGUMENTS :" << endl;
+ cout << " NEW_CONTEXT partial or full context to relabel" << endl;
+ cout << " DIR starting directory to replace" << endl;
+ cout << endl;
+ cout << "EXPRESSION:" << endl;
+ cout << " -t TYPE, --type=TYPE find contexts with type TYPE" << endl;
+ cout << " -u USER, --user=USER find contexts with user USER" << endl;
+ cout << " -r ROLE, --role=ROLE find contexts with role ROLE" << endl;
+ cout << " -m RANGE, --mls-range=RANGE find contexts with MLS range RANGE" << endl;
+ cout << " --context=CONTEXT partial or full context to find" << endl;
+ cout << " (overrides expression options above)" << endl;
+ cout << " -p PATH, --path=PATH find files in PATH" << endl;
+ cout << " -c CLASS, --class=CLASS find files of object class CLASS" << endl;
+ cout << endl;
+
+ cout << "OPTIONS:" << endl;
+ cout << " -R, --regex enable regular expressions" << endl;
+ cout << " -v, --verbose show context of matching files" << endl;
+ cout << " -h, --help print this help text and exit" << endl;
+ cout << " -V, --version print version information and exit" << endl;
+ cout << endl;
+ cout << "If the fclist does not contain MLS ranges and -m was given," << endl;
+ cout << "then the search will return nothing." << endl;
+ cout << endl;
+ cout << "NEW_CONTEXT is as a colon separated list of user, role, type, and MLS range" << endl;
+ cout << "such as follows: user_u:object_r:user_t:s0. If a field is not specified," << endl;
+ cout << "that portion of the context will not be replaced." << endl;
+ cout << "Examples:" << endl;
+ cout << " replcon ::type_t: ." << endl;
+ cout << " Replace all files and subdirectories in current directory with" << endl;
+ cout << " type type_t, recursing within the directory." << endl;
+ cout << " replcon -u user_u *:role_r:* ." << endl;
+ cout << " Replace files that contain user_u with role role_r." << endl;
+ cout << " replcon --context ::type_t:so :::s0:c0 /tmp" << endl;
+ cout << " Replace files with type type_t and level s0 in /tmp with MLS" << endl;
+ cout << " range s0:c0." << endl;
+}
+
+static int replace_entry(sefs_fclist * fclist, const sefs_entry * e, void *arg)
+{
+ struct replcon_info *r = static_cast < struct replcon_info *>(arg);
+ const apol_context_t *scon = e->context();
+ const char *user, *role, *type;
+ char *con_str = NULL;
+ size_t len = 0;
+
+ // determine what the new context should be
+ if ((user = apol_context_get_user(r->replcon)) == NULL)
+ {
+ user = apol_context_get_user(scon);
+ }
+ if ((role = apol_context_get_role(r->replcon)) == NULL)
+ {
+ role = apol_context_get_role(scon);
+ }
+ if ((type = apol_context_get_type(r->replcon)) == NULL)
+ {
+ type = apol_context_get_type(scon);
+ }
+ if (apol_str_appendf(&con_str, &len, "%s:%s:%s", user, role, type) < 0)
+ {
+ return -1;
+ }
+ if (r->mls)
+ {
+ const apol_mls_range_t *apol_range = NULL;
+ char *range = NULL;
+ if ((apol_range = apol_context_get_range(r->replcon)) == NULL)
+ {
+ apol_range = apol_context_get_range(scon);
+ }
+ if ((range = apol_mls_range_render(NULL, apol_range)) == NULL || apol_str_appendf(&con_str, &len, ":%s", range) < 0)
+ {
+ free(range);
+ free(con_str);
+ return -1;
+ }
+ free(range);
+ }
+
+ if (r->verbose)
+ {
+ char *lcon = NULL, *rcon = NULL;
+ if (r->mls)
+ {
+ lcon = apol_context_render(NULL, r->replcon);
+ rcon = apol_context_render(NULL, scon);
+ }
+ else
+ {
+ if (asprintf(&lcon, "%s:%s:%s",
+ apol_context_get_user(r->replcon),
+ apol_context_get_role(r->replcon), apol_context_get_type(r->replcon)) < 0)
+ {
+ lcon = NULL;
+ }
+ if (asprintf(&rcon, "%s:%s:%s",
+ apol_context_get_user(scon), apol_context_get_role(scon), apol_context_get_type(scon)) < 0)
+ {
+ rcon = NULL;
+ }
+ }
+ if (lcon == NULL || rcon == NULL)
+ {
+ free(lcon);
+ free(rcon);
+ return -1;
+ }
+ printf("%s: %s --> %s\n", e->path(), lcon, rcon);
+ free(lcon);
+ free(rcon);
+ }
+
+ // until there is a way to create a security_context_t from a
+ // char *, simply perform the implicit cast below
+ if (replcon_lsetfilecon(e->path(), con_str) != 0)
+ {
+ cerr << "Could not set context " << con_str << " for file " << e->path() << "." << endl;
+ free(con_str);
+ return -1;
+ }
+
+ free(con_str);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int optc;
+ struct replcon_info r;
+
+ r.verbose = false;
+ r.replcon = NULL;
+ sefs_query *query = new sefs_query();
+
+ apol_context_t *context = NULL;
+ try
+ {
+ while ((optc = getopt_long(argc, argv, "t:u:r:m:p:c:RvhV", longopts, NULL)) != -1)
+ {
+ switch (optc)
+ {
+ case 't':
+ if (context == NULL)
+ {
+ query->type(optarg, false);
+ }
+ break;
+ case 'u':
+ if (context == NULL)
+ {
+ query->user(optarg);
+ }
+ break;
+ case 'r':
+ if (context == NULL)
+ {
+ query->role(optarg);
+ }
+ break;
+ case 'm':
+ if (context == NULL)
+ {
+ query->range(optarg, APOL_QUERY_EXACT);
+ }
+ break;
+ case OPTION_CONTEXT:
+ if ((context = apol_context_create_from_literal(optarg)) == NULL)
+ {
+ cerr << "Could not create source context." << endl;
+ throw runtime_error(strerror(errno));
+ }
+ break;
+ case 'p':
+ query->path(optarg);
+ break;
+ case 'c':
+ query->objectClass(optarg);
+ break;
+ case 'R':
+ query->regex(true);
+ break;
+ case 'v':
+ r.verbose = true;
+ break;
+ case 'h': // help
+ usage(argv[0], false);
+ exit(0);
+ case 'V': // version
+ cout << "replcon " << VERSION << endl << COPYRIGHT_INFO << endl;
+ exit(0);
+ default:
+ usage(argv[0], true);
+ exit(1);
+ }
+ if (context != NULL)
+ {
+ query->user(apol_context_get_user(context));
+ query->role(apol_context_get_role(context));
+ query->type(apol_context_get_type(context), false);
+ if (apol_context_get_range(context) != NULL)
+ {
+ char *rng = apol_mls_range_render(NULL, apol_context_get_range(context));
+ query->range(rng, APOL_QUERY_EXACT);
+ free(rng);
+ }
+ else
+ {
+ query->range(NULL, APOL_QUERY_EXACT);
+ }
+ }
+ }
+ }
+ catch(bad_alloc)
+ {
+ cerr << strerror(errno) << endl;
+ apol_context_destroy(&context);
+ delete query;
+ exit(-1);
+ }
+ apol_context_destroy(&context);
+
+ if (optind + 2 != argc)
+ {
+ usage(argv[0], 1);
+ delete query;
+ exit(-1);
+ }
+
+ sefs_fclist *fclist = NULL;
+ try
+ {
+ fclist = new sefs_filesystem(argv[optind + 1], NULL, NULL);
+ r.mls = fclist->isMLS();
+
+ if ((r.replcon = apol_context_create_from_literal(argv[optind])) == NULL)
+ {
+ cerr << "Could not create replacement context." << endl;
+ throw runtime_error(strerror(errno));
+ }
+
+ if (fclist->runQueryMap(query, replace_entry, &r) < 0)
+ {
+ throw runtime_error(strerror(errno));
+ }
+ }
+ catch(...)
+ {
+ delete query;
+ delete fclist;
+ apol_context_destroy(&(r.replcon));
+ exit(-1);
+ }
+
+ delete query;
+ delete fclist;
+ apol_context_destroy(&(r.replcon));
+ return 0;
+}
diff --git a/secmds/seinfo.c b/secmds/seinfo.c
new file mode 100644
index 0000000..fdf23e9
--- /dev/null
+++ b/secmds/seinfo.c
@@ -0,0 +1,2337 @@
+/**
+ * @file
+ *
+ * Command line tool for looking at a SELinux policy
+ * and getting various component elements and statistics.
+ *
+ * @author Frank Mayer mayerf@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author David Windsor dwindsor@tresys.com
+ * @author Steve Lawrence slawrence@tresys.com
+ *
+ * Copyright (C) 2003-2008 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+/* libapol */
+#include <apol/policy.h>
+#include <apol/policy-query.h>
+#include <apol/render.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+
+/* libqpol */
+#include <qpol/policy.h>
+#include <qpol/util.h>
+
+/* other */
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC"
+
+/* placeholder for empty set in constraint statements */
+#define CONSTRAIN_NULL_SET "<empty set>"
+
+static char *policy_file = NULL;
+
+static void print_type_attrs(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand);
+static void print_attr_types(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand);
+static void print_user_roles(FILE * fp, const qpol_user_t * user_datum, const apol_policy_t * policydb, const int expand);
+static void print_role_types(FILE * fp, const qpol_role_t * role_datum, const apol_policy_t * policydb, const int expand);
+static void print_bool_state(FILE * fp, const qpol_bool_t * bool_datum, const apol_policy_t * policydb, const int expand);
+static void print_class_perms(FILE * fp, const qpol_class_t * class_datum, const apol_policy_t * policydb, const int expand);
+static void print_cat_sens(FILE * fp, const qpol_cat_t * cat_datum, const apol_policy_t * policydb, const int expand);
+static int qpol_cat_datum_compare(const void *datum1, const void *datum2, void *data);
+static int qpol_level_datum_compare(const void *datum1, const void *datum2, void *data);
+
+enum opt_values
+{
+ OPT_SENSITIVITY = 256, OPT_CATEGORY,
+ OPT_INITIALSID, OPT_FS_USE, OPT_GENFSCON,
+ OPT_NETIFCON, OPT_NODECON, OPT_PORTCON, OPT_PROTOCOL,
+ OPT_PERMISSIVE, OPT_POLCAP,
+ OPT_ALL, OPT_STATS, OPT_CONSTRAIN
+};
+
+static struct option const longopts[] = {
+ {"class", optional_argument, NULL, 'c'},
+ {"sensitivity", optional_argument, NULL, OPT_SENSITIVITY},
+ {"category", optional_argument, NULL, OPT_CATEGORY},
+ {"type", optional_argument, NULL, 't'},
+ {"attribute", optional_argument, NULL, 'a'},
+ {"role", optional_argument, NULL, 'r'},
+ {"user", optional_argument, NULL, 'u'},
+ {"bool", optional_argument, NULL, 'b'},
+ {"constrain", no_argument, NULL, OPT_CONSTRAIN},
+ {"initialsid", optional_argument, NULL, OPT_INITIALSID},
+ {"fs_use", optional_argument, NULL, OPT_FS_USE},
+ {"genfscon", optional_argument, NULL, OPT_GENFSCON},
+ {"netifcon", optional_argument, NULL, OPT_NETIFCON},
+ {"nodecon", optional_argument, NULL, OPT_NODECON},
+ {"permissive", optional_argument, NULL, OPT_PERMISSIVE},
+ {"polcap", optional_argument, NULL, OPT_POLCAP},
+ {"portcon", optional_argument, NULL, OPT_PORTCON},
+ {"protocol", required_argument, NULL, OPT_PROTOCOL},
+ {"stats", no_argument, NULL, OPT_STATS},
+ {"all", no_argument, NULL, OPT_ALL},
+ {"line-breaks", no_argument, NULL, 'l'},
+ {"expand", no_argument, NULL, 'x'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+/**
+ * Prints a message specifying program options and usage.
+ *
+ * @param program_name Name of the program
+ * @param brief Flag indicating whether brief usage
+ * information should be displayed
+ */
+void usage(const char *program_name, int brief)
+{
+ printf("Usage: %s [OPTIONS] [EXPRESSION] [POLICY ...]\n\n", program_name);
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", program_name);
+ return;
+ }
+ printf("Print information about the components of a SELinux policy.\n\n");
+ printf("EXPRESSIONS:\n");
+ printf(" -c[NAME], --class[=NAME] print object classes\n");
+ printf(" --sensitivity[=NAME] print sensitivities\n");
+ printf(" --category[=NAME] print categories\n");
+ printf(" -t[NAME], --type[=NAME] print types (no aliases or attributes)\n");
+ printf(" -a[NAME], --attribute[=NAME] print type attributes\n");
+ printf(" -r[NAME], --role[=NAME] print roles\n");
+ printf(" -u[NAME], --user[=NAME] print users\n");
+ printf(" -b[NAME], --bool[=NAME] print conditional booleans\n");
+ printf(" --constrain print constrain statements\n");
+ printf(" --initialsid[=NAME] print initial SIDs\n");
+ printf(" --fs_use[=TYPE] print fs_use statements\n");
+ printf(" --genfscon[=TYPE] print genfscon statements\n");
+ printf(" --netifcon[=NAME] print netif contexts\n");
+ printf(" --nodecon[=ADDR] print node contexts\n");
+ printf(" --permissive print permissive types\n");
+ printf(" --polcap print policy capabilities\n");
+ printf(" --portcon[=PORT] print port contexts\n");
+ printf(" --protocol=PROTO specify a protocol for portcons\n");
+ printf(" --all print all of the above\n");
+ printf("OPTIONS:\n");
+ printf(" -x, --expand show more info for specified components\n");
+ printf(" --stats print useful policy statistics\n");
+ printf(" -l, --line-breaks print line breaks in constrain statements\n");
+ printf(" -h, --help print this help text and exit\n");
+ printf(" -V, --version print version information and exit\n");
+ printf("\n");
+ printf("For component options, if NAME is provided, then only show info for\n");
+ printf("NAME. Specifying a name is most useful when used with the -x option.\n");
+ printf("If no option is provided, display useful policy statistics (-s).\n");
+ printf("\n");
+ printf("The default source policy, or if that is unavailable the default binary\n");
+ printf("policy, will be opened if no policy is provided.\n\n");
+}
+
+/**
+ * Prints statistics regarding a policy's components.
+ *
+ * @param fp Reference to a file to which to print
+ * policy statistics
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_stats(FILE * fp, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ unsigned int n_perms = 0;
+ qpol_iterator_t *iter = NULL;
+ apol_type_query_t *type_query = NULL;
+ apol_attr_query_t *attr_query = NULL;
+ apol_perm_query_t *perm_query = NULL;
+ apol_vector_t *perms = NULL, *v = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ char *str = NULL;
+ size_t n_types = 0, n_attrs = 0;
+ size_t n_classes = 0, n_users = 0, n_roles = 0, n_bools = 0, n_conds = 0, n_levels = 0,
+ n_cats = 0, n_portcons = 0, n_netifcons = 0, n_nodecons = 0, n_fsuses = 0,
+ n_genfscons = 0, n_allows = 0, n_neverallows = 0, n_auditallows = 0, n_dontaudits = 0,
+ n_typetrans = 0, n_typechanges = 0, n_typemembers = 0, n_isids = 0, n_roleallows = 0,
+ n_roletrans = 0, n_rangetrans = 0, n_constr = 0, n_vtrans = 0, n_permissives = 0,
+ n_polcaps = 0;
+
+ assert(policydb != NULL);
+
+ fprintf(fp, "\nStatistics for policy file: %s\n", policy_file);
+
+ if (!(str = apol_policy_get_version_type_mls_str(policydb)))
+ goto cleanup;
+
+ fprintf(fp, "Policy Version & Type: ");
+ fprintf(fp, "%s\n", str);
+ free(str);
+
+ if (qpol_policy_get_class_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_classes))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+
+ perm_query = apol_perm_query_create();
+ if (!perm_query)
+ goto cleanup;
+
+ /* Match all perms */
+ if (apol_perm_get_by_query(policydb, perm_query, &perms))
+ goto cleanup;
+
+ n_perms = apol_vector_get_size(perms);
+ apol_perm_query_destroy(&perm_query);
+ apol_vector_destroy(&perms);
+ fprintf(fp, "\n Classes: %7zd Permissions: %7d\n", n_classes, n_perms);
+
+ /* sensitivities/categories */
+ if (qpol_policy_get_level_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_levels))
+ goto cleanup;
+
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_cat_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_cats))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Sensitivities: %7zd Categories: %7zd\n", n_levels, n_cats);
+
+ /* types */
+ type_query = apol_type_query_create();
+ if (!type_query)
+ goto cleanup;
+ if (apol_type_get_by_query(policydb, type_query, &v) < 0)
+ goto cleanup;
+
+ n_types = apol_vector_get_size(v);
+ apol_type_query_destroy(&type_query);
+ apol_vector_destroy(&v);
+
+ attr_query = apol_attr_query_create();
+ if (!attr_query)
+ goto cleanup;
+ if (apol_attr_get_by_query(policydb, attr_query, &v) < 0)
+ goto cleanup;
+
+ n_attrs = apol_vector_get_size(v);
+ apol_attr_query_destroy(&attr_query);
+ apol_vector_destroy(&v);
+
+ fprintf(fp, " Types: %7zd Attributes: %7zd\n", n_types, n_attrs);
+ qpol_iterator_destroy(&iter);
+
+ /* users/roles */
+ if (qpol_policy_get_user_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_users))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_role_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_roles))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Users: %7zd Roles: %7zd\n", n_users, n_roles);
+
+ /* booleans/cond. exprs. */
+ if (qpol_policy_get_bool_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_bools))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_cond_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_conds))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Booleans: %7zd Cond. Expr.: %7zd\n", n_bools, n_conds);
+
+ /* allow/neverallow */
+ if (qpol_policy_get_avrule_iter(q, QPOL_RULE_ALLOW, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_allows))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_has_capability(q, QPOL_CAP_NEVERALLOW)) {
+ if (qpol_policy_get_avrule_iter(q, QPOL_RULE_NEVERALLOW, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_neverallows))
+ goto cleanup;
+ } else {
+ n_neverallows = 0;
+ }
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Allow: %7zd Neverallow: %7zd\n", n_allows, n_neverallows);
+
+ /* auditallow/dontaudit */
+ if (qpol_policy_get_avrule_iter(q, QPOL_RULE_AUDITALLOW, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_auditallows))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_avrule_iter(q, QPOL_RULE_DONTAUDIT, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_dontaudits))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Auditallow: %7zd Dontaudit: %7zd\n", n_auditallows, n_dontaudits);
+
+ /* type_transition/type_change */
+ if (qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_TRANS, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_typetrans))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_CHANGE, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_typechanges))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Type_trans: %7zd Type_change: %7zd\n", n_typetrans, n_typechanges);
+
+ /* type_member/role allow */
+ if (qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_MEMBER, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_typemembers))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_role_allow_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_roleallows))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Type_member: %7zd Role allow: %7zd\n", n_typemembers, n_roleallows);
+
+ /* role_trans/range_trans */
+ if (qpol_policy_get_role_trans_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_roletrans))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_range_trans_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_rangetrans))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Role_trans: %7zd Range_trans: %7zd\n", n_roletrans, n_rangetrans);
+
+ /* constraints/validatetrans */
+ if (qpol_policy_get_constraint_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_constr))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_validatetrans_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_vtrans))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Constraints: %7zd Validatetrans: %7zd\n", n_constr, n_vtrans);
+
+ /* isids/fs_use */
+ if (qpol_policy_get_isid_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_isids))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_fs_use_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_fsuses))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Initial SIDs: %7zd Fs_use: %7zd\n", n_isids, n_fsuses);
+
+ /* genfscon/portcon */
+ if (qpol_policy_get_genfscon_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_genfscons))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_portcon_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_portcons))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Genfscon: %7zd Portcon: %7zd\n", n_genfscons, n_portcons);
+
+ /* netifcon/nodecon */
+ if (qpol_policy_get_netifcon_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_netifcons))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_nodecon_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_nodecons))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Netifcon: %7zd Nodecon: %7zd\n", n_netifcons, n_nodecons);
+
+ /* permissives/polcaps */
+ if (qpol_policy_get_permissive_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_permissives))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ if (qpol_policy_get_polcap_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_polcaps))
+ goto cleanup;
+ qpol_iterator_destroy(&iter);
+ fprintf(fp, " Permissives: %7zd Polcap: %7zd\n", n_permissives, n_polcaps);
+
+ fprintf(fp, "\n");
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ apol_type_query_destroy(&type_query);
+ apol_attr_query_destroy(&attr_query);
+ apol_perm_query_destroy(&perm_query);
+ apol_vector_destroy(&v);
+ apol_vector_destroy(&perms);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's object classes.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular object class; otherwise
+ * the function prints statistics about all of the policy's object
+ * classes.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to an object class' name; if NULL,
+ * all object classes will be considered
+ * @param expand Flag indicating whether to print object class
+ * permissions
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_classes(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ qpol_iterator_t *iter = NULL;
+ size_t n_classes = 0;
+ const qpol_class_t *class_datum = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ if (name != NULL) {
+ if (qpol_policy_get_class_by_name(q, name, &class_datum))
+ goto cleanup;
+ print_class_perms(fp, class_datum, policydb, expand);
+ } else {
+ if (qpol_policy_get_class_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_classes))
+ goto cleanup;
+ fprintf(fp, "Object classes: %d\n", (int)n_classes);
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&class_datum))
+ goto cleanup;
+ print_class_perms(fp, class_datum, policydb, expand);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's types.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular type; otherwise
+ * the function prints statistics about all of the policy's types.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a type's name; if NULL,
+ * all object classes will be considered
+ * @param expand Flag indicating whether to print each type's attributes
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_types(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ const qpol_type_t *type_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ apol_vector_t *type_vector = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t vector_sz;
+
+ if (name != NULL) {
+ if (qpol_policy_get_type_by_name(q, name, &type_datum))
+ goto cleanup;
+ }
+
+ /* Find the number of types in the policy */
+ if (apol_type_get_by_query(policydb, NULL, &type_vector))
+ goto cleanup;
+ vector_sz = apol_vector_get_size(type_vector);
+ apol_vector_destroy(&type_vector);
+
+ if (name == NULL) {
+ fprintf(fp, "\nTypes: %zd\n", vector_sz);
+ }
+
+ /* if name was provided, only print that name */
+ if (name != NULL) {
+ if (qpol_policy_get_type_by_name(q, name, &type_datum))
+ goto cleanup;
+ print_type_attrs(fp, type_datum, policydb, expand);
+ } else {
+ if (qpol_policy_get_type_iter(q, &iter))
+ goto cleanup;
+ /* Print all type names */
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type_datum))
+ goto cleanup;
+ print_type_attrs(fp, type_datum, policydb, expand);
+ }
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's attributes.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular attribute; otherwise
+ * the function prints statistics about all of the policy's
+ * attributes.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to an attribute's name; if NULL,
+ * all object classes will be considered
+ * @param expand Flag indicating whether to print each attribute's
+ * allowed types
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_attribs(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ apol_attr_query_t *attr_query = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_type_t *type_datum = NULL;
+ size_t n_attrs, i;
+
+ /* we are only printing information about 1 attribute */
+ if (name != NULL) {
+ attr_query = apol_attr_query_create();
+ if (!attr_query)
+ goto cleanup;
+ if (apol_attr_query_set_attr(policydb, attr_query, name))
+ goto cleanup;
+ if (apol_attr_get_by_query(policydb, attr_query, &v))
+ goto cleanup;
+ apol_attr_query_destroy(&attr_query);
+ if (apol_vector_get_size(v) == 0) {
+ apol_vector_destroy(&v);
+ ERR(policydb, "Provided attribute (%s) is not a valid attribute name.", name);
+ goto cleanup;
+ }
+
+ type_datum = apol_vector_get_element(v, (size_t) 0);
+ print_attr_types(fp, type_datum, policydb, expand);
+ } else {
+ attr_query = apol_attr_query_create();
+ if (!attr_query)
+ goto cleanup;
+ if (apol_attr_get_by_query(policydb, attr_query, &v))
+ goto cleanup;
+ apol_attr_query_destroy(&attr_query);
+ n_attrs = apol_vector_get_size(v);
+
+ fprintf(fp, "\nAttributes: %zd\n", n_attrs);
+ for (i = 0; i < n_attrs; i++) {
+ /* get qpol_type_t* item from vector */
+ type_datum = (qpol_type_t *) apol_vector_get_element(v, (size_t) i);
+ if (!type_datum)
+ goto cleanup;
+ print_attr_types(fp, type_datum, policydb, expand);
+ }
+ }
+ apol_vector_destroy(&v);
+
+ retval = 0;
+ cleanup:
+ apol_attr_query_destroy(&attr_query);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's roles.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular role; otherwise
+ * the function prints statistics about all of the policy's roles.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to an role's name; if NULL,
+ * all roles will be considered
+ * @param expand Flag indicating whether to print valid users
+ * for each role
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_roles(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ const qpol_role_t *role_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_roles = 0;
+
+ if (name != NULL) {
+ if (qpol_policy_get_role_by_name(q, name, &role_datum))
+ goto cleanup;
+ print_role_types(fp, role_datum, policydb, expand);
+ } else {
+ if (qpol_policy_get_role_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_roles))
+ goto cleanup;
+ fprintf(fp, "\nRoles: %d\n", (int)n_roles);
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&role_datum))
+ goto cleanup;
+ print_role_types(fp, role_datum, policydb, expand);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's booleans.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular boolean; otherwise
+ * the function prints statistics about all of the policy's booleans.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a boolean's name; if NULL,
+ * all booleans will be considered
+ * @param expand Flag indicating whether to print each
+ * boolean's default state
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_booleans(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ qpol_bool_t *bool_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_bools = 0;
+
+ if (name != NULL) {
+ if (qpol_policy_get_bool_by_name(q, name, &bool_datum))
+ goto cleanup;
+ print_bool_state(fp, bool_datum, policydb, expand);
+ } else {
+ if (qpol_policy_get_bool_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_bools))
+ goto cleanup;
+ fprintf(fp, "\nConditional Booleans: %zd\n", n_bools);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&bool_datum))
+ goto cleanup;
+ print_bool_state(fp, bool_datum, policydb, expand);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's users.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular user; otherwise
+ * the function prints statistics about all of the policy's
+ * users.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a user's name; if NULL,
+ * all users will be considered
+ * @param expand Flag indicating whether to print each user's
+ * roles
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_users(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ qpol_iterator_t *iter = NULL;
+ const qpol_user_t *user_datum = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_users = 0;
+
+ if (name != NULL) {
+ if (qpol_policy_get_user_by_name(q, name, &user_datum))
+ goto cleanup;
+ print_user_roles(fp, user_datum, policydb, expand);
+ } else {
+ if (qpol_policy_get_user_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_users))
+ goto cleanup;
+ fprintf(fp, "\nUsers: %d\n", (int)n_users);
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&user_datum))
+ goto cleanup;
+ print_user_roles(fp, user_datum, policydb, expand);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's MLS sensitivities.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular sensitivity; otherwise
+ * the function prints statistics about all of the policy's
+ * sensitivities.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a sensitivity's name; if NULL,
+ * all sensitivities will be considered
+ * @param expand Flag indicating whether to print each
+ * sensitivity's categories
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_sens(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ size_t i;
+ char *tmp = NULL;
+ const char *lvl_name = NULL;
+ apol_level_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_level_t *level = NULL;
+ apol_mls_level_t *ap_mls_lvl = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+
+ query = apol_level_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_level_query_set_sens(policydb, query, name))
+ goto cleanup;
+ if (apol_level_get_by_query(policydb, query, &v))
+ goto cleanup;
+
+ if (!name)
+ fprintf(fp, "\nSensitivities: %zd\n", apol_vector_get_size(v));
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ level = apol_vector_get_element(v, i);
+ if (qpol_level_get_name(q, level, &lvl_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", lvl_name);
+ if (expand) {
+ ap_mls_lvl = (apol_mls_level_t *) apol_mls_level_create_from_qpol_level_datum(policydb, level);
+ tmp = apol_mls_level_render(policydb, ap_mls_lvl);
+ apol_mls_level_destroy(&ap_mls_lvl);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " level %s\n", tmp);
+ free(tmp);
+ }
+ }
+
+ if (name && !apol_vector_get_size(v)) {
+ ERR(policydb, "Provided sensitivity (%s) is not a valid sensitivity name.", name);
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_level_query_destroy(&query);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's MLS categories.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular category; otherwise
+ * the function prints statistics about all of the policy's
+ * categories.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a MLS category's name; if NULL,
+ * all categories will be considered
+ * @param expand Flag indicating whether to print each
+ * category's sensitivities
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_cats(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = 0;
+ apol_cat_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_cat_t *cat_datum = NULL;
+ size_t i, n_cats;
+
+ query = apol_cat_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_cat_query_set_cat(policydb, query, name))
+ goto cleanup;
+ if (apol_cat_get_by_query(policydb, query, &v))
+ goto cleanup;
+ n_cats = apol_vector_get_size(v);
+ apol_vector_sort(v, &qpol_cat_datum_compare, (void *)policydb);
+
+ if (!name)
+ fprintf(fp, "Categories: %zd\n", n_cats);
+ for (i = 0; i < n_cats; i++) {
+ cat_datum = apol_vector_get_element(v, i);
+ if (!cat_datum)
+ goto cleanup;
+ print_cat_sens(fp, cat_datum, policydb, expand);
+
+ }
+
+ if (name && !n_cats) {
+ ERR(policydb, "Provided category (%s) is not a valid category name.", name);
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_cat_query_destroy(&query);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's fs_use statements.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular filesystem; otherwise
+ * the function prints statistics about all of the policy's
+ * fs_use statements.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param type Reference to the name of a file system type; if NULL,
+ * all file system types will be considered
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_fsuse(FILE * fp, const char *type, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ char *tmp = NULL;
+ apol_fs_use_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_fs_use_t *fs_use = NULL;
+ size_t i;
+
+ query = apol_fs_use_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_fs_use_query_set_filesystem(policydb, query, type))
+ goto cleanup;
+ if (apol_fs_use_get_by_query(policydb, query, &v))
+ goto cleanup;
+
+ if (!type)
+ fprintf(fp, "\nFs_use: %zd\n", apol_vector_get_size(v));
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ fs_use = apol_vector_get_element(v, i);
+ tmp = apol_fs_use_render(policydb, fs_use);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ free(tmp);
+ }
+ if (type && !apol_vector_get_size(v))
+ ERR(policydb, "No fs_use statement for filesystem of type %s.", type);
+
+ retval = 0;
+ cleanup:
+ apol_fs_use_query_destroy(&query);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's genfscons.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular genfscon; otherwise
+ * the function prints statistics about all of the policy's
+ * genfscons.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a genfscon's type; if NULL,
+ * all genfscons will be considered
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_genfscon(FILE * fp, const char *type, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ size_t i;
+ char *tmp = NULL;
+ apol_genfscon_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ qpol_genfscon_t *genfscon = NULL;
+
+ query = apol_genfscon_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ if (apol_genfscon_query_set_filesystem(policydb, query, type))
+ goto cleanup;
+ if (apol_genfscon_get_by_query(policydb, query, &v))
+ goto cleanup;
+
+ if (!type)
+ fprintf(fp, "\nGenfscon: %zd\n", apol_vector_get_size(v));
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ genfscon = (qpol_genfscon_t *) apol_vector_get_element(v, i);
+ tmp = apol_genfscon_render(policydb, genfscon);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ free(tmp);
+ }
+
+ if (type && !apol_vector_get_size(v))
+ ERR(policydb, "No genfscon statement for filesystem of type %s.", type);
+
+ retval = 0;
+ cleanup:
+ apol_genfscon_query_destroy(&query);
+ apol_vector_destroy(&v);
+
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's netifcons.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular netifcon; otherwise
+ * the function prints statistics about all of the policy's
+ * netifcons.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a network interface's name; if NULL,
+ * all netifcons will be considered
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_netifcon(FILE * fp, const char *name, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ char *tmp;
+ const qpol_netifcon_t *netifcon = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_netifcons = 0;
+
+ if (name != NULL) {
+ if (qpol_policy_get_netifcon_by_name(q, name, &netifcon))
+ goto cleanup;
+ tmp = apol_netifcon_render(policydb, netifcon);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ free(tmp);
+ } else {
+ if (qpol_policy_get_netifcon_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_netifcons))
+ goto cleanup;
+ fprintf(fp, "\nNetifcon: %zd\n", n_netifcons);
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&netifcon))
+ goto cleanup;
+ tmp = apol_netifcon_render(policydb, netifcon);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ free(tmp);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's nodecons.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular nodecon; otherwise
+ * the function prints statistics about all of the policy's
+ * nodecons.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a textually represented IP address;
+ * if NULL, all nodecons will be considered
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_nodecon(FILE * fp, const char *addr, const apol_policy_t * policydb)
+{
+ int retval = -1, protocol;
+ char *tmp = NULL;
+ uint32_t address[4] = { 0, 0, 0, 0 };
+ apol_nodecon_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ qpol_nodecon_t *nodecon = NULL;
+ size_t n_nodecons = 0, i;
+
+ query = apol_nodecon_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+
+ /* address needs to be in a libapol-understandable format */
+ if (addr) {
+ protocol = apol_str_to_internal_ip(addr, address);
+ if (protocol < 0) {
+ ERR(policydb, "%s", "Unable to parse IP address");
+ goto cleanup;
+ }
+ if (apol_nodecon_query_set_addr(policydb, query, address, protocol))
+ goto cleanup;
+ if (apol_nodecon_query_set_protocol(policydb, query, protocol))
+ goto cleanup;
+ }
+
+ if (apol_nodecon_get_by_query(policydb, query, &v))
+ goto cleanup;
+
+ n_nodecons = apol_vector_get_size(v);
+
+ if (!addr) {
+ fprintf(fp, "Nodecon: %zd\n", n_nodecons);
+ } else if (!n_nodecons) {
+ ERR(policydb, "No matching nodecon for address %s.", addr);
+ retval = 1;
+ goto cleanup;
+ }
+
+ for (i = 0; i < n_nodecons; i++) {
+ nodecon = apol_vector_get_element(v, i);
+ tmp = apol_nodecon_render(policydb, nodecon);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ free(tmp);
+ }
+
+ retval = 0;
+ cleanup:
+ apol_nodecon_query_destroy(&query);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's portcons.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular portcon; otherwise
+ * the function prints statistics about all of the policy's
+ * portcons.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param num Reference to a port number; if NULL,
+ * all ports will be considered
+ * @param protocol Reference to the name of a ISO 7498-1
+ * transport layer protocol used to communicate over a port
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_portcon(FILE * fp, const char *num, const char *protocol, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ const qpol_portcon_t *portcon = NULL;
+ qpol_iterator_t *iter = NULL;
+ uint16_t low_port, high_port;
+ uint8_t ocon_proto, proto = 0;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_portcons;
+ char *tmp = NULL;
+
+ if (protocol) {
+ if (!strcmp(protocol, "tcp"))
+ proto = IPPROTO_TCP;
+ else if (!strcmp(protocol, "udp"))
+ proto = IPPROTO_UDP;
+ else {
+ ERR(policydb, "Unable to get portcon by protocol: bad protocol %s.", protocol);
+ goto cleanup;
+ }
+ }
+ if (qpol_policy_get_portcon_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_portcons))
+ goto cleanup;
+ if (!num)
+ fprintf(fp, "\nPortcon: %zd\n", n_portcons);
+
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&portcon))
+ goto cleanup;
+ if (qpol_portcon_get_low_port(q, portcon, &low_port))
+ goto cleanup;
+ if (qpol_portcon_get_high_port(q, portcon, &high_port))
+ goto cleanup;
+ if (qpol_portcon_get_protocol(q, portcon, &ocon_proto))
+ goto cleanup;
+ if (num) {
+ if (atoi(num) < low_port || atoi(num) > high_port)
+ continue;
+ }
+ if (protocol) {
+ if (ocon_proto != proto)
+ continue;
+ }
+ fprintf(fp, " %s\n", (tmp = apol_portcon_render(policydb, portcon)));
+ free(tmp);
+ tmp = NULL;
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's initial SIDs.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular initial SID; otherwise
+ * the function prints statistics about all of the policy's
+ * initial SIDs.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a SID name; if NULL,
+ * all initial SIDs will be considered
+ * @param expand Flag indicating whether to print each
+ * initial SID's security context
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_isids(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ apol_isid_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_isid_t *isid = NULL;
+ const qpol_context_t *ctxt = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t i, n_isids = 0;
+ char *tmp = NULL;
+ const char *isid_name = NULL;
+
+ query = apol_isid_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_isid_query_set_name(policydb, query, name))
+ goto cleanup;
+ if (apol_isid_get_by_query(policydb, query, &v))
+ goto cleanup;
+ n_isids = apol_vector_get_size(v);
+
+ if (!name)
+ fprintf(fp, "\nInitial SID: %zd\n", n_isids);
+
+ for (i = 0; i < n_isids; i++) {
+ isid = apol_vector_get_element(v, i);
+ if (qpol_isid_get_name(q, isid, &isid_name))
+ goto cleanup;
+ if (!expand) {
+ fprintf(fp, " %s\n", isid_name);
+ } else {
+ if (qpol_isid_get_context(q, isid, &ctxt))
+ goto cleanup;
+ tmp = apol_qpol_context_render(policydb, ctxt);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, "%20s: %s\n", isid_name, tmp);
+ free(tmp);
+ }
+ }
+
+ if (name && !n_isids) {
+ ERR(policydb, "Provided initial SID name (%s) is not a valid name.", name);
+ goto cleanup;
+ }
+
+ retval = 0;
+ cleanup:
+ apol_isid_query_destroy(&query);
+ apol_vector_destroy(&v);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's permissives.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular permissive; otherwise
+ * the function prints statistics about all of the policy's permissives.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a permissives name; if NULL,
+ * all permissives will be considered
+ * @param expand Flag indicating whether to print each
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int print_permissives(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ const char *tmp = NULL;
+ const qpol_permissive_t *permissive_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_permissives = 0;
+
+ if (qpol_policy_get_permissive_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_permissives))
+ goto cleanup;
+ fprintf(fp, "\nPermissive Types: %zd\n", n_permissives);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&permissive_datum))
+ goto cleanup;
+ if (qpol_permissive_get_name(q, permissive_datum, &tmp))
+ goto cleanup;
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+/**
+ * Prints statistics regarding a policy's capabilities.
+ * If this function is given a name, it will attempt to
+ * print statistics about a particular capability; otherwise
+ * the function prints statistics about all of the policy's capabilities.
+ *
+ * @param fp Reference to a file to which to print statistics
+ * @param name Reference to a policy capability name; if NULL,
+ * all capabilities will be considered
+ * @param expand Flag indicating whether to print each
+ * @param policydb Reference to a policy
+ *
+ * @return 0 on success, < 0 on error.
+ */
+
+static int print_polcaps(FILE * fp, const char *name, int expand, const apol_policy_t * policydb)
+{
+ int retval = -1;
+ const char *tmp = NULL;
+ qpol_polcap_t *polcap_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_polcaps = 0;
+
+ if (qpol_policy_get_polcap_iter(q, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_polcaps))
+ goto cleanup;
+ fprintf(fp, "\nPolicy Capabilities: %zd\n", n_polcaps);
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&polcap_datum))
+ goto cleanup;
+ if (qpol_polcap_get_name(q, polcap_datum, &tmp))
+ goto cleanup;
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " %s\n", tmp);
+ }
+
+ retval = 0;
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return retval;
+}
+
+static const char *get_attr_string(int attr)
+{
+ const char *string = "";
+ switch (attr)
+ {
+ case QPOL_CEXPR_SYM_USER:
+ string = "u1";
+ break;
+ case QPOL_CEXPR_SYM_ROLE:
+ string = "r1";
+ break;
+ case QPOL_CEXPR_SYM_TYPE:
+ string = "t1";
+ break;
+
+ case QPOL_CEXPR_SYM_USER+QPOL_CEXPR_SYM_TARGET:
+ string = "u2";
+ break;
+ case QPOL_CEXPR_SYM_ROLE+QPOL_CEXPR_SYM_TARGET:
+ string = "r2";
+ break;
+ case QPOL_CEXPR_SYM_TYPE+QPOL_CEXPR_SYM_TARGET:
+ string = "t2";
+ break;
+
+ case QPOL_CEXPR_SYM_USER+QPOL_CEXPR_SYM_XTARGET:
+ string = "u3";
+ break;
+ case QPOL_CEXPR_SYM_ROLE+QPOL_CEXPR_SYM_XTARGET:
+ string = "r3";
+ break;
+ case QPOL_CEXPR_SYM_TYPE+QPOL_CEXPR_SYM_XTARGET:
+ string = "t3";
+ break;
+
+ case QPOL_CEXPR_SYM_L1L2:
+ string = "l1 l2";
+ break;
+ case QPOL_CEXPR_SYM_L1H2:
+ string = "l1 h2";
+ break;
+ case QPOL_CEXPR_SYM_H1L2:
+ string = "h1 l2";
+ break;
+ case QPOL_CEXPR_SYM_H1H2:
+ string = "h1 h2";
+ break;
+ case QPOL_CEXPR_SYM_L1H1:
+ string = "l1 h1";
+ break;
+ case QPOL_CEXPR_SYM_L2H2:
+ string = "l2 h2";
+ break;
+ }
+
+ return string;
+}
+
+static const char *get_op_string(int op)
+{
+ char *string = "";
+
+ switch (op)
+ {
+ case QPOL_CEXPR_OP_EQ:
+ string = "==";
+ break;
+ case QPOL_CEXPR_OP_NEQ:
+ string = "!=";
+ break;
+ case QPOL_CEXPR_OP_DOM:
+ string = "dom";
+ break;
+ case QPOL_CEXPR_OP_DOMBY:
+ string = "domby";
+ break;
+ case QPOL_CEXPR_OP_INCOMP:
+ string = "incomp";
+ break;
+ }
+
+ return string;
+}
+
+static int print_constraints(FILE * fp, int expand, const apol_policy_t * policydb, int linebreaks)
+{
+ int retval = -1;
+ const char *class_name = NULL;
+ char *constrain_type;
+ char *perm_list = "No Perms Extracted";
+ const qpol_constraint_expr_node_t *expr = NULL;
+ qpol_iterator_t *policy_iter = NULL; // Iterates over all constraints in a policy
+ qpol_iterator_t *perm_iter = NULL; // Iterates over permissions in a constraint
+ qpol_iterator_t *expr_iter = NULL; // Iterates over expression in a constraint
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ qpol_constraint_t *constraint = NULL;
+ const qpol_class_t *class;
+ size_t n_constraints = 0;
+ int expr_type = 0;
+ int sym_type = 0; // 'attr' in struct constraint_expr
+ int op = 0;
+
+ if (qpol_policy_get_constraint_iter(q, &policy_iter) != 0)
+ {
+ ERR (policydb, "%s", "Policy constraint iterator not accessible");
+ return retval;
+ }
+ if (qpol_iterator_get_size(policy_iter, &n_constraints) != 0)
+ {
+ ERR(policydb, "%s", "Policy size computation failed");
+ goto cleanup;
+ }
+
+ fprintf(fp, "\nConstraints: %zd\n", n_constraints);
+
+ // Iterate through constraints
+ for (; qpol_iterator_end(policy_iter) == 0; qpol_iterator_next(policy_iter))
+ {
+ constrain_type = "";
+ if (qpol_iterator_get_item(policy_iter, (void **)&constraint) != 0)
+ {
+ ERR(policydb, "%s", "Can't get constraint from iterator\n");
+ goto cleanup;
+ }
+
+ if (qpol_constraint_get_class(q, constraint, &class) != 0)
+ {
+ ERR(policydb, "%s", "Can't get class from constraint\n");
+ goto cleanup;
+ }
+
+ if (qpol_class_get_name(q, class, &class_name) != 0)
+ {
+ ERR(policydb, "%s", "Can't get class name from constraint\n");
+ goto cleanup;
+ }
+
+ // Get expression, we need to look into it.
+ if (qpol_constraint_get_expr_iter (q, constraint, &expr_iter) != 0)
+ {
+ ERR(policydb, "%s", "Can't get expression from constraint\n");
+ goto cleanup;
+ }
+ // Traverse the iterator to see if this is mlsconstrain
+ for (; qpol_iterator_end(expr_iter) == 0; qpol_iterator_next(expr_iter))
+ {
+ if (qpol_iterator_get_item(expr_iter, (void **)&expr) != 0)
+ {
+ ERR(policydb, "%s", "Can't get expression from iterator\n");
+ goto cleanup;
+ }
+
+ if (qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type) != 0)
+ {
+ ERR(policydb, "%s", "Can't get sym_type from expression\n");
+ goto cleanup;
+ }
+
+ if (sym_type >= QPOL_CEXPR_SYM_L1L2)
+ {
+ constrain_type = "mls";
+ break;
+ }
+ }
+
+ // print permissions
+ fprintf (fp, "%sconstrain { %s } { ", constrain_type, class_name);
+
+ if (qpol_constraint_get_perm_iter (q, constraint, &perm_iter) != 0)
+ {
+ ERR(policydb, "%s", "Can't get permissions from constraint\n");
+ goto cleanup;
+ }
+
+ for (; qpol_iterator_end(perm_iter) == 0; qpol_iterator_next(perm_iter))
+ {
+ if (qpol_iterator_get_item(perm_iter, (void **)&perm_list) != 0)
+ {
+ ERR(policydb, "%s", "Can't get permissions from iterator\n");
+ goto cleanup;
+ }
+
+ fprintf (fp, "%s ", perm_list);
+ free (perm_list); // Strdup created the string.
+ }
+ fprintf (fp, " } ");
+
+ // dump RPN expressions
+ if (qpol_constraint_get_expr_iter (q, constraint, &expr_iter) != 0)
+ {
+ ERR(policydb, "%s", "Can't get expression from constraint\n");
+ goto cleanup;
+ }
+
+ fprintf (fp, "\n( ");
+ for (; qpol_iterator_end(expr_iter) == 0; qpol_iterator_next(expr_iter))
+ {
+ qpol_iterator_t *names_iter = NULL;
+
+ if (qpol_iterator_get_item(expr_iter, (void **)&expr) != 0)
+ {
+ ERR(policydb, "%s", "Can't get expression from iterator\n");
+ goto cleanup;
+ }
+
+ if (qpol_constraint_expr_node_get_op (q, expr, &op) != 0)
+ {
+ ERR(policydb, "%s", "Can't get op from expression\n");
+ goto cleanup;
+ }
+
+ if (qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type) != 0)
+ {
+ ERR(policydb, "%s", "Can't get sym_type from expression\n");
+ goto cleanup;
+ }
+
+ if (qpol_constraint_expr_node_get_expr_type(q, expr, &expr_type) != 0)
+ {
+ ERR(policydb, "%s", "Can't get expr_type from expression\n");
+ goto cleanup;
+ }
+
+ if (linebreaks)
+ fprintf (fp, "\n\t");
+
+ if (expr_type == QPOL_CEXPR_TYPE_NOT)
+ {
+ fprintf (fp, " ! ");
+ }
+ if (expr_type == QPOL_CEXPR_TYPE_AND)
+ {
+ fprintf (fp, " && ");
+ }
+ if (expr_type == QPOL_CEXPR_TYPE_OR)
+ {
+ fprintf (fp, " || ");
+ }
+ if (expr_type == QPOL_CEXPR_TYPE_ATTR)
+ {
+ fprintf (fp, " %s ", get_attr_string(sym_type));
+ fprintf (fp, "%s ", get_attr_string(sym_type | QPOL_CEXPR_SYM_TARGET));
+ fprintf (fp, "%s ", get_op_string(op));
+ }
+ if (expr_type == QPOL_CEXPR_TYPE_NAMES)
+ {
+ size_t name_size=0;
+
+ fprintf (fp, " %s ", get_attr_string(sym_type));
+
+ if (qpol_constraint_expr_node_get_names_iter (q, expr, &names_iter) != 0)
+ {
+ ERR(policydb, "%s", "Can't get names iterator from expression\n");
+ goto cleanup;
+ }
+
+ if (qpol_iterator_get_size(names_iter, &name_size) != 0)
+ {
+ ERR(policydb, "%s", "Can't get size from names iterator\n");
+ goto cleanup;
+ }
+ if (name_size > 0)
+ {
+ if (name_size > 1)
+ fprintf (fp, "{ ");
+
+ for (; qpol_iterator_end(names_iter) == 0; qpol_iterator_next(names_iter))
+ {
+ char *lname = NULL;
+
+ if (qpol_iterator_get_item (names_iter, (void **)&lname) != 0)
+ {
+ ERR(policydb, "%s", "Can't get names from iterator\n");
+ goto cleanup;
+ }
+
+ fprintf (fp, "%s ", lname);
+ free (lname);
+
+ }
+ if (name_size > 1)
+ fprintf (fp, "} ");
+ } else {
+ fprintf (fp, "%s ", CONSTRAIN_NULL_SET);
+ }
+
+ fprintf (fp, "%s ", get_op_string(op));
+ }
+ }
+ if (linebreaks)
+ fprintf (fp, "\n);\n\n");
+ else
+ fprintf (fp, ");\n\n");
+ }
+
+ retval = 0;
+
+cleanup: // close and destroy iterators etc.
+ if (policy_iter != NULL) qpol_iterator_destroy(&policy_iter);
+ if (perm_iter != NULL) qpol_iterator_destroy(&perm_iter);
+ if (expr_iter != NULL) qpol_iterator_destroy(&expr_iter);
+
+ return retval;
+}
+
+
+int main(int argc, char **argv)
+{
+ int classes, types, attribs, roles, users, all, expand, stats, rt, optc, isids, bools, sens, cats, fsuse, genfs, netif,
+ node, port, permissives, polcaps, constrain, linebreaks;
+ apol_policy_t *policydb = NULL;
+ apol_policy_path_t *pol_path = NULL;
+ apol_vector_t *mod_paths = NULL;
+ apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+
+ char *class_name, *type_name, *attrib_name, *role_name, *user_name, *isid_name, *bool_name, *sens_name, *cat_name,
+ *fsuse_type, *genfs_type, *netif_name, *node_addr, *permissive_name, *polcap_name, *port_num = NULL, *protocol = NULL;
+
+ class_name = type_name = attrib_name = role_name = user_name = isid_name = bool_name = sens_name = cat_name = fsuse_type =
+ genfs_type = netif_name = node_addr = port_num = permissive_name = polcap_name = NULL;
+ classes = types = attribs = roles = users = all = expand = stats = isids = bools = sens = cats = fsuse = genfs = netif =
+ node = port = permissives = polcaps = constrain = linebreaks = 0;
+ while ((optc = getopt_long(argc, argv, "c::t::a::r::u::b::lxhV", longopts, NULL)) != -1) {
+ switch (optc) {
+ case 0:
+ break;
+ case 'c': /* classes */
+ classes = 1;
+ if (optarg != 0)
+ class_name = optarg;
+ break;
+ case OPT_SENSITIVITY:
+ sens = 1;
+ if (optarg != 0)
+ sens_name = optarg;
+ break;
+ case OPT_CATEGORY:
+ cats = 1;
+ if (optarg != 0)
+ cat_name = optarg;
+ break;
+ case 't': /* types */
+ types = 1;
+ if (optarg != 0)
+ type_name = optarg;
+ break;
+ case 'a': /* attributes */
+ attribs = 1;
+ if (optarg != 0)
+ attrib_name = optarg;
+ break;
+ case 'r': /* roles */
+ roles = 1;
+ if (optarg != 0)
+ role_name = optarg;
+ break;
+ case 'u': /* users */
+ users = 1;
+ if (optarg != 0)
+ user_name = optarg;
+ break;
+ case 'b': /* conditional booleans */
+ bools = 1;
+ if (optarg != 0)
+ bool_name = optarg;
+ break;
+ case OPT_INITIALSID:
+ isids = 1;
+ if (optarg != 0)
+ isid_name = optarg;
+ break;
+ case OPT_FS_USE:
+ fsuse = 1;
+ if (optarg != 0)
+ fsuse_type = optarg;
+ break;
+ case OPT_GENFSCON:
+ genfs = 1;
+ if (optarg != 0)
+ genfs_type = optarg;
+ break;
+ case OPT_NETIFCON:
+ netif = 1;
+ if (optarg != 0)
+ netif_name = optarg;
+ break;
+ case OPT_NODECON:
+ node = 1;
+ if (optarg != 0)
+ node_addr = optarg;
+ break;
+ case OPT_PERMISSIVE:
+ permissives = 1;
+ if (optarg != 0)
+ permissive_name = optarg;
+ break;
+ case OPT_POLCAP:
+ polcaps = 1;
+ if (optarg != 0)
+ polcap_name = optarg;
+ break;
+ case OPT_PORTCON:
+ port = 1;
+ if (optarg != 0)
+ port_num = optarg;
+ break;
+ case OPT_PROTOCOL:
+ if (optarg != 0)
+ protocol = optarg;
+ break;
+ case OPT_ALL:
+ all = 1;
+ break;
+ case 'x': /* expand */
+ expand = 1;
+ break;
+ case 'l': /* Print line breaks in constraints */
+ linebreaks=1;
+ break;
+ case OPT_CONSTRAIN: /* Print constraints */
+ constrain=1;
+ break;
+ case OPT_STATS:
+ stats = 1;
+ break;
+ case 'h': /* help */
+ usage(argv[0], 0);
+ exit(0);
+ case 'V': /* version */
+ printf("seinfo %s\n%s\n", VERSION, COPYRIGHT_INFO);
+ exit(0);
+ default:
+ usage(argv[0], 1);
+ exit(1);
+ }
+ }
+
+ /* check for naked -l */
+ if (protocol && !(port || all)) {
+ fprintf(stderr, "The --protocol flag requires either --portcon or --ALL.\n");
+ exit(1);
+ }
+
+ /* if no options, then show stats */
+ if (classes + types + attribs + roles + users + isids + bools + sens + cats + fsuse + genfs + netif + node + port + permissives + polcaps + constrain + all < 1) {
+ stats = 1;
+ }
+
+ int policy_load_options = ((stats || all) ? 0 : QPOL_POLICY_OPTION_NO_RULES);
+
+ if (argc - optind < 1) {
+ rt = qpol_default_policy_find(&policy_file);
+ if (rt < 0) {
+ fprintf(stderr, "Default policy search failed: %s\n", strerror(errno));
+ exit(1);
+ } else if (rt != 0) {
+ fprintf(stderr, "No default policy found.\n");
+ exit(1);
+ }
+ policy_load_options |= QPOL_POLICY_OPTION_MATCH_SYSTEM;
+ } else {
+ policy_file = strdup(argv[optind]);
+ if (!policy_file) {
+ fprintf(stderr, "Out of memory\n");
+ exit(1);
+ }
+ optind++;
+ }
+
+ if (argc - optind > 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ if (!(mod_paths = apol_vector_create(NULL))) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ free(policy_file);
+ exit(1);
+ }
+ for (; argc - optind; optind++) {
+ if (apol_vector_append(mod_paths, (void *)argv[optind])) {
+ ERR(policydb, "Error loading module %s", argv[optind]);
+ free(policy_file);
+ apol_vector_destroy(&mod_paths);
+ exit(1);
+ }
+ }
+ } else if (apol_file_is_policy_path_list(policy_file) > 0) {
+ pol_path = apol_policy_path_create_from_file(policy_file);
+ if (!pol_path) {
+ ERR(policydb, "%s", "invalid policy list");
+ free(policy_file);
+ exit(1);
+ }
+ }
+
+ if (!pol_path)
+ pol_path = apol_policy_path_create(path_type, policy_file, mod_paths);
+ if (!pol_path) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ free(policy_file);
+ apol_vector_destroy(&mod_paths);
+ exit(1);
+ }
+ apol_vector_destroy(&mod_paths);
+
+ policydb = apol_policy_create_from_policy_path(pol_path, policy_load_options, NULL, NULL);
+ if (!policydb) {
+ ERR(policydb, "%s", strerror(errno));
+ free(policy_file);
+ apol_policy_path_destroy(&pol_path);
+ exit(1);
+ }
+
+ /* display requested info */
+ if (stats || all)
+ print_stats(stdout, policydb);
+ if (classes || all)
+ print_classes(stdout, class_name, expand, policydb);
+ if (types || all)
+ print_types(stdout, type_name, expand, policydb);
+ if (attribs || all)
+ print_attribs(stdout, attrib_name, expand, policydb);
+ if (roles || all)
+ print_roles(stdout, role_name, expand, policydb);
+ if (users || all)
+ print_users(stdout, user_name, expand, policydb);
+ if (bools || all)
+ print_booleans(stdout, bool_name, expand, policydb);
+ if (sens || all)
+ print_sens(stdout, sens_name, expand, policydb);
+ if (cats || all)
+ print_cats(stdout, cat_name, expand, policydb);
+ if (fsuse || all)
+ print_fsuse(stdout, fsuse_type, policydb);
+ if (genfs || all)
+ print_genfscon(stdout, genfs_type, policydb);
+ if (netif || all)
+ print_netifcon(stdout, netif_name, policydb);
+ if (node || all)
+ print_nodecon(stdout, node_addr, policydb);
+ if (port || all)
+ print_portcon(stdout, port_num, protocol, policydb);
+ if (isids || all)
+ print_isids(stdout, isid_name, expand, policydb);
+ if (permissives || all)
+ print_permissives(stdout, permissive_name, expand, policydb);
+ if (polcaps || all)
+ print_polcaps(stdout, polcap_name, expand, policydb);
+ if (constrain || all)
+ print_constraints(stdout, expand, policydb, linebreaks);
+
+ apol_policy_destroy(&policydb);
+ apol_policy_path_destroy(&pol_path);
+ free(policy_file);
+ exit(0);
+}
+
+/**
+ * Prints a textual representation of a type, and possibly
+ * all of that type's attributes.
+ *
+ * @param fp Reference to a file to which to print type information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each type's
+ * attributes
+ */
+static void print_type_attrs(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand)
+{
+ qpol_iterator_t *iter = NULL;
+ unsigned char isattr, isalias;
+ const char *type_name = NULL, *attr_name = NULL;
+ const qpol_type_t *attr_datum = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+
+ if (qpol_type_get_name(q, type_datum, &type_name))
+ goto cleanup;
+ if (qpol_type_get_isattr(q, type_datum, &isattr))
+ goto cleanup;
+ if (qpol_type_get_isalias(q, type_datum, &isalias))
+ goto cleanup;
+
+ if (!isattr && !isalias) {
+ fprintf(fp, " %s\n", type_name);
+ if (expand) { /* Print this type's attributes */
+ if (qpol_type_get_attr_iter(q, type_datum, &iter))
+ goto cleanup;
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&attr_datum))
+ goto cleanup;
+ if (qpol_type_get_name(q, attr_datum, &attr_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", attr_name);
+ }
+ }
+ }
+
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return;
+}
+
+/**
+ * Prints a textual representation of an attribute, and possibly
+ * all of that attribute's types.
+ *
+ * @param fp Reference to a file to which to print attribute information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each attribute's
+ * types
+ */
+static void print_attr_types(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand)
+{
+ const qpol_type_t *attr_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ const char *attr_name = NULL, *type_name = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ unsigned char isattr;
+
+ if (qpol_type_get_name(q, type_datum, &attr_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", attr_name);
+
+ if (expand) {
+ /* get an iterator over all types this attribute has */
+ if (qpol_type_get_isattr(q, type_datum, &isattr))
+ goto cleanup;
+ if (isattr) { /* sanity check */
+ if (qpol_type_get_type_iter(q, type_datum, &iter))
+ goto cleanup;
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&attr_datum))
+ goto cleanup;
+ if (qpol_type_get_name(q, attr_datum, &type_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", type_name);
+ }
+ qpol_iterator_destroy(&iter);
+
+ } else /* this should never happen */
+ goto cleanup;
+
+ }
+
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return;
+}
+
+/**
+ * Prints a textual representation of a user, and possibly
+ * all of that user's roles.
+ *
+ * @param fp Reference to a file to which to print user information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each user's
+ * roles
+ */
+static void print_user_roles(FILE * fp, const qpol_user_t * user_datum, const apol_policy_t * policydb, const int expand)
+{
+ const qpol_role_t *role_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_mls_range_t *range = NULL;
+ const qpol_mls_level_t *dflt_level = NULL;
+ apol_mls_level_t *ap_lvl = NULL;
+ apol_mls_range_t *ap_range = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ char *tmp;
+ const char *user_name, *role_name;
+
+ if (qpol_user_get_name(q, user_datum, &user_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", user_name);
+
+ if (expand) {
+ if (qpol_policy_has_capability(q, QPOL_CAP_MLS)) {
+ /* print default level */
+ if (qpol_user_get_dfltlevel(q, user_datum, &dflt_level))
+ goto cleanup;
+ ap_lvl = apol_mls_level_create_from_qpol_mls_level(policydb, dflt_level);
+ tmp = apol_mls_level_render(policydb, ap_lvl);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " default level: %s\n", tmp);
+ free(tmp);
+ /* print default range */
+ if (qpol_user_get_range(q, user_datum, &range))
+ goto cleanup;
+ ap_range = apol_mls_range_create_from_qpol_mls_range(policydb, range);
+ tmp = apol_mls_range_render(policydb, ap_range);
+ if (!tmp)
+ goto cleanup;
+ fprintf(fp, " range: %s\n", tmp);
+ free(tmp);
+ }
+
+ fprintf(fp, " roles:\n");
+ if (qpol_user_get_role_iter(q, user_datum, &iter))
+ goto cleanup;
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&role_datum))
+ goto cleanup;
+ if (qpol_role_get_name(q, role_datum, &role_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", role_name);
+ }
+ }
+
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ apol_mls_level_destroy(&ap_lvl);
+ apol_mls_range_destroy(&ap_range);
+ return;
+}
+
+/**
+ * Prints a textual representation of a role, and possibly
+ * all of that role's types.
+ *
+ * @param fp Reference to a file to which to print role information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each role's
+ * types
+ */
+static void print_role_types(FILE * fp, const qpol_role_t * role_datum, const apol_policy_t * policydb, const int expand)
+{
+ const char *role_name = NULL, *type_name = NULL;
+ const qpol_role_t *dom_datum = NULL;
+ const qpol_type_t *type_datum = NULL;
+ qpol_iterator_t *iter = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t n_dom = 0, n_types = 0;
+
+ if (qpol_role_get_name(q, role_datum, &role_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", role_name);
+
+ if (expand) {
+ if (qpol_role_get_dominate_iter(q, role_datum, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_dom))
+ goto cleanup;
+ if ((int)n_dom > 0) {
+ fprintf(fp, " Dominated Roles:\n");
+ /* print dominated roles */
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&dom_datum))
+ goto cleanup;
+ if (qpol_role_get_name(q, dom_datum, &role_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", role_name);
+ }
+ }
+ qpol_iterator_destroy(&iter);
+
+ if (qpol_role_get_type_iter(q, role_datum, &iter))
+ goto cleanup;
+ if (qpol_iterator_get_size(iter, &n_types))
+ goto cleanup;
+ if ((int)n_types > 0) {
+ fprintf(fp, " Types:\n");
+ /* print types */
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&type_datum))
+ goto cleanup;
+ if (qpol_type_get_name(q, type_datum, &type_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", type_name);
+ }
+ }
+ }
+
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return;
+}
+
+/**
+ * Prints a textual representation of a boolean value, and possibly
+ * all of that boolean's initial state.
+ *
+ * @param fp Reference to a file to which to print boolean information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each boolean's
+ * initial state
+ */
+static void print_bool_state(FILE * fp, const qpol_bool_t * bool_datum, const apol_policy_t * policydb, const int expand)
+{
+ const char *bool_name = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ int state;
+
+ if (qpol_bool_get_name(q, bool_datum, &bool_name))
+ return;
+ fprintf(fp, " %s", bool_name);
+
+ if (expand) {
+ if (qpol_bool_get_state(q, bool_datum, &state))
+ return;
+ fprintf(fp, ": %s", state ? "TRUE" : "FALSE");
+ }
+ fprintf(fp, "\n");
+}
+
+/**
+ * Prints a textual representation of an object class and possibly
+ * all of that object class' permissions.
+ *
+ * @param fp Reference to a file to which to print object class information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each object class'
+ * permissions
+ */
+static void print_class_perms(FILE * fp, const qpol_class_t * class_datum, const apol_policy_t * policydb, const int expand)
+{
+ const char *class_name = NULL, *perm_name = NULL;
+ qpol_iterator_t *iter = NULL;
+ const qpol_common_t *common_datum = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+
+ if (!class_datum)
+ goto cleanup;
+
+ if (qpol_class_get_name(q, class_datum, &class_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", class_name);
+
+ if (expand) {
+ /* get commons for this class */
+ if (qpol_class_get_common(q, class_datum, &common_datum))
+ goto cleanup;
+ if (common_datum) {
+ if (qpol_common_get_perm_iter(q, common_datum, &iter))
+ goto cleanup;
+ /* print perms for the common */
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&perm_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", perm_name);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+ /* print unique perms for this class */
+ if (qpol_class_get_perm_iter(q, class_datum, &iter))
+ goto cleanup;
+ for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
+ if (qpol_iterator_get_item(iter, (void **)&perm_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", perm_name);
+ }
+ qpol_iterator_destroy(&iter);
+ }
+
+ cleanup:
+ qpol_iterator_destroy(&iter);
+ return;
+}
+
+/**
+ * Prints a textual representation of a MLS category and possibly
+ * all of that category's sensitivies.
+ *
+ * @param fp Reference to a file to which to print category information
+ * @param type_datum Reference to sepol type_datum
+ * @param policydb Reference to a policy
+ * @param expand Flag indicating whether to print each category's
+ * sensitivities
+ */
+static void print_cat_sens(FILE * fp, const qpol_cat_t * cat_datum, const apol_policy_t * policydb, const int expand)
+{
+ const char *cat_name, *lvl_name;
+ apol_level_query_t *query = NULL;
+ apol_vector_t *v = NULL;
+ const qpol_level_t *lvl_datum = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(policydb);
+ size_t i, n_sens = 0;
+
+ if (!fp || !cat_datum || !policydb)
+ goto cleanup;
+
+ /* get category name for apol query */
+ if (qpol_cat_get_name(q, cat_datum, &cat_name))
+ goto cleanup;
+
+ query = apol_level_query_create();
+ if (!query) {
+ ERR(policydb, "%s", strerror(ENOMEM));
+ goto cleanup;
+ }
+ if (apol_level_query_set_cat(policydb, query, cat_name))
+ goto cleanup;
+ if (apol_level_get_by_query(policydb, query, &v))
+ goto cleanup;
+ fprintf(fp, " %s\n", cat_name);
+
+ if (expand) {
+ fprintf(fp, " Sensitivities:\n");
+ apol_vector_sort(v, &qpol_level_datum_compare, (void *)policydb);
+ n_sens = apol_vector_get_size(v);
+ for (i = 0; i < n_sens; i++) {
+ lvl_datum = (qpol_level_t *) apol_vector_get_element(v, i);
+ if (!lvl_datum)
+ goto cleanup;
+ if (qpol_level_get_name(q, lvl_datum, &lvl_name))
+ goto cleanup;
+ fprintf(fp, " %s\n", lvl_name);
+ }
+ }
+
+ cleanup:
+ apol_level_query_destroy(&query);
+ apol_vector_destroy(&v);
+ return;
+}
+
+/**
+ * Compare two qpol_cat_datum_t objects.
+ * This function is meant to be passed to apol_vector_compare
+ * as the callback for performing comparisons.
+ *
+ * @param datum1 Reference to a qpol_type_datum_t object
+ * @param datum2 Reference to a qpol_type_datum_t object
+ * @param data Reference to a policy
+ * @return Greater than 0 if the first argument is less than the second argument,
+ * less than 0 if the first argument is greater than the second argument,
+ * 0 if the arguments are equal
+ */
+static int qpol_cat_datum_compare(const void *datum1, const void *datum2, void *data)
+{
+ const qpol_cat_t *cat_datum1 = NULL, *cat_datum2 = NULL;
+ apol_policy_t *policydb = NULL;
+ qpol_policy_t *q;
+ uint32_t val1, val2;
+
+ policydb = (apol_policy_t *) data;
+ q = apol_policy_get_qpol(policydb);
+ assert(policydb);
+
+ if (!datum1 || !datum2)
+ goto exit_err;
+ cat_datum1 = datum1;
+ cat_datum2 = datum2;
+
+ if (qpol_cat_get_value(q, cat_datum1, &val1))
+ goto exit_err;
+ if (qpol_cat_get_value(q, cat_datum2, &val2))
+ goto exit_err;
+
+ return (val1 > val2) ? 1 : ((val1 == val2) ? 0 : -1);
+
+ exit_err:
+ assert(0);
+ return 0;
+}
+
+/**
+ * Compare two qpol_level_datum_t objects.
+ * This function is meant to be passed to apol_vector_compare
+ * as the callback for performing comparisons.
+ *
+ * @param datum1 Reference to a qpol_level_datum_t object
+ * @param datum2 Reference to a qpol_level_datum_t object
+ * @param data Reference to a policy
+ * @return Greater than 0 if the first argument is less than the second argument,
+ * less than 0 if the first argument is greater than the second argument,
+ * 0 if the arguments are equal
+ */
+static int qpol_level_datum_compare(const void *datum1, const void *datum2, void *data)
+{
+ const qpol_level_t *lvl_datum1 = NULL, *lvl_datum2 = NULL;
+ apol_policy_t *policydb = NULL;
+ qpol_policy_t *q;
+ uint32_t val1, val2;
+
+ policydb = (apol_policy_t *) data;
+ assert(policydb);
+ q = apol_policy_get_qpol(policydb);
+
+ if (!datum1 || !datum2)
+ goto exit_err;
+ lvl_datum1 = datum1;
+ lvl_datum2 = datum2;
+
+ if (qpol_level_get_value(q, lvl_datum1, &val1))
+ goto exit_err;
+ if (qpol_level_get_value(q, lvl_datum2, &val2))
+ goto exit_err;
+
+ return (val1 > val2) ? 1 : ((val1 == val2) ? 0 : -1);
+
+ exit_err:
+ assert(0);
+ return 0;
+}
diff --git a/secmds/sesearch.c b/secmds/sesearch.c
new file mode 100644
index 0000000..ec0315f
--- /dev/null
+++ b/secmds/sesearch.c
@@ -0,0 +1,1173 @@
+/**
+ * @file
+ * Command line tool to search TE rules.
+ *
+ * @author Frank Mayer mayerf@tresys.com
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Paul Rosenfeld prosenfeld@tresys.com
+ *
+ * Copyright (C) 2003-2009 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+/* libapol */
+#include <apol/policy.h>
+#include <apol/policy-query.h>
+#include <apol/render.h>
+#include <apol/util.h>
+#include <apol/vector.h>
+
+/* libqpol*/
+#include <qpol/policy.h>
+#include <qpol/policy_extend.h>
+#include <qpol/syn_rule_query.h>
+#include <qpol/util.h>
+
+/* other */
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2003-2009 Tresys Technology, LLC"
+
+static char *policy_file = NULL;
+
+enum opt_values
+{
+ RULE_NEVERALLOW = 256, RULE_AUDIT, RULE_AUDITALLOW, RULE_DONTAUDIT,
+ RULE_ROLE_ALLOW, RULE_ROLE_TRANS, RULE_RANGE_TRANS, RULE_ALL,
+ EXPR_ROLE_SOURCE, EXPR_ROLE_TARGET
+};
+
+static struct option const longopts[] = {
+ {"allow", no_argument, NULL, 'A'},
+ {"neverallow", no_argument, NULL, RULE_NEVERALLOW},
+ {"audit", no_argument, NULL, RULE_AUDIT},
+ {"auditallow", no_argument, NULL, RULE_AUDITALLOW},
+ {"dontaudit", no_argument, NULL, RULE_DONTAUDIT},
+ {"type", no_argument, NULL, 'T'},
+ {"role_allow", no_argument, NULL, RULE_ROLE_ALLOW},
+ {"role_trans", no_argument, NULL, RULE_ROLE_TRANS},
+ {"range_trans", no_argument, NULL, RULE_RANGE_TRANS},
+ {"all", no_argument, NULL, RULE_ALL},
+
+ {"source", required_argument, NULL, 's'},
+ {"target", required_argument, NULL, 't'},
+ {"role_source", required_argument, NULL, EXPR_ROLE_SOURCE},
+ {"role_target", required_argument, NULL, EXPR_ROLE_TARGET},
+ {"class", required_argument, NULL, 'c'},
+ {"perm", required_argument, NULL, 'p'},
+ {"bool", required_argument, NULL, 'b'},
+
+ {"direct", no_argument, NULL, 'd'},
+ {"regex", no_argument, NULL, 'R'},
+ {"linenum", no_argument, NULL, 'n'},
+ {"semantic", no_argument, NULL, 'S'},
+ {"show_cond", no_argument, NULL, 'C'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+typedef struct options
+{
+ char *src_name;
+ char *tgt_name;
+ char *src_role_name;
+ char *tgt_role_name;
+ char *class_name;
+ char *permlist;
+ char *bool_name;
+ apol_vector_t *class_vector;
+ bool all;
+ bool lineno;
+ bool semantic;
+ bool indirect;
+ bool allow;
+ bool nallow;
+ bool auditallow;
+ bool dontaudit;
+ bool type;
+ bool rtrans;
+ bool role_allow;
+ bool role_trans;
+ bool useregex;
+ bool show_cond;
+ apol_vector_t *perm_vector;
+} options_t;
+
+void usage(const char *program_name, int brief)
+{
+ printf("Usage: %s [OPTIONS] RULE_TYPE [RULE_TYPE ...] [EXPESSION] [POLICY ...]\n\n", program_name);
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", program_name);
+ return;
+ }
+ printf("Search the rules in a SELinux policy.\n\n");
+ printf("RULE_TYPES:\n");
+ printf(" -A, --allow allow rules\n");
+ printf(" --neverallow neverallow rules\n");
+ printf(" --auditallow auditallow rules\n");
+ printf(" --dontaudit dontaudit rules\n");
+ printf(" -T, --type type_trans, type_member, and type_change\n");
+ printf(" --role_allow role allow rules\n");
+ printf(" --role_trans role_transition rules\n");
+ printf(" --range_trans range_transition rules\n");
+ printf(" --all all rules regardless of type, class, or perms\n");
+ printf("EXPRESSIONS:\n");
+ printf(" -s NAME, --source=NAME rules with type/attribute NAME as source\n");
+ printf(" -t NAME, --target=NAME rules with type/attribute NAME as target\n");
+ printf(" --role_source=NAME rules with role NAME as source\n");
+ printf(" --role_target=NAME rules with role NAME as target\n");
+ printf(" -c NAME, --class=NAME rules with class NAME as the object class\n");
+ printf(" -p P1[,P2,...], --perm=P1[,P2...]\n");
+ printf(" rules with the specified permission\n");
+ printf(" -b NAME, --bool=NAME conditional rules with NAME in the expression\n");
+ printf("OPTIONS:\n");
+ printf(" -d, --direct do not search for type's attributes\n");
+ printf(" -R, --regex use regular expression matching\n");
+ printf(" -n, --linenum show line number for each rule if available\n");
+ printf(" -S, --semantic search rules semantically instead of syntactically\n");
+ printf(" -C, --show_cond show conditional expression for conditional rules\n");
+ printf(" -h, --help print this help text and exit\n");
+ printf(" -V, --version print version information and exit\n");
+ printf("\n");
+ printf("If no expression is specified, then all rules are shown.\n");
+ printf("\n");
+ printf("The default source policy, or if that is unavailable the default binary\n");
+ printf("policy, will be opened if no policy is provided.\n\n");
+}
+
+static int perform_av_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v)
+{
+ apol_avrule_query_t *avq = NULL;
+ unsigned int rules = 0;
+ int error = 0;
+ char *tmp = NULL, *tok = NULL, *s = NULL;
+
+ if (!policy || !opt || !v) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!opt->all && !opt->allow && !opt->nallow && !opt->auditallow && !opt->dontaudit) {
+ *v = NULL;
+ return 0; /* no search to do */
+ }
+
+ avq = apol_avrule_query_create();
+ if (!avq) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (opt->allow || opt->all)
+ rules |= QPOL_RULE_ALLOW;
+ if (opt->nallow || opt->all) // Add this regardless of policy capabilities
+ rules |= QPOL_RULE_NEVERALLOW;
+ if (opt->auditallow || opt->all)
+ rules |= QPOL_RULE_AUDITALLOW;
+ if (opt->dontaudit || opt->all)
+ rules |= QPOL_RULE_DONTAUDIT;
+ if (rules != 0) // Setting rules = 0 means you want all the rules
+ apol_avrule_query_set_rules(policy, avq, rules);
+ apol_avrule_query_set_regex(policy, avq, opt->useregex);
+ if (opt->src_name)
+ apol_avrule_query_set_source(policy, avq, opt->src_name, opt->indirect);
+ if (opt->tgt_name)
+ apol_avrule_query_set_target(policy, avq, opt->tgt_name, opt->indirect);
+ if (opt->bool_name)
+ apol_avrule_query_set_bool(policy, avq, opt->bool_name);
+ if (opt->class_name) {
+ if (opt->class_vector == NULL) {
+ if (apol_avrule_query_append_class(policy, avq, opt->class_name)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ for (size_t i = 0; i < apol_vector_get_size(opt->class_vector); ++i) {
+ char *class_name;
+ class_name = apol_vector_get_element(opt->class_vector, i);
+ if (!class_name)
+ continue;
+ if (apol_avrule_query_append_class(policy, avq, class_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ }
+
+ if (opt->permlist) {
+ tmp = strdup(opt->permlist);
+ for (tok = strtok(tmp, ","); tok; tok = strtok(NULL, ",")) {
+ if (apol_avrule_query_append_perm(policy, avq, tok)) {
+ error = errno;
+ goto err;
+ }
+ if ((s = strdup(tok)) == NULL || apol_vector_append(opt->perm_vector, s) < 0) {
+ error = errno;
+ goto err;
+ }
+ s = NULL;
+ }
+ free(tmp);
+ }
+
+ if (!(opt->semantic) && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) {
+ if (apol_syn_avrule_get_by_query(policy, avq, v)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ if (apol_avrule_get_by_query(policy, avq, v)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ apol_avrule_query_destroy(&avq);
+ return 0;
+
+ err:
+ apol_vector_destroy(v);
+ apol_avrule_query_destroy(&avq);
+ free(tmp);
+ free(s);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+}
+
+static void print_syn_av_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i, num_rules = 0;
+ const apol_vector_t *syn_list = NULL;
+ const qpol_syn_avrule_t *rule = NULL;
+ char *tmp = NULL, *rule_str = NULL, *expr = NULL;
+ char enable_char = ' ', branch_char = ' ';
+ const qpol_cond_t *cond = NULL;
+ uint32_t enabled = 0, is_true = 0;
+ unsigned long lineno = 0;
+
+ if (!policy || !v)
+ return;
+
+ syn_list = v;
+ if (!(num_rules = apol_vector_get_size(syn_list)))
+ goto cleanup;
+
+ fprintf(stdout, "Found %zd syntactic av rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ rule = apol_vector_get_element(syn_list, i);
+ enable_char = branch_char = ' ';
+ if (opt->show_cond) {
+ if (qpol_syn_avrule_get_cond(q, rule, &cond))
+ goto cleanup;
+ if (cond) {
+ if (qpol_syn_avrule_get_is_enabled(q, rule, &enabled) < 0 || qpol_cond_eval(q, cond, &is_true) < 0)
+ goto cleanup;
+ tmp = apol_cond_expr_render(policy, cond);
+ enable_char = (enabled ? 'E' : 'D');
+ branch_char = ((is_true && enabled) || (!is_true && !enabled) ? 'T' : 'F');
+ asprintf(&expr, "[ %s ]", tmp);
+ free(tmp);
+ tmp = NULL;
+ if (!expr)
+ goto cleanup;
+ }
+ }
+ if (!(rule_str = apol_syn_avrule_render(policy, rule)))
+ goto cleanup;
+ if (opt->lineno) {
+ if (qpol_syn_avrule_get_lineno(q, rule, &lineno))
+ goto cleanup;
+ fprintf(stdout, "%c%c [%7lu] %s %s\n", enable_char, branch_char, lineno, rule_str, expr ? expr : "");
+ } else {
+ fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : "");
+ }
+ free(rule_str);
+ rule_str = NULL;
+ free(expr);
+ expr = NULL;
+ }
+
+ cleanup:
+ free(tmp);
+ free(rule_str);
+ free(expr);
+}
+
+static void print_av_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i, num_rules = 0;
+ const qpol_avrule_t *rule = NULL;
+ char *tmp = NULL, *rule_str = NULL, *expr = NULL;
+ char enable_char = ' ', branch_char = ' ';
+ qpol_iterator_t *iter = NULL;
+ const qpol_cond_t *cond = NULL;
+ uint32_t enabled = 0, list = 0;
+
+ if (!policy || !v)
+ return;
+
+ if (!(num_rules = apol_vector_get_size(v)))
+ return;
+
+ fprintf(stdout, "Found %zd semantic av rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ enable_char = branch_char = ' ';
+ if (!(rule = apol_vector_get_element(v, i)))
+ goto cleanup;
+ if (opt->show_cond) {
+ if (qpol_avrule_get_cond(q, rule, &cond))
+ goto cleanup;
+ if (qpol_avrule_get_is_enabled(q, rule, &enabled))
+ goto cleanup;
+ if (cond) {
+ if (qpol_avrule_get_which_list(q, rule, &list))
+ goto cleanup;
+ tmp = apol_cond_expr_render(policy, cond);
+ qpol_iterator_destroy(&iter);
+ enable_char = (enabled ? 'E' : 'D');
+ branch_char = (list ? 'T' : 'F');
+ asprintf(&expr, "[ %s ]", tmp);
+ free(tmp);
+ tmp = NULL;
+ if (!expr)
+ goto cleanup;
+ }
+ }
+ if (!(rule_str = apol_avrule_render(policy, rule)))
+ goto cleanup;
+ fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : "");
+ free(rule_str);
+ rule_str = NULL;
+ free(expr);
+ expr = NULL;
+ }
+
+ cleanup:
+ free(tmp);
+ free(rule_str);
+ free(expr);
+}
+
+static int perform_te_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v)
+{
+ apol_terule_query_t *teq = NULL;
+ unsigned int rules = 0;
+ int error = 0;
+
+ if (!policy || !opt || !v) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (opt->all || opt->type) {
+ rules = (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER);
+ } else {
+ *v = NULL;
+ return 0; /* no search to do */
+ }
+
+ teq = apol_terule_query_create();
+ if (!teq) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ apol_terule_query_set_rules(policy, teq, rules);
+ apol_terule_query_set_regex(policy, teq, opt->useregex);
+ if (opt->src_name)
+ apol_terule_query_set_source(policy, teq, opt->src_name, opt->indirect);
+ if (opt->tgt_name)
+ apol_terule_query_set_target(policy, teq, opt->tgt_name, opt->indirect);
+ if (opt->bool_name)
+ apol_terule_query_set_bool(policy, teq, opt->bool_name);
+ if (opt->class_name) {
+ if (opt->class_vector == NULL) {
+ if (apol_terule_query_append_class(policy, teq, opt->class_name)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ for (size_t i = 0; i < apol_vector_get_size(opt->class_vector); ++i) {
+ char *class_name;
+ class_name = apol_vector_get_element(opt->class_vector, i);
+ if (!class_name)
+ continue;
+ if (apol_terule_query_append_class(policy, teq, class_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ }
+
+ if (!(opt->semantic) && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) {
+ if (apol_syn_terule_get_by_query(policy, teq, v)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ if (apol_terule_get_by_query(policy, teq, v)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ apol_terule_query_destroy(&teq);
+ return 0;
+
+ err:
+ apol_vector_destroy(v);
+ apol_terule_query_destroy(&teq);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+}
+
+static void print_syn_te_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i, num_rules = 0;
+ const apol_vector_t *syn_list = NULL;
+ const qpol_syn_terule_t *rule = NULL;
+ char *tmp = NULL, *rule_str = NULL, *expr = NULL;
+ char enable_char = ' ', branch_char = ' ';
+ const qpol_cond_t *cond = NULL;
+ uint32_t enabled = 0, is_true = 0;
+ unsigned long lineno = 0;
+
+ if (!policy || !v)
+ return;
+
+ syn_list = v;
+ if (!(num_rules = apol_vector_get_size(syn_list)))
+ goto cleanup;
+
+ fprintf(stdout, "Found %zd syntactic te rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ rule = apol_vector_get_element(syn_list, i);
+ enable_char = branch_char = ' ';
+ if (opt->show_cond) {
+ if (qpol_syn_terule_get_cond(q, rule, &cond))
+ goto cleanup;
+ if (cond) {
+ if (qpol_syn_terule_get_is_enabled(q, rule, &enabled) < 0 || qpol_cond_eval(q, cond, &is_true) < 0)
+ goto cleanup;
+ tmp = apol_cond_expr_render(policy, cond);
+ enable_char = (enabled ? 'E' : 'D');
+ branch_char = ((is_true && enabled) || (!is_true && !enabled) ? 'T' : 'F');
+ asprintf(&expr, "[ %s ]", tmp);
+ free(tmp);
+ tmp = NULL;
+ if (!expr)
+ break;
+ }
+ }
+ if (!(rule_str = apol_syn_terule_render(policy, rule)))
+ goto cleanup;
+ if (opt->lineno) {
+ if (qpol_syn_terule_get_lineno(q, rule, &lineno))
+ goto cleanup;
+ fprintf(stdout, "%c%c [%7lu] %s %s\n", enable_char, branch_char, lineno, rule_str, expr ? expr : "");
+ } else {
+ fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : "");
+ }
+ free(rule_str);
+ rule_str = NULL;
+ free(expr);
+ expr = NULL;
+ }
+
+ cleanup:
+ free(tmp);
+ free(rule_str);
+ free(expr);
+}
+
+static void print_te_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v)
+{
+ qpol_policy_t *q = apol_policy_get_qpol(policy);
+ size_t i, num_rules = 0;
+ const qpol_terule_t *rule = NULL;
+ char *tmp = NULL, *rule_str = NULL, *expr = NULL;
+ char enable_char = ' ', branch_char = ' ';
+ qpol_iterator_t *iter = NULL;
+ const qpol_cond_t *cond = NULL;
+ uint32_t enabled = 0, list = 0;
+
+ if (!policy || !v)
+ goto cleanup;
+
+ if (!(num_rules = apol_vector_get_size(v)))
+ goto cleanup;
+
+ fprintf(stdout, "Found %zd semantic te rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ enable_char = branch_char = ' ';
+ if (!(rule = apol_vector_get_element(v, i)))
+ goto cleanup;
+ if (opt->show_cond) {
+ if (qpol_terule_get_cond(q, rule, &cond))
+ goto cleanup;
+ if (qpol_terule_get_is_enabled(q, rule, &enabled))
+ goto cleanup;
+ if (cond) {
+ if (qpol_terule_get_which_list(q, rule, &list))
+ goto cleanup;
+ if (qpol_cond_get_expr_node_iter(q, cond, &iter))
+ goto cleanup;
+ tmp = apol_cond_expr_render(policy, cond);
+ qpol_iterator_destroy(&iter);
+ enable_char = (enabled ? 'E' : 'D');
+ branch_char = (list ? 'T' : 'F');
+ asprintf(&expr, "[ %s ]", tmp);
+ free(tmp);
+ tmp = NULL;
+ if (!expr)
+ goto cleanup;
+ }
+ }
+ if (!(rule_str = apol_terule_render(policy, rule)))
+ goto cleanup;
+ fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : "");
+ free(rule_str);
+ rule_str = NULL;
+ free(expr);
+ expr = NULL;
+ }
+
+ cleanup:
+ free(tmp);
+ free(rule_str);
+ free(expr);
+}
+
+static int perform_ra_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v)
+{
+ apol_role_allow_query_t *raq = NULL;
+ int error = 0;
+
+ if (!policy || !opt || !v) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!opt->role_allow && !opt->all) {
+ *v = NULL;
+ return 0; /* no search to do */
+ }
+
+ raq = apol_role_allow_query_create();
+ if (!raq) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ apol_role_allow_query_set_regex(policy, raq, opt->useregex);
+ if (opt->src_role_name) {
+ if (apol_role_allow_query_set_source(policy, raq, opt->src_role_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (opt->tgt_role_name)
+ if (apol_role_allow_query_set_target(policy, raq, opt->tgt_role_name)) {
+ error = errno;
+ goto err;
+ }
+
+ if (apol_role_allow_get_by_query(policy, raq, v)) {
+ error = errno;
+ goto err;
+ }
+
+ apol_role_allow_query_destroy(&raq);
+ return 0;
+
+ err:
+ apol_vector_destroy(v);
+ apol_role_allow_query_destroy(&raq);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+}
+
+static void print_ra_results(const apol_policy_t * policy, const options_t * opt __attribute__ ((unused)), const apol_vector_t * v)
+{
+ size_t i, num_rules = 0;
+ const qpol_role_allow_t *rule = NULL;
+ char *tmp = NULL;
+
+ if (!policy || !v)
+ return;
+
+ if (!(num_rules = apol_vector_get_size(v)))
+ return;
+
+ fprintf(stdout, "Found %zd role allow rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ if (!(rule = apol_vector_get_element(v, i)))
+ break;
+ if (!(tmp = apol_role_allow_render(policy, rule)))
+ break;
+ fprintf(stdout, " %s\n", tmp);
+ free(tmp);
+ tmp = NULL;
+ }
+}
+
+static int perform_rt_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v)
+{
+ apol_role_trans_query_t *rtq = NULL;
+ int error = 0;
+
+ if (!policy || !opt || !v) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!opt->role_trans && !opt->all) {
+ *v = NULL;
+ return 0; /* no search to do */
+ }
+
+ rtq = apol_role_trans_query_create();
+ if (!rtq) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ apol_role_trans_query_set_regex(policy, rtq, opt->useregex);
+ if (opt->src_role_name) {
+ if (apol_role_trans_query_set_source(policy, rtq, opt->src_role_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (opt->tgt_name) {
+ if (apol_role_trans_query_set_target(policy, rtq, opt->tgt_name, opt->indirect)) {
+ error = errno;
+ goto err;
+ }
+ }
+
+ if (apol_role_trans_get_by_query(policy, rtq, v)) {
+ error = errno;
+ goto err;
+ }
+
+ apol_role_trans_query_destroy(&rtq);
+ return 0;
+
+ err:
+ apol_vector_destroy(v);
+ apol_role_trans_query_destroy(&rtq);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+}
+
+static void print_rt_results(const apol_policy_t * policy, const options_t * opt __attribute__ ((unused)), const apol_vector_t * v)
+{
+ size_t i, num_rules = 0;
+ const qpol_role_trans_t *rule = NULL;
+ char *tmp = NULL;
+
+ if (!policy || !v)
+ return;
+
+ if (!(num_rules = apol_vector_get_size(v)))
+ return;
+
+ fprintf(stdout, "Found %zd role_transition rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ if (!(rule = apol_vector_get_element(v, i)))
+ break;
+ if (!(tmp = apol_role_trans_render(policy, rule)))
+ break;
+ fprintf(stdout, " %s\n", tmp);
+ free(tmp);
+ tmp = NULL;
+ }
+}
+
+static int perform_range_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v)
+{
+ apol_range_trans_query_t *rtq = NULL;
+ int error = 0;
+
+ if (!policy || !opt || !v) {
+ ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!opt->rtrans && !opt->all) {
+ *v = NULL;
+ return 0; /* no search to do */
+ }
+
+ rtq = apol_range_trans_query_create();
+ if (!rtq) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ apol_range_trans_query_set_regex(policy, rtq, opt->useregex);
+ if (opt->src_name) {
+ if (apol_range_trans_query_set_source(policy, rtq, opt->src_name, opt->indirect)) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (opt->tgt_name) {
+ if (apol_range_trans_query_set_target(policy, rtq, opt->tgt_name, opt->indirect)) {
+ error = errno;
+ goto err;
+ }
+ }
+ if (opt->class_name) {
+ if (opt->class_vector == NULL) {
+ if (apol_range_trans_query_append_class(policy, rtq, opt->class_name)) {
+ error = errno;
+ goto err;
+ }
+ } else {
+ for (size_t i = 0; i < apol_vector_get_size(opt->class_vector); ++i) {
+ char *class_name;
+ class_name = apol_vector_get_element(opt->class_vector, i);
+ if (!class_name)
+ continue;
+ if (apol_range_trans_query_append_class(policy, rtq, class_name)) {
+ error = errno;
+ goto err;
+ }
+ }
+ }
+ }
+
+ if (apol_range_trans_get_by_query(policy, rtq, v)) {
+ error = errno;
+ goto err;
+ }
+
+ apol_range_trans_query_destroy(&rtq);
+ return 0;
+
+ err:
+ apol_vector_destroy(v);
+ apol_range_trans_query_destroy(&rtq);
+ ERR(policy, "%s", strerror(error));
+ errno = error;
+ return -1;
+}
+
+static void print_range_results(const apol_policy_t * policy, const options_t * opt
+ __attribute__ ((unused)), const apol_vector_t * v)
+{
+ size_t i, num_rules = 0;
+ const qpol_range_trans_t *rule = NULL;
+ char *tmp = NULL;
+
+ if (!policy || !v)
+ return;
+
+ if (!(num_rules = apol_vector_get_size(v)))
+ return;
+
+ fprintf(stdout, "Found %zd range_transition rules:\n", num_rules);
+
+ for (i = 0; i < num_rules; i++) {
+ if (!(rule = apol_vector_get_element(v, i)))
+ break;
+ if (!(tmp = apol_range_trans_render(policy, rule)))
+ break;
+ fprintf(stdout, " %s\n", tmp);
+ free(tmp);
+ tmp = NULL;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ options_t cmd_opts;
+ int optc, rt = -1;
+
+ apol_policy_t *policy = NULL;
+ apol_vector_t *v = NULL;
+ apol_policy_path_t *pol_path = NULL;
+ apol_vector_t *mod_paths = NULL;
+ apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+
+ memset(&cmd_opts, 0, sizeof(cmd_opts));
+ cmd_opts.indirect = true;
+ while ((optc = getopt_long(argc, argv, "ATs:t:c:p:b:dRnSChV", longopts, NULL)) != -1) {
+ switch (optc) {
+ case 0:
+ break;
+ case 's': /* source */
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing source type/attribute for -s (--source)\n");
+ exit(1);
+ }
+ cmd_opts.src_name = strdup(optarg);
+ if (!cmd_opts.src_name) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case 't': /* target */
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing target type/attribute for -t (--target)\n");
+ exit(1);
+ }
+ cmd_opts.tgt_name = strdup(optarg);
+ if (!cmd_opts.tgt_name) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case EXPR_ROLE_SOURCE:
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing source role for --role_source\n");
+ exit(1);
+ }
+ cmd_opts.src_role_name = strdup(optarg);
+ if (!cmd_opts.src_role_name) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case EXPR_ROLE_TARGET:
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing target role for --role_target\n");
+ exit(1);
+ }
+ cmd_opts.tgt_role_name = strdup(optarg);
+ if (!cmd_opts.tgt_role_name) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case 'c': /* class */
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing object class for -c (--class)\n");
+ exit(1);
+ }
+ cmd_opts.class_name = strdup(optarg);
+ if (!cmd_opts.class_name) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case 'p': /* permission */
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing permissions for -p (--perm)\n");
+ exit(1);
+ }
+ if ((cmd_opts.permlist = strdup(optarg)) == NULL
+ || (cmd_opts.perm_vector = apol_vector_create(free)) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case 'b':
+ if (optarg == 0) {
+ usage(argv[0], 1);
+ printf("Missing boolean for -b (--bool)\n");
+ exit(1);
+ }
+ cmd_opts.bool_name = strdup(optarg);
+ if (!cmd_opts.bool_name) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+ case 'd': /* direct search */
+ cmd_opts.indirect = false;
+ break;
+ case 'R': /* use regex */
+ cmd_opts.useregex = true;
+ break;
+ case 'A': /* allow */
+ cmd_opts.allow = true;
+ break;
+ case RULE_NEVERALLOW: /* neverallow */
+ cmd_opts.nallow = true;
+ break;
+ case RULE_AUDIT: /* audit */
+ cmd_opts.auditallow = true;
+ cmd_opts.dontaudit = true;
+ fprintf(stderr, "Use of --audit is deprecated; use --auditallow and --dontaudit instead.\n");
+ break;
+ case RULE_AUDITALLOW:
+ cmd_opts.auditallow = true;
+ break;
+ case RULE_DONTAUDIT:
+ cmd_opts.dontaudit = true;
+ break;
+ case 'T': /* type */
+ cmd_opts.type = true;
+ break;
+ case RULE_ROLE_ALLOW:
+ cmd_opts.role_allow = true;
+ break;
+ case RULE_ROLE_TRANS:
+ cmd_opts.role_trans = true;
+ break;
+ case RULE_RANGE_TRANS: /* range transition */
+ cmd_opts.rtrans = true;
+ break;
+ case RULE_ALL: /* all */
+ cmd_opts.all = true;
+ break;
+ case 'n': /* lineno */
+ cmd_opts.lineno = true;
+ break;
+ case 'S': /* semantic */
+ cmd_opts.semantic = true;
+ break;
+ case 'C':
+ cmd_opts.show_cond = true;
+ break;
+ case 'h': /* help */
+ usage(argv[0], 0);
+ exit(0);
+ case 'V': /* version */
+ printf("sesearch %s\n%s\n", VERSION, COPYRIGHT_INFO);
+ exit(0);
+ default:
+ usage(argv[0], 1);
+ exit(1);
+ }
+ }
+
+ if (!(cmd_opts.allow || cmd_opts.nallow || cmd_opts.auditallow || cmd_opts.dontaudit || cmd_opts.role_allow ||
+ cmd_opts.type || cmd_opts.rtrans || cmd_opts.role_trans || cmd_opts.all)) {
+ usage(argv[0], 1);
+ fprintf(stderr, "One of --all, --allow, --neverallow, --auditallow, --dontaudit,\n"
+ "--range_trans, --type, --role_allow, or --role_trans must be specified.\n");
+ exit(1);
+ }
+
+ int pol_opt = 0;
+ if (!(cmd_opts.nallow || cmd_opts.all))
+ pol_opt |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+
+ if (argc - optind < 1) {
+ rt = qpol_default_policy_find(&policy_file);
+ if (rt < 0) {
+ fprintf(stderr, "Default policy search failed: %s\n", strerror(errno));
+ exit(1);
+ } else if (rt != 0) {
+ fprintf(stderr, "No default policy found.\n");
+ exit(1);
+ }
+ pol_opt |= QPOL_POLICY_OPTION_MATCH_SYSTEM;
+ } else {
+ if ((policy_file = strdup(argv[optind])) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ exit(1);
+ }
+ optind++;
+ }
+
+ if (argc - optind > 0) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ if (!(mod_paths = apol_vector_create(NULL))) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ exit(1);
+ }
+ for (; argc - optind; optind++) {
+ if (apol_vector_append(mod_paths, (void *)argv[optind])) {
+ ERR(policy, "Error loading module %s", argv[optind]);
+ apol_vector_destroy(&mod_paths);
+ free(policy_file);
+ exit(1);
+ }
+ }
+ } else if (apol_file_is_policy_path_list(policy_file) > 0) {
+ pol_path = apol_policy_path_create_from_file(policy_file);
+ if (!pol_path) {
+ ERR(policy, "%s", "invalid policy list");
+ free(policy_file);
+ exit(1);
+ }
+ }
+
+ if (!pol_path)
+ pol_path = apol_policy_path_create(path_type, policy_file, mod_paths);
+ if (!pol_path) {
+ ERR(policy, "%s", strerror(ENOMEM));
+ free(policy_file);
+ apol_vector_destroy(&mod_paths);
+ exit(1);
+ }
+ free(policy_file);
+ apol_vector_destroy(&mod_paths);
+
+ policy = apol_policy_create_from_policy_path(pol_path, pol_opt, NULL, NULL);
+ if (!policy) {
+ ERR(policy, "%s", strerror(errno));
+ apol_policy_path_destroy(&pol_path);
+ exit(1);
+ }
+ /* handle regex for class name */
+ if (cmd_opts.useregex && cmd_opts.class_name != NULL) {
+ cmd_opts.class_vector = apol_vector_create(NULL);
+ apol_vector_t *qpol_matching_classes = NULL;
+ apol_class_query_t *regex_match_query = apol_class_query_create();
+ apol_class_query_set_regex(policy, regex_match_query, 1);
+ apol_class_query_set_class(policy, regex_match_query, cmd_opts.class_name);
+ if (apol_class_get_by_query(policy, regex_match_query, &qpol_matching_classes)) {
+ apol_class_query_destroy(&regex_match_query);
+ goto cleanup;
+ }
+ const qpol_class_t *class = NULL;
+ for (size_t i = 0; i < apol_vector_get_size(qpol_matching_classes); ++i) {
+ const char *class_name;
+ class = apol_vector_get_element(qpol_matching_classes, i);
+ if (!class)
+ break;
+ qpol_class_get_name(apol_policy_get_qpol(policy), class, &class_name);
+ apol_vector_append(cmd_opts.class_vector, (void *)class_name);
+ }
+ if (!apol_vector_get_size(qpol_matching_classes)) {
+ apol_vector_destroy(&qpol_matching_classes);
+ apol_class_query_destroy(&regex_match_query);
+ ERR(policy, "No classes match expression %s", cmd_opts.class_name);
+ goto cleanup;
+ }
+ apol_vector_destroy(&qpol_matching_classes);
+ apol_class_query_destroy(&regex_match_query);
+ }
+
+ if (!cmd_opts.semantic && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) {
+ if (qpol_policy_build_syn_rule_table(apol_policy_get_qpol(policy))) {
+ apol_policy_destroy(&policy);
+ exit(1);
+ }
+ }
+
+ /* if syntactic rules are not available always do semantic search */
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) {
+ cmd_opts.semantic = 1;
+ }
+
+ /* supress line numbers if doing semantic search or not available */
+ if (cmd_opts.semantic || !qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_LINE_NUMBERS)) {
+ cmd_opts.lineno = 0;
+ }
+
+ if (perform_av_query(policy, &cmd_opts, &v)) {
+ rt = 1;
+ goto cleanup;
+ }
+ if (v) {
+ if (!cmd_opts.semantic && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES))
+ print_syn_av_results(policy, &cmd_opts, v);
+ else
+ print_av_results(policy, &cmd_opts, v);
+ fprintf(stdout, "\n");
+ }
+ apol_vector_destroy(&v);
+ if (perform_te_query(policy, &cmd_opts, &v)) {
+ rt = 1;
+ goto cleanup;
+ }
+ if (v) {
+ if (!cmd_opts.semantic && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES))
+ print_syn_te_results(policy, &cmd_opts, v);
+ else
+ print_te_results(policy, &cmd_opts, v);
+ fprintf(stdout, "\n");
+ }
+ apol_vector_destroy(&v);
+ if (perform_ra_query(policy, &cmd_opts, &v)) {
+ rt = 1;
+ goto cleanup;
+ }
+ if (v) {
+ print_ra_results(policy, &cmd_opts, v);
+ fprintf(stdout, "\n");
+ }
+ apol_vector_destroy(&v);
+ if (perform_rt_query(policy, &cmd_opts, &v)) {
+ rt = 1;
+ goto cleanup;
+ }
+ if (v) {
+ print_rt_results(policy, &cmd_opts, v);
+ fprintf(stdout, "\n");
+ }
+ apol_vector_destroy(&v);
+ if (perform_range_query(policy, &cmd_opts, &v)) {
+ rt = 1;
+ goto cleanup;
+ }
+ if (v) {
+ print_range_results(policy, &cmd_opts, v);
+ fprintf(stdout, "\n");
+ }
+ apol_vector_destroy(&v);
+ rt = 0;
+ cleanup:
+ apol_policy_destroy(&policy);
+ apol_policy_path_destroy(&pol_path);
+ free(cmd_opts.src_name);
+ free(cmd_opts.tgt_name);
+ free(cmd_opts.class_name);
+ free(cmd_opts.permlist);
+ free(cmd_opts.bool_name);
+ free(cmd_opts.src_role_name);
+ free(cmd_opts.tgt_role_name);
+ apol_vector_destroy(&cmd_opts.perm_vector);
+ apol_vector_destroy(&cmd_opts.class_vector);
+ exit(rt);
+}
diff --git a/sediff/Makefile.am b/sediff/Makefile.am
new file mode 100644
index 0000000..3f53cd3
--- /dev/null
+++ b/sediff/Makefile.am
@@ -0,0 +1,54 @@
+setoolsdir = @setoolsdir@
+
+dist_setools_DATA = sediff_help.txt sediffx.glade \
+ sediffx.png sediffx-small.png
+
+if BUILD_GUI
+ MAYBE_SEDIFFX = sediffx
+endif
+
+bin_PROGRAMS = sediff $(MAYBE_SEDIFFX)
+
+AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \
+ @QPOL_CFLAGS@ @APOL_CFLAGS@ @POLDIFF_CFLAGS@
+AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@
+
+LDADD = @SELINUX_LIB_FLAG@ @POLDIFF_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@
+
+sediff_CFLAGS = $(AM_CFLAGS)
+sediffx_CFLAGS = $(AM_CFLAGS) \
+ @GTK_CFLAGS@ @PIXBUF_CFLAGS@ @GLADE_CFLAGS@ @GTHREAD_CFLAGS@
+
+# need the -rdynamic flag below - glade uses dlopen() upon sediffx callbacks
+sediffx_LDFLAGS = $(AM_LDFLAGS) \
+ @GTK_LIBS@ @PIXBUF_LIBS@ @GLADE_LIBS@ @GTHREAD_LIBS@ @XML_LIBS@ \
+ -rdynamic
+
+DEPENDENCIES = $(top_builddir)/libpoldiff/src/libpoldiff.so \
+ $(top_builddir)/libapol/src/libapol.so \
+ $(top_builddir)/libqpol/src/libqpol.so
+
+sediff_SOURCES = sediff.c
+
+sediffx_SOURCES = \
+ find_dialog.c find_dialog.h \
+ open_policies_dialog.c open_policies_dialog.h \
+ policy_view.c policy_view.h \
+ progress.c progress.h \
+ remap_types_dialog.c remap_types_dialog.h \
+ result_item.c result_item.h \
+ result_item_render.c result_item_render.h \
+ results.c results.h \
+ select_diff_dialog.c select_diff_dialog.h \
+ toplevel.c toplevel.h \
+ utilgui.c utilgui.h \
+ sediffx.c sediffx.h
+
+$(top_builddir)/libpoldiff/src/libpoldiff.so:
+ $(MAKE) -C $(top_builddir)/libpoldiff/src $(notdir $@)
+
+$(top_builddir)/libapol/src/libapol.so:
+ $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@)
+
+$(top_builddir)/libqpol/src/libqpol.so:
+ $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@)
diff --git a/sediff/find_dialog.c b/sediff/find_dialog.c
new file mode 100644
index 0000000..7bc3c07
--- /dev/null
+++ b/sediff/find_dialog.c
@@ -0,0 +1,184 @@
+/**
+ * @file
+ * Display a dialog to let the user search through results.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "find_dialog.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <gdk/gdkkeysyms.h>
+#include <glade/glade.h>
+
+struct find_dialog
+{
+ toplevel_t *top;
+ GladeXML *xml;
+ /** offset to start searching from if searching up and the
+ * start of the offset to select from */
+ gint start_offset;
+ /** the offset to start searching from if searching down and
+ * the offset to end selecting from */
+ gint end_offset;
+ /** the main window */
+ GtkDialog *w;
+ GtkEntry *entry;
+ GtkRadioButton *forward, *reverse;
+};
+
+static void find_dialog_search(find_dialog_t * f)
+{
+ GtkTextView *view = toplevel_get_text_view(f->top);
+ GtkTextBuffer *tb = gtk_text_view_get_buffer(view);
+ GtkTextMark *mark = gtk_text_buffer_get_insert(tb);
+ GtkTextIter iter, start, end;
+ const gchar *search_text = gtk_entry_get_text(f->entry);
+ gboolean text_found;
+ /* if nothing is selected then start the search from the
+ * cursor. otherwise, if searching forward then start search
+ * at the end of the selection, else set start to beginning of
+ * selection */
+ if (!gtk_text_buffer_get_selection_bounds(tb, &start, &end)) {
+ gtk_text_buffer_get_iter_at_mark(tb, &iter, mark);
+ } else {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->forward))) {
+ iter = end;
+ } else {
+ iter = start;
+ }
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->forward))) {
+ text_found = gtk_text_iter_forward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL);
+ if (!text_found) {
+ /* wrap search */
+ gtk_text_buffer_get_start_iter(tb, &iter);
+ text_found =
+ gtk_text_iter_forward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL);
+ }
+ } else {
+ text_found = gtk_text_iter_backward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL);
+ if (!text_found) {
+ /* wrap search */
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ text_found =
+ gtk_text_iter_backward_search(&iter, search_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &start, &end, NULL);
+ }
+ }
+ if (!text_found) {
+ GString *string = g_string_new("");
+ g_string_printf(string, "Text \"%s\" not found.", search_text);
+ util_message(GTK_WINDOW(f->w), GTK_MESSAGE_INFO, string->str);
+ g_string_free(string, TRUE);
+ } else {
+ gtk_text_view_scroll_to_iter(view, &start, 0.0, FALSE, 0.0, 0.5);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->forward))) {
+ gtk_text_buffer_select_range(tb, &end, &start);
+ } else {
+ gtk_text_buffer_select_range(tb, &start, &end);
+ }
+ }
+}
+
+static void find_dialog_on_response(GtkDialog * dialog __attribute__ ((unused)), gint response, gpointer user_data)
+{
+ find_dialog_t *f = (find_dialog_t *) user_data;
+ if (response == GTK_RESPONSE_CLOSE) {
+ gtk_widget_hide(GTK_WIDGET(f->w));
+ } else if (response == GTK_RESPONSE_OK) {
+ find_dialog_search(f);
+ }
+}
+
+static gboolean find_dialog_on_key_press_event(GtkWidget * widget __attribute__ ((unused)), GdkEventKey * event, gpointer user_data)
+{
+ find_dialog_t *f = (find_dialog_t *) user_data;
+ if (event->keyval == GDK_Escape) {
+ gtk_widget_hide(GTK_WIDGET(f->w));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void find_dialog_on_entry_activate(GtkEntry * entry __attribute__ ((unused)), gpointer user_data)
+{
+ find_dialog_t *f = (find_dialog_t *) user_data;
+ find_dialog_search(f);
+}
+
+find_dialog_t *find_dialog_create(toplevel_t * top)
+{
+ find_dialog_t *f;
+ int error = 0;
+
+ if ((f = calloc(1, sizeof(*f))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ f->top = top;
+ f->xml = glade_xml_new(toplevel_get_glade_xml(f->top), "find_dialog", NULL);
+
+ f->w = GTK_DIALOG(glade_xml_get_widget(f->xml, "find_dialog"));
+ assert(f->w != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(f->w), toplevel_get_window(f->top));
+ g_signal_connect_swapped(G_OBJECT(f->w), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), f->w);
+ g_signal_connect(G_OBJECT(f->w), "response", G_CALLBACK(find_dialog_on_response), f);
+ g_signal_connect(G_OBJECT(f->w), "key-press-event", G_CALLBACK(find_dialog_on_key_press_event), f);
+
+ f->entry = GTK_ENTRY(glade_xml_get_widget(f->xml, "find entry"));
+ f->forward = GTK_RADIO_BUTTON(glade_xml_get_widget(f->xml, "find forward radio"));
+ f->reverse = GTK_RADIO_BUTTON(glade_xml_get_widget(f->xml, "find reverse radio"));
+ assert(f->entry != NULL && f->forward != NULL && f->reverse != NULL);
+
+ /* connect the text entry callback events */
+ g_signal_connect(G_OBJECT(f->entry), "activate", G_CALLBACK(find_dialog_on_entry_activate), f);
+
+ cleanup:
+ if (error != 0) {
+ find_dialog_destroy(&f);
+ errno = error;
+ return NULL;
+ }
+ return f;
+}
+
+void find_dialog_destroy(find_dialog_t ** f)
+{
+ if (f != NULL && *f != NULL) {
+ free(*f);
+ *f = NULL;
+ }
+}
+
+void find_dialog_show(find_dialog_t * f)
+{
+ gtk_widget_show(GTK_WIDGET(f->w));
+ gtk_widget_grab_focus(GTK_WIDGET(f->entry));
+ gtk_editable_set_position(GTK_EDITABLE(f->entry), -1);
+ gtk_editable_select_region(GTK_EDITABLE(f->entry), 0, -1);
+ gtk_window_present(GTK_WINDOW(f->w));
+}
diff --git a/sediff/find_dialog.h b/sediff/find_dialog.h
new file mode 100644
index 0000000..a6628b6
--- /dev/null
+++ b/sediff/find_dialog.h
@@ -0,0 +1,63 @@
+/**
+ * @file
+ * Headers for displaying a find dialog.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FIND_DIALOG_H
+#define FIND_DIALOG_H
+
+typedef struct find_dialog find_dialog_t;
+
+#include "toplevel.h"
+
+/**
+ * Create a find dialog object. The dialog will float above the rest
+ * of sediffx; it allows the user to search for text either forwards
+ * or backwards in the currently visible text buffer.
+ *
+ * @param top Toplevel object that will control the newly opened find
+ * dialog.
+ *
+ * @return An initialized find dialog object, or NULL upon error. The
+ * caller must call find_dialog_destroy() upon the returned value.
+ */
+find_dialog_t *find_dialog_create(toplevel_t * top);
+
+/**
+ * Destroy the find_dialog object. This does nothing if the pointer
+ * is set to NULL.
+ *
+ * @param f Reference to a find dialog object. Afterwards the
+ * pointer will be set to NULL.
+ */
+void find_dialog_destroy(find_dialog_t ** f);
+
+/**
+ * (Re)show the find dialog.
+ *
+ * @param f Find dialog to show.
+ */
+void find_dialog_show(find_dialog_t * f);
+
+#endif
diff --git a/sediff/open_policies_dialog.c b/sediff/open_policies_dialog.c
new file mode 100644
index 0000000..482b95f
--- /dev/null
+++ b/sediff/open_policies_dialog.c
@@ -0,0 +1,527 @@
+/**
+ * @file
+ * Run the dialog to allow the user to open a policy.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "open_policies_dialog.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/util.h>
+#include <glade/glade.h>
+
+struct open_policy_pane
+{
+ struct open_policy *op;
+ GtkRadioButton *monolithic_radio, *modular_radio;
+ GtkLabel *main_label;
+
+ GtkHBox *bottom_hbox;
+ GtkListStore *module_store;
+
+ GtkEntry *base_entry;
+ GtkButton *base_browse_button;
+
+ GtkTreeView *module_view;
+ GtkButton *add_button, *remove_button, *import_button, *export_button;
+ char *last_module_path;
+};
+
+struct open_policy
+{
+ GladeXML *xml;
+ toplevel_t *top;
+ GtkDialog *dialog;
+ GtkButton *ok_button, *rundiff_button;
+ struct open_policy_pane pane[SEDIFFX_POLICY_NUM];
+};
+
+enum module_columns
+{
+ PATH_COLUMN = 0, NAME_COLUMN, VERSION_COLUMN, NUM_COLUMNS
+};
+
+/**
+ * Sort columns in alphabetical order.
+ */
+static gint open_policy_sort(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data)
+{
+ GValue value_a = { 0 }, value_b = {
+ 0};
+ const char *name_a, *name_b;
+ int retval, column_id = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get_value(model, a, column_id, &value_a);
+ gtk_tree_model_get_value(model, b, column_id, &value_b);
+ name_a = g_value_get_string(&value_a);
+ name_b = g_value_get_string(&value_b);
+ retval = strcmp(name_a, name_b);
+ g_value_unset(&value_a);
+ g_value_unset(&value_b);
+ return retval;
+}
+
+static void open_policy_init_pane(struct open_policy *op, sediffx_policy_e which, const char *suffix)
+{
+ struct open_policy_pane *pane = op->pane + which;
+ GString *s = g_string_new(NULL);
+ GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+
+ g_string_printf(s, "monolithic radio%s", suffix);
+ pane->monolithic_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ g_string_printf(s, "modular radio%s", suffix);
+ pane->modular_radio = GTK_RADIO_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ g_string_printf(s, "main filename label%s", suffix);
+ pane->main_label = GTK_LABEL(glade_xml_get_widget(op->xml, s->str));
+ assert(pane->monolithic_radio != NULL && pane->modular_radio != NULL && pane->main_label != NULL);
+
+ g_string_printf(s, "base entry%s", suffix);
+ pane->base_entry = GTK_ENTRY(glade_xml_get_widget(op->xml, s->str));
+ g_string_printf(s, "base browse%s", suffix);
+ pane->base_browse_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ assert(pane->base_entry != NULL && pane->base_browse_button != NULL);
+
+ g_string_printf(s, "hbox.3%s", suffix);
+ pane->bottom_hbox = GTK_HBOX(glade_xml_get_widget(op->xml, s->str));
+ assert(pane->bottom_hbox != NULL);
+
+ g_string_printf(s, "module view%s", suffix);
+ pane->module_view = GTK_TREE_VIEW(glade_xml_get_widget(op->xml, s->str));
+ assert(pane->module_view != NULL);
+ pane->module_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model(pane->module_view, GTK_TREE_MODEL(pane->module_store));
+
+ g_string_printf(s, "module add button%s", suffix);
+ pane->add_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ g_string_printf(s, "module remove button%s", suffix);
+ pane->remove_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ g_string_printf(s, "module list import button%s", suffix);
+ pane->import_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ g_string_printf(s, "module list export button%s", suffix);
+ pane->export_button = GTK_BUTTON(glade_xml_get_widget(op->xml, s->str));
+ assert(pane->add_button != NULL && pane->remove_button != NULL &&
+ pane->import_button != NULL && pane->export_button != NULL);
+
+ g_string_free(s, TRUE);
+
+ selection = gtk_tree_view_get_selection(pane->module_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+
+ column = gtk_tree_view_column_new_with_attributes("Module", renderer, "text", NAME_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NAME_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(pane->module_view, column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), NAME_COLUMN, open_policy_sort,
+ (gpointer) NAME_COLUMN, NULL);
+
+ column = gtk_tree_view_column_new_with_attributes("Version", renderer, "text", VERSION_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, VERSION_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(pane->module_view, column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), VERSION_COLUMN, open_policy_sort,
+ (gpointer) VERSION_COLUMN, NULL);
+
+ column = gtk_tree_view_column_new_with_attributes("Path", renderer, "text", PATH_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PATH_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(pane->module_view, column);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(pane->module_store), PATH_COLUMN, open_policy_sort,
+ (gpointer) PATH_COLUMN, NULL);
+
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(pane->module_store), NAME_COLUMN, GTK_SORT_ASCENDING);
+}
+
+static void open_policy_init_widgets(struct open_policy *op)
+{
+ op->dialog = GTK_DIALOG(glade_xml_get_widget(op->xml, "PoliciesOpenWindow"));
+ assert(op->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(op->dialog), toplevel_get_window(op->top));
+ op->ok_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "ok button"));
+ op->rundiff_button = GTK_BUTTON(glade_xml_get_widget(op->xml, "rundiff button"));
+ assert(op->ok_button != NULL && op->rundiff_button != NULL);
+
+ open_policy_init_pane(op, SEDIFFX_POLICY_ORIG, "");
+ open_policy_init_pane(op, SEDIFFX_POLICY_MOD, " 1");
+}
+
+static void open_policy_on_policy_type_toggle(GtkToggleButton * widget, gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ /* clicking on the radio buttons emit two toggle signals, one for
+ * the original button and one for the new one. thus only need to
+ * handle half of all signals */
+ if (!gtk_toggle_button_get_active(widget)) {
+ return;
+ }
+ char *prefix;
+ GString *s = g_string_new(NULL);
+ if (pane == &(pane->op->pane[SEDIFFX_POLICY_ORIG])) {
+ prefix = "Original";
+ } else {
+ prefix = "Modified";
+ }
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio))) {
+ gtk_widget_set_sensitive(GTK_WIDGET(pane->bottom_hbox), FALSE);
+ g_string_printf(s, "<b>%s Policy Filename:</b>", prefix);
+ gtk_label_set_markup(pane->main_label, s->str);
+ } else {
+ gtk_widget_set_sensitive(GTK_WIDGET(pane->bottom_hbox), TRUE);
+ g_string_printf(s, "<b>%s Base Filename:</b>", prefix);
+ gtk_label_set_markup(pane->main_label, s->str);
+ }
+ g_string_free(s, TRUE);
+}
+
+static void open_policy_on_entry_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy *op = (struct open_policy *)user_data;
+ gboolean sens_orig = FALSE, sens_mod = FALSE;
+ if (strcmp(gtk_entry_get_text(op->pane[SEDIFFX_POLICY_ORIG].base_entry), "") != 0) {
+ sens_orig = TRUE;
+ }
+ if (strcmp(gtk_entry_get_text(op->pane[SEDIFFX_POLICY_MOD].base_entry), "") != 0) {
+ sens_mod = TRUE;
+ }
+ gboolean sens = sens_orig && sens_mod;
+ gtk_widget_set_sensitive(GTK_WIDGET(op->pane[SEDIFFX_POLICY_ORIG].export_button), sens_orig);
+ gtk_widget_set_sensitive(GTK_WIDGET(op->pane[SEDIFFX_POLICY_MOD].export_button), sens_mod);
+ gtk_widget_set_sensitive(GTK_WIDGET(op->ok_button), sens);
+ gtk_widget_set_sensitive(GTK_WIDGET(op->rundiff_button), sens);
+}
+
+static void open_policy_on_base_browse_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ const char *current_path = gtk_entry_get_text(pane->base_entry);
+ char *title;
+ apol_vector_t *paths;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio))) {
+ title = "Open Monolithic Policy";
+ } else {
+ title = "Open Modular Policy";
+ }
+ if (strcmp(current_path, "") == 0) {
+ current_path = NULL;
+ }
+ if ((paths = util_open_file(GTK_WINDOW(pane->op->dialog), title, current_path, 0)) == NULL) {
+ return;
+ }
+ gtk_entry_set_text(pane->base_entry, apol_vector_get_element(paths, 0));
+ apol_vector_destroy(&paths);
+}
+
+/**
+ * Attempt to load a module and retrieve its name and version. Upon
+ * success add an entry to the list store.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+static int open_policy_load_module(struct open_policy *op, struct open_policy_pane *pane, const char *path)
+{
+ const char *module_name, *version_string;
+ int module_type;
+ qpol_module_t *module = NULL;
+ GtkTreeIter iter;
+
+ /* check if modulue was already loaded */
+ gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pane->module_store), &iter);
+ while (iter_valid) {
+ char *s;
+ gtk_tree_model_get(GTK_TREE_MODEL(pane->module_store), &iter, PATH_COLUMN, &s, -1);
+ if (strcmp(s, path) == 0) {
+ toplevel_ERR(op->top, "Module %s was already added.", path);
+ return -1;
+ }
+ iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(pane->module_store), &iter);
+ }
+ if ((qpol_module_create_from_file(path, &module)) < 0) {
+ toplevel_ERR(op->top, "Error opening module %s: %s", path, strerror(errno));
+ return -1;
+ }
+ if (qpol_module_get_name(module, &module_name) < 0 ||
+ qpol_module_get_version(module, &version_string) < 0 || qpol_module_get_type(module, &module_type) < 0) {
+ toplevel_ERR(op->top, "Error reading module %s: %s", path, strerror(errno));
+ qpol_module_destroy(&module);
+ return -1;
+ }
+ if (module_type != QPOL_MODULE_OTHER) {
+ toplevel_ERR(op->top, "%s is not a loadable module.", path);
+ qpol_module_destroy(&module);
+ return -1;
+ }
+ gtk_list_store_append(pane->module_store, &iter);
+ gtk_list_store_set(pane->module_store, &iter, PATH_COLUMN, path, NAME_COLUMN, module_name, VERSION_COLUMN, version_string,
+ -1);
+ qpol_module_destroy(&module);
+ return 0;
+}
+
+static void open_policy_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ apol_vector_t *paths;
+ const char *path = NULL, *prev_path;
+ size_t i;
+ if ((prev_path = pane->last_module_path) == NULL) {
+ prev_path = gtk_entry_get_text(pane->base_entry);
+ if (strcmp(prev_path, "") == 0) {
+ prev_path = NULL;
+ }
+ }
+ paths = util_open_file(GTK_WINDOW(pane->op->dialog), "Open Module", prev_path, 1);
+ if (paths == NULL) {
+ return;
+ }
+ int all_succeed = 1;
+ for (i = 0; i < apol_vector_get_size(paths); i++) {
+ path = apol_vector_get_element(paths, i);
+ if (open_policy_load_module(pane->op, pane, path) < 0) {
+ all_succeed = 0;
+ }
+ }
+ if (all_succeed) {
+ assert(path != NULL);
+ free(pane->last_module_path);
+ pane->last_module_path = strdup(path);
+ }
+ apol_vector_destroy(&paths);
+}
+
+static void open_policy_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(pane->module_view);
+ GtkTreeIter iter;
+ char *path;
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return;
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(pane->module_store), &iter, 0, &path, -1);
+ gtk_list_store_remove(pane->module_store, &iter);
+}
+
+static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data);
+static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data);
+
+static void open_policy_on_selection_change(GtkTreeSelection * selection, gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL);
+ gtk_widget_set_sensitive(GTK_WIDGET(pane->remove_button), sens);
+}
+
+static void open_policy_init_signals(struct open_policy *op)
+{
+ sediffx_policy_e i;
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ struct open_policy_pane *pane = op->pane + i;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(pane->module_view);
+ g_signal_connect(pane->monolithic_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), pane);
+ g_signal_connect(pane->modular_radio, "toggled", G_CALLBACK(open_policy_on_policy_type_toggle), pane);
+ g_signal_connect(pane->base_entry, "event-after", G_CALLBACK(open_policy_on_entry_event_after), op);
+ g_signal_connect(pane->base_browse_button, "clicked", G_CALLBACK(open_policy_on_base_browse_click), pane);
+ g_signal_connect(selection, "changed", G_CALLBACK(open_policy_on_selection_change), pane);
+ g_signal_connect(pane->add_button, "clicked", G_CALLBACK(open_policy_on_add_click), pane);
+ g_signal_connect(pane->remove_button, "clicked", G_CALLBACK(open_policy_on_remove_click), pane);
+ g_signal_connect(pane->import_button, "clicked", G_CALLBACK(open_policy_on_import_click), pane);
+ g_signal_connect(pane->export_button, "clicked", G_CALLBACK(open_policy_on_export_click), pane);
+ }
+}
+
+static void open_policy_init_value(struct open_policy *op, const apol_policy_path_t * path, struct open_policy_pane *pane)
+{
+ apol_policy_path_type_e path_type = apol_policy_path_get_type(path);
+ const char *primary_path = apol_policy_path_get_primary(path);
+ gtk_entry_set_text(pane->base_entry, primary_path);
+ gtk_list_store_clear(pane->module_store);
+ if (path_type == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pane->monolithic_radio), TRUE);
+ } else if (path_type == APOL_POLICY_PATH_TYPE_MODULAR) {
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t i;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pane->modular_radio), TRUE);
+ for (i = 0; i < apol_vector_get_size(modules); i++) {
+ char *module_path = apol_vector_get_element(modules, i);
+ if (open_policy_load_module(op, pane, module_path) < 0) {
+ break;
+ }
+ }
+ } else {
+ /* should never get here */
+ toplevel_ERR(pane->op->top, "Unknown policy path type %d.", path_type);
+ }
+}
+
+static void open_policy_init_values(struct open_policy *op, const apol_policy_path_t * orig_path,
+ const apol_policy_path_t * mod_path)
+{
+ if (orig_path != NULL) {
+ open_policy_init_value(op, orig_path, op->pane + SEDIFFX_POLICY_ORIG);
+ }
+ if (mod_path != NULL) {
+ open_policy_init_value(op, mod_path, op->pane + SEDIFFX_POLICY_MOD);
+ }
+}
+
+/**
+ * Build the policy path corresponding to the user's inputs on this
+ * dialog.
+ *
+ * @return path for the dialog, or NULL upon error. The caller must
+ * call apol_policy_path_destroy() afterwards.
+ */
+static apol_policy_path_t *open_policy_build_path(struct open_policy_pane *pane)
+{
+ const char *primary_path = gtk_entry_get_text(pane->base_entry);
+ apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ apol_vector_t *modules = NULL;
+ apol_policy_path_t *path = NULL;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pane->modular_radio))) {
+ path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ GtkTreeIter iter;
+ if ((modules = apol_vector_create(free)) == NULL) {
+ toplevel_ERR(pane->op->top, "%s", strerror(errno));
+ return NULL;
+ }
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(pane->module_store), &iter)) {
+ do {
+ GValue value = { 0 };
+ char *module_path;
+ gtk_tree_model_get_value(GTK_TREE_MODEL(pane->module_store), &iter, PATH_COLUMN, &value);
+ module_path = g_value_dup_string(&value);
+ g_value_unset(&value);
+ if (apol_vector_append(modules, module_path) < 0) {
+ toplevel_ERR(pane->op->top, "%s", strerror(errno));
+ free(module_path);
+ apol_vector_destroy(&modules);
+ return NULL;
+ }
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(pane->module_store), &iter));
+ }
+ }
+ path = apol_policy_path_create(path_type, primary_path, modules);
+ apol_vector_destroy(&modules);
+ if (path == NULL) {
+ toplevel_ERR(pane->op->top, "%s", strerror(errno));
+ return NULL;
+ }
+ return path;
+}
+
+static void open_policy_on_import_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ apol_vector_t *paths = NULL;
+ apol_policy_path_t *ppath = NULL;
+ const char *path = NULL, *prev_path;
+ if ((prev_path = pane->last_module_path) == NULL) {
+ prev_path = gtk_entry_get_text(pane->base_entry);
+ if (strcmp(prev_path, "") == 0) {
+ prev_path = NULL;
+ }
+ }
+ paths = util_open_file(GTK_WINDOW(pane->op->dialog), "Import Policy List", prev_path, 1);
+ if (paths == NULL) {
+ return;
+ }
+ path = apol_vector_get_element(paths, 0);
+ ppath = apol_policy_path_create_from_file(path);
+ if (ppath == NULL) {
+ toplevel_ERR(pane->op->top, "Error importing policy list %s: %s", path, strerror(errno));
+ goto cleanup;
+ }
+ open_policy_init_value(pane->op, ppath, pane);
+ free(pane->last_module_path);
+ pane->last_module_path = strdup(path);
+ cleanup:
+ apol_vector_destroy(&paths);
+ apol_policy_path_destroy(&ppath);
+}
+
+static void open_policy_on_export_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct open_policy_pane *pane = (struct open_policy_pane *)user_data;
+ char *path = util_save_file(GTK_WINDOW(pane->op->dialog), "Export Policy List", NULL);
+ apol_policy_path_t *ppath = NULL;
+ if (path == NULL) {
+ return;
+ }
+ ppath = open_policy_build_path(pane);
+ if (ppath == NULL) {
+ goto cleanup;
+ }
+ if (apol_policy_path_to_file(ppath, path) < 0) {
+ toplevel_ERR(pane->op->top, "Error exporting policy list %s: %s", path, strerror(errno));
+ }
+ cleanup:
+ g_free(path);
+ apol_policy_path_destroy(&ppath);
+}
+
+void open_policies_dialog_run(toplevel_t * top, const apol_policy_path_t * orig_path, const apol_policy_path_t * mod_path)
+{
+ struct open_policy op;
+ gint response;
+ apol_policy_path_t *input[SEDIFFX_POLICY_NUM];
+
+ memset(&op, 0, sizeof(op));
+ op.top = top;
+ op.xml = glade_xml_new(toplevel_get_glade_xml(top), "PoliciesOpenWindow", NULL);
+ op.pane[SEDIFFX_POLICY_ORIG].op = &op;
+ op.pane[SEDIFFX_POLICY_MOD].op = &op;
+
+ open_policy_init_widgets(&op);
+ open_policy_init_signals(&op);
+ open_policy_init_values(&op, orig_path, mod_path);
+ open_policy_on_entry_event_after(NULL, NULL, &op);
+
+ while (1) {
+ response = gtk_dialog_run(op.dialog);
+ if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) {
+ break;
+ }
+ if ((input[SEDIFFX_POLICY_ORIG] = open_policy_build_path(&op.pane[SEDIFFX_POLICY_ORIG])) == NULL ||
+ (input[SEDIFFX_POLICY_MOD] = open_policy_build_path(&op.pane[SEDIFFX_POLICY_MOD])) == NULL) {
+ continue;
+ }
+ if (toplevel_open_policies(op.top, input[SEDIFFX_POLICY_ORIG], input[SEDIFFX_POLICY_MOD]) == 0) {
+ break;
+ }
+ }
+ gtk_widget_destroy(GTK_WIDGET(op.dialog));
+ free(op.pane[SEDIFFX_POLICY_ORIG].last_module_path);
+ free(op.pane[SEDIFFX_POLICY_MOD].last_module_path);
+ g_object_unref(op.pane[SEDIFFX_POLICY_ORIG].module_store);
+ g_object_unref(op.pane[SEDIFFX_POLICY_MOD].module_store);
+
+ if (response == 0) {
+ /* Run Diff button was clicked */
+ toplevel_run_diff(op.top);
+ }
+}
diff --git a/sediff/open_policies_dialog.h b/sediff/open_policies_dialog.h
new file mode 100644
index 0000000..c00291a
--- /dev/null
+++ b/sediff/open_policies_dialog.h
@@ -0,0 +1,45 @@
+/**
+ * @file
+ * Dialog that allows the user to select two policies, each either a
+ * monolithic policy or a base policy + list of modules. The dialog
+ * then attempts to open those policies.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OPEN_POLICIES_DIALOG_H
+#define OPEN_POLICIES_DIALOG_H
+
+#include "toplevel.h"
+#include <apol/policy-path.h>
+
+/**
+ * Display and run a dialog that allows the user open two policies,
+ * each either a monolithic or a modular policy.
+ *
+ * @param top Toplevel for the application.
+ * @param orig_path If not NULL, the default path for the original
+ * policy.
+ * @param mod_path If not NULL, the default path for the modified
+ * policy.
+ */
+void open_policies_dialog_run(toplevel_t * top, const apol_policy_path_t * orig_path, const apol_policy_path_t * mod_path);
+
+#endif
diff --git a/sediff/policy_view.c b/sediff/policy_view.c
new file mode 100644
index 0000000..bc733a7
--- /dev/null
+++ b/sediff/policy_view.c
@@ -0,0 +1,391 @@
+/**
+ * @file
+ * Routines are responsible calculating a policy's statistics and
+ * displaying its source.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "policy_view.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <glade/glade.h>
+
+struct policy_view
+{
+ toplevel_t *top;
+ sediffx_policy_e which;
+ GladeXML *xml;
+ GtkTextBuffer *stats, *source;
+ GtkTextView *stats_view, *source_view;
+ GtkNotebook *notebook;
+ GtkLabel *line_number;
+ void *mmap_start;
+ size_t mmap_length;
+};
+
+static const char *policy_view_widget_names[SEDIFFX_POLICY_NUM][3] = {
+ {"toplevel policy_orig stats text", "toplevel policy_orig source text",
+ "toplevel policy_orig notebook"},
+ {"toplevel policy_mod stats text", "toplevel policy_mod source text",
+ "toplevel policy_mod notebook"}
+};
+
+/**
+ * As the user moves the cursor within the source policy text view
+ * update the bottom label with the line number. Once that view is
+ * hidden, by flipping to a different tab, then clear the label.
+ */
+static void policy_view_notebook_on_event_after(GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)), gpointer user_data)
+{
+ policy_view_t *view = (policy_view_t *) user_data;
+ guint main_pagenum, view_pagenum;
+ GtkTextMark *mark = NULL;
+ GtkTextIter iter;
+
+ main_pagenum = toplevel_get_notebook_page(view->top);
+ view_pagenum = gtk_notebook_get_current_page(view->notebook);
+ if (main_pagenum == 1 + view->which && view_pagenum == 1) {
+ mark = gtk_text_buffer_get_insert(view->source);
+ if (mark != NULL) {
+ GString *string = g_string_new("");
+ gtk_text_buffer_get_iter_at_mark(view->source, &iter, mark);
+ g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1);
+ gtk_label_set_text(view->line_number, string->str);
+ g_string_free(string, TRUE);
+ }
+ } else {
+ gtk_label_set_text(view->line_number, "");
+ }
+}
+
+policy_view_t *policy_view_create(toplevel_t * top, sediffx_policy_e which)
+{
+ policy_view_t *view;
+ GtkTextTag *mono_tag;
+ int error = 0;
+
+ if ((view = calloc(1, sizeof(*view))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ view->top = top;
+ view->which = which;
+
+ view->xml = glade_get_widget_tree(GTK_WIDGET(toplevel_get_window(view->top)));
+ view->stats = gtk_text_buffer_new(NULL);
+ view->source = gtk_text_buffer_new(NULL);
+ mono_tag = gtk_text_buffer_create_tag(view->source, "mono-tag",
+ "style", PANGO_STYLE_NORMAL,
+ "weight", PANGO_WEIGHT_NORMAL, "family", "monospace", NULL);
+
+ view->stats_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][0]));
+ view->source_view = GTK_TEXT_VIEW(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][1]));
+ assert(view->stats_view != NULL && view->source_view != NULL);
+ gtk_text_view_set_buffer(view->stats_view, view->stats);
+ gtk_text_view_set_buffer(view->source_view, view->source);
+
+ view->notebook = GTK_NOTEBOOK(glade_xml_get_widget(view->xml, policy_view_widget_names[view->which][2]));
+ view->line_number = GTK_LABEL(glade_xml_get_widget(view->xml, "toplevel line label"));
+ assert(view->notebook != NULL && view->line_number != NULL);
+ g_signal_connect_after(G_OBJECT(view->notebook), "event-after", G_CALLBACK(policy_view_notebook_on_event_after), view);
+
+ cleanup:
+ if (error != 0) {
+ policy_view_destroy(&view);
+ errno = error;
+ return NULL;
+ }
+ return view;
+}
+
+void policy_view_destroy(policy_view_t ** view)
+{
+ if (view != NULL && *view != NULL) {
+ free(*view);
+ *view = NULL;
+ }
+}
+
+/**
+ * Update the policy stats text buffer (and hence its view) to show
+ * statistics about the given policy.
+ *
+ * @param view View to update.
+ * @param p New policy whose statistics to get.
+ * @param path Path to the new policy.
+ */
+static void policy_view_stats_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path)
+{
+ GtkTextIter iter;
+ gchar *contents = NULL;
+ char *path_desc, *tmp = NULL;
+ size_t num_classes = 0,
+ num_commons = 0,
+ num_perms = 0,
+ num_types = 0,
+ num_attribs = 0,
+ num_allow = 0, num_auditallow = 0, num_dontaudit = 0,
+ num_type_change = 0, num_type_member, num_type_trans = 0,
+ num_roles = 0, num_roleallow = 0, num_role_trans = 0, num_users = 0, num_bools = 0;
+ apol_vector_t *vec = NULL;
+ qpol_iterator_t *i = NULL;
+ qpol_policy_t *q = apol_policy_get_qpol(p);
+
+ util_text_buffer_clear(view->stats);
+
+ if ((path_desc = util_policy_path_to_full_string(path)) == NULL) {
+ toplevel_ERR(view->top, "%s", strerror(errno));
+ return;
+ }
+ tmp = apol_policy_get_version_type_mls_str(p);
+ contents = g_strdup_printf("Policy: %s\n" "Policy Version & Type: %s\n", path_desc, tmp);
+ free(path_desc);
+ free(tmp);
+ tmp = NULL;
+ gtk_text_buffer_get_end_iter(view->stats, &iter);
+ gtk_text_buffer_insert(view->stats, &iter, contents, -1);
+ g_free(contents);
+
+ apol_class_get_by_query(p, NULL, &vec);
+ num_classes = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ apol_common_get_by_query(p, NULL, &vec);
+ num_commons = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ apol_perm_get_by_query(p, NULL, &vec);
+ num_perms = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ contents = g_strdup_printf("\nNumber of Classes and Permissions:\n"
+ "\tObject Classes: %zd\n"
+ "\tCommon Classes: %zd\n" "\tPermissions: %zd\n", num_classes, num_commons, num_perms);
+ gtk_text_buffer_insert(view->stats, &iter, contents, -1);
+ g_free(contents);
+
+ apol_type_get_by_query(p, NULL, &vec);
+ num_types = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ apol_attr_get_by_query(p, NULL, &vec);
+ num_attribs = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ contents = g_strdup_printf("\nNumber of Types and Attributes:\n"
+ "\tTypes: %zd\n" "\tAttributes: %zd\n", num_types, num_attribs);
+ gtk_text_buffer_insert(view->stats, &iter, contents, -1);
+ g_free(contents);
+
+ qpol_policy_get_avrule_iter(q, QPOL_RULE_ALLOW, &i);
+ qpol_iterator_get_size(i, &num_allow);
+ qpol_iterator_destroy(&i);
+
+ qpol_policy_get_avrule_iter(q, QPOL_RULE_AUDITALLOW, &i);
+ qpol_iterator_get_size(i, &num_auditallow);
+ qpol_iterator_destroy(&i);
+
+ qpol_policy_get_avrule_iter(q, QPOL_RULE_DONTAUDIT, &i);
+ qpol_iterator_get_size(i, &num_dontaudit);
+ qpol_iterator_destroy(&i);
+
+ qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_CHANGE, &i);
+ qpol_iterator_get_size(i, &num_type_change);
+ qpol_iterator_destroy(&i);
+
+ qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_MEMBER, &i);
+ qpol_iterator_get_size(i, &num_type_member);
+ qpol_iterator_destroy(&i);
+
+ qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_TRANS, &i);
+ qpol_iterator_get_size(i, &num_type_trans);
+ qpol_iterator_destroy(&i);
+
+ contents = g_strdup_printf("\nNumber of Rules:\n"
+ "\tallow: %zd\n"
+ "\tauditallow: %zd\n"
+ "\tdontaudit %zd\n"
+ "\tneverallow: not calculated\n"
+ "\ttype_change: %zd\n"
+ "\ttype_member: %zd\n"
+ "\ttype_transition: %zd\n",
+ num_allow, num_auditallow, num_dontaudit, num_type_change, num_type_member, num_type_trans);
+ gtk_text_buffer_insert(view->stats, &iter, contents, -1);
+ g_free(contents);
+
+ apol_role_get_by_query(p, NULL, &vec);
+ num_roles = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ qpol_policy_get_role_allow_iter(q, &i);
+ qpol_iterator_get_size(i, &num_roleallow);
+ qpol_iterator_destroy(&i);
+
+ qpol_policy_get_role_trans_iter(q, &i);
+ qpol_iterator_get_size(i, &num_roleallow);
+ qpol_iterator_destroy(&i);
+
+ contents = g_strdup_printf("\nNumber of Roles: %zd\n"
+ "\nNumber of RBAC Rules:\n"
+ "\tallow: %zd\n" "\trole_transition %zd\n", num_roles, num_roleallow, num_role_trans);
+ gtk_text_buffer_insert(view->stats, &iter, contents, -1);
+ g_free(contents);
+
+ apol_user_get_by_query(p, NULL, &vec);
+ num_users = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ apol_bool_get_by_query(p, NULL, &vec);
+ num_bools = apol_vector_get_size(vec);
+ apol_vector_destroy(&vec);
+
+ contents = g_strdup_printf("\nNumber of Users: %zd\n" "\nNumber of Booleans: %zd\n", num_users, num_bools);
+ gtk_text_buffer_insert(view->stats, &iter, contents, -1);
+ g_free(contents);
+}
+
+/**
+ * Attempt to load the primary policy into this view's source policy
+ * buffer. If the policy is not a source policy then show an
+ * appropriate message. Otherwise mmap() the policy's contents to the
+ * buffer.
+ *
+ * @param view View whose source buffer to update.
+ * @param policy Policy to show, or NULL if none loaded.
+ * @param path Path to the policy.
+ */
+static void policy_view_source_update(policy_view_t * view, apol_policy_t * p, apol_policy_path_t * path)
+{
+ const char *primary_path;
+ util_text_buffer_clear(view->source);
+
+ /* clear out any old data */
+ if (view->mmap_start != NULL) {
+ munmap(view->mmap_start, view->mmap_length);
+ view->mmap_start = NULL;
+ view->mmap_length = 0;
+ }
+ if (p == NULL) {
+ gtk_text_buffer_set_text(view->source, "No policy has been loaded.", -1);
+ return;
+ }
+ primary_path = apol_policy_path_get_primary(path);
+ if (!qpol_policy_has_capability(apol_policy_get_qpol(p), QPOL_CAP_SOURCE)) {
+ GString *string = g_string_new("");
+ g_string_printf(string, "Policy file %s is not a source policy.", primary_path);
+ gtk_text_buffer_set_text(view->source, string->str, -1);
+ g_string_free(string, TRUE);
+ } else {
+ /* load the policy by mmap()ing the file */
+ struct stat statbuf;
+ int fd;
+
+ if ((fd = open(primary_path, O_RDONLY)) < 0) {
+ toplevel_ERR(view->top, "Could not open %s for reading: %s", primary_path, strerror(errno));
+ return;
+ }
+ if (fstat(fd, &statbuf) < 0) {
+ toplevel_ERR(view->top, "Could not stat %s: %s", primary_path, strerror(errno));
+ close(fd);
+ return;
+ }
+
+ view->mmap_length = statbuf.st_size;
+ if ((view->mmap_start = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+ toplevel_ERR(view->top, "Could not mmap %s: %s", primary_path, strerror(errno));
+ close(fd);
+ view->mmap_start = NULL;
+ return;
+ }
+ close(fd);
+ gtk_text_buffer_set_text(view->source, view->mmap_start, view->mmap_length);
+ }
+}
+
+void policy_view_update(policy_view_t * view, apol_policy_t * policy, apol_policy_path_t * path)
+{
+ policy_view_stats_update(view, policy, path);
+ policy_view_source_update(view, policy, path);
+}
+
+void policy_view_show_policy_line(policy_view_t * view, unsigned long line)
+{
+ GtkTextTagTable *table = NULL;
+ GtkTextIter iter, end_iter;
+ GtkTextMark *mark = NULL;
+ GString *string = g_string_new("");
+
+ gtk_notebook_set_current_page(view->notebook, 1);
+
+ /* when moving the buffer we must use marks to scroll because
+ * goto_line if called before the line height has been
+ * calculated can produce undesired results, in our case we
+ * get no scrolling at all */
+ table = gtk_text_buffer_get_tag_table(view->source);
+ gtk_text_buffer_get_start_iter(view->source, &iter);
+ gtk_text_iter_set_line(&iter, line);
+ gtk_text_buffer_get_start_iter(view->source, &end_iter);
+ gtk_text_iter_set_line(&end_iter, line);
+ while (!gtk_text_iter_ends_line(&end_iter)) {
+ gtk_text_iter_forward_char(&end_iter);
+ }
+
+ mark = gtk_text_buffer_create_mark(view->source, "line-position", &iter, TRUE);
+ assert(mark);
+ gtk_text_view_scroll_to_mark(view->source_view, mark, 0.0, TRUE, 0.0, 0.5);
+
+ /* destroying the mark and recreating is faster than doing a
+ * move on a mark that still exists, so we always destroy it
+ * once we're done */
+ gtk_text_buffer_delete_mark(view->source, mark);
+ gtk_text_view_set_cursor_visible(view->source_view, TRUE);
+ gtk_text_buffer_place_cursor(view->source, &iter);
+ gtk_text_buffer_select_range(view->source, &iter, &end_iter);
+
+ gtk_container_set_focus_child(GTK_CONTAINER(view->notebook), GTK_WIDGET(view->source_view));
+
+ g_string_printf(string, "Line: %d", gtk_text_iter_get_line(&iter) + 1);
+ gtk_label_set_text(view->line_number, string->str);
+ g_string_free(string, TRUE);
+}
+
+GtkTextView *policy_view_get_text_view(policy_view_t * view)
+{
+ gint pagenum = gtk_notebook_get_current_page(view->notebook);
+ if (pagenum == 0) {
+ return view->stats_view;
+ } else {
+ return view->source_view;
+ }
+}
diff --git a/sediff/policy_view.h b/sediff/policy_view.h
new file mode 100644
index 0000000..ee2c400
--- /dev/null
+++ b/sediff/policy_view.h
@@ -0,0 +1,84 @@
+/**
+ * @file
+ * Header for routines related to showing the parts of a policy --
+ * its statistics and its source, if available.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef POLICY_VIEW_H
+#define POLICY_VIEW_H
+
+#include "sediffx.h"
+#include "toplevel.h"
+
+typedef struct policy_view policy_view_t;
+
+/**
+ * Allocate and return an instance of a policy view object. This
+ * object is responsible for showing the statistics and policy source
+ * (if available) for a particular policy.
+ *
+ * @param top Toplevel object containing view's widgets.
+ * @param which Which policy to show.
+ *
+ * @return An initialized policy_view object, or NULL upon error. The
+ * caller must call policy_view_destroy() afterwards.
+ */
+policy_view_t *policy_view_create(toplevel_t * top, sediffx_policy_e which);
+
+/**
+ * Deallocate all space associated with the referenced view. This
+ * does nothing if the pointer is already NULL.
+ *
+ * @param view Reference to a policy_view to destroy. Afterwards the
+ * pointer will be set to NULL.
+ */
+void policy_view_destroy(policy_view_t ** view);
+
+/**
+ * Direct the given policy view to update its display, to reflect the
+ * policy currently loaded. Note that this should be called prior to
+ * building the poldiff_t object, for poldiff_create() will take
+ * ownership of the policy.
+ *
+ * @parav view View to update.
+ */
+void policy_view_update(policy_view_t * view, apol_policy_t * policy, apol_policy_path_t * path);
+
+/**
+ * Direct the given policy view to show its source policy tab and then
+ * scroll to the given line number. Line numbers are zero indexed.
+ *
+ * @param view View whose source tab to show.
+ * @param line Line to show.
+ */
+void policy_view_show_policy_line(policy_view_t * view, unsigned long line);
+
+/**
+ * Get the currently showing text view for the policy view.
+ *
+ * @param view View containing text view.
+ *
+ * @return Currently visible text view.
+ */
+GtkTextView *policy_view_get_text_view(policy_view_t * view);
+
+#endif
diff --git a/sediff/progress.c b/sediff/progress.c
new file mode 100644
index 0000000..efaa120
--- /dev/null
+++ b/sediff/progress.c
@@ -0,0 +1,202 @@
+/**
+ * @file
+ * Routines to show a progress dialog, indicating that the
+ * application is doing something.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "progress.h"
+#include "utilgui.h"
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+struct progress
+{
+ toplevel_t *top;
+ GtkWidget *progress;
+ GtkWidget *label1, *label2;
+ char *s;
+ int done;
+ GCond *cond;
+ GMutex *mutex;
+};
+
+progress_t *progress_create(toplevel_t * top)
+{
+ progress_t *p;
+ GtkWidget *vbox;
+
+ if ((p = calloc(1, sizeof(*p))) == NULL) {
+ return NULL;
+ }
+ p->top = top;
+ p->progress = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_modal(GTK_WINDOW(p->progress), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(p->progress), toplevel_get_window(top));
+ gtk_window_set_default_size(GTK_WINDOW(p->progress), 300, 100);
+ vbox = gtk_vbox_new(FALSE, 2);
+ gtk_container_add(GTK_CONTAINER(p->progress), vbox);
+ p->label1 = gtk_label_new(NULL);
+ gtk_container_add(GTK_CONTAINER(vbox), p->label1);
+ p->label2 = gtk_label_new(NULL);
+ gtk_container_add(GTK_CONTAINER(vbox), p->label2);
+ gtk_widget_show(p->label1);
+ gtk_widget_show(p->label2);
+ gtk_widget_show(vbox);
+ util_cursor_wait(p->progress);
+ p->cond = g_cond_new();
+ p->mutex = g_mutex_new();
+ return p;
+}
+
+void progress_destroy(progress_t ** progress)
+{
+ if (progress != NULL && *progress != NULL) {
+ free((*progress)->s);
+ g_cond_free((*progress)->cond);
+ g_mutex_free((*progress)->mutex);
+ free(*progress);
+ *progress = NULL;
+ }
+}
+
+void progress_show(progress_t * progress, const char *title)
+{
+ gtk_label_set_text(GTK_LABEL(progress->label1), title);
+ gtk_label_set_text(GTK_LABEL(progress->label2), "");
+ gtk_widget_show(progress->progress);
+ gtk_window_deiconify(GTK_WINDOW(progress->progress));
+ gtk_window_set_title(GTK_WINDOW(progress->progress), title);
+ progress->done = 0;
+}
+
+void progress_hide(progress_t * progress)
+{
+ gtk_widget_hide(progress->progress);
+}
+
+int progress_wait(progress_t * progress)
+{
+ GTimeVal wait_time = { 0, 50000 };
+ g_mutex_lock(progress->mutex);
+ while (!progress->done) {
+ if (progress->s != NULL) {
+ gtk_label_set_text(GTK_LABEL(progress->label2), progress->s);
+ free(progress->s);
+ progress->s = NULL;
+ }
+ // only process one event -- the policy source could
+ // still be loading, and this dialog should not block
+ // until the entire source has been read
+ gtk_main_iteration_do(FALSE);
+ g_cond_timed_wait(progress->cond, progress->mutex, &wait_time);
+ }
+ g_mutex_unlock(progress->mutex);
+ if (progress->done < 0) {
+ toplevel_ERR(progress->top, GTK_LABEL(progress->label2)->label);
+ return progress->done;
+ } else if (progress->done > 1) {
+ toplevel_WARN(progress->top, GTK_LABEL(progress->label2)->label);
+ return progress->done - 1;
+ } else {
+ progress->done = 0;
+ return 0;
+ }
+}
+
+void progress_done(progress_t * progress)
+{
+ g_mutex_lock(progress->mutex);
+ progress->done = 1;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+void progress_warn(progress_t * progress, char *reason, ...)
+{
+ gchar *s;
+ va_list ap;
+ g_mutex_lock(progress->mutex);
+ if (reason != NULL) {
+ va_start(ap, reason);
+ g_vasprintf(&s, reason, ap);
+ free(progress->s);
+ progress->s = s;
+ va_end(ap);
+ }
+ progress->done = 2;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+void progress_abort(progress_t * progress, char *reason, ...)
+{
+ gchar *s;
+ va_list ap;
+ g_mutex_lock(progress->mutex);
+ if (reason != NULL) {
+ va_start(ap, reason);
+ g_vasprintf(&s, reason, ap);
+ free(progress->s);
+ progress->s = s;
+ va_end(ap);
+ }
+ progress->done = -1;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+static void progress_update_label(progress_t * progress, const char *fmt, va_list va_args)
+{
+ gchar *s = NULL;
+ g_vasprintf(&s, fmt, va_args);
+ g_mutex_lock(progress->mutex);
+ free(progress->s);
+ progress->s = s;
+ g_cond_signal(progress->cond);
+ g_mutex_unlock(progress->mutex);
+}
+
+void progress_update(progress_t * progress, char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ progress_update_label(progress, fmt, ap);
+ va_end(ap);
+}
+
+void progress_poldiff_handle_func(void *arg, const poldiff_t * diff __attribute__ ((unused)), int level
+ __attribute__ ((unused)), const char *fmt, va_list va_args)
+{
+ progress_t *progress = arg;
+ progress_update_label(progress, fmt, va_args);
+}
+
+void progress_apol_handle_func(void *varg, const apol_policy_t * p __attribute__ ((unused)), int level
+ __attribute__ ((unused)), const char *fmt, va_list argp)
+{
+ progress_t *progress = varg;
+ progress_update_label(progress, fmt, argp);
+}
diff --git a/sediff/progress.h b/sediff/progress.h
new file mode 100644
index 0000000..43432d7
--- /dev/null
+++ b/sediff/progress.h
@@ -0,0 +1,139 @@
+/**
+ * @file
+ * Header for showing progress dialogs.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef PROGRESS_H
+#define PROGRESS_H
+
+typedef struct progress progress_t;
+
+#include "toplevel.h"
+
+#include <apol/policy.h>
+#include <poldiff/poldiff.h>
+
+/**
+ * Allocate and return a new progress dialog object.
+ *
+ * @param top Toplevel object that will control the progress object.
+ *
+ * @return An initialized progress object, or NULL upon error. The
+ * caller is responsible for calling progress_destroy() afterwards.
+ */
+progress_t *progress_create(toplevel_t * top);
+
+/**
+ * Destroy a progress dialog. Does nothing if the pointer is already
+ * NULL.
+ *
+ * @param prefs Reference to a progress object to destroy. This will
+ * be set to NULL afterwards.
+ */
+void progress_destroy(progress_t ** progress);
+
+/**
+ * Display a progress dialog.
+ *
+ * @param progress Progress dialog to show.
+ * @param title Title for the progress window.
+ */
+void progress_show(progress_t * progress, const char *title);
+
+/**
+ * Hide the progress dialog. Note that this does not actually destroy
+ * the object.
+ *
+ * @param progress Progress dialog to hide.
+ */
+void progress_hide(progress_t * progress);
+
+/* the rest of these are for multi-threaded progress dialog */
+
+/**
+ * Block the current thread until the progress dialog receives a done
+ * signal via progress_done() or progress_abort(). The dialog will
+ * periodically awake and update the user interface, based upon
+ * message received by its handle implementations.
+ *
+ * @param progress Progress object to wait against.
+ *
+ * @return 0 if the progress object got a progress_done(), < 0 if
+ * progress_abort().
+ */
+int progress_wait(progress_t * progress);
+
+/**
+ * Signal to a progress object that this thread is ending
+ * successfully. This will cause all threads waiting upon the
+ * progress object to resume.
+ *
+ * @param progress Progress object to signal completion.
+ */
+void progress_done(progress_t * progress);
+
+/**
+ * Signal to a progress object that this thread completed with
+ * warnings. This will cause all threads waiting upon the progress
+ * object to resume.
+ *
+ * @param progress Progress object to signal completion.
+ * @param reason Explanation for warning, or NULL to use most recently
+ * written message as the reason.
+ */
+void progress_warn(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Signal to a progress object that this thread is aborting. This
+ * will cause all threads waiting upon the progress object to resume.
+ *
+ * @param progress Progress object to signal completion.
+ * @param reason Explanation for abort, or NULL to abort for no
+ * reason. The most recently written message will be used as the
+ * reason.
+ */
+void progress_abort(progress_t * progress, char *reason, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Have the progress dialog show a message upon its next refresh.
+ *
+ * @param progress Progress object to update.
+ * @param fmt Format for string to display.
+ */
+void progress_update(progress_t * progress, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Implementation of libpoldiff's message callback function. This
+ * will route messages generated by libpoldiff to the progress
+ * dialog's display. To use this, pass the progress_t object as
+ * poldiff_create()'s callback_arg parameter.
+ */
+void progress_poldiff_handle_func(void *arg, const poldiff_t * diff, int level, const char *fmt, va_list va_args);
+
+/**
+ * Implementation of a libapol message callback function. This will
+ * route messages generated by libapol to the progress dialog's
+ * display. To use this, pass the progress_t object as
+ * apol_policy_open()'s varg parameter.
+ */
+void progress_apol_handle_func(void *varg, const apol_policy_t * p, int level, const char *fmt, va_list argp);
+
+#endif
diff --git a/sediff/remap_types_dialog.c b/sediff/remap_types_dialog.c
new file mode 100644
index 0000000..24c5b21
--- /dev/null
+++ b/sediff/remap_types_dialog.c
@@ -0,0 +1,564 @@
+/**
+ * @file
+ * Displays a dialog that allows users to explicitly remap/remap
+ * types from one policy to the other.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "remap_types_dialog.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <apol/type-query.h>
+#include <apol/util.h>
+#include <glade/glade.h>
+#include <poldiff/type_map.h>
+
+struct remap_types
+{
+ toplevel_t *top;
+ GladeXML *xml;
+ poldiff_t *diff;
+ /** main dialog widget for type remapps*/
+ GtkDialog *dialog;
+ GtkTreeView *view;
+ GtkCheckButton *show_inferred;
+ /** drop-down combo boxes that allow user to add new remap */
+ GtkComboBoxEntry *combo[SEDIFFX_POLICY_NUM];
+ GtkCheckButton *show_unmapped_types;
+ GtkButton *add, *remove;
+ GtkListStore *remaps;
+ GtkTreeModelFilter *filter;
+ GtkTreeModelSort *sort;
+ /** non-zero if a type map was added or removed */
+ int changed;
+};
+
+enum
+{
+ ORIG_NAME_COL = 0, MOD_NAME_COL, ORIG_HIGHLIGHT_VALUE_COL, MOD_HIGHLIGHT_VALUE_COL, ENTRY_COL, NUM_COLUMNS
+};
+
+static GtkTreeModelFilter *types_filter[SEDIFFX_POLICY_NUM] = { NULL, NULL };
+static GtkListStore *types[SEDIFFX_POLICY_NUM] = { NULL, NULL };
+static gboolean show_only_unmapped = TRUE;
+
+/**
+ * Go through all entries in the list store; for those that map the
+ * current entries for the combo boxes, highlight them.
+ */
+static void remap_types_highlight_entries(struct remap_types *rt)
+{
+ const gchar *orig_text = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_ORIG]));
+ const gchar *mod_text = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_MOD]));
+ int num_orig_matches = 0, num_mod_matches = 0;
+ gboolean iter_valid;
+ GtkTreeIter iter;
+ poldiff_type_remap_entry_t *entry;
+ apol_vector_t *v;
+ size_t idx;
+ iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rt->remaps), &iter);
+ while (iter_valid) {
+ gtk_tree_model_get(GTK_TREE_MODEL(rt->remaps), &iter, ENTRY_COL, &entry, -1);
+ if (orig_text != NULL && strcmp(orig_text, "") != 0) {
+ v = poldiff_type_remap_entry_get_original_types(rt->diff, entry);
+ if (apol_vector_get_index(v, orig_text, apol_str_strcmp, NULL, &idx) == 0) {
+ gtk_list_store_set(rt->remaps, &iter, ORIG_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_BOLD, -1);
+ num_orig_matches++;
+ if (apol_vector_get_size(v) > 1) {
+ /* this will disallow the
+ * original type from being
+ * added again */
+ num_orig_matches++;
+ }
+ } else {
+ gtk_list_store_set(rt->remaps, &iter, ORIG_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_NORMAL, -1);
+ }
+ apol_vector_destroy(&v);
+ }
+ if (mod_text != NULL && strcmp(mod_text, "") != 0) {
+ v = poldiff_type_remap_entry_get_modified_types(rt->diff, entry);
+ if (apol_vector_get_index(v, mod_text, apol_str_strcmp, NULL, &idx) == 0) {
+ gtk_list_store_set(rt->remaps, &iter, MOD_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_BOLD, -1);
+ num_mod_matches++;
+ if (apol_vector_get_size(v) > 1) {
+ /* this will disallow the
+ * modified type from being
+ * added again */
+ num_mod_matches++;
+ }
+ } else {
+ gtk_list_store_set(rt->remaps, &iter, MOD_HIGHLIGHT_VALUE_COL, PANGO_WEIGHT_NORMAL, -1);
+ }
+ apol_vector_destroy(&v);
+ }
+ iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(rt->remaps), &iter);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(rt->add), FALSE);
+ if (orig_text != NULL && strcmp(orig_text, "") != 0 && mod_text != NULL && strcmp(mod_text, "") != 0) {
+ /* enable the add button if and only if: number of
+ * orig and mod matches are both 0, or one side is 1
+ * and the other is 0 */
+ if ((num_orig_matches == 0 && num_mod_matches == 0) ||
+ (num_orig_matches == 1 && num_mod_matches == 0) || (num_orig_matches == 0 && num_mod_matches == 1)) {
+ gtk_widget_set_sensitive(GTK_WIDGET(rt->add), TRUE);
+ }
+ }
+}
+
+/**
+ * Populate the main tree model with all type remaps currently within
+ * the poldiff object.
+ */
+static void remap_types_update_view(struct remap_types *rt)
+{
+ apol_vector_t *entries = poldiff_type_remap_get_entries(rt->diff);
+ apol_vector_t *origs = NULL, *mods = NULL;
+ apol_vector_t *used_origs = NULL, *used_mods = NULL;
+ char *orig_string = NULL, *mod_string = NULL, *s, *t;
+ size_t i, j;
+ GtkTreeIter iter;
+ if ((used_origs = apol_vector_create(NULL)) == NULL || (used_mods = apol_vector_create(NULL)) == NULL) {
+ toplevel_ERR(rt->top, "%s", strerror(errno));
+ goto cleanup;
+ }
+ gtk_list_store_clear(rt->remaps);
+ for (i = 0; i < apol_vector_get_size(entries); i++) {
+ poldiff_type_remap_entry_t *e = apol_vector_get_element(entries, i);
+ if ((origs = poldiff_type_remap_entry_get_original_types(rt->diff, e)) == NULL ||
+ (mods = poldiff_type_remap_entry_get_modified_types(rt->diff, e)) == NULL ||
+ (orig_string = apol_str_join(origs, ", ")) == NULL || (mod_string = apol_str_join(mods, ", ")) == NULL) {
+ toplevel_ERR(rt->top, "%s", strerror(errno));
+ goto cleanup;
+ }
+ for (j = 0; j < apol_vector_get_size(origs); j++) {
+ s = apol_vector_get_element(origs, j);
+ if (apol_vector_append(used_origs, s) < 0) {
+ toplevel_ERR(rt->top, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ for (j = 0; j < apol_vector_get_size(mods); j++) {
+ s = apol_vector_get_element(mods, j);
+ if (apol_vector_append(used_mods, s) < 0) {
+ toplevel_ERR(rt->top, "%s", strerror(errno));
+ goto cleanup;
+ }
+ }
+ gtk_list_store_append(rt->remaps, &iter);
+ gtk_list_store_set(rt->remaps, &iter, ORIG_NAME_COL, orig_string, MOD_NAME_COL, mod_string, ENTRY_COL, e, -1);
+ apol_vector_destroy(&origs);
+ apol_vector_destroy(&mods);
+ free(orig_string);
+ free(mod_string);
+ orig_string = NULL;
+ mod_string = NULL;
+ }
+
+ /* mark entries in the 'types' list store that are used. the
+ * algorithm works only because the types list store was
+ * sorted by remap_types_update(). */
+ apol_vector_sort_uniquify(used_origs, apol_str_strcmp, NULL);
+ apol_vector_sort_uniquify(used_mods, apol_str_strcmp, NULL);
+ i = 0;
+ s = apol_vector_get_element(used_origs, i);
+ gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(types[SEDIFFX_POLICY_ORIG]), &iter);
+ while (iter_valid && s != NULL) {
+ gtk_tree_model_get(GTK_TREE_MODEL(types[SEDIFFX_POLICY_ORIG]), &iter, 0, &t, -1);
+ if (strcmp(s, t) == 0) {
+ gtk_list_store_set(types[SEDIFFX_POLICY_ORIG], &iter, 1, TRUE, -1);
+ i++;
+ s = apol_vector_get_element(used_origs, i);
+ } else {
+ gtk_list_store_set(types[SEDIFFX_POLICY_ORIG], &iter, 1, FALSE, -1);
+ }
+ iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(types[SEDIFFX_POLICY_ORIG]), &iter);
+ }
+ assert(i >= apol_vector_get_size(used_origs));
+
+ i = 0;
+ s = apol_vector_get_element(used_mods, i);
+ iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(types[SEDIFFX_POLICY_MOD]), &iter);
+ while (iter_valid && s != NULL) {
+ gtk_tree_model_get(GTK_TREE_MODEL(types[SEDIFFX_POLICY_MOD]), &iter, 0, &t, -1);
+ if (strcmp(s, t) == 0) {
+ gtk_list_store_set(types[SEDIFFX_POLICY_MOD], &iter, 1, TRUE, -1);
+ i++;
+ s = apol_vector_get_element(used_mods, i);
+ } else {
+ gtk_list_store_set(types[SEDIFFX_POLICY_MOD], &iter, 1, FALSE, -1);
+ }
+ iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(types[SEDIFFX_POLICY_MOD]), &iter);
+ }
+ assert(i >= apol_vector_get_size(used_mods));
+
+ remap_types_highlight_entries(rt);
+ gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_ORIG]);
+ gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_MOD]);
+ cleanup:
+ apol_vector_destroy(&used_origs);
+ apol_vector_destroy(&used_mods);
+ apol_vector_destroy(&origs);
+ apol_vector_destroy(&mods);
+ free(orig_string);
+ free(mod_string);
+}
+
+static void remap_types_on_show_inferred_toggle(GtkToggleButton * toggle __attribute__ ((unused)), gpointer user_data)
+{
+ struct remap_types *rt = (struct remap_types *)user_data;
+ gtk_tree_model_filter_refilter(rt->filter);
+}
+
+static void remap_types_on_show_unmapped_types_toggle(GtkToggleButton * toggle, gpointer user_data __attribute__ ((unused)))
+{
+ show_only_unmapped = gtk_toggle_button_get_active(toggle);
+ gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_ORIG]);
+ gtk_tree_model_filter_refilter(types_filter[SEDIFFX_POLICY_MOD]);
+}
+
+static void remap_types_on_selection_change(GtkTreeSelection * selection, gpointer user_data)
+{
+ struct remap_types *rt = (struct remap_types *)user_data;
+ gboolean sens = gtk_tree_selection_get_selected(selection, NULL, NULL);
+ gtk_widget_set_sensitive(GTK_WIDGET(rt->remove), sens);
+}
+
+static void remap_types_on_combo_change(GtkComboBox * widget __attribute__ ((unused)), gpointer user_data)
+{
+ struct remap_types *rt = (struct remap_types *)user_data;
+ remap_types_highlight_entries(rt);
+}
+
+static void remap_types_on_add_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct remap_types *rt = (struct remap_types *)user_data;
+ apol_vector_t *orig = NULL, *mod = NULL;
+ apol_vector_t *old_orig = NULL, *old_mod = NULL;
+ const gchar *orig_type = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_ORIG]));
+ const gchar *mod_type = util_combo_box_get_active_text(GTK_COMBO_BOX(rt->combo[SEDIFFX_POLICY_MOD]));
+
+ if ((orig = apol_str_split(orig_type, " ")) == NULL || (mod = apol_str_split(mod_type, " ")) == NULL) {
+ toplevel_ERR(rt->top, "%s", strerror(errno));
+ }
+ if (apol_vector_get_size(orig) > 1 && apol_vector_get_size(mod) > 1) {
+ toplevel_ERR(rt->top, "%s", "Remappings may be 1 to many or many to 1, but not many to many.");
+ goto cleanup;
+ }
+
+ /* check all existing remap entries, to see if the user's
+ * entries should be appended to an existing entry */
+ GtkTreeIter iter;
+ gboolean iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rt->remaps), &iter);
+ poldiff_type_remap_entry_t *entry = NULL, *e;
+ /* remap_types_highlight_entries() should have already marked
+ * which of the existing entries match the user's inputs */
+ while (iter_valid) {
+ gint orig_marked, mod_marked;
+ gtk_tree_model_get(GTK_TREE_MODEL(rt->remaps), &iter, ORIG_HIGHLIGHT_VALUE_COL, &orig_marked,
+ MOD_HIGHLIGHT_VALUE_COL, &mod_marked, ENTRY_COL, &e, -1);
+ assert(orig_marked != PANGO_WEIGHT_BOLD || mod_marked != PANGO_WEIGHT_BOLD);
+ if (orig_marked == PANGO_WEIGHT_BOLD) {
+ assert(entry == NULL);
+ entry = e;
+ } else if (mod_marked == PANGO_WEIGHT_BOLD) {
+ assert(entry == NULL);
+ entry = e;
+ }
+ iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(rt->remaps), &iter);
+ }
+ if (entry != NULL) {
+ size_t i;
+ char *s;
+ old_orig = poldiff_type_remap_entry_get_original_types(rt->diff, entry);
+ old_mod = poldiff_type_remap_entry_get_modified_types(rt->diff, entry);
+ assert(old_orig != NULL && old_mod != NULL);
+ for (i = 0; i < apol_vector_get_size(old_orig); i++) {
+ s = strdup(apol_vector_get_element(old_orig, i));
+ if (apol_vector_append_unique(orig, s, apol_str_strcmp, NULL) > 0) {
+ free(s);
+ }
+ }
+ for (i = 0; i < apol_vector_get_size(old_mod); i++) {
+ s = strdup(apol_vector_get_element(old_mod, i));
+ if (apol_vector_append_unique(mod, s, apol_str_strcmp, NULL) > 0) {
+ free(s);
+ }
+ }
+ poldiff_type_remap_entry_remove(rt->diff, entry);
+ }
+
+ if (poldiff_type_remap_create(rt->diff, orig, mod) < 0) {
+ toplevel_ERR(rt->top, "%s", "This was not a valid type remap.");
+ goto cleanup;
+ } else {
+ remap_types_update_view(rt);
+ rt->changed = 1;
+ }
+ cleanup:
+ apol_vector_destroy(&orig);
+ apol_vector_destroy(&mod);
+ apol_vector_destroy(&old_orig);
+ apol_vector_destroy(&old_mod);
+}
+
+static void remap_types_on_remove_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ struct remap_types *rt = (struct remap_types *)user_data;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(rt->view);
+ GtkTreeIter iter;
+ if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ poldiff_type_remap_entry_t *entry;
+ gtk_tree_model_get(GTK_TREE_MODEL(rt->sort), &iter, ENTRY_COL, &entry, -1);
+ poldiff_type_remap_entry_remove(rt->diff, entry);
+ remap_types_update_view(rt);
+ rt->changed = 1;
+ }
+}
+
+static gint remap_types_sort_compare(GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data)
+{
+ gint column = GPOINTER_TO_INT(user_data);
+ char *s, *t;
+ gtk_tree_model_get(model, a, column, &s, -1);
+ gtk_tree_model_get(model, b, column, &t, -1);
+ /* these next two conditionals are needed because while the remap
+ * list store is being built, a row will temporarily have empty
+ * strings */
+ if (s == NULL) {
+ s = "";
+ }
+ if (t == NULL) {
+ t = "";
+ }
+ return strcmp(s, t);
+}
+
+static gboolean remap_types_filter_visible(GtkTreeModel * model, GtkTreeIter * iter, gpointer user_data)
+{
+ struct remap_types *rt = (struct remap_types *)user_data;
+ poldiff_type_remap_entry_t *entry;
+ gtk_tree_model_get(model, iter, ENTRY_COL, &entry, -1);
+ if (poldiff_type_remap_entry_get_is_inferred(entry)) {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt->show_inferred))) {
+ return TRUE;
+ }
+ return FALSE;
+ } else {
+ /* explicit maps are always shown */
+ return TRUE;
+ }
+}
+
+static void remap_types_init_widgets(struct remap_types *rt)
+{
+ GtkTreeSelection *selection;
+ GtkCellRenderer *orig_renderer, *mod_renderer;
+ GtkTreeViewColumn *column;
+
+ rt->dialog = GTK_DIALOG(glade_xml_get_widget(rt->xml, "remap_types"));
+ assert(rt->dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(rt->dialog), toplevel_get_window(rt->top));
+ rt->view = GTK_TREE_VIEW(glade_xml_get_widget(rt->xml, "remap_types treeview"));
+ rt->show_inferred = GTK_CHECK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types inferred checkbutton"));
+ rt->combo[SEDIFFX_POLICY_ORIG] = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(rt->xml, "remap_types orig combo"));
+ rt->combo[SEDIFFX_POLICY_MOD] = GTK_COMBO_BOX_ENTRY(glade_xml_get_widget(rt->xml, "remap_types mod combo"));
+ rt->show_unmapped_types = GTK_CHECK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types only unmapped checkbutton"));
+ assert(rt->view != NULL && rt->show_inferred && rt->combo[SEDIFFX_POLICY_ORIG] != NULL
+ && rt->combo[SEDIFFX_POLICY_MOD] != NULL && rt->show_unmapped_types != NULL);
+ g_signal_connect(rt->show_inferred, "toggled", G_CALLBACK(remap_types_on_show_inferred_toggle), rt);
+ g_signal_connect(rt->show_unmapped_types, "toggled", G_CALLBACK(remap_types_on_show_unmapped_types_toggle), rt);
+ show_only_unmapped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt->show_unmapped_types));
+
+ rt->add = GTK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types add button"));
+ rt->remove = GTK_BUTTON(glade_xml_get_widget(rt->xml, "remap_types remove button"));
+ assert(rt->add != NULL && rt->remove != NULL);
+ g_signal_connect(rt->add, "clicked", G_CALLBACK(remap_types_on_add_click), rt);
+ g_signal_connect(rt->remove, "clicked", G_CALLBACK(remap_types_on_remove_click), rt);
+
+ rt->remaps = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER);
+ rt->filter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(rt->remaps), NULL));
+ gtk_tree_model_filter_set_visible_func(rt->filter, remap_types_filter_visible, rt, NULL);
+ rt->sort = GTK_TREE_MODEL_SORT(gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(rt->filter)));
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(rt->sort), ORIG_NAME_COL, remap_types_sort_compare,
+ GINT_TO_POINTER(ORIG_NAME_COL), NULL);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(rt->sort), MOD_NAME_COL, remap_types_sort_compare,
+ GINT_TO_POINTER(MOD_NAME_COL), NULL);
+ gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(rt->sort), remap_types_sort_compare,
+ GINT_TO_POINTER(ORIG_NAME_COL), NULL);
+ gtk_tree_view_set_model(rt->view, GTK_TREE_MODEL(rt->sort));
+
+ selection = gtk_tree_view_get_selection(rt->view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ g_signal_connect(selection, "changed", G_CALLBACK(remap_types_on_selection_change), rt);
+
+ orig_renderer = gtk_cell_renderer_text_new();
+ mod_renderer = gtk_cell_renderer_text_new();
+ g_object_set(orig_renderer, "weight", PANGO_WEIGHT_BOLD, NULL);
+ g_object_set(mod_renderer, "weight", PANGO_WEIGHT_BOLD, NULL);
+ column = gtk_tree_view_column_new_with_attributes("Original Policy", orig_renderer, "text", ORIG_NAME_COL, "weight",
+ ORIG_HIGHLIGHT_VALUE_COL, NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, ORIG_NAME_COL);
+ gtk_tree_view_column_set_sort_indicator(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(rt->view), column);
+ gtk_tree_view_column_clicked(column);
+
+ column = gtk_tree_view_column_new_with_attributes("Modified Policy", mod_renderer, "text", MOD_NAME_COL, "weight",
+ MOD_HIGHLIGHT_VALUE_COL, NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, MOD_NAME_COL);
+ gtk_tree_view_column_set_sort_indicator(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(rt->view), column);
+}
+
+static gboolean remap_types_types_filter_visible(GtkTreeModel * model, GtkTreeIter * iter, gpointer user_data);
+
+/**
+ * Set up the combo boxes to show only types unique to that policy.
+ * (The lists of types were calculated by remap_types_update().)
+ */
+static void remap_types_init_combos(struct remap_types *rt)
+{
+ sediffx_policy_e i;
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ gtk_combo_box_set_model(GTK_COMBO_BOX(rt->combo[i]), GTK_TREE_MODEL(types_filter[i]));
+ gtk_combo_box_entry_set_text_column(rt->combo[i], 0);
+ g_signal_connect(rt->combo[i], "changed", G_CALLBACK(remap_types_on_combo_change), rt);
+ }
+}
+
+int remap_types_run(toplevel_t * top)
+{
+ struct remap_types rt;
+ gint response;
+ static gboolean prev_show_inferred = FALSE;
+ static gboolean prev_show_unmapped = TRUE;
+
+ memset(&rt, 0, sizeof(rt));
+ rt.top = top;
+ rt.xml = glade_xml_new(toplevel_get_glade_xml(rt.top), "remap_types", NULL);
+ rt.diff = toplevel_get_poldiff(rt.top);
+
+ remap_types_init_widgets(&rt);
+ remap_types_init_combos(&rt);
+ remap_types_update_view(&rt);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rt.show_inferred), prev_show_inferred);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rt.show_unmapped_types), prev_show_unmapped);
+
+ response = gtk_dialog_run(rt.dialog);
+
+ prev_show_inferred = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt.show_inferred));
+ prev_show_unmapped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rt.show_unmapped_types));
+ gtk_widget_destroy(GTK_WIDGET(rt.dialog));
+ g_object_unref(rt.sort);
+ g_object_unref(rt.filter);
+ g_object_unref(rt.remaps);
+ return rt.changed;
+}
+
+static gboolean remap_types_types_filter_visible(GtkTreeModel * model, GtkTreeIter * iter, gpointer user_data
+ __attribute__ ((unused)))
+{
+ gboolean in_use;
+ if (!show_only_unmapped) {
+ return TRUE;
+ }
+ /* otherwise show types that are not being used */
+ gtk_tree_model_get(model, iter, 1, &in_use, -1);
+ return !in_use;
+}
+
+/**
+ * Alphabetize a list of qpol_type_t, base upon their names.
+ */
+static int remap_types_qpol_type_cmp(const void *a, const void *b, void *data)
+{
+ const qpol_type_t *x = a;
+ const qpol_type_t *y = b;
+ qpol_policy_t *q = (qpol_policy_t *) data;
+ const char *s, *t;
+ qpol_type_get_name(q, x, &s);
+ qpol_type_get_name(q, y, &t);
+ return strcmp(s, t);
+}
+
+int remap_types_update(apol_policy_t * orig_policy, apol_policy_t * mod_policy)
+{
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_policy);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_policy);
+ apol_vector_t *v = NULL;
+ sediffx_policy_e which;
+ size_t i;
+ const qpol_type_t *t;
+ const char *type_name;
+ GtkTreeIter iter;
+ int error = 0, retval = -1;
+
+ for (which = SEDIFFX_POLICY_ORIG; which < SEDIFFX_POLICY_NUM; which++) {
+ if (types[which] == NULL) {
+ types[which] = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ types_filter[which] = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(types[which]), NULL));
+ gtk_tree_model_filter_set_visible_func(types_filter[which], remap_types_types_filter_visible, NULL, NULL);
+ } else {
+ gtk_list_store_clear(types[which]);
+ }
+ }
+
+ if (apol_type_get_by_query(orig_policy, NULL, &v) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v, remap_types_qpol_type_cmp, oq);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ t = apol_vector_get_element(v, i);
+ qpol_type_get_name(oq, t, &type_name);
+ gtk_list_store_append(types[SEDIFFX_POLICY_ORIG], &iter);
+ gtk_list_store_set(types[SEDIFFX_POLICY_ORIG], &iter, 0, type_name, -1);
+ }
+ apol_vector_destroy(&v);
+
+ if (apol_type_get_by_query(mod_policy, NULL, &v) < 0) {
+ error = errno;
+ goto cleanup;
+ }
+ apol_vector_sort(v, remap_types_qpol_type_cmp, mq);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ t = apol_vector_get_element(v, i);
+ qpol_type_get_name(mq, t, &type_name);
+ gtk_list_store_append(types[SEDIFFX_POLICY_MOD], &iter);
+ gtk_list_store_set(types[SEDIFFX_POLICY_MOD], &iter, 0, type_name, -1);
+ }
+ retval = 0;
+ cleanup:
+ apol_vector_destroy(&v);
+ if (retval != 0) {
+ errno = error;
+ return retval;
+ }
+ return retval;
+}
diff --git a/sediff/remap_types_dialog.h b/sediff/remap_types_dialog.h
new file mode 100644
index 0000000..7e52313
--- /dev/null
+++ b/sediff/remap_types_dialog.h
@@ -0,0 +1,56 @@
+/**
+ * @file
+ * Headers for a dialog that allows users to explicitly remap/remap
+ * types.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef REMAP_TYPES_DIALOG_H
+#define REMAP_TYPES_DIALOG_H
+
+#include "toplevel.h"
+
+/**
+ * Display and run a dialog that allows the user to add and remove
+ * type remappings.
+ *
+ * @param top Toplevel containing poldiff structure.
+ *
+ * @return Non-zero if any mapping was added or removed, zero if there
+ * were no changes.
+ */
+int remap_types_run(toplevel_t * top);
+
+/**
+ * Notify the remap types dialog that the currently loaded policies
+ * have changed. This function updates its lists of types from the
+ * policies. This function must be called at least once prior to
+ * remap_types_run().
+ *
+ * @param orig_policy Newly loaded original policy.
+ * @param mod_policy Newly loaded modified policy.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int remap_types_update(apol_policy_t * orig_policy, apol_policy_t * mod_policy);
+
+#endif
diff --git a/sediff/result_item.c b/sediff/result_item.c
new file mode 100644
index 0000000..5d47583
--- /dev/null
+++ b/sediff/result_item.c
@@ -0,0 +1,1361 @@
+/**
+ * @file
+ * Implementation of the result item class.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "result_item.h"
+#include "result_item_render.h"
+#include "utilgui.h"
+
+#include <assert.h>
+
+typedef void (*destructor_fn_t) (result_item_t * item);
+typedef void (*policy_changed_fn_t) (result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol);
+typedef void (*poldiff_run_fn_t) (result_item_t * item, poldiff_t * diff, int incremental);
+typedef GtkTextBuffer *(*get_buffer_fn_t) (result_item_t * item, poldiff_form_e form);
+typedef int (*is_render_slow_fn_t) (result_item_t * item, poldiff_form_e form);
+typedef void (*get_forms_fn_t) (result_item_t * item, int forms[5]);
+typedef void (*set_current_sort_fn_t) (result_item_t * item, poldiff_form_e form, results_sort_e sort, results_sort_dir_e dir);
+typedef void (*print_diff_fn_t) (result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form);
+
+struct result_item
+{
+ const char *label;
+ /** bit value corresponding to polidiff/poldiff.h defines */
+ uint32_t bit_pos;
+ /** if either policy does not support a particular policy
+ component then this will be zero */
+ int supported;
+ /* protected members below */
+ poldiff_t *diff;
+ size_t stats[5];
+ gint offsets[5];
+ results_sort_e sorts[5];
+ results_sort_dir_e sort_dirs[5];
+ /* below are required functions to get poldiff results */
+ const apol_vector_t *(*get_vector) (const poldiff_t *);
+ poldiff_form_e(*get_form) (const void *);
+ char *(*get_string) (const poldiff_t *, const void *);
+ /* below is a virtual function table */
+ destructor_fn_t destructor;
+ /** if the result item does not care about the type of
+ policies are loaded then this can be NULL */
+ policy_changed_fn_t policy_changed;
+ poldiff_run_fn_t poldiff_run;
+ get_buffer_fn_t get_buffer;
+ is_render_slow_fn_t is_render_slow;
+ get_forms_fn_t get_forms;
+ /** if the result item cannot be sorted then this will be an array
+ of zeroes */
+ set_current_sort_fn_t set_current_sort;
+ /** data specific to subclasses of result_item */
+ union
+ {
+ int type_can_modify;
+ struct
+ {
+ int has_line_numbers[SEDIFFX_POLICY_NUM];
+ int cached[5];
+ GtkTextBuffer *buffers[5];
+ apol_vector_t *items[5];
+ print_diff_fn_t print_diff;
+ } multi;
+ } data;
+};
+
+/** map from a poldiff_form_e to an integer */
+static const poldiff_form_e form_reverse_map[] = {
+ -1, 0, 2, 4, 1, 3
+};
+
+/******************** single buffer functions ********************/
+
+/* below is the implementation of the 'single buffer result item'
+ class. most policy components are instances of this class.*/
+
+static GtkTextBuffer *single_buffer = NULL;
+
+/**
+ * For single items, results are always destroyed. (Recalculating
+ * results is very fast.)
+ */
+static void result_item_single_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental __attribute__ ((unused)))
+{
+ item->diff = diff;
+ memset(item->stats, 0, sizeof(item->stats));
+}
+
+/**
+ * For single items, re-use the same buffer each time.
+ */
+static GtkTextBuffer *result_item_single_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ util_text_buffer_clear(single_buffer);
+ if (form == POLDIFF_FORM_NONE) {
+ result_item_print_summary(item, single_buffer);
+ } else {
+ result_item_print_header(item, single_buffer, form);
+ result_item_print_diff(item, single_buffer, form);
+ }
+ return single_buffer;
+}
+
+/**
+ * For single items, rendering is (supposed to be) very fast.
+ */
+static int result_item_single_is_render_slow(result_item_t * item __attribute__ ((unused)), poldiff_form_e form
+ __attribute__ ((unused)))
+{
+ return 0;
+}
+
+/**
+ * For single items, re-use the same buffer each time. This function
+ * calls a modified rendering functions to be used explicitly by
+ * rules.
+ */
+static GtkTextBuffer *result_item_single_get_rule_buffer(result_item_t * item, poldiff_form_e form)
+{
+ util_text_buffer_clear(single_buffer);
+ if (form == POLDIFF_FORM_NONE) {
+ result_item_print_summary(item, single_buffer);
+ } else {
+ result_item_print_header(item, single_buffer, form);
+ result_item_print_rule_diff(item, single_buffer, form);
+ }
+ return single_buffer;
+}
+
+static void result_item_single_get_forms(result_item_t * item, int forms[5])
+{
+ int i, was_run = poldiff_is_run(item->diff, item->bit_pos);
+ if (was_run) {
+ poldiff_get_stats(item->diff, item->bit_pos, item->stats);
+ }
+ for (i = 0; i < 5; i++) {
+ if (!result_item_is_supported(item) || i == form_reverse_map[POLDIFF_FORM_ADD_TYPE]
+ || i == form_reverse_map[POLDIFF_FORM_REMOVE_TYPE]) {
+ /* single items do not have add-by-type and
+ * remove-by-type forms */
+ forms[i] = -1;
+ } else {
+ forms[i] = was_run;
+ }
+ }
+}
+
+/**
+ * Constructor for the abstract single buffer item class.
+ */
+static result_item_t *result_item_single_create(GtkTextTagTable * table)
+{
+ result_item_t *item = calloc(1, sizeof(*item));
+ if (item == NULL) {
+ return item;
+ }
+ if (single_buffer == NULL) {
+ single_buffer = gtk_text_buffer_new(table);
+ }
+ item->supported = 1;
+ item->policy_changed = NULL;
+ item->poldiff_run = result_item_single_poldiff_run;
+ item->get_buffer = result_item_single_get_buffer;
+ item->is_render_slow = result_item_single_is_render_slow;
+ item->get_forms = result_item_single_get_forms;
+ return item;
+}
+
+/******************** constructors below ********************/
+
+result_item_t *result_item_create_classes(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Classes";
+ item->bit_pos = POLDIFF_DIFF_CLASSES;
+ item->get_vector = poldiff_get_class_vector;
+ item->get_form = poldiff_class_get_form;
+ item->get_string = poldiff_class_to_string;
+ return item;
+}
+
+result_item_t *result_item_create_commons(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Commons";
+ item->bit_pos = POLDIFF_DIFF_COMMONS;
+ item->get_vector = poldiff_get_common_vector;
+ item->get_form = poldiff_common_get_form;
+ item->get_string = poldiff_common_to_string;
+ return item;
+}
+
+/**
+ * Print an appropriate error message if levels are not supported.
+ */
+static GtkTextBuffer *result_item_level_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ if (!result_item_is_supported(item)) {
+ gtk_text_buffer_set_text(single_buffer,
+ "Level diffs are not supported because neither policy is a MLS policy.", -1);
+ return single_buffer;
+ } else {
+ return result_item_single_get_buffer(item, form);
+ }
+}
+
+/**
+ * Only support sensitivites and levels if either policy (can) has
+ * them.
+ */
+static void result_item_level_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_pol);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_pol);
+ if (!qpol_policy_has_capability(oq, QPOL_CAP_MLS) && !qpol_policy_has_capability(mq, QPOL_CAP_MLS)) {
+ item->supported = 0;
+ } else {
+ item->supported = 1;
+ }
+}
+
+/* levels require at least one MLS policy */
+result_item_t *result_item_create_levels(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Levels";
+ item->bit_pos = POLDIFF_DIFF_LEVELS;
+ item->get_vector = poldiff_get_level_vector;
+ item->get_form = poldiff_level_get_form;
+ item->get_string = poldiff_level_to_string;
+ item->get_buffer = result_item_level_get_buffer;
+ item->policy_changed = result_item_level_policy_changed;
+ return item;
+}
+
+/**
+ * Print an appropriate error message if categories are not supported.
+ */
+static GtkTextBuffer *result_item_category_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ if (!result_item_is_supported(item)) {
+ gtk_text_buffer_set_text(single_buffer,
+ "Category diffs are not supported because neither policy is a MLS policy.", -1);
+ return single_buffer;
+ } else {
+ return result_item_single_get_buffer(item, form);
+ }
+}
+
+/**
+ * The modified form is always unsupported.
+ */
+static void result_item_category_get_forms(result_item_t * item, int forms[5])
+{
+ result_item_single_get_forms(item, forms);
+ forms[4] = -1;
+}
+
+/* categories have no modified form; they also require two MLS
+ policies to be supported */
+result_item_t *result_item_create_categories(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Categories";
+ item->bit_pos = POLDIFF_DIFF_CATS;
+ item->get_vector = poldiff_get_cat_vector;
+ item->get_form = poldiff_cat_get_form;
+ item->get_string = poldiff_cat_to_string;
+ item->get_buffer = result_item_category_get_buffer;
+ item->policy_changed = result_item_level_policy_changed; /* [sic] */
+ item->get_forms = result_item_category_get_forms;
+ return item;
+}
+
+/**
+ * Only show modified types if it makes sense -- i.e, when both
+ * policies have meaningful attribute names.
+ */
+static void result_item_type_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_pol);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_pol);
+ if (!qpol_policy_has_capability(oq, QPOL_CAP_ATTRIB_NAMES) || !qpol_policy_has_capability(mq, QPOL_CAP_ATTRIB_NAMES)) {
+ item->data.type_can_modify = 0;
+ } else {
+ item->data.type_can_modify = 1;
+ }
+}
+
+static void result_item_type_get_forms(result_item_t * item, int forms[5])
+{
+ result_item_single_get_forms(item, forms);
+ if (!item->data.type_can_modify) {
+ forms[4] = -1;
+ }
+}
+
+/* the type result item is a subclass of single item. it differs in
+ that it might not have a modified form */
+result_item_t *result_item_create_types(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Types";
+ item->bit_pos = POLDIFF_DIFF_TYPES;
+ item->get_vector = poldiff_get_type_vector;
+ item->get_form = poldiff_type_get_form;
+ item->get_string = poldiff_type_to_string;
+ item->policy_changed = result_item_type_policy_changed;
+ item->get_forms = result_item_type_get_forms;
+ return item;
+}
+
+/**
+ * Only support attributes when both policies (can) have them.
+ */
+static void result_item_attribute_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_pol);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_pol);
+ if (!qpol_policy_has_capability(oq, QPOL_CAP_ATTRIB_NAMES) || !qpol_policy_has_capability(mq, QPOL_CAP_ATTRIB_NAMES)) {
+ item->supported = 0;
+ } else {
+ item->supported = 1;
+ }
+}
+
+/**
+ * Print an appropriate error message if attributes are not supported.
+ */
+static GtkTextBuffer *result_item_attribute_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ if (!result_item_is_supported(item)) {
+ gtk_text_buffer_set_text(single_buffer,
+ "Attribute diffs are not supported because one of the policies does not contain attribute names.",
+ -1);
+ return single_buffer;
+ } else {
+ return result_item_single_get_buffer(item, form);
+ }
+}
+
+/* the attribute result item is a subclass of single item. it differs
+ in that it might not exist if attributes are not supported in
+ either policy */
+result_item_t *result_item_create_attributes(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Attributes";
+ item->bit_pos = POLDIFF_DIFF_ATTRIBS;
+ item->get_vector = poldiff_get_attrib_vector;
+ item->get_form = poldiff_attrib_get_form;
+ item->get_string = poldiff_attrib_to_string;
+ item->get_buffer = result_item_attribute_get_buffer;
+ item->policy_changed = result_item_attribute_policy_changed;
+ return item;
+}
+
+result_item_t *result_item_create_roles(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Roles";
+ item->bit_pos = POLDIFF_DIFF_ROLES;
+ item->get_vector = poldiff_get_role_vector;
+ item->get_form = poldiff_role_get_form;
+ item->get_string = poldiff_role_to_string;
+ return item;
+}
+
+/**
+ * Construct a string, similar to poldiff_user_to_string(), but with
+ * the proper color coding enabled.
+ */
+static void result_item_user_print_modified(result_item_t * item, poldiff_user_t * user, GtkTextBuffer * tb, GtkTextIter * iter)
+{
+ GString *string = g_string_new("");
+ g_string_printf(string, "* %s\n", poldiff_user_get_name(user));
+ result_item_print_string(tb, iter, string->str, 1);
+
+ const apol_vector_t *added = poldiff_user_get_added_roles(user);
+ const apol_vector_t *removed = poldiff_user_get_removed_roles(user);
+ const apol_vector_t *unmodified = poldiff_user_get_unmodified_roles(user);
+ size_t i;
+ char *s;
+ if (apol_vector_get_size(added) > 0 || apol_vector_get_size(removed) > 0) {
+ g_string_assign(string, " roles {");
+ for (i = 0; i < apol_vector_get_size(unmodified); i++) {
+ s = (char *)apol_vector_get_element(unmodified, i);
+ g_string_append_printf(string, " %s", s);
+ }
+ for (i = 0; i < apol_vector_get_size(added); i++) {
+ s = (char *)apol_vector_get_element(added, i);
+ g_string_append_printf(string, " +%s", s);
+ }
+ for (i = 0; i < apol_vector_get_size(removed); i++) {
+ s = (char *)apol_vector_get_element(removed, i);
+ g_string_append_printf(string, " -%s", s);
+ }
+ g_string_append(string, " }\n");
+ result_item_print_string_inline(tb, iter, string->str, 1);
+ }
+
+ const poldiff_level_t *orig = poldiff_user_get_original_dfltlevel(user);
+ const poldiff_level_t *mod = poldiff_user_get_modified_dfltlevel(user);
+ if (orig != NULL) {
+ result_item_print_string_inline(tb, iter, " level:\n", 1);
+ s = poldiff_level_to_string_brief(item->diff, orig);
+ g_string_printf(string, " %s", s);
+ if (poldiff_level_get_form(orig) != POLDIFF_FORM_MODIFIED) {
+ result_item_print_string(tb, iter, string->str, 1);
+ } else {
+ result_item_print_string_inline(tb, iter, string->str, 1);
+ }
+ free(s);
+ if (mod != NULL) {
+ s = poldiff_level_to_string_brief(item->diff, mod);
+ g_string_printf(string, " %s", s);
+ if (poldiff_level_get_form(mod) != POLDIFF_FORM_MODIFIED) {
+ result_item_print_string(tb, iter, string->str, 1);
+ } else {
+ result_item_print_string_inline(tb, iter, string->str, 1);
+ }
+ free(s);
+ }
+ }
+
+ const poldiff_range_t *range = poldiff_user_get_range(user);
+ if (range != NULL) {
+ result_item_print_modified_range(item, range, tb, iter);
+ }
+ result_item_print_string(tb, iter, "\n", 0);
+ g_string_free(string, TRUE);
+}
+
+/**
+ * Printing a modified user is special, for it spans multiple lines
+ * and has inline markers.
+ */
+static GtkTextBuffer *result_item_user_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ if (form != POLDIFF_FORM_MODIFIED) {
+ return result_item_single_get_buffer(item, form);
+ } else {
+ util_text_buffer_clear(single_buffer);
+ result_item_print_header(item, single_buffer, form);
+ GtkTextIter iter;
+ const apol_vector_t *v;
+ size_t i;
+ poldiff_user_t *user;
+
+ gtk_text_buffer_get_end_iter(single_buffer, &iter);
+ v = item->get_vector(item->diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ user = (poldiff_user_t *) apol_vector_get_element(v, i);
+ if (item->get_form(user) == form) {
+ result_item_user_print_modified(item, user, single_buffer, &iter);
+ }
+ }
+ return single_buffer;
+ }
+}
+
+/* the user result item is a subclass of a single item. */
+result_item_t *result_item_create_users(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Users";
+ item->bit_pos = POLDIFF_DIFF_USERS;
+ item->get_vector = poldiff_get_user_vector;
+ item->get_form = poldiff_user_get_form;
+ item->get_string = poldiff_user_to_string;
+ item->get_buffer = result_item_user_get_buffer;
+ return item;
+}
+
+/**
+ * Only support booleans if either policy (can) has them.
+ */
+static void result_item_boolean_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_pol);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_pol);
+ if (!qpol_policy_has_capability(oq, QPOL_CAP_CONDITIONALS) && !qpol_policy_has_capability(mq, QPOL_CAP_CONDITIONALS)) {
+ item->supported = 0;
+ } else {
+ item->supported = 1;
+ }
+}
+
+result_item_t *result_item_create_booleans(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Booleans";
+ item->bit_pos = POLDIFF_DIFF_BOOLS;
+ item->get_vector = poldiff_get_bool_vector;
+ item->get_form = poldiff_bool_get_form;
+ item->get_string = poldiff_bool_to_string;
+ item->policy_changed = result_item_boolean_policy_changed;
+ return item;
+}
+
+/* role trans are a subclass of single buffer item, in that they have
+ a special rendering function for modified items */
+result_item_t *result_item_create_role_allows(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Role Allows";
+ item->bit_pos = POLDIFF_DIFF_ROLE_ALLOWS;
+ item->get_vector = poldiff_get_role_allow_vector;
+ item->get_form = poldiff_role_allow_get_form;
+ item->get_string = poldiff_role_allow_to_string;
+ item->get_buffer = result_item_single_get_rule_buffer;
+ return item;
+}
+
+static void result_item_role_trans_get_forms(result_item_t * item, int forms[5])
+{
+ int i, was_run = poldiff_is_run(item->diff, item->bit_pos);
+ if (was_run) {
+ poldiff_get_stats(item->diff, item->bit_pos, item->stats);
+ }
+ for (i = 0; i < 5; i++) {
+ if (!result_item_is_supported(item)) {
+ forms[i] = -1;
+ } else {
+ forms[i] = was_run;
+ }
+ }
+}
+
+/* role trans are a subclass of single buffer item, in that they have
+ a special rendering function for modified items and that they have
+ add-type and remove-type forms */
+result_item_t *result_item_create_role_trans(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Role Transitions";
+ item->bit_pos = POLDIFF_DIFF_ROLE_TRANS;
+ item->get_vector = poldiff_get_role_trans_vector;
+ item->get_form = poldiff_role_trans_get_form;
+ item->get_string = poldiff_role_trans_to_string;
+ item->get_forms = result_item_role_trans_get_forms;
+ item->get_buffer = result_item_single_get_rule_buffer;
+ return item;
+}
+
+/**
+ * Construct a string, similar to poldiff_range_trans_to_string(), but
+ * with the proper color coding enabled.
+ */
+static void result_item_range_trans_print_modified(result_item_t * item, const poldiff_range_trans_t * rt, GtkTextBuffer * tb,
+ GtkTextIter * iter)
+{
+ GString *string = g_string_new("");
+ char *orig_s = poldiff_range_trans_to_string(item->diff, rt);
+ char *next_s = orig_s;
+ const poldiff_range_t *range = poldiff_range_trans_get_range(rt);
+
+ /* first line should always be printed with normal font */
+ char *s = strsep(&next_s, "\n");
+ g_string_printf(string, "%s\n", s);
+ result_item_print_string(tb, iter, string->str, 1);
+
+ /* all subsequent lines are printed as normal (yes, this
+ * discards lines from poldiff_range_trans_to_string() */
+ free(orig_s);
+ result_item_print_modified_range(item, range, tb, iter);
+}
+
+/**
+ * Printing a modified range_transition is special, for it spans
+ * multiple lines and has inline markers.
+ */
+static GtkTextBuffer *result_item_range_trans_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ if (form != POLDIFF_FORM_MODIFIED) {
+ return result_item_single_get_buffer(item, form);
+ } else {
+ util_text_buffer_clear(single_buffer);
+ result_item_print_header(item, single_buffer, form);
+ GtkTextIter iter;
+ const apol_vector_t *v;
+ size_t i;
+ poldiff_range_trans_t *rt;
+
+ gtk_text_buffer_get_end_iter(single_buffer, &iter);
+ v = item->get_vector(item->diff);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ rt = (poldiff_range_trans_t *) apol_vector_get_element(v, i);
+ if (item->get_form(rt) == form) {
+ result_item_range_trans_print_modified(item, rt, single_buffer, &iter);
+ }
+ }
+ return single_buffer;
+ }
+}
+
+/* range trans are a subclass of single buffer item, in that they have
+ a special rendering function for modified items and that they have
+ add-type and remove-type forms */
+result_item_t *result_item_create_range_trans(GtkTextTagTable * table)
+{
+ result_item_t *item = result_item_single_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ item->label = "Range Transitions";
+ item->bit_pos = POLDIFF_DIFF_RANGE_TRANS;
+ item->get_vector = poldiff_get_range_trans_vector;
+ item->get_form = poldiff_range_trans_get_form;
+ item->get_string = poldiff_range_trans_to_string;
+ item->get_forms = result_item_role_trans_get_forms; /* [sic] */
+ item->get_buffer = result_item_range_trans_get_buffer;
+ item->policy_changed = result_item_level_policy_changed; /* [sic] */
+ return item;
+}
+
+/******************** AV and Type rules below ********************/
+
+/**
+ * Show line numbers if the policy has them.
+ */
+static void result_item_multi_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_pol);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_pol);
+ item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG] = qpol_policy_has_capability(oq, QPOL_CAP_LINE_NUMBERS);
+ item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD] = qpol_policy_has_capability(mq, QPOL_CAP_LINE_NUMBERS);
+}
+
+/**
+ * Clear the cache whenever poldiff is (re-)run.
+ */
+static void result_item_multi_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental __attribute__ ((unused)))
+{
+ item->diff = diff;
+ memset(item->stats, 0, sizeof(item->stats));
+ int i;
+ for (i = 0; i < 5; i++) {
+ item->data.multi.cached[i] = 0;
+ util_text_buffer_clear(item->data.multi.buffers[i]);
+ }
+}
+
+/**
+ * Render the buffer if it has not yet been cached, then return it.
+ */
+static GtkTextBuffer *result_item_multi_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ GtkTextBuffer *tb;
+ if (form == POLDIFF_FORM_NONE) {
+ /* just use the global single_buffer when printing the
+ * summary */
+ util_text_buffer_clear(single_buffer);
+ result_item_print_summary(item, single_buffer);
+ tb = single_buffer;
+ } else {
+ tb = item->data.multi.buffers[form_reverse_map[form]];
+ if (!item->data.multi.cached[form_reverse_map[form]]) {
+ util_text_buffer_clear(tb);
+ result_item_print_header(item, tb, form);
+ item->data.multi.print_diff(item, tb, form);
+ item->data.multi.cached[form_reverse_map[form]] = 1;
+ }
+ }
+ return tb;
+}
+
+/**
+ * If the item is cached or if there are less than 50 things to show,
+ * then rendering is considered to be fast.
+ */
+static int result_item_multi_is_render_slow(result_item_t * item, poldiff_form_e form)
+{
+ if (form == POLDIFF_FORM_NONE || item->data.multi.cached[form_reverse_map[form]]
+ || result_item_get_num_differences(item, form) < 50) {
+ return 0;
+ }
+ return 1;
+}
+
+static void result_item_multi_set_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e sort,
+ results_sort_dir_e dir)
+{
+ if (item->sorts[form_reverse_map[form]] != sort || item->sort_dirs[form_reverse_map[form]] != dir) {
+ item->sorts[form_reverse_map[form]] = sort;
+ item->sort_dirs[form_reverse_map[form]] = dir;
+ item->data.multi.cached[form_reverse_map[form]] = 0;
+ }
+}
+
+static void result_item_multi_destructor(result_item_t * item)
+{
+ size_t i;
+ for (i = 0; i < 5; i++) {
+ apol_vector_destroy(&item->data.multi.items[i]);
+ }
+}
+
+/**
+ * Constructor for the abstract multi buffer item class. Multi-buffer
+ * items are capable of sorting and potentially take some time to
+ * render their buffers.
+ */
+static result_item_t *result_item_multi_create(GtkTextTagTable * table)
+{
+ result_item_t *item = calloc(1, sizeof(*item));
+ if (item == NULL) {
+ return item;
+ }
+ if (single_buffer == NULL) {
+ single_buffer = gtk_text_buffer_new(table);
+ }
+ item->supported = 1;
+ item->destructor = result_item_multi_destructor;
+ item->policy_changed = result_item_multi_policy_changed;
+ item->poldiff_run = result_item_multi_poldiff_run;
+ item->get_buffer = result_item_multi_get_buffer;
+ item->is_render_slow = result_item_multi_is_render_slow;
+ item->get_forms = result_item_role_trans_get_forms; /* [sic] */
+ item->set_current_sort = result_item_multi_set_current_sort;
+ int i;
+ for (i = 0; i < 5; i++) {
+ item->data.multi.buffers[i] = gtk_text_buffer_new(table);
+ item->sorts[i] = RESULTS_SORT_DEFAULT;
+ item->sort_dirs[i] = RESULTS_SORT_ASCEND;
+ }
+ return item;
+}
+
+struct sort_opts
+{
+ poldiff_t *diff;
+ results_sort_e field;
+ results_sort_dir_e direction;
+};
+
+static int result_item_avrule_comp(const void *a, const void *b, void *data)
+{
+ const poldiff_avrule_t *a1 = a;
+ const poldiff_avrule_t *a2 = b;
+ struct sort_opts *opts = data;
+ const char *s1, *s2;
+ switch (opts->field) {
+ case RESULTS_SORT_SOURCE:
+ {
+ s1 = poldiff_avrule_get_source_type(a1);
+ s2 = poldiff_avrule_get_source_type(a2);
+ break;
+ }
+ case RESULTS_SORT_TARGET:
+ {
+ s1 = poldiff_avrule_get_target_type(a1);
+ s2 = poldiff_avrule_get_target_type(a2);
+ break;
+ }
+ case RESULTS_SORT_CLASS:
+ {
+ s1 = poldiff_avrule_get_object_class(a1);
+ s2 = poldiff_avrule_get_object_class(a2);
+ break;
+ }
+ case RESULTS_SORT_COND:
+ {
+ const qpol_cond_t *q1, *q2;
+ const apol_policy_t *p1, *p2;
+ uint32_t w1, w2;
+ poldiff_avrule_get_cond(opts->diff, a1, &q1, &w1, &p1);
+ poldiff_avrule_get_cond(opts->diff, a2, &q2, &w2, &p2);
+ if (q1 != q2) {
+ return opts->direction * ((char *)q1 - (char *)q2);
+ }
+ return opts->direction * (w1 - w2);
+ break;
+ }
+ default:
+ {
+ /* shouldn't get here */
+ assert(0);
+ return 0;
+ }
+ }
+ return opts->direction * strcmp(s1, s2);
+}
+
+static apol_vector_t *result_item_avrule_sort(result_item_t * item, poldiff_form_e form)
+{
+ const apol_vector_t *orig_v;
+ apol_vector_t *v;
+ size_t i;
+ void *elem;
+ struct sort_opts opts = { item->diff, item->sorts[form_reverse_map[form]], item->sort_dirs[form_reverse_map[form]] };
+
+ orig_v = item->get_vector(item->diff);
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(orig_v); i++) {
+ elem = apol_vector_get_element(orig_v, i);
+ if (poldiff_avrule_get_form(elem) == form && apol_vector_append(v, elem) < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ }
+ if (opts.field != RESULTS_SORT_DEFAULT) {
+ apol_vector_sort(v, result_item_avrule_comp, &opts);
+ }
+ return v;
+}
+
+static void result_item_avrule_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form)
+{
+ GtkTextIter iter;
+ size_t i;
+ void *elem;
+ char *s;
+ GString *string = g_string_new("");
+ const apol_vector_t *syn_linenos;
+ apol_vector_t *rules = result_item_avrule_sort(item, form);
+ char *orig_prefix;
+ char *mod_prefix;
+
+ apol_vector_destroy(&item->data.multi.items[form_reverse_map[form]]);
+ item->data.multi.items[form_reverse_map[form]] = rules;
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ for (i = 0; i < apol_vector_get_size(rules); i++) {
+ elem = apol_vector_get_element(rules, i);
+ if ((s = poldiff_avrule_to_string(item->diff, elem)) == NULL) {
+ goto cleanup;
+ }
+ result_item_print_string_avrule(tb, &iter, s, 1);
+ if (form != POLDIFF_FORM_MODIFIED) {
+ orig_prefix = NULL;
+ mod_prefix = NULL;
+ } else {
+ orig_prefix = "op: ";
+ mod_prefix = "mp: ";
+ }
+ if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG] &&
+ (syn_linenos = poldiff_avrule_get_orig_line_numbers((poldiff_avrule_t *) elem)) != NULL) {
+ result_item_print_linenos(tb, &iter, orig_prefix, syn_linenos, "line-pol_orig", string);
+ }
+ if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD] &&
+ (syn_linenos = poldiff_avrule_get_mod_line_numbers((poldiff_avrule_t *) elem)) != NULL) {
+ result_item_print_linenos(tb, &iter, mod_prefix, syn_linenos, "line-pol_mod", string);
+ }
+ free(s);
+ gtk_text_buffer_insert(tb, &iter, "\n", -1);
+ }
+ cleanup:
+ g_string_free(string, TRUE);
+}
+
+/**
+ * Only support neverallows if either policy (can) has them.
+ */
+static void result_item_avrule_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ item->supported = 1;
+ if (item->bit_pos == POLDIFF_DIFF_AVNEVERALLOW) {
+ qpol_policy_t *oq = apol_policy_get_qpol(orig_pol);
+ qpol_policy_t *mq = apol_policy_get_qpol(mod_pol);
+ int orig_type = -1, mod_type = -1;
+ // don't use capability to check if neverallows are
+ // supported, because policies are always loaded
+ // without them. libpoldiff could then reload the
+ // policies with neverallow, in which case they would
+ // become capable (but item->supported still claims to
+ // be 0)
+ qpol_policy_get_type(oq, &orig_type);
+ qpol_policy_get_type(mq, &mod_type);
+ if (orig_type == QPOL_POLICY_KERNEL_BINARY && mod_type == QPOL_POLICY_KERNEL_BINARY) {
+ item->supported = 0;
+ }
+ }
+ result_item_multi_policy_changed(item, orig_pol, mod_pol);
+}
+
+/**
+ * Print an appropriate error message if neverallows are not supported.
+ */
+static GtkTextBuffer *result_item_avrule_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ if (!result_item_is_supported(item)) {
+ gtk_text_buffer_set_text(single_buffer,
+ "Neverallow diffs are not supported because both policies are kernel binaries.", -1);
+ return single_buffer;
+ } else {
+ return result_item_multi_get_buffer(item, form);
+ }
+}
+
+static result_item_t *result_item_create_from_flag(GtkTextTagTable * table, uint32_t flag)
+{
+ result_item_t *item = result_item_multi_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ const poldiff_component_record_t *rec = poldiff_get_component_record(flag);
+ item->label = poldiff_component_record_get_label(rec);
+ item->bit_pos = flag;
+ item->get_vector = poldiff_component_record_get_results_fn(rec);
+ item->get_form = poldiff_component_record_get_form_fn(rec);
+ item->get_string = poldiff_component_record_get_to_string_fn(rec);
+ item->get_buffer = result_item_avrule_get_buffer;
+ item->data.multi.print_diff = result_item_avrule_print_diff;
+ item->policy_changed = result_item_avrule_policy_changed;
+ return item;
+}
+
+result_item_t *result_item_create_avrules_allow(GtkTextTagTable * table)
+{
+ return result_item_create_from_flag(table, POLDIFF_DIFF_AVALLOW);
+}
+
+result_item_t *result_item_create_avrules_auditallow(GtkTextTagTable * table)
+{
+ return result_item_create_from_flag(table, POLDIFF_DIFF_AVAUDITALLOW);
+}
+
+result_item_t *result_item_create_avrules_dontaudit(GtkTextTagTable * table)
+{
+ return result_item_create_from_flag(table, POLDIFF_DIFF_AVDONTAUDIT);
+}
+
+result_item_t *result_item_create_avrules_neverallow(GtkTextTagTable * table)
+{
+ return result_item_create_from_flag(table, POLDIFF_DIFF_AVNEVERALLOW);
+}
+
+static int result_item_terule_comp(const void *a, const void *b, void *data)
+{
+ const poldiff_terule_t *a1 = a;
+ const poldiff_terule_t *a2 = b;
+ struct sort_opts *opts = data;
+ const char *s1, *s2;
+ switch (opts->field) {
+ case RESULTS_SORT_SOURCE:
+ {
+ s1 = poldiff_terule_get_source_type(a1);
+ s2 = poldiff_terule_get_source_type(a2);
+ break;
+ }
+ case RESULTS_SORT_TARGET:
+ {
+ s1 = poldiff_terule_get_target_type(a1);
+ s2 = poldiff_terule_get_target_type(a2);
+ break;
+ }
+ case RESULTS_SORT_CLASS:
+ {
+ s1 = poldiff_terule_get_object_class(a1);
+ s2 = poldiff_terule_get_object_class(a2);
+ break;
+ }
+ case RESULTS_SORT_COND:
+ {
+ const qpol_cond_t *q1, *q2;
+ const apol_policy_t *p1, *p2;
+ uint32_t w1, w2;
+ poldiff_terule_get_cond(opts->diff, a1, &q1, &w1, &p1);
+ poldiff_terule_get_cond(opts->diff, a2, &q2, &w2, &p2);
+ if (q1 != q2) {
+ return opts->direction * ((char *)q1 - (char *)q2);
+ }
+ return opts->direction * (w1 - w2);
+ break;
+ }
+ default:
+ {
+ /* shouldn't get here */
+ assert(0);
+ return 0;
+ }
+ }
+ return opts->direction * strcmp(s1, s2);
+}
+
+static apol_vector_t *result_item_terule_sort(result_item_t * item, poldiff_form_e form)
+{
+ const apol_vector_t *orig_v;
+ apol_vector_t *v;
+ size_t i;
+ void *elem;
+ struct sort_opts opts = { item->diff, item->sorts[form_reverse_map[form]], item->sort_dirs[form_reverse_map[form]] };
+ orig_v = item->get_vector(item->diff);
+ if ((v = apol_vector_create(NULL)) == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < apol_vector_get_size(orig_v); i++) {
+ elem = apol_vector_get_element(orig_v, i);
+ if (poldiff_terule_get_form(elem) == form && apol_vector_append(v, elem) < 0) {
+ apol_vector_destroy(&v);
+ return NULL;
+ }
+ }
+ if (opts.field != RESULTS_SORT_DEFAULT) {
+ apol_vector_sort(v, result_item_terule_comp, &opts);
+ }
+ return v;
+}
+
+static void result_item_terule_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form)
+{
+ GtkTextIter iter;
+ size_t i;
+ void *elem;
+ char *s;
+ GString *string = g_string_new("");
+ apol_vector_t *syn_linenos;
+ apol_vector_t *rules = result_item_terule_sort(item, form);
+ char *orig_prefix;
+ char *mod_prefix;
+
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ for (i = 0; i < apol_vector_get_size(rules); i++) {
+ elem = apol_vector_get_element(rules, i);
+ if ((s = poldiff_terule_to_string(item->diff, elem)) == NULL) {
+ goto cleanup;
+ }
+ if (form != POLDIFF_FORM_MODIFIED) {
+ orig_prefix = NULL;
+ mod_prefix = NULL;
+ result_item_print_string(tb, &iter, s, 1);
+ } else {
+ orig_prefix = "op: ";
+ mod_prefix = "mp: ";
+ result_item_print_string_inline(tb, &iter, s, 1);
+ }
+ if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG] &&
+ (syn_linenos = poldiff_terule_get_orig_line_numbers((poldiff_terule_t *) elem)) != NULL) {
+ result_item_print_linenos(tb, &iter, orig_prefix, syn_linenos, "line-pol_orig", string);
+ }
+ if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD] &&
+ (syn_linenos = poldiff_terule_get_mod_line_numbers((poldiff_terule_t *) elem)) != NULL) {
+ result_item_print_linenos(tb, &iter, mod_prefix, syn_linenos, "line-pol_mod", string);
+ }
+ free(s);
+ gtk_text_buffer_insert(tb, &iter, "\n", -1);
+ }
+ cleanup:
+ apol_vector_destroy(&rules);
+ g_string_free(string, TRUE);
+}
+
+static result_item_t *result_item_create_terules_from_flag(GtkTextTagTable * table, uint32_t flag)
+{
+ result_item_t *item = result_item_multi_create(table);
+ if (item == NULL) {
+ return item;
+ }
+ const poldiff_component_record_t *rec = poldiff_get_component_record(flag);
+ item->label = poldiff_component_record_get_label(rec);
+ item->bit_pos = flag;
+ item->get_vector = poldiff_component_record_get_results_fn(rec);
+ item->get_form = poldiff_component_record_get_form_fn(rec);
+ item->get_string = poldiff_component_record_get_to_string_fn(rec);
+ item->data.multi.print_diff = result_item_terule_print_diff;
+ return item;
+}
+
+result_item_t *result_item_create_terules_change(GtkTextTagTable * table)
+{
+ return result_item_create_terules_from_flag(table, POLDIFF_DIFF_TECHANGE);
+}
+
+result_item_t *result_item_create_terules_member(GtkTextTagTable * table)
+{
+ return result_item_create_terules_from_flag(table, POLDIFF_DIFF_TEMEMBER);
+}
+
+result_item_t *result_item_create_terules_trans(GtkTextTagTable * table)
+{
+ return result_item_create_terules_from_flag(table, POLDIFF_DIFF_TETRANS);
+}
+
+/******************** public methods below ********************/
+
+void result_item_destroy(result_item_t ** item)
+{
+ if (item != NULL && *item != NULL) {
+ if ((*item)->destructor != NULL) {
+ (*item)->destructor(*item);
+ } else {
+ free(*item);
+ }
+ *item = NULL;
+ }
+}
+
+const char *result_item_get_label(const result_item_t * item)
+{
+ return item->label;
+}
+
+void result_item_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol)
+{
+ if (item->policy_changed != NULL) {
+ item->policy_changed(item, orig_pol, mod_pol);
+ }
+}
+
+GtkTextBuffer *result_item_get_buffer(result_item_t * item, poldiff_form_e form)
+{
+ return item->get_buffer(item, form);
+}
+
+int result_item_is_render_slow(result_item_t * item, poldiff_form_e form)
+{
+ return item->is_render_slow(item, form);
+}
+
+void result_item_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental)
+{
+ item->poldiff_run(item, diff, incremental);
+}
+
+int result_item_is_supported(const result_item_t * item)
+{
+ return item->supported;
+}
+
+void result_item_get_forms(result_item_t * item, int forms[5])
+{
+ item->get_forms(item, forms);
+}
+
+size_t result_item_get_num_differences(result_item_t * item, poldiff_form_e form)
+{
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ return item->stats[0];
+ case POLDIFF_FORM_REMOVED:
+ return item->stats[1];
+ case POLDIFF_FORM_MODIFIED:
+ return item->stats[2];
+ case POLDIFF_FORM_ADD_TYPE:
+ return item->stats[3];
+ case POLDIFF_FORM_REMOVE_TYPE:
+ return item->stats[4];
+ default: /* should never get here */
+ assert(0);
+ return 0;
+ }
+}
+
+int result_item_get_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e * sort, results_sort_dir_e * dir)
+{
+ if (item->set_current_sort == NULL || form == POLDIFF_FORM_NONE) {
+ return 0;
+ }
+ *sort = item->sorts[form_reverse_map[form]];
+ *dir = item->sort_dirs[form_reverse_map[form]];
+ return 1;
+}
+
+void result_item_set_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e sort, results_sort_dir_e dir)
+{
+ if (item->set_current_sort != NULL && form != POLDIFF_FORM_NONE) {
+ item->set_current_sort(item, form, sort, dir);
+ }
+}
+
+void result_item_save_current_line(result_item_t * item, poldiff_form_e form, gint offset)
+{
+ /* don't care about the summary page */
+ if (form != POLDIFF_FORM_NONE) {
+ item->offsets[form_reverse_map[form]] = offset;
+ }
+}
+
+gint result_item_get_current_line(result_item_t * item, poldiff_form_e form)
+{
+ /* don't care about the summary page */
+ if (form == POLDIFF_FORM_NONE) {
+ return 0;
+ }
+ return item->offsets[form_reverse_map[form]];
+}
+
+static void result_item_on_orig_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ toplevel_t *top = (toplevel_t *) user_data;
+ GtkWidget *label = gtk_bin_get_child(GTK_BIN(menuitem));
+ unsigned long line = atoi(gtk_label_get_label(GTK_LABEL(label))) - 1;
+ toplevel_show_policy_line(top, SEDIFFX_POLICY_ORIG, line);
+}
+
+static void result_item_on_mod_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ toplevel_t *top = (toplevel_t *) user_data;
+ GtkWidget *label = gtk_bin_get_child(GTK_BIN(menuitem));
+ unsigned long line = atoi(gtk_label_get_label(GTK_LABEL(label))) - 1;
+ toplevel_show_policy_line(top, SEDIFFX_POLICY_MOD, line);
+}
+
+void result_item_inline_link_event(result_item_t * item, toplevel_t * top, GtkWidget * container, GdkEventButton * event,
+ poldiff_form_e form, int line_num, const char *s)
+{
+ /* for now, inline links only work for avrule items */
+ assert(item->bit_pos & POLDIFF_DIFF_AVRULES);
+ const char *perm;
+ poldiff_form_e perm_form = form;
+ if (*s == '+' || *s == '-' || *s == '*') {
+ if (*s == '+') {
+ perm_form = POLDIFF_FORM_ADDED;
+ } else if (*s == '-') {
+ perm_form = POLDIFF_FORM_REMOVED;
+ }
+ perm = s + 1;
+ } else {
+ perm = s;
+ }
+ apol_vector_t *rules = item->data.multi.items[form_reverse_map[form]];
+ size_t i = line_num - 3; /* subtract 3 because the header consume
+ * three lines in the text buffer */
+ poldiff_avrule_t *rule = apol_vector_get_element(rules, i);
+ assert(rule != NULL);
+
+ GtkMenu *menu = GTK_MENU(gtk_menu_new());
+ GtkWidget *menuitem;
+ GString *string = g_string_new("");
+ int button, event_time;
+ gtk_menu_set_title(menu, perm);
+ menuitem = gtk_menu_item_new_with_label(perm);
+ gtk_widget_set_sensitive(menuitem, FALSE);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ menuitem = gtk_menu_item_new_with_label("Original Policy");
+ gtk_widget_set_sensitive(menuitem, FALSE);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ if (perm_form == POLDIFF_FORM_REMOVED || perm_form == POLDIFF_FORM_REMOVE_TYPE || perm_form == POLDIFF_FORM_MODIFIED) {
+ if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_ORIG]) {
+ apol_vector_t *v = poldiff_avrule_get_orig_line_numbers_for_perm(item->diff, rule, perm);
+ if (v != NULL && apol_vector_get_size(v) > 0) {
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ unsigned long line = (unsigned long)apol_vector_get_element(v, i);
+ g_string_printf(string, " %lu", line);
+ menuitem = gtk_menu_item_new_with_label(string->str);
+ g_signal_connect(menuitem, "activate", G_CALLBACK(result_item_on_orig_activate), top);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ }
+ }
+ } else {
+ menuitem = gtk_menu_item_new_with_label(" line numbers not available");
+ gtk_widget_set_sensitive(menuitem, FALSE);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ }
+ }
+
+ menuitem = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ menuitem = gtk_menu_item_new_with_label("Modified Policy");
+ gtk_widget_set_sensitive(menuitem, FALSE);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ if (perm_form == POLDIFF_FORM_ADDED || perm_form == POLDIFF_FORM_ADD_TYPE || perm_form == POLDIFF_FORM_MODIFIED) {
+ if (item->data.multi.has_line_numbers[SEDIFFX_POLICY_MOD]) {
+ apol_vector_t *v = poldiff_avrule_get_mod_line_numbers_for_perm(item->diff, rule, perm);
+ if (v != NULL && apol_vector_get_size(v) > 0) {
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ unsigned long line = (unsigned long)apol_vector_get_element(v, i);
+ g_string_printf(string, " %lu", line);
+ menuitem = gtk_menu_item_new_with_label(string->str);
+ g_signal_connect(menuitem, "activate", G_CALLBACK(result_item_on_mod_activate), top);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ }
+ }
+ } else {
+ menuitem = gtk_menu_item_new_with_label(" line numbers not available");
+ gtk_widget_set_sensitive(menuitem, FALSE);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+ }
+ }
+ g_string_free(string, TRUE);
+ if (event != NULL) {
+ button = event->button;
+ event_time = event->time;
+ } else {
+ button = 0;
+ event_time = gtk_get_current_event_time();
+ }
+ gtk_menu_attach_to_widget(menu, container, NULL);
+ gtk_widget_show_all(GTK_WIDGET(menu));
+ gtk_menu_popup(menu, NULL, NULL, NULL, NULL, button, event_time);
+}
+
+/******************** friend methods below ********************/
+
+poldiff_t *result_item_get_diff(result_item_t * item)
+{
+ return item->diff;
+}
+
+const apol_vector_t *result_item_get_vector(result_item_t * item)
+{
+ return item->get_vector(item->diff);
+}
+
+poldiff_form_e result_item_get_form(result_item_t * item, void *elem)
+{
+ return item->get_form(elem);
+}
+
+char *result_item_get_string(result_item_t * item, void *elem)
+{
+ return item->get_string(item->diff, elem);
+}
diff --git a/sediff/result_item.h b/sediff/result_item.h
new file mode 100644
index 0000000..466b1f9
--- /dev/null
+++ b/sediff/result_item.h
@@ -0,0 +1,251 @@
+/**
+ * @file
+ * Header for showing a diff result for a single component.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef RESULT_ITEM_H
+#define RESULT_ITEM_H
+
+typedef struct result_item result_item_t;
+
+#include "results.h"
+
+#include <gtk/gtk.h>
+#include <poldiff/poldiff.h>
+#include <poldiff/component_record.h>
+
+/* constructors for various result items */
+
+result_item_t *result_item_create_classes(GtkTextTagTable * table);
+result_item_t *result_item_create_commons(GtkTextTagTable * table);
+result_item_t *result_item_create_levels(GtkTextTagTable * table);
+result_item_t *result_item_create_categories(GtkTextTagTable * table);
+result_item_t *result_item_create_types(GtkTextTagTable * table);
+result_item_t *result_item_create_attributes(GtkTextTagTable * table);
+result_item_t *result_item_create_roles(GtkTextTagTable * table);
+result_item_t *result_item_create_users(GtkTextTagTable * table);
+result_item_t *result_item_create_booleans(GtkTextTagTable * table);
+
+result_item_t *result_item_create_avrules_allow(GtkTextTagTable * table);
+result_item_t *result_item_create_avrules_auditallow(GtkTextTagTable * table);
+result_item_t *result_item_create_avrules_dontaudit(GtkTextTagTable * table);
+result_item_t *result_item_create_avrules_neverallow(GtkTextTagTable * table);
+
+result_item_t *result_item_create_terules_change(GtkTextTagTable * table);
+result_item_t *result_item_create_terules_member(GtkTextTagTable * table);
+result_item_t *result_item_create_terules_trans(GtkTextTagTable * table);
+
+result_item_t *result_item_create_role_allows(GtkTextTagTable * table);
+result_item_t *result_item_create_role_trans(GtkTextTagTable * table);
+result_item_t *result_item_create_range_trans(GtkTextTagTable * table);
+
+/**
+ * Deallocate all space associated with a result item, including the
+ * pointer itself. Does nothing if the pointer is already set to NULL.
+ *
+ * @param item Reference to the item to destroy. Afterwards it will
+ * be set to NULL.
+ */
+void result_item_destroy(result_item_t ** item);
+
+/**
+ * Get the title case label for this result item. The string will be
+ * used for printing.
+ *
+ * @param item Result item to query.
+ *
+ * @return Name of this result item.
+ */
+const char *result_item_get_label(const result_item_t * item);
+
+/**
+ * Function to update a result item whenever the policies are changed.
+ * The result item can then configure its own rendering routine.
+ *
+ * @param item Result item to modify based upon the policies.
+ * @param orig_pol Original policy to diff.
+ * @param mod_pol Modified policy to diff.
+ */
+void result_item_policy_changed(result_item_t * item, apol_policy_t * orig_pol, apol_policy_t * mod_pol);
+
+/**
+ * Function to update a result item whenever the user (re)runs the
+ * diff. This will clear the result item's cache as necessary.
+ *
+ * @param item Result item to update based upon the poldiff object.
+ * @param diff Poldiff item that was (re)run.
+ * @param incremental If non-zero, the diff was incrementally run;
+ * existing results should not be destroyed.
+ */
+void result_item_poldiff_run(result_item_t * item, poldiff_t * diff, int incremental);
+
+/**
+ * Return a text buffer that contains the rendered results for a
+ * particular policy component's form. This will re-render the buffer
+ * as necessary.
+ *
+ * @param item Result item whose display to obtain.
+ * @param form Form to display, or POLDIFF_FORM_NONE if just the
+ * summary is requested.
+ *
+ * @return A text buffer containing the results.
+ */
+GtkTextBuffer *result_item_get_buffer(result_item_t * item, poldiff_form_e form);
+
+/**
+ * If it will take a "significant" amount of time (where "significant"
+ * is some arbitrary amount) to render a buffer then sediffx will
+ * display a progress dialog while working. This function returns
+ * non-zero if it will be significantly long, 0 or not. This function
+ * will be called prior to result_item_get_buffer().
+ *
+ * @param item Result item to query.
+ * @param form Form that will be displayed, or POLDIFF_FORM_NONE if
+ * just the summary is requested.
+ *
+ * @return Non-zero if a progress dialog should be displayed, zero if
+ * not.
+ */
+int result_item_is_render_slow(result_item_t * item, poldiff_form_e form);
+
+/**
+ * Determine if a result item is capable of being run according to the
+ * given policies. For example, for binary policies prior to version
+ * 20, it is not possible to have modified types. Note that this does
+ * not necessarily mean the item has been run yet, for libpoldiff
+ * supports incremental diffing.
+ *
+ * @param item Result item to query.
+ * @param form Particular form to check if it is capable of being run
+ * or not.
+ *
+ * @return Non-zero if the result item could be run, zero if not.
+ */
+int result_item_is_supported(const result_item_t * item);
+
+/**
+ * Get a list of forms that were actually run. The result is an array
+ * of 5 integers, each corresponding to the five poldiff forms (added,
+ * add by type, removed, remove by type, modified). For each form,
+ * the possible values are:
+ *
+ * <dl>
+ * <dt>less than zero
+ * <dd>form is not supported
+ * <dt>zero
+ * <dd>form was not run
+ * <dt>greater than zero
+ * <dd>form was run
+ * </dl>
+ *
+ * @param item Result item to query.
+ * @param diff Diff structure that was run.
+ * @param forms Array into which write results.
+ */
+void result_item_get_forms(result_item_t * item, int forms[5]);
+
+/**
+ * Get the number of differences for a particular form.
+ *
+ * @param item Result item to query.
+ * @param form Difference form to query.
+ *
+ * @return Number of differences, or zero if the result item is not
+ * support or was not run.
+ */
+size_t result_item_get_num_differences(result_item_t * item, poldiff_form_e form);
+
+/**
+ * Get the current sorting algorithm and sort direction for the given
+ * result item.
+ *
+ * @param item Result item to query.
+ * @param form Form whose sort algorithm and direction to get.
+ * @param sort Reference to where to write the current sorting algorithm.
+ * @param dir Reference to where to write the current sorting direction.
+ *
+ * @return Non-zero if the result item supports sorting, zero if it
+ * does not.
+ */
+int result_item_get_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e * sort, results_sort_dir_e * dir);
+
+/**
+ * Set the current sorting algorithm and sort direction for the given
+ * result item. The next time result_item_get_buffer() is called the
+ * contents of the buffer will be updated as necessary. (This
+ * function does not update the buffer.)
+ *
+ * @param item Result item to modify.
+ * @param form Form whose sort algorithm and direction to set.
+ * @param sort New sorting algorithm.
+ * @param dir New sorting direction.
+ */
+void result_item_set_current_sort(result_item_t * item, poldiff_form_e form, results_sort_e sort, results_sort_dir_e dir);
+
+/**
+ * Tell the result item to store a particular line offset for the
+ * given form.
+ *
+ * @param item Result item to modify.
+ * @param form Particular form's line number to store.
+ * @param offset Line number to store.
+ */
+void result_item_save_current_line(result_item_t * item, poldiff_form_e form, gint offset);
+
+/**
+ * Return the saved line number for a result item's particular form.
+ * If a line number has not yet been saved then return 0.
+ *
+ * @param item Result item to query.
+ * @param form Form whose line number to retrieve.
+ *
+ * @return The stored line number.
+ */
+gint result_item_get_current_line(result_item_t * item, poldiff_form_e form);
+
+/**
+ * Callback invoked by results_t whenever a inlink-link tag was
+ * clicked. This will pop a menu that will let the user jump to the
+ * exact line in the policy that contains that string.
+ *
+ * @param item Result item upon which the event occurred.
+ * @param top Toplevel containing policy sources.
+ * @param container Containing GTK widget within which the result item
+ * is being displayed.
+ * @param event GdkEvent that gives the button used to click on the
+ * inlink link, or NULL if the event was generated by hitting the Menu
+ * button.
+ * @param form Form upon which the event occurred.
+ * @param line_num Line number of the text buffer where the clicked occurred.
+ * @param s The string that was clicked.
+ */
+void result_item_inline_link_event(result_item_t * item, toplevel_t * top, GtkWidget * container, GdkEventButton * event,
+ poldiff_form_e form, int line_num, const char *s);
+
+/* these next three functions exist because C has no concept of
+ * 'friend' like in C++; result_item_render needs access to three
+ * fields within the result_item. */
+poldiff_t *result_item_get_diff(result_item_t * item);
+const apol_vector_t *result_item_get_vector(result_item_t * item);
+poldiff_form_e result_item_get_form(result_item_t * item, void *elem);
+char *result_item_get_string(result_item_t * item, void *elem);
+
+#endif
diff --git a/sediff/result_item_render.c b/sediff/result_item_render.c
new file mode 100644
index 0000000..36fbbfb
--- /dev/null
+++ b/sediff/result_item_render.c
@@ -0,0 +1,421 @@
+/**
+ * @file
+ * Common rendering routines for result items.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "result_item_render.h"
+
+#include <assert.h>
+
+static const char *form_name_map[] = {
+ "Added", "Added New Type", "Removed", "Removed Missing Type", "Modified"
+};
+static const char *form_name_long_map[] = {
+ "Added", "Added because of new type", "Removed", "Removed because of missing type", "Modified"
+};
+static const char *tag_map[] = {
+ "added-header", "added-header", "removed-header", "removed-header", "modified-header"
+};
+static const poldiff_form_e form_map[] = {
+ POLDIFF_FORM_ADDED, POLDIFF_FORM_ADD_TYPE,
+ POLDIFF_FORM_REMOVED, POLDIFF_FORM_REMOVE_TYPE,
+ POLDIFF_FORM_MODIFIED
+};
+
+/**
+ * Show a single diff item string. This will add the appropriate
+ * color tags based upon the item's first non-space character.
+ */
+void result_item_print_string(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level)
+{
+ const char *c;
+ unsigned int i;
+ size_t start = 0, end = 0;
+ static const char *indent = "\t";
+ const gchar *tag = NULL;
+ for (i = 0; i < indent_level; i++) {
+ gtk_text_buffer_insert(tb, iter, indent, -1);
+ }
+ for (c = s; *c && tag == NULL; c++) {
+ switch (*c) {
+ case '+':
+ {
+ tag = "added";
+ break;
+ }
+ case '-':
+ {
+ tag = "removed";
+ break;
+ }
+ case ' ':
+ case '\t':
+ case '\n':
+ {
+ break;
+ }
+ default:
+ {
+ tag = "modified";
+ break;
+ }
+ }
+ }
+ for (c = s; *c; c++, end++) {
+ if (*c == '\n' && *(c + 1) != '\0') {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start + 1, tag, NULL);
+ for (i = 0; i < indent_level; i++) {
+ gtk_text_buffer_insert(tb, iter, indent, -1);
+ }
+ start = end + 1;
+ }
+ }
+ if (start < end) {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, tag, NULL);
+ }
+}
+
+void result_item_print_string_inline(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level)
+{
+ const char *c = s;
+ unsigned int i;
+ size_t start = 0, end = 0;
+ static const char *indent = "\t";
+ const gchar *current_tag = "modified";
+ for (i = 0; i < indent_level; i++) {
+ gtk_text_buffer_insert(tb, iter, indent, -1);
+ }
+ for (; *c; c++, end++) {
+ switch (*c) {
+ case '+':
+ {
+ if (end > 0) {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, current_tag, NULL);
+ }
+ start = end;
+ current_tag = "added";
+ break;
+ }
+ case '-':
+ {
+ if (end > 0) {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, current_tag, NULL);
+ }
+ start = end;
+ current_tag = "removed";
+ break;
+ }
+ case '\n':
+ {
+ if (*(c + 1) != '\0') {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start + 1, current_tag, NULL);
+ for (i = 0; i < indent_level; i++) {
+ gtk_text_buffer_insert(tb, iter, indent, -1);
+ }
+ start = end + 1;
+ }
+ break;
+ }
+ case ' ':
+ {
+ if (current_tag != "modified") {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start + 1, current_tag, NULL);
+ start = end + 1;
+ current_tag = "modified";
+ }
+ break;
+ }
+ }
+ }
+ if (start < end) {
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s + start, end - start, current_tag, NULL);
+ }
+}
+
+void result_item_print_string_avrule(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level)
+{
+ const char *c, *next_c;
+ const char *tag = NULL, *init_tag;
+ unsigned int i;
+ static const char *indent = "\t";
+ for (i = 0; i < indent_level; i++) {
+ gtk_text_buffer_insert(tb, iter, indent, -1);
+ }
+ for (c = s; *c && tag == NULL; c++) {
+ switch (*c) {
+ case '+':
+ {
+ tag = "added";
+ break;
+ }
+ case '-':
+ {
+ tag = "removed";
+ break;
+ }
+ case ' ':
+ case '\t':
+ case '\n':
+ {
+ break;
+ }
+ default:
+ {
+ tag = "modified";
+ break;
+ }
+ }
+ }
+ init_tag = tag;
+ c = strchr(s, '{');
+ assert(c != NULL);
+ /* advance past the opening brace (which designates start of
+ * the permissions list) and the following space character */
+ c += 2;
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, s, c - s, init_tag, NULL);
+ while (*c != '}') {
+ assert(*c != '\0');
+ next_c = strchr(c, ' ');
+ assert(next_c != NULL);
+ switch (*c) {
+ case '+':
+ {
+ tag = "added";
+ break;
+ }
+ case '-':
+ {
+ tag = "removed";
+ break;
+ }
+ default:
+ {
+ tag = init_tag;
+ }
+ }
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, c, next_c - c, tag, "inline-link", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, " ", 1, init_tag, NULL);
+ /* advance past the space */
+ c = next_c + 1;
+ }
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, c, -1, init_tag, NULL);
+}
+
+void result_item_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form)
+{
+ GtkTextIter iter;
+ const apol_vector_t *v;
+ size_t i;
+ void *elem;
+ char *s = NULL;
+
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ v = result_item_get_vector(item);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ elem = apol_vector_get_element(v, i);
+ if (result_item_get_form(item, elem) == form) {
+ s = result_item_get_string(item, elem);
+ result_item_print_string(tb, &iter, s, 1);
+ free(s);
+ gtk_text_buffer_insert(tb, &iter, "\n", -1);
+ }
+ }
+}
+
+void result_item_print_rule_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form)
+{
+ GtkTextIter iter;
+ const apol_vector_t *v;
+ size_t i;
+ void *elem;
+ char *s = NULL;
+
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ v = result_item_get_vector(item);
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ elem = apol_vector_get_element(v, i);
+ if (result_item_get_form(item, elem) == form) {
+ s = result_item_get_string(item, elem);
+ if (form != POLDIFF_FORM_MODIFIED) {
+ result_item_print_string(tb, &iter, s, 1);
+ } else {
+ result_item_print_string_inline(tb, &iter, s, 1);
+ }
+ free(s);
+ gtk_text_buffer_insert(tb, &iter, "\n", -1);
+ }
+ }
+}
+
+void result_item_print_summary(result_item_t * item, GtkTextBuffer * tb)
+{
+ GtkTextIter iter;
+ int i, forms[5];
+ GString *string = g_string_new("");
+
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ g_string_printf(string, "%s:\n", result_item_get_label(item));
+ gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, "subheader", NULL);
+
+ int was_run = 0;
+ result_item_get_forms(item, forms);
+ for (i = 0; i < 5; i++) {
+ if (forms[i] > 0) {
+ g_string_printf(string, "\t%s: %zd\n",
+ form_name_long_map[i], result_item_get_num_differences(item, form_map[i]));
+ gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, tag_map[i], NULL);
+ was_run = 1;
+ }
+ }
+ if (!was_run && result_item_is_supported(item)) {
+ gtk_text_buffer_set_text(tb, "This component has not yet been diffed.", -1);
+ }
+ g_string_free(string, TRUE);
+}
+
+/**
+ * Show a common header when printing a policy component diff.
+ */
+void result_item_print_header(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form)
+{
+ GtkTextIter iter;
+ int i, forms[5];
+ GString *string = g_string_new("");
+ char *tag = NULL;
+ const char *label = result_item_get_label(item);
+ int add_separator = 0;
+
+ gtk_text_buffer_get_end_iter(tb, &iter);
+ result_item_get_forms(item, forms);
+ g_string_printf(string, "%s (", label);
+ for (i = 0; i < 5; i++) {
+ if (forms[i] > 0) {
+ g_string_append_printf(string, "%s%zd %s",
+ (add_separator ? ", " : ""),
+ result_item_get_num_differences(item, form_map[i]), form_name_map[i]);
+ add_separator = 1;
+ }
+ }
+ g_string_append_printf(string, ")\n\n");
+ gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, "header", NULL);
+
+ switch (form) {
+ case POLDIFF_FORM_ADDED:
+ {
+ g_string_printf(string, "Added %s:", label);
+ tag = "added-header";
+ break;
+ }
+ case POLDIFF_FORM_ADD_TYPE:
+ {
+ g_string_printf(string, "Added %s because of new type:", label);
+ tag = "added-header";
+ break;
+ }
+ case POLDIFF_FORM_REMOVED:
+ {
+ g_string_printf(string, "Removed %s:", label);
+ tag = "removed-header";
+ break;
+ }
+ case POLDIFF_FORM_REMOVE_TYPE:
+ {
+ g_string_printf(string, "Removed %s because of missing type:", label);
+ tag = "removed-header";
+ break;
+ }
+ case POLDIFF_FORM_MODIFIED:
+ {
+ g_string_printf(string, "Modified %s:", label);
+ tag = "modified-header";
+ break;
+ }
+ default:
+ {
+ assert(0);
+ tag = NULL;
+ }
+ }
+ g_string_append_printf(string, " %zd\n", result_item_get_num_differences(item, form));
+ gtk_text_buffer_insert_with_tags_by_name(tb, &iter, string->str, -1, tag, NULL);
+ g_string_free(string, TRUE);
+}
+
+void result_item_print_linenos(GtkTextBuffer * tb, GtkTextIter * iter,
+ const gchar * prefix, const apol_vector_t * linenos, const gchar * tag, GString * string)
+{
+ size_t i;
+ unsigned long lineno;
+ gtk_text_buffer_insert(tb, iter, " [", -1);
+ if (prefix != NULL) {
+ gtk_text_buffer_insert(tb, iter, prefix, -1);
+ }
+ for (i = 0; i < apol_vector_get_size(linenos); i++) {
+ lineno = (unsigned long)apol_vector_get_element(linenos, i);
+ if (i > 0) {
+ gtk_text_buffer_insert(tb, iter, ", ", -1);
+ }
+ g_string_printf(string, "%lu", lineno);
+ gtk_text_buffer_insert_with_tags_by_name(tb, iter, string->str, -1, tag, NULL);
+ }
+ gtk_text_buffer_insert(tb, iter, "]", -1);
+}
+
+void result_item_print_modified_range(result_item_t * item, const poldiff_range_t * range, GtkTextBuffer * tb, GtkTextIter * iter)
+{
+ poldiff_t *diff = result_item_get_diff(item);
+ char *orig_s = poldiff_range_to_string_brief(diff, range);
+ char *next_s = orig_s;
+ GString *string = g_string_new("");
+
+ /* first line should always be printed with normal font */
+ char *s = strsep(&next_s, "\n");
+ result_item_print_string(tb, iter, s, 1);
+ gtk_text_buffer_insert(tb, iter, "\n", -1);
+
+ /* if the next line is minimum category set differences then
+ * display it */
+ if (strncmp(next_s, " minimum categories:", strlen(" minimum categories:")) == 0) {
+ s = strsep(&next_s, "\n");
+ result_item_print_string_inline(tb, iter, s, 1);
+ gtk_text_buffer_insert(tb, iter, "\n", -1);
+ }
+ /* all subsequent lines are printed as normal (yes, this
+ * discards lines from poldiff_range_to_string_brief() */
+ free(orig_s);
+ apol_vector_t *levels = poldiff_range_get_levels(range);
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(levels); i++) {
+ poldiff_level_t *l = apol_vector_get_element(levels, i);
+ s = poldiff_level_to_string_brief(diff, l);
+ g_string_printf(string, " %s", s);
+ if (poldiff_level_get_form(l) != POLDIFF_FORM_MODIFIED) {
+ result_item_print_string(tb, iter, string->str, 1);
+ } else {
+ result_item_print_string_inline(tb, iter, string->str, 1);
+ }
+ free(s);
+ }
+ g_string_free(string, TRUE);
+}
diff --git a/sediff/result_item_render.h b/sediff/result_item_render.h
new file mode 100644
index 0000000..e593550
--- /dev/null
+++ b/sediff/result_item_render.h
@@ -0,0 +1,79 @@
+/**
+ * @file
+ * Header for rendering a result item.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef RESULT_ITEM_RENDER_H
+#define RESULT_ITEM_RENDER_H
+
+#include "result_item.h"
+
+/**
+ * Show a single diff item string. This will add the appropriate
+ * color tags based upon the item's first non-space character.
+ */
+void result_item_print_string(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level);
+
+/**
+ * Print a string to a text buffer. Note that this differs from the
+ * more general results_print_string() because there are inline '+'
+ * and '-' markers.
+ */
+void result_item_print_string_inline(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level);
+
+/**
+ * Show the results for a single AV rule diff string.
+ */
+void result_item_print_string_avrule(GtkTextBuffer * tb, GtkTextIter * iter, const char *s, unsigned int indent_level);
+
+/**
+ * Show the results for non-rules diff components.
+ */
+void result_item_print_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form);
+
+/**
+ * Show the results for rules diff components.
+ */
+void result_item_print_rule_diff(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form);
+
+/**
+ * Show a summary of the diff for a particular policy component.
+ */
+void result_item_print_summary(result_item_t * item, GtkTextBuffer * tb);
+
+/**
+ * Show a common header when printing a policy component diff.
+ */
+void result_item_print_header(result_item_t * item, GtkTextBuffer * tb, poldiff_form_e form);
+
+/**
+ * Given a vector of unsigned long integers, write to the text buffer
+ * those line numbers using the given tag.
+ */
+void result_item_print_linenos(GtkTextBuffer * tb, GtkTextIter * iter,
+ const gchar * prefix, const apol_vector_t * linenos, const gchar * tag, GString * string);
+
+/**
+ * Show the results for a modified range.
+ */
+void result_item_print_modified_range(result_item_t * item, const poldiff_range_t * range, GtkTextBuffer * tb, GtkTextIter * iter);
+
+#endif
diff --git a/sediff/results.c b/sediff/results.c
new file mode 100644
index 0000000..f19689f
--- /dev/null
+++ b/sediff/results.c
@@ -0,0 +1,708 @@
+/**
+ * @file
+ * Routines for displaying the results after running poldiff.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "result_item.h"
+#include "results.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <glade/glade.h>
+#include <qpol/cond_query.h>
+
+enum
+{
+ RESULTS_SUMMARY_COLUMN_LABEL = 0,
+ RESULTS_SUMMARY_COLUMN_FORM,
+ RESULTS_SUMMARY_COLUMN_ITEM,
+ RESULTS_SUMMARY_COLUMN_STYLE,
+ RESULTS_SUMMARY_COLUMN_NUM
+};
+
+#define NUM_RESULT_ITEMS 19
+
+struct results
+{
+ toplevel_t *top;
+ GladeXML *xml;
+ GtkTreeStore *summary_tree;
+ GtkTreeView *summary_view;
+ GtkTextBuffer *main_buffer, *key_buffer;
+ GtkTextView *view;
+ GtkTextTag *policy_orig_tag, *policy_mod_tag;
+ GtkLabel *stats;
+ /** pointer to within items[] of the currently selected result
+ * item, or NULL if the summary page or none are selected */
+ result_item_t *current_item;
+ /** form that is currently selected, or POLDIFF_FORM_NONE if
+ * the summary or item's summary page is selected */
+ poldiff_form_e current_form;
+ /** saved cursor's line number for the summary page */
+ gint summary_offset;
+ result_item_t *items[NUM_RESULT_ITEMS];
+};
+
+static const poldiff_form_e form_map[] = {
+ POLDIFF_FORM_ADDED, POLDIFF_FORM_ADD_TYPE,
+ POLDIFF_FORM_REMOVED, POLDIFF_FORM_REMOVE_TYPE,
+ POLDIFF_FORM_MODIFIED
+};
+
+/**
+ * Array or result_item constructors. Note that the order given below
+ * governs the order that the items appear in the results summary
+ * tree.
+ */
+static result_item_t *(*result_item_constructors[NUM_RESULT_ITEMS]) (GtkTextTagTable *) = {
+result_item_create_commons, result_item_create_classes,
+ result_item_create_levels, result_item_create_categories,
+ result_item_create_types, result_item_create_attributes,
+ result_item_create_roles, result_item_create_users,
+ result_item_create_booleans,
+ result_item_create_avrules_allow,
+ result_item_create_avrules_auditallow,
+ result_item_create_avrules_dontaudit,
+ result_item_create_avrules_neverallow,
+ result_item_create_terules_change,
+ result_item_create_terules_member,
+ result_item_create_terules_trans,
+ result_item_create_role_allows, result_item_create_role_trans, result_item_create_range_trans};
+
+static void results_summary_on_change(GtkTreeSelection * selection, gpointer user_data);
+
+static gboolean results_on_inline_link_event(GtkTextTag * tag, GObject * event_object,
+ GdkEvent * event, const GtkTextIter * iter, gpointer user_data);
+static gboolean results_on_line_event(GtkTextTag * tag, GObject * event_object,
+ GdkEvent * event, const GtkTextIter * iter, gpointer user_data);
+static gboolean results_on_popup_menu(GtkWidget * widget, gpointer user_data);
+static gboolean results_on_text_view_motion(GtkWidget * widget, GdkEventMotion * event, gpointer user_data);
+/**
+ * Callback whenever the user double-clicks a row in the summary tree.
+ */
+static void results_summary_on_row_activate(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column,
+ gpointer user_data);
+
+/**
+ * Build a GTK tree store to hold the summary table of contents; then
+ * add that (empty) tree to the tree view.
+ */
+static void results_create_summary(results_t * r)
+{
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *selection;
+
+ r->summary_tree = gtk_tree_store_new(RESULTS_SUMMARY_COLUMN_NUM, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_BOOLEAN);
+ r->summary_view = GTK_TREE_VIEW(glade_xml_get_widget(r->xml, "toplevel summary view"));
+ assert(r->summary_view != NULL);
+ col = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
+ gtk_tree_view_column_set_title(col, "Differences");
+ gtk_tree_view_append_column(r->summary_view, col);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(col, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(col, renderer,
+ "text", RESULTS_SUMMARY_COLUMN_LABEL,
+ "strikethrough", RESULTS_SUMMARY_COLUMN_STYLE, NULL);;
+ gtk_tree_view_set_model(r->summary_view, GTK_TREE_MODEL(r->summary_tree));
+
+ selection = gtk_tree_view_get_selection(r->summary_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(results_summary_on_change), r);
+ g_signal_connect(r->summary_view, "row-activated", G_CALLBACK(results_summary_on_row_activate), r);
+}
+
+results_t *results_create(toplevel_t * top)
+{
+ results_t *r;
+ int i;
+ GtkTextTagTable *tag_table;
+ GtkTextAttributes *attr;
+ GtkTextView *text_view;
+ gint size;
+ PangoTabArray *tabs;
+
+ if ((r = calloc(1, sizeof(*r))) == NULL) {
+ return NULL;
+ }
+ r->top = top;
+ r->xml = glade_get_widget_tree(GTK_WIDGET(toplevel_get_window(r->top)));
+ results_create_summary(r);
+
+ tag_table = gtk_text_tag_table_new();
+ r->main_buffer = gtk_text_buffer_new(tag_table);
+ gtk_text_buffer_create_tag(r->main_buffer, "header", "style", PANGO_STYLE_ITALIC, "weight", PANGO_WEIGHT_BOLD, "family",
+ NULL, NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "subheader", "weight", PANGO_WEIGHT_BOLD, "underline", PANGO_UNDERLINE_SINGLE,
+ "family", NULL, NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "removed-header", "foreground", "red", "weight", PANGO_WEIGHT_BOLD, NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "added-header", "foreground", "dark green", "weight", PANGO_WEIGHT_BOLD, NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "modified-header", "foreground", "dark blue", "weight", PANGO_WEIGHT_BOLD, NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "removed", "foreground", "red", NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "added", "foreground", "dark green", NULL);
+ gtk_text_buffer_create_tag(r->main_buffer, "modified", "foreground", "dark blue", NULL);
+ GtkTextTag *inline_tag = gtk_text_buffer_create_tag(r->main_buffer, "inline-link", NULL);
+ g_signal_connect_after(G_OBJECT(inline_tag), "event", G_CALLBACK(results_on_inline_link_event), r);
+ r->policy_orig_tag = gtk_text_buffer_create_tag(r->main_buffer, "line-pol_orig",
+ "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
+ g_signal_connect_after(G_OBJECT(r->policy_orig_tag), "event", G_CALLBACK(results_on_line_event), r);
+ r->policy_mod_tag = gtk_text_buffer_create_tag(r->main_buffer, "line-pol_mod",
+ "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
+ g_signal_connect_after(G_OBJECT(r->policy_mod_tag), "event", G_CALLBACK(results_on_line_event), r);
+
+ r->view = GTK_TEXT_VIEW(glade_xml_get_widget(r->xml, "toplevel results view"));
+ assert(r->view != NULL);
+ g_signal_connect(G_OBJECT(r->view), "popup-menu", G_CALLBACK(results_on_popup_menu), r);
+ g_signal_connect(G_OBJECT(r->view), "motion-notify-event", G_CALLBACK(results_on_text_view_motion), r);
+ attr = gtk_text_view_get_default_attributes(r->view);
+ size = pango_font_description_get_size(attr->font);
+ tabs = pango_tab_array_new_with_positions(4,
+ FALSE,
+ PANGO_TAB_LEFT, 3 * size,
+ PANGO_TAB_LEFT, 6 * size, PANGO_TAB_LEFT, 9 * size, PANGO_TAB_LEFT, 12 * size);
+ gtk_text_view_set_tabs(r->view, tabs);
+ gtk_text_view_set_buffer(r->view, r->main_buffer);
+
+ r->key_buffer = gtk_text_buffer_new(tag_table);
+ text_view = GTK_TEXT_VIEW(glade_xml_get_widget(r->xml, "toplevel key view"));
+ assert(text_view != NULL);
+ gtk_text_view_set_buffer(text_view, r->key_buffer);
+
+ PangoFontDescription *font_desc = pango_font_description_new();
+ pango_font_description_set_family_static(font_desc, "monospace");
+ gtk_widget_modify_font(GTK_WIDGET(r->view), font_desc);
+ gtk_widget_modify_font(GTK_WIDGET(text_view), font_desc);
+ pango_font_description_free(font_desc);
+
+ r->stats = GTK_LABEL((glade_xml_get_widget(r->xml, "toplevel stats label")));
+ assert(r->stats != NULL);
+ gtk_label_set_text(r->stats, "");
+
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ if ((r->items[i] = result_item_constructors[i] (tag_table)) == NULL) {
+ results_destroy(&r);
+ return NULL;
+ }
+ }
+ return r;
+}
+
+void results_destroy(results_t ** r)
+{
+ if (r != NULL && *r != NULL) {
+ int i;
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ result_item_destroy(&((*r)->items[i]));
+ }
+ free(*r);
+ *r = NULL;
+ }
+}
+
+void results_open_policies(results_t * r, apol_policy_t * orig, apol_policy_t * mod)
+{
+ int i;
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ result_item_policy_changed(r->items[i], orig, mod);
+ }
+}
+
+void results_clear(results_t * r)
+{
+ gtk_tree_store_clear(r->summary_tree);
+ gtk_text_view_set_buffer(r->view, r->main_buffer);
+ util_text_buffer_clear(r->main_buffer);
+ util_text_buffer_clear(r->key_buffer);
+ gtk_label_set_text(r->stats, "");
+ r->current_item = NULL;
+ r->current_form = POLDIFF_FORM_NONE;
+ r->summary_offset = 0;
+}
+
+/**
+ * Update the summary tree and summary buffer to reflect the number of
+ * items added/removed/modified.
+ */
+static void results_update_summary(results_t * r)
+{
+ GtkTreeIter topiter, childiter;
+ GtkTextIter iter;
+ size_t sum_diffs;
+ int i, j, forms[5];
+ GString *s = g_string_new("");
+
+ gtk_tree_store_append(r->summary_tree, &topiter, NULL);
+ gtk_tree_store_set(r->summary_tree, &topiter,
+ RESULTS_SUMMARY_COLUMN_LABEL, "Summary",
+ RESULTS_SUMMARY_COLUMN_FORM, POLDIFF_FORM_NONE, RESULTS_SUMMARY_COLUMN_ITEM, NULL, -1);
+ gtk_text_buffer_get_start_iter(r->main_buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name(r->main_buffer, &iter, "Policy Difference Statistics\n", -1, "header", NULL);
+ static const char *form_name_short_map[] = {
+ "Added", "Added Type", "Removed", "Removed Type", "Modified"
+ };
+ static const char *form_name_long_map[] = {
+ "Added", "Added because of new type", "Removed", "Removed because of missing type", "Modified"
+ };
+ static const char *tag_map[] = {
+ "added-header", "added-header", "removed-header", "removed-header", "modified-header"
+ };
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ const char *label = result_item_get_label(r->items[i]);
+ gtk_tree_store_append(r->summary_tree, &topiter, NULL);
+ gtk_tree_store_set(r->summary_tree, &topiter,
+ RESULTS_SUMMARY_COLUMN_FORM, POLDIFF_FORM_NONE, RESULTS_SUMMARY_COLUMN_ITEM, r->items[i], -1);
+ if (result_item_is_supported(r->items[i])) {
+ result_item_get_forms(r->items[i], forms);
+ sum_diffs = 0;
+ g_string_printf(s, "\n%s:\n", label);
+ gtk_text_buffer_insert_with_tags_by_name(r->main_buffer, &iter, s->str, -1, "subheader", NULL);
+ int was_run = 0;
+ for (j = 0; j < 5; j++) {
+ if (forms[j] > 0) {
+ size_t num_diffs;
+ num_diffs = result_item_get_num_differences(r->items[i], form_map[j]);
+ g_string_printf(s, "\t%s: %zd\n", form_name_long_map[j], num_diffs);
+ gtk_text_buffer_insert_with_tags_by_name(r->main_buffer, &iter, s->str, -1, tag_map[j],
+ NULL);
+ sum_diffs += num_diffs;
+ gtk_tree_store_append(r->summary_tree, &childiter, &topiter);
+ g_string_printf(s, "%s (%zd)", form_name_short_map[j], num_diffs);
+ gtk_tree_store_set(r->summary_tree, &childiter,
+ RESULTS_SUMMARY_COLUMN_LABEL, s->str,
+ RESULTS_SUMMARY_COLUMN_FORM, form_map[j],
+ RESULTS_SUMMARY_COLUMN_ITEM, r->items[i], -1);
+ was_run = 1;
+ }
+ }
+ if (!was_run) {
+ g_string_printf(s, "%s (\?\?\?)", label);
+ } else {
+ g_string_printf(s, "%s (%zd)", label, sum_diffs);
+ }
+ gtk_tree_store_set(r->summary_tree, &topiter,
+ RESULTS_SUMMARY_COLUMN_LABEL, s->str, RESULTS_SUMMARY_COLUMN_STYLE, FALSE, -1);
+ } else {
+ /* item is not supported at all */
+ g_string_printf(s, "%s (N/A)", label);
+ gtk_tree_store_set(r->summary_tree, &topiter,
+ RESULTS_SUMMARY_COLUMN_LABEL, s->str, RESULTS_SUMMARY_COLUMN_STYLE, FALSE, -1);
+ }
+ }
+
+ g_string_free(s, TRUE);
+}
+
+/**
+ * Show the legend of the symbols used in results displays.
+ */
+static void results_populate_key_buffer(results_t * r)
+{
+ GString *string = g_string_new("");
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_end_iter(r->key_buffer, &iter);
+
+ g_string_printf(string, " Added(+):\n Items added in\n modified policy.\n\n");
+ gtk_text_buffer_insert_with_tags_by_name(r->key_buffer, &iter, string->str, -1, "added", NULL);
+ g_string_printf(string, " Removed(-):\n Items removed\n from original\n policy.\n\n");
+ gtk_text_buffer_insert_with_tags_by_name(r->key_buffer, &iter, string->str, -1, "removed", NULL);
+ g_string_printf(string, " Modified(*):\n Items modified\n from original\n policy to\n modified policy.");
+ gtk_text_buffer_insert_with_tags_by_name(r->key_buffer, &iter, string->str, -1, "modified", NULL);
+ g_string_free(string, TRUE);
+}
+
+/**
+ * Populate the status bar with summary info of our diff.
+ */
+static void results_update_stats(results_t * r)
+{
+ GString *string = g_string_new("");
+ int i, j, forms[5];
+ size_t diffs[5] = { 0, 0, 0, 0, 0 };
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ if (result_item_is_supported(r->items[i])) {
+ const char *label;
+ result_item_get_forms(r->items[i], forms);
+ label = result_item_get_label(r->items[i]);
+ for (j = 0; j < 5; j++) {
+ if (forms[j] > 0) {
+ diffs[j] += result_item_get_num_differences(r->items[i], form_map[j]);
+ }
+ }
+ }
+ }
+ g_string_printf(string, "Total Differences: %zd", diffs[0] + diffs[1] + diffs[2] + diffs[3] + diffs[4]);
+ gtk_label_set_text(r->stats, string->str);
+ g_string_free(string, TRUE);
+}
+
+void results_update(results_t * r)
+{
+ int i, j, forms[5], was_diff_run = 0;
+ poldiff_t *diff = toplevel_get_poldiff(r->top);
+
+ results_clear(r);
+
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ result_item_poldiff_run(r->items[i], diff, 0);
+ }
+ /* only show diff-relevant buffers if a diff was actually run */
+ for (i = 0; i < NUM_RESULT_ITEMS; i++) {
+ if (result_item_is_supported(r->items[i])) {
+ result_item_get_forms(r->items[i], forms);
+ for (j = 0; j < 5; j++) {
+ if (forms[j] > 0) {
+ was_diff_run = 1;
+ break;
+ }
+ }
+ }
+ }
+ if (was_diff_run) {
+ results_update_summary(r);
+ results_populate_key_buffer(r);
+ results_update_stats(r);
+
+ /* select the summary item */
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(r->summary_view);
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(r->summary_tree), &iter);
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+}
+
+void results_switch_to_page(results_t * r)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(r->summary_view);
+ GtkTreeIter iter;
+ gboolean sens = FALSE;
+ if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ int f;
+ result_item_t *item;
+ results_sort_e sort;
+ results_sort_dir_e dir;
+ gtk_tree_model_get(GTK_TREE_MODEL(r->summary_tree), &iter, RESULTS_SUMMARY_COLUMN_FORM, &f,
+ RESULTS_SUMMARY_COLUMN_ITEM, &item, -1);
+ poldiff_form_e form = (poldiff_form_e) f;
+ if (item != NULL && result_item_get_current_sort(item, form, &sort, &dir)) {
+ sens = TRUE;
+ }
+ }
+ toplevel_set_sort_menu_sensitivity(r->top, sens);
+}
+
+struct run_datum
+{
+ results_t *r;
+ progress_t *progress;
+ GtkTextBuffer *tb;
+};
+
+static gpointer results_get_slow_buffer_runner(gpointer data)
+{
+ struct run_datum *run = (struct run_datum *)data;
+ results_t *r = run->r;
+ run->tb = result_item_get_buffer(r->current_item, r->current_form);
+ progress_done(run->progress);
+ return NULL;
+}
+
+/**
+ * Spawn a thread that will get the result_item buffer as given by
+ * r->current_item and r->current_form. This will display a progress
+ * dialog while waiting.
+ */
+static GtkTextBuffer *results_get_slow_buffer(results_t * r, const char *action)
+{
+ struct run_datum run;
+ run.r = r;
+ run.progress = toplevel_get_progress(r->top);
+ util_cursor_wait(GTK_WIDGET(toplevel_get_window(r->top)));
+ GString *s = g_string_new("");
+ g_string_printf(s, "Rendering %s", result_item_get_label(r->current_item));
+ progress_show(run.progress, s->str);
+ g_string_free(s, TRUE);
+ progress_update(run.progress, "%s", action);
+ g_thread_create(results_get_slow_buffer_runner, &run, FALSE, NULL);
+ progress_wait(run.progress);
+ util_cursor_clear(GTK_WIDGET(toplevel_get_window(r->top)));
+ progress_hide(run.progress);
+ return run.tb;
+}
+
+/**
+ * Callback invoked when the user selects an entry from the summary
+ * tree.
+ */
+static void results_summary_on_change(GtkTreeSelection * selection, gpointer user_data)
+{
+ results_t *r = (results_t *) user_data;
+ GtkTreeIter iter;
+ if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ int form;
+ GdkRectangle rect;
+ GtkTextIter textiter;
+ GtkTextMark *mark;
+ gint offset;
+ GtkTextBuffer *tb;
+ results_sort_e sort;
+ results_sort_dir_e dir;
+ gboolean sens = FALSE;
+
+ gtk_text_view_get_visible_rect(r->view, &rect);
+ gtk_text_view_get_iter_at_location(r->view, &textiter, rect.x, rect.y);
+ offset = gtk_text_iter_get_offset(&textiter);
+ if (r->current_item == NULL) {
+ r->summary_offset = offset;
+ } else {
+ result_item_save_current_line(r->current_item, r->current_form, offset);
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(r->summary_tree), &iter, RESULTS_SUMMARY_COLUMN_FORM, &form,
+ RESULTS_SUMMARY_COLUMN_ITEM, &r->current_item, -1);
+ r->current_form = (poldiff_form_e) form;
+ if (r->current_item == NULL) {
+ tb = r->main_buffer;
+ offset = r->summary_offset;
+ } else {
+ int render_is_slow = result_item_is_render_slow(r->current_item, r->current_form);
+ if (result_item_get_current_sort(r->current_item, r->current_form, &sort, &dir)) {
+ sens = TRUE;
+ toplevel_set_sort_menu_selection(r->top, sort, dir);
+ }
+ if (render_is_slow) {
+ tb = results_get_slow_buffer(r, "Displaying Items");
+ } else {
+ tb = result_item_get_buffer(r->current_item, r->current_form);
+ }
+ offset = result_item_get_current_line(r->current_item, r->current_form);
+ }
+
+ gtk_text_view_set_buffer(r->view, tb);
+
+ /* restore saved location. use marks to ensure that
+ * we go to this position even if it hasn't been
+ * drawn. */
+ gtk_text_buffer_get_start_iter(tb, &textiter);
+ gtk_text_iter_set_offset(&textiter, offset);
+ mark = gtk_text_buffer_create_mark(tb, "location-mark", &textiter, FALSE);
+ gtk_text_view_scroll_to_mark(r->view, mark, 0.0, TRUE, 0.0, 0.0);
+ gtk_text_buffer_delete_mark(tb, mark);
+
+ toplevel_set_sort_menu_sensitivity(r->top, sens);
+ }
+}
+
+static void results_summary_on_row_activate(GtkTreeView * tree_view, GtkTreePath * path, GtkTreeViewColumn * column
+ __attribute__ ((unused)), gpointer user_data __attribute__ ((unused)))
+{
+ gboolean expanded = gtk_tree_view_row_expanded(tree_view, path);
+ if (!expanded) {
+ gtk_tree_view_expand_row(tree_view, path, 1);
+ } else {
+ gtk_tree_view_collapse_row(tree_view, path);
+ }
+}
+
+/**
+ * Callback invoked when the user clicks on an inline link. This will
+ * spawn a pop-up menu where the user and get more information on the
+ * clicked string (which is hopefully an AV rule's permission).
+ */
+static gboolean results_on_inline_link_event(GtkTextTag * tag, GObject * event_object
+ __attribute__ ((unused)), GdkEvent * event, const GtkTextIter * iter,
+ gpointer user_data)
+{
+ results_t *r = (results_t *) user_data;
+ if (event->type == GDK_BUTTON_PRESS) {
+ GtkTextIter *start = gtk_text_iter_copy(iter);
+ while (!gtk_text_iter_begins_tag(start, tag))
+ gtk_text_iter_backward_char(start);
+ GtkTextIter *end = gtk_text_iter_copy(start);
+ while (!gtk_text_iter_ends_tag(end, tag))
+ gtk_text_iter_forward_char(end);
+ gint line = gtk_text_iter_get_line(start);
+ char *s = gtk_text_iter_get_slice(start, end);
+ result_item_inline_link_event(r->current_item, r->top, GTK_WIDGET(r->view), (GdkEventButton *) event,
+ r->current_form, line, s);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Callback invoked when the user clicks on a line number tag. This
+ * will flip to the appropriate policy's source page and jump to that
+ * line.
+ */
+static gboolean results_on_line_event(GtkTextTag * tag, GObject * event_object __attribute__ ((unused)),
+ GdkEvent * event, const GtkTextIter * iter, gpointer user_data)
+{
+ results_t *r = (results_t *) user_data;
+ sediffx_policy_e which_pol = -1;
+ unsigned long line;
+ GtkTextIter *start, *end;
+ if (event->type == GDK_BUTTON_PRESS) {
+ start = gtk_text_iter_copy(iter);
+
+ while (!gtk_text_iter_starts_word(start))
+ gtk_text_iter_backward_char(start);
+ end = gtk_text_iter_copy(start);
+ while (!gtk_text_iter_ends_word(end))
+ gtk_text_iter_forward_char(end);
+
+ /* the line # in policy starts with 1, in the buffer it
+ * starts at 0 */
+ line = atoi(gtk_text_iter_get_slice(start, end)) - 1;
+ if (tag == r->policy_orig_tag) {
+ which_pol = SEDIFFX_POLICY_ORIG;
+ } else if (tag == r->policy_mod_tag) {
+ which_pol = SEDIFFX_POLICY_MOD;
+ } else {
+ /* should never get here */
+ assert(0);
+ }
+ toplevel_show_policy_line(r->top, which_pol, line);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean results_on_popup_menu(GtkWidget * widget, gpointer user_data)
+{
+ results_t *r = (results_t *) user_data;
+ GtkTextView *textview = GTK_TEXT_VIEW(widget);
+ gint ex, ey, x, y;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ GSList *tags, *tagp;
+ gboolean hovering = FALSE;
+ gdk_window_at_pointer(&ex, &ey);
+ gtk_text_view_window_to_buffer_coords(textview, GTK_TEXT_WINDOW_WIDGET, ex, ey, &x, &y);
+ buffer = gtk_text_view_get_buffer(textview);
+ gtk_text_view_get_iter_at_location(textview, &iter, x, y);
+ tags = gtk_text_iter_get_tags(&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ if (strcmp(GTK_TEXT_TAG(tagp->data)->name, "inline-link") == 0) {
+ hovering = TRUE;
+ break;
+ }
+ }
+ if (hovering) {
+ GtkTextIter *start = gtk_text_iter_copy(&iter);
+ while (!gtk_text_iter_begins_tag(start, GTK_TEXT_TAG(tagp->data)))
+ gtk_text_iter_backward_char(start);
+ GtkTextIter *end = gtk_text_iter_copy(start);
+ while (!gtk_text_iter_ends_tag(end, GTK_TEXT_TAG(tagp->data)))
+ gtk_text_iter_forward_char(end);
+ gint line = gtk_text_iter_get_line(start);
+ char *s = gtk_text_iter_get_slice(start, end);
+ g_slist_free(tags);
+ result_item_inline_link_event(r->current_item, r->top, widget, NULL, r->current_form, line, s);
+ return TRUE;
+ }
+ g_slist_free(tags);
+ return FALSE;
+}
+
+/**
+ * Set the cursor to a hand when user scrolls over a line number in
+ * when displaying te diff.
+ */
+static gboolean results_on_text_view_motion(GtkWidget * widget, GdkEventMotion * event, gpointer user_data __attribute__ ((unused)))
+{
+ GtkTextBuffer *buffer;
+ GtkTextView *textview;
+ GdkCursor *cursor;
+ GtkTextIter iter;
+ GSList *tags, *tagp;
+ gint x, ex, ey, y;
+ int hovering = 0;
+
+ textview = GTK_TEXT_VIEW(widget);
+
+ if (event->is_hint) {
+ gdk_window_get_pointer(event->window, &ex, &ey, NULL);
+ } else {
+ ex = event->x;
+ ey = event->y;
+ }
+
+ gtk_text_view_window_to_buffer_coords(textview, GTK_TEXT_WINDOW_WIDGET, ex, ey, &x, &y);
+ buffer = gtk_text_view_get_buffer(textview);
+ gtk_text_view_get_iter_at_location(textview, &iter, x, y);
+ tags = gtk_text_iter_get_tags(&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next) {
+ if (strncmp(GTK_TEXT_TAG(tagp->data)->name, "line", 4) == 0 ||
+ strcmp(GTK_TEXT_TAG(tagp->data)->name, "inline-link") == 0) {
+ hovering = TRUE;
+ break;
+ }
+ }
+ if (hovering) {
+ cursor = gdk_cursor_new(GDK_HAND2);
+ gdk_window_set_cursor(event->window, cursor);
+ gdk_cursor_unref(cursor);
+ gdk_flush();
+ } else {
+ gdk_window_set_cursor(event->window, NULL);
+ }
+ g_slist_free(tags);
+ return FALSE;
+}
+
+void results_sort(results_t * r, results_sort_e field, results_sort_dir_e direction)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(r->summary_view);
+ GtkTreeIter iter;
+ int f;
+ result_item_t *item;
+#ifndef NDEBUG
+ results_sort_e sort;
+ results_sort_dir_e dir;
+#endif
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return;
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(r->summary_tree), &iter, RESULTS_SUMMARY_COLUMN_FORM, &f,
+ RESULTS_SUMMARY_COLUMN_ITEM, &item, -1);
+ poldiff_form_e form = (poldiff_form_e) f;
+ assert(item != NULL && item == r->current_item && form == r->current_form
+ && result_item_get_current_sort(item, form, &sort, &dir) != 0);
+ result_item_set_current_sort(item, form, field, direction);
+ if (result_item_is_render_slow(r->current_item, form)) {
+ results_get_slow_buffer(r, "Sorting Items");
+ } else {
+ result_item_get_buffer(item, form);
+ }
+}
+
+GtkTextView *results_get_text_view(results_t * r)
+{
+ return r->view;
+}
diff --git a/sediff/results.h b/sediff/results.h
new file mode 100644
index 0000000..3d781b6
--- /dev/null
+++ b/sediff/results.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Header for showing diff results.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2004-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef RESULTS_H
+#define RESULTS_H
+
+typedef enum results_sort
+{
+ RESULTS_SORT_DEFAULT = 0,
+ RESULTS_SORT_SOURCE, RESULTS_SORT_TARGET,
+ RESULTS_SORT_CLASS, RESULTS_SORT_COND
+} results_sort_e;
+
+typedef enum results_sort_dir
+{
+ RESULTS_SORT_DESCEND = -1, RESULTS_SORT_ASCEND = 1
+} results_sort_dir_e;
+
+#include "toplevel.h"
+
+#include <gtk/gtk.h>
+#include <poldiff/poldiff.h>
+
+typedef struct results results_t;
+
+/**
+ * Allocate and return a results object. This object is responsible
+ * for showing the results of a poldiff run, and all sorting of
+ * results.
+ *
+ * @param top Toplevel object to contain the results object.
+ *
+ * @return Results object, or NULL upon error. The caller is
+ * responsible for calling results_destroy() afterwards.
+ */
+results_t *results_create(toplevel_t * top);
+
+/**
+ * Destroy the results object. This does nothing if the pointer is
+ * set to NULL.
+ *
+ * @param r Reference to a results object. Afterwards the pointer
+ * will be set to NULL.
+ */
+void results_destroy(results_t ** r);
+
+/**
+ * Notify the results object that the policies have been changed.
+ *
+ * @param r Results object to notify.
+ * @param orig The (possibly newly loaded) original policy to diff.
+ * @param mod The (possibly newly loaded) modified policy to diff.
+ */
+void results_open_policies(results_t * r, apol_policy_t * orig, apol_policy_t * mod);
+
+/**
+ * Clear all text from the results object. This should be done prior
+ * to running a new diff.
+ *
+ * @param r Results object to clear.
+ */
+void results_clear(results_t * r);
+
+/**
+ * Update the results display to match the most recent poldiff run.
+ *
+ * @param r Results object to update.
+ */
+void results_update(results_t * r);
+
+/**
+ * Called whenever the user switches to the results page. This
+ * function is responsible for setting up its menus and other widgets.
+ *
+ * @param r Results object to update.
+ */
+void results_switch_to_page(results_t * r);
+
+/**
+ * Sort the currently visible result buffer (which hopefully is the TE
+ * rules diff) using the given criteria.
+ *
+ * @param r Results object to sort.
+ * @param field Component of a rule to sort against.
+ * @param direction Direction of sort, either RESULTS_SORT_ASCEND or
+ * RESULTS_SORT_DESCEND.
+ */
+void results_sort(results_t * r, results_sort_e field, results_sort_dir_e direction);
+
+/**
+ * Get the currently showing text view for the results object.
+ *
+ * @param r Results object containing text view.
+ *
+ * @return Currently visible text view.
+ */
+GtkTextView *results_get_text_view(results_t * r);
+
+#endif
diff --git a/sediff/sediff.c b/sediff/sediff.c
new file mode 100644
index 0000000..6022775
--- /dev/null
+++ b/sediff/sediff.c
@@ -0,0 +1,653 @@
+/**
+ * @file
+ * Command line frontend for computing a semantic policy difference.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2006-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <poldiff/poldiff.h>
+#include <poldiff/component_record.h>
+#include <apol/policy.h>
+#include <apol/vector.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC"
+
+enum opt_values
+{
+ DIFF_LEVEL = 256, DIFF_CATEGORY,
+ DIFF_AUDITALLOW, DIFF_DONTAUDIT, DIFF_NEVERALLOW,
+ DIFF_TYPE_CHANGE, DIFF_TYPE_MEMBER, DIFF_TYPE_TRANS,
+ DIFF_ROLE_TRANS, DIFF_ROLE_ALLOW, DIFF_RANGE_TRANS,
+ OPT_STATS
+};
+
+/* command line options struct */
+static struct option const longopts[] = {
+ {"class", no_argument, NULL, 'c'},
+ {"level", no_argument, NULL, DIFF_LEVEL},
+ {"category", no_argument, NULL, DIFF_CATEGORY},
+ {"type", no_argument, NULL, 't'},
+ {"attribute", no_argument, NULL, 'a'},
+ {"role", no_argument, NULL, 'r'},
+ {"user", no_argument, NULL, 'u'},
+ {"bool", no_argument, NULL, 'b'},
+ {"allow", no_argument, NULL, 'A'},
+ {"auditallow", no_argument, NULL, DIFF_AUDITALLOW},
+ {"dontaudit", no_argument, NULL, DIFF_DONTAUDIT},
+ {"neverallow", no_argument, NULL, DIFF_NEVERALLOW},
+ {"type_change", no_argument, NULL, DIFF_TYPE_CHANGE},
+ {"type_trans", no_argument, NULL, DIFF_TYPE_TRANS},
+ {"type_member", no_argument, NULL, DIFF_TYPE_MEMBER},
+ {"role_trans", no_argument, NULL, DIFF_ROLE_TRANS},
+ {"role_allow", no_argument, NULL, DIFF_ROLE_ALLOW},
+ {"range_trans", no_argument, NULL, DIFF_RANGE_TRANS},
+ {"stats", no_argument, NULL, OPT_STATS},
+ {"quiet", no_argument, NULL, 'q'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+static void usage(const char *prog_name, int brief)
+{
+ printf("Usage: %s [OPTIONS] ORIGINAL_POLICY ; MODIFIED_POLICY\n\n", prog_name);
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", prog_name);
+ return;
+ }
+ printf("Semantically differentiate two policies. By default, all supported\n");
+ printf("policy elements sans neverallows are examined. The following options\n");
+ printf("are available:\n\n");
+ printf(" -c, --class object class and common permission definitions\n");
+ printf(" --level MLS level definitions\n");
+ printf(" --category MLS category definitions\n");
+ printf(" -t, --type type definitions\n");
+ printf(" -a, --attribute attribute definitions\n");
+ printf(" -r, --role role definitions\n");
+ printf(" -u, --user user definitions\n");
+ printf(" -b, --bool boolean definitions and default values\n");
+ printf(" -A, --allow allow rules\n");
+ printf(" --auditallow auditallow rules\n");
+ printf(" --dontaudit dontaudit rules\n");
+ printf(" --neverallow neverallow rules\n");
+ printf(" --type_change type_change rules\n");
+ printf(" --type_member type_member rules\n");
+ printf(" --type_trans type_transition rules\n");
+ printf(" --role_trans role_transition rules\n");
+ printf(" --role_allow role allow rules\n");
+ printf(" --range_trans range_transition rules\n");
+ printf("\n");
+ printf(" -q, --quiet suppress status output for elements with no differences\n");
+ printf(" --stats print only statistics\n");
+ printf(" -h, --help print this help text and exit\n");
+ printf(" -V, --version print version information and exit\n\n");
+}
+
+static void print_diff_string(const char *str, unsigned int indent_level)
+{
+ const char *c = str;
+ unsigned int i;
+ static const char *indent = " ";
+
+ for (i = 0; i < indent_level; i++)
+ printf("%s", indent);
+ for (; *c; c++) {
+ if (*c == '\n') {
+ if (*(c + 1) == '\0')
+ break;
+ printf("%c", *c);
+ for (i = 0; i < indent_level; i++)
+ printf("%s", indent);
+ } else if (*c == '\t') {
+ printf("%s", indent);
+ } else {
+ printf("%c", *c);
+ }
+ }
+}
+
+static void print_rule_section(const poldiff_t * diff, const poldiff_component_record_t * rec, const apol_vector_t * v,
+ poldiff_form_e form)
+{
+ int i;
+ char *str = NULL;
+ const void *item1;
+
+ for (i = 0; i < apol_vector_get_size(v); i++) {
+ item1 = apol_vector_get_element(v, i);
+ if (poldiff_component_record_get_form_fn(rec) (item1) == form) {
+ if ((str = poldiff_component_record_get_to_string_fn(rec) (diff, item1)) == NULL) {
+ return;
+ }
+ print_diff_string(str, 1);
+ printf("\n");
+ free(str);
+ str = NULL;
+ }
+ }
+}
+
+#define PRINT_ADDED_REMOVED 1
+#define PRINT_MODIFIED 2
+#define PRINT_ALL 4
+
+static void print_rule_diffs(const poldiff_t * diff, const poldiff_component_record_t * rec, int stats_only, const char *name,
+ uint32_t flags, apol_vector_comp_func sort_by)
+{
+ const apol_vector_t *internal_v = NULL;
+ apol_vector_t *v = NULL;
+ size_t stats[5] = { 0, 0, 0, 0, 0 };
+
+ if (!rec || !diff)
+ return;
+
+ poldiff_component_record_get_stats_fn(rec) (diff, stats);
+ if (flags == PRINT_ADDED_REMOVED) {
+ printf("%s (Added %zd, Removed %zd)\n", name, stats[0], stats[1]);
+ } else if (flags == PRINT_MODIFIED) {
+ printf("%s (Added %zd, Removed %zd, Modified %zd)\n", name, stats[0], stats[1], stats[2]);
+ } else if (flags == PRINT_ALL) {
+ printf("%s (Added %zd, Added New Type %zd, Removed %zd, Removed Missing Type %zd, Modified %zd)\n", name, stats[0],
+ stats[3], stats[1], stats[4], stats[2]);
+ } else {
+ fprintf(stderr, "Error, unhandled flag type\n");
+ }
+
+ if (stats_only)
+ return;
+ if ((internal_v = poldiff_component_record_get_results_fn(rec) (diff)) == NULL) {
+ return;
+ }
+ if (!(v = apol_vector_create_from_vector(internal_v, NULL, NULL, NULL))) {
+ perror("Error printig results");
+ return;
+ }
+
+ if (sort_by) {
+ apol_vector_sort(v, sort_by, NULL);
+ }
+
+ printf(" Added %s: %zd\n", name, stats[0]);
+ print_rule_section(diff, rec, v, POLDIFF_FORM_ADDED);
+
+ if (flags == PRINT_ALL) {
+ printf(" Added %s because of new type: %zd\n", name, stats[3]);
+ print_rule_section(diff, rec, v, POLDIFF_FORM_ADD_TYPE);
+ }
+
+ printf(" Removed %s: %zd\n", name, stats[1]);
+ print_rule_section(diff, rec, v, POLDIFF_FORM_REMOVED);
+
+ if (flags == PRINT_ALL) {
+ printf(" Removed %s because of missing type: %zd\n", name, stats[4]);
+ print_rule_section(diff, rec, v, POLDIFF_FORM_REMOVE_TYPE);
+ }
+ if (flags == PRINT_MODIFIED || flags == PRINT_ALL) {
+ printf(" Modified %s: %zd\n", name, stats[2]);
+ print_rule_section(diff, rec, v, POLDIFF_FORM_MODIFIED);
+ }
+ printf("\n");
+ apol_vector_destroy(&v);
+ return;
+}
+
+static void print_class_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_CLASSES), stats_only, "Classes", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_bool_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_BOOLS), stats_only, "Booleans", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_common_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_COMMONS), stats_only, "Commons", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_level_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_LEVELS), stats_only, "Levels", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_cat_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_CATS), stats_only, "Categories", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_role_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLES), stats_only, "Roles", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_user_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_USERS), stats_only, "Users", PRINT_MODIFIED, NULL);
+ return;
+}
+
+static void print_avallow_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVALLOW), stats_only, "AV-Allow Rules", PRINT_ALL, NULL);
+}
+
+static void print_avauditallow_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVAUDITALLOW), stats_only, "AV-Audit Allow Rules",
+ PRINT_ALL, NULL);
+}
+
+static void print_avdontaudit_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVDONTAUDIT), stats_only, "AV-Don't Audit Rules",
+ PRINT_ALL, NULL);
+}
+
+static void print_avneverallow_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_AVNEVERALLOW), stats_only, "AV-Never Allow Rules",
+ PRINT_ALL, NULL);
+}
+
+static void print_role_allow_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLE_ALLOWS), stats_only, "Role Allow Rules",
+ PRINT_MODIFIED, NULL);
+}
+
+static void print_role_trans_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ROLE_TRANS), stats_only, "Role Transitions", PRINT_ALL,
+ NULL);
+}
+
+static void print_range_trans_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_RANGE_TRANS), stats_only, "Range Transitions",
+ PRINT_MODIFIED, NULL);
+}
+
+/** compare the names for two poldiff_type_t objects.
+ * used to sort items prior to display. */
+static int type_name_cmp(const void *a, const void *b, void *user_data __attribute__ ((unused)))
+{
+ poldiff_type_t *ta = (poldiff_type_t *) a;
+ poldiff_type_t *tb = (poldiff_type_t *) b;
+ if (ta == NULL || tb == NULL)
+ return -1;
+ return strcmp(poldiff_type_get_name(ta), poldiff_type_get_name(tb));
+}
+
+static void print_type_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TYPES), stats_only, "Types", PRINT_MODIFIED,
+ type_name_cmp);
+}
+
+static void print_attrib_diffs(const poldiff_t * diff, int stats_only)
+{
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_ATTRIBS), stats_only, "Attributes", PRINT_MODIFIED, NULL);
+}
+
+static size_t get_diff_total(const poldiff_t * diff, uint32_t flags)
+{
+ size_t total = 0;
+ uint32_t i;
+ size_t stats[5] = { 0, 0, 0, 0, 0 };
+
+ if (!diff || !flags)
+ return 0;
+
+ /* for all 32 bits possible in flags */
+ for (i = 0x80000000; i; i = i >> 1) {
+ if (flags & i) {
+ poldiff_get_stats(diff, i, stats);
+ total += (stats[0] + stats[1] + stats[2] + stats[3] + stats[4]);
+ }
+ }
+
+ return total;
+}
+
+static void print_diff(const poldiff_t * diff, uint32_t flags, int stats, int quiet)
+{
+ if (flags & POLDIFF_DIFF_CLASSES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_CLASSES))) {
+ print_class_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_COMMONS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_COMMONS))) {
+ print_common_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_LEVELS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_LEVELS))) {
+ print_level_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_CATS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_CATS))) {
+ print_cat_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_TYPES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TYPES))) {
+ print_type_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_ATTRIBS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ATTRIBS))) {
+ print_attrib_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_ROLES && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLES))) {
+ print_role_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_USERS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_USERS))) {
+ print_user_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_BOOLS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_BOOLS))) {
+ print_bool_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_AVALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVALLOW))) {
+ print_avallow_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_AVAUDITALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVAUDITALLOW))) {
+ print_avauditallow_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_AVDONTAUDIT && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVDONTAUDIT))) {
+ print_avdontaudit_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_AVNEVERALLOW && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_AVNEVERALLOW))) {
+ print_avneverallow_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_TECHANGE && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TECHANGE))) {
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TECHANGE), stats, "TE type_change", PRINT_ALL,
+ NULL);
+ }
+ if (flags & POLDIFF_DIFF_TEMEMBER && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TEMEMBER))) {
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TEMEMBER), stats, "TE type_member", PRINT_ALL,
+ NULL);
+ }
+ if (flags & POLDIFF_DIFF_TETRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_TETRANS))) {
+ print_rule_diffs(diff, poldiff_get_component_record(POLDIFF_DIFF_TETRANS), stats, "TE type_trans", PRINT_ALL, NULL);
+ }
+ if (flags & POLDIFF_DIFF_ROLE_ALLOWS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLE_ALLOWS))) {
+ print_role_allow_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_ROLE_TRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_ROLE_TRANS))) {
+ print_role_trans_diffs(diff, stats);
+ }
+ if (flags & POLDIFF_DIFF_RANGE_TRANS && !(quiet && !get_diff_total(diff, POLDIFF_DIFF_RANGE_TRANS))) {
+ print_range_trans_diffs(diff, stats);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int optc = 0, quiet = 0, stats = 0, default_all = 0;
+ uint32_t flags = 0;
+ apol_policy_t *orig_policy = NULL, *mod_policy = NULL;
+ apol_policy_path_type_e orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ char *orig_base_path = NULL;
+ apol_vector_t *orig_module_paths = NULL;
+ apol_policy_path_t *orig_pol_path = NULL;
+ apol_policy_path_type_e mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ char *mod_base_path = NULL;
+ apol_vector_t *mod_module_paths = NULL;
+ apol_policy_path_t *mod_pol_path = NULL;
+ poldiff_t *diff = NULL;
+ size_t total = 0;
+
+ while ((optc = getopt_long(argc, argv, "ctarubANDLMCRqhV", longopts, NULL)) != -1) {
+ switch (optc) {
+ case 0:
+ break;
+ case 'c':
+ flags |= (POLDIFF_DIFF_CLASSES | POLDIFF_DIFF_COMMONS);
+ break;
+ case DIFF_LEVEL:
+ flags |= POLDIFF_DIFF_LEVELS;
+ break;
+ case DIFF_CATEGORY:
+ flags |= POLDIFF_DIFF_CATS;
+ break;
+ case 't':
+ flags |= POLDIFF_DIFF_TYPES;
+ break;
+ case 'a':
+ flags |= POLDIFF_DIFF_ATTRIBS;
+ break;
+ case 'r':
+ flags |= POLDIFF_DIFF_ROLES;
+ break;
+ case 'u':
+ flags |= POLDIFF_DIFF_USERS;
+ break;
+ case 'b':
+ flags |= POLDIFF_DIFF_BOOLS;
+ break;
+ case 'A':
+ flags |= POLDIFF_DIFF_AVALLOW;
+ break;
+ case DIFF_AUDITALLOW:
+ flags |= POLDIFF_DIFF_AVAUDITALLOW;
+ break;
+ case DIFF_DONTAUDIT:
+ flags |= POLDIFF_DIFF_AVDONTAUDIT;
+ break;
+ case DIFF_NEVERALLOW:
+ flags |= POLDIFF_DIFF_AVNEVERALLOW;
+ break;
+ case DIFF_TYPE_CHANGE:
+ flags |= POLDIFF_DIFF_TECHANGE;
+ break;
+ case DIFF_TYPE_MEMBER:
+ flags |= POLDIFF_DIFF_TEMEMBER;
+ break;
+ case DIFF_TYPE_TRANS:
+ flags |= POLDIFF_DIFF_TETRANS;
+ break;
+ case DIFF_ROLE_ALLOW:
+ flags |= POLDIFF_DIFF_ROLE_ALLOWS;
+ break;
+ case DIFF_ROLE_TRANS:
+ flags |= POLDIFF_DIFF_ROLE_TRANS;
+ break;
+ case DIFF_RANGE_TRANS:
+ flags |= POLDIFF_DIFF_RANGE_TRANS;
+ break;
+ case OPT_STATS:
+ stats = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ usage(argv[0], 0);
+ exit(0);
+ case 'V':
+ printf("sediff %s\n%s\n", VERSION, COPYRIGHT_INFO);
+ exit(0);
+ default:
+ usage(argv[0], 1);
+ exit(1);
+ }
+ }
+
+ if (!flags) {
+ flags = POLDIFF_DIFF_ALL & ~POLDIFF_DIFF_AVNEVERALLOW;
+ default_all = 1;
+ }
+
+ if (argc - optind < 2) {
+ usage(argv[0], 1);
+ exit(1);
+ }
+
+ if (!strcmp(";", argv[optind])) {
+ ERR(NULL, "%s", "Missing path to original policy.");
+ goto err;
+ }
+ orig_base_path = argv[optind++];
+ orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ if (!(orig_module_paths = apol_vector_create(NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ for (; argc - optind; optind++) {
+ if (!strcmp(";", argv[optind])) {
+ optind++;
+ break;
+ }
+ if (apol_vector_append(orig_module_paths, (void *)argv[optind])) {
+ ERR(NULL, "Error loading module %s", argv[optind]);
+ goto err;
+ }
+ orig_path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ }
+ if (apol_file_is_policy_path_list(orig_base_path) > 0) {
+ orig_pol_path = apol_policy_path_create_from_file(orig_base_path);
+ if (!orig_pol_path) {
+ ERR(NULL, "%s", "invalid policy list");
+ goto err;
+ }
+ } else {
+ orig_pol_path = apol_policy_path_create(orig_path_type, orig_base_path, orig_module_paths);
+ if (!orig_pol_path) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ }
+ apol_vector_destroy(&orig_module_paths);
+
+ if (argc - optind == 0) {
+ ERR(NULL, "%s", "Missing path to modified policy.");
+ goto err;
+ }
+
+ mod_base_path = argv[optind++];
+ mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ if (!(mod_module_paths = apol_vector_create(NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ for (; argc - optind; optind++) {
+ if (apol_vector_append(mod_module_paths, (void *)argv[optind])) {
+ ERR(NULL, "Error loading module %s", argv[optind]);
+ goto err;
+ }
+ mod_path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ }
+ if (apol_file_is_policy_path_list(mod_base_path) > 0) {
+ mod_pol_path = apol_policy_path_create_from_file(mod_base_path);
+ if (!mod_pol_path) {
+ ERR(NULL, "%s", "invalid policy list");
+ goto err;
+ }
+ } else {
+ mod_pol_path = apol_policy_path_create(mod_path_type, mod_base_path, mod_module_paths);
+ if (!mod_pol_path) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ }
+ apol_vector_destroy(&mod_module_paths);
+
+ int policy_opt = 0;
+ if (!(flags & POLDIFF_DIFF_AVNEVERALLOW)) {
+ policy_opt |= QPOL_POLICY_OPTION_NO_NEVERALLOWS;
+ }
+ if (!(flags & POLDIFF_DIFF_RULES)) {
+ policy_opt |= QPOL_POLICY_OPTION_NO_RULES;
+ }
+ orig_policy = apol_policy_create_from_policy_path(orig_pol_path, policy_opt, NULL, NULL);
+ if (!orig_policy) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ mod_policy = apol_policy_create_from_policy_path(mod_pol_path, policy_opt, NULL, NULL);
+ if (!mod_policy) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+
+ qpol_policy_t *orig_qpol = apol_policy_get_qpol(orig_policy);
+ qpol_policy_t *mod_qpol = apol_policy_get_qpol(mod_policy);
+ /* we disable attribute diffs if either policy does not
+ * support attribute names because the fake attribute names
+ * won't make sense */
+ if ((flags & POLDIFF_DIFF_ATTRIBS)
+ && (!(qpol_policy_has_capability(orig_qpol, QPOL_CAP_ATTRIB_NAMES))
+ || !(qpol_policy_has_capability(mod_qpol, QPOL_CAP_ATTRIB_NAMES)))) {
+ flags &= ~POLDIFF_DIFF_ATTRIBS;
+ WARN(NULL, "%s", "Attribute diffs are not supported for current policies.");
+ }
+
+ /* we disable MLS diffs if both policies do not support MLS
+ * but do not warn if it was implicitly requested for two
+ * non-MLS policies */
+ if ((flags & POLDIFF_DIFF_MLS)
+ && (!(qpol_policy_has_capability(orig_qpol, QPOL_CAP_MLS)) && !(qpol_policy_has_capability(mod_qpol, QPOL_CAP_MLS)))) {
+ flags &= ~(POLDIFF_DIFF_MLS);
+ if (!default_all) {
+ WARN(NULL, "%s", "MLS diffs are not supported for current policies.");
+ }
+ }
+
+ /* default callback for error handling is sufficient here */
+ if (!(diff = poldiff_create(orig_policy, mod_policy, NULL, NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ /* poldiff now owns the policies */
+ orig_policy = mod_policy = NULL;
+
+ if (poldiff_run(diff, flags)) {
+ goto err;
+ }
+
+ print_diff(diff, flags, stats, quiet);
+
+ total = get_diff_total(diff, flags);
+
+ apol_policy_path_destroy(&orig_pol_path);
+ apol_policy_path_destroy(&mod_pol_path);
+ poldiff_destroy(&diff);
+
+ if (total)
+ return 1;
+ else
+ return 0;
+
+ err:
+ apol_policy_destroy(&orig_policy);
+ apol_policy_destroy(&mod_policy);
+ apol_policy_path_destroy(&orig_pol_path);
+ apol_policy_path_destroy(&mod_pol_path);
+ apol_vector_destroy(&orig_module_paths);
+ apol_vector_destroy(&mod_module_paths);
+ poldiff_destroy(&diff);
+ return 1;
+}
diff --git a/sediff/sediff_help.txt b/sediff/sediff_help.txt
new file mode 100644
index 0000000..8929528
--- /dev/null
+++ b/sediff/sediff_help.txt
@@ -0,0 +1,259 @@
+Semantic Policy Difference Tool for Security Enhanced Linux
+
+
+Overview:
+---------
+The sediff and sediffx programs are policy analysis tools that take
+two policies and compare them, showing a list of the differences. The
+former is a command-line only program while the latter is a GTK+
+application. They can compare source, monolithic binary, and modular
+binary policies; they can also compare different versions of policy.
+The two programs support source policies versions 12 and higher,
+monolithic binary versions 15 and higher, and modular binary versions
+5 and higher.
+
+
+Limitations:
+------------
+The programs currently compare the following policy elements:
+ - commons and object classes
+ - levels and categories
+ - types and attributes
+ - roles
+ - users
+ - booleans
+ - access vector rules (allow, neverallow, etc.)
+ - type rules (type_transition, type_member, etc.)
+ - role allow rules
+ - role transition rules
+ - range transition rules
+
+
+What is a Semantic Diff?
+------------------------
+The challenge with comparing two policies is that a straightforward
+textual comparison is of little value. What one needs is the ability
+to determine semantically how two policies differ. For example, one
+could not simply grep for allow rules with a given type, and then
+compare them to a similar list from another policy. Many factors
+affect the semantic meaning of the rules. For example, multiple rules
+can allow differing sets of permissions. Attributes can allow
+permissions to or from a type. What was a type in one policy could
+become an alias in another.
+
+What sediff and sediffx do are analyze each policy semantically. We
+define "semantically" as how the kernel security server uses a policy
+to make enforcement decisions. This approach also allows binary and
+source policies to be compared, as well as different versions of
+policies.
+
+NOTE: The one semantic assumption sediff and sediffx make is that when
+an identifier (e.g., a type name) has the same string value in each
+policy, then it represents the same semantic meaning in both policies.
+
+
+sediff and sediffx Commands:
+----------------------------
+Policies may be differentiated upon the command line (see "sediff
+--help") or in a graphical environment (see "sediffx --help"). The
+sediffx tool is recommended because it gives additional details about
+policy differences and affords type remappings. The remainder of this
+document focuses on sediffx.
+
+
+Understanding sediffx's Results:
+--------------------------------
+After calculating differences between two policies, the GUI shows the
+compared policy components in the top-left frame. Besides each policy
+component is a number representing the total number of differences for
+that component. Select a policy component to show detailed results in
+the right-hand window.
+
+NOTE: All differences are shown from the perspective of the first
+policy given (i.e., "original policy") to the second ("modified
+policy"). There are five types of differences shown:
+
+ - Added (+): A policy component was added by the second policy (in
+ modified policy but not original policy).
+
+ - Removed (-): A policy component was removed by the second policy
+ (in original policy but not modified policy).
+
+ - Modified (*): A policy component was present in both policies, but
+ is different in the modified policy.
+
+Where appropriate, two other differences are possible:
+
+ - Added because of new type (+): This policy component could not
+ exist in the original policy because that policy does not declare
+ a necessary type.
+
+ - Removed because of missing type (-): This policy component could
+ not exist in the modified policy because that policy no longer
+ declares a necessary type.
+
+
+Supported Policy Areas Differences:
+-----------------------------------
+Below is an explanation of the difference for each supported policy
+area:
+
+ Commons:
+ --------
+ Classes can be added, removed, or modified. Modified means that
+ the list of permissions associated with a common is different.
+
+ Classes:
+ --------
+ Classes are compared much like commons. They too may be added,
+ removed, or modified.
+
+ Levels:
+ -------
+ If either policy is MLS then levels will be compared. Levels can be
+ added or removed; a modified level means that the categories
+ assigned to its corresponding sensitivity has changed. Be aware
+ that levels' aliases are ignored by the diff algorithm.
+
+ Categories:
+ -----------
+ If either policy is MLS then categories will be compared. They can
+ be added or removed; there is no such thing as a "modified"
+ category. Be aware that categories' aliases are ignored by the
+ diff algorithm.
+
+ Types:
+ ------
+ Types can be added, removed, or modified. Modified means that the
+ attributes associated with a type are different between the two
+ policies.
+
+ Attributes:
+ -----------
+ Attributes are compared like types. They can be added, removed,
+ or modified. Modified means that the types associated with the
+ attributes are different (types can be added or removed from the
+ attribute).
+
+ Roles:
+ ------
+ Roles can be added, removed, or modified. Modified means that the
+ types associated with a role are different between the two policies.
+ Types can be added or removed from a role.
+
+ Users:
+ ------
+ Users can be added, removed, or modified. Modified means that the
+ roles associated with a user are different between the two policies.
+ Roles can be added or removed from a user. In addition, if either
+ policy is MLS then the users' ranges and default levels are also
+ compared.
+
+ Booleans:
+ ---------
+ Booleans can be added, removed, or modified. If comparing a version
+ 15 or earlier policy with a version 16 or later policy, all the
+ booleans will be added or removed, for booleans were introduced in
+ version 16. Modified means that the default value is different
+ between the two policies.
+
+ AV Rules:
+ ---------
+ Finding differences in access vector rules (allow, neverallow, etc.)
+ consumes the majority of time when diffing two policies. The rule
+ comparison is truly semantic. All issues of redundancy and
+ duplication, as well as indirect access through attributes are
+ resolved. All rules are keyed by the "source-target-class" (STC)
+ triple. In addition, conditional rules are distinguished from
+ non-conditional rules. Thus, for example, two rules with the same
+ STC will not be compared if one is non-conditional and the other is
+ conditional, or if both are conditional but conditioned on two
+ different conditional expressions. For conditional rules, the
+ conditional expression is compared to ensure that conditional rules
+ are meaningfully compared. In the results pane, conditional rules
+ are displayed with their associated conditional expression and if
+ the rule was in conditional's TRUE or FALSE branch.
+
+ NOTE: For conditional rules, the default and current values of
+ the booleans are ignored. Conditional expressions are compared
+ as if all booleans were in the same state.
+
+ Rules can be added, removed, or modified. Added means the STC
+ triple for that rule is not present in the original policy but in
+ the modified one. Removed means the STC triple is present in the
+ original but not modified policy. Modified means that the
+ permissions for the rule are different between the policies.
+
+ When source policies are compared, hyperlinked line numbers are
+ shown that takes the user to the policy's source where the rule was
+ defined. If there were more than one source rules that contributed
+ to a STC triple for a given rule, then all relevant source rules are
+ linked. Furthermore, the user may click upon an individual
+ permission to obtain a list of lines that contributed just that
+ permission.
+
+ Type Rules:
+ -----------
+ Type rules are type_transition, type_member, and type_change. They
+ are differentiated much like AV rules in that their STCs are used as
+ keys. For type rules, modified means that the default type is
+ different between the policies.
+
+ Role Allows:
+ ------------
+ Role allow rules determine if a role is allowed to transition to
+ another role. Diffing a role allow involves taking the source role
+ and checking to see if there are corresponding rules in the other
+ policy with the same source role. A modified role allow means the
+ same source exist in both policies but target roles differ.
+
+ Role Transitions:
+ -----------------
+ Role transitions are keyed against both the source role and target
+ type. If a role transition exists in both policies but has a
+ different default role then it is marked as modified.
+
+ Range Transitions:
+ ------------------
+ Range transitions have a STC much like AV rules. A modified range
+ transition indicates a difference in the rules' target ranges. This
+ could be either a difference in level or in minimal category set.
+
+
+Policy Tabs:
+------------
+Each policy has a tab on the main window labeled Policy #: followed by
+the policy file name. Under these tabs are a policy statistics tab
+and a source tab.
+
+ Policy Statistics Tab:
+ ----------------------
+ The policy statistics tab displays a summary of that policy's
+ contents.
+
+ Source Tab:
+ -----------
+ If the policy is a source policy, this tab displays the source of
+ that policy.
+
+
+Remapping Types:
+----------------
+The diff algorithm implicitly treats a type with the same name across
+both policies as the same semantic item. This includes a name that
+was a type in one policy but became an alias in the other. There may
+be instances where the operator has special knowledge of the remaining
+unmapped types. From sediffx's main interface, select "Remap Types"
+from the Tools menu to open a dialog box. Add additional remappings
+between types as necessary.
+
+There are times when a one-to-one mapping is not sufficient for
+analysis purposes. Occasionally a type is "split" into two or more
+types; conversely multiple types are "joined" into a single type. For
+example, a policy has the type "games_t" for a number of programs. At
+some point in the future the policy writer decides to give NetHack its
+own type, "nethack_t". To represent this type split in sediffx, go to
+the Remap Types dialog. There should already be an inferred mapping
+from the original policy's games_t to the modified policy. Add a new
+mapping from games_t to nethack_t. The mappings list will be updated
+to show that games_t now maps to both games_t and nethack_t.
diff --git a/sediff/sediffx-small.png b/sediff/sediffx-small.png
new file mode 100644
index 0000000..21354b8
--- /dev/null
+++ b/sediff/sediffx-small.png
Binary files differ
diff --git a/sediff/sediffx-small.xcf b/sediff/sediffx-small.xcf
new file mode 100644
index 0000000..d936489
--- /dev/null
+++ b/sediff/sediffx-small.xcf
Binary files differ
diff --git a/sediff/sediffx.c b/sediff/sediffx.c
new file mode 100644
index 0000000..44cf686
--- /dev/null
+++ b/sediff/sediffx.c
@@ -0,0 +1,317 @@
+/**
+ * @file
+ * Main program for running sediffx in a GTK+ environment.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "sediffx.h"
+#include "toplevel.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+
+struct sediffx
+{
+ apol_policy_path_t *paths[SEDIFFX_POLICY_NUM];
+ apol_policy_t *policies[SEDIFFX_POLICY_NUM];
+ toplevel_t *top;
+ poldiff_t *poldiff;
+ uint32_t flags;
+};
+
+static struct option const longopts[] = {
+ {"run-diff", no_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+};
+
+void sediffx_set_policy(sediffx_t * s, sediffx_policy_e which, apol_policy_t * policy, apol_policy_path_t * path)
+{
+ poldiff_destroy(&s->poldiff);
+ if (policy != NULL) {
+ apol_policy_destroy(&s->policies[which]);
+ s->policies[which] = policy;
+ if (path != s->paths[which]) {
+ apol_policy_path_destroy(&s->paths[which]);
+ }
+ s->paths[which] = path;
+ } else {
+ apol_policy_destroy(&s->policies[which]);
+ apol_policy_path_destroy(&s->paths[which]);
+ }
+}
+
+const apol_policy_path_t *sediffx_get_policy_path(sediffx_t * sediffx, const sediffx_policy_e which)
+{
+ return sediffx->paths[which];
+}
+
+poldiff_t *sediffx_get_poldiff(sediffx_t * s, poldiff_handle_fn_t fn, void *arg)
+{
+ if (s->poldiff != NULL) {
+ return s->poldiff;
+ }
+ if (s->policies[SEDIFFX_POLICY_ORIG] == NULL || s->policies[SEDIFFX_POLICY_MOD] == NULL) {
+ return NULL;
+ }
+ s->poldiff = poldiff_create(s->policies[SEDIFFX_POLICY_ORIG], s->policies[SEDIFFX_POLICY_MOD], fn, arg);
+ if (s->poldiff != NULL) {
+ /* poldiff_create() took ownership of the policies */
+ s->policies[SEDIFFX_POLICY_ORIG] = NULL;
+ s->policies[SEDIFFX_POLICY_MOD] = NULL;
+ }
+ return s->poldiff;
+}
+
+void sediffx_set_poldiff_run_flags(sediffx_t * s, uint32_t flags)
+{
+ s->flags = flags;
+}
+
+uint32_t sediffx_get_poldiff_run_flags(sediffx_t * s)
+{
+ return s->flags;
+}
+
+static void print_version_info(void)
+{
+ printf("sediffx %s\n%s\n", VERSION, COPYRIGHT_INFO);
+}
+
+static void usage(const char *program_name, int brief)
+{
+ printf("Usage: %s [-d] [ORIGINAL_POLICY ; MODIFIED_POLICY]\n\n", program_name);
+ if (brief) {
+ printf("\tTry %s --help for more help.\n\n", program_name);
+ return;
+ }
+ printf("Semantically differentiate two policies. All supported policy elements\n");
+ printf("are examined. The following options are available:\n");
+ printf("\n");
+ printf(" -d, --diff-now load policies and diff immediately\n");
+ printf(" -h, --help print this help text and exit\n");
+ printf(" -V, --version print version information and exit\n\n");
+}
+
+struct delayed_main_data
+{
+ apol_policy_path_t *orig_path, *mod_path;
+ int run_diff;
+ toplevel_t *top;
+};
+
+/*
+ * We don't want to do the heavy work of loading and displaying
+ * the diff before the main loop has started because it will freeze
+ * the gui for too long. To solve this, the function is called from an
+ * idle callback set-up in main.
+ */
+static gboolean delayed_main(gpointer data)
+{
+ struct delayed_main_data *dmd = (struct delayed_main_data *)data;
+ if (toplevel_open_policies(dmd->top, dmd->orig_path, dmd->mod_path) == 0 && dmd->run_diff) {
+ toplevel_run_diff(dmd->top);
+ }
+ return FALSE;
+}
+
+static void sediffx_destroy(sediffx_t ** sediffx)
+{
+ if (sediffx != NULL && *sediffx != NULL) {
+ int i;
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ apol_policy_path_destroy(&((*sediffx)->paths[i]));
+ apol_policy_destroy(&((*sediffx)->policies[i]));
+ }
+ poldiff_destroy(&((*sediffx)->poldiff));
+ free(*sediffx);
+ *sediffx = NULL;
+ }
+}
+
+static void sediffx_parse_command_line(int argc, char **argv, apol_policy_path_t ** orig_path, apol_policy_path_t ** mod_path,
+ int *run_diff)
+{
+ int optc;
+ *orig_path = NULL;
+ *mod_path = NULL;
+ *run_diff = 0;
+ while ((optc = getopt_long(argc, argv, "dhV", longopts, NULL)) != -1) {
+ switch (optc) {
+ case 0:
+ break;
+ case 'd': /* run the diff only for gui */
+ *run_diff = 1;
+ break;
+ case 'h': /* help */
+ usage(argv[0], 0);
+ exit(EXIT_SUCCESS);
+ case 'V': /* version */
+ print_version_info();
+ exit(EXIT_SUCCESS);
+ default:
+ usage(argv[0], 1);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (argc - optind == 0) {
+ /* here we have found no missing arguments, but
+ * perhaps the user specified -d with no files */
+ if (*run_diff) {
+ usage(argv[0], 0);
+ exit(EXIT_FAILURE);
+ }
+ return;
+ } else if (argc - optind == 1) {
+ usage(argv[0], 1);
+ exit(EXIT_FAILURE);
+ }
+ if (argc - optind == 2) {
+ /* sediffx with file names, old syntax */
+ if (strcmp(argv[optind], ";") == 0 || strcmp(argv[optind + 1], ";") == 0) {
+ usage(argv[0], 1);
+ exit(EXIT_FAILURE);
+ }
+ *orig_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, argv[optind], NULL);
+ *mod_path = apol_policy_path_create(APOL_POLICY_PATH_TYPE_MONOLITHIC, argv[optind + 1], NULL);
+ if (*orig_path == NULL || *mod_path == NULL) {
+ ERR(NULL, "%s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ return;
+ }
+
+ /* module lists */
+ char *orig_base_path = NULL;
+ apol_vector_t *orig_module_paths = NULL;
+ char *mod_base_path = NULL;
+ apol_vector_t *mod_module_paths = NULL;
+ apol_policy_path_type_e orig_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+ apol_policy_path_type_e mod_path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC;
+
+ orig_base_path = argv[optind++];
+ if (!(orig_module_paths = apol_vector_create(NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ for (; argc - optind; optind++) {
+ if (!strcmp(";", argv[optind])) {
+ optind++;
+ break;
+ }
+ if (apol_vector_append(orig_module_paths, (void *)argv[optind])) {
+ ERR(NULL, "Error loading module %s", argv[optind]);
+ goto err;
+ }
+ orig_path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ }
+ if (apol_file_is_policy_path_list(orig_base_path) > 0) {
+ *orig_path = apol_policy_path_create_from_file(orig_base_path);
+ if (*orig_path == NULL) {
+ ERR(NULL, "%s", "invalid policy list");
+ goto err;
+ }
+ } else {
+ *orig_path = apol_policy_path_create(orig_path_type, orig_base_path, orig_module_paths);
+ if (*orig_path == NULL) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ }
+ apol_vector_destroy(&orig_module_paths);
+
+ if (argc - optind == 0) {
+ ERR(NULL, "%s", "Missing path to modified policy.");
+ goto err;
+ }
+
+ mod_base_path = argv[optind++];
+ if (!(mod_module_paths = apol_vector_create(NULL))) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ for (; argc - optind; optind++) {
+ if (apol_vector_append(mod_module_paths, (void *)argv[optind])) {
+ ERR(NULL, "Error loading module %s", argv[optind]);
+ goto err;
+ }
+ mod_path_type = APOL_POLICY_PATH_TYPE_MODULAR;
+ }
+ if (apol_file_is_policy_path_list(mod_base_path) > 0) {
+ *mod_path = apol_policy_path_create_from_file(mod_base_path);
+ if (*mod_path == NULL) {
+ ERR(NULL, "%s", "invalid policy list");
+ goto err;
+ }
+ } else {
+ *mod_path = apol_policy_path_create(mod_path_type, mod_base_path, mod_module_paths);
+ if (*mod_path == NULL) {
+ ERR(NULL, "%s", strerror(errno));
+ goto err;
+ }
+ }
+ apol_vector_destroy(&mod_module_paths);
+ return;
+ err:
+ apol_policy_path_destroy(orig_path);
+ apol_policy_path_destroy(mod_path);
+ apol_vector_destroy(&orig_module_paths);
+ apol_vector_destroy(&mod_module_paths);
+}
+
+int main(int argc, char **argv)
+{
+ sediffx_t *app;
+ apol_policy_path_t *orig_path, *mod_path;
+ int run_diff;
+
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+
+ gtk_init(&argc, &argv);
+ sediffx_parse_command_line(argc, argv, &orig_path, &mod_path, &run_diff);
+ glade_init();
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+ if ((app = calloc(1, sizeof(*app))) == NULL || (app->top = toplevel_create(app)) == NULL) {
+ ERR(NULL, "%s", strerror(errno));
+ sediffx_destroy(&app);
+ exit(EXIT_FAILURE);
+ }
+ if (orig_path != NULL && mod_path != NULL) {
+ struct delayed_main_data dmd = { orig_path, mod_path, run_diff, app->top };
+ g_idle_add(&delayed_main, &dmd);
+ }
+ gtk_main();
+
+ sediffx_destroy(&app);
+ exit(EXIT_SUCCESS);
+}
diff --git a/sediff/sediffx.glade b/sediff/sediffx.glade
new file mode 100644
index 0000000..baeea93
--- /dev/null
+++ b/sediff/sediffx.glade
@@ -0,0 +1,3619 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="toplevel">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Semantic Policy Difference Tool </property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="default_width">800</property>
+ <property name="default_height">600</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <signal name="destroy" handler="toplevel_on_destroy" object="toplevel" last_modification_time="Wed, 10 Jan 2007 20:20:42 GMT"/>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="main_menubar">
+ <property name="visible">True</property>
+ <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+ <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
+
+ <child>
+ <widget class="GtkMenuItem" id="file menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="file menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Open">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Open Policies</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_open_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:14:14 GMT"/>
+ <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image201">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Quit">
+ <property name="visible">True</property>
+ <property name="label">gtk-quit</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="toplevel_on_quit_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:14:08 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="edit menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="edit menu">
+ <signal name="activate_current" handler="toplevel_on_edit_menu_activate" object="toplevel" last_modification_time="Tue, 16 Jan 2007 16:29:46 GMT"/>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Copy">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label">gtk-copy</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="toplevel_on_copy_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:14:02 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator2">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="Select All">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">Select _All</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_select_all_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:56 GMT"/>
+ <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="tools menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Tools</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="tools menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Find">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">_Find Text</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_find_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:50 GMT"/>
+ <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image202">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Run Diff">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">_Run Diff</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_run_diff_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:44 GMT"/>
+ <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image203">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Remap Types">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">Remap _Types</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_remap_types_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:37 GMT"/>
+ <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image204">
+ <property name="visible">True</property>
+ <property name="stock">gtk-preferences</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator3">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="sort menu item">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">Sort Rules</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="sort menu">
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Default Sort">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Default Sort</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <signal name="activate" handler="toplevel_on_default_sort_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:04:31 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="source type menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Source Type</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="source type menu">
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Ascending source type">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Ascending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_source_type_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:38 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Descending source type">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Descending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_source_type_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:45 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="target type menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Target Type</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="target type menu">
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Ascending target type">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Ascending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_target_type_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:53 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Descending target type">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Descending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_target_type_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:12:59 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="object class menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Object Class</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="object class menu">
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Ascending object class">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Ascending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_class_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:05 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Descending object class">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Descending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_class_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:10 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="conditional menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Conditional</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="conditional menu">
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Ascending conditional">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Ascending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_conditional_asc_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:17 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkRadioMenuItem" id="Descending conditional">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Descending</property>
+ <property name="use_underline">True</property>
+ <property name="active">False</property>
+ <property name="group">Default Sort</property>
+ <signal name="activate" handler="toplevel_on_conditional_des_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 21:13:23 GMT"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenuItem" id="help menu item">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+
+ <child>
+ <widget class="GtkMenu" id="help menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="Help">
+ <property name="visible">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="toplevel_on_help_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 20:29:03 GMT"/>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator4">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="About sediffx">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_About sediffx</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="toplevel_on_about_sediffx_activate" object="toplevel" last_modification_time="Tue, 09 Jan 2007 20:28:55 GMT"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image213">
+ <property name="visible">True</property>
+ <property name="stock">gtk-about</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+ <property name="tooltips">True</property>
+ <property name="show_arrow">True</property>
+
+ <child>
+ <widget class="GtkToolButton" id="open policies button">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Open Policies</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-open</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">True</property>
+ <signal name="clicked" handler="toplevel_on_open_policies_button_click" object="toplevel" last_modification_time="Tue, 16 Jan 2007 23:30:33 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="run diff button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">Run Diff</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-execute</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="toplevel_on_run_diff_button_click" object="toplevel" last_modification_time="Wed, 10 Jan 2007 21:33:48 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkToolButton" id="remap types button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="label" translatable="yes">Remap Types</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-preferences</property>
+ <property name="visible_horizontal">True</property>
+ <property name="visible_vertical">True</property>
+ <property name="is_important">False</property>
+ <signal name="clicked" handler="toplevel_on_remap_types_button_click" object="toplevel" last_modification_time="Tue, 09 Jan 2007 23:49:40 GMT"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="toplevel main notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkHPaned" id="hpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">170</property>
+
+ <child>
+ <widget class="GtkVPaned" id="vpaned2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">300</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow18">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="toplevel summary view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow11">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkViewport" id="viewport3">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Difference Key</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow16">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="toplevel key view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">False</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVPaned" id="vpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">491</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow8">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="toplevel results view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK</property>
+ <property name="extension_events">GDK_EXTENSION_EVENTS_ALL</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">True</property>
+ <property name="resize">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label" translatable="yes">Differences</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="toplevel policy_orig notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow12">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="toplevel policy_orig stats text">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="has_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Policy Statistics</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow13">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="toplevel policy_orig source text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Original Policy</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="toplevel policy_mod notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">True</property>
+ <property name="show_border">True</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="toplevel policy_mod stats text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">False</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="label" translatable="yes">Policy Statistics</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow15">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTextView" id="toplevel policy_mod source text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="overwrite">False</property>
+ <property name="accepts_tab">True</property>
+ <property name="justification">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap_mode">GTK_WRAP_NONE</property>
+ <property name="cursor_visible">True</property>
+ <property name="pixels_above_lines">0</property>
+ <property name="pixels_below_lines">0</property>
+ <property name="pixels_inside_wrap">0</property>
+ <property name="left_margin">0</property>
+ <property name="right_margin">0</property>
+ <property name="indent">0</property>
+ <property name="text" translatable="yes"></property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Source</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Modified Policy</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="toplevel line label">
+ <property name="width_request">82</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="toplevel stats label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">4</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="find_dialog">
+ <property name="title" translatable="yes">Sediff Find</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="find close button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="find find button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-find</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkEntry" id="find entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="find forward radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Forward</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="find reverse radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Reverse</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">find forward radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="remap_types">
+ <property name="title" translatable="yes">Remap Types</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="default_width">500</property>
+ <property name="default_height">450</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="closebutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-7</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkFrame" id="frame3">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow17">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="remap_types treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">True</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox3">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="remap_types remove button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="remap_types inferred checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Show inferred mappings</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Remap Types</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox7">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Original Policy</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">3</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="remap_types orig combo">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox8">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Modified Policy</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">3</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="remap_types mod combo">
+ <property name="visible">True</property>
+ <property name="add_tearoffs">False</property>
+ <property name="has_frame">True</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox2">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="remap_types add button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="remap_types only unmapped checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Show only unmapped types</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">True</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add New Remap</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="PoliciesOpenWindow">
+ <property name="title" translatable="yes">Open Policies</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="modal">True</property>
+ <property name="default_width">690</property>
+ <property name="default_height">360</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area6">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancel button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="ok button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="rundiff button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="sediff_open_dialog_on_open_and_diff_button_clicked" last_modification_time="Wed, 02 Nov 2005 16:20:30 GMT"/>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox21">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image214">
+ <property name="visible">True</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Run Diff</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox22">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox.1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Original Policy Type:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment.1.2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">10</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox.1.2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="monolithic radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Monolithic policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="modular radio">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Modular policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">monolithic radio</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox.2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="main filename label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Original Policy Filename:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.2.2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkEntry" id="base entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="base browse">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment.2.2.1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.2.2.1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image.2.2.1.1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label.2.2.1.1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.3">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow.3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="module view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox.3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkButton" id="module add button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module remove button">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module list import button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox27">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image218">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Import</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module list export button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox26">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image217">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">E_xport</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVSeparator" id="vseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">10</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Modified Policy Type:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment12">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">10</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="monolithic radio 1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Monolithic policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="modular radio 1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Modular policy</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">monolithic radio 1</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="main filename label 1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Modified Policy Filename:&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox23">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkEntry" id="base entry 1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="base browse 1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment13">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox24">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image215">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Browse</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox.3 1">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow19">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="module view 1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">True</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">False</property>
+ <property name="fixed_height_mode">False</property>
+ <property name="hover_selection">False</property>
+ <property name="hover_expand">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVButtonBox" id="vbuttonbox4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
+ <property name="spacing">8</property>
+
+ <child>
+ <widget class="GtkButton" id="module add button 1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module remove button 1">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module list import button 1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox28">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image219">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Import</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="module list export button 1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment18">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox29">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image220">
+ <property name="visible">True</property>
+ <property name="stock">gtk-save</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">E_xport</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+<widget class="GtkDialog" id="select_components">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes" context="yes">Run Diff</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">True</property>
+ <property name="resizable">False</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area7">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="cancelbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox16">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">&lt;b&gt;Select Components to Diff&lt;/b&gt;</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox17">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Basic Components:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">20</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment20">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">8</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox18">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="commons checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Commons</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="classes checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Classes</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="users checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Users</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="bools checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Booleans</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox21">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label41">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Type Enforcement:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment22">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">8</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox22">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="attribs checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Attributes</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="types checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Types</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="allow checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Access vector rules: allow</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="auditallow checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Access vector rules: auditallow</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="dontaudit checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Access vector rules: dontaudit</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="neverallow checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Access vector rules: neverallow</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="tooltip">This option may dramatically increase run time.</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="type_change checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Type rules: type__change</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="type_member checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Type rules: type__member</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="type_transition checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Type rules: type__transition</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox23">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label42">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">RBAC:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment23">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">8</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox24">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="roles checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Roles</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="roleallows checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Role allows</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="roletrans checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Role transitions</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox19">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label40">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">MLS:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment21">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">8</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox20">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="levels checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Levels</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="cats checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Categories</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="rangetrans checkbutton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes" context="yes">Range transitions</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="border_width">4</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkButton" id="select all button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="label" translatable="yes" context="yes">Select All</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="select none button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="label" translatable="yes" context="yes">Select None</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/sediff/sediffx.h b/sediff/sediffx.h
new file mode 100644
index 0000000..01d4cf8
--- /dev/null
+++ b/sediff/sediffx.h
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * Headers for main sediffx program.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SEDIFFX_H
+#define SEDIFFX_H
+
+#include <apol/policy.h>
+#include <apol/policy-path.h>
+#include <poldiff/poldiff.h>
+
+typedef struct sediffx sediffx_t;
+
+/** enumeration of which policy to affect -- the original policy (used
+ to be called "policy 1") or the modified policy ("policy 2") */
+typedef enum sediffx_policy
+{
+ SEDIFFX_POLICY_ORIG = 0, SEDIFFX_POLICY_MOD, SEDIFFX_POLICY_NUM
+} sediffx_policy_e;
+
+#define COPYRIGHT_INFO "Copyright (C) 2004-2007 Tresys Technology, LLC"
+
+/**
+ * Set one of the policies for sediffx. This will invalidate any
+ * currently executed poldiff_t objects.
+ *
+ * @param s sediffx object to query.
+ * @param which Which policy to set.
+ * @param policy New policy file for sediffx. If NULL then no policy
+ * is opened. Afterwards sediffx takes ownership of the policy.
+ * @param path If policy is not NULL, then the path that was used to
+ * open the policy.
+ */
+void sediffx_set_policy(sediffx_t * s, sediffx_policy_e which, apol_policy_t * policy, apol_policy_path_t * path);
+
+/**
+ * Return the policy path for the policy given. If the policy has not
+ * yet been loaded then return NULL.
+ *
+ * @param s sediffx object to query.
+ * @param which Which policy path to get.
+ *
+ * @return Path to the policy, or NULL if none set.
+ */
+const apol_policy_path_t *sediffx_get_policy_path(sediffx_t * s, const sediffx_policy_e which);
+
+/**
+ * Return the currently active poldiff object. If one is not yet
+ * created or if a policy has changed since the last time this
+ * function was called, then build a new one and return it. Note that
+ * this does not actually call poldiff_run(); it is up to the caller
+ * of this function to do that.
+ *
+ * @param s sediffx object to query.
+ * @param fn If a poldiff object is being created, a valid callback
+ * function to receive poldiff messages.
+ * @param arg Arbitrary argument to poldiff callback handler.
+ *
+ * @return Poldiff object for currently loaded policies, or NULL upon
+ * error.
+ */
+poldiff_t *sediffx_get_poldiff(sediffx_t * s, poldiff_handle_fn_t fn, void *arg);
+
+/**
+ * Set the flags that were used to run a poldiff. This function
+ * should be called immediately proceeding a call to poldiff_run().
+ *
+ * @param s sediffx object that contained the poldiff object that ran.
+ * @param flags Flags for that were used during the run.
+ *
+ * @see sediffx_get_poldiff_run_flags
+ */
+void sediffx_set_poldiff_run_flags(sediffx_t * s, uint32_t flags);
+
+/**
+ * Get the flags that were used to run a poldiff.
+ *
+ * @param s sediffx object to query.
+ *
+ * @return poldiff run flags, or 0 in none set.
+ *
+ * @see sediffx_set_poldiff_run_flags
+ */
+uint32_t sediffx_get_poldiff_run_flags(sediffx_t * s);
+
+#endif
diff --git a/sediff/sediffx.png b/sediff/sediffx.png
new file mode 100644
index 0000000..db4f83b
--- /dev/null
+++ b/sediff/sediffx.png
Binary files differ
diff --git a/sediff/sediffx.xcf b/sediff/sediffx.xcf
new file mode 100644
index 0000000..ded254f
--- /dev/null
+++ b/sediff/sediffx.xcf
Binary files differ
diff --git a/sediff/select_diff_dialog.c b/sediff/select_diff_dialog.c
new file mode 100644
index 0000000..881dbe5
--- /dev/null
+++ b/sediff/select_diff_dialog.c
@@ -0,0 +1,134 @@
+/**
+ * @file
+ * Run the dialog to allow the user to select components.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "select_diff_dialog.h"
+
+#include <assert.h>
+#include <glade/glade.h>
+
+struct component
+{
+ const char *name;
+ const uint32_t bit;
+};
+
+static const struct component comps[] = {
+ {"attribs checkbutton", POLDIFF_DIFF_ATTRIBS},
+ {"allow checkbutton", POLDIFF_DIFF_AVALLOW},
+ {"auditallow checkbutton", POLDIFF_DIFF_AVAUDITALLOW},
+ {"dontaudit checkbutton", POLDIFF_DIFF_AVDONTAUDIT},
+ {"neverallow checkbutton", POLDIFF_DIFF_AVNEVERALLOW},
+ {"bools checkbutton", POLDIFF_DIFF_BOOLS},
+ {"cats checkbutton", POLDIFF_DIFF_CATS},
+ {"classes checkbutton", POLDIFF_DIFF_CLASSES},
+ {"commons checkbutton", POLDIFF_DIFF_COMMONS},
+ {"levels checkbutton", POLDIFF_DIFF_LEVELS},
+ {"rangetrans checkbutton", POLDIFF_DIFF_RANGE_TRANS},
+ {"roles checkbutton", POLDIFF_DIFF_ROLES},
+ {"roleallows checkbutton", POLDIFF_DIFF_ROLE_ALLOWS},
+ {"roletrans checkbutton", POLDIFF_DIFF_ROLE_TRANS},
+ {"users checkbutton", POLDIFF_DIFF_USERS},
+ {"type_change checkbutton", POLDIFF_DIFF_TECHANGE},
+ {"type_member checkbutton", POLDIFF_DIFF_TEMEMBER},
+ {"type_transition checkbutton", POLDIFF_DIFF_TETRANS},
+ {"types checkbutton", POLDIFF_DIFF_TYPES},
+ {NULL, 0}
+};
+
+static uint32_t prev_selection = POLDIFF_DIFF_ALL & ~POLDIFF_DIFF_AVNEVERALLOW;
+
+static void select_diff_on_select_all_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ GladeXML *xml = (GladeXML *) user_data;
+ size_t i;
+ const struct component *c;
+ for (i = 0; comps[i].name != NULL; i++) {
+ c = comps + i;
+ GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name));
+ gtk_toggle_button_set_active(cb, TRUE);
+ }
+}
+
+static void select_diff_on_select_none_click(GtkButton * button __attribute__ ((unused)), gpointer user_data)
+{
+ GladeXML *xml = (GladeXML *) user_data;
+ size_t i;
+ const struct component *c;
+ for (i = 0; comps[i].name != NULL; i++) {
+ c = comps + i;
+ GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name));
+ gtk_toggle_button_set_active(cb, FALSE);
+ }
+}
+
+int select_diff_dialog_run(toplevel_t * top)
+{
+ GladeXML *xml = glade_xml_new(toplevel_get_glade_xml(top), "select_components", NULL);
+ GtkDialog *dialog = GTK_DIALOG(glade_xml_get_widget(xml, "select_components"));
+ assert(dialog != NULL);
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), toplevel_get_window(top));
+
+ size_t i;
+ const struct component *c;
+ for (i = 0; comps[i].name != NULL; i++) {
+ c = comps + i;
+ GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name));
+ assert(cb != NULL);
+ gtk_toggle_button_set_active(cb, c->bit & prev_selection ? TRUE : FALSE);
+ }
+
+ GtkButton *b = GTK_BUTTON(glade_xml_get_widget(xml, "select all button"));
+ assert(b != NULL);
+ g_signal_connect(b, "clicked", G_CALLBACK(select_diff_on_select_all_click), xml);
+ b = GTK_BUTTON(glade_xml_get_widget(xml, "select none button"));
+ assert(b != NULL);
+ g_signal_connect(b, "clicked", G_CALLBACK(select_diff_on_select_none_click), xml);
+
+ uint32_t result = 0;
+ while (result == 0) {
+ result = 0;
+ if (gtk_dialog_run(dialog) != GTK_RESPONSE_OK) {
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ return 0;
+ }
+
+ for (i = 0; comps[i].name != NULL; i++) {
+ c = comps + i;
+ GtkToggleButton *cb = GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, c->name));
+ if (gtk_toggle_button_get_active(cb)) {
+ result |= c->bit;
+ }
+ }
+
+ if (result == 0) {
+ toplevel_ERR(top, "%s", "At least one component must be selected.");
+ }
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ prev_selection = result;
+ return result;
+}
diff --git a/sediff/select_diff_dialog.h b/sediff/select_diff_dialog.h
new file mode 100644
index 0000000..227ab04
--- /dev/null
+++ b/sediff/select_diff_dialog.h
@@ -0,0 +1,42 @@
+/**
+ * @file
+ * Dialog that allows the user to select which policy components to
+ * diff.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SELECT_DIFF_DIALOG_H
+#define SELECT_DIFF_DIALOG_H
+
+#include "toplevel.h"
+
+/**
+ * Display and run a dialog that allows the user to select which
+ * policy components to diff.
+ *
+ * @param top Toplevel for the application.
+ *
+ * @return Bitmap of which components to diff; the bits correspond to
+ * those defined in poldiff/poldiff.h
+ */
+int select_diff_dialog_run(toplevel_t * top);
+
+#endif
diff --git a/sediff/toplevel.c b/sediff/toplevel.c
new file mode 100644
index 0000000..db6d1f5
--- /dev/null
+++ b/sediff/toplevel.c
@@ -0,0 +1,728 @@
+/**
+ * @file
+ * Implementation for sediffx's main toplevel window.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "find_dialog.h"
+#include "open_policies_dialog.h"
+#include "policy_view.h"
+#include "remap_types_dialog.h"
+#include "sediffx.h"
+#include "select_diff_dialog.h"
+#include "toplevel.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <apol/util.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+
+struct toplevel
+{
+ sediffx_t *s;
+ progress_t *progress;
+ find_dialog_t *find;
+ results_t *results;
+ policy_view_t *views[SEDIFFX_POLICY_NUM];
+ GladeXML *xml;
+ /** filename for glade file */
+ char *xml_filename;
+ /** toplevel window widget */
+ GtkWindow *w;
+ /** toplevel notebook widget */
+ GtkNotebook *notebook;
+};
+
+/**
+ * Enable/disable all items (menus and buttons) that depend upon if a
+ * policy is loaded.
+ *
+ * @param top Toplevel object containing menu widgets.
+ * @param TRUE to enable items, FALSE to disable.
+ */
+static void toplevel_enable_policy_items(toplevel_t * top, gboolean sens)
+{
+ static const char *items[] = {
+ "Copy", "Select All",
+ "Find", "Run Diff", "Remap Types",
+ "run diff button", "remap types button",
+ NULL
+ };
+ size_t i;
+ const char *s;
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(w != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+/**
+ * Update the toplevel's title bar to list the policies currently
+ * opened.
+ *
+ * @param top Toplevel to modify.
+ */
+static void toplevel_update_title_bar(toplevel_t * top)
+{
+ const apol_policy_path_t *paths[SEDIFFX_POLICY_NUM];
+ char *types[SEDIFFX_POLICY_NUM] = { "Policy", "Policy" }, *s;
+ const char *primaries[SEDIFFX_POLICY_NUM] = { NULL, NULL };
+ sediffx_policy_e i;
+
+ paths[SEDIFFX_POLICY_ORIG] = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_ORIG);
+ paths[SEDIFFX_POLICY_MOD] = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_MOD);
+
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ if (paths[i] == NULL) {
+ primaries[i] = "No Policy";
+ } else {
+ if (apol_policy_path_get_type(paths[i]) == APOL_POLICY_PATH_TYPE_MODULAR) {
+ types[i] = "Base";
+ }
+ primaries[i] = apol_policy_path_get_primary(paths[i]);
+ }
+ }
+ if (asprintf(&s, "sediffx - [%s file: %s] [%s file: %s]",
+ types[SEDIFFX_POLICY_ORIG], primaries[SEDIFFX_POLICY_ORIG],
+ types[SEDIFFX_POLICY_MOD], primaries[SEDIFFX_POLICY_MOD]) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ gtk_window_set_title(top->w, s);
+ free(s);
+}
+
+/**
+ * Initialize the application icons for the program. These icons are
+ * the ones shown by the window manager within title bars and pagers.
+ * The last icon listed in the array will be displayed in the About
+ * dialog.
+ *
+ * @param top Toplevel whose icon to set. All child windows will
+ * inherit these icons.
+ */
+static void init_icons(toplevel_t * top)
+{
+ static const char *icon_names[] = { "sediffx-small.png", "sediffx.png" };
+ GdkPixbuf *icon;
+ char *path;
+ GList *icon_list = NULL;
+ size_t i;
+ for (i = 0; i < sizeof(icon_names) / sizeof(icon_names[0]); i++) {
+ if ((path = apol_file_find_path(icon_names[i])) == NULL) {
+ continue;
+ }
+ icon = gdk_pixbuf_new_from_file(path, NULL);
+ free(path);
+ if (icon == NULL) {
+ continue;
+ }
+ icon_list = g_list_append(icon_list, icon);
+ }
+ gtk_window_set_default_icon_list(icon_list);
+ gtk_window_set_icon_list(top->w, icon_list);
+}
+
+static void toplevel_on_switch_page(GtkNotebook * notebook __attribute__ ((unused)), GtkNotebookPage * page
+ __attribute__ ((unused)), guint page_num, gpointer user_data)
+{
+ toplevel_t *top = (toplevel_t *) user_data;
+ if (page_num != 0) {
+ toplevel_set_sort_menu_sensitivity(top, FALSE);
+ } else {
+ results_switch_to_page(top->results);
+ }
+}
+
+toplevel_t *toplevel_create(sediffx_t * s)
+{
+ toplevel_t *top;
+ int error = 0;
+ sediffx_policy_e i;
+ if ((top = calloc(1, sizeof(*top))) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ top->s = s;
+ if ((top->xml_filename = apol_file_find_path("sediffx.glade")) == NULL ||
+ (top->xml = glade_xml_new(top->xml_filename, "toplevel", NULL)) == NULL) {
+ fprintf(stderr, "Could not open sediffx.glade.\n");
+ error = EIO;
+ goto cleanup;
+ }
+ top->w = GTK_WINDOW(glade_xml_get_widget(top->xml, "toplevel"));
+ top->notebook = GTK_NOTEBOOK(glade_xml_get_widget(top->xml, "toplevel main notebook"));
+ assert(top->w != NULL && top->notebook != NULL);
+ init_icons(top);
+ g_object_set_data(G_OBJECT(top->w), "toplevel", top);
+ gtk_widget_show(GTK_WIDGET(top->w));
+ g_signal_connect(G_OBJECT(top->notebook), "switch-page", G_CALLBACK(toplevel_on_switch_page), top);
+
+ /* initialize sub-windows, now that glade XML file has been
+ * read */
+ if ((top->find = find_dialog_create(top)) == NULL ||
+ (top->progress = progress_create(top)) == NULL || (top->results = results_create(top)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ if ((top->views[i] = policy_view_create(top, i)) == NULL) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ error = errno;
+ goto cleanup;
+ }
+ }
+
+ glade_xml_signal_autoconnect(top->xml);
+
+ cleanup:
+ if (error != 0) {
+ toplevel_destroy(&top);
+ errno = error;
+ return NULL;
+ }
+ return top;
+}
+
+void toplevel_destroy(toplevel_t ** top)
+{
+ if (top != NULL && *top != NULL) {
+ sediffx_policy_e i;
+ find_dialog_destroy(&(*top)->find);
+ progress_destroy(&(*top)->progress);
+ results_destroy(&(*top)->results);
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ policy_view_destroy(&(*top)->views[i]);
+ }
+ free((*top)->xml_filename);
+ free(*top);
+ *top = NULL;
+ }
+}
+
+struct policy_run_datum
+{
+ toplevel_t *top;
+ apol_policy_path_t *paths[2];
+ apol_policy_t *policies[2];
+ int result;
+};
+
+/**
+ * Thread that loads and parses a policy file. It will write to
+ * progress_seaudit_handle_func() its status during the load.
+ *
+ * @param data Pointer to a struct policy_run_datum, for control
+ * information.
+ */
+static gpointer toplevel_open_policy_runner(gpointer data)
+{
+ struct policy_run_datum *run = (struct policy_run_datum *)data;
+ sediffx_policy_e i;
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ apol_policy_path_t *path = run->paths[i];
+ char *title = util_policy_path_to_string(path);
+ if (title == NULL) {
+ run->result = -1;
+ progress_abort(run->top->progress, "%s", strerror(errno));
+ return NULL;
+ }
+ progress_update(run->top->progress, "Opening %s", title);
+ free(title);
+ run->policies[i] =
+ apol_policy_create_from_policy_path(path, QPOL_POLICY_OPTION_NO_RULES, progress_apol_handle_func,
+ run->top->progress);
+ // poldiff_run() will rebuild the policies as needed
+ if (run->policies[i] == NULL) {
+ run->result = -1;
+ progress_abort(run->top->progress, NULL);
+ return NULL;
+ }
+ }
+ run->result = 0;
+ progress_done(run->top->progress);
+ return NULL;
+}
+
+int toplevel_open_policies(toplevel_t * top, apol_policy_path_t * orig_path, apol_policy_path_t * mod_path)
+{
+ struct policy_run_datum run;
+ memset(&run, 0, sizeof(run));
+ run.top = top;
+ run.paths[0] = orig_path;
+ run.paths[1] = mod_path;
+ sediffx_policy_e i;
+
+ util_cursor_wait(GTK_WIDGET(top->w));
+ progress_show(top->progress, "Loading Policies");
+ g_thread_create(toplevel_open_policy_runner, &run, FALSE, NULL);
+ progress_wait(top->progress);
+ progress_hide(top->progress);
+ util_cursor_clear(GTK_WIDGET(top->w));
+ if (run.result < 0) {
+ apol_policy_path_destroy(&run.paths[0]);
+ apol_policy_path_destroy(&run.paths[1]);
+ return run.result;
+ }
+ if (remap_types_update(run.policies[SEDIFFX_POLICY_ORIG], run.policies[SEDIFFX_POLICY_MOD]) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ apol_policy_path_destroy(&run.paths[0]);
+ apol_policy_path_destroy(&run.paths[1]);
+ return -1;
+ }
+ results_open_policies(top->results, run.policies[SEDIFFX_POLICY_ORIG], run.policies[SEDIFFX_POLICY_MOD]);
+ for (i = SEDIFFX_POLICY_ORIG; i < SEDIFFX_POLICY_NUM; i++) {
+ policy_view_update(top->views[i], run.policies[i], run.paths[i]);
+ sediffx_set_policy(top->s, i, run.policies[i], run.paths[i]);
+ }
+ results_update(top->results);
+ toplevel_enable_policy_items(top, TRUE);
+ toplevel_update_title_bar(top);
+ return 0;
+}
+
+struct run_datum
+{
+ toplevel_t *top;
+ uint32_t run_flags;
+ int result;
+};
+
+static gpointer toplevel_run_diff_runner(gpointer data)
+{
+ struct run_datum *run = data;
+ poldiff_t *diff = sediffx_get_poldiff(run->top->s, progress_poldiff_handle_func, run->top->progress);
+ if (diff == NULL) {
+ run->result = -1;
+ progress_abort(run->top->progress, "Could not get a poldiff object: %s", strerror(errno));
+ return NULL;
+ }
+ run->result = poldiff_run(diff, run->run_flags);
+ sediffx_set_poldiff_run_flags(run->top->s, run->run_flags);
+ if (run->run_flags & (POLDIFF_DIFF_AVRULES | POLDIFF_DIFF_TERULES)) {
+ poldiff_enable_line_numbers(diff);
+ }
+ if (run->result < 0) {
+ progress_abort(run->top->progress, NULL);
+ } else {
+ progress_done(run->top->progress);
+ }
+ return NULL;
+}
+
+void toplevel_run_diff(toplevel_t * top)
+{
+ struct run_datum r;
+
+ r.run_flags = select_diff_dialog_run(top);
+ if (r.run_flags == 0) {
+ return;
+ }
+ r.top = top;
+ r.result = 0;
+
+ results_clear(top->results);
+ util_cursor_wait(GTK_WIDGET(top->w));
+ progress_show(top->progress, "Running Diff");
+ g_thread_create(toplevel_run_diff_runner, &r, FALSE, NULL);
+ progress_wait(top->progress);
+ progress_hide(top->progress);
+ util_cursor_clear(GTK_WIDGET(top->w));
+ if (r.result == 0) {
+ results_update(top->results);
+ }
+}
+
+void toplevel_show_policy_line(toplevel_t * top, sediffx_policy_e which, unsigned long line)
+{
+ gtk_notebook_set_current_page(top->notebook, 1 + which);
+ policy_view_show_policy_line(top->views[which], line);
+}
+
+void toplevel_set_sort_menu_sensitivity(toplevel_t * top, gboolean sens)
+{
+ GtkWidget *w = glade_xml_get_widget(top->xml, "sort menu item");
+ assert(w != NULL);
+ gtk_widget_set_sensitive(w, sens);
+}
+
+void toplevel_set_sort_menu_selection(toplevel_t * top, results_sort_e field, results_sort_dir_e dir)
+{
+ static const char *menu_items[][2] = {
+ {"Default Sort", "Default Sort"},
+ {"Ascending source type", "Descending source type"},
+ {"Ascending target type", "Descending target type"},
+ {"Ascending object class", "Descending object class"},
+ {"Ascending conditional", "Descending conditonal"}
+ };
+ int direction = 1;
+ if (dir == RESULTS_SORT_ASCEND) {
+ direction = 0;
+ }
+ GtkWidget *w = glade_xml_get_widget(top->xml, menu_items[field][direction]);
+ assert(w != NULL);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), TRUE);
+}
+
+char *toplevel_get_glade_xml(toplevel_t * top)
+{
+ return top->xml_filename;
+}
+
+gint toplevel_get_notebook_page(toplevel_t * top)
+{
+ return gtk_notebook_get_current_page(top->notebook);
+}
+
+progress_t *toplevel_get_progress(toplevel_t * top)
+{
+ return top->progress;
+}
+
+GtkWindow *toplevel_get_window(toplevel_t * top)
+{
+ return top->w;
+}
+
+GtkTextView *toplevel_get_text_view(toplevel_t * top)
+{
+ gint pagenum = gtk_notebook_get_current_page(top->notebook);
+ switch (pagenum) {
+ case 0:
+ return results_get_text_view(top->results);
+ case 1:
+ return policy_view_get_text_view(top->views[SEDIFFX_POLICY_ORIG]);
+ case 2:
+ return policy_view_get_text_view(top->views[SEDIFFX_POLICY_MOD]);
+ }
+ /* should never get here */
+ assert(0);
+ return NULL;
+}
+
+poldiff_t *toplevel_get_poldiff(toplevel_t * top)
+{
+ return sediffx_get_poldiff(top->s, progress_poldiff_handle_func, top->progress);
+}
+
+uint32_t toplevel_get_poldiff_run_flags(toplevel_t * top)
+{
+ return sediffx_get_poldiff_run_flags(top->s);
+}
+
+/**
+ * Pop-up a dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param msg_type Type of message being displayed.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+static void toplevel_message(toplevel_t * top, GtkMessageType msg_type, const char *fmt, va_list ap)
+{
+ GtkWidget *dialog;
+ char *msg;
+ if (vasprintf(&msg, fmt, ap) < 0) {
+ ERR(NULL, "%s", strerror(errno));
+ return;
+ }
+ dialog = gtk_message_dialog_new(top->w, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg);
+ free(msg);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+void toplevel_ERR(toplevel_t * top, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ toplevel_message(top, GTK_MESSAGE_ERROR, format, ap);
+ va_end(ap);
+}
+
+void toplevel_WARN(toplevel_t * top, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ toplevel_message(top, GTK_MESSAGE_WARNING, format, ap);
+ va_end(ap);
+}
+
+/******************** menu callbacks below ********************/
+
+void toplevel_on_open_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ const apol_policy_path_t *orig_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_ORIG);
+ const apol_policy_path_t *mod_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_MOD);
+ open_policies_dialog_run(top, orig_path, mod_path);
+}
+
+void toplevel_on_quit_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ top->w = NULL;
+ gtk_main_quit();
+}
+
+void toplevel_on_edit_menu_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkTextView *view = toplevel_get_text_view(top);
+ GtkTextBuffer *txt = gtk_text_view_get_buffer(view);
+ GtkWidget *copy = glade_xml_get_widget(top->xml, "Copy");
+ GtkWidget *select_all = glade_xml_get_widget(top->xml, "Select All");
+ GtkTextIter start, end;
+ assert(copy != NULL && select_all != NULL);
+
+ /* check to see if anything has been selected and set copy
+ * button up */
+ if (gtk_text_buffer_get_selection_bounds(txt, &start, &end)) {
+ gtk_widget_set_sensitive(copy, TRUE);
+ } else {
+ gtk_widget_set_sensitive(copy, FALSE);
+ }
+ /* check to see if there is anything currently in this buffer
+ * that can be selected */
+ gtk_text_buffer_get_start_iter(txt, &start);
+ gtk_text_buffer_get_end_iter(txt, &end);
+ if (gtk_text_iter_get_offset(&start) == gtk_text_iter_get_offset(&end)) {
+ gtk_widget_set_sensitive(select_all, FALSE);
+ } else {
+ gtk_widget_set_sensitive(select_all, TRUE);
+ }
+}
+
+void toplevel_on_copy_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkClipboard *clipboard = gtk_clipboard_get(NULL);
+ GtkTextView *view = toplevel_get_text_view(top);
+ GtkTextBuffer *txt = gtk_text_view_get_buffer(view);
+ if (gtk_text_buffer_get_selection_bounds(txt, NULL, NULL)) {
+ gtk_text_buffer_copy_clipboard(txt, clipboard);
+ }
+}
+
+void toplevel_on_select_all_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkTextView *view = toplevel_get_text_view(top);
+ GtkTextBuffer *txt = gtk_text_view_get_buffer(view);
+ GtkTextIter start, end;
+ gtk_text_buffer_get_start_iter(txt, &start);
+ gtk_text_buffer_get_end_iter(txt, &end);
+ gtk_text_buffer_select_range(txt, &start, &end);
+}
+
+void toplevel_on_find_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ find_dialog_show(top->find);
+}
+
+void toplevel_on_run_diff_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_run_diff(top);
+}
+
+void toplevel_on_remap_types_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ remap_types_run(top);
+}
+
+void toplevel_on_default_sort_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_DEFAULT, RESULTS_SORT_ASCEND);
+ }
+}
+
+void toplevel_on_source_type_asc_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_SOURCE, RESULTS_SORT_ASCEND);
+ }
+}
+
+void toplevel_on_source_type_des_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_SOURCE, RESULTS_SORT_DESCEND);
+ }
+}
+
+void toplevel_on_target_type_asc_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_TARGET, RESULTS_SORT_ASCEND);
+ }
+}
+
+void toplevel_on_target_type_des_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_TARGET, RESULTS_SORT_DESCEND);
+ }
+}
+
+void toplevel_on_class_asc_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_CLASS, RESULTS_SORT_ASCEND);
+ }
+}
+
+void toplevel_on_class_des_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_CLASS, RESULTS_SORT_DESCEND);
+ }
+}
+
+void toplevel_on_conditional_asc_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_COND, RESULTS_SORT_ASCEND);
+ }
+}
+
+void toplevel_on_conditional_des_activate(gpointer user_data, GtkMenuItem * menuitem)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
+ results_sort(top->results, RESULTS_SORT_COND, RESULTS_SORT_DESCEND);
+ }
+}
+
+void toplevel_on_help_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkWidget *window;
+ GtkWidget *scroll;
+ GtkWidget *text_view;
+ GtkTextBuffer *buffer;
+ char *help_text = NULL;
+ size_t len;
+ int rt;
+ char *dir;
+
+ window = gtk_dialog_new_with_buttons("sediffx Help",
+ GTK_WINDOW(top->w),
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE);
+ g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ text_view = gtk_text_view_new();
+ gtk_window_set_default_size(GTK_WINDOW(window), 520, 300);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), scroll);
+ gtk_container_add(GTK_CONTAINER(scroll), text_view);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_NONE);
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
+ if ((dir = apol_file_find_path("sediff_help.txt")) == NULL) {
+ toplevel_ERR(top, "Cannot find help file.");
+ return;
+ }
+ rt = apol_file_read_to_buffer(dir, &help_text, &len);
+ free(dir);
+ if (rt != 0) {
+ free(help_text);
+ return;
+ }
+ gtk_text_buffer_set_text(buffer, help_text, len);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_widget_show(text_view);
+ gtk_widget_show(scroll);
+ gtk_widget_show(window);
+}
+
+void toplevel_on_about_sediffx_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+#ifdef GTK_2_8
+ gtk_show_about_dialog(top->w,
+ "comments", "Policy Semantic Difference Tool for Security Enhanced Linux",
+ "copyright", COPYRIGHT_INFO,
+ "name", "sediffx", "version", VERSION, "website", "http://oss.tresys.com/projects/setools", NULL);
+#else
+ GtkWidget *w = gtk_message_dialog_new(top->w,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "%s %s\n%s\n%s\n%s",
+ "sediffx", VERSION,
+ "Policy Semantic Difference Tool for Security Enhanced Linux",
+ COPYRIGHT_INFO,
+ "http://oss.tresys.com/projects/setools");
+ gtk_dialog_run(GTK_DIALOG(w));
+ gtk_widget_destroy(w);
+#endif
+}
+
+void toplevel_on_open_policies_button_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ const apol_policy_path_t *orig_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_ORIG);
+ const apol_policy_path_t *mod_path = sediffx_get_policy_path(top->s, SEDIFFX_POLICY_MOD);
+ open_policies_dialog_run(top, orig_path, mod_path);
+}
+
+void toplevel_on_run_diff_button_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_run_diff(top);
+}
+
+void toplevel_on_remap_types_button_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ remap_types_run(top);
+}
+
+void toplevel_on_destroy(gpointer user_data, GtkObject * object __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ top->w = NULL;
+ gtk_main_quit();
+}
diff --git a/sediff/toplevel.h b/sediff/toplevel.h
new file mode 100644
index 0000000..14074a7
--- /dev/null
+++ b/sediff/toplevel.h
@@ -0,0 +1,202 @@
+/**
+ * @file
+ * Headers for main toplevel window.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Brandon Whalen bwhalen@tresys.com
+ * @author Randy Wicks rwicks@tresys.com
+ *
+ * Copyright (C) 2005-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TOPLEVEL_H
+#define TOPLEVEL_H
+
+typedef struct toplevel toplevel_t;
+
+#include "progress.h"
+#include "results.h"
+#include "sediffx.h"
+#include <apol/policy-path.h>
+#include <gtk/gtk.h>
+#include <poldiff/poldiff.h>
+
+/**
+ * Allocate and return an instance of the toplevel window object.
+ * This will create the window, set up the menus and icons, then
+ * display the window.
+ *
+ * @param s Main sediffx object that will control the toplevel.
+ *
+ * @return An initialized toplevel object, or NULL upon error. The
+ * caller must call toplevel_destroy() afterwards.
+ */
+toplevel_t *toplevel_create(sediffx_t * s);
+
+/**
+ * Destroy the toplevel window. This function will recursively
+ * destroy all other windows. This does nothing if the pointer is set
+ * to NULL.
+ *
+ * @param top Reference to a toplevel object. Afterwards the pointer
+ * will be set to NULL.
+ */
+void toplevel_destroy(toplevel_t ** top);
+
+/**
+ * Open the policy files. Upon success destroy the existing policies
+ * and current poldiff objects.
+ *
+ * @param top Toplevel object, used for UI control.
+ * @param orig_path Path to the original policy. This function takes
+ * ownership of this object.
+ * @param mod_path Path to the modified policy. This function takes
+ * ownership of this object.
+ *
+ * @return 0 on successful open, < 0 on error.
+ */
+int toplevel_open_policies(toplevel_t * top, apol_policy_path_t * orig_path, apol_policy_path_t * mod_path);
+
+/**
+ * Run the current poldiff object. Afterwards this function will
+ * notify the results object to update its view.
+ *
+ * @param top Toplevel object whose poldiff to run.
+ */
+void toplevel_run_diff(toplevel_t * top);
+
+/**
+ * Switch to the given policy's source tab, if not already visible,
+ * and then scroll the view to show the given line. Policy line
+ * numbers are zero-indexed.
+ *
+ * @param top Toplevel object containing policy source tabs.
+ * @param which Which policy's source tab to show.
+ * @param line Line to show.
+ */
+void toplevel_show_policy_line(toplevel_t * top, sediffx_policy_e which, unsigned long line);
+
+/**
+ * Enable or disable the toplevel's sort menu. The sort menu should
+ * be enabled only when showing the differences for TE rules;
+ * otherwise it should be disabled.
+ *
+ * @param top Toplevel object containing sort menu.
+ * @param sens New sensitivity for the menu.
+ */
+void toplevel_set_sort_menu_sensitivity(toplevel_t * top, gboolean sens);
+
+/**
+ * Set the current sort menu selection to the given field and
+ * direction.
+ *
+ * @param top Toplevel object containing sort menu.
+ * @param field Sort field to select.
+ * @param dir Sort direction to select.
+ */
+void toplevel_set_sort_menu_selection(toplevel_t * top, results_sort_e field, results_sort_dir_e dir);
+
+/**
+ * Return the filename containing sediffx's glade file.
+ *
+ * @param top Toplevel containing glade XML declarations.
+ *
+ * @return Name of the glade file. Do not modify this string.
+ */
+char *toplevel_get_glade_xml(toplevel_t * top);
+
+/**
+ * Return the current page number for the toplevel main notebook.
+ *
+ * @param top Toplevel to query.
+ *
+ * @return Page number for the currently showing tab.
+ */
+gint toplevel_get_notebook_page(toplevel_t * top);
+
+/**
+ * Return the progress object, so that sub-windows may also show the
+ * threaded progress object.
+ *
+ * @param top Toplevel containing progress object.
+ *
+ * @return Progress object. Do not free() this pointer.
+ */
+progress_t *toplevel_get_progress(toplevel_t * top);
+
+/**
+ * Return the main application window. Sub-windows should be set
+ * transient to this window.
+ *
+ * @param top Toplevel containing main window.
+ *
+ * @return Main window.
+ */
+GtkWindow *toplevel_get_window(toplevel_t * top);
+
+/**
+ * Get the currently showing text view. This depends upon which
+ * toplevel notebook page is showing.
+ *
+ * @param top Toplevel containing text views.
+ *
+ * @return Currently visible text view.
+ */
+GtkTextView *toplevel_get_text_view(toplevel_t * top);
+
+/**
+ * Retrieve the currently active poldiff object. If policies have not
+ * yet been loaded then this returns NULL. Note that the poldiff
+ * object will not be run yet; for that call toplevel_run_diff().
+ *
+ * @param top Toplevel containing poldiff object.
+ *
+ * @return poldiff object, or NULL if none availble or upon error.
+ */
+poldiff_t *toplevel_get_poldiff(toplevel_t * top);
+
+/**
+ * Get the flags that were used the most recently run poldiff.
+ *
+ * @param top Toplevel object to query.
+ *
+ * @return poldiff run flags, or 0 in none set.
+ */
+uint32_t toplevel_get_poldiff_run_flags(toplevel_t * top);
+
+/**
+ * Pop-up an error dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param format Format string to print, using syntax of printf(3).
+ */
+void toplevel_ERR(toplevel_t * top, const char *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+/**
+ * Pop-up a warning dialog with a line of text and wait for the user
+ * to dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param format Format string to print, using syntax of printf(3).
+ */
+void toplevel_WARN(toplevel_t * top, const char *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+#endif
diff --git a/sediff/utilgui.c b/sediff/utilgui.c
new file mode 100644
index 0000000..04e1e05
--- /dev/null
+++ b/sediff/utilgui.c
@@ -0,0 +1,168 @@
+/**
+ * @file
+ * Miscellaneous helper functions for GTK+ applications.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "utilgui.h"
+#include <string.h>
+#include <apol/util.h>
+
+void util_message(GtkWindow * parent, GtkMessageType msg_type, const char *msg)
+{
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+void util_cursor_wait(GtkWidget * widget)
+{
+ GdkCursor *cursor;
+ if (widget->window != NULL) {
+ cursor = gdk_cursor_new(GDK_WATCH);
+ gdk_window_set_cursor(widget->window, cursor);
+ gdk_cursor_unref(cursor);
+ }
+}
+
+/**
+ * WARNING: this is sort of a hack
+ *
+ * If we reset the pointer at the end of a callback, it gets reset too
+ * soon (i.e. before all of the pending events have been processed. To
+ * avoid this, this function is put in an idle handler by
+ * util_cursor_clear().
+ */
+static gboolean pointer_reset(gpointer data)
+{
+ gdk_window_set_cursor(GTK_WIDGET(data)->window, NULL);
+ return FALSE;
+}
+
+void util_cursor_clear(GtkWidget * widget)
+{
+ g_idle_add(&pointer_reset, widget);
+}
+
+void util_text_buffer_clear(GtkTextBuffer * txt)
+{
+ GtkTextIter start, end;
+ gtk_text_buffer_get_start_iter(txt, &start);
+ gtk_text_buffer_get_end_iter(txt, &end);
+ gtk_text_buffer_remove_all_tags(txt, &start, &end);
+ gtk_text_buffer_delete(txt, &start, &end);
+}
+
+apol_vector_t *util_open_file(GtkWindow * parent, const char *title, const char *init_path, gboolean multiple)
+{
+ GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+ apol_vector_t *paths = NULL;
+ if (init_path != NULL) {
+ gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), init_path);
+ }
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), multiple);
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ GSList *files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+ GSList *f;
+ paths = apol_vector_create(g_free);
+ for (f = files; f != NULL; f = f->next) {
+ apol_vector_append(paths, f->data);
+ }
+ g_slist_free(files);
+ }
+ gtk_widget_destroy(dialog);
+ return paths;
+}
+
+char *util_save_file(GtkWindow * parent, const char *title, const char *init_path)
+{
+ GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
+ char *path = NULL;
+#ifdef GTK_2_8
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+#endif
+ if (init_path != NULL) {
+ gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), init_path);
+ } else {
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled");
+ }
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ }
+ gtk_widget_destroy(dialog);
+ return path;
+}
+
+char *util_policy_path_to_string(const apol_policy_path_t * path)
+{
+ char *s;
+ const char *primary_path = apol_policy_path_get_primary(path);
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ return strdup(primary_path);
+ } else {
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t num_modules = apol_vector_get_size(modules);
+ if (asprintf(&s, "%s + %zd module%s", primary_path, num_modules, num_modules == 1 ? "" : "s") < 0) {
+ return NULL;
+ }
+ return s;
+ }
+}
+
+char *util_policy_path_to_full_string(const apol_policy_path_t * path)
+{
+ const char *primary_path = apol_policy_path_get_primary(path);
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ return strdup(primary_path);
+ } else {
+ char *s = NULL, *t = NULL;
+ size_t len = 0;
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t num_modules = apol_vector_get_size(modules);
+ if (apol_str_appendf(&s, &len, "%s + %zd module%s", primary_path, num_modules, num_modules == 1 ? "" : "s") < 0) {
+ return NULL;
+ }
+ if (num_modules > 0) {
+ if ((t = apol_str_join(modules, "\n\t")) == NULL || apol_str_appendf(&s, &len, "\n\t%s", t) < 0) {
+ free(t);
+ free(s);
+ return NULL;
+ }
+ }
+ return s;
+ }
+}
+
+const gchar *util_combo_box_get_active_text(GtkComboBox * w)
+{
+#ifdef GTK_2_8
+ return gtk_combo_box_get_active_text(w);
+#else
+ return gtk_entry_get_text(GTK_ENTRY(GTK_BIN(w)->child));
+#endif
+}
diff --git a/sediff/utilgui.h b/sediff/utilgui.h
new file mode 100644
index 0000000..ad91514
--- /dev/null
+++ b/sediff/utilgui.h
@@ -0,0 +1,125 @@
+/**
+ * @file
+ * Miscellaneous helper functions for GTK+ applications.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef UTILGUI_H
+#define UTILGUI_H
+
+#include <apol/policy-path.h>
+#include <gtk/gtk.h>
+
+/**
+ * Pop-up a dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param parent Parent window; this message dialog will be centered
+ * upon the parent.
+ * @param msg_type Type of message being displayed.
+ * @param msg Text of message to display.
+ */
+void util_message(GtkWindow * parent, GtkMessageType msg_type, const char *msg);
+/**
+ * Set the cursor over a widget to the watch cursor.
+ *
+ * @param widget Widget whose cursor to set.
+ */
+void util_cursor_wait(GtkWidget * widget);
+
+/**
+ * Clear the cursor over a widget, setting it to the default arrow.
+ *
+ * @param widget Widget whose cursor to set.
+ */
+void util_cursor_clear(GtkWidget * widget);
+
+/**
+ * Given some arbitrary GtkTextBuffer, remove all of its text and
+ * attributes. This will not delete the buffer's tag table.
+ *
+ * @param txt Text buffer to clear.
+ */
+void util_text_buffer_clear(GtkTextBuffer * txt);
+
+/**
+ * Allow the user select an existing file. Run the dialog and return
+ * the selected filename.
+ *
+ * @param parent Parent window; this dialog will be centered upon the
+ * parent.
+ * @param title Name of the dialog.
+ * @param init_path If not NULL, the default filename.
+ * @param multiple If true, allow the user to select multiple files.
+ * Otherwise only one file at a time may be chosen.
+ *
+ * @return Name of the file selected, or NULL if no file was selected.
+ * The caller must free the returned value with g_free().
+ */
+apol_vector_t *util_open_file(GtkWindow * parent, const char *title, const char *init_path, gboolean multiple);
+
+/**
+ * Allow the user select an existing file or enter a new file for
+ * writing. Run the dialog and return the selected filename.
+ *
+ * @param parent Parent window; this dialog will be centered upon the
+ * parent.
+ * @param title Name of the dialog.
+ * @param init_path If not NULL, the default filename.
+ *
+ * @return Name of the file selected, or NULL if no file was selected.
+ * The caller must free the returned value with g_free().
+ */
+char *util_save_file(GtkWindow * parent, const char *title, const char *init_path);
+
+/**
+ * Given a policy path, return a newly allocated string that briefly
+ * describes the path. This string is suitable for showing to the
+ * user.
+ *
+ * @param path Policy path to describe.
+ *
+ * @return String describing the path, or NULL upon error. The caller
+ * must free the string afterwards.
+ */
+char *util_policy_path_to_string(const apol_policy_path_t * path);
+
+/**
+ * Given a policy path, return a newly allocated string that fully
+ * describes the path. This string is suitable for showing to the
+ * user.
+ *
+ * @param path Policy path to describe.
+ *
+ * @return String describing the path, or NULL upon error. The caller
+ * must free the string afterwards.
+ */
+char *util_policy_path_to_full_string(const apol_policy_path_t * path);
+
+/**
+ * Get the active text from a GtkComboBox.
+ *
+ * Whereas GTK 2.6 has gtk_combo_box_get_active_text(), GTK 2.4
+ * (another supported platform) does not.
+ */
+const gchar *util_combo_box_get_active_text(GtkComboBox * w);
+
+#endif