#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2005,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
#-----------------------------------------------------------------------------
# >>> DISPLAY WITH TABSTOPS SET TO 4  --  DISPLAY WITH TABSTOPS SET TO 4 <<<
#-----------------------------------------------------------------------------
# File Name:		ctscfg
# Component:		ctsec
# Description:		Implements the "ctscfg" CtSec configuration file update
#					command.  This script modifies the "ctsec.cfg" file to
#					add, remove, or modify mechanism pluggable module (MPM)
#					information for the security subsystem.
# Usage:			
#
# Exit Status:		0		Successful
#					1		Successful, but a backup copy of the existing
#							configuration file could not be made
#					4		Incorrect options or arguments
#					21		Configuration file does not exist or appears to
#							be corrupted (have incorrcet contents)
#					23		Unexpected condition - system commands not found
#					36		User does not have sufficient permission to alter
#							CtSec configuration
#					105		Cannot record the modified configuration file
#-----------------------------------------------------------------------------

# ----------- #
# Definitions #
# ----------- #
CMDNAME=$(basename $0)
CPCMD=$(which cp)
RMCMD=$(which rm)
MVCMD=$(which mv)
DIFFCMD=$(which diff)
TOUCHCMD=$(which touch)
RSCTCMDPATH="/usr/sbin/rsct/bin"
MSGCMD=${RSCTCMDPATH}/ctdspmsg
MSGMAPPATH=/usr/sbin/rsct/msgmaps
export MSGMAPPATH

CFGBASE="ctsec.cfg"
DEFDIR="/usr/sbin/rsct/cfg"
OVRDIR="/var/ct/cfg"

# ----------------- #
# Global Data Areas #
# ----------------- #
typeset CURRFILE
typeset DESTFILE

typeset FILECMNTS
typeset FILEIN
typeset FILEOUT
typeset MPMMNEM
typeset MPMCODE
typeset MPMPATH
typeset MPMFLAG

integer	MPMCNT
integer MAXPRIOR

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_help
# Description:		Displays help information for this script
# Parameters:		$1		Indicates if "full" help message should be displayed
# Global Data:		No global data areas are modified by this function
# Return Values:	None
#-----------------------------------------------------------------------------
function ctscfg_help
{
	integer FULL

	FULL=$1
	$MSGCMD ctscfg ctseclib.cat ImsgctscfgHelpMsg $CMDNAME
	if ((0 != $FULL))
	then
		$MSGCMD ctscfg ctseclib.cat ImsgctscfgHelpDtl $CMDNAME
	fi
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_check_hex
# Description:		Determines if the argument to the function is a hexidecimal
#					value
# Parameters:		$1		Value to be tested
# Global Data:		No global data areas are modified by this function
# Return Values		0		Value is hexidecimal
#					4		Value has at least on non-hex character
#-----------------------------------------------------------------------------
function ctscfg_check_hex
{
	typeset	A
	typeset	B
	typeset	C
	integer I
	integer	RC

	A=$1
	B=A
	C=""
	RC=0

	I=0
	while ((0 == I))
	do
		if [[ -n $A ]]
		then
			B=${A#?}
			C=${A%$B}
			if [[ $C != [a-f] && $C != [A-F]  && $C != [0-9]  ]]
			then
				RC=4
				break
			fi
			A=$B
		else
			break
		fi
	done

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_get_cfgpath
# Description:		Determines the currently active CtSec configuration file
# Parameters:		None
# Global Data:		No global data areas are modified by this function
# Return Values:	Character string containing CtSec configuration file path
#					name
#-----------------------------------------------------------------------------
function ctscfg_get_cfgpath
{
	typeset	DEFPATH
	typeset OVRPATH

	DEFPATH=$DEFDIR/$CFGBASE
	OVRPATH=$OVRDIR/$CFGBASE

	if [ -f $OVRPATH ]
	then
		print $OVRPATH
	else
		print $DEFPATH
	fi
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_test_file
# Description:		Verifies that a file exists and is not empty
# Parameters:		$1		Path name to file
# Global Data:		No global data areas are modified by this function
# Return Values:	0		File exists and is not empty
#					21		File does not exist or is empty
#-----------------------------------------------------------------------------
function ctscfg_test_file
{
	typeset FNAME
	integer RC

	RC=0
	FNAME=$1

	if [ -f $FNAME ]
	then
		if [ ! -s $FNAME ]
		then
			RC=21
		fi
	else
		RC=21
	fi

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_perm
# Description:		Determines if the user has sufficient permissions to change
#					the CtSec configuration
# Parameters:		$1			Name of the destination configuration file
# Global Data:		No global data areas are modified by this function
# Return Values:	0			Premission okay
#					36			Insufficient permisions
#-----------------------------------------------------------------------------
function ctscfg_perm
{
	typeset	CFGFILE
	typeset	TMPFILE
	integer	RC

	RC=0
	CFGFILE="$1"
	TMPFILE="$CFGFILE.$$"
	if [[ -f $CFGFILE ]]
	then
		if [[ ! -O $CFGFILE ]]
		then
			RC=36
		fi
	else
		$TOUCHCMD $TMPFILE > /dev/null 2> /dev/null
		if [[ ! -f $TMPFILE ]]
		then
			RC=36
		else
			$RMCMD -f $TMPFILE > /dev/null 2> /dev/null
		fi
	fi

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_read_cfg
# Description:		Reads contents of currently active CtSec configuration
#					file into appropriate global arrays
# Parameters:		$1			Path name of active CtSec configuration file
# Global Data:		FILECMNTS	"Preamble" comments from the config file
#					FILEIN		Configuration lines from the config file
#					MPMCNT		Number of configuration lines (assumed to be
#								the number of MPMs available)
# Return Values:	0			Contents read
#					21			Contents invalid (no configuration lines)
#-----------------------------------------------------------------------------
function ctscfg_read_cfg
{
	typeset INLINE
	typeset	FNAME
	integer	CMNT
	integer	TMP
	integer RC
	integer CCNT

	RC=0
	CCNT=0
	CMNT=1
	MPMCNT=0
	FNAME=$1
	exec 4< $FNAME

	# Read currently active CtSec configuration file & assign to global vars
	while read -u4 INLINE
	do
		# Assuming our basic file structure here: preamble comments, followed
		# by the entries, no blank lines or trailing comments.  Comments will
		# be considered "preamble" until the first non-comment is seen, and
		# will be assigned to its own global data area.  Non-comments will be
		# considered configuration information.  Blank lines and any comments
		# after the "preamble" are ignored.
		if [[ -n $INLINE ]]
		then
			case $INLINE in
				\#*)	if ((1 == CMNT))
						then
							((CCNT = CCNT + 1))
							((TMP = CCNT + 1))
							FILECMNTS[$CCNT]=$INLINE
							FILECMNTS[$TMP]=""
						fi
						;;
				*)		CMNT=0
						((MPMCNT = MPMCNT + 1))
						FILEIN[$MPMCNT]=$INLINE
						;;
			esac
		else
			CMNT=0
		fi
	done
	exec 4<&-

	# Verify that at least one non-comment was detected
	# --- REMOVED --- THIS IS A VALID CONFIGURATION ("unauthenticated")
	#if ((0 == $MPMCNT))
	#then
	#	RC=21
	#else
	#	RC=0
	#fi

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_init_arrays
# Description:		Initialize global data arrays
# Parameters:		$1			Number of entries read from configuration
# Global Data:		MPMMNEM		Array of already known MPM mnemonic names
#					MPMCODE		  "   "     "      "    "  codes (hex values)
#					MPMPATH		  "   "     "      "    "  file names
#					MPMFLAG		  "   "     "      "    "  load flags
# Return Values:	None
#-----------------------------------------------------------------------------
function ctscfg_init_arrays
{
	integer I
	integer	J

	J=$1
	I=0
	# Initialize fields needed to store existing information
	while ((I <= J))
	do
		MPMMNEM[$I]=""
		MPMCODE[$I]=""
		MPMPATH[$I]=""
		MPMFLAG[$I]=""
		((I = I + 1))
	done
	# Initialize an additional field in each array in case of an 'add' request
	MPMMNEM[$I]=""
	MPMCODE[$I]=""
	MPMPATH[$I]=""
	MPMFLAG[$I]=""
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_parse_cfg
# Description:		Parse configuration information obtained from the active
#					CtSec configuration file
# Parameters:		None
# Global Data:		MPMMNEM		Array of already known MPM mnemonic names
#					MPMCODE		  "   "     "      "    "  codes (hex values)
#					MPMPATH		  "   "     "      "    "  file names
#					MPMFLAG		  "   "     "      "    "  load flags
#					MAXPRIOR	Maximum MPM priority level seen in configuration
# Return Values:	0			Configuration parsed
#					21			Configuration format appears invalid
#-----------------------------------------------------------------------------
function ctscfg_parse_cfg
{
	typeset	MNEMONIC
	typeset	HEXCODE
	typeset	MPATH
	typeset	FLAGS
	integer	PRIORITY
	integer	RC
	integer	I

	MAXPRIOR=0
	RC=0
	I=1
	while ((I <= MPMCNT))
	do
		# Initialize values before parsing
		MNEMONIC=""
		HEXCODE=""
		MPATH=""
		FLAGS=""
		PRIORITY=0
		# Parse the configuration line into fields.
		# (The following should work, why doesn't it?)
		# echo ${FILEIN[$I]} | read PRIORITY MNEMONIC HEXCODE MPATH FLAGS
		PRIORITY=$(echo ${FILEIN[$I]} | cut -d' ' -f1)
		MNEMONIC=$(echo ${FILEIN[$I]} | cut -d' ' -f2)
		HEXCODE=$(echo ${FILEIN[$I]} | cut -d' ' -f3)
		MPATH=$(echo ${FILEIN[$I]} | cut -d' ' -f4)
		FLAGS=$(echo ${FILEIN[$I]} | cut -d' ' -f5-)
		# Make sure required fields are present and appear valid
		if ((0 == $PRIORITY))
		then
			RC=21
			break
		fi
		if [[ -z $MNEMONIC || -z $HEXCODE || -z $MPATH ]]
		then
			RC=21
			break
		fi
		if [[ $MPATH != /* ]]
		then
			RC=21
			break
		fi
		if [[ ! -f $MPATH ]]
		then
			RC=21
			break
		fi
		# Parse information into the proper arrays.  The MPM's priority will
		# be used as its insertion point into the array (so if any two entries
		# have the same priority, they'll attempt to use the same array
		# element, and we'll detect that we have an invalid configuration)
		if [[ -n ${MPMMNEM[$PRIORITY]} ]]
		then
			RC=21
			break
		fi
		# Array element available - parse & store information
		MPMMNEM[$PRIORITY]=$MNEMONIC
		MPMCODE[$PRIORITY]=$HEXCODE
		MPMPATH[$PRIORITY]=$MPATH
		MPMFLAG[$PRIORITY]=$FLAGS
		if ((MAXPRIOR < PRIORITY))
		then
			MAXPRIOR=$PRIORITY
		fi
		# Move on to next entry
		((I = I + 1))
	done

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_load_cfg
# Description:		Loads the contents of the currently active CtSec configu-
#					ration file into the script's memory
# Parameters:		$1			Name of configuration file
# Global Data:		MPMMNEM		Array of already known MPM mnemonic names
#					MPMCODE		  "   "     "      "    "  codes (hex values)
#					MPMPATH		  "   "     "      "    "  file names
#					MPMFLAG		  "   "     "      "    "  load flags
# 					FILECMNTS	"Preamble" comments from the config file
#					FILEIN		Configuration lines from the config file
#					MPMCNT		Number of configuration lines (assumed to be
#								the number of MPMs available)
# Return Values:	0			Configuration loaded
#					21			Configuration file missing or contents not
#								valid
#-----------------------------------------------------------------------------
function ctscfg_load_cfg
{
	typeset CFILE
	integer RC
	integer	I

	CFILE=$1
	RC=0
	I=1

	until ((I == 0))
	do
		# Set break-out condition - only do one pass of the loop
		I=0

		# Verify presence of configuration file
		ctscfg_test_file $CFILE
		RC=$?
		if ((0 != RC))
		then
			if [[ $CFILE = /usr/sbin/rsct/cfg/* ]]
			then
				$MSGCMD ctscfg ctseclib.cat EMsgctscfgDefCfgEmpty $CMDNAME \
				        $CFILE
			else
				$MSGCMD ctscfg ctseclib.cat EMsgctscfgCfgEmpty $CMDNAME $CFILE
			fi
			break
		fi

		# Read contents of configuration file into memory
		ctscfg_read_cfg $CFILE
		RC=$?
		if ((0 != RC))
		then
			if [[ $CFILE = /usr/sbin/rsct/cfg/* ]]
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgDefReadCfg $CMDNAME \
				        $CFILE
			else
				$MSGCMD ctscfg ctseclib.cat EMsgctscfgReadCfg $CMDNAME $CFILE
			fi
			break
		fi

		# Parse existing configuration information
		ctscfg_init_arrays $MPMCNT
		ctscfg_parse_cfg
		RC=$?
		if ((0 != RC))
		then
			if [[ $CFILE = /usr/sbin/rsct/cfg/* ]]
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgDefCfgCorrupt $CMDNAME \
				        $CFILE
			else
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgCfgCorrupt $CMDNAME \
				        $CFILE
			fi
			break
		fi
	done

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_list_cfg
# Description:		Displays a listing of the CtSec configuration to standard
#					output (or wherever standard output is being directed)
# Parameters:		$1		Indicates whether a heading should be displayed
# Global Data:		No global data areas are modified by this function
# Return Values:	None
#-----------------------------------------------------------------------------
function ctscfg_list_cfg
{
	integer I
	integer J
	integer	HDR

	HDR=$1

	if ((0 < MPMCNT))
	then
		if ((0 != HDR))
		then
			$MSGCMD ctscfg ctseclib.cat ImsgctscfgOutHdr 
		fi
		I=1
		J=1
		while ((I <= MPMCNT))
		do
			if [[ -n ${MPMMNEM[$J]} ]]
			then
				print "$J\t${MPMMNEM[$J]}\t${MPMCODE[$J]}\t${MPMPATH[$J]}\t${MPMFLAG[$J]}"
				((I = I + 1))
			fi
			((J = J + 1))
		done
	fi
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_verify_path
# Description:		Verifies that an appropriate path name was provided as
#					an argument to the '-o' option.  Paths must be absolute.
# Parameters:		$1		Argument to the '-o' option
# Global Data:		No global data areas are modified by this function
# Return Values:	0		Argument appears valid
#					4		Argument is not valid
#-----------------------------------------------------------------------------
function ctscfg_verify_path
{
	typeset	OARGVAL
	integer	RC

	RC=0
	OARGVAL=$1

	if [[ $OARGVAL = /* ]]
	then
		ctscfg_test_file $OARGVAL
		RC=$?
	else
		RC=4
	fi

	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_verify_code
# Description:		Verifies that an appropriate code value was provided as
#					an argument to the '-c' option.  Codes must be hexidecimal.
#					Code values of 0x0 are not allowed.
# Parameters:		$1		Argument to the '-c' option.
# Global Data:		No global data areas are modified by this function
# Return Values:	Value returned as output that must be captured by the
#					caller:
#					(hex) 16#0		Argument is not a valid hex value
#					(hex) > 16#0	Argument is a valid hex value - value is
#									returned at the output of this function
#-----------------------------------------------------------------------------
function ctscfg_verify_code
{
	typeset	CARGVAL
	typeset	STRIPPEDVAL
	integer	SRC
	integer -i16 RC

	SRC=0
	RC=16#0
	CARGVAL=$1
	STRIPPEDVAL=""

	if [[ $CARGVAL = 0x* ]]
	then
		STRIPPEDVAL="${CARGVAL#0x}"
		ctscfg_check_hex $STRIPPEDVAL
		SRC=$?
		if ((0 == SRC))
		then
			RC=16#$STRIPPEDVAL
		else
			RC=16#0
		fi
	fi

	print "$RC"
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_find_in_cfg
# Description:		Checks the existing configuration for a specific MPM and
#					outputs its priority (if it exists)
# Parameters:		At least $1 or $2 must be provided:
#					$1		MPM code number (hex) - may be 0 if an MPM
#							mnemonic is to be used to locate the MPM
#					$2		MPM mnemonic (string) - may be "" if an MPM code
#							is to be used to locate the MPM
#					$3		Path to MPM binary (string) - may be "", will only
#							be provided in some cases where $2 also provided
#							(never provided when $2 is blank)
# Output:			0		MPM not found in configuration, or error
#					> 0		Priority of the MPM (found in configuration)
# Return Values:	0		Success - MPM found in configuration
#					3		MPM not found in configuration
#					4		MPM name and code values do not reference the
#							same MPM
#					23		Function used without correct parameters
#-----------------------------------------------------------------------------
function ctscfg_find_in_cfg
{
	typeset SRCNAME
	typeset SRCPATH
	integer -i16 SRCCODE
	integer -i16 CFGCODE
	integer I
	integer J
	integer CPRIOR
	integer NPRIOR
	integer	FPRIOR
	integer	RC

	SRCCODE=$1
	SRCNAME=$2
	SRCPATH=$3
	CPRIOR=0
	NPRIOR=0
	FPRIOR=4
	RC=4

	# Validate parameters - at least one must be provided
	if ((16#0 == $SRCCODE))
	then
		if [[ -z $SRCNAME ]]
		then
			RC=23
			print "0"
			return $RC
		fi
	fi

	# Search by code first.
	if ((16#0 != $SRCCODE))
	then
		J=1
		I=1
		while ((J <= MPMCNT))
		do
			# If entry in configuration for this priority is empty, skip to next
			if [[ -z ${MPMMNEM[$I]} ]]
			then
				((I = I + 1))
				continue
			fi
			# Entry not empty, compare code to what caller provided
			CFGCODE=16#${MPMCODE[$I]#0x}
			if ((CFGCODE == SRCCODE))
			then
				CPRIOR=$I
				break
			fi
			# Didn't find based on code - check next known MPM
			((J = J + 1))
			((I = I + 1))
		done
	fi

	# Search by mnemonic next
	if [[ -n $SRCNAME ]]
	then
		J=1
		I=1
		while ((J <= MPMCNT))
		do
			# If entry in configuration for this priority is empty, skip to next
			if [[ -z ${MPMMNEM[$I]} ]]
			then
				((I = I + 1))
				continue
			fi
			# Entry not empty, compare mnemonic to what caller provided
			if [[ $SRCNAME == ${MPMMNEM[$I]} ]]
			then
				NPRIOR=$I
				break
			fi
			# Didn't find based on mnemonic - check next known MPM
			((J = J + 1))
			((I = I + 1))
		done
	fi

	# Stop here if nothing found
	if ((0 == CPRIOR && 0 == NPRIOR))
	then
		print "0"
		RC=4
		return $RC
	fi

	# If both the code and the mnemonic were provided, they must reference
	# the same entry.
	if ((16#0 != $SRCCODE))
	then
		if [[ -n $SRCNAME ]]
		then
			if ((CPRIOR != NPRIOR))
			then
				print "0"
				RC=3
				return $RC
			else
				FPRIOR$=NPRIOR
			fi
		else
			FPRIOR=$CPRIOR
		fi
	else
		FPRIOR=$NPRIOR
	fi

	# Verify that the path to the MPM is the same (if a path is provided)
	if [[ -n $SRCPATH ]]
	then
		if [[ $SRCPATH != ${MPMPATH[$FPRIOR]} ]]
		then
			print "0"
			RC=3
			return $RC
		fi
	fi

	# Getting this far, we know that the name and/or code (and also the
	# path, if provided), located an MPM entry.
	print "$FPRIOR"
	RC=0
	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_del
# Description:		Deletes an MPM from the current CtSec configuration
# Parameters:		$1			Priority of the MPM to be removed
# Global Data:		MPMMNEM		Array of already known MPM mnemonic names
#					MPMCODE		  "   "     "      "    "  codes (hex values)
#					MPMPATH		  "   "     "      "    "  file names
#					MPMFLAG		  "   "     "      "    "  load flags
#					MPMCNT		Number of configuration lines (assumed to be
#								the number of MPMs available)
# Return Values:	0			In all cases
#-----------------------------------------------------------------------------
function ctscfg_del
{
	integer DELPRIOR
	integer	I
	integer	J
	integer K

	DELPRIOR=$1

	if ((DELPRIOR <= MAXPRIOR))
	then
		# Delete the MPM of a given priority by copying lower priority MPM data
		# over the top of it.
		I=$DELPRIOR
		while ((I < MAXPRIOR))
		do
			((J = I + 1))
			MPMMNEM[$I]="${MPMMNEM[$J]}"
			MPMCODE[$I]="${MPMCODE[$J]}"
			MPMPATH[$I]="${MPMPATH[$J]}"
			MPMFLAG[$I]="${MPMFLAG[$J]}"
			((I = I + 1))
		done
		# Blank out the last entry
		MPMMNEM[$MAXPRIOR]=""
		MPMCODE[$MAXPRIOR]=""
		MPMPATH[$MAXPRIOR]=""
		MPMFLAG[$MAXPRIOR]=""
		# Decrement count of MPMs in configuration
		((MPMCNT = MPMCNT - 1))
	fi
	return 0
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_add
# Description:		Adds an MPM entry to the CtSec configuration, or updates
#					an existing entry to change its priority.
# Parameters:		$1			Priority of any existing entry for this MPM
#					$2			Priority to assign to this MPM
#					$3			Mnemonic associated with the MPM
#					$4			Hex code to associate with the MPM
#					$5			Path name (absolute) for the MPM binary
#					$6			Load flags for the MPM
# Global Data:		MPMMNEM		Array of already known MPM mnemonic names
#					MPMCODE		  "   "     "      "    "  codes (hex values)
#					MPMPATH		  "   "     "      "    "  file names
#					MPMFLAG		  "   "     "      "    "  load flags
#					MPMCNT		Number of configuration lines (assumed to be
#								the number of MPMs available)
# Return Values:	0			In all cases
#-----------------------------------------------------------------------------
function ctscfg_add
{
	typeset	ADDMNEM
	typeset ADDPATH
	typeset ADDFLAG
	typeset	REALCODE
	typeset -Z5 TEMPCODE
	integer FOUNDPRIOR
	integer	ADDPRIOR
	integer	RC
	integer	I
	integer J
	integer	ADDCNT
	integer -i16 ADDCODE

	FOUNDPRIOR=$1
	ADDPRIOR=$2
	ADDMNEM=$3
	ADDCODE=$4
	ADDPATH=$5
	ADDFLAG="$6"
	TEMPCODE=$(print "${ADDCODE#16?}")
	REALCODE="0x$TEMPCODE"
	RC=0

	# Nothing to do if the MPM was already found at the given priority
	if ((ADDPRIOR == FOUNDPRIOR))
	then
		return $RC
	fi

	# If MPM already exists at a different priority, remove it & readjust
	# other MPM priorities.
	if ((0 != FOUNDPRIOR))
	then
		ADDMNEM="${MPMMNEM[$FOUNDPRIOR]}"
		REALCODE="${MPMCODE[$FOUNDPRIOR]}"
		ADDPATH="${MPMPATH[$FOUNDPRIOR]}"
		if [[ -z $ADDFLAG ]]
		then
			ADDFLAG="${MPMFLAG[$FOUNDPRIOR]}"
		fi
		ctscfg_del $FOUNDPRIOR
	fi

	# If needed, insert a blank entry for this MPM at the appropriate priority
	I=MPMCNT
	while ((I >= ADDPRIOR))
	do
		((J = I + 1))
		MPMMNEM[$J]="${MPMMNEM[$I]}"
		MPMCODE[$J]="${MPMCODE[$I]}"
		MPMPATH[$J]="${MPMPATH[$I]}"
		MPMFLAG[$J]="${MPMFLAG[$I]}"
		((I = I - 1))
	done

	# Insert information for new MPM at appropriate priority level
	MPMMNEM[$ADDPRIOR]="$ADDMNEM"
	MPMCODE[$ADDPRIOR]="$REALCODE"
	MPMPATH[$ADDPRIOR]="$ADDPATH"
	MPMFLAG[$ADDPRIOR]="$ADDFLAG"

	# Increment active MPM count and return
	((MPMCNT = MPMCNT + 1))
	return $RC
}

#-----------------------------------------------------------------------------
# Function Name:	ctscfg_upd_flags
# Description:		Updates flags field of an existing entry, when the priority
#					of an MPM is not being changed.
# Return Values:	0			In all cases
#-----------------------------------------------------------------------------
function ctscfg_upd_flags
{
	typeset	NEWFLAGS
	integer INDEX

	INDEX=$1
	NEWFLAGS="$2"

	if ((0 != INDEX))
	then
		MPMFLAG[$INDEX]="$NEWFLAGS"
	fi
	return 0
}


#-----------------------------------------------------------------------------
# Function Name:	ctscfg_rec_cfg
# Description:		Records (modified) configuration to the "override" CtSec
#					configuration file.
# Return Values		0			"Override" file successfully recorded
#					1			"Override" file successfully recorded, but a
#								backup copy of the existing "override" file
#								could not be made
#					36			User does not have sufficient privilege
#					105			Error in recording the modified "override" file
#-----------------------------------------------------------------------------
function ctscfg_rec_cfg
{
	typeset	OUTCFG
	typeset TMPCFG
	typeset	BKUPCFG
	typeset	INLINE
	typeset	CHECKLINE
	integer	I
	integer	J
	integer RC
	integer	BRC

	OUTCFG=$1
	TMPCFG="$OUTCFG.rev.$$"
	BKUPCFG="$OUTCFG.bak"
	RC=0
	BRC=0

	# Create temporary file
	$RMCMD -f $TMPCFG > /dev/null 2> /dev/null
	if [[ -f $TMPCFG ]]
	then
		RC=105
		return $RC
	fi
	$TOUCHCMD $TMPCFG > /dev/null 2> /dev/null
	if [[ ! -f $TMPCFG ]]
	then
		RC=36
		return $RC
	fi
	chmod 644 $TMPCFG
	# Dump out "preamble" comments
	I=1
	while [[ -n ${FILECMNTS[$I]} ]]
	do
		if ((1 == I))
		then
			print "${FILECMNTS[$I]}" > $TMPCFG
		else
			print "${FILECMNTS[$I]}" >> $TMPCFG
		fi
		((I = I + 1))
	done
	# Dump new config information
	ctscfg_list_cfg 0 >> $TMPCFG
	# Change permissions on file to correct set of permissions
	chmod 444 $TMPCFG

	# Verify the modified information file exists
	ctscfg_test_file $TMPCFG
	RC=$?
	if ((0 == RC))
	then
		exec 4< $TMPCFG
		# Verify "preamble" comments
		I=1
		while [[ -n ${FILECMNTS[$I]} ]]
		do
			INLINE=""
			read -u4 INLINE
			if [[ -z $INLINE ]]
			then
				RC=105
				return $RC
			fi
			if [[ "$INLINE" != "${FILECMNTS[$I]}" ]]
			then
				RC=105
				return $RC
			fi
			((I = I + 1))
		done
		# Verify configuration data against our memory
		I=1
		J=1
		while ((I <= MPMCNT))
		do
			if [[ -n ${MPMMNEM[$J]} ]]
			then
				INLINE=""
				read -u4 INLINE
				if [[ -z $INLINE ]]
				then
					RC=105
					return $RC
				fi
				CHECKLINE=$(print "$J\t${MPMMNEM[$J]}\t${MPMCODE[$J]}\t${MPMPATH[$J]}\t${MPMFLAG[$J]}")
				CHECKLINE=${CHECKLINE%	}
				if [[ "$INLINE" != "$CHECKLINE" ]]
				then
					RC=105
					return $RC
				fi
				((I = I + 1))
			fi
			((J = J + 1))
		done
		# Verify no additional file content
		INLINE=""
		read -u4 INLINE
		if [[ -n $INLINE ]]
		then
			RC=105
			return $RC
		fi
		# Modification file okay - close it
		exec 4<&-
		# Make a backup of the existing configration file, in case the
		# configuration change needs to be backed off at a later time.
		if [[ $CURRFILE = $OUTCFG ]]
		then
			$CPCMD -p $OUTCFG $BKUPCFG > /dev/null 2> /dev/null
			ctscfg_test_file $BKUPCFG
			BRC=$?
			if ((0 != BRC))
			then
				BRC=1
			fi
		fi
		# Overlay modifications onto "override" configuration file
		$CPCMD -p $TMPCFG $OUTCFG > /dev/null 2> /dev/null
		$DIFFCMD $TMPCFG $OUTCFG > /dev/null 2> /dev/null
		BRC=$?
		if ((0 != BRC))
		then	
			RC=105
			return $RC
		fi
		# Remove modifications file now that copy is complete
		$RMCMD -f $TMPCFG > /dev/null 2> /dev/null
		RC=0
	else
		RC=105
		return $RC
	fi

	if ((0 == RC && 0 != BRC))
	then
		RC=$BRC
	fi
	return $RC
}

#-----------------------------------------------------------------------------
#     MAIN ROUTINE  --  MAIN ROUTINE  --  MAIN ROUTINE  --  MAIN ROUTINE 
#-----------------------------------------------------------------------------

typeset USROPT
typeset USRNAME
typeset USRPATH
typeset USRFLAGS
typeset USRCODE
integer	USRPRIOR
integer RC
integer	ADDFLAG
integer	DELFLAG
integer UPDFLAG
integer HELPFLAG
integer LISTFLAG
integer	OLDPRIOR
integer	I
integer	-i16 REALCODE

# Initializations
USRNAME=""
USRPATH=""
USRFLAGS=""
USRCODE=""
USRPRIOR=0
ADDFLAG=0
DELFLAG=0
UPDFLAG=0
HELPFLAG=0
LISTFLAG=0
REALCODE=0
OLDPRIOR=0
I=0

# Check for essential system commands
if [[ -z $RMCMD || -z $CPCMD || -z $MVCMD || -z $DIFFCMD || -z $TOUCHCMD ]]
then
	$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingCmd $CMDNAME \
	        "  rm  cp  mv  diff  touch"
	RC=23
	exit $RC
fi

# Parse command line arguments
while getopts :ac:df:hln:o:p:u USROPT 2> /dev/null
do
	case $USROPT in
		a)	# ADD a new entry to the CtSec configuration
			if ((0 != ADDFLAG))
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				ADDFLAG=1
			fi
			;;
		c)	# MPM code number (hexidecimal) to add or delete
			if [[ -n $USRCODE ]]
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				if [[ $OPTARG = -* ]]
				then
					$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingArg $CMDNAME \
					        $USROPT
					RC=4
				else
					USRCODE="$OPTARG"
				fi
			fi
			;;
		d)	# DELETE an existing entry from the CtSec configuration
			if ((0 != DELFLAG))
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				DELFLAG=1
			fi
			;;
		f)	# Flags for the MPM (meaningful only in ADD commands)
			if [[ -n $USRFLAGS ]]
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				USRFLAGS="$OPTARG"
			fi
			;;
		h)	# Displays help message
			HELPFLAG=1
			;;
		l)	# List contents of the CtSec configuration - if used with ADD or
			# DELETE request, the listing will be of the result
			if ((0 != LISTFLAG))
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				LISTFLAG=1
			fi
			;;
		n)	# MPM mnemonic to add or delete
			if [[ -n $USRNAME ]]
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				if [[ $OPTARG = -* ]]
				then
					$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingArg $CMDNAME \
					        $USROPT
					RC=4
				else
					USRNAME="$OPTARG"
				fi
			fi
			;;
		o)	# MPM path name (an absolute path - makes sense only in ADD
			# commands
			if [[ -n $USRPATH ]]
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				if [[ $OPTARG = -* ]]
				then
					$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingArg $CMDNAME \
					        $USROPT
					RC=4
				else
					USRPATH="$OPTARG"
				fi
			fi
			;;
		p)	# MPM priority (decimal - makes sense only in ADD commands)
			if ((0 != USRPRIOR))
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				if [[ $OPTARG = -* ]]
				then
					$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingArg $CMDNAME \
					        $USROPT
					RC=4
				else
					USRPRIOR="$OPTARG"
				fi
			fi
			;;
		u)	# UPDATE an existing entry from the CtSec configuration
			if ((0 != UPDFLAG))
			then
				$MSGCMD ctscfg ctseclib.cat EmsgctscfgRepeatOpt $CMDNAME \
				        $USROPT
				RC=4
			else
				UPDFLAG=1
			fi
			;;
		:)	# Valid command option is missing a required argument
			shift $(($OPTIND - 2))
			$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingArg $CMDNAME $1
			RC=4
			;;
		*)	# Unrecognized or unsupported command option
#			$MSGCMD ctscfg ctseclib.cat EmsgctscfgUnsuptOpt $CMDNAME $USROPT
			$MSGCMD ctscfg ctseclib.cat EmsgctscfgUnsuptOpt $CMDNAME $1
			RC=4
			;;
	esac
	if ((0 != RC))
	then
		ctscfg_help 0
		exit $RC
	fi
done

# Help option overrides everything else - display help and exit
if ((0 != HELPFLAG))
then
	ctscfg_help 1
	exit $RC
fi

# Validate input from the command line
if ((0 == ADDFLAG && 0 == DELFLAG && 0 == UPDFLAG && 0 == LISTFLAG))
then
	RC=4
	$MSGCMD ctscfg ctseclib.cat EmsgctscfgReqdOpt $CMDNAME
	ctscfg_help 0
	exit $RC
fi
if ((0 != ADDFLAG && 0 != DELFLAG))
then
	RC=4
	$MSGCMD ctscfg ctseclib.cat EMsgctscfgMutExclOpt $CMDNAME a d
	ctscfg_help 0
	exit $RC
fi
if ((0 != ADDFLAG && 0 != UPDFLAG))
then
	RC=4
	$MSGCMD ctscfg ctseclib.cat EmsgctscfgMutExclOpt $CMDNAME a u
	ctscfg_help 0
	exit $RC
fi
if ((0 != UPDFLAG && 0 != DELFLAG))
then
	RC=4
	$MSGCMD ctscfg ctseclib.cat EmsgctscfgMutExclOpt $CMDNAME d u
	ctscfg_help 0
	exit $RC
fi
if [[ -n $USRCODE ]]
then
	REALCODE=$(ctscfg_verify_code $USRCODE)
	if ((0 == REALCODE))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgBadMpmCode $CMDNAME $USRCODE
		ctscfg_help 0
		exit $RC
	fi
fi
if [[ -n $USRPATH ]]
then
	ctscfg_verify_path $USRPATH
	RC=$?
	if ((0 != RC))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgBadMpmPath $CMDNAME $USRPATH
		ctscfg_help 0
		RC=4
		exit $RC
	fi
fi
if ((0 != ADDFLAG))
then
	if [[ -z $USRNAME || -z $USRCODE || -z $USRPATH ]]
	then
		RC=4
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingOpts $CMDNAME "-n  -c  -o"
	else
		if ((0 == USRPRIOR))
		then
			RC=4
			$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingOpts $CMDNAME "-p"
		fi
	fi
	if ((0 != RC))
	then
		ctscfg_help 0
		exit $RC
	fi
fi
if ((0 != UPDFLAG))
then
	if [[ -z $USRCODE && -z $USRNAME ]]
	then
		RC=4
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingOpts $CMDNAME "-n  -c"
	else
		if ((0 != USRPRIOR))
		then
			I=1
		fi
		if [[ -n $USRFLAGS ]]
		then
			I=1
		fi
		if ((0 == I))
		then
			RC=4
			$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingOpts $CMDNAME "-p  -f"
		fi
	fi
	if ((0 != RC))
	then
		ctscfg_help 0
		exit $RC
	fi
fi
if ((0 != DELFLAG))
then
	if [[ -z $USRCODE && -z $USRNAME ]]
	then
		RC=4
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgMissingOpts $CMDNAME "-n  -c"
	fi
	if ((0 != RC))
	then
		ctscfg_help 0
		exit $RC
	fi
fi

# Determine the configuration files we will be dealing with
CURRFILE=$(ctscfg_get_cfgpath)
DESTFILE=$OVRDIR/$CFGBASE

# Load current configuration
ctscfg_load_cfg $CURRFILE
RC=$?
if ((0 != RC))
then
	exit $RC
fi

# Handle ADD and DELETE requests
if ((0 != ADDFLAG || 0 != DELFLAG || 0 != UPDFLAG))
then
	# Dies user have permissions?
	ctscfg_perm $DESTFILE
	RC=$?
	if ((0 != RC))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgUsrPerm $CMDNAME /var/ct/cfg \
		        $CURRFILE
		exit $RC
	fi
	# See if the MPM we want to ADD or DELETE is in the configuration
	OLDPRIOR=$(ctscfg_find_in_cfg $REALCODE $USRNAME $USRPATH)
	RC=$?
	if ((23 == RC))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgInternalErr $CMDNAME 
		exit $RC
	fi
	if ((3 == RC))
	then
		if ((0 != ADDFLAG || 0 != UPDFLAG))
		then
			$MSGCMD ctscfg ctseclib.cat EmsgctscfgCodeNameFound $CMDNAME \
			        $USRCODE $USRNAME
		fi
		if ((0 != DELFLAG))
		then
			$MSGCMD ctscfg ctseclib.cat EmsgctscfgCodeNameDif $CMDNAME \
			        $USRCODE $USRNAME
		fi
		exit $RC
	fi
	if ((4 == RC && 0 != DELFLAG))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgNoMpmExist $CMDNAME 
		exit $RC
	fi
	if ((4 == RC && 0 != UPDFLAG))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgNoMpmExist $CMDNAME 
		exit $RC
	fi
	if ((0 == RC && 0 != ADDFLAG))
	then
		$MSGCMD ctscfg ctseclib.cat EmsgctscfgMpmExist $CMDNAME
		exit $RC
	fi
	# Perform request
	if ((0 != DELFLAG))
	then
		# Perform any DELETE request
		ctscfg_del $OLDPRIOR
		RC=$?
	else
		# Perform any ADD or UPDATE request
		if ((0 != UPDFLAG))
		then
			if [[ -z $USRNAME ]]
			then
				USRNAME="none"
			fi
			if [[ -z $USRPATH ]]
			then
				USRPATH="none"
			fi
		fi
		if ((0 == USRPRIOR || USRPRIOR == OLDPRIOR))
		then
			ctscfg_upd_flags $OLDPRIOR "$USRFLAGS"
			RC=$?
		else
			ctscfg_add $OLDPRIOR $USRPRIOR $USRNAME $REALCODE $USRPATH "$USRFLAGS"
			RC=$?
		fi
	fi
	if ((0 != RC))
	then
		exit $RC
	fi
	# Record updates to "override" configuration file location
	ctscfg_rec_cfg $DESTFILE
	RC=$?
	if ((0 != RC))
	then
		exit $RC
	fi
	# If modifications leave an empty configuration, call attention to it.
	if ((0 == MPMCNT))
	then
		$MSGCMD ctscfg ctseclib.cat ImsgctscfgEmptyCfg $CMDNAME
	fi
fi

# List the contents of the configuration - when combined with an ADD or
# DELETE request, this is done last to show the results.
if ((0 != LISTFLAG))
then
	ctscfg_list_cfg 1
fi

# Normal exit
exit 0
