#!/bin/sh
#
# Draft script to automatically configure Kerberos KDC for Debian Edu.
# It might be a better idea to use Heimdal Kerberos instead of MIT
# Kerberos, as the former has had support for storing principals in
# LDAP for a long time.
#
# Based on
#   http://www.nuug.no/aktiviteter/20100413-kerberos/
#   https://help.ubuntu.com/9.10/serverguide/C/kerberos-ldap.html
#   https://wiki.ubuntu.com/NetworkAuthentication/Client
#   https://help.ubuntu.com/community/Alternate_Pam_Krb5LDAP_Authentication
#   http://www.h5l.org/manual/HEAD/info/heimdal/Using-LDAP-to-store-the-database.html
#   http://wiki.mandriva.com/en/Projects/OpenLDAP_DIT#Heimdal_.28kerberos.29

# Preseeding values (for server and clients)
# krb5-config     krb5-config/add_servers_realm   string  INTERN
# krb5-config     krb5-config/default_realm       string  INTERN
# krb5-config     krb5-config/kerberos_servers    string  kerberos
# krb5-config     krb5-config/admin_server        string  kerberos

set -e

echo "info: setting up kerberos server"

## The communication with debconf happens via standard output, so
## avoid any interference by sending all output to standard error.
## FIXME: Is this ("1>&2") still necessary after all db_* queries are done?
## If you want to know what's going on, use DEBCONF_DEBUG:
#export DEBCONF_DEBUG='developer'
. /usr/share/debconf/confmodule

## query the debconf database for the KDC password:
db_get debian-edu-config/kdc-password
KDC_PW="$RET"

## Make sure to ask for the password if it is not available
## Kerberos KDC password. Parts borrowed from the user-setup package.
## Check if we are on the main-server and if there is no stash file:
if [ -f /etc/debian-edu/config ] && grep -q Main-Server /etc/debian-edu/config \
    && [ ! -f /etc/krb5kdc/stash ] && [ -z "$KDC_PW" ]; then
    loop=0
    while [ $loop -lt 50 ]; do
        loop=$(($loop+1))
        db_input critical debian-edu-config/kdc-password || true
        db_input critical debian-edu-config/kdc-password-again || true
        db_go
        ## Check if password is non-empty:
        db_get debian-edu-config/kdc-password
        KDC_PW="$RET"
        if [ -z "$KDC_PW" ]; then
            db_set debian-edu-config/kdc-password ""
            db_set debian-edu-config/kdc-password-again ""
            db_fset debian-edu-config/kdc-password-empty seen false
            db_input critical debian-edu-config/kdc-password-empty
            db_fset debian-edu-config/kdc-password seen false
            db_fset debian-edu-config/kdc-password-again seen false
            continue
        fi
        ## Check if both entered passwords are identical:
        db_get debian-edu-config/kdc-password-again
        if [ "$KDC_PW" != "$RET" ]; then
            db_set debian-edu-config/kdc-password ""
            db_set debian-edu-config/kdc-password-again ""
            db_fset debian-edu-config/kdc-password-mismatch seen false
            db_input critical debian-edu-config/kdc-password-mismatch
            db_fset debian-edu-config/kdc-password seen false
            db_fset debian-edu-config/kdc-password-again seen false
        else
            break
        fi
    done
else
    echo "To initialize a brand new Kerberos KDC, remove '/etc/krb5kdc/stash' " 1>&2
    echo "and all '/etc/krb5.keytab*' files." 1>&2
fi

## clear passwords in the database:
db_set debian-edu-config/kdc-password ''
db_set debian-edu-config/kdc-password-again ''
## reset all questions/templates:
db_fset debian-edu-config/kdc-password seen false
db_fset debian-edu-config/kdc-password-mismatch seen false
db_fset debian-edu-config/kdc-password-again seen false
db_fset debian-edu-config/kdc-password-empty seen false
echo "Kerberos kdc-passwords cleared from debconf database." 1>&2

## check if the provided kerberos password is non-empty:
if [ -z "$KDC_PW" ]; then
    echo "The provided Kerberos KDC password is empty: Skipping KDC setup." 1>&2
    exit 1
else
    echo "The provided Kerberos KDC password is valid." 1>&2
fi

LDAP_PW=$1

#DN_LDAP_ADMIN="cn=gosa-admin,dc=skole,dc=skolelinux,dc=no"
DN_LDAP_ADMIN=`awk '/^dn: cn=gosa-admin,/ {print $2}' /etc/ldap/root.ldif`
#DN_KRB_CONT="cn=kerberos,dc=skole,dc=skolelinux,dc=no"
DN_KRB_CONT=`awk '/^dn: cn=kerberos,/ {print $2}' /etc/ldap/krb5.ldif`

if [ -z $DN_LDAP_ADMIN ] || [ -z $DN_KRB_CONT ] ; then
    echo "error: Distinguished name for gosa-admin or kerberos not found:" 1>&2
    echo "gosa-admin: '$DN_LDAP_ADMIN'" 1>&2
    echo "kerberos: '$DN_KRB_CONT'" 1>&2
    echo "error: skipping KDC setup." 1>&2
    exit 1
else
    echo "Using '$DN_LDAP_ADMIN' and '$DN_KRB_CONT' for KDC setup." 1>&2
fi

DN_KDC="cn=kdc-service,$DN_KRB_CONT"
DN_KADMIN="cn=kadmin-service,$DN_KRB_CONT"
STASHFILE="/etc/krb5kdc/stash"

mit_kerberos() {
    ## create debian-edu configuration:
    if [ -f /etc/krb5.conf ] ; then
	mv /etc/krb5.conf /etc/krb5.conf_non-edu
    fi
    cat > /etc/krb5.conf <<EOF
[libdefaults]
        ## FIXME: setting enctypes still needed due to #521878#24
#       allow_weak_crypto = true
        permitted_enctypes = des-cbc-crc rc4-hmac des3-cbc-sha1-kd aes128-cts-hmac-sha1-96 aes256-cts-hmac-sha1-96
        default_realm = INTERN
# Should probably use this in [libdefaults] to look up servers in DNS:
#        dns_lookup_realm = false
#        dns_lookup_kdc = true

[realms]
        INTERN = {
                kdc = kerberos
                admin_server = kerberos
                database_module = LDAP
        }

[domain_realm]
        .intern = INTERN
        intern = INTERN

[logging]
        kdc = FILE:/var/log/kdc.log
        kadmin = FILE:/var/log/kadmin.log
        default = FILE:/var/log/krb5.log

[dbdefaults]
        ldap_kerberos_container_dn = $DN_KRB_CONT

[dbmodules]
        LDAP = {
              db_library = kldap
              ldap_kerberos_container_dn = $DN_KRB_CONT
              ldap_kdc_dn = $DN_KDC
              ldap_kadmind_dn = $DN_KADMIN
              ldap_service_password_file = /etc/krb5kdc/service.keyfile
              # Is TLS not implemented in mit-kerberos for connections to ldap?
              # We might have to use: ldaps://ldap.intern or ldapi://ldap.intern;
              # ldap://ldap.intern does not work for me
              ldap_servers = ldapi://
              ldap_conns_per_server = 5
       }
EOF
    chmod 644 /etc/krb5.conf
}

mit_kerberos_kdc() {
    LDAP_ADMIN_PW=$1
    KDC_MASTER_PW=$2
    if [ -f /etc/krb5kdc/kdc.conf ] ; then
	mv /etc/krb5kdc/kdc.conf /etc/krb5kdc/kdc.conf_non-edu
    fi
    cat > /etc/krb5kdc/kdc.conf <<EOF
[kdcdefaults]
    kdc_ports = 750,88

[realms]
    INTERN = {
        admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
        acl_file = /etc/krb5kdc/kadm5.acl
        key_stash_file = $STASHFILE
        kdc_ports = 750,88
        max_life = 10h 0m 0s
        max_renewable_life = 7d 0h 0m 0s
        master_key_type = des3-hmac-sha1
        supported_enctypes = aes256-cts:normal arcfour-hmac:normal des3-hmac-sha1:normal des-cbc-crc:normal des:normal des:v4 des:norealm des:onlyrealm des:afs3
        default_principal_flags = +preauth
    }
EOF
    chmod 644 /etc/krb5kdc/kdc.conf
    if [ ! -f /etc/krb5kdc/kadm5.acl ] ; then
	cat > /etc/krb5kdc/kadm5.acl <<EOF
root/admin@INTERN *
*@INTERN cil
*/*@INTERN i
EOF
    chmod 644 /etc/krb5kdc/kadm5.acl
    fi
    ## create kerberos subtree in ldap database:
    kdb5_ldap_util -s -D $DN_LDAP_ADMIN -w $LDAP_ADMIN_PW \
	create -subtrees dc=skole,dc=skolelinux,dc=no -H ldapi:// -P $KDC_MASTER_PW

    ## The objects and the service-keyfile have been created during ldap bootstrap.
    ## This could have been done with the following command:
    # kdb5_ldap_util -D $DN_LDAP_ADMIN -H ldapi:/// create_service -kdc -randpw $DN_KDC
    # kdb5_ldap_util -D $DN_LDAP_ADMIN -H ldapi:/// create_service -admin -randpw $DN_KADMIN
    # kdb5_ldap_util -H ldaps:// stashsrvpw $DN_KDC
    # kdb5_ldap_util -H ldaps:// stashsrvpw $DN_KADMIN
    ## (Commands need #580502 fixed).

    ## needs root or kdc passwd:
    kadmin.local -q "addprinc -pw $KDC_MASTER_PW root/admin"
    kadmin.local -q "addprinc -pw $KDC_MASTER_PW root"

    ## create machine principals and add them to the keytab:
    kadmin.local -q "addprinc -randkey host/tjener.intern"
    kadmin.local -q "ktadd host/tjener.intern"

    ## create service principals and add them to the keytab:
    kadmin.local -q "addprinc -randkey nfs/tjener.intern"
    kadmin.local -q "ktadd nfs/tjener.intern"

    kadmin.local -q "addprinc -randkey cifs/tjener.intern"
    kadmin.local -q "ktadd cifs/tjener.intern"

    ## does ldap.intern also work?
    kadmin.local -q "addprinc -randkey ldap/tjener.intern"
    kadmin.local -q "ktadd -k /etc/krb5.keytab.ldap ldap/tjener.intern"
    chown openldap:openldap /etc/krb5.keytab.ldap

    kadmin.local -q "addprinc -randkey imap/tjener.intern"
    kadmin.local -q "ktadd -k /etc/krb5.keytab.imap imap/tjener.intern"
    chown dovecot:dovecot /etc/krb5.keytab.imap

    kadmin.local -q "addprinc -randkey smtp/tjener.intern"
    kadmin.local -q "ktadd -k /etc/krb5.keytab.smtp smtp/tjener.intern"
    chown Debian-exim:Debian-exim /etc/krb5.keytab.smtp

    # Kerberos policy setup
    kadmin.local -q "addpol -minlength 5 users"
    kadmin.local -q "addpol -minclasses 2 hosts"
}

firstuser_post() {
    db_get debian-edu-config/first-user-name
    FIRSTUSERNAME="$RET"

    db_get debian-edu-config/first-user-password
    FIRSTUSERPWD="$RET"

    # Create home directory and add Kerberos attributes.
    # Can not use gosa-create and gosa-sync because ldapsearch in
    # these scripts is not working at this point.
    USERDN=$(awk '/dn: uid=\$FIRSTUSERNAME,/ { print $2 }' /etc/ldap/firstuser.ldif | sed "s/\$FIRSTUSERNAME/$FIRSTUSERNAME/")

    HOMEDIR=/skole/tjener/home0/$FIRSTUSERNAME
    echo "Creating $HOMEDIR" 1>&2
    cp -r /etc/skel $HOMEDIR

    # Must use uid/gid as NSS is not able to connect to LDAP yet
    UID=1000
    GID=1000
    chown -R $UID:$GID $HOMEDIR

    pwlen=$(echo -n "$FIRSTUSERPWD" | wc -c)
    echo "Creating Kerberos principal for $USERDN (password length $pwlen)"
    kadmin.local -q "add_principal -policy users -pw \"$FIRSTUSERPWD\" -x dn=$USERDN $FIRSTUSERNAME" 1>&2
    db_set debian-edu-config/first-user-password ''
    echo "First user password cleared from debconf database." 1>&2
}

## check if there is no kdc yet:
if [ -f $STASHFILE ] ; then
    echo "The stashfile $STASHFILE exists: Skipping KDC setup." 1>&2
    exit 1
fi

## check if slapd is running:
PID=`pidof slapd || /bin/true`
if [ -z "$PID" ]; then
    echo "The ldap server slapd seems not to be running. Trying to start slapd." 1>&2
    if [ -x /sbin/start-stop-daemon.REAL ] ; then
	## needed to start slapd during installation:
	mv /sbin/start-stop-daemon /sbin/start-stop-daemon.FAKE
	cp /sbin/start-stop-daemon.REAL /sbin/start-stop-daemon
    fi
    /etc/init.d/slapd start
    slapd_started=true

    # Make sure there is no race problem if kerberos try to talk to slapd
    # before it is operational.
    sleep 5
fi

PID=`pidof slapd || /bin/true`
if [ -z "$PID" ]; then
    echo "error: the ldap server is not running. Skipping KDC setup." 1>&2
    exit 1
else
    mit_kerberos
    mit_kerberos_kdc $LDAP_PW $KDC_PW

    firstuser_post || echo "error: unable to set up first LDAP user."
fi

if [ true = "$slapd_started" ] ; then
    /etc/init.d/slapd stop
    if [ -x /sbin/start-stop-daemon.REAL ] ; then
	mv /sbin/start-stop-daemon.FAKE /sbin/start-stop-daemon
    fi
fi

exit 0

