#!/bin/ksh -p
#
# ident "@(#)utgenpolicy.sh	1.19 04/08/18 SMI"
#
# Copyright 2002-2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

# This script takes policy options and writes the policy stack to
# /etc/opt/SUNWut/policy/utpolicy and also updated auth.props to specify
# which file should be used for policy which in this case would be
# /etc/opt/SUNWut/policy/utpolicy.
#
# NOTE: As this file is not meant to be run as an admin tool from the
# command line, this script will not check for proper/correct policies!  
# This should be done at the admin level.

# Explicitly set umask so that files get created with correct permissions
umask 022

#exec > /tmp/utgenpolicy.$$ 2>&1
#set -x

PROG=$0

PATH=/bin:/usr/bin
export PATH

BASEDIR=/etc/opt/SUNWut/basedir
SUNWUT=`${BASEDIR}/lib/utprodinfo -r SUNWuto 2>/dev/null`/SUNWut
SUNWUTLIB=$SUNWUT/lib
SUNWUTSBIN=$SUNWUT/sbin
SUNWUTETC=/etc/opt/SUNWut
SUNWUTTOKENS=/var/opt/SUNWut/tokens

## This file is created by the command /opt/SUNWbb/bin/bbmkuser
USER_CONF=/var/opt/SUNWbb/users.conf

# Accessing TIDLIST fails on linux if enable set -u
# set -u

# Directory where policy files reside
POLICYDIR=$SUNWUTETC/policy

# Default policy file after policy is set
# Before utconfig is run, policy file is set to ZeroAdmin
POLICYFILE=$POLICYDIR/utpolicy
# This property file has an entry
# policy = XXXXX, where XXXX is the policy file to be used
# The default is "utpolicy" and therefore is not present in the file
# But, if policy is ZeroAdmin or RegisterDistributed, you'll see a line
# policy = ZeroAdmin or policy = RegisterDistributed
# in the file 
AUTHPROPS=$SUNWUTETC/auth.props
# This configuration file has information regarding security policy.
SECURITYCONF=$SUNWUTETC/security.conf
#
# If we are creating a policy and the user didn't direct us
# to ignore the admin database, then use the admin database.
#
POLICY=""

TIDCONFIG=""
TIDINIT=false
TIDCLEAR=false
BOGUSREMOVE=false
UPDATETIDLIST=false;
# TIDLIST=""
DOWNCASEENA=true
SRPASSWORD=true

# Somehow in 1.3 we have wound up in the unfortunate situation of
# using the term "logical token" to mean two things - it used to only
# mean the registered form of a raw token which is stored in our DB,
# but now it also means tokens which have been translated by policy
# (including the old "logical tokens").	 In utpolicy I will use the
# term "translated tokens" for translated tokens to differentiate them
# from the pre-existing "logical tokens" in the DB.
#
# We only need this list until we properly introduce the "card" type,
# so we don't have to reject everything else when we really wanted
# only to accept cards, so this code should go away. XXXRAD
REJECTTRANSLATEDTOKENS="-r user -r mobile -r asc -r auth -r escape"

#
# Set default values
#
TEST=false
UNREASONABLE=false
APPLY=false
SERVERSELECT=false
TERMINALGROUP=false
POLICYSET=false
MOBILE=false
EXITENABLE=true
ASC=false
ASCCARDS=""
ACCEPTASCCARDS=""
REJECTASCCARDS=""
REGISTERED=false

AWK=/usr/bin/awk

# If BEN is true, w/o -a flag one can still check the validity of the 
# commandline options
BEN=false
#
# Functions
#

#
# Command syntax
#


function Error {
	typeset ecode=${1:-99}
	typeset message=${2:-}
	if [[ -n "$message" ]]
	then
		print -u2 -- ERROR: $message
	fi
	exit $ecode
}

function gettidconfig {
  typeset policyfile=${1:-}
  typeset tidconfig=""
  if [[ -r $policyfile ]]
  then
    tidconfig=`$AWK '/^TerminalId/ ' $policyfile | $AWK '{for (i = 0; i < NF; i++) { if ($i == "-c") print $++i}}'`
  fi
  print $tidconfig
}

function getcurrentpolicyfile {
	typeset policyfile=""
	policyfile=$POLICYDIR/$(sed -n '
		s/^[ 	]*//
		s/[ 	]*$//
		s/#.*$//
		s/[ 	][ 	]*=[ 	][ 	]*/=/
		s/^policy=\(.*\)$/\1/p
		' $AUTHPROPS)
	print $policyfile
}

function showcurrentpolicy {
	typeset pd=""
	typeset policyfile=""
	typeset asccards=""

	policyfile=$(getcurrentpolicyfile)


	print -- "# Reading policy file: $policyfile"
	if [[ ! -r $policyfile || ! -f $policyfile ]]
        then
                Error 1 "Policyfile is not readable"
        fi
	pd=$(policy2pd $policyfile)
	print -- "# Current Policy:"
	print -n -- "$PROG -a"

	print -- "$pd" | while read line
	do
		case $line in
		(*=db)
			what=${line%=*}
			print -n -- " -r $what"
			;;
		(*=register)
			what=${line%=*}
			print -n -- " -s $what"
			;;
		(*=kiosk)
			what=${line%=*}
			print -n -- " -k $what"
			;;
		(mobile=true)
			print -n " -M"
			;;
		(asc=*)
			asccards=${line#asc=}
			print -n " -S '$asccards'"
			;;
		(*=default)
			what=${line%=*}
			print -n -- " -z $what"
			;;
		(terminals=*)
			terminalsFile=${line#terminals=}
			print -n -- " -t clear"
			cat $terminalsFile | while read line
			do
				case $line in
				(terminalId=*)
					terminal=${line#terminalId=}
					print -n -- " -t add:$terminal"
					;;
				(*)
					;;
				esac
			done
			;;
		(requirepw=*)
			if ! ${line#*=}
			then
				print -n " -p"
			fi
			;;
		(pseudo=insertcard)
			;;
		(serverselect=true)
			print -n " -g"
			;;
		(terminalgroup=true)
			print -n " -m"
			;;
		(*)
			print -u2 -- "\nInvalid policy parameter: $line"
			;;
		esac
	done
	if [[ ${EXITENABLE} == "false" ]]
	then
		print -n " -d"
	fi
	print
}



#
# tidinit - initialize the list of terminals from /etc/opt/SUNWut/terminals
#
function tidinit {
	TIDCONFIG=$(gettidconfig $POLICYFILE)
        if [ -z $TIDCONFIG ]
	then
	  TIDCONFIG=$SUNWUTETC/terminals
	fi
	typeset tidfile=${1:-$TIDCONFIG}
	typeset tid_allow=""
	typeset tid_annotateTokens=""
	typeset tid_claim=""
	typeset tid_session=""
	typeset tid_terminalId=""
	typeset tid_block=false

	if $TIDINIT
	then
		return
	fi
	TIDINIT=true
	unset TIDLIST
	if [ ! -f $TIDCONFIG ]
	then
		return
	fi
	# read current configuration while deleting comment lines
	sed '
	    s/^[ 	][ 	]*//
	    s/[ 	][ 	]*$//
	    s/[ 	][ 	]*=[ 	][ 	]*/=/
	    /^#/d
	    ' $tidfile |
	while read line
	do
		case $line in
		(begin)
			tid_block=true
			;;
		(allow=*)
			tid_allow="${line#*=}";
			;;
		(annotateTokens=*)
			tid_annotateTokens="${line#*=}";
			;;
		(claim=*)
			tid_claim="${line#*=}";
			;;
		(session=*)
			tid_session="${line#*=}";
			;;
		(terminalId=*)
			tid_terminalId="${line#*=}";
			if ! $tid_block
			then
				tidadd $tid_terminalId
				tid_terminalId=""
				tid_allow=""
				tid_annotateTokens=""
				tid_claim=""
				tid_session=""
				tid_block=false
			fi
			;;
		(end)
			tidadd $tid_terminalId $tid_allow $tid_annotateTokens $tid_claim $tid_session
			tid_terminalId=""
			tid_allow=""
			tid_annotateTokens=""
			tid_claim=""
			tid_session=""
			tid_block=false
			;;
		esac
	done
}


# tidlist - print the current list of terminal ids
function tidlist {
	tidinit
	typeset -i i=0
	typeset -i limit=${#TIDLIST[@]}
	typeset saveIFS="$IFS"

	# if policy file contains TerminalId line, then read the terminalid file. o/w there
	# should be no terminalid listed
	
	if ! grep TerminalId $POLICYFILE > /dev/null
	then
	     return 1
	fi

	while [[ $i -lt $limit ]]
	do
		if [[ -n "${TIDLIST[$i]}" ]]
		then
			IFS=:
			set ${TIDLIST[$i]}
			IFS="${saveIFS}"
			print $1
		fi
		i=i+1	# no let statement needed since "typeset -i" is used
	done

}


# tidclear - clear the current list of terminal ids
function tidclear {
	unset TIDLIST
	TIDINIT=true
	TIDCLEAR=true

        # clear the terminals file
        if [ -z $TIDCONFIG ]
        then
          TIDCONFIG=$SUNWUTETC/terminals
        fi

        echo >> $TIDCONFIG

}


# tidadd tid [allow claim session annotate]
# Default to "IEEE802" if model label is missing from the terminalId
# All terminalIDs formed via gui will have a model prefix, 
# however the  model label is not mandatory since the  auth manager
# will be matching against the $ena (currently mac addr) part of the TerminalID
function tidadd {
	tidinit
	typeset tid_macADD="$1"
	typeset tid="$1"
	typeset allow="${2:-}"		# optional
	typeset claim="${3:-}"		# optional
	typeset session="${4:-}"	# optional
	typeset annotate="${5:-}"	# optional
	typeset HEX="0-9a-f"

	case $tid in
	\\*)
		# literal, just strip off the leading backslash
		tid=${tid#\\}
		DOWNCASEENA=false;
		;;
	*.*)
		# A fully qualified tid is fine
		;;
	*:*)        # Tid cannot have ":" in it because it will be used as a field separator.
                        Error 16 "Terminal ID cannot contain ':'. Please check your input."
		;;
	*)
		tid="IEEE802.$tid"
		;;
	esac
	if $DOWNCASEENA
	then
		case $tid in
		*.*)
			typeset model=${tid%.*}		# XXX
			typeset -l ena=${tid#*.}	# XXX
			#error if ena part of tid is not 12 hex digits ( a mac address ) 
		        [[ ${#ena} -ne 12  || ${ena} != +([$HEX]) ]] && 
			   Error 20 "Terminal ID '$model.$ena' is improperly formed. Please check your input."
				
			tid=$model.$ena			# XXX
			tid_macAdd=$ena	
			;;
		*)
			;;
		esac
	fi

	typeset tokenspec="$tid:$allow:$claim:$session:$annotate"
	typeset tokenspec2="$tid_macAdd:$allow:$claim:$session:$annotate"

	#
	# Check for duplicates
	#
	typeset -i limit=${#TIDLIST[@]}
	typeset -i i=0
	while [[ $i -lt $limit ]]
	do
		typeset -l tidlist_macAdd=${TIDLIST[$i]#*.}
		if [[ "$tidlist_macAdd" == "$tokenspec2" ]]
		then
			print -u2 "Warning: duplicate terminal id \"$tid\" ignored"
			return 0
		fi
		((i+=1))
	done

	# Append the new entry to the indexed array
	TIDLIST[${#TIDLIST[@]}]="$tid:$allow:$claim:$session:$annotate"
	return 0
}


# tiddel - remove a terminal id from the list of terminal ids
function tiddel {
	tidinit
	typeset tid=$1
	typeset tid_macAdd=$1
	typeset -i i=0
	typeset -i limit=${#TIDLIST[@]}
	typeset saveIFS="$IFS"
	case $tid in
	\\*)
		# literal, just strip off the leading backslash
		tid=${tid#\\}
		DOWNCASEENA=false;
		;;
	*.*)
		# A fully qualified tid is fine
		;;
	*)
		tid="IEEE802.$tid"
		;;
	esac
	if $DOWNCASEENA
	then
		case $tid in
		*.*)
			typeset model=${tid%.*}		# XXX
			typeset -l ena=${tid#*.}	# XXX
			tid=$model.$ena			# XXX
			tid_macAdd=$ena
			;;
		*)
			;;
		esac
	fi

	typeset -i removed=0

	while [[ $i -lt $limit ]]
	do
		IFS=:
		set ${TIDLIST[$i]}
		IFS="${saveIFS}"
		typeset -l tidlist_macAdd=${1#*.}
		if [[ "$tidlist_macAdd" == "$tid_macAdd" || "$1" == "${ena:-NOENA}" ]] 
		then
			# delete the entry by copying the last entry over it
			TIDLIST[$i]=${TIDLIST[$((${#TIDLIST[@]} - 1))]}
			# and then unsetting the last entry
			unset TIDLIST[$((${#TIDLIST[@]} - 1))]
			removed=removed+1
			return 0
		fi
		i=i+1	# no let statement needed since "typeset -i" is used
	done

	return 1
}


# tidsave - write the configuration for the TerminalId authentication module
function tidsave {
	typeset -i i=0
	typeset -i limit=${#TIDLIST[@]}
	typeset tid
	typeset allow
	typeset claim
	typeset session
	typeset annotate
	typeset saveIFS="$IFS"

	if [[ ${#TIDLIST} -eq 0 ]]
	then
		return 0
	fi
	while [[ $i -lt $limit ]]
	do
		IFS=:
		set ${TIDLIST[$i]}
		IFS="${saveIFS}"
		tid=$1
		allow=${2:-}
		claim=${3:-}
		session=${4:-}
		annotate=${5:-}
		if [[ -n "${allow}" || -n "${claim}" || -n "${session}" ||
		    -n "${annotate}" ]]
		then
			print begin
			print terminalId=$tid
			[[ -n "${allow}" ]] &&
				print allow=${allow}
			[[ -n "${claim}" ]] &&
				print claim=${claim}
			[[ -n "${session}" ]] &&
				print session=${session}
			[[ -n "${annotate}" ]] &&
				print annotateTokens=${annotate}
			print end
		else
			print terminalId=$tid
		fi

		i=$i+1
	done
}
# 
# Helper function to pd2policy
# Remove comma from the key=value pairs
# Example of $1 passed in: card=db,requirepw=false,card=register,pseudo=default
#
function removeComma {
           typeset line=$1

           print -- $line | sed -e 's/[ 	][ 	]*//g' -e 's/,/ /g'
}
#
# Translate from a single line policy description to the multi-line
# format required by the Corona authentication manager
#
# Example of $1 passed in: card=db,requirepw=false,card=register,pseudo=default
#            filter: card=db
#            token: card
#            action db
#
# Warning: This function relies on the fact that the input policy
# description will always have "register" before db.
function pd2policy {
	typeset filter=""
	typeset action=""
	typeset token=""
	typeset flags=""
	typeset module=""
	typeset instance=0
	typeset CAUGHTCARD=false
	typeset CAUGHTPSEUDO=false
	typeset USEDPSEUDO=false;
	typeset USEDCARD=false;
	typeset selfregtoken="";
	typeset barrier="-s noentry";
	typeset bothenabled=""
	typeset pseudoenabled=""

	if [[ -z "${1:-}" ]]
	then
		# XXX should set an error string and use it in caller
		print -u2 Empty Policy
		return 1
	fi

	if [[ ${TERMINALGROUP} == true ]]
	then
		print TerminalGroup.m${instance}
		((instance+=1))
	fi
	if [[ ${#TIDLIST} -gt 0 ]]
	then
                 # if "both=" is defined in the arg, or "pseudo=" is defined,
                 # then do not set token reader to display token reader icon

                 echo $1 | grep "both=" > /dev/null
                 bothenabled=$?

                 echo $1 | grep "pseudo=" > /dev/null
                 pseudoenabled=$?

                 if [[ $bothenabled == 0 || $pseudoenabled == 0 ]] then
                     print TerminalId.m${instance} -c $TIDCONFIG
                 else
                     print TerminalId.m${instance} -s tokenreader -c $TIDCONFIG
                 fi
                ((instance+=1))
	fi
	print StartSession.m${instance} -t raw -a unknown -s carderror
	((instance+=1))
	# Set positional parameters: key=value [key=value [key=value]]
	set $(removeComma $1)
	for filter
	do

		flags=""
		token=${filter%=*}	     # key   (LHS)
		action=${filter#*=}	     # value (RHS)
		#
		# When -p is specified in the policy file PFILE, we
		# get a filter like requirepw=false.  The following case
		# statment will have a case of action=false, which
		# writes a ZeroAdmin module to utpolicy file like
		# ZeroAdmin.m2 -s false, but there is no such thing as
		# "false session!"  also the correct session type
		# -s register -p is never written to utpolicy file.
		# This only happens if the -f pfile (reading policy
		# from a file) is used.  but it does not happen with
		# -p just specified on the commandline. So, the
		# following if-stmt is a hack that will work around
		# this problem.
		#
		if [ "$token" == "requirepw" ]
		then
			SRPASSWORD=$action
			continue
		fi

		#
		# Certain policies stand alone, such as
		# -t, -M, and -o.  Skip their processing here,
		# they are handled elsewhere.
		case $token in
		(terminals|mobile|asc)
			continue
			;;
		esac

		case $action in
		(db)
			# In this case the module name is merely a
			# placeholder since we need to eject
			# ServerSelect first.  The module Registered
			# no longer exists.
			module=Registered
			;;
		(default)
			# In this case the module name is merely a
			# placeholder since we need to eject
			# ServerSelect first.  The module ZeroAdmin
			# no longer exists.
			module=ZeroAdmin
			;;
		(register)
			module=Registeredxlation
			flags="-n -s $action"
			if ! $SRPASSWORD
			then
				flags="$flags -p"
			fi
			;;
		(*)	# Session type
			print -u2 "Internal Error: Unknown session type"
			;;
		esac

		case $token in
		(card)
			flags="-r pseudo $flags"
			if [[ $module != Registered ]]
			then
				CAUGHTCARD=true
			fi
			USEDCARD=true;
			if [[ $module == Registeredxlation ]]
			then
				flags="$REJECTTRANSLATEDTOKENS $flags"
			fi
			;;
		(pseudo)
			flags="-a pseudo $flags"
			if [[ $module != Registered ]]
			then
				CAUGHTPSEUDO=true
			fi
			USEDPSEUDO=true;
			;;
		(both)
			flags="-a default $flags"
			if [[ $module != Registered ]]
			then
				CAUGHTCARD=true
				CAUGHTPSEUDO=true
			fi
			USEDPSEUDO=true;
			USEDCARD=true;
			if [[ $module == Registeredxlation ]]
			then
				flags="$REJECTTRANSLATEDTOKENS $flags"
			fi
			;;
		esac
		case $module in
		(Registered)
			# Place a barrier for unregistered things
			# policy says must be registered.
			# Self-registration provides a barrier
			# (registered) naturally so optimize out this
			# barrier if SR covered it already.  See
			# barrier setting in Registeredxlation case.
			if [[ $selfregtoken != $token ]]
			then
				case $token in
				(pseudo)
					flags="-a pseudo"
					CAUGHTPSEUDO=true
					;;
				(card)
					flags="-r pseudo"
					CAUGHTCARD=true
					;;
				(both)
					flags="-a default"
					CAUGHTCARD=true
					CAUGHTPSEUDO=true
					;;
				esac
				print Registeredxlation.m${instance} "$REJECTTRANSLATEDTOKENS" $flags -n $barrier
				((instance+=1))
			fi
			;;
		(Registeredxlation)
			selfregtoken=$token
			# Set the barrier type we might need if user is
			# requiring registration for more than he is
			# allowing to self-register.  Will be used
			# later when processing the db flag.
			case $selfregtoken in
			(pseudo)
				barrier="-s noentry"
				;;
			(card)
				barrier="-s insertcard"
				;;
			esac
			print -- Registeredxlation.m$instance $flags
			((instance+=1))
			;;
		(ZeroAdmin)
			;;
		(*)
			print -u2 "Internal Error: unknown module $module"
			;;
		esac
	done

	guline=""
	authline=""
	# put gulogin policy 
	if [[ ${MOBILE} == true ]]
	then
	        guline="StartxlationSession.m${instance} -t raw -a pseudo"
	fi
	
	if [[ ${ASC} == true ]]
	then
		if [[ -z ${guline} ]]
		then
		    guline="StartxlationSession.m${instance} -t raw $ACCEPTASCCARDS"
		else
		    guline="$guline  $ACCEPTASCCARDS"
		fi
	fi

	if [[ ! -z ${guline} ]]
	then
		print $guline -c username -s gulogin
		((instance+=1))
	fi
	
	# put Authxlation line
	if [[ ${MOBILE} == true ]]
	then
		authline="Authxlation.m${instance} -a pseudo"
	fi
	
	if [[ ${ASC} == true ]]
	then
		if [[ -z ${authline} ]]
		then
		    authline="Authxlation.m${instance} $ACCEPTASCCARDS"
		else
		    authline="$authline $ACCEPTASCCARDS"
		fi
	fi
		    
        # If registered policy is enabled, then authxlation should
        # tranlsate the "user." token as well, for registered terminals and
        # registered authenticated smartcards.
        # fixes bugid 4849646
        if [[ ${MOBILE} == true || ${ASC} == true && ${REGISTERED} == true ]]
        then
            authline="$authline -a user"
        fi
		    
	if [[ ! -z ${authline} ]]
	then
		print $authline
		((instance+=1))
	fi

	if [[ ${SERVERSELECT} == true ]]
	then
		print ServerSelect.m${instance}
		((instance+=1))
	fi


	#put nsclogin, sclogin policies
	if [[ ${MOBILE} == true ]]
	then
		# Set a catch clause for escape sessions, otherwise they fall into 
		# the nscmlogin policy, as raw token is still pseudo for these.
	        print StartSession.m${instance} -a escape
		((instance+=1))

		print StartxlationSession.m${instance} -t raw -T raw -a pseudo -o mobile -s nsclogin 
		((instance+=1))
		USEDPSEUDO=true
		CAUGHTPSEUDO=true

		# set the property in ldap using utprop cli.
		${SUNWUTLIB}/utprop -a -f ExitEnable ${EXITENABLE}		
	fi

	if [[ ${ASC} == true ]]
	then
		print StartxlationSession.m${instance} -t raw -T raw $ACCEPTASCCARDS  -o asc -s sclogin
		((instance+=1))
		USEDCARD=true
	fi

	# put some barrier policies at the end of the file to create
	# "error sessions" for token types which don't have applicable
	# policy.
	case $CAUGHTPSEUDO.$CAUGHTCARD in
	(true.true)
		# Everything is always caught, no additional policy is needed
		;;
	(false.false)
		#
		if [[ ${ASC} == true ]]
		then
			REJECTLIST="$REJECTASCCARDS"
		else
			REJECTLIST=""
		fi
		case $USEDCARD.$USEDPSEUDO in
		(true.true|true.false)
			print StartSession.m$instance -t raw -T raw -a pseudo -s insertcard
			((instance+=1))
			print StartSession.m$instance -t raw -T raw -r pseudo $REJECTLIST -s noentry
			((instance+=1))
			;;
		(false.true)
			print StartSession.m$instance -t raw -T raw $REJECTLIST -a default -s noentry
			((instance+=1))
		(false.false)
			# Can't happen
			print -u2 -- "Internal error - nothing caught nor used"
			;;
		esac
		;;
	(false.true)
		# Cards are always caught but pseudo can only get
		# in if it is registered or pseudo can never get in.
		print StartSession.m$instance -t raw -T raw -a pseudo -s insertcard
		((instance+=1))
		;;
	(true.false)
		# Centrally registered but card is denied access
		# or all Cards are prohibited all the time
		REJECTLIST="-r pseudo"
		if [[ ${ASC} == true ]]
		then
			REJECTLIST="$REJECTLIST $REJECTASCCARDS"
		fi
		print StartSession.m$instance -t raw -T raw $REJECTLIST -s noentry
		((instance+=1))
		;;
	esac

	if [[ -n ${KIOSK:-} ]]
	then
		case ${KIOSK:-} in
		(pseudo)
			flags="-a pseudo"
			;;
		(card)
			flags="-r pseudo"
			;;
		(both)
			flags="-a default"
			;;
		esac
		print StartSession.m${instance} -t raw -T raw $flags -s kiosk
		((instance+=1))
	fi

	# Normal sessions are created here
	if [[ ${KIOSK:-} != both ]]
	then
		print StartSession.m${instance} -a default
		((instance+=1))
	fi

	return 0
}
#
# helper function to parseFilterOption
# from an input string like : -r pseudo -s register
# extracts out "register"
#
function extractAction {
	    typeset line=$1

	print -- $line | sed -n -e 's/.*-s[ 	]*\([^ 	]*\).*$/\1/p'
}
#
# This function helps to translate between the auth manager's policy
# files and a single line policy description.
# It returns string like "requirepw=true\ncard=register" or simply "pseudo=insertcard"
#
function parseFilterOption {
	typeset line=$1
	typeset token=""
	typeset action=""
	# Catch default session type sepeification

	case $line in
		(*-s*)
			# extract the "action" which should just be "register"
			action=$(extractAction "$line")
			if [[ $action == "register" ]]
			then
				case $line in
				(*-p*)
					print requirepw=false
					;;
				(*)
					print requirepw=true
					;;
				esac
			fi
			;;
		(*)
			action=default
			;;
	esac
	#
	# From the policy line, find out if 
	#-a pseudo :  accept pseudo tokens
	# -a default: accept default, both pseudo and card are ok
	# -r pseudo : reject pseudo token, card is required
	# flags are specified
	case $(print -- $line |
	    sed -n -e 's/.*-\([ar]\)[ 	]*\([^ 	]*\).*$/\1,\2/p') in
	(a,pseudo)
		token=pseudo
		;;
	(a,default)
		token=both
		;;
	(r,pseudo)
		token=card
		;;
	(*)
		print -u2 -- ERROR: Invalid policy file
		exit 6
		;;
	esac
	print -- $token=$action

}

#
# This function translates between the auth manager's policy
# files and a single line policy description.
#
# Example of $line: ZeroAdmin.m0 -a unknown -s carderror
#                   Registered.m1 -r pseudo
#            $instance: m0   $spec : card=db     $token: card    
#            $spec: requirepw=true\ncard=register $token=card (this is a special case which
#                   creates a bad policy file and chokes on isReasonable.
# 
function policy2pd {
	typeset pfile=$1
	typeset line=""
	typeset terminalsFile=""
	typeset zaCard=true
	typeset zaPseudo=true
	typeset selfreg=none
	typeset registrationEjected=false
	typeset asccards=""
	sed '
	    s/[ 	][ 	]*/ /g
	    s/^ //
	    s/#.*$//
	    s/ $//
	    /^$/d
	    ' $pfile |
	while read line
	do

		spec=""
		case "$line" in
		(*unknown*)			# card read errors
			;;
		(Authxlation*)			# MTSS always on
			;;
		(TerminalId.*-c*)	# uncaught tokens
			terminalsFile=${line#*-c }
			tidinit $terminalsFile
			;;
		(StartSession*"-t raw"*insertcard)		# pseudo filter
			zaPseudo=false
			;;
		(StartSession*"-t raw"*noentry)			# card filter
			zaCard=false
			;;
		(StartxlationSession*gulogin)
			## Ignore this.
			;;
		(StartxlationSession*nsclogin)
			line=${line#StartxlationSession.}
			instance=${line%% *}
			line=${line#${instance} }
			spec="mobile=true"
			;;
		(StartxlationSession*sclogin)
			line=${line#StartxlationSession.}
			instance=${line%% *}
			line=${line#${instance} }
			# grab all the accepted cards for sclogin, and
			# recreate the comma-separated list of card types
			cards="${line#*-a }"
			asccards=`print -- ${cards%-[!a]*} |
			          sed -e 's/-a //g' -e 's/ /,/g'`
			spec="asc=$asccards"
			;;
		(StartSession*"-s kiosk"*)
			line=${line#StartSession.}
			instance=${line%% *}
			line=${line#${instance} }
			spec=$(parseFilterOption "${line}")
			;;
		(StartSession*)
		# Note - keep this case after all the other
		# StartSession cases.  Its purpose is as a cattle
		# guard to clear out all the extraneous StartSession
		# invocations for creating sessions at the end of the
		# policy file - we learn about all the policy
		# particulars through the other modules and barriers
			;;
		(Registeredxlation*)
			origline=
			line=${line#Registeredxlation.}
			instance=${line%% *}
			line=${line#${instance} }
			line=${line#${REJECTTRANSLATEDTOKENS}}
			spec=$(parseFilterOption "${line}")
			tail=${spec##*[
 ]}							# strip through newline
			token=${tail%%=*}		# key
			action=${tail##*=}		# value
			case $token in
			(pseudo)
				zaPseudo=false
				;;
			(card)
				zaCard=false
				;;
			(both)
				zaPseudo=false
				zaCard=false
				;;
			esac
			case $action in
			(insertcard|noentry)
				if [[ "$spec" == "$tail" ]]
				then
					spec="${token}=db"
				else
					spec="${spec%${tail}}\012${token}=db"
				fi
				registrationEjected=true
				;;
			(register)
				selfreg=$token
				;;
			esac
			;;
		(ServerSelect*)
			line=${line#ServerSelect.}
			instance=${line%% *}
			line=${line#${instance} }
			spec="serverselect=true"
			;;
		(TerminalGroup*)
			line=${line#TerminalGroup.}
			instance=${line%% *}
			line=${line#${instance} }
			spec="terminalgroup=true"
			;;
		(*)
			print -- "ERROR:"
			print -- "BAD LINE: $line"
			print -- "<${line#Registered}>"
			print -- "<${line#ZeroAdmin}>"
			exit 7
			;;
		esac

		if [[ -n "$spec" ]]
		then
			print -- "$spec"
		fi


	done

	if [[ -n "$terminalsFile" ]]
	then

		print -- terminals=$terminalsFile
	fi

	case $zaCard.$zaPseudo in
	(true.true)
		print -- "both=default"
		;;
	(true.false)
		print -- "card=default"
		;;
	(false.true)
		print -- "pseudo=default"
		;;
	(false.false)
		;;
	esac

	if [[ $registrationEjected == "false" && $selfreg != "none" ]]
	then
		print -- ${selfreg}=db
	fi
}

# Loop though all the policies that this program has a hope to parse
# and translate them to utauthd policy files and back. Check for
# "unreasonable" policies.
function testprog {
	typeset POLICY
	typeset P1
	typeset P2
	typeset reg
	typeset self
	typeset za
	for reg in card pseudo both ""
	do
		if [[ -n "$reg" ]]
		then
			P1="$reg=db"
		else
			P1=""
		fi
		for self in card pseudo both ""
		do
			if [[ -n "$self" ]]
			then
				P2="$self=register"
			else
				P2=""
			fi
			for za in card pseudo both ""
			do
				if [[ -n "$za" ]]
				then
					P3="$za=default"
				else
					P3=""
				fi
				POLICY=$(print "$P1,$P2,$P3" | sed \
					-e 's/^,,*//' \
					-e 's/,,*$//' \
					-e 's/,,/,/'
					)
				print "\nPD=$POLICY"
				pd2policy $POLICY > /tmp/.$$
				ANDBACK=$(policy2pd /tmp/.$$ | tr ' \012' ',,')
				ANDBACK=${ANDBACK#,}
				ANDBACK=${ANDBACK%,}
				print "   $ANDBACK"
				cat -n /tmp/.$$
				rm -f /tmp/.$$
			done
		done
	done
}


#
# Modify "policy" field of auth.props to indicate which policy file is being used
# e.g. policy=ZeroAdmin or policy=utpolicy
#
function editAuthProps {

	typeset kv=${1}
	typeset key=${kv%=*}
	if [[ ! -w $(dirname $AUTHPROPS) ]]
	then
		print -u2 Cannot update $AUTHPROPS
		return 1
	fi
	[[ -f "$AUTHPROPS.bu" ]] && rm -f $AUTHPROPS.bu
	[[ -f "$AUTHPROPS" ]] && cp -p $AUTHPROPS $AUTHPROPS.bu
	chmod u+w $AUTHPROPS
	# the "-" after ed tells ed that this is not interactive
	ed - $AUTHPROPS <<-! 2>/dev/null 1>&2
	g/^$key/d
	i
	$kv
	.
	w
	q
	!
	chmod u-w $AUTHPROPS
	return $?
}

function mustBeRoot {
	case "$(id)" in
	'uid=0('*)
		;;
	*)
		Error 15 "Must be root to make policy changes" 
		;;
	esac
}

#
# Modify TIDCONFIG and POLICYFILE,if terminal ids are added or deleted after modifying card reader information
# Remove the TerminalId module from the policy file if no card reader is available
# Put it in if card reader(s) is available
#
function modifytid {
            #
            # Save terminal ids info to TIDCONFIG file
	tidsave > $TIDCONFIG
	
	#
	# Modify POLICYFILE to include/exclude TerminalId module
	#
	if [[ ${#TIDLIST} -gt 0 ]]
	then
	   grep "^TerminalId" $POLICYFILE 2> /dev/null 1>&2
	   if [[ $? -ne 0 ]]
	   then
	   	grep "^TerminalGroup" $POLICYFILE 2> /dev/null 1>&2
	   	if [[ $? -eq 0 ]]
	   	then
		    ed - $POLICYFILE <<-! 2>/dev/null 1>&2
			g/^TerminalGroup/
			.a
			TerminalId.m1 -c $TIDCONFIG
			.
			w
			q
			!
		else
		    ed - $POLICYFILE <<-! 2>/dev/null 1>&2
			0a
			TerminalId.m0 -c $TIDCONFIG
			.
			w
			q
			!
		fi
	   fi
	else
	    ed - $POLICYFILE <<-! 2>/dev/null 1>&2
		g/^TerminalId/d
		.
		w
		q
		!
	fi
}

#
# Determin the option to allow multiple -r, -s, or -z options in
# one policy string.
# getarg $newoption $previous_option
# OUTPUT: combined_option
# For example, getarg "card" "pseudo" would output "both"
#
getarg() {
	if [[ $# -eq 1 ]]
	then
		print $1
	elif [[ ( "$1" == "both" ) || ( "$2" == "both" ) ||
		( "$1" == "card" && "$2" == "pseudo" ) ||
		( "$1" == "pseudo" && "$2" == "card" ) ]]
	then
		print "both"
	else
		print $1
	fi
}

#
# Main program
#

if [ $# -eq 0 ]
then
  exit 0
else
  mustBeRoot
fi

#
# Handle the case for passing in arguments without options 
#
case $1 in
--*)	break;;
esac

#
# Reset the OPTIND variable before calling getopts again
#
OPTIND=1

# Parse Options
#
DB=""
SELFREG=""
ZEROADMIN=""
KIOSK=""
UT_OPTIONS=$@
IS_POLICY_CHANGE=false
while getopts abdgmk:t:r:s:z:P:pf:luQT:F:MS: c $UT_OPTIONS 2>/dev/null
do
	case $c in
	(a)	APPLY=true
		tidinit
		;;
	(d)	EXITENABLE=false
		;;
	(Q)	TEST=true
		;;
	(k)	if [[ -e $USER_CONF ]]
		then
			KIOSK=$(getarg $OPTARG $KIOSK)
			IS_POLICY_CHANGE=true
		else
			Error 9 "Cannot enable Controlled Access Mode.  Please run utconfig."             
		fi
		;;
	(r)	DB=$(getarg $OPTARG $DB)
		[[ -n "$POLICY" ]] && Error 1 "Conflicting policies specified" 
		POLICYSET=true
		IS_POLICY_CHANGE=true
		REGISTERED=true
		;;
	(s)	SELFREG=$(getarg $OPTARG $SELFREG)
		[[ -n "$POLICY" ]] && Error 1 "Conflicting policies specified"
		POLICYSET=true	# not a real policy, but let it through for error
				# checking purposes
		IS_POLICY_CHANGE=true
		;;
	(z)	ZEROADMIN=$(getarg $OPTARG $ZEROADMIN)
		[[ -n "$POLICY" ]] && Error 1 "Conflicting policies specified"
		POLICYSET=true
		IS_POLICY_CHANGE=true
		;;
	(g)	SERVERSELECT=true
		;;
	(m)	TERMINALGROUP=true
		IS_POLICY_CHANGE=true
		;;
	(M)	MOBILE=true
		IS_POLICY_CHANGE=true
		;;
	(p)	SRPASSWORD=false
		IS_POLICY_CHANGE=true
		;;
	(P)	POLICY=$OPTARG
		[[ -n "${DB:-}" || -n "${SELFREG:-}" || -n "${ZEROADMIN:-}" ]] ||
                    Error 1 "Conflicting policies specified"
		;;
	(f)	PFILE=$OPTARG
		[[ -r $PFILE ]] || Error 1 "Cannot read $PFILE" 
		;;
	(F)	POLICYFILE=$OPTARG
		POLICYDIR=$(dirname $OPTARG)
		[[ -n "$POLICYDIR" ]] || POLICYDIR=$PWD
		POLICYFILE=$POLICYDIR/$(basename $POLICYFILE)
		[[ -w $POLICYFILE || -w $POLICYDIR ]] ||
                    Error 11 "Cannot create policy file"
		;;
	(t)	
	       case $OPTARG in
		(list) POLICYFILE=$(getcurrentpolicyfile)
		       TIDCONFIG=$(gettidconfig $POLICYFILE)
		       if [[ -n "$TIDCONFIG" ]]
			then
			  tidlist
			else
			  print "Current policy does not have Terminal Id information."
			fi
			exit 0
			;;
		(del:*)	tiddel ${OPTARG#del:}
			;;
		(add:*)	tidadd ${OPTARG#add:}
			;;
		(clear)	tidclear
			;;
		(*) Error 1 "Invalid argument $OPTARG"
		    ;;
		esac
		UPDATETIDLIST=true;
		POLICYSET=true
		;;
	(T)	TIDCONFIG=$OPTARG
		POLICYSET=true
		;;
	(l)	DOWNCASEENA=false
		;;
	(u)	UNREASONABLE=true
		;;
	(b)	# unsupported flag!
		BEN=true
		;;
	(S)	ASCCARDS="$ASCCARDS $OPTARG"
		ASC=true
		IS_POLICY_CHANGE=true
		POLICYSET=true
		;;
	(\?)
#		shift $(( $OPTIND - 2 ))
		if [[ "$1" != "-?" ]]
                then
                        Error 1 "Invalid option: $1";
                else
                        Error 1
                fi
	esac
done

if $ASC
then
    ASCCARDS=$(print $ASCCARDS | sed 's/,/ /'g)
    for card in $ASCCARDS
    do
	ACCEPTASCCARDS="$ACCEPTASCCARDS -a $card"
	REJECTASCCARDS="$REJECTASCCARDS -r $card"
    done
fi

#
# The test flag overrides everything else
#
if $TEST
then
	testprog
	exit 0
fi

#
# If nothing is to be applied and it's not a test, print a warning message and exit.
#
if  ! $APPLY  && ! $BEN
then
    exit 0
fi

#
# if there is only -a but no policy, complain and exit
#
if [[ "${APPLY}" == true ]]
then
	if [[ $POLICYSET == false ]]
	then
		Error 1 "Nothing to apply. Please specify policy."
	fi
fi

#
# See if policy is being constructed and set POLICY
# When we talk about POLICY, only stuff set by -r, -s -z are checked.
# Setting Terminal Id using -t flag should be part of the POLICY as well tho'.
#
if [[ -n "${DB:-}" || -n "${SELFREG:-}" || -n "${ZEROADMIN:-}"
    || -n ${ASCCARDS:-} || -n ${MOBILE:-} ]]
then
	if [[ -n "$POLICY" ]]
        then
                Error 1 "P flag cannot be used with -r, -s, or -z flags"
        fi
	if [[ -n "${SELFREG:-}" ]]
	then
		POLICY="$POLICY,$SELFREG=register"
	fi
	if [[ -n "${DB:-}" ]]
	then
		POLICY="$POLICY,$DB=db"
	fi
	if [[ -n "${ZEROADMIN:-}" ]]
	then
		POLICY="$POLICY,$ZEROADMIN=default"
	fi
	if [[ ${ASC} == true ]]
	then
		POLICY="$POLICY,asc=true"
	fi
	if [[ ${MOBILE} == true ]]
	then
		POLICY="$POLICY,mobile=true"
	fi
fi

#
# POLICY could be specified on command line or read from a file when using -f option.
# But they cannot both be used.

# On command line user specifies using -s -r or -z flags
if [[ -n "$POLICY" ]] 
then
	#
	# If policy was specified on command line, it cannot also be
	# taken from the policy file.  Flag an error message.
	#
	if [[ -n "${PFILE:-}" ]]
        then
                Error 1 "Conflicting policies specified"
        fi

	#
	# Translate the policy specification into "key=value, key2=value2, key3=value3 ..."
	# Remove the prefixing "," from POLICY
	#

	POLICY=${POLICY#,}
	#
	# See if the policy makes sense. Complain if it doesn't.
	#
else
	#
	# Arrive here if -o, -M, -r, -s, and -z flags were not used
	# User might have used -f flag to read policy from a file	 
	#

	# No policy specified at all, not from file or command line.
	if [[ -z "${PFILE:-}" ]]
	then
		#
		# Things that can be done without -r, -s, -z, -f, and -P:
		#
		# 1) -t option: clear, add or del
		#     UPDATEIDLIST is true
		#
		# 2) Turn DB on and off (-x, +x options which cannot be used with -r -s)
		#
		if [[ "$UPDATETIDLIST" == true && "$APPLY" == true ]]
		then
			# Update terminal Id information
			modifytid

			#
			# Modify auth.props file to specify new policy
			#
			editAuthProps policy=$(basename $POLICYFILE)

			# Display current policy to /var/opt/SUNWut/log/messages
			logger -p user.info -t utpolicy -- `eval showcurrentpolicy`

			# I am done. Get out.
			exit 0
		fi
	else 
	  # -f flag is used
	  # if PFILE is not empty, convert it's content policy to pd format
		pd=$(policy2pd $PFILE | tr ' \012' ',,')

		if [[ $? != 0 ]]
		then
			print -u2 -- "ERROR: Invalid policy file: $PFILE"
			exit 9
		else
			# clean up if needed to remove the ending "," from POLICY
			POLICY=${pd%,}

			# Deal with tid if PFILE contains terminal information
			case $POLICY in
			(*terminal*)
				 tidinit
				 if $APPLY 
				 then
				       modifytid
				 fi
				 ;;
			(*);;
			esac
		fi
	fi
fi


#
# Now apply the new policy if -a was on the command line
# Config files that are affected: utpolicy, auth.props, terminals (only if -t is used)
#
if $APPLY
then
	#
	# back up current POLICYFILE
	# Make sure file exists to avoid an error message from cp
	#
	if [ -f ${POLICYFILE} ] 
	then
	       cp -p $POLICYFILE $POLICYFILE.bu
	fi

	# 
	# If terminal Id list is >= 0, write the TIDCONFIG file
	# In case of 0, it will zero out the file
	#
	if [[ ${#TIDLIST} -ge 0 ]]
	then
		tidsave > $TIDCONFIG	
	fi

	#
	# update the policy file
	pd2policy $POLICY > $POLICYFILE

	#
	# Edit the AUTHPROPS file 

	editAuthProps policy=$(basename $POLICYFILE)

	# print current policy to /var/opt/SUNWut/log/messages
	logger -p user.info -t utpolicy -- `eval showcurrentpolicy`

else	   
	#
	# User is testing out a policy by using -b flag.
	#
	if [[ ${#TIDLIST} -ne 0 ]]
	then
		tidsave
		print -- -------
	fi

	# 
	# Convert Policy descriptor to policy and print it.
	#
	pd2policy $POLICY
fi

# Done. Good bye.
exit 0
