kclient.sh revision 7c478bd95313f5f23a4c958a745db2134aa03244
#!/bin/ksh -p
#
# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
# This script is used to setup the Kerberos client by
# supplying information about the Kerberos realm and kdc.
#
# The kerberos configuration file (/etc/krb5/krb5.conf) would
# be generated and local host's keytab file setup. The script
# can also optionally setup the system to do kerberized nfs and
# bringover a master krb5.conf copy from a specified location.
error_message() {
kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1
rm -f $TMP_FILE
printf "---------------------------------------------------\n"
printf "$(gettext "Setup FAILED").\n\n"
exit 1
}
cannot_create() {
typeset filename="$1"
typeset stat="$2"
if [ $stat -ne 0 ]; then
printf "\n$(gettext "Cannot create/edit %s, exiting").\n" $filename
error_message
fi
}
modify_nfssec_conf() {
if [ -r $NFSSEC_FILE ]; then
cat $NFSSEC_FILE > $NFSSEC_FILE.sav
cannot_create $NFSSEC_FILE.sav $?
fi
cat $NFSSEC_FILE > $TMP_FILE
cannot_create $TMP_FILE $?
if grep -s "#krb5" $NFSSEC_FILE > /dev/null 2>&1; then
sed "s%^#krb5%krb5%" $TMP_FILE >$NFSSEC_FILE
cannot_create $NFSSEC_FILE $?
fi
}
call_kadmin() {
typeset svc="$1"
typeset bool1 bool2 bool3 bool4
for listentry in $fqdnlist; do
# Reset conditional vars to 1
bool1=1; bool2=1; bool3=1; bool4=1
service_princ=$(echo "$svc/$listentry")
getprincsubcommand="getprinc $service_princ"
anksubcommand="addprinc -randkey $service_princ"
ktaddsubcommand="ktadd $service_princ"
kadmin -c $TMP_CCACHE -q "$getprincsubcommand" 1>$TMP_FILE 2>&1
egrep -s "get_principal: Principal does not exist" $TMP_FILE
bool1=$?
egrep -s "get_principal: Operation requires ``get" $TMP_FILE
bool2=$?
if [[ $bool1 -eq 0 || $bool2 -eq 0 ]]; then
kadmin -c $TMP_CCACHE -q "$anksubcommand" 1>$TMP_FILE 2>&1
egrep -s "add_principal: Principal or policy already exists while creating \"$service_princ@$REALM\"." $TMP_FILE
bool3=$?
egrep -s "Principal \"$service_princ@$REALM\" created." $TMP_FILE
bool4=$?
if [[ $bool3 -eq 0 || $bool4 -eq 0 ]]; then
printf "$(gettext "%s entry ADDED to KDC database").\n" $service_princ
else
cat $TMP_FILE;
printf "\n$(gettext "kadmin: add_principal of %s failed, exiting").\n" $service_princ
error_message
fi
else
printf "$(gettext "%s entry already exists in KDC database").\n" $service_princ
fi
klist -k 1>$TMP_FILE 2>&1
egrep -s "$service_princ@$REALM" $TMP_FILE
if [ $? -eq 0 ]; then
printf "$(gettext "%s entry already present in keytab").\n" $service_princ
else
kadmin -c $TMP_CCACHE -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1
egrep -s "added to keytab WRFILE:$KRB5_KEYTAB_FILE." $TMP_FILE
if [ $? -ne 0 ]; then
cat $TMP_FILE;
printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ
error_message
else
printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ
fi
fi
done
}
writeup_krb5_conf() {
printf "\n$(gettext "Setting up %s").\n" $KRB5_CONFIG_FILE
if [ -r $KRB5_CONFIG_FILE ]; then
cat $KRB5_CONFIG_FILE > $KRB5_CONFIG_FILE.sav
cannot_create $KRB5_CONFIG_FILE.sav $?
fi
exec > $KRB5_CONFIG_FILE
if [ $? -ne 0 ]; then
exec > /dev/tty
printf "\n$(gettext "Cannot write to %s, exiting").\n" $KRB5_CONFIG_FILE
error_message
fi
printf "[libdefaults]\n"
if [ "$dns_lookup" = yes ]; then
printf "\t$dnsarg = on\n"
if [ "$dnsarg" = dns_lookup_kdc ]; then
printf "\tdefault_realm = $REALM\n"
printf "\n[domain_realm]\n"
printf "\t$KDC = $REALM\n"
printf "\t$client_machine = $REALM\n"
printf "\t.$fqdn = $REALM\n\n"
else
if [ "$dnsarg" = dns_lookup_realm ]; then
printf "\n[realms]\n"
printf "\t$REALM = {\n"
printf "\t\tkdc = $KDC\n"
printf "\t\tadmin_server = $KDC\n"
printf "\t}\n\n"
else
printf "\n\n"
fi
fi
else
printf "\tdefault_realm = $REALM\n\n"
printf "[realms]\n"
printf "\t$REALM = {\n"
printf "\t\tkdc = $KDC\n"
printf "\t\tadmin_server = $KDC\n"
printf "\t}\n\n"
printf "[domain_realm]\n"
printf "\t$KDC = $REALM\n"
printf "\t$client_machine = $REALM\n"
printf "\t.$fqdn = $REALM\n\n"
fi
printf "[logging]\n"
printf "\tdefault = FILE:/var/krb5/kdc.log\n"
printf "\tkdc = FILE:/var/krb5/kdc.log\n"
#
# return output to TTY
#
exec > /dev/tty
}
ask() {
question=$1
default_answer=$2
if [ -z "$default_answer" ]; then
printf "$question :\c"
else
printf "$question [$default_answer]: \c"
fi
read answer
test -z "$answer" && answer="$default_answer"
}
yesno() {
typeset question="$1"
answer=""
while [ -z "$answer" ]; do
ask "$question" y/n
case "$answer" in
y|yes) answer=yes;;
n|no) answer=no;;
*) answer="";;
esac
done
}
query() {
yesno "$*"
if [ "$answer" = no ]; then
printf "\t$(gettext "No action performed").\n"
fi
}
read_profile() {
typeset param value
typeset file="$1"
if [[ ! -d $file && -r $file ]]; then
while read param value
do
case "$param" in
REALM) if [ -z "$REALM" ]; then
REALM="$value"
checkval="REALM"; check_value $REALM
fi
;;
KDC) if [ -z "$KDC" ]; then
KDC="$value"
checkval="KDC"; check_value $KDC
fi
;;
ADMIN) if [ -z "$ADMIN_PRINC" ]; then
ADMIN_PRINC="$value"
checkval="ADMIN_PRINC"
check_value $ADMIN_PRINC
fi
;;
FILEPATH) if [ -z "$filepath" ]; then
filepath="$value"
fi
;;
NFS) if [ -z "$add_nfs" ]; then
if [ "$value" = 1 ]; then
add_nfs=yes
else
add_nfs=no
fi
fi
;;
DNSLOOKUP) if [ -z "$dnsarg" ]; then
dnsarg="$value"
checkval="DNS_OPTIONS"
check_value $dnsarg
fi
;;
FQDN) if [ -z "$fqdnlist" ]; then
fqdnlist="$value"
checkval="FQDN"
check_value $fqdnlist
verify_fqdnlist "$fqdnlist"
fi
;;
esac
done <$file
else
printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file
error_message
fi
}
ping_check() {
typeset machine="$1"
typeset string="$2"
if ping $machine > /dev/null; then
:
else
printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine
error_message
fi
# Output timesync warning if not using a profile, i.e. in
# interactive mode.
if [[ -z "$profile" && "$string" = KDC ]]; then
# It's difficult to sync up time with KDC esp. if in a
# zone so just print a warning about KDC time sync.
printf "\n$(gettext "Note, this system and the KDC's time must be within 5 minutes of each other for Kerberos to function. Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n"
fi
}
check_value() {
typeset arg="$1"
if [ -z "$arg" ]; then
printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval
error_message
else
echo "$arg">$TMP_FILE
if egrep -s '[*$^#!]+' $TMP_FILE; then
printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval
error_message
fi
fi
}
set_dns_value() {
typeset arg="$1"
if [[ "$arg" = dns_lookup_kdc || "$arg" = dns_lookup_realm || "$arg" = dns_fallback ]]; then
dns_lookup=yes
else
arg=$(echo "$arg"|tr '[A-Z]' '[a-z]')
if [ "$arg" = none ]; then
dns_lookup=no
else
printf "\n$(gettext "Invalid DNS lookup option, exiting").\n"
error_message
fi
fi
}
verify_fqdnlist() {
integer count=1
list=$(echo "$1" | tr -d " " | tr -d "\t")
hostname=$(uname -n | tr '[A-Z]' '[a-z]' | cut -d"." -f1)
fqdnlist=$client_machine
eachfqdn=$(echo "$list" | cut -d"," -f$count)
if [ -z "$eachfqdn" ]; then
printf "\n$(gettext "If the -f option is used, atleast one FQDN should be listed").\n\n"
usage
else
while [ ! -z "$eachfqdn" ]; do
tmpvar=$(echo "$eachfqdn" | cut -d"." -f1)
if [ -z "$tmpvar" ]; then
fullhost="$hostname$eachfqdn"
else
fullhost="$hostname.$eachfqdn"
fi
ping_check $fullhost "System"
if [ "$fullhost" = "$client_machine" ]; then
:
else
fqdnlist="$fqdnlist $fullhost"
fi
if [[ "$list" == *,* ]]; then
((count = count + 1))
eachfqdn=$(echo "$list" | cut -d"," -f$count)
else
break
fi
done
fi
}
usage() {
printf "\n$(gettext "Usage: kclient [ -n ] [ -R realm ] [ -k kdc ] [ -a adminuser ] [ -c filepath ] [ -d dnsarg ] [ -f fqdn_list ] [ -p profile ]")\n\n"
printf "$(gettext "Refer kclient(1M) for details, exiting").\n"
error_message
}
###########################
# Main section #
###########################
#
# Set the Kerberos config file and some default strings/files
#
KRB5_CONFIG_FILE="/etc/krb5/krb5.conf"
KRB5_KEYTAB_FILE="/etc/krb5/krb5.keytab"
RESOLV_CONF_FILE="/etc/resolv.conf"
NFSSEC_FILE="/etc/nfssec.conf"
dns_lookup="no"
ask_fqdns="no"
checkval=""
profile=""
# Set OS release level to Solaris 10, inorder to track the requirement
# of the root/fqdn service principal for kerberized NFS.
release_level=10
if [ -x /usr/bin/mktemp ]; then
TMP_FILE=$(/usr/bin/mktemp /etc/krb5/krb5tmpfile.XXXXXX)
TMP_CCACHE=$(/usr/bin/mktemp /etc/krb5/krb5tmpccache.XXXXXX)
else
TMP_FILE="/etc/krb5/krb5tmpfile.$$"
TMP_CCACHE="/etc/krb5/krb5tmpccache.$$"
fi
if [[ -z "$TMP_FILE" || -z "$TMP_CCACHE" ]]; then
printf "\n$(gettext "Temporary file creation failed, exiting").\n" >&2
exit 1
fi
#
# If we are interrupted, cleanup after ourselves
#
trap "/usr/bin/rm -f $TMP_FILE $TMP_CCACHE; exit 1" HUP INT QUIT TERM
if [ -d /usr/bin ]; then
if [ -d /usr/sbin ]; then
PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
export PATH
else
printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2
exit 1
fi
else
printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2
exit 1
fi
printf "\n$(gettext "Starting client setup")\n\n"
printf "---------------------------------------------------\n"
#
# Check for uid 0, disallow otherwise
#
id 1>$TMP_FILE 2>&1
if [ $? -eq 0 ]; then
if egrep -s "uid=0\(root\)" $TMP_FILE; then
# uid is 0, go ahead ...
:
else
printf "\n$(gettext "Root privileges are required to run this script, exiting").\n"
error_message
fi
else
cat $TMP_FILE;
printf "\n$(gettext "uid check failed, exiting").\n"
error_message
fi
#
# Check for /etc/resolv.conf
#
if [ -r $RESOLV_CONF_FILE ]; then
while read label text
do
case "$label" in
domain) # Copy the entry into $fqdn
if [ -z "$text" ]; then
printf "\n$(gettext "DNS domain info malformed in %s, exiting").\n" $RESOLV_CONF_FILE
error_message
fi
fqdn=$(echo "$text"|tr '[A-Z]' '[a-z]')
break
;;
esac
done <$RESOLV_CONF_FILE
if [ -z "$fqdn" ]; then
printf "\n$(gettext "DNS domain info missing in %s, exiting").\n" $RESOLV_CONF_FILE
error_message
fi
else
#
# /etc/resolv.conf not present, exit ...
#
printf "\n$(gettext "%s does not exist and is required for Kerberos setup")\n" $RESOLV_CONF_FILE
printf "$(gettext "Refer resolv.conf(4), exiting").\n"
error_message
fi
client_machine=$(uname -n | tr '[A-Z]' '[a-z]' | cut -d"." -f1).$fqdn
#
# Process the command-line arguments (if any)
#
OPTIND=1
while getopts np:R:k:a:c:d:f: OPTIONS
do
case $OPTIONS in
p) options="$options -p"
profile="$OPTARG"
read_profile $profile
;;
R) options="$options -R"
REALM="$OPTARG"
checkval="REALM"; check_value $REALM
;;
k) options="$options -k"
KDC="$OPTARG"
checkval="KDC"; check_value $KDC
;;
a) options="$options -a"
ADMIN_PRINC="$OPTARG"
checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
;;
c) options="$options -c"
filepath="$OPTARG"
;;
d) options="$options -d"
dnsarg="$OPTARG"
checkval="DNS_OPTIONS"; check_value $dnsarg
;;
f) options="$options -f"
fqdnlist="$OPTARG"
verify_fqdnlist "$fqdnlist"
;;
n) options="$options -n"
add_nfs=yes
;;
\?) usage
;;
*) usage
;;
esac
done
#correct argument count after options
shift `expr $OPTIND - 1`
if [ -z "$options" ]; then
:
else
if [ $# -ne 0 ]; then
usage
fi
fi
if [ -z "$dnsarg" ]; then
query "$(gettext "Do you want to use DNS for kerberos lookups") ?"
if [ "$answer" = yes ]; then
printf "\n$(gettext "Valid DNS lookup options are dns_lookup_kdc, dns_lookup_realm\nand dns_fallback. Refer krb5.conf(4) for further details").\n"
printf "\n$(gettext "Enter required DNS option"): \c"
read dnsarg
checkval="DNS_OPTIONS"; check_value $dnsarg
set_dns_value $dnsarg
fi
else
set_dns_value $dnsarg
fi
if [ -z "$REALM" ]; then
printf "$(gettext "Enter the Kerberos realm"): \c"
read REALM
checkval="REALM"; check_value $REALM
fi
if [ -z "$KDC" ]; then
printf "$(gettext "Specify the KDC hostname for the above realm"): \c"
read KDC
checkval="KDC"; check_value $KDC
fi
REALM=$(echo "$REALM"|tr '[a-z]' '[A-Z]')
KDC=$(echo "$KDC"|tr '[A-Z]' '[a-z]')
echo "$KDC">$TMP_FILE
if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then
# do nothing, KDC is in fqdn format
:
echo "$KDC"
else
if egrep -s '\.+' $TMP_FILE; then
printf "\n$(gettext "Improper format of KDC hostname, exiting").\n"
error_message
else
# Attach fqdn to KDC, to get the Fully Qualified Domain Name
# of the KDC requested
KDC=$(echo "$KDC.$fqdn")
fi
fi
#
# Ping to see if the kdc is alive !
#
ping_check $KDC "KDC"
#
# Start writing up the krb5.conf file, save the existing one
# if already present
#
writeup_krb5_conf
#
# Done creating krb5.conf, so now we ...
#
# 1. kinit with ADMIN_PRINC
#
if [ -z "$ADMIN_PRINC" ]; then
printf "\n$(gettext "Enter the krb5 administrative principal to be used"): \c"
read ADMIN_PRINC
checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
fi
echo "$ADMIN_PRINC">$TMP_FILE
if egrep -s '\/admin' $TMP_FILE; then
# Already in "/admin" format, do nothing
:
else
if egrep -s '\/' $TMP_FILE; then
printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n"
error_message
else
ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin")
fi
fi
printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC
KDC=$(echo "$KDC"|tr '[A-Z]' '[a-z]')
kinit -c $TMP_CCACHE -S kadmin/$KDC $ADMIN_PRINC
klist -c $TMP_CCACHE 1>$TMP_FILE 2>&1
if egrep -s "Valid starting" $TMP_FILE && egrep -s "kadmin/$KDC@$REALM" $TMP_FILE; then
:
else
printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC
error_message
fi
#
# 2. Do we want to create and/or add service principal(s) for fqdn's
# other than the one listed in resolv.conf(4) ?
#
if [ -z "$options" ]; then
echo
query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $REALM ?"
ask_fqdns=$answer
if [ "$ask_fqdns" = yes ]; then
printf "$(gettext "Enter a comma-seperated list of DNS domain names"): \c"
read fqdnlist
verify_fqdnlist "$fqdnlist"
else
fqdnlist=$client_machine
fi
else
if [ -z "$fqdnlist" ]; then
fqdnlist=$client_machine
fi
fi
#
# 3. Set up keytab/config files for nfs/host/root entries (if requested)
#
echo
if [ -z "$options" ]; then
query "$(gettext "Do you plan on doing Kerberized nfs") ?"
add_nfs=$answer
fi
if [ "$add_nfs" = yes ]; then
modify_nfssec_conf
echo; call_kadmin nfs
#
# Check to see if the system is a pre-S10 system which would
# require the root/FQDN svc principal for kerberized NFS.
#
current_release=$(uname -r | cut -d"." -f2)
if [ $current_release -lt $release_level ]; then
echo; call_kadmin root
fi
fi
# Add the host entry to the keytab
echo; call_kadmin host
#
# 4. Copy over krb5.conf master copy from filepath
#
if [ -z "$options" ]; then
echo
query "$(gettext "Do you want to copy over the master krb5.conf file") ?"
if [ "$answer" = yes ]; then
printf "$(gettext "Enter the pathname of the file to be copied"): \c"
read filepath
fi
fi
if [ -z "$filepath" ]; then
:
else
if [ -r $filepath ]; then
cp $filepath $KRB5_CONFIG_FILE
if [ $? -eq 0 ]; then
printf "\n$(gettext "Copied %s").\n" $filepath
else
printf "\n$(gettext "Copy of %s failed, exiting").\n" $filepath
error_message
fi
else
printf "\n$(gettext "%s not found, exiting").\n" $filepath
error_message
fi
fi
printf "\n---------------------------------------------------\n"
printf "$(gettext "Setup COMPLETE").\n"
#
# 5. Cleanup, please !
#
kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1
rm -f $TMP_FILE
exit 0