
# Install SELinux prequisites
#
# === Global variables
#
#   $selinux_type:
#       SELinux mode to use. Valid values are enforcing, permissive and
#       disabled. Defaults to permissive.
#
class selinux {

    if $kernel != "Linux" {
        fail("SELinux supported only on Linux systems")
    }

    if ! $selinux_type {
        $selinux_type = "permissive"
    }
    case $selinux_type {
        "enforcing":  {}
        "permissive": {}
        "disabled":   {}
        default:      { fail("Invalid SELinux mode ${selinux_type}") }
    }

    package { [ "selinux-policy-targeted", "setroubleshoot" ]:
        ensure => installed,
    }

    file { "/etc/selinux/config":
        ensure  => present,
        content => template("selinux/config.erb"),
        mode    => "0644",
        owner   => "root",
        group   => "root",
        require => Package["selinux-policy-targeted"],
        notify  => Exec["set-selinux-mode"],
    }

    if $::operatingsystem in ["CentOS","RedHat"] and $::operatingsystemrelease =~ /^[1-5]\./ {
        service { "setroubleshoot":
            ensure    => $selinux_type ? {
                disabled => stopped,
                default  => running,
            },
            enable    => $selinux_type ? {
                disabled => false,
                default  => true,
            },
            hasstatus => true,
            require   => Package["setroubleshoot"],
        }
    }

    exec { "set-selinux-mode":
        command => $selinux_type ? {
            "enforcing"  => "setenforce 1",
            "permissive" => "setenforce 0",
            "disabled"   => "/bin/true",
        },
        path    => "/bin:/usr/bin:/sbin:/usr/sbin",
        unless  => "getenforce | egrep -i '${selinux_type}'",
        require => File["/etc/selinux/config"],
    }

    include selinux::tools

}


# Install tools for managing SELinux
#
class selinux::tools {

    case $::operatingsystem {
        "fedora": { $package = "policycoreutils-python" }
        "centos","redhat": {
            case $::operatingsystemrelease {
                /^6/: { $package = "policycoreutils-python" }
                default: { $package = "policycoreutils" }
            }
        }
        default: {
            fail("selinux::tools not supported on ${::operatingsystem}")
        }
    }

    package { $package:
        ensure => installed,
    }

}


# Enable restorecond service.
#
class selinux::restorecond {

    if $::selinux == "true" {
        file { "/etc/selinux/restorecond.conf":
            ensure  => present,
            mode    => "0644",
            owner   => "root",
            group   => "root",
            seltype => "selinux_config_t",
            source  => [
                "puppet:///files/selinux/restorecond.conf.${homename}",
                "puppet:///files/selinux/restorecond.conf",
                "puppet:///modules/selinux/restorecond.conf",
            ],
            notify  => Service["restorecond"],
        }

        service { "restorecond":
            ensure => running,
            enable => true,
        }
    }

}


# Set SELinux boolean value
#
# === Parameters
#
#   $name:
#       SELinux key to set
#   $value:
#       Value for given key (on or off)
#
# === Sample usage
#
# selinux::boolean { "use_nfs_home_dirs":
#     value => "on",
# }
#
define selinux::boolean($value) {

    if $::selinux == "true" {
        selboolean { $name:
            value      => $value,
            persistent => true,
        }
    }

}


# Configure SELinux file contexts
#
# === Parameters
#
#   $name:
#       Regexp of path to configure
#   $type:
#       SELinux type for file
#   $recurse:
#       Recursively run restorecon on given path. Defaults to true.
#
# === Sample usage
#
# selinux::manage_fcontext { "/srv/www/http(/.*)?":
#     type => "httpd_sys_content_t",
# }
#
define selinux::manage_fcontext($type, $recurse = true) {

    if "${selinux}" == "true" {

        include selinux::tools

        exec { "semanage fcontext -a -t '${type}' '${name}'":
            path    => "/bin:/usr/bin:/sbin:/usr/sbin",
            unless  => "matchpathcon `echo '${name}' | sed -e 's/(.*$//'` | egrep -q ':${type}(:s[0-9]*)?$'",
            notify  => Exec["restorecon ${name}"],
            require => Class["selinux::tools"],
        }

        if $recurse {
            $restorecon_opts = "-R"
        } else {
            $restorecon_opts = ""
        }

        exec { "restorecon ${name}":
            command     => "restorecon -i ${restorecon_opts} `echo '${name}' | sed -e 's/(.*$//'`",
            path        => "/bin:/usr/bin:/sbin:/usr/sbin",
            refreshonly => true,
        }

    }

}


# Configure SELinux port authorizations
#
# === Parameters
#
#   $name:
#       Port range to configure
#   $type:
#       SELinux type for port range
#   $proto:
#       Protocol for port (tcp or udp)
#
# === Sample usage
#
# selinux::manage_port { "18140-18143":
#     type  => "http_port_t",
#     proto => "tcp",
# }
#
define selinux::manage_port($type, $proto) {

    if "${selinux}" == "true" {

        include selinux::tools

        exec { "semanage port -a -t ${type} -p ${proto} ${name}":
            path    => "/bin:/usr/bin:/sbin:/usr/sbin",
            unless  => "semanage port -ln | egrep '^${type}[ ]*${proto}' | egrep ' ${name}(,.*)?\$'",
            require => Class["selinux::tools"],
        }

    }

}


# Install new SELinux module
#
# === Parameters
#
#   $name:
#       Module name
#   $source:
#       Module source (.te) or compiled file (.pp).
#
# === Sample usage
#
# selinux::module { "munin-local":
#     source => "puppet:///files/common/selinux/munin-local.te",
# }
#
define selinux::module($source) {

    if "${selinux}" == "true" {

        $ext = regsubst($source, '.*\.(te|pp)', '\1')
        case $ext {
            "te": {
                include selinux::module::devel
                file { "/usr/local/src/selinux/${name}.te":
                    ensure  => present,
                    source  => $source,
                    mode    => "0644",
                    owner   => "root",
                    group   => "root",
                    require => File["/usr/local/src/selinux"],
                    notify  => Exec["selinux-module-compile"],
                }
                $module = "/usr/local/src/selinux/${name}.pp"
            }
            "pp": {
                $module = $source
            }
            default: {
                fail("Invalid source '${source}' for selinux::module")
            }
        }

        file { "/usr/share/selinux/targeted/${name}.pp":
            ensure  => present,
            source  => $module,
            mode    => "0644",
            owner   => "root",
            group   => "root",
            require => $ext ? {
                "te"    => Exec["selinux-module-compile"],
                default => undef,
            },
        }

        selmodule { $name:
            ensure      => present,
            require     => File["/usr/share/selinux/targeted/${name}.pp"],
            syncversion => true,
        }

    }

}


# Prequisites for compiling SELinux modules
#
class selinux::module::devel {

    include selinux::tools

    if $::operatingsystem in ["CentOS","RedHat"] {
        if $::operatingsystemrelease =~ /^5\.[0-9]/ {
            package { "selinux-policy-devel":
                ensure => installed,
                before => File["/usr/local/src/selinux"],
            }
        }
    }

    file { "/usr/local/src/selinux":
        ensure  => directory,
        mode    => "0755",
        owner   => "root",
        group   => "root",
        require => Class["selinux::tools"],
    }

    exec { "selinux-module-compile":
        path        => "/bin:/usr/bin:/sbin:/usr/sbin",
        command     => "make -f /usr/share/selinux/devel/Makefile",
        cwd         => "/usr/local/src/selinux",
        user        => "root",
        refreshonly => true,
        require     => Class["selinux::tools"],
    }

}
