1#! /usr/bin/python -Es 2# Copyright (C) 2012-2013 Red Hat 3# AUTHOR: Dan Walsh <dwalsh@redhat.com> 4# AUTHOR: Miroslav Grepl <mgrepl@redhat.com> 5# see file 'COPYING' for use and warranty information 6# 7# semanage is a tool for managing SELinux configuration files 8# 9# This program is free software; you can redistribute it and/or 10# modify it under the terms of the GNU General Public License as 11# published by the Free Software Foundation; either version 2 of 12# the License, or (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program; if not, write to the Free Software 21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 22# 02111-1307 USA 23# 24# 25__all__ = [ 'ManPage', 'HTMLManPages', 'manpage_domains', 'manpage_roles', 'gen_domains' ] 26 27import string 28import argparse 29import selinux 30import sepolicy 31from sepolicy import * 32 33import commands 34import sys, os, re, time 35 36equiv_dict={ "smbd" : [ "samba" ], "httpd" : [ "apache" ], "virtd" : [ "virt", "libvirt", "svirt", "svirt_tcg", "svirt_lxc_t", "svirt_lxc_net_t" ], "named" : [ "bind" ], "fsdaemon" : [ "smartmon" ], "mdadm" : [ "raid" ] } 37 38equiv_dirs=[ "/var" ] 39modules_dict = None 40def gen_modules_dict(path = "/usr/share/selinux/devel/policy.xml"): 41 global modules_dict 42 if modules_dict: 43 return modules_dict 44 45 import xml.etree.ElementTree 46 modules_dict = {} 47 try: 48 tree = xml.etree.ElementTree.fromstring(policy_xml(path)) 49 for l in tree.findall("layer"): 50 for m in l.findall("module"): 51 name = m.get("name") 52 if name == "user" or name == "unconfined": 53 continue 54 if name == "unprivuser": 55 name = "user" 56 if name == "unconfineduser": 57 name = "unconfined" 58 for b in m.findall("summary"): 59 modules_dict[name] = b.text 60 except IOError, e: 61 pass 62 return modules_dict 63 64users = None 65users_range = None 66def get_all_users_info(): 67 global users 68 global users_range 69 if users and users_range: 70 return users, users_range 71 72 users = [] 73 users_range ={} 74 allusers = [] 75 allusers_info = info(USER) 76 77 for d in allusers_info: 78 allusers.append(d['name']) 79 users_range[d['name'].split("_")[0]] = d['range'] 80 81 for u in allusers: 82 if u not in [ "system_u", "root", "unconfined_u" ]: 83 users.append(u.replace("_u","")) 84 users.sort() 85 return users, users_range 86 87all_entrypoints = None 88def get_entrypoints(): 89 global all_entrypoints 90 if not all_entrypoints: 91 all_entrypoints = sepolicy.info(sepolicy.ATTRIBUTE,"entry_type")[0]["types"] 92 return all_entrypoints 93 94domains = None 95def gen_domains(): 96 global domains 97 if domains: 98 return domains 99 domains = [] 100 for d in get_all_domains(): 101 found = False 102 domain = d[:-2] 103# if domain + "_exec_t" not in get_entrypoints(): 104# continue 105 if domain in domains: 106 continue 107 domains.append(domain) 108 109 for role in get_all_roles(): 110 if role[:-2] in domains or role == "system_r": 111 continue 112 domains.append(role[:-2]) 113 114 domains.sort() 115 return domains 116 117types = None 118def _gen_types(): 119 global types 120 if types: 121 return types 122 all_types = sepolicy.info(sepolicy.TYPE) 123 types = {} 124 for rec in all_types: 125 try: 126 types[rec["name"]] = rec["attributes"] 127 except: 128 types[rec["name"]] = [] 129 return types 130 131def prettyprint(f,trim): 132 return " ".join(f[:-len(trim)].split("_")) 133 134# for HTML man pages 135manpage_domains = [] 136manpage_roles = [] 137 138fedora_releases = ["Fedora17","Fedora18"] 139rhel_releases = ["RHEL6","RHEL7"] 140 141def get_alphabet_manpages(manpage_list): 142 alphabet_manpages = dict.fromkeys(string.ascii_letters, []) 143 for i in string.ascii_letters: 144 temp = [] 145 for j in manpage_list: 146 if j.split("/")[-1][0] == i: 147 temp.append(j.split("/")[-1]) 148 149 alphabet_manpages[i] = temp 150 151 return alphabet_manpages 152 153def convert_manpage_to_html(html_manpage,manpage): 154 rc, output = commands.getstatusoutput("/usr/bin/groff -man -Thtml %s 2>/dev/null" % manpage) 155 if rc == 0: 156 print html_manpage, " has been created" 157 fd = open(html_manpage,'w') 158 fd.write(output) 159 fd.close() 160 161class HTMLManPages: 162 """ 163 Generate a HHTML Manpages on an given SELinux domains 164 """ 165 166 def __init__(self, manpage_roles, manpage_domains, path, os_version): 167 self.manpage_roles = get_alphabet_manpages(manpage_roles) 168 self.manpage_domains = get_alphabet_manpages(manpage_domains) 169 self.os_version = os_version 170 self.old_path = path + "/" 171 self.new_path = self.old_path + self.os_version+"/" 172 173 if self.os_version in fedora_releases or rhel_releases: 174 self.__gen_html_manpages() 175 else: 176 print("SELinux HTML man pages can not be generated for this %s" % os_version) 177 exit(1) 178 179 def __gen_html_manpages(self): 180 self._write_html_manpage() 181 self._gen_index() 182 self._gen_body() 183 self._gen_css() 184 185 def _write_html_manpage(self): 186 if not os.path.isdir(self.new_path): 187 os.mkdir(self.new_path) 188 189 for domain in self.manpage_domains.values(): 190 if len(domain): 191 for d in domain: 192 convert_manpage_to_html((self.new_path+d.split("_selinux")[0]+".html"),self.old_path+d) 193 194 for role in self.manpage_roles.values(): 195 if len(role): 196 for r in role: 197 convert_manpage_to_html((self.new_path+r.split("_selinux")[0]+".html"),self.old_path+r) 198 199 200 def _gen_index(self): 201 index = self.old_path+"index.html" 202 fd = open(index,'w') 203 fd.write(""" 204<html> 205<head> 206 <link rel=stylesheet type="text/css" href="style.css" title="style"> 207 <title>SELinux man pages online</title> 208</head> 209<body> 210<h1>SELinux man pages</h1> 211<br></br> 212Fedora or Red Hat Enterprise Linux Man Pages.</h2> 213<br></br> 214<hr> 215<h3>Fedora</h3> 216<table><tr> 217<td valign="middle"> 218</td> 219</tr></table> 220<pre> 221""") 222 for f in fedora_releases: 223 fd.write(""" 224<a href=%s/%s.html>%s</a> - SELinux man pages for %s """ % (f,f,f,f)) 225 226 fd.write(""" 227</pre> 228<hr> 229<h3>RHEL</h3> 230<table><tr> 231<td valign="middle"> 232</td> 233</tr></table> 234<pre> 235""") 236 for r in rhel_releases: 237 fd.write(""" 238<a href=%s/%s.html>%s</a> - SELinux man pages for %s """ % (r,r,r,r)) 239 240 fd.write(""" 241</pre> 242 """) 243 fd.close() 244 print("%s has been created") % index 245 246 def _gen_body(self): 247 html = self.new_path+self.os_version+".html" 248 fd = open(html,'w') 249 fd.write(""" 250<html> 251<head> 252 <link rel=stylesheet type="text/css" href="../style.css" title="style"> 253 <title>Linux man-pages online for Fedora18</title> 254</head> 255<body> 256<h1>SELinux man pages for Fedora18</h1> 257<hr> 258<table><tr> 259<td valign="middle"> 260<h3>SELinux roles</h3> 261""") 262 for letter in self.manpage_roles: 263 if len(self.manpage_roles[letter]): 264 fd.write(""" 265<a href=#%s_role>%s</a>""" 266 % (letter,letter)) 267 268 fd.write(""" 269</td> 270</tr></table> 271<pre> 272""") 273 rolename_body = "" 274 for letter in self.manpage_roles: 275 if len(self.manpage_roles[letter]): 276 rolename_body += "<p>" 277 for r in self.manpage_roles[letter]: 278 rolename = r.split("_selinux")[0] 279 rolename_body += "<a name=%s_role></a><a href=%s.html>%s_selinux(8)</a> - Security Enhanced Linux Policy for the %s SELinux user\n" % (letter,rolename,rolename,rolename) 280 281 fd.write("""%s 282</pre> 283<hr> 284<table><tr> 285<td valign="middle"> 286<h3>SELinux domains</h3>""" 287% rolename_body) 288 289 for letter in self.manpage_domains: 290 if len(self.manpage_domains[letter]): 291 fd.write(""" 292<a href=#%s_domain>%s</a> 293 """ % (letter,letter)) 294 295 fd.write(""" 296</td> 297</tr></table> 298<pre> 299""") 300 domainname_body = "" 301 for letter in self.manpage_domains: 302 if len(self.manpage_domains[letter]): 303 domainname_body += "<p>" 304 for r in self.manpage_domains[letter]: 305 domainname = r.split("_selinux")[0] 306 domainname_body += "<a name=%s_domain></a><a href=%s.html>%s_selinux(8)</a> - Security Enhanced Linux Policy for the %s SELinux processes\n" % (letter,domainname,domainname,domainname) 307 308 fd.write("""%s 309</pre> 310</body> 311</html> 312""" % domainname_body) 313 314 fd.close() 315 print("%s has been created") % html 316 317 def _gen_css(self): 318 style_css = self.old_path+"style.css" 319 fd = open(style_css,'w') 320 fd.write(""" 321html, body { 322 background-color: #fcfcfc; 323 font-family: arial, sans-serif; 324 font-size: 110%; 325 color: #333; 326} 327 328h1, h2, h3, h4, h5, h5 { 329 color: #2d7c0b; 330 font-family: arial, sans-serif; 331 margin-top: 25px; 332} 333 334a { 335 color: #336699; 336 text-decoration: none; 337} 338 339a:visited { 340 color: #4488bb; 341} 342 343a:hover, a:focus, a:active { 344 color: #07488A; 345 text-decoration: none; 346} 347 348a.func { 349 color: red; 350 text-decoration: none; 351} 352a.file { 353 color: red; 354 text-decoration: none; 355} 356 357pre.code { 358 background-color: #f4f0f4; 359// font-family: monospace, courier; 360 font-size: 110%; 361 margin-left: 0px; 362 margin-right: 60px; 363 padding-top: 5px; 364 padding-bottom: 5px; 365 padding-left: 8px; 366 padding-right: 8px; 367 border: 1px solid #AADDAA; 368} 369 370.url { 371 font-family: serif; 372 font-style: italic; 373 color: #440064; 374} 375""") 376 377 fd.close() 378 print("%s has been created") % style_css 379 380class ManPage: 381 """ 382 Generate a Manpage on an SELinux domain in the specified path 383 """ 384 modules_dict = None 385 enabled_str = ["Disabled", "Enabled"] 386 387 def __init__(self, domainname, path = "/tmp", root="/", source_files = False ,html = False): 388 self.html = html 389 self.source_files = source_files 390 self.root = root 391 self.portrecs = gen_port_dict()[0] 392 self.domains = gen_domains() 393 self.all_domains = get_all_domains() 394 self.all_attributes = get_all_attributes() 395 self.all_bools = get_all_bools() 396 self.all_port_types = get_all_port_types() 397 self.all_roles = get_all_roles() 398 self.all_users = get_all_users_info()[0] 399 self.all_users_range = get_all_users_info()[1] 400 self.all_file_types = get_all_file_types() 401 self.role_allows = get_all_role_allows() 402 self.types = _gen_types() 403 404 if self.source_files: 405 self.fcpath = self.root + "file_contexts" 406 else: 407 self.fcpath = self.root + selinux.selinux_file_context_path() 408 409 self.fcdict = get_fcdict(self.fcpath) 410 411 if not os.path.exists(path): 412 os.makedirs(path) 413 414 self.path = path 415 416 if self.source_files: 417 self.xmlpath = self.root + "policy.xml" 418 else: 419 self.xmlpath = self.root + "/usr/share/selinux/devel/policy.xml" 420 self.booleans_dict = gen_bool_dict(self.xmlpath) 421 422 self.domainname, self.short_name = gen_short_name(domainname) 423 424 self.type = self.domainname + "_t" 425 self._gen_bools() 426 self.man_page_path = "%s/%s_selinux.8" % (path, self.domainname) 427 self.fd = open(self.man_page_path, 'w') 428 if self.domainname + "_r" in self.all_roles: 429 self.__gen_user_man_page() 430 if self.html: 431 manpage_roles.append(self.man_page_path) 432 else: 433 if self.html: 434 manpage_domains.append(self.man_page_path) 435 self.__gen_man_page() 436 self.fd.close() 437 438 for k in equiv_dict.keys(): 439 if k == self.domainname: 440 for alias in equiv_dict[k]: 441 self.__gen_man_page_link(alias) 442 443 def _gen_bools(self): 444 self.bools=[] 445 self.domainbools=[] 446 types = [self.type] 447 if self.domainname in equiv_dict: 448 for t in equiv_dict[self.domainname]: 449 if t + "_t" in self.all_domains: 450 types.append(t+"_t") 451 452 for t in types: 453 domainbools, bools = get_bools(t) 454 self.bools += bools 455 self.domainbools += domainbools 456 457 self.bools.sort() 458 self.domainbools.sort() 459 460 def get_man_page_path(self): 461 return self.man_page_path 462 463 def __gen_user_man_page(self): 464 self.role = self.domainname + "_r" 465 if not self.modules_dict: 466 self.modules_dict = gen_modules_dict(self.xmlpath) 467 468 try: 469 self.desc = self.modules_dict[self.domainname] 470 except: 471 self.desc = "%s user role" % self.domainname 472 473 if self.domainname in self.all_users: 474 self.attributes = sepolicy.info(sepolicy.TYPE,(self.type))[0]["attributes"] 475 self._user_header() 476 self._user_attribute() 477 self._can_sudo() 478 self._xwindows_login() 479 # until a new policy build with login_userdomain attribute 480 #self.terminal_login() 481 self._network() 482 self._booleans() 483 self._home_exec() 484 self._transitions() 485 else: 486 self._role_header() 487 self._booleans() 488 489 self._port_types() 490 self._writes() 491 self._footer() 492 493 def __gen_man_page_link(self, alias): 494 path = "%s/%s_selinux.8" % (self.path, alias) 495 self.fd = open("%s/%s_selinux.8" % (self.path, alias), 'w') 496 self.fd.write(".so man8/%s_selinux.8" % self.domainname) 497 self.fd.close() 498 print path 499 500 def __gen_man_page(self): 501 self.anon_list = [] 502 503 self.attributes = {} 504 self.ptypes = [] 505 self._get_ptypes() 506 507 for domain_type in self.ptypes: 508 self.attributes[domain_type] = sepolicy.info(sepolicy.TYPE,("%s") % domain_type)[0]["attributes"] 509 510 self._header() 511 self._entrypoints() 512 self._process_types() 513 self._booleans() 514 self._nsswitch_domain() 515 self._port_types() 516 self._writes() 517 self._file_context() 518 self._public_content() 519 self._footer() 520 521 def _get_ptypes(self): 522 for f in self.all_domains: 523 if f.startswith(self.short_name) or f.startswith(self.domainname): 524 self.ptypes.append(f) 525 526 def _header(self): 527 self.fd.write('.TH "%(domainname)s_selinux" "8" "%(date)s" "%(domainname)s" "SELinux Policy %(domainname)s"' 528 % {'domainname':self.domainname, 'date': time.strftime("%y-%m-%d")}) 529 self.fd.write(r""" 530.SH "NAME" 531%(domainname)s_selinux \- Security Enhanced Linux Policy for the %(domainname)s processes 532.SH "DESCRIPTION" 533 534Security-Enhanced Linux secures the %(domainname)s processes via flexible mandatory access control. 535 536The %(domainname)s processes execute with the %(domainname)s_t SELinux type. You can check if you have these processes running by executing the \fBps\fP command with the \fB\-Z\fP qualifier. 537 538For example: 539 540.B ps -eZ | grep %(domainname)s_t 541 542""" % {'domainname':self.domainname}) 543 544 545 def _format_boolean_desc(self, b): 546 desc = self.booleans_dict[b][2][0].lower() + self.booleans_dict[b][2][1:] 547 if desc[-1] == ".": 548 desc = desc[:-1] 549 return desc 550 551 def _gen_bool_text(self): 552 booltext = "" 553 for b, enabled in self.domainbools + self.bools: 554 if b.endswith("anon_write") and b not in self.anon_list: 555 self.anon_list.append(b) 556 else: 557 if b not in self.booleans_dict: 558 continue 559 booltext += """ 560.PP 561If you want to %s, you must turn on the %s boolean. %s by default. 562 563.EX 564.B setsebool -P %s 1 565 566.EE 567""" % (self._format_boolean_desc(b), b, self.enabled_str[enabled], b) 568 return booltext 569 570 def _booleans(self): 571 self.booltext = self._gen_bool_text() 572 573 if self.booltext != "": 574 self.fd.write(""" 575.SH BOOLEANS 576SELinux policy is customizable based on least access required. %s policy is extremely flexible and has several booleans that allow you to manipulate the policy and run %s with the tightest access possible. 577 578""" % (self.domainname, self.domainname)) 579 580 self.fd.write(self.booltext) 581 582 def _nsswitch_domain(self): 583 nsswitch_types = [] 584 nsswitch_booleans = ['authlogin_nsswitch_use_ldap', 'kerberos_enabled'] 585 nsswitchbooltext = "" 586 for k in self.attributes.keys(): 587 if "nsswitch_domain" in self.attributes[k]: 588 nsswitch_types.append(k) 589 590 if len(nsswitch_types): 591 self.fd.write(""" 592.SH NSSWITCH DOMAIN 593""") 594 for b in nsswitch_booleans: 595 nsswitchbooltext += """ 596.PP 597If you want to %s for the %s, you must turn on the %s boolean. 598 599.EX 600.B setsebool -P %s 1 601.EE 602""" % (self._format_boolean_desc(b),(", ".join(nsswitch_types)), b, b) 603 604 self.fd.write(nsswitchbooltext) 605 606 def _process_types(self): 607 if len(self.ptypes) == 0: 608 return 609 self.fd.write(r""" 610.SH PROCESS TYPES 611SELinux defines process types (domains) for each process running on the system 612.PP 613You can see the context of a process using the \fB\-Z\fP option to \fBps\bP 614.PP 615Policy governs the access confined processes have to files. 616SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible. 617.PP 618The following process types are defined for %(domainname)s: 619""" % {'domainname':self.domainname}) 620 self.fd.write(""" 621.EX 622.B %s 623.EE""" % ", ".join(self.ptypes)) 624 self.fd.write(""" 625.PP 626Note: 627.B semanage permissive -a %(domainname)s_t 628can be used to make the process type %(domainname)s_t permissive. SELinux does not deny access to permissive process types, but the AVC (SELinux denials) messages are still generated. 629""" % {'domainname':self.domainname}) 630 631 def _port_types(self): 632 self.ports = [] 633 for f in self.all_port_types: 634 if f.startswith(self.short_name) or f.startswith(self.domainname): 635 self.ports.append(f) 636 637 if len(self.ports) == 0: 638 return 639 self.fd.write(""" 640.SH PORT TYPES 641SELinux defines port types to represent TCP and UDP ports. 642.PP 643You can see the types associated with a port by using the following command: 644 645.B semanage port -l 646 647.PP 648Policy governs the access confined processes have to these ports. 649SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible. 650.PP 651The following port types are defined for %(domainname)s:""" % {'domainname':self.domainname}) 652 653 for p in self.ports: 654 self.fd.write(""" 655 656.EX 657.TP 5 658.B %s 659.TP 10 660.EE 661""" % p) 662 once = True 663 for prot in ( "tcp", "udp" ): 664 if (p,prot) in self.portrecs: 665 if once: 666 self.fd.write(""" 667 668Default Defined Ports:""") 669 once = False 670 self.fd.write(r""" 671%s %s 672.EE""" % (prot, ",".join(self.portrecs[(p,prot)]))) 673 674 def _file_context(self): 675 flist=[] 676 mpaths=[] 677 for f in self.all_file_types: 678 if f.startswith(self.domainname): 679 flist.append(f) 680 if f in self.fcdict: 681 mpaths = mpaths + self.fcdict[f]["regex"] 682 if len(mpaths) == 0: 683 return 684 mpaths.sort() 685 mdirs={} 686 for mp in mpaths: 687 found = False 688 for md in mdirs: 689 if mp.startswith(md): 690 mdirs[md].append(mp) 691 found = True 692 break 693 if not found: 694 for e in equiv_dirs: 695 if mp.startswith(e) and mp.endswith('(/.*)?'): 696 mdirs[mp[:-6]] = [] 697 break 698 699 equiv = [] 700 for m in mdirs: 701 if len(mdirs[m]) > 0: 702 equiv.append(m) 703 704 self.fd.write(r""" 705.SH FILE CONTEXTS 706SELinux requires files to have an extended attribute to define the file type. 707.PP 708You can see the context of a file using the \fB\-Z\fP option to \fBls\bP 709.PP 710Policy governs the access confined processes have to these files. 711SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible. 712.PP 713""" % {'domainname':self.domainname}) 714 715 if len(equiv) > 0: 716 self.fd.write(r""" 717.PP 718.B EQUIVALENCE DIRECTORIES 719""") 720 for e in equiv: 721 self.fd.write(r""" 722.PP 723%(domainname)s policy stores data with multiple different file context types under the %(equiv)s directory. If you would like to store the data in a different directory you can use the semanage command to create an equivalence mapping. If you wanted to store this data under the /srv dirctory you would execute the following command: 724.PP 725.B semanage fcontext -a -e %(equiv)s /srv/%(alt)s 726.br 727.B restorecon -R -v /srv/%(alt)s 728.PP 729""" % {'domainname':self.domainname, 'equiv': e, 'alt': e.split('/')[-1] }) 730 731 self.fd.write(r""" 732.PP 733.B STANDARD FILE CONTEXT 734 735SELinux defines the file context types for the %(domainname)s, if you wanted to 736store files with these types in a diffent paths, you need to execute the semanage command to sepecify alternate labeling and then use restorecon to put the labels on disk. 737 738.B semanage fcontext -a -t %(type)s '/srv/%(domainname)s/content(/.*)?' 739.br 740.B restorecon -R -v /srv/my%(domainname)s_content 741 742Note: SELinux often uses regular expressions to specify labels that match multiple files. 743""" % {'domainname':self.domainname, "type":flist[0] }) 744 745 self.fd.write(r""" 746.I The following file types are defined for %(domainname)s: 747""" % {'domainname':self.domainname}) 748 for f in flist: 749 self.fd.write(""" 750 751.EX 752.PP 753.B %s 754.EE 755 756- %s 757""" % ( f, sepolicy.get_description(f))) 758 759 if f in self.fcdict: 760 plural = "" 761 if len(self.fcdict[f]["regex"]) > 1: 762 plural = "s" 763 self.fd.write(""" 764.br 765.TP 5 766Path%s: 767%s""" % (plural, self.fcdict[f]["regex"][0])) 768 for x in self.fcdict[f]["regex"][1:]: 769 self.fd.write(", %s" % x) 770 771 self.fd.write(""" 772 773.PP 774Note: File context can be temporarily modified with the chcon command. If you want to permanently change the file context you need to use the 775.B semanage fcontext 776command. This will modify the SELinux labeling database. You will need to use 777.B restorecon 778to apply the labels. 779""") 780 781 def _see_also(self): 782 ret = "" 783 for d in self.domains: 784 if d == self.domainname: 785 continue 786 if d.startswith(self.short_name): 787 ret += ", %s_selinux(8)" % d 788 if d.startswith(self.domainname + "_"): 789 ret += ", %s_selinux(8)" % d 790 self.fd.write(ret) 791 792 def _public_content(self): 793 if len(self.anon_list) > 0: 794 self.fd.write(""" 795.SH SHARING FILES 796If you want to share files with multiple domains (Apache, FTP, rsync, Samba), you can set a file context of public_content_t and public_content_rw_t. These context allow any of the above domains to read the content. If you want a particular domain to write to the public_content_rw_t domain, you must set the appropriate boolean. 797.TP 798Allow %(domainname)s servers to read the /var/%(domainname)s directory by adding the public_content_t file type to the directory and by restoring the file type. 799.PP 800.B 801semanage fcontext -a -t public_content_t "/var/%(domainname)s(/.*)?" 802.br 803.B restorecon -F -R -v /var/%(domainname)s 804.pp 805.TP 806Allow %(domainname)s servers to read and write /var/%(domainname)s/incoming by adding the public_content_rw_t type to the directory and by restoring the file type. You also need to turn on the %(domainname)s_anon_write boolean. 807.PP 808.B 809semanage fcontext -a -t public_content_rw_t "/var/%(domainname)s/incoming(/.*)?" 810.br 811.B restorecon -F -R -v /var/%(domainname)s/incoming 812.br 813.B setsebool -P %(domainname)s_anon_write 1 814""" % {'domainname':self.domainname}) 815 for b in self.anon_list: 816 desc = self.booleans_dict[b][2][0].lower() + self.booleans_dict[b][2][1:] 817 self.fd.write(""" 818.PP 819If you want to %s, you must turn on the %s boolean. 820 821.EX 822.B setsebool -P %s 1 823.EE 824""" % (desc, b, b)) 825 826 def _footer(self): 827 self.fd.write(""" 828.SH "COMMANDS" 829.B semanage fcontext 830can also be used to manipulate default file context mappings. 831.PP 832.B semanage permissive 833can also be used to manipulate whether or not a process type is permissive. 834.PP 835.B semanage module 836can also be used to enable/disable/install/remove policy modules. 837""") 838 839 if len(self.ports) > 0: 840 self.fd.write(""" 841.B semanage port 842can also be used to manipulate the port definitions 843""") 844 845 if self.booltext != "": 846 self.fd.write(""" 847.B semanage boolean 848can also be used to manipulate the booleans 849""") 850 851 self.fd.write(""" 852.PP 853.B system-config-selinux 854is a GUI tool available to customize SELinux policy settings. 855 856.SH AUTHOR 857This manual page was auto-generated using 858.B "sepolicy manpage". 859 860.SH "SEE ALSO" 861selinux(8), %s(8), semanage(8), restorecon(8), chcon(1), sepolicy(8) 862""" % (self.domainname)) 863 864 if self.booltext != "": 865 self.fd.write(", setsebool(8)") 866 867 self._see_also() 868 869 def _valid_write(self, check, attributes): 870 if check in [ self.type, "domain" ]: 871 return False 872 if check.endswith("_t"): 873 for a in attributes: 874 if a in self.types[check]: 875 return False 876 return True 877 878 def _entrypoints(self): 879 try: 880 entrypoints = map(lambda x: x['target'], sepolicy.search([sepolicy.ALLOW],{'source':self.type, 'permlist':['entrypoint'], 'class':'file'})) 881 except: 882 return 883 884 self.fd.write (""" 885.SH "ENTRYPOINTS" 886""") 887 if len(entrypoints) > 1: 888 entrypoints_str = "\\fB%s\\fP file types" % ", ".join(entrypoints) 889 else: 890 entrypoints_str = "\\fB%s\\fP file type" % entrypoints[0] 891 892 self.fd.write (""" 893The %s_t SELinux type can be entered via the %s. 894 895The default entrypoint paths for the %s_t domain are the following: 896""" % (self.domainname, entrypoints_str, self.domainname)) 897 if "bin_t" in entrypoints: 898 entrypoints.remove("bin_t") 899 self.fd.write (""" 900All executeables with the default executable label, usually stored in /usr/bin and /usr/sbin.""") 901 902 paths=[] 903 for entrypoint in entrypoints: 904 if entrypoint in self.fcdict: 905 paths += self.fcdict[entrypoint]["regex"] 906 907 self.fd.write(""" 908%s""" % ", ".join(paths)) 909 910 def _writes(self): 911 permlist = sepolicy.search([sepolicy.ALLOW],{'source':self.type, 'permlist':['open', 'write'], 'class':'file'}) 912 if permlist == None or len(permlist) == 0: 913 return 914 915 all_writes = [] 916 attributes = ["proc_type", "sysctl_type"] 917 for i in permlist: 918 if not i['target'].endswith("_t"): 919 attributes.append(i['target']) 920 921 for i in permlist: 922 if self._valid_write(i['target'],attributes): 923 if i['target'] not in all_writes: 924 all_writes.append(i['target']) 925 926 if len(all_writes) == 0: 927 return 928 self.fd.write (""" 929.SH "MANAGED FILES" 930""") 931 self.fd.write (""" 932The SELinux process type %s_t can manage files labeled with the following file types. The paths listed are the default paths for these file types. Note the processes UID still need to have DAC permissions. 933""" % self.domainname) 934 935 all_writes.sort() 936 if "file_type" in all_writes: 937 all_writes = [ "file_type" ] 938 for f in all_writes: 939 self.fd.write(""" 940.br 941.B %s 942 943""" % f) 944 if f in self.fcdict: 945 for path in self.fcdict[f]["regex"]: 946 self.fd.write("""\t%s 947.br 948""" % path) 949 950 def _get_users_range(self): 951 if self.domainname in self.all_users_range: 952 return self.all_users_range[self.domainname] 953 return "s0" 954 955 def _user_header(self): 956 self.fd.write('.TH "%(type)s_selinux" "8" "%(type)s" "mgrepl@redhat.com" "%(type)s SELinux Policy documentation"' 957 % {'type':self.domainname}) 958 959 self.fd.write(r""" 960.SH "NAME" 961%(user)s_u \- \fB%(desc)s\fP - Security Enhanced Linux Policy 962 963.SH DESCRIPTION 964 965\fB%(user)s_u\fP is an SELinux User defined in the SELinux 966policy. SELinux users have default roles, \fB%(user)s_r\fP. The 967default role has a default type, \fB%(user)s_t\fP, associated with it. 968 969The SELinux user will usually login to a system with a context that looks like: 970 971.B %(user)s_u:%(user)s_r:%(user)s_t:%(range)s 972 973Linux users are automatically assigned an SELinux users at login. 974Login programs use the SELinux User to assign initial context to the user's shell. 975 976SELinux policy uses the context to control the user's access. 977 978By default all users are assigned to the SELinux user via the \fB__default__\fP flag 979 980On Targeted policy systems the \fB__default__\fP user is assigned to the \fBunconfined_u\fP SELinux user. 981 982You can list all Linux User to SELinux user mapping using: 983 984.B semanage login -l 985 986If you wanted to change the default user mapping to use the %(user)s_u user, you would execute: 987 988.B semanage login -m -s %(user)s_u __default__ 989 990""" % {'desc': self.desc, 'type':self.type, 'user':self.domainname,'range':self._get_users_range()}) 991 992 if "login_userdomain" in self.attributes and "login_userdomain" in self.all_attributes: 993 self.fd.write(""" 994If you want to map the one Linux user (joe) to the SELinux user %(user)s, you would execute: 995 996.B $ semanage login -a -s %(user)s_u joe 997 998""" % {'user':self.domainname}) 999 1000 def _can_sudo(self): 1001 sudotype = "%s_sudo_t" % self.domainname 1002 self.fd.write(""" 1003.SH SUDO 1004""") 1005 if sudotype in self.types: 1006 role = self.domainname + "_r" 1007 self.fd.write(""" 1008The SELinux user %(user)s can execute sudo. 1009 1010You can set up sudo to allow %(user)s to transition to an administrative domain: 1011 1012Add one or more of the following record to sudoers using visudo. 1013 1014""" % { 'user':self.domainname } ) 1015 for adminrole in self.role_allows[role]: 1016 self.fd.write(""" 1017USERNAME ALL=(ALL) ROLE=%(admin)s_r TYPE=%(admin)s_t COMMAND 1018.br 1019sudo will run COMMAND as %(user)s_u:%(admin)s_r:%(admin)s_t:LEVEL 1020""" % {'admin':adminrole[:-2], 'user':self.domainname } ) 1021 1022 self.fd.write(""" 1023You might also need to add one or more of these new roles to your SELinux user record. 1024 1025List the SELinux roles your SELinux user can reach by executing: 1026 1027.B $ semanage user -l |grep selinux_name 1028 1029Modify the roles list and add %(user)s_r to this list. 1030 1031.B $ semanage user -m -R '%(roles)s' %(user)s_u 1032 1033For more details you can see semanage man page. 1034 1035""" % {'user':self.domainname, "roles": " ".join([role] + self.role_allows[role]) } ) 1036 else: 1037 self.fd.write(""" 1038The SELinux type %s_t is not allowed to execute sudo. 1039""" % self.domainname) 1040 1041 def _user_attribute(self): 1042 self.fd.write(""" 1043.SH USER DESCRIPTION 1044""") 1045 if "unconfined_usertype" in self.attributes: 1046 self.fd.write(""" 1047The SELinux user %s_u is an unconfined user. It means that a mapped Linux user to this SELinux user is supposed to be allow all actions. 1048""" % self.domainname) 1049 1050 if "unpriv_userdomain" in self.attributes: 1051 self.fd.write(""" 1052The SELinux user %s_u is defined in policy as a unprivileged user. SELinux prevents unprivileged users from doing administration tasks without transitioning to a different role. 1053""" % self.domainname) 1054 1055 if "admindomain" in self.attributes: 1056 self.fd.write(""" 1057The SELinux user %s_u is an admin user. It means that a mapped Linux user to this SELinux user is intended for administrative actions. Usually this is assigned to a root Linux user. 1058""" % self.domainname) 1059 1060 def _xwindows_login(self): 1061 if "x_domain" in self.all_attributes: 1062 self.fd.write(""" 1063.SH X WINDOWS LOGIN 1064""") 1065 if "x_domain" in self.attributes: 1066 self.fd.write(""" 1067The SELinux user %s_u is able to X Windows login. 1068""" % self.domainname) 1069 else: 1070 self.fd.write(""" 1071The SELinux user %s_u is not able to X Windows login. 1072""" % self.domainname) 1073 1074 def _terminal_login(self): 1075 if "login_userdomain" in self.all_attributes: 1076 self.fd.write(""" 1077.SH TERMINAL LOGIN 1078""") 1079 if "login_userdomain" in self.attributes: 1080 self.fd.write(""" 1081The SELinux user %s_u is able to terminal login. 1082""" % self.domainname) 1083 else: 1084 self.fd.write(""" 1085The SELinux user %s_u is not able to terminal login. 1086""" % self.domainname) 1087 1088 def _network(self): 1089 from sepolicy import network 1090 self.fd.write(""" 1091.SH NETWORK 1092""") 1093 for net in ("tcp", "udp"): 1094 portdict = network.get_network_connect(self.type, net, "name_bind") 1095 if len(portdict) > 0: 1096 self.fd.write(""" 1097.TP 1098The SELinux user %s_u is able to listen on the following %s ports. 1099""" % (self.domainname, net)) 1100 for p in portdict: 1101 for t, ports in portdict[p]: 1102 self.fd.write(""" 1103.B %s 1104""" % ",".join(ports)) 1105 portdict = network.get_network_connect(self.type, "tcp", "name_connect") 1106 if len(portdict) > 0: 1107 self.fd.write(""" 1108.TP 1109The SELinux user %s_u is able to connect to the following tcp ports. 1110""" % (self.domainname)) 1111 for p in portdict: 1112 for t, ports in portdict[p]: 1113 self.fd.write(""" 1114.B %s 1115""" % ",".join(ports)) 1116 1117 def _home_exec(self): 1118 permlist = sepolicy.search([sepolicy.ALLOW],{'source':self.type,'target':'user_home_type', 'class':'file', 'permlist':['ioctl', 'read', 'getattr', 'execute', 'execute_no_trans', 'open']}) 1119 self.fd.write(""" 1120.SH HOME_EXEC 1121""" ) 1122 if permlist is not None: 1123 self.fd.write(""" 1124The SELinux user %s_u is able execute home content files. 1125""" % self.domainname) 1126 1127 else: 1128 self.fd.write(""" 1129The SELinux user %s_u is not able execute home content files. 1130""" % self.domainname) 1131 1132 def _transitions(self): 1133 self.fd.write(r""" 1134.SH TRANSITIONS 1135 1136Three things can happen when %(type)s attempts to execute a program. 1137 1138\fB1.\fP SELinux Policy can deny %(type)s from executing the program. 1139 1140.TP 1141 1142\fB2.\fP SELinux Policy can allow %(type)s to execute the program in the current user type. 1143 1144Execute the following to see the types that the SELinux user %(type)s can execute without transitioning: 1145 1146.B search -A -s %(type)s -c file -p execute_no_trans 1147 1148.TP 1149 1150\fB3.\fP SELinux can allow %(type)s to execute the program and transition to a new type. 1151 1152Execute the following to see the types that the SELinux user %(type)s can execute and transition: 1153 1154.B $ search -A -s %(type)s -c process -p transition 1155 1156""" % {'user':self.domainname, 'type':self.type}) 1157 1158 def _role_header(self): 1159 self.fd.write('.TH "%(user)s_selinux" "8" "%(user)s" "mgrepl@redhat.com" "%(user)s SELinux Policy documentation"' 1160 % {'user':self.domainname}) 1161 1162 self.fd.write(r""" 1163.SH "NAME" 1164%(user)s_r \- \fB%(desc)s\fP - Security Enhanced Linux Policy 1165 1166.SH DESCRIPTION 1167 1168SELinux supports Roles Based Access Control (RBAC), some Linux roles are login roles, while other roles need to be transition into. 1169 1170.I Note: 1171Examples in this man page will use the 1172.B staff_u 1173SELinux user. 1174 1175Non login roles are usually used for administrative tasks. For example, tasks that require root privileges. Roles control which types a user can run processes with. Roles often have default types assigned to them. 1176 1177The default type for the %(user)s_r role is %(user)s_t. 1178 1179The 1180.B newrole 1181program to transition directly to this role. 1182 1183.B newrole -r %(user)s_r -t %(user)s_t 1184 1185.B sudo 1186is the preferred method to do transition from one role to another. You setup sudo to transition to %(user)s_r by adding a similar line to the /etc/sudoers file. 1187 1188USERNAME ALL=(ALL) ROLE=%(user)s_r TYPE=%(user)s_t COMMAND 1189 1190.br 1191sudo will run COMMAND as staff_u:%(user)s_r:%(user)s_t:LEVEL 1192 1193When using a a non login role, you need to setup SELinux so that your SELinux user can reach %(user)s_r role. 1194 1195Execute the following to see all of the assigned SELinux roles: 1196 1197.B semanage user -l 1198 1199You need to add %(user)s_r to the staff_u user. You could setup the staff_u user to be able to use the %(user)s_r role with a command like: 1200 1201.B $ semanage user -m -R 'staff_r system_r %(user)s_r' staff_u 1202 1203""" % {'desc': self.desc, 'user':self.domainname}) 1204 troles = [] 1205 for i in self.role_allows: 1206 if self.domainname +"_r" in self.role_allows[i]: 1207 troles.append(i) 1208 if len(troles) > 0: 1209 plural = "" 1210 if len(troles) > 1: 1211 plural = "s" 1212 1213 self.fd.write(""" 1214 1215SELinux policy also controls which roles can transition to a different role. 1216You can list these rules using the following command. 1217 1218.B search --role_allow 1219 1220SELinux policy allows the %s role%s can transition to the %s_r role. 1221 1222""" % (", ".join(troles), plural, self.domainname)) 1223