#!/bin/ksh
#
# Copyright (c) 1999-2000 by Sun Microsystems, Inc.
# All rights reserved.
#
#pragma ident "@(#)CDESmartCardAdmin.sh	1.4 00/09/14 SMI"
#
#
# This script enables and disables smart card usage for CDE
# (dtlogin and dtsession). It modifies CDE X properties to
# add or remove properties that specify whether or not to use
# smart cards, and it edits /etc/pam.conf to add or remove
# pam_smartcard.so actions.
#

BASENAME="/bin/basename"
     MOD="`$BASENAME $0`"
 DIRNAME="/bin/dirname"

EGREP="/bin/egrep"
   CP="/bin/cp"
   MV="/bin/mv"
  CAT="/bin/cat"
   RM="/bin/rm"
MKDIR="/bin/mkdir"
  AWK="/bin/awk"
  SED="/bin/sed"
 GREP="/bin/grep"

   # override these variables for debugging
   SCETC="/etc"
   SCUSR="/usr"
   SCTMP="/var/run"

     TMP="$SCTMP/$MOD.$$"
 TMP_CDE="$TMP.CDEsmartcard"
TMP_CONF="$TMP.pam.conf"

# CDE application names in pam.conf
  DTLOGIN="dtlogin"
DTSESSION="dtsession"

  SMARTCARDDIR="$SCETC/smartcard"
      PAM_CONF="$SCETC/pam.conf"
SAVED_PAM_CONF="$SMARTCARDDIR/$PAM_CONF"

CDE_ETC="$SCETC"
CDE_USR="$SCUSR"

  DTLOGIN_PROPS_FILE="dt/config/Xconfig"
DTSESSION_PROPS_FILE="dt/app-defaults/C/Dtsession"

  DTLOGIN_USESC_PROPNAME="Dtlogin.useSmartCard"
DTSESSION_USESC_PROPNAME="Dtsession.useSmartCard"

# note single quotes so that $ISA doesn't get expanded by the shell
  SCLIB='/usr/lib/security/$ISA/pam_smartcard.so.1'
SCLIBso="`$BASENAME $SCLIB | awk -F. '{print $1\".\"$2}'`"
UNIXLIB='/usr/lib/security/$ISA/pam_unix.so.1'

#
# Check if the /etc versions of the dtlogin and dtsession proeprties
# files exist - if they don't copy them from /usr to /etc
# If the CDE files in /usr don't exist and we need to copy them to /etc,
# then bail out. Note that it's possible to have only the /etc files
# and not the /usr files.
#
function check_CDEetc_file {

    FNAME="$1"

    if test ! -f $CDE_ETC/$FNAME ; then
	if test ! -f $CDE_USR/$FNAME ; then
	    echo "$MOD: unable to read $CDE_USR/$FNAME"
	    exit 1
	fi
	$MKDIR -p `$DIRNAME $CDE_ETC/$FNAME`
	$CP -frp $CDE_USR/$FNAME $CDE_ETC/$FNAME
    fi

}

#
# Update the smart card properties in CDE files.
#
#    Usage: update_SCprop {propertyname} {propfile}
#
function update_SCprop {

    PROPNAME="$1"
    PROPFILE="$2"
    FLAG="$3"
    sc_prop="$PROPNAME: $FLAG"
    line_no=""

    $GREP -ns "^#[	 ]*$PROPNAME:" $PROPFILE > /dev/null 2>&1
    if [ $? -eq 0 ] ; then
	$GREP -ns "^#[	 ]*$PROPNAME:" $PROPFILE > /var/tmp/tmp.$$
	line_no="`cat /var/tmp/tmp.$$ | tail -1 | awk -F: '{print $1}'`"
	$RM -f /var/tmp/tmp.$$
    fi

    $GREP -ns "^!#[	 ]*$PROPNAME:" $PROPFILE > /dev/null 2>&1
    if [ $? -eq 0 ] ; then
	$GREP -ns "^!#[	 ]*$PROPNAME:" $PROPFILE > /var/tmp/tmp.$$
	line_no="`cat /var/tmp/tmp.$$ | tail -1 | awk -F: '{print $1}'`"
	$RM -f /var/tmp/tmp.$$
    fi

    $GREP -ns "^[	 ]*$PROPNAME" $PROPFILE > /dev/null 2>&1
    if [ $? -eq 0 ] ; then
	$GREP -ns "^[	 ]*$PROPNAME" $PROPFILE > /var/tmp/tmp.$$
	line_no="`cat /var/tmp/tmp.$$ | tail -1 | awk -F: '{print $1}'`"
	$RM -f /var/tmp/tmp.$$
    fi

    if [ -n "$line_no" ]
	then
	    $SED -e "$line_no c\\
$sc_prop" $PROPFILE > /var/tmp/tmp.$$
	    $CP /var/tmp/tmp.$$ $PROPFILE
	    $RM -f /var/tmp/tmp.$$
	else
	    echo "$PROPNAME property not found"
	    echo "$sc_prop" >> $PROPFILE
    fi

}

#
# Enable smart card usage for CDE; remove any stale smart card
# properties from CDE's X properties and replace them with the
# properties to activate smart card usage
#
function add_sc_to_CDE {

    # check if /etc versions of files exist; if not, create them
    check_CDEetc_file $DTLOGIN_PROPS_FILE
    check_CDEetc_file $DTSESSION_PROPS_FILE

    update_SCprop $DTLOGIN_USESC_PROPNAME $CDE_ETC/$DTLOGIN_PROPS_FILE True
    update_SCprop $DTSESSION_USESC_PROPNAME $CDE_ETC/$DTSESSION_PROPS_FILE true

}

#
# Disable smart card usage for CDE; replace them with
# properties to deactivate smartcard usage
#
function remove_sc_from_CDE {

    # check if /etc versions of files exist; if not, create them
    check_CDEetc_file $DTLOGIN_PROPS_FILE
    check_CDEetc_file $DTSESSION_PROPS_FILE

    update_SCprop $DTLOGIN_USESC_PROPNAME $CDE_ETC/$DTLOGIN_PROPS_FILE False
    update_SCprop $DTSESSION_USESC_PROPNAME $CDE_ETC/$DTSESSION_PROPS_FILE false

}

#
# Remove smart card properties from CDE files. Do the right thing
# with the /usr and /etc file cases.
#
#    Usage: remove_sc_from_CDEfile {filename} {propertyname}
#
function remove_sc_from_CDEfile {

    FNAME="$1"
    PROPNAME="$2"

    #
    # If we're being asked to remove smart card entries but there
    # is no corresponding /etc file then check the /usr file; if
    # no smart card entries exist in that file, then don't do
    # anything.
    #
    if test ! -f $CDE_ETC/$FNAME ; then
	#
	# If we find the property in the /usr file, then create an /etc
	# version of that file and remove the property from that file.
	# We do it this way since we're not really supposed to edit
	# the /usr version of this file.
	#
	if $EGREP -is $PROPNAME $CDE_USR/$FNAME ; then
	    check_CDEetc_file $FNAME
	    remove_CDE_prop $CDE_ETC/$FNAME $PROPNAME
	fi
    #
    # There is an /etc version of this file, now check it for
    # the smart card property and remove the property if it
    # is present.
    #
    else
	remove_CDE_prop $CDE_ETC/$FNAME $PROPNAME
    fi

}

#
# Remove the CDE property from the CDE property file.
#
#    Usage: remove_CDE_prop {filename} {propertyname}
#
function remove_CDE_prop {

    FNAME="$1"
    PROPNAME="$2"

    $EGREP -iv $PROPNAME $FNAME >$TMP_CDE
    $MV $TMP_CDE $FNAME

}

#
# Remove the appname + property from the passed file.
#
#    Usage: remove_PAM_prop {filename} {appname} [{propertyname}]
#
#		{propertyname} is optional, and if not set, then
#		all instances of {appname} will be removed
#
function remove_PAM_prop {

    FNAME="$1"
    APPNAME="$2"
    PROPNAME="$3"

    #
    # Note that it's OK to pass an empty $PROPNAME - that causes
    # the awk script to match only on $APPNAME
    #
    $AWK "{
	if (!(\$1 == \"$APPNAME\" && index(\$4, \"$PROPNAME\") > 0))
	    printf( \$0 \"\n\" );
	}" $FNAME >$TMP_CDE

    $MV $TMP_CDE $FNAME

}

#
# Enable smart card authentication for CDE.
#
function add_sc_to_PAM {

    # Remove any existing smart card authentication
    remove_sc_from_PAM

    REQ="required"

    for CDEPROG in $DTLOGIN $DTSESSION
      do
	for TYPE in auth account session password
	  do
	    echo "$CDEPROG\t$TYPE\t$REQ\t$SCLIB" >>$PAM_CONF
	  done
      done

}

#
# Remove smart card authentication for CDE from $PAM_CONF
#
function remove_sc_from_PAM {

    remove_PAM_prop $PAM_CONF $DTLOGIN `$BASENAME $SCLIBso`
    remove_PAM_prop $PAM_CONF $DTSESSION `$BASENAME $SCLIBso`

}

#
# Restore $PAM_CONF to the state it was in before we enabled
# smart card authentication for CDE.
#
function restore_PAM {

    $CAT $SAVED_PAM_CONF >>$PAM_CONF

    # XXX Do we really need to remove the saved file?
    # $RM -f $SAVED_PAM_CONF

}

#
# Restore the CDE entries in $PAM_CONF to reasonable defaults.
# These defaults are to use pam_unix.so for dtlogin and
# dtsession. The 'requisite' entry for dtlogin to use
# pam_roles.so is also added.
#
function restore_PAM_CDE_to_defaults {

    REQ="required"

    for CDEPROG in $DTLOGIN $DTSESSION
      do
	for TYPE in auth account session password
	  do
	    if [ "$CDEPROG" = "$DTLOGIN" -a "$TYPE" = "account" ] ; then
		REQ1="requisite"
		echo "$CDEPROG\t$TYPE\t$REQ1\t$UNIXLIB" >>$PAM_CONF
	    fi
	    echo "$CDEPROG\t$TYPE\t$REQ\t$UNIXLIB" >>$PAM_CONF
	  done
      done

}

#
# Remove all uncommented CDE entries from $PAM_CONF and save
# them in the passed file.
#
#    Usage: remove_CDE_from_PAM {savefilespec}
#
function remove_CDE_from_PAM {

    FNAME="$1"

    if [ "$FNAME" != "" ] ; then
	$MKDIR -p "`$DIRNAME $FNAME`"
	$EGREP -v '#' $PAM_CONF | $EGREP -i "$DTLOGIN|$DTSESSION" >$FNAME
    fi

    remove_PAM_prop $PAM_CONF $DTLOGIN
    remove_PAM_prop $PAM_CONF $DTSESSION

}

########################################################################
#                                                                      #
#			Main code starts here.                         #
#                                                                      #
########################################################################

    #
    # Check for some files and directories that we must have
    # to make this all work.
    #
    if test ! -f $PAM_CONF ; then
	echo "$MOD: $PAM_CONF does not exist"
	exit 1
    fi

    if test ! -d $SMARTCARDDIR ; then
	echo "$MOD: $SMARTCARDDIR dir doesn't exist!"
	exit 1
    fi

    #
    # If we're being asked to enable smart card usage for CDE then
    # check to see if it is already enabled.
    #
    if [ "$1" = "-e" ] ; then

	#
	# CDE is considered enabled for smart card authentication
	# if the token $SCLIBso is in $PAM_CONF
	#
	if $EGREP -is $SCLIBso $PAM_CONF ; then
	    exit 0
	fi

	#
	# smart card is not enabled for CDE so do some cleanup
	# before we enable it:
	#
	#    - remove $SAVED_PAM_CONF, the saved $PAM_CONF state
	#	file, since it's contents are no longer valid
	#    - remove all un-commented CDE entries in $PAM_CONF
	#	and save them in a new version of $SAVED_PAM_CONF
	#	that has our current $PAM_CONF entries for CDE
	#
	$RM -f $SAVED_PAM_CONF
	remove_CDE_from_PAM $SAVED_PAM_CONF

	# enable smart card authentication for CDE in $PAM_CONF
	add_sc_to_PAM

	# enable smart card in CDE X properties
	add_sc_to_CDE

    fi

    #
    # If we're being asked to disable smart card usage for CDE then
    # check to see if it is already disabled.
    #
    if [ "$1" = "-d" ] ; then

	#
	# CDE is considered disabled for smart card authentication
	# if the token $SCLIBso is not in $PAM_CONF
	#
	if ! $EGREP -is $SCLIBso $PAM_CONF ; then
	    exit 0
	fi

	# remove all un-commented CDE entries in $PAM_CONF
	remove_CDE_from_PAM

	#
	# If the file $SAVED_PAM_CONF exists, that is assumed to be
	# the file that contains the $PAM_CONF entries for CDE, so
	# restore them. If that file deos not exist, restore the CDE
	# entries in $PAM_CONF to a reasonable set of defaults.
	#
	if test -f $SAVED_PAM_CONF ; then
	    restore_PAM
	else
	    restore_PAM_CDE_to_defaults
	fi

	# disable smart card in CDE X properties
	remove_sc_from_CDE

    fi

    #
    # Restore $PAM_CONF to reasonable defaults.
    #
    if [ "$1" = "-pamdefault" ] ; then
	restore_PAM_CDE_to_defaults
    fi

    exit 0
