57bf9f2
#! /usr/bin/python -Es
57bf9f2
# Copyright (C) 2012 Red Hat 
57bf9f2
# AUTHOR: Dan Walsh <dwalsh@redhat.com>
57bf9f2
# see file 'COPYING' for use and warranty information
57bf9f2
#
57bf9f2
# semanage is a tool for managing SELinux configuration files
57bf9f2
#
57bf9f2
#    This program is free software; you can redistribute it and/or
57bf9f2
#    modify it under the terms of the GNU General Public License as
57bf9f2
#    published by the Free Software Foundation; either version 2 of
57bf9f2
#    the License, or (at your option) any later version.
57bf9f2
#
57bf9f2
#    This program is distributed in the hope that it will be useful,
57bf9f2
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
57bf9f2
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
57bf9f2
#    GNU General Public License for more details.
57bf9f2
#
57bf9f2
#    You should have received a copy of the GNU General Public License
57bf9f2
#    along with this program; if not, write to the Free Software
57bf9f2
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA     
57bf9f2
#                                        02111-1307  USA
57bf9f2
#
57bf9f2
#  
57bf9f2
import seobject
57bf9f2
import selinux
57bf9f2
import datetime
57bf9f2
import setools
57bf9f2
import sys
57bf9f2
57bf9f2
all_attributes = map(lambda x: x['name'], setools.seinfo(setools.ATTRIBUTE))
57bf9f2
entrypoints =  setools.seinfo(setools.ATTRIBUTE,"entry_type")[0]["types"]
57bf9f2
alldomains =  setools.seinfo(setools.ATTRIBUTE,"domain")[0]["types"]
57bf9f2
domains = []
57bf9f2
57bf9f2
for d in alldomains:
57bf9f2
    found = False
57bf9f2
    if d[:-2] + "_exec_t" not in entrypoints:
57bf9f2
        continue
57bf9f2
    name = d.split("_")[0]
57bf9f2
    if name in domains or name == "pam":
57bf9f2
        continue
57bf9f2
    domains.append(name)
57bf9f2
57bf9f2
domains.sort()
57bf9f2
57bf9f2
file_types =  setools.seinfo(setools.ATTRIBUTE,"file_type")[0]["types"]
57bf9f2
file_types.sort()
57bf9f2
57bf9f2
port_types =  setools.seinfo(setools.ATTRIBUTE,"port_type")[0]["types"]
57bf9f2
port_types.sort()
57bf9f2
57bf9f2
portrecs = seobject.portRecords().get_all_by_type()
57bf9f2
filerecs = seobject.fcontextRecords()
57bf9f2
files_dict = {}
57bf9f2
fdict = filerecs.get_all()
57bf9f2
for i in fdict:
57bf9f2
    if fdict[i]:
57bf9f2
        if fdict[i][2] in files_dict:
57bf9f2
            files_dict[fdict[i][2]].append(i)
57bf9f2
        else:
57bf9f2
            files_dict[fdict[i][2]] = [i]
57bf9f2
boolrecs = seobject.booleanRecords()
57bf9f2
bools = seobject.booleans_dict.keys()
57bf9f2
57bf9f2
man = {}
57bf9f2
date = datetime.datetime.now().strftime("%d %b %Y")
57bf9f2
def prettyprint(f,trim):
57bf9f2
    return " ".join(f[:-len(trim)].split("_"))
57bf9f2
57bf9f2
class ManPage:
57bf9f2
    def __init__(self, domainname, path="/tmp"):
57bf9f2
        self.domainname = domainname
57bf9f2
        if self.domainname[-1]=='d':
57bf9f2
            self.short_name = self.domainname[:-1]
57bf9f2
        else:
57bf9f2
            self.short_name = domainname
57bf9f2
57bf9f2
        self.anon_list = []
57bf9f2
        self.fd = open("%s/%s_selinux.8" % (path, domainname), 'w')
57bf9f2
57bf9f2
        self.attributes = {}
57bf9f2
        self.ptypes = []
57bf9f2
        self.get_ptypes()
57bf9f2
57bf9f2
        for domain_type in self.ptypes:
57bf9f2
            self.attributes[domain_type] = setools.seinfo(setools.TYPE,("%s") % domain_type)[0]["attributes"]
57bf9f2
57bf9f2
        self.header()
57bf9f2
        self.booleans()
57bf9f2
        self.nsswitch_domain()
57bf9f2
        self.public_content()
57bf9f2
        self.file_context()
57bf9f2
        self.port_types()
57bf9f2
        self.process_types()
57bf9f2
        self.footer()
57bf9f2
        self.fd.close()
57bf9f2
57bf9f2
    def get_ptypes(self):
57bf9f2
        for f in alldomains:
57bf9f2
            if f.startswith(self.short_name):
57bf9f2
                self.ptypes.append(f)
57bf9f2
57bf9f2
    def header(self):
57bf9f2
        self.fd.write('.TH  "%(domainname)s_selinux"  "8"  "%(domainname)s" "dwalsh@redhat.com" "%(domainname)s SELinux Policy documentation"'
57bf9f2
                 % {'domainname':self.domainname})
57bf9f2
        self.fd.write(r"""
57bf9f2
.SH "NAME"
57bf9f2
%(domainname)s_selinux \- Security Enhanced Linux Policy for the %(domainname)s processes
57bf9f2
.SH "DESCRIPTION"
57bf9f2
57bf9f2
Security-Enhanced Linux secures the %(domainname)s processes via flexible mandatory access
57bf9f2
control.  
57bf9f2
""" % {'domainname':self.domainname})
57bf9f2
57bf9f2
57bf9f2
    def explain(self, f):
57bf9f2
        if f.endswith("_var_run_t"):
57bf9f2
            return "store the %s files under the /run directory." % prettyprint(f, "_var_run_t")
57bf9f2
        if f.endswith("_pid_t"):
57bf9f2
            return "store the %s files under the /run directory." % prettyprint(f, "_pid_t")
57bf9f2
        if f.endswith("_var_lib_t"):
57bf9f2
            return "store the %s files under the /var/lib directory."  % prettyprint(f, "_var_lib_t")
57bf9f2
        if f.endswith("_var_t"):
57bf9f2
            return "store the %s files under the /var directory."  % prettyprint(f, "_var_lib_t")
57bf9f2
        if f.endswith("_var_spool_t"):
57bf9f2
            return "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t")
57bf9f2
        if f.endswith("_spool_t"):
57bf9f2
            return "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t")
57bf9f2
        if f.endswith("_cache_t") or f.endswith("_var_cache_t"):
57bf9f2
            return "store the files under the /var/cache directory."
57bf9f2
        if f.endswith("_keytab_t"):
57bf9f2
            return "treat the files as kerberos keytab files."
57bf9f2
        if f.endswith("_lock_t"):
57bf9f2
            return "treat the files as %s lock data, stored under the /var/lock directory" % prettyprint(f,"_lock_t")
57bf9f2
        if f.endswith("_log_t"):
57bf9f2
            return "treat the data as %s log data, usually stored under the /var/log directory." % prettyprint(f,"_log_t")
57bf9f2
        if f.endswith("_config_t"):
57bf9f2
            return "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f,"_config_t")
57bf9f2
        if f.endswith("_conf_t"):
57bf9f2
            return "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f,"_conf_t")
57bf9f2
        if f.endswith("_exec_t"):
57bf9f2
            return "transition an executable to the %s_t domain." % f[:-len("_exec_t")]
57bf9f2
        if f.endswith("_cgi_content_t"):
57bf9f2
            return "treat the files as %s cgi content." % prettyprint(f, "_cgi_content_t")
57bf9f2
        if f.endswith("_rw_content_t"):
57bf9f2
            return "treat the files as %s read/write content." % prettyprint(f,"_rw_content_t")
57bf9f2
        if f.endswith("_rw_t"):
57bf9f2
            return "treat the files as %s read/write content." % prettyprint(f,"_rw_t")
57bf9f2
        if f.endswith("_write_t"):
57bf9f2
            return "treat the files as %s read/write content." % prettyprint(f,"_write_t")
57bf9f2
        if f.endswith("_db_t"):
57bf9f2
            return "treat the files as %s database content." % prettyprint(f,"_db_t")
57bf9f2
        if f.endswith("_ra_content_t"):
57bf9f2
            return "treat the files as %s read/append content." % prettyprint(f,"_ra_conten_t")
57bf9f2
        if f.endswith("_cert_t"):
57bf9f2
            return "treat the files as %s certificate data." % prettyprint(f,"_cert_t")
57bf9f2
        if f.endswith("_key_t"):
57bf9f2
            return "treat the files as %s key data." % prettyprint(f,"_key_t")
57bf9f2
57bf9f2
        if f.endswith("_secret_t"):
57bf9f2
            return "treat the files as %s secret data." % prettyprint(f,"_key_t")
57bf9f2
57bf9f2
        if f.endswith("_ra_t"):
57bf9f2
            return "treat the files as %s read/append content." % prettyprint(f,"_ra_t")
57bf9f2
57bf9f2
        if f.endswith("_ro_t"):
57bf9f2
            return "treat the files as %s read/only content." % prettyprint(f,"_ro_t")
57bf9f2
57bf9f2
        if f.endswith("_modules_t"):
57bf9f2
            return "treat the files as %s modules." % prettyprint(f, "_modules_t")
57bf9f2
57bf9f2
        if f.endswith("_content_t"):
57bf9f2
            return "treat the files as %s content." % prettyprint(f, "_content_t")
57bf9f2
57bf9f2
        if f.endswith("_state_t"):
57bf9f2
            return "treat the files as %s state data." % prettyprint(f, "_state_t")
57bf9f2
57bf9f2
        if f.endswith("_files_t"):
57bf9f2
            return "treat the files as %s content." % prettyprint(f, "_files_t")
57bf9f2
57bf9f2
        if f.endswith("_file_t"):
57bf9f2
            return "treat the files as %s content." % prettyprint(f, "_file_t")
57bf9f2
57bf9f2
        if f.endswith("_data_t"):
57bf9f2
            return "treat the files as %s content." % prettyprint(f, "_data_t")
57bf9f2
57bf9f2
        if f.endswith("_file_t"):
57bf9f2
            return "treat the data as %s content." % prettyprint(f, "_file_t")
57bf9f2
57bf9f2
        if f.endswith("_tmp_t"):
57bf9f2
            return "store %s temporary files in the /tmp directories." % prettyprint(f, "_tmp_t")
57bf9f2
        if f.endswith("_etc_t"):
57bf9f2
            return "store %s files in the /etc directories." % prettyprint(f, "_tmp_t")
57bf9f2
        if f.endswith("_home_t"):
57bf9f2
            return "store %s files in the users home directory." % prettyprint(f, "_home_t")
57bf9f2
        if f.endswith("_tmpfs_t"):
57bf9f2
            return "store %s files on a tmpfs file system." % prettyprint(f, "_tmpfs_t")
57bf9f2
        if f.endswith("_unit_file_t"):
57bf9f2
            return "treat files as a systemd unit file." 
57bf9f2
        if f.endswith("_htaccess_t"):
57bf9f2
            return "treat the file as a %s access file." % prettyprint(f, "_htaccess_t")
57bf9f2
57bf9f2
        return "treat the files as %s data." % prettyprint(f,"_t")
57bf9f2
57bf9f2
    def booleans(self):
57bf9f2
        self.booltext = ""
57bf9f2
        for b in bools:
57bf9f2
            if b.find(self.short_name) >= 0:
57bf9f2
                if b.endswith("anon_write"):
57bf9f2
                    self.anon_list.append(b)
57bf9f2
                else:
57bf9f2
                    desc = seobject.booleans_dict[b][2][0].lower() + seobject.booleans_dict[b][2][1:-1]
57bf9f2
                    self.booltext += """
57bf9f2
.PP
57bf9f2
If you want to %s, you must turn on the %s boolean.
57bf9f2
57bf9f2
.EX
57bf9f2
.B setsebool -P %s 1
57bf9f2
.EE
57bf9f2
""" % (desc, b, b)
57bf9f2
    
57bf9f2
        if self.booltext != "":        
57bf9f2
            self.fd.write("""
57bf9f2
.SH BOOLEANS
57bf9f2
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.
57bf9f2
57bf9f2
""" % (self.domainname, self.domainname))
57bf9f2
57bf9f2
            self.fd.write(self.booltext)
57bf9f2
57bf9f2
    def nsswitch_domain(self):
57bf9f2
        nsswitch_types = []
57bf9f2
        nsswitch_booleans = ['authlogin_nsswitch_use_ldap', 'allow_kerberos', 'allow_ypbind']
57bf9f2
        nsswitchbooltext = ""
57bf9f2
        if "nsswitch_domain" in all_attributes:
57bf9f2
            self.fd.write("""
57bf9f2
.SH NSSWITCH DOMAIN
57bf9f2
""")
57bf9f2
            for k in self.attributes.keys():    
57bf9f2
                if "nsswitch_domain" in self.attributes[k]:
57bf9f2
                    nsswitch_types.append(k)
57bf9f2
57bf9f2
            if len(nsswitch_types):
57bf9f2
                for i in nsswitch_booleans:
57bf9f2
                    desc = seobject.booleans_dict[i][2][0].lower() + seobject.booleans_dict[i][2][1:-1]
57bf9f2
                    nsswitchbooltext += """
57bf9f2
.PP
57bf9f2
If you want to %s for the %s, you must turn on the %s boolean.
57bf9f2
57bf9f2
.EX
57bf9f2
setsebool -P %s 1
57bf9f2
.EE
57bf9f2
""" % (desc,(", ".join(nsswitch_types)), i, i)
57bf9f2
57bf9f2
        self.fd.write(nsswitchbooltext)
57bf9f2
57bf9f2
    def process_types(self):
57bf9f2
        if len(self.ptypes) == 0:
57bf9f2
            return
57bf9f2
        self.fd.write(r"""
57bf9f2
.SH PROCESS TYPES
57bf9f2
SELinux defines process types (domains) for each process running on the system
57bf9f2
.PP
57bf9f2
You can see the context of a process using the \fB\-Z\fP option to \fBps\bP
57bf9f2
.PP
57bf9f2
Policy governs the access confined processes have to files. 
57bf9f2
SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
57bf9f2
.PP 
57bf9f2
The following process types are defined for %(domainname)s:
57bf9f2
""" % {'domainname':self.domainname})
57bf9f2
        self.fd.write("""
57bf9f2
.EX
57bf9f2
.B %s 
57bf9f2
.EE""" % ", ".join(self.ptypes))
57bf9f2
        self.fd.write("""
57bf9f2
.PP
57bf9f2
Note: 
57bf9f2
.B semanage permissive -a PROCESS_TYPE 
57bf9f2
can be used to make a process type permissive. Permissive process types are not denied access by SELinux. AVC messages will still be generated.
57bf9f2
""")
57bf9f2
57bf9f2
    def port_types(self):
57bf9f2
        self.ports = []
57bf9f2
        for f in port_types:
57bf9f2
            if f.startswith(self.short_name):
57bf9f2
                self.ports.append(f)
57bf9f2
57bf9f2
        if len(self.ports) == 0:
57bf9f2
            return
57bf9f2
        self.fd.write("""
57bf9f2
.SH PORT TYPES
57bf9f2
SELinux defines port types to represent TCP and UDP ports. 
57bf9f2
.PP
57bf9f2
You can see the types associated with a port by using the following command: 
57bf9f2
57bf9f2
.B semanage port -l
57bf9f2
57bf9f2
.PP
57bf9f2
Policy governs the access confined processes have to these ports. 
57bf9f2
SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
57bf9f2
.PP 
57bf9f2
The following port types are defined for %(domainname)s:""" % {'domainname':self.domainname})
57bf9f2
57bf9f2
        for p in self.ports:
57bf9f2
            self.fd.write("""
57bf9f2
57bf9f2
.EX
57bf9f2
.TP 5
57bf9f2
.B %s 
57bf9f2
.TP 10
57bf9f2
.EE
57bf9f2
""" % p)
57bf9f2
            once = True
57bf9f2
            for prot in ( "tcp", "udp" ):
57bf9f2
               if (p,prot) in portrecs:
57bf9f2
                    if once:
57bf9f2
                        self.fd.write("""
57bf9f2
57bf9f2
Default Defined Ports:""")
57bf9f2
                    once = False
57bf9f2
                    self.fd.write(r"""
57bf9f2
%s %s
57bf9f2
.EE""" % (prot, ",".join(portrecs[(p,prot)])))
57bf9f2
57bf9f2
    def file_context(self):
57bf9f2
        self.fd.write(r"""
57bf9f2
.SH FILE CONTEXTS
57bf9f2
SELinux requires files to have an extended attribute to define the file type. 
57bf9f2
.PP
57bf9f2
You can see the context of a file using the \fB\-Z\fP option to \fBls\bP
57bf9f2
.PP
57bf9f2
Policy governs the access confined processes have to these files. 
57bf9f2
SELinux %(domainname)s policy is very flexible allowing users to setup their %(domainname)s processes in as secure a method as possible.
57bf9f2
.PP 
57bf9f2
The following file types are defined for %(domainname)s:
57bf9f2
""" % {'domainname':self.domainname})
57bf9f2
        for f in file_types:
57bf9f2
            if f.startswith(self.domainname):
57bf9f2
                self.fd.write("""
57bf9f2
57bf9f2
.EX
57bf9f2
.PP
57bf9f2
.B %s 
57bf9f2
.EE
57bf9f2
57bf9f2
- Set files with the %s type, if you want to %s
57bf9f2
""" % (f, f, self.explain(f)))
57bf9f2
57bf9f2
                if f in files_dict:
57bf9f2
                    plural = ""
57bf9f2
                    if len(files_dict[f]) > 1:
57bf9f2
                        plural = "s"
57bf9f2
                        self.fd.write("""
57bf9f2
.br
57bf9f2
.TP 5
57bf9f2
Path%s: 
57bf9f2
%s""" % (plural, files_dict[f][0][0]))
57bf9f2
                        for x in files_dict[f][1:]:
57bf9f2
                            self.fd.write(", %s" % x[0])
57bf9f2
57bf9f2
        self.fd.write("""
57bf9f2
57bf9f2
.PP
57bf9f2
Note: File context can be temporarily modified with the chcon command.  If you want to permanantly change the file context you need to use the 
57bf9f2
.B semanage fcontext 
57bf9f2
command.  This will modify the SELinux labeling database.  You will need to use
57bf9f2
.B restorecon
57bf9f2
to apply the labels.
57bf9f2
""")
57bf9f2
57bf9f2
    def public_content(self):
57bf9f2
        if len(self.anon_list) > 0:
57bf9f2
            self.fd.write("""
57bf9f2
.SH SHARING FILES
57bf9f2
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.
57bf9f2
.TP
57bf9f2
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.
57bf9f2
.PP
57bf9f2
.B
57bf9f2
semanage fcontext -a -t public_content_t "/var/%(domainname)s(/.*)?"
57bf9f2
.br
57bf9f2
.B restorecon -F -R -v /var/%(domainname)s
57bf9f2
.pp
57bf9f2
.TP
57bf9f2
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.
57bf9f2
.PP
57bf9f2
.B
57bf9f2
semanage fcontext -a -t public_content_rw_t "/var/%(domainname)s/incoming(/.*)?"
57bf9f2
.br
57bf9f2
.B restorecon -F -R -v /var/%(domainname)s/incoming
57bf9f2
57bf9f2
"""  % {'domainname':self.domainname})
57bf9f2
            for b in self.anon_list:
57bf9f2
                desc = seobject.booleans_dict[b][2][0].lower() + seobject.booleans_dict[b][2][1:]
57bf9f2
                self.fd.write("""
57bf9f2
.PP
57bf9f2
If you want to %s, you must turn on the %s boolean.
57bf9f2
57bf9f2
.EX
57bf9f2
.B setsebool -P %s 1
57bf9f2
.EE
57bf9f2
""" % (desc, b, b))
57bf9f2
57bf9f2
    def footer(self):
57bf9f2
        self.fd.write("""
57bf9f2
.SH "COMMANDS"
57bf9f2
.B semanage fcontext
57bf9f2
can also be used to manipulate default file context mappings.
57bf9f2
.PP
57bf9f2
.B semanage permissive
57bf9f2
can also be used to manipulate whether or not a process type is permissive.
57bf9f2
.PP
57bf9f2
.B semanage module
57bf9f2
can also be used to enable/disable/install/remove policy modules.
57bf9f2
""")
57bf9f2
57bf9f2
        if len(self.ports) > 0:
57bf9f2
            self.fd.write("""
57bf9f2
.B semanage port
57bf9f2
can also be used to manipulate the port definitions
57bf9f2
""")
57bf9f2
57bf9f2
        if self.booltext != "":        
57bf9f2
            self.fd.write("""
57bf9f2
.B semanage boolean
57bf9f2
can also be used to manipulate the booleans
57bf9f2
""")
57bf9f2
57bf9f2
        self.fd.write("""
57bf9f2
.PP
57bf9f2
.B system-config-selinux 
57bf9f2
is a GUI tool available to customize SELinux policy settings.
57bf9f2
57bf9f2
.SH AUTHOR	
57bf9f2
This manual page was autogenerated by genman.py.
57bf9f2
57bf9f2
.SH "SEE ALSO"
57bf9f2
selinux(8), %s(8), semanage(8), restorecon(8), chcon(1)
57bf9f2
""" % self.domainname)
57bf9f2
57bf9f2
        if self.booltext != "":        
57bf9f2
            self.fd.write(", setsebool(8)")
57bf9f2
57bf9f2
for domainname in domains:
57bf9f2
    ManPage(domainname, sys.argv[1])