diff options
Diffstat (limited to 'apol/top.tcl')
-rw-r--r-- | apol/top.tcl | 1228 |
1 files changed, 1228 insertions, 0 deletions
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] +} |