#!/bin/ksh -p
#
# Copyright (c) 1999-04/17/00, Sun Microsystems, Inc. All Rights Reserved
# @(#)utpolicy.sh	@(#)utpolicy.sh	1.62  00/04/17
#
# If utpolicy is being run in order to query, then you must get
# the name of the policy file from 
#    $(pkginfo -r SUNWutr)/etc/opt/SUNWut/auth.props
#
# If utpolicy is being run to write the policy file, then it should
# write the new configuration to 
#    $(pkginfo -r SUNWutr)/etc/opt/SUNWut/policy/utpolicy 
# and update auth.props accordingly.
#
# If the action is a read/modify/write operation, then one could reasonably
# combine the above rules.
#
# Explicitly set umask so that files get created with correct permissions
umask 022

#exec > /tmp/utpolicy.$$ 2>&1
PROG=$0
#set -x
PATH=/bin:/usr/bin
export PATH
# keep log file after -i clear option
TMP=/var/tmp/utpolicy.log.$$
BASE=`pkginfo -r SUNWuto 2>/dev/null`
SUNWUT=$BASE/SUNWut
SUNWUTLIB=$SUNWUT/lib
SUNWUTSBIN=$SUNWUT/sbin
SUNWUTETC=/etc/opt/SUNWut
SUNWUTLOG=/var/opt/SUNWut/log
SUNWUTTOKENS=/var/opt/SUNWut/tokens
SUNWUTXLOCK=/etc/dt/config/config.lock
GREP=/bin/grep
# XXX to do: "-a -t del:foo.bar" doesn't work - says who? 

set -u

# trap "???do what" 0 1 2 15

#PS4='${LINENO}	|'

ETCDIR=$(pkginfo -r SUNWutr 2>/dev/null)/etc/opt/SUNWut
case "$ETCDIR" in
(//*)	ETCDIR="${ETCDIR#/}"
	;;
esac

# Directory where policy files reside
POLICYDIR=$ETCDIR/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=$ETCDIR/auth.props
#
# If we are creating a policy and the user didn't direct us
# to ignore the admin database, then use the admin database.
#
USEADMINDB=true
POLICY=""

# This file should be read in from the policy file, not dead set over here!
#TIDCONFIG=$ETCDIR/terminals
TIDCONFIG=""
TIDINIT=false
TIDCLEAR=false
BOGUSREMOVE=false
UPDATETIDLIST=false;
# TIDLIST=""
DOWNCASEENA=true
SRPASSWORD=true

#
# Set default values
#
TEST=false
UNREASONABLE=false
APPLY=false
SERVERSELECT=false
# If BEN is true, w/o -a flag one can still check the validity of the 
# commandline options
BEN=false
#
# Functions
#

#
# Command syntax
#
function usage {
	typeset ecode=${1:-99}
	typeset message=${2:-}
	if [[ -n "$message" ]]
	then
		print -u2 -- ERROR: $message
	fi
	cat <<!
Usage: 
${0##*/} 
${0##*/} -i clear|soft 
${0##*/} -a { [-g] [-p] [-r type] [-s type] [-z type] }
${0##*/} -a { [-t list] [-t clear] [-t add:<tid>] [-t del:<tid>] ... }

	-a			# Apply the policy as the active system policy.
	-p			# Do not require Solaris name and password for
				# self registration.
	-g			# Turn on session selection within a server
				# group.
	-r card|pseudo|both	# Policy will lookup and use token DB entry.
	-s card|pseudo|both	# Policy will allow self registration of tokens.
	-z card|pseudo|both	# Policy grant access to tokens w/o DB entry.
	-t list			# Print the list of card reader mode DTUs.
	-t clear		# Clear the list of card reader mode DTUs.
	-t add:<terminalId>	# Add terminal to card reader list.
	-t del:<terminalId>	# Remove terminal from card reader list.

	-i clear|soft		# Restart/init SunRay services.
				# "clear" clears out all existing sessions.
				# "soft" leaves sessions intact. Some sessions	
				# may be unreachable after restart.
				# May not be combined with any other option.

	-h			# Print this message.

	# The following options are RESERVED:
	-f -l -u +x -x -G -P -Q 

    With no arguments, ${0##*/} will print out the policy in effect.
  
!
	exit $ecode
}


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=$(sed -n 's/^TerminalId.* \(.*\) \(.*\)$/\2/p' $policyfile)
  fi
  print $tidconfig
}

function getcurrentlocalpolicy {
	typeset uselocalpolicy=""
	uselocalpolicy=$(sed -n '
		s/^[ 	]*//
		s/[ 	]*$//
		s/#.*$//
		s/[ 	][ 	]*=[ 	][ 	]*/=/
		s/^useLocalPolicy=\(.*\)$/\1/p
		' $AUTHPROPS)
	print $uselocalpolicy
}

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=""

	policyfile=$(getcurrentpolicyfile)


	print -- "# Reading policy file: $policyfile"
	if [[ ! -r $policyfile || ! -f $policyfile ]]
	then
		usage 8 "Policyfile is not readable"
	fi
	pd=$(policy2pd $policyfile)
	print -- "# Current Policy:"
	print -n -- "$0 -a"
	print -- "$pd" | while read line
	do
		case $line in
		(*=db)
			what=${line%=*}
			print -n -- " -r $what"
			;;
		(*=register)
			what=${line%=*}
			print -n -- " -s $what"
			;;
		(*=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"
			;;
		(*)
			print -u2 -- "\nInvalid policy parameter: $line"
			;;
		esac
	done
	print
}

function bogusRemoveError {
BOGUSREMOVE=true
cat <<!

ERROR: Policy unchanged. Trying to remove an Invalid Terminal ID. 

!

}
function removeWarning {
typeset terminalID
terminalID=$1
cat << !

WARNING: All token readers are cleared if there were any. 
         The option "-t clear" clears all token readers;
         option "-t del:$terminalID" is ignored.
	 To remove a specific token reader, use "-t del:" option without "-t clear".
!
}

function applyWarning {
cat <<!

WARNING: Please use -a option to effect a policy change.

!

}
function restartinstructions {
        # We don't want any restart instruction if a bogus delete of token reader was sepecified
        # at command line
        if $BOGUSREMOVE
	then
	  return
	fi

	#
	# If user did not use -a option, no need to print out restart instruction
	#
	if ! $APPLY
	then
	  applyWarning
	  return
	fi
	CHANGE=${1:-minor}

	if [[ -r "$POLICYFILE" && -r "$POLICYFILE.bu" ]]
	then
		if ! diff $POLICYFILE $POLICYFILE.bu > /dev/null
		then
			CHANGE=major
		fi
	fi

	if [[ "$CHANGE" == "major" ]]
	then
		if [[ "${UT_TERSE:-}" == "limitlog" ]] 
		then
			terse_major_msg
		else
			major_msg
		fi
	else
		if [[ "${UT_TERSE:-}" == "limitlog" ]] 
		then
			terse_minor_msg
		else
			minor_msg
		fi
	fi
}


#
# tidinit - initialize the list of terminals from /etc/opt/SUNWut/terminals
#
function tidinit {
	TIDCONFIG=$(gettidconfig $POLICYFILE)
        if [ -z $TIDCONFIG ]
	then
	  TIDCONFIG=$ETCDIR/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=$ETCDIR/terminals
        fi

        echo >> $TIDCONFIG

}


# tidadd tid [allow claim session annotate]
# Default to "pseudo" 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="$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="pseudo.$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
			;;
		*)
			;;
		esac
	fi

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

	#
	# Check for duplicates
	#
	typeset -i limit=${#TIDLIST[@]}
	typeset -i i=0
	while [[ $i -lt $limit ]]
	do
		if [[ "${TIDLIST[$i]}" == "$tokenspec" ]]
		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 -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="pseudo.$tid"
		;;
	esac
	if $DOWNCASEENA
	then
		case $tid in
		*.*)
			typeset model=${tid%.*}		# XXX
			typeset -l ena=${tid#*.}	# XXX
			tid=$model.$ena			# XXX
			;;
		*)
			;;
		esac
	fi

	typeset -i removed=0

	while [[ $i -lt $limit ]]
	do
		IFS=:
		set ${TIDLIST[$i]}
		IFS="${saveIFS}"
		if [[ "$1" == "$tid" || "$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

	if [ $removed -eq 0 ] 
	then
	  # if users used -t clear option, the -t del:xxx has no effect any more
	  # It wouldn't be necessary to give warning then whether user tries to delete
	  # a valid terminal or not.
	  if [ $TIDCLEAR = false ]
	  then
	    bogusRemoveError
	    exit 18
	  else
	    removeWarning $tid
	  fi
	fi
	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
# [[card|pseudo|both]=[db|sessionType]][,...]
#
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;

	if [[ -z "${1:-}" ]]
	then
		# XXX should set an error string and use it in caller
		print -u2 Empty Policy
		return 1
	fi
	print ZeroAdmin.m0 -a unknown -s carderror
	if [[ ${SERVERSELECT} == true ]]
	then
		((instance+=1))
		print ServerSelect.m${instance}
	fi
	# Set positional parameters: key=value [key=value [key=value]]
	set $(removeComma $1)
	for filter
	do

		((instance+=1))
		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

		#
		# Terminal ID setting is not intepreted here
		# At "Apply" time, a line that specifies where the Terminal 
		# ID file is will be added to the policy file.
		# So, we skip the token here.
		#
		if [ "$token" == "terminals" ]
		then
		        continue
		fi
		case $action in
		(db)
			module=Registered
			;;
		(default)
			module=ZeroAdmin
			;;
		(*)	# Session type
			module=ZeroAdmin
			flags="-s $action"
			if [[ "$action" == "register" ]]
			then
				if ! $SRPASSWORD
				then
					flags="$flags -p"
	
				fi
			fi
			;;
		esac

		case $token in
		(card)
			flags="-r pseudo $flags"
			if [[ $module != Registered ]]
			then
				CAUGHTCARD=true
			fi
			USEDCARD=true;
			;;
		(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;
			;;
		esac
		print -- $module.m$instance $flags
	done

	case $CAUGHTPSEUDO.$CAUGHTCARD in
	(true.true)
		# Everything is always caught, no additional policy is needed
		;;
	(false.false)
		# 
		if $USEDPSEUDO
		then
			((instance+=1))
			# Only registered terminals get in, this one denied
			# but if cards also used, ask for card
			if $USEDCARD
			then
				print ZeroAdmin.m$instance -a pseudo -s insertcard
			else
				print ZeroAdmin.m$instance -a default -s noentry
			fi
		elif $USEDCARD
		then
			# pseudo is not used, so this is central registered
			# ask for card
			((instance+=1))
			print ZeroAdmin.m$instance -a pseudo -s insertcard
			((instance+=1))
			print ZeroAdmin.m$instance -r pseudo -s noentry
		else
			# Unless an explicit TerminalId was used, there is
			# no way to get in.
			((instance+=1))
			print ZeroAdmin.m$instance -r pseudo -s noentry
		fi
		;;
	(false.true)
		# Cards are always caught but pseudo can only get
		# in if it is registered or pseudo can never get in.
		((instance+=1))
		print ZeroAdmin.m$instance -a pseudo -s insertcard
		;;
	(true.false)
		# Centrally registered but card is denied access
		# or all Cards are prohibited all the time
		((instance+=1))
		print ZeroAdmin.m$instance -r pseudo -s noentry
		;;
	esac
	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=""
	sed '
	    s/[ 	][ 	]*/ /g
	    s/^ //
	    s/#.*$//
	    s/ $//
	    /^$/d
	    ' $pfile |
	while read line
	do

		spec=""
		case "$line" in

		(*unknown*)			# card read errors
			;;
		("ZeroAdmin"*"-s noentry")	# uncaught tokens
			;;
		(TerminalId.*-c*)	# uncaught tokens
			terminalsFile=${line#*-c }
			tidinit $terminalsFile
			;;
		(ZeroAdmin*)
			line=${line#ZeroAdmin.}
			instance=${line%% *}
			line=${line#${instance} }
			spec=$(parseFilterOption "${line}")
			;;
		(Registered*)
			line=${line#Registered.}
			instance=${line%% *}
			line=${line#${instance} }
			spec=$(parseFilterOption "${line}")
			token=${spec%%=*}
			spec="$token=db"
			;;
		(ServerSelect*)
			line=${line#ServerSelect.}
			instance=${line%% *}
			line=${line#%{instance} }
			spec="serverselect=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
}

# 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/,,/,/'
					)
				if isReasonable "$P1" "$P2" "$P3"
				then
					print "\nPD=$POLICY"
					pd2policy $POLICY > /tmp/.$$
					ANDBACK=$(policy2pd /tmp/.$$ | tr ' \012' ',,')
					ANDBACK=${ANDBACK#,}
					ANDBACK=${ANDBACK%,}
					print "   $ANDBACK"
					cat -n /tmp/.$$
					rm -f /tmp/.$$
				else
					print XXX $POLICY
				fi
			done
		done
	done
}

#
# Return true if the given policy has no redundant entries or
# dead ends.
#
function isReasonable {
	typeset db=""
	typeset self=""
	typeset za=""
	typeset ok=true
	ERR=""
	#
	# this (require*) case is put because -p flag from PFILE
	# caused it to fail. 
	#
	for arg
	do
		case $arg in
		(*=db)	db=${arg%=*};;
		(*=register)	self=${arg%=*};;
		(*=default)	za=${arg%%=*};;
		("")	;;
		(require*) ;;              
		(*)	return 1;;
		esac
	done

	[[ "$db" == "card" && "$self" == both ]]	&& ok=false &&
		ERR="DB entries for pseudo are not used.\n"
	[[ "$db" == "card" && "$self" == pseudo ]]	&& ok=false &&
		ERR="DB entries for pseudo are not used.\n$ERR"
	[[ "$db" == "pseudo" && "$self" == both ]]	&& ok=false &&
		ERR="DB entries for card are not used.\n$ERR"
	[[ "$db" == "pseudo" && "$self" == card ]]	&& ok=false &&
		ERR="DB entries for card are not used.\n$ERR"
	[[ "$self" == "both" && -n "$za" ]]		&& ok=false &&
		ERR="both get stuck in self register, -z flag is not used.\n$ERR"
	[[ -n "$self" && "$self" == "$za" ]]		&& ok=false &&
		ERR="$self gets stuck in self register, -z flag is not used.\n$ERR"
	[[ -n "$self" && "$za" == "both" ]]		&& ok=false &&
		ERR="$self gets stuck in self register, change -z flag.\n$ERR"
	[[ -z "$db" && -n "$self" ]]			&& ok=false &&
		ERR="Database entries are not used. Use -r flag also.\n$ERR"
	[[ -z "$db" && -z "$za" ]]			&& ok=false &&
		ERR="Normal sessions are not accessible with this policy.\n$ERR"

	if ! $USEADMINDB
	then
		[[ -n "$db" || -n "$self" ]]		&& ok=false &&
			ERR="Administrative database would be disabled. Use -z flag only or remove -x flag.\n$ERR"
	fi

	if $ok
	then
		return 0
	else
		ERR="${ERR}Use the -u flag to override this message and to use the policy anyway."
		return 1
	fi
}

#
# 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 $?
}

#
# enableAdminDB - turn the DB on or off as far as utauthd is concerned
# XXX this function might be too fragile
# Modify auth.props's adminConfigFile field
#
function enableAdminDB {
	typeset on=${1:-false}

	if $on
	then
		if egrep -s '^#[ 	]*adminConfigFile' $AUTHPROPS
		then
			ed - $AUTHPROPS <<-! 2>/dev/null 1>&2
			g/^#[ 	]*adminConfigFile/s/^#[ 	]*//
			w
			q
			!
			return 0
		fi
	else
		if egrep -s '^[ 	]*adminConfigFile' $AUTHPROPS
		then
			ed - $AUTHPROPS <<-! 2>/dev/null 1>&2
			g/^[ 	]*adminConfigFile/s/^/#/
			w
			q
			!
			return 0
		fi
	fi
	return 1
}


function justRestart {

	print "Any command line option other than \"-i soft\" was ignored."

	${SUNWUTLIB}/utauthd -e
	sleep 5
	(
		cd ${SUNWUTLOG}
		trap "" HUP
		${SUNWUTLIB}/utagelog auth_log 9
		/bin/priocntl -c TS -p 30 -m 30 -e \
			${SUNWUTLIB}/utauthd -b > auth_log 2>&1 & 
	) >/dev/null 1>&2
	exit 0
}


function purgeAndRestart {

            # User might execute the -i clear when on a sunray box. Though the message displayed
            # will be too quick to see, the warning is still given.

            if [[ -n "${CORONA_TOKEN:-}" ]]
	    then
	          print "Your session will also be terminated as the command executes."
	    else
		  print "Restarting servers... messages will be logged to $SUNWUTLOG/messages."
	    fi

	#
	# Prevent utpolicy to recurse forever. Setting RECURSE=STOP before the program
	# as a terminating condition
	#
            if [[ -z "${RECURSE:-}"  ]] 
	    then
	          (nohup  /bin/ksh -p -c "RECURSE=STOP $PROG -i clear " > /dev/null  </dev/null &)
	    else
	          # The output will be logged to messages
	          exec > $TMP 2>&1

	          print "Any command line option other than -i was ignored."

	          /etc/init.d/utsvc stop

	          #
	          # Clean out the SunRay sessions configuration
	          # (utacleanup with no arg does this unconditionally)
	          #
	          /etc/init.d/utacleanup

	          kill -HUP $(/bin/cat /var/dt/Xpid)	# tell dtlogin parent to read config again

	          #
	          # Wait for all SunRay X-sessions to exit
	          #
	          print -n -- \
		  "Waiting 60+ seconds to insure that all SunRay sessions exit..."
	          sleep 65
	          print "\nRestarting SunRay services"

	          (/etc/init.d/utsvc start ) >/dev/null 1>&2
	          logger -f $TMP -p user.info -t UTPOLICY
		  # when invoked with -G we want some additional output on stdout
	          [[ ! -z ${UT_GLOBALRUN:-} ]] && showcurrentpolicy 	

	          # clean up temp file after messages are logged
	          [ -w $TMP ] && /bin/rm $TMP
	          exit 0
	     fi
}


function mustBeRoot {
        if [[ "$(/usr/xpg4/bin/id -u)" != "0" ]]
        then
               	Error 15 "Must be root to make policy changes" 
        fi
}

function checkarg {
	typeset arg=${1:-}
	case $arg in
	(card|both|pseudo)
		return 0
		;;
	(*)
		usage 6 "Invalid argument $arg"
		;;
	esac
}
#
# enable or disable AdminDB and give a restart instruction
#
function handleAdminDB {

	if $USEADMINDB
	then
	  enableAdminDB true
	else
	  enableAdminDB false
	fi
	if [[ $? -eq 0 ]]
	then
	  restartinstructions major
	else
	  restartinstructions minor
	fi
}

#
# Check if policy is reasonable.
#
function validatepolicy {
       typeset policy=${1:-}
       set $(print $policy | tr ',' ' ')
       if ! $UNREASONABLE && ! isReasonable "${1:-}" "${2:-}" "${3:-}"
       then
	 print -u2 -- "ERROR: unreasonable policy: '$policy'"
	 print -u2 -- "$ERR"
	 exit 12

       fi
}
#
# 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
	ed - $POLICYFILE <<-! 2>/dev/null 1>&2
	g/TerminalId.m0/d
	0a
	TerminalId.m0 -c $TIDCONFIG
	.
	w
	q
	!
	fi
		if [[ ${#TIDLIST} -le 0 ]]
	then
	ed - $POLICYFILE <<-! 2>/dev/null 1>&2
	g/TerminalId.m0/d
	.
	w
	q
	!
	fi
}

#
#  Default policy is local but when we configure groups we want 
#  uniform policies on all group members 
#
#

function globalpolicy {
	# have to prevent a possible runaway situation in case the passed command
	# include the -G option 

	if [[ -z "${UT_GLOBALRUN:-}" ]] 
	then
		UT_GLOBALRUN=onceisenough
		UT_TERSE=limitlog
		
	else
		Error 17 "Cannot process  \"-G\" option twice:  \" $UT_OPTIONS \" " 
	fi
	
	if [[ $# -gt 1 ]]
	then
	# We were passed options -- nothing to do 
	# but set UT_GLOBALRUN to "localrun" 
	# so that we don't reset OPTIND
		UT_GLOBALRUN=localrun
		UT_TERSE=limitlog
		:
	else
	# No options passed 
	# get the ldap entry 
	# then set UT_OPTIONS for getopt to set local policy
		GLOBALPOLICYOPTS=`$SUNWUTSBIN/utglpolicy`
		UT_OPTIONS=$GLOBALPOLICYOPTS
	fi

}


major_msg(){

cat <<!

THE MOST RECENT POLICY CHANGE WAS SIGNIFICANT. 

(If you cannot afford to terminate existing sessions, then you can restart
the authentication manager without clearing existing sessions.  Note that
some sessions that were granted access under the old policy may persist. 
Use the following command to restart the authentication manager without
clearing existing sessions: "$SUNWUTSBIN/utpolicy -i soft")

The authentication manager must be restarted for changes to take
effect.  Note that all existing sessions will be terminated. Please run
the following command:

	$SUNWUTSBIN/utpolicy -i clear
!

}

terse_major_msg() {
cat <<!
THE MOST RECENT POLICY CHANGE WAS SIGNIFICANT. 
Run utpolicy -i clear to restart the authentication manager.
!

}

minor_msg(){

cat <<!

The authentication manager must be restarted for changes to take effect. 
If a significant policy change has been made then the following command
should be run, note that all existing sessions will be terminated:

	$SUNWUTSBIN/utpolicy -i clear

If a minor policy change was made, such as adding a dedicated card reader
terminal, then it is not necessary to terminate existing sessions and the
following command is preferred:

	$SUNWUTSBIN/utpolicy -i soft
!

}

terse_minor_msg(){

cat <<!
A policy change has occurred.  Authentication manager must 
be restarted for changes to take effect.
!
}


#
# Main program
#


#
# No argument on cmd line, simply print out current policy and exit
#
if [ $# -eq 0 ]
then
  showcurrentpolicy
  exit 0
else
  mustBeRoot
fi

#
# Handle the case for passing in arguments without options 
#
case $1 in
  (-G)	# check useLocalPolicy to see if we need to continue processing	
	  USELOCALPOLICY=$(getcurrentlocalpolicy)
	if  $USELOCALPOLICY 
	then
		print -- "# Using local policy"
		exit 0	
	fi
	;;
  (-*) break;;
  (*) usage 7 "Invalid or extraneous arguments on command line: $@";;
esac

#
# Check for invalid options before connecting to ldap
#
while getopts abgi:t:r:s:z:P:pf:luxQT:F:Gh c 2>/dev/null
do
        case $c in
	(\?) usage 7 "Invalid or extraneous arguments on command line: $@"
	esac
done

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

#
# Check to see if utconfig has been run first; o/w exit
# if utuser does not work, it means the database was never set up
# i.e. utconfig was not run and utpolicy -a +x was not executed.
# 
$SUNWUTSBIN/utuser -l 1>/dev/null 2>&1
if [ $? -ne 0 ]
then
  Error 1 "Please run utconfig first before further setting/querying of utpolicy."
fi

# Parse Options
#
UT_OPTIONS=$@
while getopts abgi:t:r:s:z:P:pf:luxQT:F:Gh c $UT_OPTIONS 2>/dev/null
do
	case $c in
	(a)	APPLY=true
		tidinit
		;;
	(Q)	TEST=true
		;;
	(r)	DB=$OPTARG
		checkarg $OPTARG
		[[ -n "$POLICY" ]] && usage 19 "Conflicting policies specified"
		;;
	(s)	SELFREG=$OPTARG
		checkarg $OPTARG
		[[ -n "$POLICY" ]] && usage 1 "Conflicting policies specified"
		;;
	(z)	ZEROADMIN=$OPTARG
		checkarg $OPTARG
		[[ -n "$POLICY" ]] && usage 2 "Conflicting policies specified"
		;;
	(g)	SERVERSELECT=true
		;;
	(p)	SRPASSWORD=false
		;;
	(P)	POLICY=$OPTARG
		[[ -n "${DB:-}" || -n "${SELFREG:-}" || -n "${ZEROADMIN:-}" ]] ||
		    usage 3 "Conflicting policies specified"
		;;
	(f)	PFILE=$OPTARG
		[[ -r $PFILE ]] || usage 3 "Cannot read $PFILE"
		;;
	(F)	POLICYFILE=$OPTARG
		POLICYDIR=$(dirname $OPTARG)
		[[ -n "$POLICYDIR" ]] || POLICYDIR=$PWD
		POLICYFILE=$POLICYDIR/$(basename $POLICYFILE)
		[[ -w $POLICYFILE || -w $POLICYDIR ]] ||
		    usage 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
			;;
	        (*) usage 6 "Invalid argument $OPTARG"
		    ;;
		esac
		UPDATETIDLIST=true;
		;;
	(T)	TIDCONFIG=$OPTARG
		;;
	(l)	DOWNCASEENA=false
		;;
	(u)	UNREASONABLE=true
		;;
	(x)	USEADMINDB=false
		;;
	(+x)	USEADMINDB=true
		;;
	(i)	case $OPTARG in
		(clear)	 purgeAndRestart;;
		(soft)	justRestart;;
		(*)	usage 4 "Invalid argument to -i option";;
		esac
		exit 0
		;;
	(b)     # unsupported flag!
	        BEN=true
		;;
	(G)    	# 
		# The check for useLocalPolicy is redundant here  
		# but we'll leave it here for clarity
		# 
		[[ "$1" != "-G" ]] && usage 9 "Incorrect use of -G option"
		USELOCALPOLICY=$(getcurrentlocalpolicy)
		if  $USELOCALPOLICY 
		then
			print "useLocalPolicy is set"
			print "There's nothing to do"
			exit 0	
		else
			globalpolicy  $UT_OPTIONS
			if [[ "${UT_GLOBALRUN:-}" == "localrun" ]]
			then
				# continue to process the options
				:
			else
				# we reset OPTIND to process the new global UT_OPTIONS
				OPTIND=1
			fi
		fi
		;;
	(h)	usage 0
		;;
	(\?)
#		shift $(( $OPTIND - 2 ))
		if [[ "$1" != "-?" ]]
		then
			usage 4 "Invalid option: $1";
		else
			usage 20
		fi
	esac
done
#
# We handle the options a little differently now so we'll need to 
# take another look to verify how we should handle this case
#
#shift $(( $OPTIND - 1 ))
#
#if [[ $# != 0 ]]
#then
#	usage 7 "Invalid or extraneous arguments on command line: $@"
#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
    applyWarning
    exit 0
fi

#
# if there is only -a but no policy, complain and exit
#
if [[ "${APPLY}" == true && "${OPTIND}" -eq 2 ]]
then
  Error 1 "Nothing to apply. Please specify policy."
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:-}" ]]
then
	if [[ -n "$POLICY" ]]
	then
		usage 5 "P flag cannot be used with -r, -s, or -z flags"
	fi
	if [[ -n "${DB:-}" ]]
	then
		POLICY="$POLICY,$DB=db"
	fi
	if [[ -n "${SELFREG:-}" ]]
	then
		POLICY="$POLICY,$SELFREG=register"
	fi
	if [[ -n "${ZEROADMIN:-}" ]]
	then
		POLICY="$POLICY,$ZEROADMIN=default"
	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
		usage 10 "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.
	#
	validatepolicy $POLICY
else
	#
	# Arrive here if -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)

			# 
			# turn on or off AdminDB
			#
			handleAdminDB

			# Display current policy to /var/opt/SUNWut/log/messages
			logger -p user.info -t UTPOLICY `eval showcurrentpolicy`
			# when invoked by -G we want additional output on stdout
			[[ ! -z ${UT_GLOBALRUN:-} ]] && showcurrentpolicY

			# I am done. Get out.
			exit 0
		elif [[ -n $USEADMINDB  && "$APPLY" == true ]]
		then
		  # User just want to turn on or off admin db by useing the "x" flag
		  # whether USEADMINDB is true or fase, as long as user uses +x or -x, restart major
		  enableAdminDB $USEADMINDB
		  restartinstructions major
		  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%,}
			validatepolicy $POLICY

			# 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

	#
	# If there is more than one terminal Id, add the TerminalId module 
	# into the policy file. Otherwise, just update the policyfile
	#
	if [[ ${#TIDLIST} -gt 0 ]]
	then
	  print TerminalId.m0 -c $TIDCONFIG > $POLICYFILE
	  pd2policy $POLICY >> $POLICYFILE
	else
	  pd2policy $POLICY > $POLICYFILE
	fi

	#
	# Edit the AUTHPROPS file 

	editAuthProps policy=$(basename $POLICYFILE)
	
	# turn on or off AdminDB
	handleAdminDB

	# print current policy to /var/opt/SUNWut/log/messages
	logger -p user.info -t UTPOLICY `eval showcurrentpolicy`
        # when invoked with -G we want some additional output on stdout
        [[ ! -z ${UT_GLOBALRUN:-} ]] && showcurrentpolicy 	

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

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

# Done. Good bye.
exit 0
