ab5473d
#! /usr/bin/python -Es
ab5473d
# Copyright (C) 2012 Red Hat 
ab5473d
# AUTHOR: Dan Walsh <dwalsh@redhat.com>
ab5473d
# see file 'COPYING' for use and warranty information
ab5473d
#
ab5473d
# semanage is a tool for managing SELinux configuration files
ab5473d
#
ab5473d
#    This program is free software; you can redistribute it and/or
ab5473d
#    modify it under the terms of the GNU General Public License as
ab5473d
#    published by the Free Software Foundation; either version 2 of
ab5473d
#    the License, or (at your option) any later version.
ab5473d
#
ab5473d
#    This program is distributed in the hope that it will be useful,
ab5473d
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
ab5473d
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ab5473d
#    GNU General Public License for more details.
ab5473d
#
ab5473d
#    You should have received a copy of the GNU General Public License
ab5473d
#    along with this program; if not, write to the Free Software
ab5473d
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA     
ab5473d
#                                        02111-1307  USA
ab5473d
#
ab5473d
#  
ab5473d
import senetwork
ab5473d
import seobject
ab5473d
import selinux
ab5473d
import datetime
ab5473d
import setools
ab5473d
import commands
ab5473d
import sys
234c764
import os
ab5473d
ab5473d
import xml.etree.ElementTree
ab5473d
modules_dict = {}
ab5473d
try:
ab5473d
	tree = xml.etree.ElementTree.parse("/usr/share/selinux/devel/policy.xml")
ab5473d
	for l in  tree.findall("layer"):
ab5473d
		for m in  l.findall("module"):
ab5473d
			name = m.get("name")
ab5473d
			if name == "user" or name == "unconfined":
ab5473d
				continue
ab5473d
			if name == "unprivuser":
ab5473d
				name = "user"
ab5473d
			if name == "unconfineduser":
ab5473d
				name = "unconfined"
ab5473d
			for b in  m.findall("summary"):
ab5473d
				modules_dict[name] = b.text
ab5473d
except IOError, e:
ab5473d
	pass
ab5473d
ab5473d
all_attributes = map(lambda x: x['name'], setools.seinfo(setools.ATTRIBUTE))
ab5473d
entrypoints =  setools.seinfo(setools.ATTRIBUTE,"entry_type")[0]["types"]
ab5473d
alldomains =  setools.seinfo(setools.ATTRIBUTE,"domain")[0]["types"]
ab5473d
domains = []
ab5473d
ab5473d
fc_path = selinux.selinux_file_context_path()
ab5473d
ab5473d
fd = open(selinux.selinux_file_context_path(), "r")
ab5473d
fc = fd.readlines()
ab5473d
fd.close()
ab5473d
fd = open(selinux.selinux_file_context_path()+".homedirs", "r")
ab5473d
fc += fd.readlines()
ab5473d
fd.close()
ab5473d
fcdict = {}
ab5473d
for i in fc:
ab5473d
    rec = i.split()
ab5473d
    try:
ab5473d
        t = rec[-1].split(":")[2]
ab5473d
        if t in fcdict:
ab5473d
            fcdict[t].append(rec[0])
ab5473d
        else:
ab5473d
            fcdict[t] = [ rec[0] ]
ab5473d
    except:
ab5473d
        pass
ab5473d
fcdict["logfile"] = [ "all log files" ]
ab5473d
fcdict["user_tmp_type"] = [ "all user tmp files" ]
ab5473d
fcdict["user_home_type"] = [ "all user home files" ]
ab5473d
fcdict["virt_image_type"] = [ "all virtual image files" ]
ab5473d
fcdict["noxattrfs"] = [ "all files on file systems which do not support extended attributes" ]
ab5473d
fcdict["sandbox_tmpfs_type"] = [ "all sandbox content in tmpfs file systems" ]
ab5473d
fcdict["user_tmpfs_type"] = [ "all user content in tmpfs file systems" ]
ab5473d
fcdict["file_type"] = [ "all files on the system" ]
ab5473d
ab5473d
role_allows = {}
ab5473d
for rule in commands.getoutput("sesearch --role_allow").split("\n"):
ab5473d
	role = rule.split()
ab5473d
	if len(role) == 3 and role[0] == "allow" and role[1] != "system_r" and role[2][:-1] != "system_r":
ab5473d
		if role[1] not in role_allows:
ab5473d
			role_allows[role[1]] = [role[2][:-1]]
ab5473d
		else:
ab5473d
			role_allows[role[1]].append(role[2][:-1])
ab5473d
#role_allows = setools.sesearch([setools.ROLE_ALLOW],{'scontext':self.type,'tcontext':'ping_t', 'class':'process'})
ab5473d
roles = []
ab5473d
allroles = map(lambda x: x['name'], setools.seinfo(setools.ROLE))
ab5473d
for r in allroles:
ab5473d
	if r not in [ "system_r", "object_r" ]:
ab5473d
		roles.append(r[:-2])
ab5473d
ab5473d
for d in alldomains:
ab5473d
    found = False
ab5473d
    domain = d[:-2]
ab5473d
    if domain + "_exec_t" not in entrypoints:
ab5473d
        continue
ab5473d
    if domain in domains or name == "pam":
ab5473d
        continue
ab5473d
    domains.append(domain)
ab5473d
ab5473d
for role in roles:
ab5473d
    if role in domains:
ab5473d
        continue
ab5473d
    domains.append(role)
ab5473d
ab5473d
domains.sort()
ab5473d
ab5473d
users = []
ab5473d
allusers = map(lambda x: x['name'], setools.seinfo(setools.USER))
ab5473d
for u in allusers:
ab5473d
	if u not in [ "system_u", "root", "unconfined_u" ]:
ab5473d
		users.append(u.replace("_u",""))
ab5473d
users.sort()
ab5473d
ab5473d
import setools
ab5473d
all_types =  setools.seinfo(setools.TYPE)
ab5473d
types = {}
ab5473d
for rec in all_types:
ab5473d
    try:
ab5473d
        types[rec["name"]] = rec["attributes"]
ab5473d
    except:
ab5473d
        types[rec["name"]] = []
ab5473d
ab5473d
file_types =  setools.seinfo(setools.ATTRIBUTE,"file_type")[0]["types"]
ab5473d
file_types.sort()
ab5473d
ab5473d
port_types =  setools.seinfo(setools.ATTRIBUTE,"port_type")[0]["types"]
ab5473d
port_types.sort()
ab5473d
ab5473d
portrecs = seobject.portRecords().get_all_by_type()
ab5473d
filerecs = seobject.fcontextRecords()
ab5473d
files_dict = {}
ab5473d
fdict = filerecs.get_all()
ab5473d
for i in fdict:
ab5473d
    if fdict[i]:
ab5473d
        if fdict[i][2] in files_dict:
ab5473d
            files_dict[fdict[i][2]].append(i)
ab5473d
        else:
ab5473d
            files_dict[fdict[i][2]] = [i]
ab5473d
boolrecs = seobject.booleanRecords()
ab5473d
bools = seobject.booleans_dict.keys()
ab5473d
ab5473d
man = {}
ab5473d
date = datetime.datetime.now().strftime("%d %b %Y")
ab5473d
def prettyprint(f,trim):
ab5473d
    return " ".join(f[:-len(trim)].split("_"))
ab5473d
ab5473d
class ManPage:
234c764
    def __init__(self, domainname, path, os_version):
ab5473d
        self.domainname = domainname
ab5473d
	self.short_name = domainname
234c764
        self.os_version = os_version
ab5473d
        self.type = self.domainname + "_t"
ab5473d
        self.fd = open("%s/%s_selinux.8" % (path, domainname), 'w')
ab5473d
        if domainname in roles:
ab5473d
            self.__gen_user_man_page()
ab5473d
        else:
ab5473d
            self.__gen_man_page()
ab5473d
        self.fd.close()
ab5473d
ab5473d
    def __gen_user_man_page(self):
ab5473d
        self.role = self.domainname + "_r"
ab5473d
ab5473d
        try:
ab5473d
            self.desc = modules_dict[self.domainname]
ab5473d
        except:
ab5473d
            self.desc = "%s user role" % self.domainname 
ab5473d
            
ab5473d
        if self.domainname in users:
ab5473d
            self.attributes = setools.seinfo(setools.TYPE,(self.type))[0]["attributes"]
ab5473d
            self.user_header()
ab5473d
            self.user_attribute()
ab5473d
            self.can_sudo()
ab5473d
            self.xwindows_login()
ab5473d
            # until a new policy build with login_userdomain attribute
ab5473d
        #self.terminal_login()
ab5473d
            self.network()
ab5473d
            self.booleans()
ab5473d
            self.home_exec()
ab5473d
            self.transitions()
ab5473d
        else:
ab5473d
            self.role_header()
ab5473d
            self.booleans()
ab5473d
ab5473d
        self.port_types()
ab5473d
        self.writes()
ab5473d
        self.footer()
ab5473d
ab5473d
    def __gen_man_page(self):
ab5473d
        if self.domainname[-1]=='d':
ab5473d
            self.short_name = self.domainname[:-1]
ab5473d
ab5473d
        self.anon_list = []
ab5473d
ab5473d
        self.attributes = {}
ab5473d
        self.ptypes = []
ab5473d
        self.get_ptypes()
ab5473d
ab5473d
        for domain_type in self.ptypes:
ab5473d
            self.attributes[domain_type] = setools.seinfo(setools.TYPE,("%s") % domain_type)[0]["attributes"]
ab5473d
ab5473d
        self.header()
ab5473d
        self.booleans()
ab5473d
        self.nsswitch_domain()
ab5473d
        self.public_content()
ab5473d
        self.file_context()
ab5473d
        self.port_types()
ab5473d
        self.process_types()
ab5473d
        self.writes()
ab5473d
        self.footer()
ab5473d
ab5473d
    def get_ptypes(self):
ab5473d
        for f in alldomains:
ab5473d
            if f.startswith(self.short_name):
ab5473d
                self.ptypes.append(f)
ab5473d
ab5473d
    def header(self):
ab5473d
        self.fd.write('.TH  "%(domainname)s_selinux"  "8"  "%(domainname)s" "dwalsh@redhat.com" "%(domainname)s SELinux Policy documentation"'
ab5473d
                 % {'domainname':self.domainname})
ab5473d
        self.fd.write(r"""
ab5473d
.SH "NAME"
ab5473d
%(domainname)s_selinux \- Security Enhanced Linux Policy for the %(domainname)s processes
ab5473d
.SH "DESCRIPTION"
ab5473d
ab5473d
Security-Enhanced Linux secures the %(domainname)s processes via flexible mandatory access
ab5473d
control.  
ab5473d
""" % {'domainname':self.domainname})
ab5473d
ab5473d
ab5473d
    def explain(self, f):
ab5473d
        if f.endswith("_var_run_t"):
ab5473d
            return "store the %s files under the /run directory." % prettyprint(f, "_var_run_t")
ab5473d
        if f.endswith("_pid_t"):
ab5473d
            return "store the %s files under the /run directory." % prettyprint(f, "_pid_t")
ab5473d
        if f.endswith("_var_lib_t"):
ab5473d
            return "store the %s files under the /var/lib directory."  % prettyprint(f, "_var_lib_t")
ab5473d
        if f.endswith("_var_t"):
ab5473d
            return "store the %s files under the /var directory."  % prettyprint(f, "_var_lib_t")
ab5473d
        if f.endswith("_var_spool_t"):
ab5473d
            return "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t")
ab5473d
        if f.endswith("_spool_t"):
ab5473d
            return "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t")
ab5473d
        if f.endswith("_cache_t") or f.endswith("_var_cache_t"):
ab5473d
            return "store the files under the /var/cache directory."
ab5473d
        if f.endswith("_keytab_t"):
ab5473d
            return "treat the files as kerberos keytab files."
ab5473d
        if f.endswith("_lock_t"):
ab5473d
            return "treat the files as %s lock data, stored under the /var/lock directory" % prettyprint(f,"_lock_t")
ab5473d
        if f.endswith("_log_t"):
ab5473d
            return "treat the data as %s log data, usually stored under the /var/log directory." % prettyprint(f,"_log_t")
ab5473d
        if f.endswith("_config_t"):
ab5473d
            return "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f,"_config_t")
ab5473d
        if f.endswith("_conf_t"):
ab5473d
            return "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f,"_conf_t")
ab5473d
        if f.endswith("_exec_t"):
ab5473d
            return "transition an executable to the %s_t domain." % f[:-len("_exec_t")]
ab5473d
        if f.endswith("_cgi_content_t"):
ab5473d
            return "treat the files as %s cgi content." % prettyprint(f, "_cgi_content_t")
ab5473d
        if f.endswith("_rw_content_t"):
ab5473d
            return "treat the files as %s read/write content." % prettyprint(f,"_rw_content_t")
ab5473d
        if f.endswith("_rw_t"):
ab5473d
            return "treat the files as %s read/write content." % prettyprint(f,"_rw_t")
ab5473d
        if f.endswith("_write_t"):
ab5473d
            return "treat the files as %s read/write content." % prettyprint(f,"_write_t")
ab5473d
        if f.endswith("_db_t"):
ab5473d
            return "treat the files as %s database content." % prettyprint(f,"_db_t")
ab5473d
        if f.endswith("_ra_content_t"):
ab5473d
            return "treat the files as %s read/append content." % prettyprint(f,"_ra_conten_t")
ab5473d
        if f.endswith("_cert_t"):
ab5473d
            return "treat the files as %s certificate data." % prettyprint(f,"_cert_t")
ab5473d
        if f.endswith("_key_t"):
ab5473d
            return "treat the files as %s key data." % prettyprint(f,"_key_t")
ab5473d
ab5473d
        if f.endswith("_secret_t"):
ab5473d
            return "treat the files as %s secret data." % prettyprint(f,"_key_t")
ab5473d
ab5473d
        if f.endswith("_ra_t"):
ab5473d
            return "treat the files as %s read/append content." % prettyprint(f,"_ra_t")
ab5473d
ab5473d
        if f.endswith("_ro_t"):
ab5473d
            return "treat the files as %s read/only content." % prettyprint(f,"_ro_t")
ab5473d
ab5473d
        if f.endswith("_modules_t"):
ab5473d
            return "treat the files as %s modules." % prettyprint(f, "_modules_t")
ab5473d
ab5473d
        if f.endswith("_content_t"):
ab5473d
            return "treat the files as %s content." % prettyprint(f, "_content_t")
ab5473d
ab5473d
        if f.endswith("_state_t"):
ab5473d
            return "treat the files as %s state data." % prettyprint(f, "_state_t")
ab5473d
ab5473d
        if f.endswith("_files_t"):
ab5473d
            return "treat the files as %s content." % prettyprint(f, "_files_t")
ab5473d
ab5473d
        if f.endswith("_file_t"):
ab5473d
            return "treat the files as %s content." % prettyprint(f, "_file_t")
ab5473d
ab5473d
        if f.endswith("_data_t"):
ab5473d
            return "treat the files as %s content." % prettyprint(f, "_data_t")
ab5473d
ab5473d
        if f.endswith("_file_t"):
ab5473d
            return "treat the data as %s content." % prettyprint(f, "_file_t")
ab5473d
ab5473d
        if f.endswith("_tmp_t"):
ab5473d
            return "store %s temporary files in the /tmp directories." % prettyprint(f, "_tmp_t")
ab5473d
        if f.endswith("_etc_t"):
ab5473d
            return "store %s files in the /etc directories." % prettyprint(f, "_tmp_t")
ab5473d
        if f.endswith("_home_t"):
ab5473d
            return "store %s files in the users home directory." % prettyprint(f, "_home_t")
ab5473d
        if f.endswith("_tmpfs_t"):
ab5473d
            return "store %s files on a tmpfs file system." % prettyprint(f, "_tmpfs_t")
ab5473d
        if f.endswith("_unit_file_t"):
ab5473d
            return "treat files as a systemd unit file." 
ab5473d
        if f.endswith("_htaccess_t"):
ab5473d
            return "treat the file as a %s access file." % prettyprint(f, "_htaccess_t")
ab5473d
ab5473d
        return "treat the files as %s data." % prettyprint(f,"_t")
ab5473d
ab5473d
    def booleans(self):
ab5473d
        self.booltext = ""
ab5473d
        for b in bools:
ab5473d
            if b.find(self.short_name) >= 0:
ab5473d
                if b.endswith("anon_write"):
ab5473d
                    self.anon_list.append(b)
ab5473d
                else:
ab5473d
                    desc = seobject.booleans_dict[b][2][0].lower() + seobject.booleans_dict[b][2][1:]
ab5473d
                    if desc[-1] == ".":
ab5473d
                        desc = desc[:-1]
ab5473d
                    self.booltext += """
ab5473d
.PP
ab5473d
If you want to %s, you must turn on the %s boolean.
ab5473d
ab5473d
.EX
ab5473d
.B setsebool -P %s 1
ab5473d
.EE
ab5473d
""" % (desc, b, b)
ab5473d
    
ab5473d
        if self.booltext != "":        
ab5473d
            self.fd.write("""
ab5473d
.SH BOOLEANS
ab5473d
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.
ab5473d
ab5473d
""" % (self.domainname, self.domainname))
ab5473d
ab5473d
            self.fd.write(self.booltext)
ab5473d
ab5473d
    def nsswitch_domain(self):
ab5473d
        nsswitch_types = []
ab5473d
        nsswitch_booleans = ['authlogin_nsswitch_use_ldap', 'kerberos_enabled']
ab5473d
        nsswitchbooltext = ""
ab5473d
        if "nsswitch_domain" in all_attributes:
ab5473d
            self.fd.write("""
ab5473d
.SH NSSWITCH DOMAIN
ab5473d
""")
ab5473d
            for k in self.attributes.keys():    
ab5473d
                if "nsswitch_domain" in self.attributes[k]:
ab5473d
                    nsswitch_types.append(k)
ab5473d
ab5473d
            if len(nsswitch_types):
ab5473d
                for i in nsswitch_booleans:
ab5473d
                    desc = seobject.booleans_dict[i][2][0].lower() + seobject.booleans_dict[i][2][1:-1]
ab5473d
                    nsswitchbooltext += """
ab5473d
.PP
ab5473d
If you want to %s for the %s, you must turn on the %s boolean.
ab5473d
ab5473d
.EX
ab5473d
.B setsebool -P %s 1
ab5473d
.EE
ab5473d
""" % (desc,(", ".join(nsswitch_types)), i, i)
ab5473d
ab5473d
        self.fd.write(nsswitchbooltext)
ab5473d
ab5473d
    def process_types(self):
ab5473d
        if len(self.ptypes) == 0:
ab5473d
            return
ab5473d
        self.fd.write(r"""
ab5473d
.SH PROCESS TYPES
ab5473d
SELinux defines process types (domains) for each process running on the system
ab5473d
.PP
ab5473d
You can see the context of a process using the \fB\-Z\fP option to \fBps\bP
ab5473d
.PP
ab5473d
Policy governs the access confined processes have to files. 
ab5473d
SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
ab5473d
.PP 
ab5473d
The following process types are defined for %(domainname)s:
ab5473d
""" % {'domainname':self.domainname})
ab5473d
        self.fd.write("""
ab5473d
.EX
ab5473d
.B %s 
ab5473d
.EE""" % ", ".join(self.ptypes))
ab5473d
        self.fd.write("""
ab5473d
.PP
ab5473d
Note: 
ab5473d
.B semanage permissive -a PROCESS_TYPE 
ab5473d
can be used to make a process type permissive. Permissive process types are not denied access by SELinux. AVC messages will still be generated.
ab5473d
""")
ab5473d
ab5473d
    def port_types(self):
ab5473d
        self.ports = []
ab5473d
        for f in port_types:
ab5473d
            if f.startswith(self.short_name):
ab5473d
                self.ports.append(f)
ab5473d
ab5473d
        if len(self.ports) == 0:
ab5473d
            return
ab5473d
        self.fd.write("""
ab5473d
.SH PORT TYPES
ab5473d
SELinux defines port types to represent TCP and UDP ports. 
ab5473d
.PP
ab5473d
You can see the types associated with a port by using the following command: 
ab5473d
ab5473d
.B semanage port -l
ab5473d
ab5473d
.PP
ab5473d
Policy governs the access confined processes have to these ports. 
ab5473d
SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
ab5473d
.PP 
ab5473d
The following port types are defined for %(domainname)s:""" % {'domainname':self.domainname})
ab5473d
ab5473d
        for p in self.ports:
ab5473d
            self.fd.write("""
ab5473d
ab5473d
.EX
ab5473d
.TP 5
ab5473d
.B %s 
ab5473d
.TP 10
ab5473d
.EE
ab5473d
""" % p)
ab5473d
            once = True
ab5473d
            for prot in ( "tcp", "udp" ):
ab5473d
               if (p,prot) in portrecs:
ab5473d
                    if once:
ab5473d
                        self.fd.write("""
ab5473d
ab5473d
Default Defined Ports:""")
ab5473d
                    once = False
ab5473d
                    self.fd.write(r"""
ab5473d
%s %s
ab5473d
.EE""" % (prot, ",".join(portrecs[(p,prot)])))
ab5473d
ab5473d
    def file_context(self):
ab5473d
        self.fd.write(r"""
ab5473d
.SH FILE CONTEXTS
ab5473d
SELinux requires files to have an extended attribute to define the file type. 
ab5473d
.PP
ab5473d
You can see the context of a file using the \fB\-Z\fP option to \fBls\bP
ab5473d
.PP
ab5473d
Policy governs the access confined processes have to these files. 
ab5473d
SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
ab5473d
.PP 
ab5473d
The following file types are defined for %(domainname)s:
ab5473d
""" % {'domainname':self.domainname})
ab5473d
        for f in file_types:
ab5473d
            if f.startswith(self.domainname):
ab5473d
                self.fd.write("""
ab5473d
ab5473d
.EX
ab5473d
.PP
ab5473d
.B %s 
ab5473d
.EE
ab5473d
ab5473d
- Set files with the %s type, if you want to %s
ab5473d
""" % (f, f, self.explain(f)))
ab5473d
ab5473d
                if f in files_dict:
ab5473d
                    plural = ""
ab5473d
                    if len(files_dict[f]) > 1:
ab5473d
                        plural = "s"
ab5473d
                        self.fd.write("""
ab5473d
.br
ab5473d
.TP 5
ab5473d
Path%s: 
ab5473d
%s""" % (plural, files_dict[f][0][0]))
ab5473d
                        for x in files_dict[f][1:]:
ab5473d
                            self.fd.write(", %s" % x[0])
ab5473d
ab5473d
        self.fd.write("""
ab5473d
ab5473d
.PP
ab5473d
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 
ab5473d
.B semanage fcontext 
ab5473d
command.  This will modify the SELinux labeling database.  You will need to use
ab5473d
.B restorecon
ab5473d
to apply the labels.
ab5473d
""")
ab5473d
ab5473d
    def see_also(self):
ab5473d
	    ret = ""
ab5473d
	    for d in domains:
ab5473d
		    if d == self.domainname:
ab5473d
			    continue
ab5473d
		    if d.startswith(self.short_name):
ab5473d
			    ret += ", %s_selinux(8)" % d
ab5473d
		    if self.domainname.startswith(d):
ab5473d
			    ret += ", %s_selinux(8)" % d
ab5473d
	    self.fd.write(ret)
ab5473d
ab5473d
    def public_content(self):
ab5473d
        if len(self.anon_list) > 0:
ab5473d
            self.fd.write("""
ab5473d
.SH SHARING FILES
ab5473d
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.
ab5473d
.TP
ab5473d
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.
ab5473d
.PP
ab5473d
.B
ab5473d
semanage fcontext -a -t public_content_t "/var/%(domainname)s(/.*)?"
ab5473d
.br
ab5473d
.B restorecon -F -R -v /var/%(domainname)s
ab5473d
.pp
ab5473d
.TP
ab5473d
Allow %(domainname)s servers to read and write /var/tmp/incoming by adding the public_content_rw_t type to the directory and by restoring the file type.  This also requires the allow_%(domainname)sd_anon_write boolean to be set.
ab5473d
.PP
ab5473d
.B
ab5473d
semanage fcontext -a -t public_content_rw_t "/var/%(domainname)s/incoming(/.*)?"
ab5473d
.br
ab5473d
.B restorecon -F -R -v /var/%(domainname)s/incoming
ab5473d
ab5473d
"""  % {'domainname':self.domainname})
ab5473d
            for b in self.anon_list:
ab5473d
                desc = seobject.booleans_dict[b][2][0].lower() + seobject.booleans_dict[b][2][1:]
ab5473d
                self.fd.write("""
ab5473d
.PP
ab5473d
If you want to %s, you must turn on the %s boolean.
ab5473d
ab5473d
.EX
ab5473d
.B setsebool -P %s 1
ab5473d
.EE
ab5473d
""" % (desc, b, b))
ab5473d
ab5473d
    def footer(self):
ab5473d
        self.fd.write("""
ab5473d
.SH "COMMANDS"
ab5473d
.B semanage fcontext
ab5473d
can also be used to manipulate default file context mappings.
ab5473d
.PP
ab5473d
.B semanage permissive
ab5473d
can also be used to manipulate whether or not a process type is permissive.
ab5473d
.PP
ab5473d
.B semanage module
ab5473d
can also be used to enable/disable/install/remove policy modules.
ab5473d
""")
ab5473d
ab5473d
        if len(self.ports) > 0:
ab5473d
            self.fd.write("""
ab5473d
.B semanage port
ab5473d
can also be used to manipulate the port definitions
ab5473d
""")
ab5473d
ab5473d
        if self.booltext != "":        
ab5473d
            self.fd.write("""
ab5473d
.B semanage boolean
ab5473d
can also be used to manipulate the booleans
ab5473d
""")
ab5473d
ab5473d
        self.fd.write("""
ab5473d
.PP
ab5473d
.B system-config-selinux 
ab5473d
is a GUI tool available to customize SELinux policy settings.
ab5473d
ab5473d
.SH AUTHOR	
ab5473d
This manual page was auto-generated by genman.py.
ab5473d
ab5473d
.SH "SEE ALSO"
ab5473d
selinux(8), %s(8), semanage(8), restorecon(8), chcon(1)
ab5473d
""" % self.domainname)
ab5473d
ab5473d
        if self.booltext != "":        
ab5473d
            self.fd.write(", setsebool(8)")
ab5473d
ab5473d
	self.see_also()
ab5473d
ab5473d
    def valid_write(self, check, attributes):
ab5473d
            if check in [ self.type, "domain" ]:
ab5473d
		    return False
ab5473d
	    if check.endswith("_t"):
ab5473d
		    for a in attributes:
ab5473d
			    if a in types[check]:
ab5473d
				    return False
ab5473d
	    return True
ab5473d
ab5473d
    def writes(self):
ab5473d
        permlist = setools.sesearch([setools.ALLOW],{'scontext':self.type,  'permlist':['open', 'write'], 'class':'file'})
ab5473d
	if permlist == None:
ab5473d
		return
ab5473d
ab5473d
        self.fd.write ("""
ab5473d
.SH "MANAGED FILES"
ab5473d
""")
ab5473d
        all_writes = []
ab5473d
	attributes = ["proc_type", "sysctl_type"]
ab5473d
	for i in permlist:
ab5473d
		if not i['tcontext'].endswith("_t"):
ab5473d
			attributes.append(i['tcontext'])
ab5473d
ab5473d
        for i in permlist:
ab5473d
		if self.valid_write(i['tcontext'],attributes):
ab5473d
			if i['tcontext'] not in all_writes:
ab5473d
				all_writes.append(i['tcontext'])
ab5473d
ab5473d
        self.fd.write ("""
ab5473d
The SELinux user 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.
ab5473d
"""   %	self.domainname)
ab5473d
ab5473d
        all_writes.sort()
ab5473d
        if "file_type" in all_writes:
ab5473d
            all_writes = [ "file_type" ]
ab5473d
        for f in all_writes:
ab5473d
            self.fd.write("""
ab5473d
.br
ab5473d
.B %s
ab5473d
ab5473d
""" % f)
ab5473d
            if f in fcdict:
ab5473d
                for path in fcdict[f]:
ab5473d
                    self.fd.write("""\t%s
ab5473d
.br
ab5473d
""" % path)
ab5473d
ab5473d
    def user_header(self):
ab5473d
        self.fd.write('.TH  "%(type)s_selinux"  "8"  "%(type)s" "mgrepl@redhat.com" "%(type)s SELinux Policy documentation"'
ab5473d
                      %	{'type':self.domainname})
ab5473d
ab5473d
        self.fd.write(r"""
ab5473d
.SH "NAME"
ab5473d
%(user)s_u \- \fB%(desc)s\fP - Security Enhanced Linux Policy 
ab5473d
ab5473d
.SH DESCRIPTION
ab5473d
ab5473d
\fB%(user)s_u\fP is an SELinux User defined in the SELinux
ab5473d
policy. SELinux users have default roles, \fB%(user)s_r\fP.  The
ab5473d
default role has a default type, \fB%(user)s_t\fP, associated with it.
ab5473d
ab5473d
The SELinux user will usually login to a system with a context that looks like:
ab5473d
ab5473d
.B %(user)s_u:%(user)s_r:%(user)s_t:s0-s0:c0.c1023
ab5473d
ab5473d
Linux users are automatically assigned an SELinux users at login.  
ab5473d
Login programs use the SELinux User to assign initial context to the user's shell.
ab5473d
ab5473d
SELinux policy uses the context to control the user's access.
ab5473d
ab5473d
By default all users are assigned to the SELinux user via the \fB__default__\fP flag
ab5473d
ab5473d
On Targeted policy systems the \fB__default__\fP user is assigned to the \fBunconfined_u\fP SELinux user.
ab5473d
ab5473d
You can list all Linux User to SELinux user mapping using:
ab5473d
ab5473d
.B semanage login -l
ab5473d
ab5473d
If you wanted to change the default user mapping to use the %(user)s_u user, you would execute:
ab5473d
ab5473d
.B semanage login -m -s %(user)s_u __default__
ab5473d
ab5473d
""" % {'desc': self.desc, 'type':self.type, 'user':self.domainname})
ab5473d
ab5473d
        if "login_userdomain" in self.attributes and "login_userdomain" in all_attributes:
ab5473d
            self.fd.write("""
ab5473d
If you want to map the one Linux user (joe) to the SELinux user %(user)s, you would execute:
ab5473d
ab5473d
.B $ semanage login -a -s %(user)s_u joe
ab5473d
ab5473d
"""	%	{'user':self.domainname})
ab5473d
ab5473d
    def can_sudo(self):
ab5473d
        sudotype = "%s_sudo_t" % self.domainname
ab5473d
        self.fd.write("""
ab5473d
.SH SUDO
ab5473d
""")
ab5473d
        if sudotype in types:
ab5473d
            role = self.domainname + "_r"
ab5473d
            self.fd.write("""
ab5473d
The SELinux user %(user)s can execute sudo. 
ab5473d
ab5473d
You can set up sudo to allow %(user)s to transition to an administrative domain:
ab5473d
ab5473d
Add one or more of the following record to sudoers using visudo.
ab5473d
ab5473d
""" % { 'user':self.domainname } )
ab5473d
            for adminrole in role_allows[role]:
ab5473d
                self.fd.write("""
ab5473d
USERNAME ALL=(ALL) ROLE=%(admin)s_r TYPE=%(admin)s_t COMMAND
ab5473d
.br
ab5473d
sudo will run COMMAND as %(user)s_u:%(admin)s_r:%(admin)s_t:LEVEL
ab5473d
""" % {'admin':adminrole[:-2], 'user':self.domainname } )
ab5473d
ab5473d
                self.fd.write("""
ab5473d
You might also need to add one or more of these new roles to your SELinux user record.
ab5473d
ab5473d
List the SELinux roles your SELinux user can reach by executing:
ab5473d
ab5473d
.B $ semanage user -l |grep selinux_name
ab5473d
ab5473d
Modify the roles list and add %(user)s_r to this list.
ab5473d
ab5473d
.B $ semanage user -m -R '%(roles)s' %(user)s_u 
ab5473d
ab5473d
For more details you can see semanage man page.
ab5473d
ab5473d
""" % {'user':self.domainname, "roles": " ".join([role] + role_allows[role]) } )
ab5473d
            else:
ab5473d
                self.fd.write("""
ab5473d
The SELinux type %s_t is not allowed to execute sudo. 
ab5473d
""" % self.domainname)
ab5473d
ab5473d
    def user_attribute(self):
ab5473d
        self.fd.write("""
ab5473d
.SH USER DESCRIPTION
ab5473d
""")
ab5473d
        if "unconfined_usertype" in self.attributes:
ab5473d
            self.fd.write("""
ab5473d
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. 		
ab5473d
""" % self.domainname)
ab5473d
ab5473d
        if "unpriv_userdomain" in self.attributes:
ab5473d
            self.fd.write("""
ab5473d
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.
ab5473d
""" % self.domainname)
ab5473d
ab5473d
        if "admindomain" in self.attributes:
ab5473d
            self.fd.write("""
ab5473d
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.  
ab5473d
""" % self.domainname)
ab5473d
ab5473d
    def xwindows_login(self):
ab5473d
        if "x_domain" in all_attributes:
ab5473d
            self.fd.write("""
ab5473d
.SH X WINDOWS LOGIN
ab5473d
""")
ab5473d
            if "x_domain" in self.attributes:
ab5473d
                self.fd.write("""
ab5473d
The SELinux user %s_u is able to X Windows login.
ab5473d
""" % self.domainname)
ab5473d
            else:
ab5473d
                self.fd.write("""
ab5473d
The SELinux user %s_u is not able to X Windows login.
ab5473d
""" % self.domainname)
ab5473d
ab5473d
    def terminal_login(self):
ab5473d
        if "login_userdomain" in all_attributes:
ab5473d
            self.fd.write("""
ab5473d
.SH TERMINAL LOGIN
ab5473d
""")
ab5473d
            if "login_userdomain" in self.attributes:
ab5473d
                self.fd.write("""
ab5473d
The SELinux user %s_u is able to terminal login.
ab5473d
""" % self.domainname)
ab5473d
            else:
ab5473d
                self.fd.write("""
ab5473d
The SELinux user %s_u is not able to terminal login.
ab5473d
""" % self.domainname)
ab5473d
ab5473d
    def network(self):
ab5473d
        self.fd.write("""
ab5473d
.SH NETWORK
ab5473d
""")
ab5473d
        for net in ("tcp", "udp"):
ab5473d
            portdict = senetwork.get_network_connect(self.type, net, "name_bind")
ab5473d
            if len(portdict) > 0:
ab5473d
                self.fd.write("""
ab5473d
.TP
ab5473d
The SELinux user %s_u is able to listen on the following %s ports.
ab5473d
""" % (self.domainname, net))
ab5473d
                for p in portdict:
ab5473d
                    for recs in portdict[p]:
ab5473d
                        self.fd.write("""
ab5473d
.B %s
ab5473d
""" % recs)
ab5473d
            portdict = senetwork.get_network_connect(self.type, "tcp", "name_connect")
ab5473d
            if len(portdict) > 0:
ab5473d
                self.fd.write("""
ab5473d
.TP
ab5473d
The SELinux user %s_u is able to connect to the following tcp ports.
ab5473d
""" % (self.domainname))
ab5473d
                for p in portdict:
ab5473d
                    for recs in portdict[p]:
ab5473d
                        self.fd.write("""
ab5473d
.B %s
ab5473d
""" % recs)
ab5473d
ab5473d
    def home_exec(self):
ab5473d
        permlist = setools.sesearch([setools.ALLOW],{'scontext':self.type,'tcontext':'user_home_type', 'class':'file', 'permlist':['ioctl', 'read', 'getattr', 'execute', 'execute_no_trans', 'open']})
ab5473d
        self.fd.write("""
ab5473d
.SH HOME_EXEC
ab5473d
""" )
ab5473d
        if permlist is not None:
ab5473d
            self.fd.write("""
ab5473d
The SELinux user %s_u is able execute home content files.
ab5473d
"""  % self.domainname)
ab5473d
ab5473d
        else:
ab5473d
            self.fd.write("""
ab5473d
The SELinux user %s_u is not able execute home content files.
ab5473d
"""  % self.domainname)
ab5473d
ab5473d
    def transitions(self):
ab5473d
        self.fd.write(r"""
ab5473d
.SH TRANSITIONS
ab5473d
ab5473d
Three things can happen when %(type)s attempts to execute a program.
ab5473d
ab5473d
\fB1.\fP SELinux Policy can deny %(type)s from executing the program.
ab5473d
ab5473d
.TP
ab5473d
ab5473d
\fB2.\fP SELinux Policy can allow %(type)s to execute the program in the current user type.
ab5473d
ab5473d
Execute the following to see the types that the SELinux user %(type)s can execute without transitioning:
ab5473d
ab5473d
.B sesearch -A -s %(type)s -c file -p execute_no_trans
ab5473d
ab5473d
.TP
ab5473d
ab5473d
\fB3.\fP SELinux can allow %(type)s to execute the program and transition to a new type.
ab5473d
ab5473d
Execute the following to see the types that the SELinux user %(type)s can execute and transition:
ab5473d
ab5473d
.B $ sesearch -A -s %(type)s -c process -p transition
ab5473d
ab5473d
"""	% {'user':self.domainname, 'type':self.type})
ab5473d
ab5473d
    def role_header(self):
ab5473d
        self.fd.write('.TH  "%(user)s_selinux"  "8"  "%(user)s" "mgrepl@redhat.com" "%(user)s SELinux Policy documentation"'
ab5473d
                      %	{'user':self.domainname})
ab5473d
ab5473d
        self.fd.write(r"""
ab5473d
.SH "NAME"
ab5473d
%(user)s_r \- \fB%(desc)s\fP - Security Enhanced Linux Policy 
ab5473d
ab5473d
.SH DESCRIPTION
ab5473d
ab5473d
SELinux supports Roles Based Access Control (RBAC), some Linux roles are login roles, while other roles need to be transition into. 
ab5473d
ab5473d
.I Note: 
ab5473d
Examples in this man page will use the 
ab5473d
.B staff_u 
ab5473d
SELinux user.
ab5473d
ab5473d
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. 
ab5473d
ab5473d
The default type for the %(user)s_r role is %(user)s_t.
ab5473d
ab5473d
The 
ab5473d
.B newrole 
ab5473d
program to transition directly to this role.
ab5473d
ab5473d
.B newrole -r %(user)s_r -t %(user)s_t
ab5473d
ab5473d
.B sudo 
ab5473d
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.
ab5473d
ab5473d
USERNAME ALL=(ALL) ROLE=%(user)s_r TYPE=%(user)s_t COMMAND
ab5473d
ab5473d
.br
ab5473d
sudo will run COMMAND as staff_u:%(user)s_r:%(user)s_t:LEVEL
ab5473d
ab5473d
When using a a non login role, you need to setup SELinux so that your SELinux user can reach %(user)s_r role.
ab5473d
ab5473d
Execute the following to see all of the assigned SELinux roles:
ab5473d
ab5473d
.B semanage user -l
ab5473d
ab5473d
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:
ab5473d
ab5473d
.B $ semanage user -m -R 'staff_r system_r %(user)s_r' staff_u 
ab5473d
ab5473d
""" % {'desc': self.desc, 'user':self.domainname})
ab5473d
        troles = []
ab5473d
        for i in role_allows:
ab5473d
            if self.domainname +"_r" in role_allows[i]:
ab5473d
                troles.append(i)
ab5473d
        if len(troles) > 0:
ab5473d
            plural = ""
ab5473d
            if len(troles) > 1:
ab5473d
                plural = "s"
ab5473d
ab5473d
                self.fd.write("""
ab5473d
ab5473d
SELinux policy also controls which roles can transition to a different role.  
ab5473d
You can list these rules using the following command.
ab5473d
ab5473d
.B sesearch --role_allow
ab5473d
ab5473d
SELinux policy allows the %s role%s can transition to the %s_r role.
ab5473d
ab5473d
""" % (", ".join(troles), plural, self.domainname))
ab5473d
ab5473d
if __name__ == '__main__':
234c764
43a709d
	import argparse
43a709d
	parser = argparse.ArgumentParser(description='Generate SELinux man pages')
234c764
43a709d
	parser.add_argument("-p", "--path", dest="path", required=True, default="/tmp", help="Path for SELinux man pages")
43a709d
	parser.add_argument("-r", "--version", dest="os_version", default="Fedora18",help="Version of OS")
43a709d
	parser.add_argument("-l", "--list", dest="test_domains", default="", nargs="+", help="List of domains")
234c764
43a709d
	try:
43a709d
		args = parser.parse_args()
234c764
43a709d
		os_version = args.os_version
43a709d
		path = args.path
234c764
43a709d
		print os_version
43a709d
		print path
234c764
43a709d
		if len(args.test_domains) == 0:
234c764
			test_domains = domains
43a709d
		else:
43a709d
			test_domains = args.test_domains
234c764
234c764
	except Exception,e:
43a709d
		sys.stderr.write("exception %s: %s" % (e.__class__.__name__, str(e)))
43a709d
		sys.exit(1)
234c764
234c764
	if not os.path.isdir(path):
43a709d
		sys.stderr.write("%s does not exist" % path)
234c764
		sys.exit(1)
ab5473d
ab5473d
	for domain in test_domains:
ab5473d
		print domain
234c764
		ManPage(domain, path, os_version)