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