Home | History | Annotate | Download | only in sepolicy
      1 #! /usr/bin/python -Es
      2 # Copyright (C) 2012-2013 Red Hat
      3 # AUTHOR: Dan Walsh <dwalsh (at] redhat.com>
      4 # AUTHOR: Miroslav Grepl <mgrepl (at] 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 
     27 import string
     28 import argparse
     29 import selinux
     30 import sepolicy
     31 from sepolicy import *
     32 
     33 import commands
     34 import sys, os, re, time
     35 
     36 equiv_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 
     38 equiv_dirs=[ "/var" ]
     39 modules_dict = None
     40 def 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 
     64 users = None
     65 users_range = None
     66 def 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 
     87 all_entrypoints = None
     88 def 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 
     94 domains = None
     95 def 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 
    117 types = None
    118 def _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 
    131 def prettyprint(f,trim):
    132     return " ".join(f[:-len(trim)].split("_"))
    133 
    134 # for HTML man pages
    135 manpage_domains = []
    136 manpage_roles = []
    137 
    138 fedora_releases = ["Fedora17","Fedora18"]
    139 rhel_releases = ["RHEL6","RHEL7"]
    140 
    141 def 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 
    153 def 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 
    161 class 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>
    212 Fedora 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("""
    321 html, body {
    322     background-color: #fcfcfc;
    323     font-family: arial, sans-serif;
    324     font-size: 110%;
    325     color: #333;
    326 }
    327 
    328 h1, h2, h3, h4, h5, h5 {
    329 	color: #2d7c0b;
    330 	font-family: arial, sans-serif;
    331 	margin-top: 25px;
    332 }
    333 
    334 a {
    335     color: #336699;
    336     text-decoration: none;
    337 }
    338 
    339 a:visited {
    340     color: #4488bb;
    341 }
    342 
    343 a:hover, a:focus, a:active {
    344     color: #07488A;
    345     text-decoration: none;
    346 }
    347 
    348 a.func {
    349     color: red;
    350     text-decoration: none;
    351 }
    352 a.file {
    353     color: red;
    354     text-decoration: none;
    355 }
    356 
    357 pre.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 
    380 class 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 
    534 Security-Enhanced Linux secures the %(domainname)s processes via flexible mandatory access control.
    535 
    536 The %(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 
    538 For 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
    561 If 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
    576 SELinux 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
    597 If 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
    611 SELinux defines process types (domains) for each process running on the system
    612 .PP
    613 You can see the context of a process using the \fB\-Z\fP option to \fBps\bP
    614 .PP
    615 Policy governs the access confined processes have to files.
    616 SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
    617 .PP
    618 The 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
    626 Note:
    627 .B semanage permissive -a %(domainname)s_t
    628 can 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
    641 SELinux defines port types to represent TCP and UDP ports.
    642 .PP
    643 You can see the types associated with a port by using the following command:
    644 
    645 .B semanage port -l
    646 
    647 .PP
    648 Policy governs the access confined processes have to these ports.
    649 SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
    650 .PP
    651 The 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 
    668 Default 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
    706 SELinux requires files to have an extended attribute to define the file type.
    707 .PP
    708 You can see the context of a file using the \fB\-Z\fP option to \fBls\bP
    709 .PP
    710 Policy governs the access confined processes have to these files.
    711 SELinux %(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 
    735 SELinux defines the file context types for the %(domainname)s, if you wanted to
    736 store 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 
    742 Note: 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
    766 Path%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
    774 Note: 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
    776 command.  This will modify the SELinux labeling database.  You will need to use
    777 .B restorecon
    778 to 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
    796 If 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
    798 Allow %(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
    801 semanage fcontext -a -t public_content_t "/var/%(domainname)s(/.*)?"
    802 .br
    803 .B restorecon -F -R -v /var/%(domainname)s
    804 .pp
    805 .TP
    806 Allow %(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
    809 semanage 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
    819 If 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
    830 can also be used to manipulate default file context mappings.
    831 .PP
    832 .B semanage permissive
    833 can also be used to manipulate whether or not a process type is permissive.
    834 .PP
    835 .B semanage module
    836 can 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
    842 can also be used to manipulate the port definitions
    843 """)
    844 
    845 	if self.booltext != "":
    846 	    self.fd.write("""
    847 .B semanage boolean
    848 can also be used to manipulate the booleans
    849 """)
    850 
    851 	self.fd.write("""
    852 .PP
    853 .B system-config-selinux
    854 is a GUI tool available to customize SELinux policy settings.
    855 
    856 .SH AUTHOR
    857 This manual page was auto-generated using
    858 .B "sepolicy manpage".
    859 
    860 .SH "SEE ALSO"
    861 selinux(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 ("""
    893 The %s_t SELinux type can be entered via the %s.
    894 
    895 The 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 ("""
    900 All 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 ("""
    932 The 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 (at] 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
    966 policy. SELinux users have default roles, \fB%(user)s_r\fP.  The
    967 default role has a default type, \fB%(user)s_t\fP, associated with it.
    968 
    969 The 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 
    973 Linux users are automatically assigned an SELinux users at login.
    974 Login programs use the SELinux User to assign initial context to the user's shell.
    975 
    976 SELinux policy uses the context to control the user's access.
    977 
    978 By default all users are assigned to the SELinux user via the \fB__default__\fP flag
    979 
    980 On Targeted policy systems the \fB__default__\fP user is assigned to the \fBunconfined_u\fP SELinux user.
    981 
    982 You can list all Linux User to SELinux user mapping using:
    983 
    984 .B semanage login -l
    985 
    986 If 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("""
    994 If 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("""
   1008 The SELinux user %(user)s can execute sudo.
   1009 
   1010 You can set up sudo to allow %(user)s to transition to an administrative domain:
   1011 
   1012 Add 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("""
   1017 USERNAME ALL=(ALL) ROLE=%(admin)s_r TYPE=%(admin)s_t COMMAND
   1018 .br
   1019 sudo 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("""
   1023 You might also need to add one or more of these new roles to your SELinux user record.
   1024 
   1025 List the SELinux roles your SELinux user can reach by executing:
   1026 
   1027 .B $ semanage user -l |grep selinux_name
   1028 
   1029 Modify the roles list and add %(user)s_r to this list.
   1030 
   1031 .B $ semanage user -m -R '%(roles)s' %(user)s_u
   1032 
   1033 For 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("""
   1038 The 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("""
   1047 The 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("""
   1052 The 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("""
   1057 The 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("""
   1067 The SELinux user %s_u is able to X Windows login.
   1068 """ % self.domainname)
   1069 	    else:
   1070 		self.fd.write("""
   1071 The 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("""
   1081 The SELinux user %s_u is able to terminal login.
   1082 """ % self.domainname)
   1083 	    else:
   1084 		self.fd.write("""
   1085 The 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
   1098 The 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
   1109 The 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("""
   1124 The SELinux user %s_u is able execute home content files.
   1125 """  % self.domainname)
   1126 
   1127 	else:
   1128 	    self.fd.write("""
   1129 The 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 
   1136 Three 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 
   1144 Execute 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 
   1152 Execute 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 (at] 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 
   1168 SELinux supports Roles Based Access Control (RBAC), some Linux roles are login roles, while other roles need to be transition into.
   1169 
   1170 .I Note:
   1171 Examples in this man page will use the
   1172 .B staff_u
   1173 SELinux user.
   1174 
   1175 Non 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 
   1177 The default type for the %(user)s_r role is %(user)s_t.
   1178 
   1179 The
   1180 .B newrole
   1181 program to transition directly to this role.
   1182 
   1183 .B newrole -r %(user)s_r -t %(user)s_t
   1184 
   1185 .B sudo
   1186 is 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 
   1188 USERNAME ALL=(ALL) ROLE=%(user)s_r TYPE=%(user)s_t COMMAND
   1189 
   1190 .br
   1191 sudo will run COMMAND as staff_u:%(user)s_r:%(user)s_t:LEVEL
   1192 
   1193 When using a a non login role, you need to setup SELinux so that your SELinux user can reach %(user)s_r role.
   1194 
   1195 Execute the following to see all of the assigned SELinux roles:
   1196 
   1197 .B semanage user -l
   1198 
   1199 You 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 
   1215 SELinux policy also controls which roles can transition to a different role.
   1216 You can list these rules using the following command.
   1217 
   1218 .B search --role_allow
   1219 
   1220 SELinux policy allows the %s role%s can transition to the %s_r role.
   1221 
   1222 """ % (", ".join(troles), plural, self.domainname))
   1223