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