--- old/usr/src/cmd/ldap/ns_ldap/idsconfig.sh Tue Sep 24 02:36:02 2013 +++ new/usr/src/cmd/ldap/ns_ldap/idsconfig.sh Tue Sep 24 02:36:01 2013 @@ -1,5 +1,7 @@ -#!/bin/sh +#!/bin/ksh93 # +# $Id: idsconfig.sh,v 66c6ff17d001 2013-09-24 00:35:53Z jel+illumos $ +# # CDDL HEADER START # # The contents of this file are subject to the terms of the @@ -19,270 +21,694 @@ # # CDDL HEADER END # -# -# idsconfig -- script to setup iDS 5.x/6.x/7.x for Native LDAP II. -# # Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. -# +# Portions Copyright 2013 Jens Elkner. -# -# display_msg(): Displays message corresponding to the tag passed in. -# -display_msg() -{ - case "$1" in - usage) cat < Get setup info from input file. - o Generate a server configuration output file. - v Verbose mode -EOF - ;; - backup_server) cat </dev/null ; } ) + integer I + for (( I=${#AX[@]}-1; I > 0; I++ )); do + X=${AX[$I]#*.} + [[ -n $X ]] && break + done + fi -HELP - ldbm database is an internal database for storage of our suffix data. - Database name must be alphanumeric due to Directory Server restriction. + # Actually one needs to init non-null/zero values, only. However, we want + # to have the keys registered ... -EOF - ;; - backup_help) cat < 2 )) && TMPF[SYNTAX]=0 + TMP[FILE]='' # tmp output filename - see nextFile() + TMP[LDAP_ROOTPWF]=${TMP[DIR]}/rootPWD # filename containing the root PW + TMP[WARN]='' # set if possibly incompatible DS + TMP[SUFFIX_OBJ]='' # suffix objectclass (long name) + TMP[SUFFIX_ATT]='' # suffix RDN attr name + TMP[SUFFIX_VAL]='' # suffix RDN attr value + TMP[DS_DBS_AVAIL]='' # next available suffix DBs + TMP[VLV_CMDS]='' # where VLV commands to exec are stored + # Set via getopts - just make sure, they are registered + [[ -z ${TMP[IN]} ]] && TMP[IN]='' # the config file to read + [[ -z ${TMP[OUT]} ]] && TMP[OUT]='' # the config file to write + # redirect ldap output to file instead of stdout + if (( TMPF[FD] > 2 )); then + exec 4>${TMP[DIR]}/ldap.out + fi + return 0 +} + +# internal +function showProgress { + typeset NUM=${ printf "%3d" TMPF[STEP] ; } + Log.info "${NUM}. " "$*" + (( TMPF[STEP]++ )) +} + +# internal +function nextFile { + typeset CMD="$1" FN="${2// /_}" EXT='cmd' + (( TMPF[STEP]++ )) + case "$1" in + add|modify|delete) CMD="ldap$1" EXT='ldif' ;; + sh) EXT='sh' + esac + TMP[FILE]=${ printf "${TMP[DIR]}/%03d_${CMD}_${FN}.${EXT}" ${TMPF[STEP]} ; } + [[ -e ${TMP[FILE]} ]] && Log.warn "Overwriting ${TMP[FILE]} ..." +} + +Man.addFunc show_vars '' '[+NAME?show_vars - List of all config variables and their values.] +[+DESCRIPTION?Lists all config variables and their current values in a more or less human readable manner.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} +' +function show_vars { + (( ! VERBOSE )) && return + + typeset S=${!STR[@]} OUT='Current non-NULL values:\n' + S=${ print ${S// /$'\n'} | sort ; } + for KEY in $S ; do + [[ -n ${STR[$KEY]} ]] && \ + OUT+=${ printf "%32s = '%s'" ${KEY} "${STR[$KEY]}" ; }'\n' + done + S=${!INT[@]} + S=${ print ${S// /$'\n'} | sort ; } + for KEY in $S ; do + (( INT[$KEY] )) && \ + OUT+=${ printf "%32s = %d" ${KEY} "${INT[$KEY]}" ; }'\n' + done + if (( ${#SSD[@]} )); then + OUT+=${ printf "%32s = (" 'SSD' ; } + for S in "${SSD[@]}" ; do + OUT+="\t\t${S}\n" + done + OUT+='\t)\n' + fi + print -u2 "${OUT}" +} + +Man.addFunc save_password '' '[+NAME?save_password - Save password to temporary file.] +[+DESCRIPTION?Save the current password from \bSTR[LDAP_ROOTPWD]]\b to the file \bTMP[LDAP_ROOTPWF]]\b.] +' +function save_password { + print "${STR[LDAP_ROOTPWD]}" >"${TMP[LDAP_ROOTPWF]}" +} +############################################################################# +# EOG +############################################################################# + + +############################################################################# +# config file stuff +############################################################################# +Man.addFunc create_config_file '' '[+NAME?create_config_file - Write config data to the specified file.] +[+DESCRIPTION?Write config data to \afile\a.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} +[+RETURN VALUES]{ + [+0?file was written successfully.] + [+>= 1?otherwise.] +} +[+SEE ALSO?\bload_config_file()\b.] +\n\n\afile\a +' +function create_config_file { + typeset OUT="$1" KEY + [[ -z ${OUT} ]] && return 1 + + # Create output file. + ( + print ' +# '"${OUT}"' - This file contains configuration information for +# LDAP naming services. Use the '"${PROG}"' tool to load it. +# +# WARNING: This file was generated by '"${PROG}"', and is intended to +# be loaded by '"${PROG}"' as is. DO NOT EDIT THIS FILE! +' + print 'typeset -A -i IIN=( )' + for KEY in "${!INT[@]}" ; do + print "IIN[${KEY}]=${INT[${KEY}]}" + done + print '\ntypeset -A SIN=( )' + for KEY in "${!STR[@]}" ; do + printf "SIN[${KEY}]=%q\n" "${STR[${KEY}]}" + done + print '\ntypeset -a SIN_SSD=( )' + for KEY in "${SSD[@]}" ; do + [[ -n ${KEY} ]] && printf "SIN_SSD+=( %q )\n" "${KEY}" + done + print "\n# End of ${OUT}" + ) >"${OUT}" +} + +# for backward compatibilty, but not documented: old format is bogus and should +# not be used anymore. +function source_old_config { + typeset IN="$1" TODO='' VNAME KEY VAL LINE + typeset -A OLDMAP=( + [DS_HOST]=IDS_SERVER + [DS_PORT]=IDS_PORT + [DS_DB]=IDS_DATABASE + [DS_TIMELIMIT]=IDS_TIMELIMIT + [DS_SIZELIMIT]=IDS_SIZELIMIT + [SSD]=LDAP_SERV_SRCH_DES + ) + + for KEY in "${!INT[@]}" ; do + VNAME=${OLDMAP[${KEY}]} + [[ -z ${VNAME} ]] && VNAME=${KEY} + typeset -n VAR=${VNAME} + INT[${KEY}]=${VAR} + TODO+="${VNAME} " + done + for KEY in "${!STR[@]}" ; do + VNAME=${OLDMAP[${KEY}]} + [[ -z ${VNAME} ]] && VNAME=${KEY} + typeset -n VAR=${VNAME} + STR[${KEY}]="${VAR}" + TODO+="${VNAME} " + done + while read LINE; do + if [[ ${LINE:0:19} == 'LDAP_SERV_SRCH_DES=' ]]; then + VAL="${LINE:19}" + [[ -n ${VAL} ]] && SSD+=( "${VAL}" ) + fi + done < "${IN}" + + # cleanup "global" namespace + [[ -n ${TODO} ]] && eval "unset ${TODO}" +} + +Man.addFunc load_config_file '' '[+NAME?load_config_file - load an '"${PROG}"' config file.] +[+DESCRIPTION?Loads the initial config from \afile\a, which has been generated in a previous run by '"${PROG}"'. Sets \bTMPF[DEL_OLD_PROFILE]]\b to 1 on success.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66? \afile\a is not a file or unreadable.] +} +[+SEE ALSO?\bsave_password()\b, \bshow_vars()\b.] +\n\n\afile\a +' +function load_config_file { + typeset IN="$1" KEY VAL + integer I + [[ ! -f ${IN} ]] && Log.fatal "${IN} is not a file" && return 66 + [[ ! -r ${IN} ]] && Log.fatal "${IN} is not readable" && return 67 + + # source in the stuff - not nice but less work ;-) + . "${IN}" + + KEY=${ typeset -p IIN ; } + if [[ -z ${KEY} ]]; then + source_old_config "${IN}" || return 68 + else + for KEY in "${!INT[@]}" ; do + INT[${KEY}]=${IIN[${KEY}]} + done + for KEY in "${!STR[@]}" ; do + STR[${KEY}]="${SIN[${KEY}]}" + done + for KEY in "${SIN_SSD[@]}" ; do + [[ -n ${KEY} ]] && SSD+=( "${KEY}" ) + done + # cleanup "global" namespace + unset IIN SIN SIN_SSD + fi + + TMPF[DEL_OLD_PROFILE]=1 + save_password + show_vars + + return 0 +} +############################################################################# +# End Of config file stuff +############################################################################# + + +############################################################################ +# Basic [menu] stuff +############################################################################ +Man.addFunc display_msg '' '[+NAME?display_msg - display a message] +[+DESCRIPTION?Display a message corresponding to the \atag\a passed in.] +[+ENVIRONMENT VARIABLES]{'"${ Man.varUsage INT STR TMP ; }" '} +\n\n\atag\a +' +function display_msg { + typeset X + integer I + + case "$1" in + backup_server) + print ' +It is strongly recommended that you BACKUP the directory server +before running '"${PROG}"'. + +Hit Ctrl-C at any time before the final confirmation to exit.\n' + ;; + cred_level_menu) + print 'The following are the supported credential levels:' + for (( I=0; I < ${#CRED_LEVELS[@]}; I++ )); do + printf ' %2d %s\n' $((I+1)) "${CRED_LEVELS[I]}" + done + ;; + auth_method_menu) + print 'The following are the supported Client Authentication Methods:' + for (( I=0; I < ${#AUTH_METHODS[@]}; I++ )); do + printf ' %-2d %s\n' $((I+1)) "${AUTH_METHODS[I]}" + done + ;; + srvauth_method_menu) + print 'The following are the supported Service Authentication Methods:' + # skip 'none' + for (( I=1; I < ${#AUTH_METHODS[@]}; I++ )); do + printf ' %-2d %s\n' $((I)) "${AUTH_METHODS[I]}" + done + ;; + prompt_ssd_menu) + print ' A Add a Service Search Descriptor (SSD) + D Delete a SSD + M Modify a SSD + P Display all SSDs + H Help + X Clear all SSDs + + Q Exit menu' + ;; + summary_menu) + typeset SUFFIX_INFO='' + typeset DB_INFO='' + + if (( TMPF[NEED_CREATE_SUFFIX] )); then + SUFFIX_INFO=' + Suffix to create : '"${STR[LDAP_SUFFIX]}"'\n' + (( TMPF[NEED_CREATE_BACKEND] )) && DB_INFO=' + Database to create : '"${STR[DS_DB]}"'\n' + fi + typeset BASEDN + BASEDN="${STR[LDAP_BASEDN]}${STR[SUFFIX_INFO]}${STR[DB_INFO]}" + + print ' Summary of Configuration + + 1 Domain to serve : '"${STR[LDAP_DOMAIN]}"' + 2 Base DN to setup : '"${BASEDN}"' + 3 Profile name to create : '"${STR[LDAP_PROFILE_NAME]}"' + 4 Default Server List : '"${STR[LDAP_SERVER_LIST]}"' + 5 Preferred Server List : '"${STR[LDAP_PREF_SRVLIST]}"' + 6 Default Search Scope : '"${STR[LDAP_SEARCH_SCOPE]}"' + 7 Credential Level : '"${STR[LDAP_CRED_LEVEL]}"' + 8 Authentication Method : '"${STR[LDAP_AUTHMETHOD]}"' + 9 Enable Follow Referrals : '"${INT[LDAP_FOLLOWREF]}"' + 10 DS Time Limit : '"${INT[DS_TIMELIMIT]}"' + 11 DS Size Limit : '"${INT[DS_SIZELIMIT]}"' + 12 Enable crypt password storage : '"${INT[NEED_CRYPT]}"' + 13 Service Auth Method pam_ldap : '"${STR[LDAP_SRV_AUTHMETHOD_PAM]}"' + 14 Service Auth Method keyserv : '"${STR[LDAP_SRV_AUTHMETHOD_KEY]}"' + 15 Service Auth Method passwd-cmd: '"${STR[LDAP_SRV_AUTHMETHOD_CMD]}"' + 16 Search Time Limit : '"${INT[LDAP_SEARCH_TIME_LIMIT]}"' + 17 Profile Time to Live : '"${INT[LDAP_PROFILE_TTL]}"' + 18 Bind Limit : '"${INT[LDAP_BIND_LIMIT]}"' + 19 Enable shadow update : '"${INT[LDAP_ENABLE_SHADOW_UPDATE]}"' + 20 Service Search Descriptors Menu\n' + ;; + sorry) + print ' +HELP - No help is available for this topic.\n' + ;; + create_suffix_help) + print ' +HELP - Our Base DN is "'"${STR[LDAP_BASEDN]}"'" + and we need to create a Directory Suffix, + which can be equal to Base DN itself or be any of Base DN parents. + All intermediate entries up to suffix will be created on demand.\n' + ;; + enter_ldbm_db_help) + print ' +HELP - database backend is an internal database for storage of our suffix data. + Backend name must be alphanumeric due to Directory Server restriction. +' + ;; + backup_help) + print ' +HELP - Since '"${PROG}"' modifies the directory server configuration, + it is strongly recommended that you backup the server prior + to running this utility. This is especially true if the server + being configured is a production server.\n' + ;; + port_help) + print ' +HELP - Enter the port number the directory server is configured to + use for LDAP.\n' + ;; + adminport_help) + print ' +HELP - Enter the Admin port number the directory server is configured to + use for for DS administration commands.\n' + ;; + domain_help) + print ' HELP - This is the DNS domain name this server will be serving. You must provide this name even if the server is not going to be populated with hostnames. Any unqualified hostname stored in the directory - will be fully qualified using this DNS domain name. - -EOF - ;; - basedn_help) cat <= -1 from the user.] +[+DESCRIPTION?Read a string from stdin until it represents a number >= -1. Arguments:] { - while : - do - get_number "$1" "$2" "$3" - if is_negative $ANS - then - if [ "$ANS" = "-1" ]; then - break # -1 is OK, so break. - else # Need to re-enter number. - ${ECHO} "Invalid number: please enter -1 or positive number." - fi - else - break # Positive number - fi - done + [+prompt?instruction/comment/description/question to show.] + [+default?Default value to use if no answer/empty string was read.] + [+helpTag?Tag of the help message to show, when the string read is h|H|?|help|Help.] } +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NUM ANS ; }" '} +[+SEE ALSO?\bget_number()\b] +\n\n\aprompt\a \adefault\a [\ahelpTag\a] +' +function get_negone_num { + while : ; do + get_number "$1" "$2" "$3" + (( NUM >= -1 )) && break + Log.warn 'Invalid number! Enter a number >= -1' + done +} - -# -# get_passwd(): Reads a password from the user and verify with second. -# $@ instruction/comment/description/question -# -get_passwd() +Man.addFunc get_passwd '' '[+NAME?get_passwd - get a password from the user.] +[+DESCRIPTION?Read a password from stdin and verify with second until both match. Password gets not echoed to stdout. Arguments:] { - [ $DEBUG -eq 1 ] && ${ECHO} "In get_passwd()" + [+prompt?instruction/comment/description/question to show.] + [+default?Default value to use if no answer/empty string was read.] +} +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} +[+SEE ALSO?\bget_ans()\b] +\n\n\aprompt\a [\adefault\a] +' +function get_passwd { + typeset _PASS1='' _PASS2='' - # Temporary PASSWD variables - _PASS1="" - _PASS2="" + ${STTY} -echo # Turn echo OFF - /usr/bin/stty -echo # Turn echo OFF + # continue until passwd and re-entered passwd match + while : ; do + ANS='' + # Don't allow NULL for first try. + while [[ -z ${ANS} ]]; do + get_ans "$@" + [[ -z ${ANS} ]] && \ + print -u2 && Log.warn 'Empty password not allowed' + done + _PASS1="${ANS}" # Store first try. - # Endless loop that continues until passwd and re-entered passwd - # match. - while : - do - ANS="" # Set ANS to NULL. + print + get_ans 'Re-enter password:' + _PASS2="${ANS}" - # Don't allow NULL for first try. - while [ "$ANS" = "" ] - do - get_ans "$@" - [ "$ANS" = "" ] && ${ECHO} "" && ${ECHO} "NULL passwd not allowed!" + [[ ${_PASS1} == ${_PASS2} ]] && break + print -u2 && Log.warn "passwords don't match - try again" done - _PASS1=$ANS # Store first try. - # Get second try. - ${ECHO} "" - get_ans "Re-enter passwd:" - _PASS2=$ANS + ${STTY} echo # Turn echo ON - # Test if passwords are identical. - if [ "$_PASS1" = "$_PASS2" ]; then - break - fi - - # Move cursor down to next line and print ERROR message. - ${ECHO} "" - ${ECHO} "ERROR: passwords don't match; try again." - done - - /usr/bin/stty echo # Turn echo ON - - ${ECHO} "" + print } - -# -# get_passwd_nochk(): Reads a password from the user w/o check. -# $@ instruction/comment/description/question -# -get_passwd_nochk() +Man.addFunc get_passwd_nochk '' '[+NAME?get_passwd_nochk - get a password from the user w/o check.] +[+DESCRIPTION?Read a password from stdin. Actually the same as \bget_ans()\b but echoing characters read to stdout is switched off. Arguments:] { - [ $DEBUG -eq 1 ] && ${ECHO} "In get_passwd_nochk()" - - /usr/bin/stty -echo # Turn echo OFF - - get_ans "$@" - - /usr/bin/stty echo # Turn echo ON - - ${ECHO} "" + [+prompt?instruction/comment/description/question to show.] + [+default?Default value to use if no answer/empty string was read.] } +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} +[+SEE ALSO?\bget_ans()\b] +\n\n\aprompt\a [\adefault\a] +' +function get_passwd_nochk { + ${STTY} -echo # Turn echo OFF + get_ans "$@" + ${STTY} echo # Turn echo ON + print +} - -# -# get_menu_choice(): Get a menu choice from user. Continue prompting -# till the choice is in required range. -# $1 .. Message text. -# $2 .. min value -# $3 .. max value -# $4 .. OPTIONAL: default value -# -# Return value: -# MN_CH will contain the value selected. -# -get_menu_choice() +Man.addFunc get_menu_choice '' '[+NAME?get_menu_choice - get a valid menu choice number.] +[+DESCRIPTION?Get a menu choice from user. Continue prompting until the choice is in required range. Arguments:] { - # Check for req parameter. - if [ $# -lt 3 ]; then - ${ECHO} "get_menu_choice(): Did not get required parameters." - return 1 - fi + [+prompt?Message text.] + [+min?Min value.] + [+max?Max value.] + [+default?Default value to use if no answer/empty string was read.] + [+helpTag?If given, "h" is allowed as well and the help message for this tag gets shown, when it got typed and before the prompt is shown the first time. The prompt should contain a hint in this case.] + [+menuTag?If given the menu choices for the given menu gest displayed at start and after each help text.] +} +[+RETURN VALUES]{ + [+-1?on error (invalid parameters).] + [+>= \amin\a?the selected value.] +} +[+SEE ALSO?\bis_numeric()\b, \bget_ans()\b, \bdisplay_msg()\b.] +\n\n\aprompt\a \amin\a \amax\a [\adefault\a [\ahelpTag\a]] +' +function get_menu_choice { + if (( $# < 3 )); then + Log.warn "${.sh.fun}(): Did not get required parameters" + return -1 + fi - while : - do - get_ans "$1" "$4" - MN_CH=$ANS - is_negative $MN_CH - if [ $? -eq 1 ]; then - if [ $MN_CH -ge $2 ]; then - if [ $MN_CH -le $3 ]; then - return + [[ -n $6 ]] && display_msg "$6" + while : ; do + get_ans "$1" "$4" + if is_numeric ${ANS} && NUM=${ANS} && (( NUM >= $2 )) && (( NUM <= $3 )) + then + return ${NUM} fi - fi - fi - ${ECHO} "Invalid choice: $MN_CH" - done + if [[ -n $5 && ${ANS} == 'h' ]]; then + display_msg "$5" + [[ -n $6 ]] && display_msg "$6" + continue + fi + Log.warn "Invalid choice! Enter a number in the range $2 .. $3" + done + return -1 } - -# -# get_confirm(): Get confirmation from the user. (Y/Yes or N/No) -# $1 - Message -# $2 - default value. -# -get_confirm() +Man.addFunc get_confirm '' '[+NAME?get_confirm - Get confirmation from the user.] +[+DESCRIPTION?Read a string from stdin until it matches Y|y|Yes|yes|YES|N|n|No|no|NO or an empty string. Arguments:] { - _ANSWER= + [+prompt?instruction/comment/description/question to show.] + [+default?Default value to use if no answer/empty string was read.] + [+helpTag?Tag of the help message to show, when the string read is h|H|?|help|Help.] +} +[+RETURN VALUES]{ + [+0?for NO] + [+1?for YES] +} +[+SEE ALSO?\bdisplay_msg()\b] +\n\n\aprompt\a \adefault\a [\ahelpTag\a] +' +function get_confirm { + typeset _ANSWER='' - while : - do - # Display Internal ERROR if $2 not set. - if [ -z "$2" ] - then - ${ECHO} "INTERNAL ERROR: get_confirm requires 2 args, 3rd is optional." - exit 2 + if [[ -z $2 ]]; then + Log.fatal "INTERNAL ERROR: ${.sh.fun} requires 2 args, 3rd is optional" + exit 2 fi - # Display prompt. - ${ECHO} "$1 [$2] \c" + while : ; do + read _ANSWER?"$1 [$2] " + [[ -z ${_ANSWER} ]] && _ANSWER="$2" - # Get the ANSWER. - read _ANSWER - if [ "$_ANSWER" = "" ] && [ -n "$2" ] ; then - _ANSWER=$2 - fi - case "$_ANSWER" in - [Yy] | yes | Yes | YES) return 1 ;; - [Nn] | no | No | NO) return 0 ;; - [Hh] | help | Help | \?) display_msg ${3:-sorry};; - * ) ${ECHO} "Please enter y or n." ;; - esac - done + case "${_ANSWER}" in + [Yy] | yes | Yes | YES) return 1 ;; + [Nn] | no | No | NO) return 0 ;; + [Hh?] | help | Help) display_msg ${3:-sorry} ;; + * ) Log.warn 'Please enter y or n' ;; + esac + done } - -# -# get_confirm_nodef(): Get confirmation from the user. (Y/Yes or N/No) -# No default value supported. -# -get_confirm_nodef() +Man.addFunc get_confirm_nodef '' '[+NAME?get_confirm_nodef - Get confirmation from the user.] +[+DESCRIPTION?Read a string from stdin until it matches Y|y|Yes|yes|YES|N|n|No|no|NO. No default value and help tag supported. Arguments:] { - _ANSWER= + [+prompt?instruction/comment/description/question to show.] +} +[+RETURN VALUES]{ + [+0?for NO] + [+1?for YES] +} +\n\n[\aprompt\a]... +' +function get_confirm_nodef { + typeset _ANSWER='' - while : - do - ${ECHO} "$@ \c" - read _ANSWER - case "$_ANSWER" in - [Yy] | yes | Yes | YES) return 1 ;; - [Nn] | no | No | NO) return 0 ;; - * ) ${ECHO} "Please enter y or n." ;; - esac - done + while : ; do + read _ANSWER?"$@ " + case "${_ANSWER}" in + [Yy] | yes | Yes | YES) return 1 ;; + [Nn] | no | No | NO) return 0 ;; + * ) Log.warn 'Please enter y or n' ;; + esac + done } +############################################################################ +# End of basic [menu] stuff +############################################################################ -# -# is_numeric(): Tells is a string is numeric. -# 0 = Numeric -# 1 = NOT Numeric -# -is_numeric() -{ - # Check for parameter. - if [ $# -ne 1 ]; then - return 1 - fi +###################################################################### +# FUNCTIONS FOR prompt_config_info() START HERE. +###################################################################### +Man.addFunc get_ids_server '' '[+NAME?get_ids_server - Prompt for DS server name.] +[+DESCRIPTION?Ask the user for the DS hostname, store it into \bSTR[DS_HOST]]\b and adjusts \bCON_ARGS\b.] +[+ENVIRONMENT VARIABLES]{'"${ Man.varUsage CON_ARGS ; }"'} +[+SEE ALSO?\bget_ans()\b, \bget_ids_port()\b, \bping\b(1M).] +' +function get_ids_server { + typeset SRV="${STR[DS_HOST]}" + typeset OS=${ uname -s ; } - # Determine if numeric. - expr "$1" + 1 > /dev/null 2>&1 - if [ $? -ge 2 ]; then - return 1 - fi + while : ; do + get_ans "Enter the Directory Server's hostname to setup:" \ + "${STR[DS_HOST]}" + SRV="${ANS}" - # Made it here, it's Numeric. - return 0 + # Ping server to see if alive. If reachable break out of loop + if [[ ${OS} == 'SunOS' ]]; then + ${PING} "${SRV}" 3 >/dev/null 2>&1 && break + else + # assume Linux + ${PING} -W 3 "${SRV}" >/dev/null 2>&1 && break + fi + Log.warn "Server '${SRV}' is invalid or unreachable" + done + STR[DS_HOST]="${SRV}" + + # Set CON_ARGS since values might have changed + CON_ARGS="-h ${SRV} -p ${INT[DS_PORT]}" } +Man.addFunc get_ids_port '' '[+NAME?get_ids_port - Prompt for DS port number.] +[+DESCRIPTION?Ask the user for the DS port number and host, store it into \bINT[DS_PORT]]\b, \bSTR[DS_HOST]]\b and adjust \bCON_ARGS\b.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage CON_ARGS ; }" '} +[+SEE ALSO?\bget_number()\b, \bget_ids_server()\b, \bldapsearch\b(1).] +\n\n\aarg\a' +function get_ids_port { + typeset ASK='Enter the port number for the directory server (h=help):' + typeset HELP='port_help' KEY='DS_PORT' -# -# not_numeric(): Reverses the return values of is_numeric. Useful -# for if and while statements that want to test for -# non-numeric data. -# 0 = NOT Numeric -# 1 = Numeric -# -not_numeric() -{ - is_numeric $1 - if [ $? -eq 0 ]; then - return 1 - else - return 0 - fi + integer PORT + while : ; do + # Enter port number + get_number "${ASK}" ${INT[${KEY}]} "${HELP}" + PORT=${ANS} + # Do a simple search to check hostname and LDAP port number + if [[ -z $1 ]]; then + if ${LDAPSEARCH} -h ${STR[DS_HOST]} -p ${PORT} -b '' \ + -s base 'objectclass=*' > /dev/null 2>&1 + then + break + fi + Log.warn "Invalid host or port '${STR[DS_HOST]}:${PORT}'" + get_ids_server + else + break + fi + done + INT[${KEY}]=${PORT} + [[ -n $1 ]] && return + + # Set CON_ARGS since values might have changed + CON_ARGS="-h ${STR[DS_HOST]} -p ${PORT}" } +Man.addFunc chk_ids_version '' '[+NAME?chk_ids_version - Read the DS version info.] +[+DESCRIPTION?Query the DS for version info and store it into \bTMP[DS_INFO]]\b as "ProductName MajorVersionNum MinorVersionNum". If the DS is OpenDS or OpenDJ \bTMPF[IS_OPENDJ]]\b gets set to 1, 0 otherwise. If the DS seems to be unsupported by this script, an appropriate message gets stored into \bTMP[WARN]]\b.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?on fatal error (got no info).] +} +[+SEE ALSO?\bldapsearch\b(1).] +' +function chk_ids_version { + typeset PROD='' X LINE + typeset -a SPLIT=( ) + integer MAJOR=0 MINOR=0 + ${LDAPSEARCH} ${CON_ARGS} -b cn=monitor -s base 'objectclass=*' \ + 2>/dev/null | \ + while read LINE ; do + if [[ ${LINE:0:14} == 'vendorVersion=' ]]; then + # OpenD* + X=${LINE:14} + X=${X//Directory Server } # usually <= 2.3.x + PROD=${X%% *} + SPLIT=( ${.sh.match//./ } ) + MAJOR=${SPLIT[0]} + MINOR=${SPLIT[1]} + break + elif [[ ${LINE:0:8} == 'version=' ]]; then + # *DSEE + X=${LINE:8} + PROD=${X%%/*} + X=${.sh.match#/} + SPLIT=( ${X//./ } ) + MAJOR=${SPLIT[0]} + MINOR=${SPLIT[1]} + break + fi + done + if [[ -z ${PROD} ]] || (( MAJOR == 0 )); then + Log.fatal 'Can not determine the version number of the DS' + return 66 + fi -# -# is_negative(): Tells is a Numeric value is less than zero. -# 0 = Negative Numeric -# 1 = Positive Numeric -# 2 = NOT Numeric -# -is_negative() -{ - # Check for parameter. - if [ $# -ne 1 ]; then - return 1 - fi + TMP[DS_INFO]="${PROD} ${MAJOR} ${MINOR}" - # Determine if numeric. Can't use expr because -0 is - # considered positive?? - if is_numeric $1; then - case "$1" in - -*) return 0 ;; # Negative Numeric - *) return 1 ;; # Positive Numeric - esac - else - return 2 + # for easier maintainance we don't put it into a single expr + X='' + if [[ ${PROD} == 'OpenDS' || ${PROD} == 'OpenDJ' ]]; then + TMPF[IS_OPENDJ]=1 + (( MAJOR < 2 )) && X='1' + elif (( MAJOR < 5 || (7 < MAJOR && MAJOR < 11) )); then + # not a supported DSEE version + TMPF[IS_OPENDJ]=0 + X='1' fi + if [[ -n ${X} ]]; then + TMP[WARN]="$PROG only works with DSEE version 5.x, 6.x, 7.x, " + TMP[WARN]+='ODSEE 11g and OpenDS/OpenDJ 2.x' + Log.warn "${TMP[WARN]}" + fi + Log.printMarker + Log.info "Detected DS: ${PROD} ${MAJOR}.${MINOR}" + Log.printMarker + return 0 } - -# -# check_domainname(): check validity of a domain name. Currently we check -# that it has at least two components. -# $1 the domain name to be checked -# -check_domainname() -{ - if [ ! -z "$1" ] - then - t=`expr "$1" : '[^.]\{1,\}[.][^.]\{1,\}'` - if [ "$t" = 0 ] - then - return 1 - fi - fi - return 0 +Man.addFunc get_dirmgr_dn '' '[+NAME?get_dirmgr_dn - get the directory manger DN.] +[+DESCRIPTION?Ask the user for the directory manger DN and store it into \bSTR[LDAP_ROOTDN]]\b and adjust \bAUTH_ARGS\b.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage AUTH_ARGS ; }" '} +[+SEE ALSO?\bget_ans()\b.] +' +function get_dirmgr_dn { + get_ans 'Enter the directory manager DN:' "${STR[LDAP_ROOTDN]}" + STR[LDAP_ROOTDN]="${ANS}" + AUTH_ARGS=( '-D' "${STR[LDAP_ROOTDN]}" '-j' "${TMP[LDAP_ROOTPWF]}" ) } +Man.addFunc get_dirmgr_pw '' '[+NAME?get_dirmgr_pw - get the Root DN password.] +[+DESCRIPTION?Ask the user for the Root DN (\bSTR[LDAP_ROOTDN]]\b), store it into \bSTR[LDAP_ROOTPWD]]\b as well as a temp file for later use. Finally adjust \bAUTH_ARGS\b and check, whether the changes work.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage AUTH_ARGS ; }" '} +[+SEE ALSO?\bget_passwd_nochk()\b, \bsave_password()\b, \bldapsearch\b(1).] +' +function get_dirmgr_pw { + typeset RES -# -# check_baseDN(): check validity of the baseDN name. -# $1 the baseDN name to be checked -# -# NOTE: The check_baseDN function does not catch all invalid DN's. -# Its purpose is to reduce the number of invalid DN's to -# get past the input routine. The invalid DN's will be -# caught by the LDAP server when they are attempted to be -# created. -# -check_baseDN() -{ - ck_DN=$1 - ${ECHO} " Checking LDAP Base DN ..." - if [ ! -z "$ck_DN" ]; then - [ $DEBUG -eq 1 ] && ${ECHO} "Checking baseDN: $ck_DN" - # Check for = (assignment operator) - ${ECHO} "$ck_DN" | ${GREP} "=" > /dev/null 2>&1 - if [ $? -ne 0 ]; then - [ $DEBUG -eq 1 ] && ${ECHO} "check_baseDN: No '=' in baseDN." - return 1 - fi - - # Check all keys. - while : - do - # Get first key. - dkey=`${ECHO} $ck_DN | cut -d'=' -f1` + while : ; do + get_passwd_nochk "Enter passwd for ${STR[LDAP_ROOTDN]} :" + STR[LDAP_ROOTPWD]="${ANS}" # stored for create_config_file(), only + save_password + AUTH_ARGS=( '-D' "${STR[LDAP_ROOTDN]}" '-j' "${TMP[LDAP_ROOTPWF]}" ) - # Check that the key string is valid - check_attrName $dkey - if [ $? -ne 0 ]; then - [ $DEBUG -eq 1 ] && ${ECHO} "check_baseDN: invalid key=${dkey}" - return 1 - fi + # Verify that ROOTDN and ROOTPWD are valid + RES=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b '' -s base 'objectclass=*' supportedLDAPVersion 2>&1 ; } + (( $? == 0 )) && break # Both are valid. - [ $DEBUG -eq 1 ] && ${ECHO} "check_baseDN: valid key=${dkey}" - - # Remove first key from DN - ck_DN=`${ECHO} $ck_DN | cut -s -d',' -f2-` - - # Break loop if nothing left. - if [ "$ck_DN" = "" ]; then - break - fi - done - fi - return 0 + if [[ ${RES} =~ (credential|no password) ]]; then + Log.warn 'Password for Root DN is invalid' + else + Log.warn "Root DN '${STR[LDAP_ROOTDN]}' is invalid" + get_dirmgr_dn + fi + done } - -# -# domain_2_dc(): Convert a domain name into dc string. -# $1 .. Domain name. -# -domain_2_dc() -{ - _DOM=$1 # Domain parameter. - _DOM_2_DC="" # Return value from function. - _FIRST=1 # Flag for first time. - - export _DOM_2_DC # Make visible for others. - - # Convert "."'s to spaces for "for" loop. - domtmp="`${ECHO} ${_DOM} | tr '.' ' '`" - for i in $domtmp; do - if [ $_FIRST -eq 1 ]; then - _DOM_2_DC="dc=${i}" - _FIRST=0 - else - _DOM_2_DC="${_DOM_2_DC},dc=${i}" - fi +Man.addFunc get_domain '' '[+NAME?get_domain - Ask user for domain to be served.] +[+DESCRIPTION?Ask the user for the Domain that will be served by the LDAP server and store it into \bSTR[LDAP_DOMAIN]]\b.] +[+SEE ALSO?\bget_ans()\b.] +' +function get_domain { + while : ; do + get_ans 'Enter the domainname to be served (h=help):' \ + "${STR[LDAP_DOMAIN]}" + case "${ANS}" in + [Hh?] | help | Help) display_msg 'domain_help' ; continue ;; + esac + # check, whether it has at least 2 dot separated components + [[ ${ANS} =~ [^.]+\.[^.]+ ]] && break + Log.warn "Invalid domainname '${ANS}'" done + STR[LDAP_DOMAIN]="${ANS}" } +Man.addFunc getDSobjectclasses '' '[+NAME?getDSobjectclasses - get all DS schema objectclasses.] +[+DESCRIPTION?Fetch all objectclasses definitions of the DS via \bcn=schema\b and cache them into \bOID2ODEF\b. Once successfully fetched, this function does nothing but return 0.] +[+RETURN VALUES]{ + [+0?on success.] + [+>0?failed to retrieve attribute definitions.] +} +' +function getDSobjectclasses { + (( ${#OID2ODEF[@]} )) && return 0 -# -# is_root_user(): Check to see if logged in as root user. -# -is_root_user() -{ - case `id` in - uid=0\(root\)*) return 0 ;; - * ) return 1 ;; - esac + typeset AT OID NAME TAIL + set -o pipefail + ${LDAPSEARCH} ${CON_ARGS} -b cn=schema -s base 'objectclass=*' \ + objectclasses 2>/dev/null | \ + while read AT OID NAME TAIL ; do + [[ ${AT} != 'objectclasses=(' ]] && continue + TAIL=${TAIL%%+( )$')'}; + OID2ODEF["${OID}"]="${TAIL}" + : # always return 0 + done } +Man.addFunc getDSattributes '' '[+NAME?getDSattributes - get all DS schema attributes.] +[+DESCRIPTION?Fetch all attribute definitions of the DS via \bcn=schema\b and cache them into \bOID2ADEF\b. Also populate the hash map \bANAME2OID\b for all the attributes definied. Once successfully fetched, this function does nothing but return 0.] +[+RETURN VALUES]{ + [+0?on success.] + [+>0?failed to retrieve attribute definitions.] +} +' +function getDSattributes { + (( ${#ANAME2OID[@]} )) && return 0 -# -# parse_arg(): Parses the command line arguments and sets the -# appropriate variables. -# -parse_arg() -{ - while getopts "dvhi:o:" ARG - do - case $ARG in - d) DEBUG=1;; - v) VERB="";; - i) INPUT_FILE=$OPTARG;; - o) OUTPUT_FILE=$OPTARG;; - \?) display_msg usage - exit 1;; - *) ${ECHO} "**ERROR: Supported option missing handler!" - display_msg usage - exit 1;; - esac - done - return `expr $OPTIND - 1` + typeset AT OID NAME TAIL + typeset -l ALIAS + set -o pipefail + ${LDAPSEARCH} ${CON_ARGS} -b cn=schema -s base 'objectclass=*' \ + attributeTypes 2>/dev/null | \ + while read AT OID NAME TAIL ; do + [[ ${AT} != 'attributeTypes=(' ]] && continue + TAIL=${TAIL%%+( )$')'}; + OID2ADEF["${OID}"]="${TAIL}" + # 'name' + if [[ ${TAIL:0:1} == "'" ]]; then + NAME=${TAIL:1} + ALIAS=${NAME%%"'"*} + ANAME2OID["${ALIAS}"]="${OID}" + continue + fi + # ( 'name' 'name 1' ... ) + NAME=${TAIL%%$')'*} + NAME=${NAME##$'('*([[:space:]])} + while [[ ${NAME} == ~(E)"'"([^"'"]+)"'" ]]; do + ALIAS=${.sh.match[1]} + ANAME2OID["${ALIAS}"]="${OID}" + NAME=${NAME:${#ALIAS}+2} + NAME=${NAME#*([[:space:]])} + done + : # always return 0 + done } +Man.addFunc check_attrName '' '[+NAME?check_attrName - validate an attribute name.] +[+DESCRIPTION?Check that \akey\a is a valid attribute name.] +[+RETURN VALUES]{ + [0?key is a valid name.] + [1?key is a invalid name.] + [66?failed to fetch attribute definitions from server.] +} +[+SEE ALSO?\bldapsearch\b(1).] +\n\n\akey\a +' +function check_attrName { + typeset KEY=$1 -# -# init(): initializes variables and options -# -init() -{ - # General variables. - PROG=`basename $0` # Program name - PID=$$ # Program ID - VERB='> /dev/null 2>&1' # NULL or "> /dev/null" - ECHO="/bin/echo" # print message on screen - EVAL="eval" # eval or echo - EGREP="/usr/bin/egrep" - GREP="/usr/bin/grep" - DEBUG=0 # Set Debug OFF - BACKUP=no_ldap # backup suffix - HOST="" # NULL or - NAWK="/usr/bin/nawk" - RM="/usr/bin/rm" - WC="/usr/bin/wc" - CAT="/usr/bin/cat" - SED="/usr/bin/sed" - MV="/usr/bin/mv" + if ! getDSattributes ; then + Log.fatal 'Unable to fetch attribute definitions from server' + return 66 + fi + [[ -z ${KEY} ]] && return 1 + if [[ ${KEY} =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + # OID value + [[ -n ${OID2ADEF[${KEY}]} ]] && return 0 + else + # symbol. name + KEY=${ANAME2OID[${KEY}]} + # if have a mapping, than we also have a definition for it - no need + # to double check + [[ -n ${KEY} ]] && return 0 + fi + return 1 +} - DOM="" # Set to NULL - # If DNS domain (resolv.conf) exists use that, otherwise use domainname. - if [ -f /etc/resolv.conf ]; then - DOM=`/usr/xpg4/bin/grep -i -E '^domain|^search' /etc/resolv.conf \ - | awk '{ print $2 }' | tail -1` - fi +Man.addFunc check_baseDN '' '[+NAME?check_baseDN - check validity of the baseDN name.] +[+DESCRIPTION?Check that \abaseDN\a is a valid base DN.] +[+RETURN VALUES]{ + [+0?baseDN is a valid.] + [+>= 1?baseDN is a invalid.] +} +[+SEE ALSO?\bcheck_attrName()\b.] +[+NOTES?This function does not catch all invalid DNs. Its purpose is to reduce the number of invalid DNs to get past the input routine. The invalid DNs will be caught by the LDAP server when they are attempted to be created.] +\n\n\abaseDN\a +' +function check_baseDN { + # NOTE: we preserve spaces! And try to report all errors instead of just one + Log.info 'Checking LDAP Base DN ...' + [[ -z $1 ]] && return 0 - # If for any reason the DOM did not get set (error'd resolv.conf) set - # DOM to the domainname command's output. - if [ "$DOM" = "" ]; then - DOM=`domainname` # domain from domainname command. - fi + # NOTE: when fancy debug via trap is enabled, splitting via IFS does not + # work: IFS=',' PAIRS=( $1 ) - so we do it manually - STEP=1 - INTERACTIVE=1 # 0 = on, 1 = off (For input file mode) - DEL_OLD_PROFILE=0 # 0 (default), 1 = delete old profile. + typeset PAIR KEY VAL TAIL="${1}," + typeset -A UNIQ_KEYS=( ) # avoid repeated lookup of the same attrName + integer ERR=0 + while [[ -n ${TAIL} ]]; do + TAIL=${TAIL#*,} + PAIR=${.sh.match%,} + VAL=${PAIR#*=} + KEY=${.sh.match%=} + if [[ -z ${KEY} || -z ${VAL} ]]; then + Log.warn "Invalid key=value pair '${PAIR}'" + (( ERR++ )) + continue + fi + UNIQ_KEYS[${KEY}]=1 + done - # idsconfig specific variables. - INPUT_FILE="" - OUTPUT_FILE="" - LDAP_ENABLE_SHADOW_UPDATE="FALSE" - NEED_PROXY=0 # 0 = No Proxy, 1 = Create Proxy. - NEED_ADMIN=0 # 0 = No Admin, 1 = Create Admin. - NEED_HOSTACL=0 # 0 = No Host ACL, 1 = Create Host ACL. - EXISTING_PROFILE=0 - LDAP_PROXYAGENT="" - LDAP_ADMINDN="" - LDAP_SUFFIX="" - LDAP_DOMAIN=$DOM # domainname on Server (default value) - GEN_CMD="" - PROXY_ACI_NAME="LDAP_Naming_Services_proxy_password_read" + for KEY in "${!UNIQ_KEYS[@]}" ; do + if ! check_attrName "${KEY}" ; then + Log.warn "Unknown attribute name '${KEY}'" + fi + done + return ${ERR} +} - # LDAP COMMANDS - LDAPSEARCH="/bin/ldapsearch -r" - LDAPMODIFY=/bin/ldapmodify - LDAPADD=/bin/ldapadd - LDAPDELETE=/bin/ldapdelete - LDAP_GEN_PROFILE=/usr/sbin/ldap_gen_profile +Man.addFunc discover_serv_suffix '' '[+NAME?discover_serv_suffix - query the DS to find suffixes available.] +[+DESCRIPTION?Query the DS to find suffixes available. All suffixes found are stored into the variable \avname\a which must be an indexed array.] +[+RETURN VALUES]{ + [+0?at least one suffix was found.] + [+1?no suffix found.] +} +[+SEE ALSO?\bldapsearch\b(1).] +\n\n\avname\a +' +function discover_serv_suffix { + typeset LINE + integer NUM_TOP=0 + typeset -n DST=$1 + DST=( ); unset DST[0] - # iDS specific information - IDS_SERVER="" - IDS_PORT=389 - NEED_TIME=0 - NEED_SIZE=0 - NEED_SRVAUTH_PAM=0 - NEED_SRVAUTH_KEY=0 - NEED_SRVAUTH_CMD=0 - IDS_TIMELIMIT="" - IDS_SIZELIMIT="" + # Search the server for the TOP of the TREE. Usually none for virgin DS + ${LDAPSEARCH} ${CON_ARGS} -b '' -s base 'objectclass=*' namingContexts \ + 2>/dev/null | \ + while read LINE ; do + if [[ -n ${LINE} && ${LINE} != ~(Ei)NetscapeRoot ]]; then + (( NUM_TOP++ )) + DST+=( "${LINE#*=}\n" ) # strip off '^namingContexts=' + fi + done - # LDAP PROFILE related defaults - LDAP_ROOTDN="cn=Directory Manager" # Provide common default. - LDAP_ROOTPWD="" # NULL passwd as default (i.e. invalid) - LDAP_PROFILE_NAME="default" - LDAP_BASEDN="" - LDAP_SERVER_LIST="" - LDAP_AUTHMETHOD="" - LDAP_FOLLOWREF="FALSE" - NEED_CRYPT="" - LDAP_SEARCH_SCOPE="one" - LDAP_SRV_AUTHMETHOD_PAM="" - LDAP_SRV_AUTHMETHOD_KEY="" - LDAP_SRV_AUTHMETHOD_CMD="" - LDAP_SEARCH_TIME_LIMIT=30 - LDAP_PREF_SRVLIST="" - LDAP_PROFILE_TTL=43200 - LDAP_CRED_LEVEL="proxy" - LDAP_BIND_LIMIT=10 + if (( NUM_TOP == 0 )); then + Log.verbose 'No suffix found in LDAP tree' + return 1 + fi - # Prevent new files from being read by group or others. - umask 077 - - # Service Search Descriptors - LDAP_SERV_SRCH_DES="" - - # Set and create TMPDIR. - TMPDIR="/tmp/idsconfig.${PID}" - if mkdir -m 700 ${TMPDIR} - then - # Cleanup on exit. - trap 'rm -rf ${TMPDIR}; /usr/bin/stty echo; exit' 1 2 3 6 15 - else - echo "ERROR: unable to create a safe temporary directory." - exit 1 - fi - LDAP_ROOTPWF=${TMPDIR}/rootPWD - - # Set the SSD file name after setting TMPDIR. - SSD_FILE=${TMPDIR}/ssd_list - - # GSSAPI setup - GSSAPI_ENABLE=0 - LDAP_KRB_REALM="" - SCHEMA_UPDATED=0 - - export DEBUG VERB ECHO EVAL EGREP GREP STEP TMPDIR - export IDS_SERVER IDS_PORT LDAP_ROOTDN LDAP_ROOTPWD LDAP_SERVER_LIST - export LDAP_BASEDN LDAP_ROOTPWF - export LDAP_DOMAIN LDAP_SUFFIX LDAP_PROXYAGENT LDAP_PROXYAGENT_CRED - export NEED_PROXY - export LDAP_ENABLE_SHADOW_UPDATE LDAP_ADMINDN LDAP_ADMIN_CRED - export NEED_ADMIN NEED_HOSTACL EXISTING_PROFILE - export LDAP_PROFILE_NAME LDAP_BASEDN LDAP_SERVER_LIST - export LDAP_AUTHMETHOD LDAP_FOLLOWREF LDAP_SEARCH_SCOPE LDAP_SEARCH_TIME_LIMIT - export LDAP_PREF_SRVLIST LDAP_PROFILE_TTL LDAP_CRED_LEVEL LDAP_BIND_LIMIT - export NEED_SRVAUTH_PAM NEED_SRVAUTH_KEY NEED_SRVAUTH_CMD - export LDAP_SRV_AUTHMETHOD_PAM LDAP_SRV_AUTHMETHOD_KEY LDAP_SRV_AUTHMETHOD_CMD - export LDAP_SERV_SRCH_DES SSD_FILE - export GEN_CMD GSSAPI_ENABLE LDAP_KRB_REALM SCHEMA_UPDATED + Log.verbose "LDAP_SUFFIX_LIST = ${DST[@]}" + return 0 } - -# -# disp_full_debug(): List of all debug variables usually interested in. -# Grouped to avoid MASSIVE code duplication. -# -disp_full_debug() -{ - [ $DEBUG -eq 1 ] && ${ECHO} " IDS_SERVER = $IDS_SERVER" - [ $DEBUG -eq 1 ] && ${ECHO} " IDS_PORT = $IDS_PORT" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_ROOTDN = $LDAP_ROOTDN" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_ROOTPWD = $LDAP_ROOTPWD" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_DOMAIN = $LDAP_DOMAIN" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SUFFIX = $LDAP_SUFFIX" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_BASEDN = $LDAP_BASEDN" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_PROFILE_NAME = $LDAP_PROFILE_NAME" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SERVER_LIST = $LDAP_SERVER_LIST" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_PREF_SRVLIST = $LDAP_PREF_SRVLIST" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SEARCH_SCOPE = $LDAP_SEARCH_SCOPE" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_CRED_LEVEL = $LDAP_CRED_LEVEL" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_AUTHMETHOD = $LDAP_AUTHMETHOD" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_FOLLOWREF = $LDAP_FOLLOWREF" - [ $DEBUG -eq 1 ] && ${ECHO} " IDS_TIMELIMIT = $IDS_TIMELIMIT" - [ $DEBUG -eq 1 ] && ${ECHO} " IDS_SIZELIMIT = $IDS_SIZELIMIT" - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_CRYPT = $NEED_CRYPT" - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_SRVAUTH_PAM = $NEED_SRVAUTH_PAM" - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_SRVAUTH_KEY = $NEED_SRVAUTH_KEY" - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_SRVAUTH_CMD = $NEED_SRVAUTH_CMD" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SRV_AUTHMETHOD_PAM = $LDAP_SRV_AUTHMETHOD_PAM" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SRV_AUTHMETHOD_KEY = $LDAP_SRV_AUTHMETHOD_KEY" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SRV_AUTHMETHOD_CMD = $LDAP_SRV_AUTHMETHOD_CMD" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SEARCH_TIME_LIMIT = $LDAP_SEARCH_TIME_LIMIT" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_PROFILE_TTL = $LDAP_PROFILE_TTL" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_BIND_LIMIT = $LDAP_BIND_LIMIT" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_ENABLE_SHADOW_UPDATE = $LDAP_ENABLE_SHADOW_UPDATE" - - # Only display proxy stuff if needed. - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_PROXY = $NEED_PROXY" - if [ $NEED_PROXY -eq 1 ]; then - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_PROXYAGENT = $LDAP_PROXYAGENT" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_PROXYAGENT_CRED = $LDAP_PROXYAGENT_CRED" - fi - - # Only display admin credential if needed. - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_ADMIN = $NEED_ADMIN" - [ $DEBUG -eq 1 ] && ${ECHO} " NEED_HOSTACL = $NEED_HOSTACL" - if [ $NEED_ADMIN -eq 1 ]; then - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_ADMINDN = $LDAP_ADMINDN" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_ADMIN_CRED = $LDAP_ADMIN_CRED" - fi - - # Service Search Descriptors are a special case. - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SERV_SRCH_DES = $LDAP_SERV_SRCH_DES" +Man.addFunc get_backend '' '[+NAME?get_backend - get the relevant database backend for the Base DN.] +[+DESCRIPTION?Get the relevant database backend name for the Base DN and store it into \bSTR[DS_DB]]\b.] +[+?Prerequisite: \bSTR[LDAP_BASEDN]]\b must be set and valid.] +[+?backend is retrieved from suffixes and subsuffixes defined under "cn=mapping tree,cn=config". The nsslapd-state attribute of these suffixes entries is filled with either Backend, Disabled or referrals related values. We only want those that have a true backend database to select the relevant backend.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?an fatal error occured.] } +[+SEE ALSO?\bldapsearch\b(1).] +' +function get_backend { + typeset CUR_DN=${STR[LDAP_BASEDN]} PREV_DN='' FILTER + while [[ ${CUR_DN} != ${PREV_DN} ]]; do + typeset -a DB=( ) + Log.verbose "Testing LDAP suffix: ${CUR_DN} ..." -# -# load_config_file(): Loads the config file. -# -load_config_file() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In load_config_file()" + if (( TMPF[IS_OPENDJ] )); then + FILTER='(&(&(objectclass=ds-cfg-backend)(ds-cfg-base-dn='"${CUR_DN}" + FILTER+='))(objectClass=ds-cfg-local-db-backend))' + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b 'cn=Backends,cn=config' -s one "${FILTER}" 2>/dev/null | \ + while read LINE ; do + [[ ${LINE:0:18} == 'ds-cfg-backend-id=' && \ + ${LINE: -10} != ',cn=config' ]] && DB+=( "${LINE:18}" ) + done + else - # Remove SSD lines from input file before sourcing. - # The SSD lines must be removed because some forms of the - # data could cause SHELL errors. - ${GREP} -v "LDAP_SERV_SRCH_DES=" ${INPUT_FILE} > ${TMPDIR}/inputfile.noSSD + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "cn=${CUR_DN},cn=mapping tree,cn=config" \ + -s base 'nsslapd-state=Backend' 'nsslapd-backend' 2>/dev/null |\ + while read LINE ; do + [[ ${LINE:0:16} == 'nsslapd-backend=' ]] && DB+=( "${LINE:16}" ) + done + fi - # Source the input file. - . ${TMPDIR}/inputfile.noSSD + if (( ${#DB[@]} == 0 )); then + # not a suffix, or suffix not activated; try next + PREV_DN=${CUR_DN} + CUR_DN="${CUR_DN#*,}" + elif (( ${#DB[@]} == 1 )); then + break + else + Log.fatal "More than one database is configured for '${CUR_DN}'!" \ + "$PROG can not configure suffixes where" \ + 'more than one database is used for one suffix' + return 66 + fi + done - # If LDAP_SUFFIX is no set, try to utilize LDAP_TREETOP since older - # config files use LDAP_TREETOP - LDAP_SUFFIX="${LDAP_SUFFIX:-$LDAP_TREETOP}" + if (( ${#DB[@]} == 0 )); then + # should not happen, since STR[LDAP_BASEDN] is supposed to be valid + Log.fatal "Could not find a valid backend for '${STR[LDAP_BASEDN]}'" + if (( TMPF[IS_OPENDJ] )); then + Log.fatal 'Check the "Creating a New Database Backend" section in the Administration Guide' + fi + return 67 + fi + STR[DS_DB]="${DB[0]}" - # Save password to temporary file. - save_password + Log.verbose "DB backend '${STR[DS_DB]}' selected" - # Create the SSD file. - create_ssd_file + return 0 +} - # Display FULL debugging info. - disp_full_debug +Man.addFunc normalizeDN '' '[+NAME?normalizeDN - normalize a [relative]] distinguished name.] +[+DESCRIPTION?Strip off unnecessary spaces around \b,\b and \b=\b as well as at the beginning and end of the given \adn\a, optionally convert it to lower or upper case (if either \bl\b or \bu\b is given) and print it to stdout.] +\n\n\adn\a [\bl\b|\bu\b] +' +function normalizeDN { + [[ -z $1 ]] && return + typeset VAL=",${1}," + VAL="${VAL//*( ),*( )/,}" + VAL="${VAL//*( )=*( )/=}" + if [[ $2 == 'l' ]]; then + typeset -l LC="${VAL}" + VAL="${LC}" + elif [[ $2 == 'u' ]]; then + typeset -u UC="${VAL}" + VAL="${UC}" + fi + print -- "${VAL:1:${#VAL}-2}" } -# -# save_password(): Save password to temporary file. -# -save_password() -{ - cat > ${LDAP_ROOTPWF} <= 66?a fatal error occured.] } +[+SEE ALSO?\bdiscover_serv_suffix()\b, \bldapsearch\b(1), \bnormalizeDN()\b, \bget_backend()\b.] +' +function check_basedn_suffix { + Log.info 'Validating LDAP Base DN and Suffix ...' + TMPF[NEED_CREATE_SUFFIX]=0 -###################################################################### -# FUNCTIONS FOR prompt_config_info() START HERE. -###################################################################### + # check that LDAP Base DN might be added + typeset CUR_DN="${STR[LDAP_BASEDN]}" PREV_DN='' -# -# get_ids_server(): Prompt for iDS server name. -# -get_ids_server() -{ - while : - do - # Prompt for server name. - get_ans "Enter the JES Directory Server's hostname to setup:" "$IDS_SERVER" - IDS_SERVER="$ANS" + while [[ ${CUR_DN} != ${PREV_DN} ]]; do + ${LDAPSEARCH} ${CON_ARGS} -b "${CUR_DN}" -s one 'objectclass=*' \ + >/dev/null 2>&1 && break + PREV_DN=${CUR_DN} + CUR_DN="${CUR_DN#*,}" # remove leading component + done - # Ping server to see if live. If valid break out of loop. - ping $IDS_SERVER > /dev/null 2>&1 - if [ $? -eq 0 ]; then - break + if [[ ${CUR_DN} == ${PREV_DN} ]]; then + Log.info "No valid suffixes were found for Base DN '${STR[LDAP_BASEDN]}'" + TMPF[NEED_CREATE_SUFFIX]=1 + return 0 fi - # Invalid server, enter a new name. - ${ECHO} "ERROR: Server '${IDS_SERVER}' is invalid or unreachable." - IDS_SERVER="" - done + # find out existing suffixes + typeset -a SUFFIXES=( ) + discover_serv_suffix SUFFIXES - # Set SERVER_ARGS and LDAP_ARGS since values might of changed. - SERVER_ARGS="-h ${IDS_SERVER} -p ${IDS_PORT}" - LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}" - export SERVER_ARGS + # Now looking for relevant suffix for this entry (using lower case - LC). + # STR[LDAP_SUFFIX] will then be used to add necessary base objects via + # add_base_objects(). + typeset LC_DN=,${ normalizeDN "${CUR_DN}" l ; } + typeset SFX LC_SFX + for SFX in "${SUFFIXES[@]}" ; do + Log.verbose "Testing suffix: ${SFX} ..." + # LC_DN ends with ,SFX ? + LC_SFX=,${ normalizeDN "${SFX}" l ; } + if [[ ${LC_DN: -${#LC_SFX}} == ${LC_SFX} ]]; then + STR[LDAP_SUFFIX]="${SFX}" + break + fi + done -} - -# -# get_ids_port(): Prompt for iDS port number. -# -get_ids_port() -{ - # Get a valid iDS port number. - while : - do - # Enter port number. - get_number "Enter the port number for iDS (h=help):" "$IDS_PORT" "port_help" - IDS_PORT=$ANS - # Do a simple search to check hostname and port number. - # If search returns SUCCESS, break out, host and port must - # be valid. - ${LDAPSEARCH} -h ${IDS_SERVER} -p ${IDS_PORT} -b "" -s base "objectclass=*" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - break + if [[ -z ${STR[LDAP_SUFFIX]} ]]; then + # should not happen, since we found the entry + Log.fatal "Could not find a valid suffix for '${STR[LDAP_BASEDN]}'" + return 66 fi - # Invalid host/port pair, Re-enter. - ${ECHO} "ERROR: Invalid host or port: ${IDS_SERVER}:${IDS_PORT}, Please re-enter!" - get_ids_server - done + # Getting relevant database (backend) + # DS_DB will then be used to create indexes. + get_backend || return 67 - # Set SERVER_ARGS and LDAP_ARGS since values might of changed. - SERVER_ARGS="-h ${IDS_SERVER} -p ${IDS_PORT}" - LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}" - export SERVER_ARGS + return 0 } +Man.addFunc get_objectclass '' '[+NAME?get_objectclass - get the objectclass for the given attribute name.] +[+DESCRIPTION?Get the objectclass for the given attribute name \aattrName\a and print it to stdout. Right now ou, dc, o, and c and related aliases/OIDs are supported, only. For all others nothing will be printed.] +[+NOTES?An attribute name can be valid but still we might not be able to determine the objectclass from the table. In such cases, the user needs to create the necessary object(s).] +\n\n\aattrName\a +' +function get_objectclass { + typeset -l ANAME="$1" -# -# chk_ids_version(): Read the slapd config file and set variables -# -chk_ids_version() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In chk_ids_version()" + case "${ANAME}" in + ou | organizationalunitname | 2.5.4.11) + print 'organizationalUnit' + ;; + dc | domaincomponent | 0.9.2342.19200300.100.1.25) + print 'domain' + ;; + o | organizationname | 2.5.4.10) + print 'organization' + ;; + c | countryname | 2.5.4.6) + print 'country' + ;; + esac +} - # check iDS version number. - eval "${LDAPSEARCH} ${SERVER_ARGS} -b cn=monitor -s base \"objectclass=*\" version | ${GREP} \"^version=\" | cut -f2 -d'/' | cut -f1 -d' ' > ${TMPDIR}/checkDSver 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} "ERROR: Can not determine the version number of iDS!" - exit 1 - fi - IDS_VER=`cat ${TMPDIR}/checkDSver` - IDS_MAJVER=`${ECHO} ${IDS_VER} | cut -f1 -d.` - IDS_MINVER=`${ECHO} ${IDS_VER} | cut -f2 -d.` - case "${IDS_MAJVER}" in - 5|6|7) : ;; - *) ${ECHO} "ERROR: $PROG only works with JES DS version 5.x, 6.x or 7.x, not ${IDS_VER}."; exit 1;; - esac - - if [ $DEBUG -eq 1 ]; then - ${ECHO} " IDS_MAJVER = $IDS_MAJVER" - ${ECHO} " IDS_MINVER = $IDS_MINVER" - fi +function checkSuffixRDN { + typeset RDN="${STR[LDAP_SUFFIX]%%,*}" + TMP[SUFFIX_VAL]="${RDN#*=}" + TMP[SUFFIX_ATT]="${.sh.match%=}" + + # find out an objectclass for suffix entry if it is not defined yet + TMP[SUFFIX_OBJ]=${ get_objectclass "${TMP[SUFFIX_ATT]}" ; } + if [[ -z ${TMP[SUFFIX_OBJ]} ]]; then + Log.fatal 'Unable to find an objectclass for' "'${TMP[SUFFIX_ATT]}'" \ + 'attribute' + return 1 + fi + return 0 } - -# -# get_dirmgr_dn(): Get the directory manger DN. -# -get_dirmgr_dn() -{ - get_ans "Enter the directory manager DN:" "$LDAP_ROOTDN" - LDAP_ROOTDN=$ANS - - # Update ENV variables using DN. - AUTH_ARGS="-D \"${LDAP_ROOTDN}\" -j ${LDAP_ROOTPWF}" - LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}" - export AUTH_ARGS LDAP_ARGS +Man.addFunc prep_create_sfx_entry '' '[+NAME?prep_create_sfx_entry - prepare for the suffix entry creation.] +[+DESCRIPTION?Prepare suffix entry creation based on \bSTR[LDAP_BASEDN|LDAP_SUFFIX]]\b and \bTMP[SUFFIX_OBJ]]\b. If the latter are unset (no config file read), set them to defaults depending on the baseDN. Finally check, whether the suffix entry already exists and if so check for consistency with the current config. This function sets:]{ + [+TMPF[NEED_CREATE_BACKEND]]?0 .. backend already exists. 1 .. create a backend.] + [+TMP[SUFFIX_ATT]]?The attribute name of the leading RDN of the suffix (ID).] + [+TMP[SUFFIX_VAL]]?The attribute value of the leading RDN of the suffix (ID).] } +[+RETURN VALUES]{ + [+0?on success.] + [+1?an attribute/consistence related error occured.] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\bnormalizeDN()\b, \bdisplay_msg()\b, \badd_suffix()\b, \bldapsearch\b(1)] +' +function prep_create_sfx_entry { + # check whether suffix corresponds to base dn (i.e. baseDN ends with suffix) + typeset X=,${ normalizeDN "${STR[LDAP_BASEDN]}" l ; } + typeset SFX=,${STR[LDAP_SUFFIX]} + if [[ ${X: -${#SFX}} != ${SFX} ]]; then + Log.warn "Sorry, suffix '${STR[LDAP_SUFFIX]}' is not suitable for" \ + "Base DN '${STR[LDAP_BASEDN]}'" + return 1 + fi + checkSuffixRDN || return 66 + Log.verbose "Suffix entry object: '${TMP[SUFFIX_OBJ]}'" -# -# get_dirmgr_pw(): Get the Root DN passwd. (Root DN found in slapd.conf) -# -get_dirmgr_pw() -{ - while : - do - # Get passwd. - get_passwd_nochk "Enter passwd for ${LDAP_ROOTDN} :" - LDAP_ROOTPWD=$ANS + TMPF[NEED_CREATE_BACKEND]=0 - # Store password in file. - save_password - - # Update ENV variables using DN's PW. - AUTH_ARGS="-D \"${LDAP_ROOTDN}\" -j ${LDAP_ROOTPWF}" - LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}" - export AUTH_ARGS LDAP_ARGS - - # Verify that ROOTDN and ROOTPWD are valid. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" > ${TMPDIR}/checkDN 2>&1" - if [ $? -ne 0 ]; then - eval "${GREP} credential ${TMPDIR}/checkDN ${VERB}" - if [ $? -eq 0 ]; then - ${ECHO} "ERROR: Root DN passwd is invalid." - else - ${ECHO} "ERROR: Invalid Root DN <${LDAP_ROOTDN}>." - get_dirmgr_dn - fi + if (( TMPF[IS_OPENDJ] )); then + # 1st just check, whether any backend matches the suffix + typeset FILTER='(&(objectClass=ds-cfg-local-db-backend)' + FILTER+="(|(ds-cfg-base-dn=${STR[LDAP_SUFFIX]})" + FILTER+="(ds-cfg-base-dn=*,${STR[LDAP_SUFFIX]})))" + X=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b 'cn=Backends,cn=config' -s one "${FILTER}" 2>/dev/null ; } + if [[ -n ${X} ]]; then + Log.verbose 'Suffix backend already exists' + get_backend || return 66 + return 0 + fi else - break # Both are valid. + # DSEE: check the suffix mapping tree ... + # If mapping exists, suffix should work, otherwise DSEE inconsistent + # NOTE: -b 'cn=mapping tree,cn=config' -s one 'cn=\"$1\"' won't work + # in case of 'cn' value in LDAP is not quoted by '"', + # -b 'cn=\"$1\",cn=mapping tree,cn=config' works in all cases + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "cn=\"${STR[LDAP_SUFFIX]}\",cn=mapping tree,cn=config" \ + -s base 'objectclass=*' dn 2>/dev/null + then + Log.verbose 'Suffix mapping already exists' + get_backend || return 66 + return 0 + fi + + # no suffix mapping, just in case check ldbm backends consistency - + # there are must be NO any databases pointing to STR[LDAP_SUFFIX] + X=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b 'cn=ldbm database,cn=plugins,cn=config' \ + -s one "nsslapd-suffix=${STR[LDAP_SUFFIX]}" dn 2>/dev/null ; } + if [[ -n ${X} ]]; then + Log.warn 'Sorry, there is no suffix mapping for' \ + "'${STR[LDAP_SUFFIX]}', while ldbm database exists, server" \ + 'configuration needs to be fixed manually, look at cn=mapping' \ + 'tree,cn=config and cn=ldbm database,cn=plugins,cn=config' + return 1 + fi fi - done - + Log.verbose 'Backend needs to be created ...' + TMPF[NEED_CREATE_BACKEND]=1 + return 0 } - -# -# get_domain(): Get the Domain that will be served by the LDAP server. -# $1 - Help argument. -# -get_domain() -{ - # Use LDAP_DOMAIN as default. - get_ans "Enter the domainname to be served (h=help):" $LDAP_DOMAIN - - # Check domainname, and have user re-enter if not valid. - check_domainname $ANS - while [ $? -ne 0 ] - do - case "$ANS" in - [Hh] | help | Help | \?) display_msg ${1:-sorry} ;; - * ) ${ECHO} "Invalid domainname: \"${ANS}\"." - ;; - esac - get_ans "Enter domainname to be served (h=help):" $DOM - - check_domainname $ANS - done - - # Set the domainname to valid name. - LDAP_DOMAIN=$ANS +Man.addFunc prep_create_sfx_backend '' '[+NAME?prep_create_sfx_backend - prepare for the suffix backend creation.] +[+DESCRIPTION?Prepare for the suffix backend creation by checking available DBs starting with \bSTR[DS_DB]]\b. Sets \bTMP[DS_DB_AVAIL]]\b if not yet set or != \bSTR[DS_DB]]\b.] +[+RETURN VALUES]{ + [+0?backend db name is ok.] + [+1?\bSTR[DS_DB]]\b exists, so \bTMP[DS_DB_AVAIL]]\b contains available name.] + [+2?unable to find any available name.] } +[+SEE ALSO?\bldapsearch\b(1).] +' +function prep_create_sfx_backend { + # check if requested name available + [[ ${STR[DS_DB]} == ${TMP[DS_DBS_AVAIL]} ]] && return 0 + # get the list of database names start with a requested name + typeset -a DBS=( ) + if (( TMPF[IS_OPENDJ] )); then + typeset FILTER='(&(objectclass=ds-cfg-local-db-backend)' + FILTER+="(ds-cfg-backend-id=${STR[DS_DB]}*))" + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b 'cn=Backends,cn=config' -s one "${FILTER}" ds-cfg-backend-id \ + 2>/dev/null | \ + while read LINE ; do + [[ -n ${LINE} && ${LINE: -10} != ',cn=config' ]] && \ + DBS+=( "${LINE#ds-cfg-backend-id=}" ) + done + else + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b 'cn=ldbm database,cn=plugins,cn=config' \ + -s one "cn=${STR[DS_DB]}*" cn 2>/dev/null | \ + while read LINE ; do + [[ -n ${LINE} && ${LINE: -10} != ',cn=config' ]] && \ + DBS+=( "${LINE#cn=}" ) + done + fi + if (( ${#DBS[@]} == 0 )); then + # no backend name starts with STR[DS_DB] + TMP[DS_DBS_AVAIL]="${STR[DS_DB]}" + return 0 + fi -# -# get_basedn(): Query for the Base DN. -# -get_basedn() -{ - # Set the $_DOM_2_DC and assign to LDAP_BASEDN as default. - # Then call get_basedn(). This method remakes the default - # each time just in case the domain changed. - domain_2_dc $LDAP_DOMAIN - LDAP_BASEDN=$_DOM_2_DC - - # Get Base DN. - while : - do - get_ans_req "Enter LDAP Base DN (h=help):" "${_DOM_2_DC}" - check_baseDN "$ANS" - while [ $? -ne 0 ] - do - case "$ANS" in - [Hh] | help | Help | \?) display_msg basedn_help ;; - * ) ${ECHO} "Invalid base DN: \"${ANS}\"." - ;; - esac - - # Re-Enter the BaseDN - get_ans_req "Enter LDAP Base DN (h=help):" "${_DOM_2_DC}" - check_baseDN "$ANS" + # find a non-existing db name based on a requested name + integer I FOUND + typeset -l NAME DB + for (( I=0 ; I < 10; I++ )); do + (( I )) && NAME="${STR[DS_DB]}${I}" || NAME="${STR[DS_DB]}" + FOUND=0 + for DB in "${DBS[@]}" ; do + [[ ${DB} == ${NAME} ]] && FOUND=1 && break + done + if (( ! FOUND )); then + if (( I == 0 )); then + TMP[DS_DBS_AVAIL]="${STR[DS_DB]}" + return 0 # requested name is available + fi + TMP[DS_DBS_AVAIL]="${STR[DS_DB]}${I}" + break + fi done - # Set base DN and check its suffix - LDAP_BASEDN=${ANS} - check_basedn_suffix || - { - cleanup - exit 1 - } + if [[ -n ${TMP[DS_DBS_AVAIL]} ]]; then + Log.warn "Database backend '${STR[DS_DB]}' already exists," \ + "however '${TMP[DS_DBS_AVAIL]}' is available" + return 1 + fi - # suffix may need to be created, in that case get suffix from user - [ -n "${NEED_CREATE_SUFFIX}" ] && - { - get_suffix || continue - } - - # suffix is ok, break out of the base dn inquire loop - break - done + Log.warn 'Unable to find any available database backend close to' \ + "'${STR[DS_DB]}'" + return 2 } -# -# get_want_shadow_update(): Ask user if want to enable shadow update? -# -get_want_shadow_update() -{ - MSG="Do you want to enable shadow update (y/n/h)?" - get_confirm "$MSG" "n" "enable_shadow_update_help" - if [ $? -eq 1 ]; then - LDAP_ENABLE_SHADOW_UPDATE="TRUE" - else - LDAP_ENABLE_SHADOW_UPDATE="FALSE" - fi +Man.addFunc get_suffix '' '[+NAME?get_suffix - Ask user for suffix and related backend.] +[+DESCRIPTION?Ask the user for the suffix (default: \bSTR[LDAP_BASEDN]]\b) to create and the db name if it does not yet exist. \bTMP[DS_DBS_AVAIL]]\b gets set to the given db name (if any), \bSTR[DS_DB]]\b to the prepared db name and \bSTR[LDAP_SUFFIX]]\b to the given suffix.] +[+RETURN VALUES]{ + [+0?on success (user gave a correct suffix).] + [+1?unable to create suffix given by user.] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b, \bnormalizeDN()\b, \bprep_create_sfx_entry()\b, \bprep_create_sfx_backend()\b.] +' +function get_suffix { + while : ; do + get_ans 'Enter suffix to be created (b=back/h=help):' \ + "${STR[LDAP_BASEDN]}" + case "${ANS}" in + [Hh?] | Help | help) + display_msg 'create_suffix_help' + continue + ;; + [Bb] | '<' | Back | back) + return 1 + ;; + esac + STR[LDAP_SUFFIX]=${ normalizeDN "${ANS}" l ; } + prep_create_sfx_entry + integer REDO=$? + (( REDO >= 66 )) && return 66 + (( REDO )) && continue -get_krb_realm() { + if (( TMPF[NEED_CREATE_BACKEND] )); then + TMP[DS_DBS_AVAIL]='' # reset the available db name + REDO=0 + while : ; do + X=${TMP[DS_DBS_AVAIL]} + # Basically for OpenDJ we could use 'userRoot' as default aka + # TMP[SUFFIX_VAL], however, users may use it for trying out + # examples from the docs + get_ans 'Enter local database backend name (b=back/h=help):' \ + "${X:-${TMP[SUFFIX_VAL]}}" + case "${ANS}" in + [Hh?]) + display_msg 'enter_ldbm_db_help' + continue + ;; + [Bb] | '<') + REDO=1 + break + ;; + esac + STR[DS_DB]="${ANS}" + prep_create_sfx_backend && break + done - # To upper cases - LDAP_KRB_REALM=`${ECHO} ${LDAP_DOMAIN} | ${NAWK} '{ print toupper($0) }'` - get_ans_req "Enter Kerberos Realm:" "$LDAP_KRB_REALM" - # To upper cases - LDAP_KRB_REALM=`${ECHO} ${ANS} | ${NAWK} '{ print toupper($0) }'` + (( REDO )) && continue + + Log.verbose 'Databse backend name for suffix ${STR[LDAP_SUFFIX]}:' \ + "${DS_DB}" + fi + # eventually everything is prepared + break + done + return 0 } -# $1: DN -# $2: ldif file -add_entry_by_DN() { +Man.addFunc domain_2_dc '' '[+NAME?domain_2_dc - Convert a domain name into dc string.] +[+DESCRIPTION?Convert a domain name \adomain\a into a dc=...,dc=... string and print it to stdout.] +\n\n\adomain\a +' +function domain_2_dc { + [[ -z $1 ]] && return + typeset DC='' SD + for SD in ${1//./ } ; do # removes leading && trailing && double dots + DC+=",dc=${SD}" + done + print -- "${DC:1}" +} - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${1}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then - ${ECHO} " ${1} already exists" - return 0 - else - ${EVAL} "${LDAPADD} ${LDAP_ARGS} -f ${2} ${VERB}" - if [ $? -eq 0 ]; then - ${ECHO} " ${1} is added" - return 0 - else - ${ECHO} " ERROR: failed to add ${1}" - return 1 - fi - fi - +Man.addFunc get_basedn '' '[+NAME?get_basedn - Ask user for Base DN.] +[+DESCRIPTION?Ask the user for the BaseDN to use, check whether to create a suffix and store the value into \bSTR[LDAP_BASEDN]]\b.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] } -# -# Kerberos princiapl to DN mapping rules -# -# Add rules for host credentails and user credentials -# -add_id_mapping_rules() { +[+SEE ALSO?\bget_ans_req()\b, \bdisplay_msg()\b, \bcheck_baseDN()\b, \bcheck_basedn_suffix()\b, \bget_suffix()\b.] +' +function get_basedn { + typeset BASEDN=${ domain_2_dc ${STR[LDAP_DOMAIN]} ; } + integer RES - ${ECHO} " Adding Kerberos principal to DN mapping rules..." + while : ; do + # Get Base DN. + while : ; do + get_ans_req 'Enter LDAP Base DN (h=help):' "${BASEDN}" + case "${ANS}" in + [Hh?] | help | Help) display_msg 'basedn_help' ; continue ;; + esac + check_baseDN "${ANS}" && break + Log.warn "Invalid base DN: '${ANS}'" + done - _C_DN="cn=GSSAPI,cn=identity mapping,cn=config" - ( cat << EOF -dn: cn=GSSAPI,cn=identity mapping,cn=config -objectClass: top -objectClass: nsContainer -cn: GSSAPI -EOF -) > ${TMPDIR}/GSSAPI_container.ldif + # Set base DN and check its suffix + STR[LDAP_BASEDN]="${ANS}" + check_basedn_suffix || return 66 - add_entry_by_DN "${_C_DN}" "${TMPDIR}/GSSAPI_container.ldif" - if [ $? -ne 0 ]; - then - ${RM} ${TMPDIR}/GSSAPI_container.ldif - return - fi + # suffix may need to be created, in that case get suffix from user + if (( TMPF[NEED_CREATE_SUFFIX] )); then + get_suffix + RES=$? + (( RES >= 66 )) && return 67 + (( RES )) && continue + fi - _H_CN="host_auth_${LDAP_KRB_REALM}" - _H_DN="cn=${_H_CN}, ${_C_DN}" - ( cat << EOF -dn: ${_H_DN} -objectClass: top -objectClass: nsContainer -objectClass: dsIdentityMapping -objectClass: dsPatternMatching -cn: ${_H_CN} -dsMatching-pattern: \${Principal} -dsMatching-regexp: host\/(.*).${LDAP_DOMAIN}@${LDAP_KRB_REALM} -dsSearchBaseDN: ou=hosts,${LDAP_BASEDN} -dsSearchFilter: (&(objectClass=ipHost)(cn=\$1)) -dsSearchScope: one + # suffix is ok, break out of the base dn inquire loop + break + done + return 0 +} -EOF -) > ${TMPDIR}/${_H_CN}.ldif +Man.addFunc chk_vlv_indexes '' '[+NAME?chk_vlv_indexes - check if server supports VLV.] +[+DESCRIPTION?Checks, whether the DS supports Virtual List Views (VLV).] +[+RETURN VALUES]{ + [+0?if VLVs are supported.] + [+>= 66?if VLVs are not supported.] +} +[+SEE ALSO?\bldapsearch\b(1).] +' +function chk_vlv_indexes { + typeset ATTR='' + ${LDAPSEARCH} ${CON_ARGS} -b '' -s base 'objectclass=*' \ + supportedControl 2>/dev/null | \ + while read LINE ; do + if [[ ${LINE#*=} == '2.16.840.1.113730.3.4.9' ]]; then + ATTR=${.sh.match%=} + break + fi + done + if [[ -z ${ATTR} ]]; then + Log.fatal 'VLV is not supported on LDAP server' + return 66 + fi + return 0 +} - add_entry_by_DN "${_H_DN}" "${TMPDIR}/${_H_CN}.ldif" - - _U_CN="user_auth_${LDAP_KRB_REALM}" - _U_DN="cn=${_U_CN}, ${_C_DN}" - ( cat << EOF -dn: ${_U_DN} -objectClass: top -objectClass: nsContainer -objectClass: dsIdentityMapping -objectClass: dsPatternMatching -cn: ${_U_CN} -dsMatching-pattern: \${Principal} -dsMatching-regexp: (.*)@${LDAP_KRB_REALM} -dsMappedDN: uid=\$1,ou=People,${LDAP_BASEDN} - -EOF -) > ${TMPDIR}/${_U_CN}.ldif - - add_entry_by_DN "${_U_DN}" "${TMPDIR}/${_U_CN}.ldif" - +Man.addFunc validate_suffix '' '[+NAME?validate_suffix - validate suffix obtained via config file.] +[+DESCRIPTION?This function validates STR[LDAP_SUFFIX]] AFTER THE LOAD OF A CONFIG FILE (should not be used for interactive use). Also does a consistency check wrt. \bSTR[LDAP_BASEDN]]\b.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] } @@ -1514,962 +1983,1047 @@ add_entry_by_DN "${_U_DN}" "${TMPDIR}/${_U_CN}.ldif" } +[+SEE ALSO?\bnormalizeDN()\b, \bldapsearch\b(1), \bprep_create_sfx_entry()\b, \bprep_create_sfx_backend()\b.] +' +function validate_suffix { + # Check STR[LDAP_SUFFIX] is not null + if [[ -z ${STR[LDAP_SUFFIX]} ]]; then + Log.fatal 'Invalid suffix (null suffix)' + return 66 + fi + # STR[LDAP_SUFFIX] and STR[LDAP_BASEDN] are consistent ? + typeset X=,${ normalizeDN "${STR[LDAP_BASEDN]}" l ; } + typeset SFX=,${ normalizeDN "${STR[LDAP_SUFFIX]}" l ; } + if [[ ${X: -${#SFX}} != ${SFX} ]]; then + Log.fatal "Invalid suffix '${SFX}' for Base DN '${X}'" + return 67 + fi -# -# Modify ACL to allow root to read all the password and only self can read -# its own password when sasl/GSSAPI bind is used -# -modify_userpassword_acl_for_gssapi() { + # STR[LDAP_SUFFIX] does exist ? + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${STR[LDAP_SUFFIX]}" \ + -s base 'objectclass=*' >/dev/null 2>&1 + then + checkSuffixRDN && return 0 || return 68 + fi - _P_DN="ou=People,${LDAP_BASEDN}" - _H_DN="ou=Hosts,${LDAP_BASEDN}" - _P_ACI="self-read-pwd" + # Well, suffix does not exist, try to prepare create it ... + TMPF[NEED_CREATE_SUFFIX]=1 + prep_create_sfx_entry || return 69 + if (( TMPF[NEED_CREATE_BACKEND] )); then + if [[ -z ${STR[DS_DB]} ]]; then + Log.fatal 'Database backend name is not set. Either set SIN[DS_DB]'\ + 'in the input file to a valid, i.e. non-existing DB name, or' \ + 'create the backend with the default DS tools' + return 70 + fi + # too risky, even if a available backend name could be found + prep_create_sfx_backend || return 71 + fi - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${_P_DN}\" -s base \"objectclass=*\" > /dev/null 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} " ${_P_DN} does not exist" - # Not Found. Create a new entry - ( cat << EOF -dn: ${_P_DN} -ou: People -objectClass: top -objectClass: organizationalUnit -EOF -) > ${TMPDIR}/gssapi_people.ldif - - add_entry_by_DN "${_P_DN}" "${TMPDIR}/gssapi_people.ldif" - else - ${ECHO} " ${_P_DN} already exists" - fi - - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${_P_DN}\" -s base \"objectclass=*\" aci > ${TMPDIR}/chk_gssapi_aci 2>&1" - - if [ $? -eq 0 ]; then - ${EVAL} "${GREP} ${_P_ACI} ${TMPDIR}/chk_gssapi_aci > /dev/null 2>&1" - if [ $? -eq 0 ]; then - ${ECHO} " userpassword ACL ${_P_ACI} already exists." - return - else - ${ECHO} " userpassword ACL ${_P_ACI} not found. Create a new one." - fi - else - ${ECHO} " Error searching aci for ${_P_DN}" - cat ${TMPDIR}/chk_gssapi_aci - cleanup - exit 1 - fi - ( cat << EOF -dn: ${_P_DN} -changetype: modify -add: aci -aci: (targetattr="userPassword")(version 3.0; acl self-read-pwd; allow (read,search) userdn="ldap:///self" and authmethod="sasl GSSAPI";) -- -add: aci -aci: (targetattr="userPassword")(version 3.0; acl host-read-pwd; allow (read,search) userdn="ldap:///cn=*+ipHostNumber=*,ou=Hosts,${LDAP_BASEDN}" and authmethod="sasl GSSAPI";) -EOF -) > ${TMPDIR}/user_gssapi.ldif - LDAP_TYPE_OR_VALUE_EXISTS=20 - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/user_gssapi.ldif ${VERB}" - - case $? in - 0) - ${ECHO} " ${_P_DN} uaserpassword ACL is updated." - ;; - 20) - ${ECHO} " ${_P_DN} uaserpassword ACL already exists." - ;; - *) - ${ECHO} " ERROR: update of userpassword ACL for ${_P_DN} failed!" - cleanup - exit 1 - ;; - esac + Log.verbose "Suffix: '${STR[LDAP_SUFFIX]}' Database: '${STR[DS_DB]}'" + return 0 } -# -# $1: objectclass or attributetyp -# $2: name -search_update_schema() { - ATTR="${1}es" - - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b cn=schema -s base \"objectclass=*\" ${ATTR} | ${GREP} -i \"${2}\" ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} "${1} ${2} does not exist." - update_schema_attr - update_schema_obj - SCHEMA_UPDATED=1 - else - ${ECHO} "${1} ${2} already exists. Schema has been updated" - fi +Man.addFunc validate_info '' '[+NAME?validate_info - validate basic info obtained via config file.] +[+DESCRIPTION?This function updates \bCON_ARGS\b as well as \bAUTH_ARGS\b, checks, whether they work so that some problems are caught right away AFTER THE LOAD OF A CONFIG FILE (should not be used for interactive use). It also validates the obtained suffix and whether the DS supports VLVs.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bldapsearch\b(1), \bchk_vlv_indexes()\b, \bvalidate_suffix()\b.] +' +function validate_info { + # Set CON_ARGS and AUTH_ARGS for the config file. + CON_ARGS="-h ${STR[DS_HOST]} -p ${INT[DS_PORT]}" + AUTH_ARGS=( '-D' "${STR[LDAP_ROOTDN]}" '-j' "${TMP[LDAP_ROOTPWF]}" ) + chk_ids_version || return 1 # Check DSEE version for compatibility -# -# Set up GSSAPI if necessary -# -gssapi_setup() { + # Check the Root DN and Root DN passwd.o Same as for get_dirmgr_pw() + RES=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b '' -s base \ + 'objectclass=*' supportedLDAPVersion 2>&1 ; } + if (( $? )); then + if [[ ${RES} =~ credential ]]; then + Log.fatal 'Root DN passwd is invalid' + else + Log.fatal "Invalid Root DN '${STR[LDAP_ROOTDN]}'" + fi + return 66 + fi + Log.verbose 'RootDN: ok RootDN passwd: ok' - GSSAPI_ENABLE=0 + # Check if the server supports the VLV + chk_vlv_indexes || return 67 + Log.verbose 'VLV indexes ... ok' - # assume sasl/GSSAPI is supported by the ldap server and may be used - GSSAPI_AUTH_MAY_BE_USED=1 + # Check LDAP suffix + validate_suffix || return 66 + Log.verbose 'LDAP suffix ... ok' + return 0 +} - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" supportedSASLMechanisms | ${GREP} GSSAPI ${VERB}" - if [ $? -ne 0 ]; then - GSSAPI_AUTH_MAY_BE_USED=0 - ${ECHO} " sasl/GSSAPI is not supported by this LDAP server" +Man.addFunc gssapi_setup '' '[+NAME?gssapi_setup - set up GSSAPI if necessary.] +[+DESCRIPTION?Check, whether the DS supports the SASL mechanism \bGSSAPI\b. If so, ask the user whether it should be enabled and depending on the answer ask for related Kerberos realm info. \bINT[GSSAPI_ENABLE]]\b and \bTMPF[GSSAPI_AUTH_MAY_BE_USED]]\b and optionally \bSTR[LDAP_KRB_REALM]]\b are set accordingly.] +[+SEE ALSO?\bldapsearch\b(1), \bget_ans_req()\b.] +' +function gssapi_setup { + INT[GSSAPI_ENABLE]=0 + TMPF[GSSAPI_AUTH_MAY_BE_USED]=0 + + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b '' -s base 'objectclass=*' \ + supportedSASLMechanisms 2>/dev/null | \ + while read LINE ; do + if [[ ${LINE:0:24} == 'supportedSASLMechanisms=' && \ + ${LINE:24} == 'GSSAPI' ]] + then + TMPF[GSSAPI_AUTH_MAY_BE_USED]=1 + break + fi + done + if (( ! TMPF[GSSAPI_AUTH_MAY_BE_USED] )); then + Log.info 'sasl/GSSAPI is not supported by this LDAP server' return fi - get_confirm "GSSAPI is supported. Do you want to set up gssapi:(y/n)" "n" - if [ $? -eq 0 ]; then - GSSAPI_ENABLE=0 - ${ECHO} - ${ECHO} "GSSAPI is not set up." - ${ECHO} "sasl/GSSAPI bind may not work if it's not set up first." + get_confirm 'GSSAPI is supported. Do you want to set up GSSAPI:(y/n)' 'n' + if (( $? )); then + INT[GSSAPI_ENABLE]=1 + # get kerberos realm + typeset -u REALM=${STR[LDAP_DOMAIN]} + get_ans_req "Enter Kerberos Realm:" "${REALM}" + REALM="${ANS}" # make sure, it is upper case + STR[LDAP_KRB_REALM]="${REALM}" else - GSSAPI_ENABLE=1 - get_krb_realm + Log.info 'GSSAPI is not set up - ' \ + 'sasl/GSSAPI bind may not work if it is not set up first' fi - } -# -# get_profile_name(): Enter the profile name. -# -get_profile_name() -{ - # Reset Delete Old Profile since getting new profile name. - DEL_OLD_PROFILE=0 - # Loop until valid profile name, or replace. - while : - do - # Prompt for profile name. - get_ans "Enter the profile name (h=help):" "$LDAP_PROFILE_NAME" +Man.addFunc get_profile_name '' '[+NAME?get_profile_name - Ask user for client profile name.] +[+DESCRIPTION?Ask the user for the name of the client profile to create, check whether it already exists, and if so ask whether it should be overwritten. Sets \bSTR[LDAP_PROFILE_NAME]]\b, \bTMPF[DEL_OLD_PROFILE]]\b and if the user wants to just enable shadow update \bINT[LDAP_ENABLE_SHADOW_UPDATE]]\b and \bINT[EXISTING_PROFILE]]\b.] +[+SEE ALSO?\bget_ans()\b, \bldapsearch\b(1), \bget_confirm()\b, \bget_confirm_nodef()\b, \bdisplay_msg()\b.] +' +function get_profile_name { + TMPF[DEL_OLD_PROFILE]=0 # Reset since getting new profile name - # Check for Help. - case "$ANS" in - [Hh] | help | Help | \?) display_msg profile_help - continue ;; - * ) ;; - esac + typeset PNAME="${STR[LDAP_PROFILE_NAME]}" + # Loop until valid profile name, or replace. + while : ; do + get_ans 'Enter the client profile name (h=help):' "${PNAME}" + case "${ANS}" in + [Hh] | help | Help | '?') display_msg 'profile_help' ; continue ;; + esac - # Search to see if profile name already exists. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${ANS},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then + # Search to see if profile name already exists. + if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "cn=${ANS},ou=profile,${STR[LDAP_BASEDN]}" -s base \ + 'objectclass=*' >/dev/null 2>&1 + then + break # Unique profile name (does not yet exist) + fi - cat << EOF + Log.info 'Profile "'"${ANS}"'" already exists, it is possible to' \ + 'enable shadow update now.' "${PROG}" 'will exit after shadow' \ + 'update is enabled. You can also continue to overwrite the' \ + 'profile or create a new one and be given the chance to enable' \ + 'shadow update later.' -Profile '${ANS}' already exists, it is possible to enable -shadow update now. idsconfig will exit after shadow update -is enabled. You can also continue to overwrite the profile -or create a new one and be given the chance to enable -shadow update later. + get_confirm 'Just enable shadow update (y/n/h)?' 'n' \ + 'enable_shadow_update_help' + if (( $? )); then + # set up credentials for shadow update + INT[LDAP_ENABLE_SHADOW_UPDATE]=1 + # display alternate messages + INT[EXISTING_PROFILE]=1 + break + fi -EOF + get_confirm_nodef \ + "Are you sure you want to overwrite profile 'cn=${ANS}'?" + if (( $? )); then + TMPF[DEL_OLD_PROFILE]=1 # Replace old profile name + break + fi + done - MSG="Just enable shadow update (y/n/h)?" - get_confirm "$MSG" "n" "enable_shadow_update_help" - if [ $? -eq 1 ]; then - [ $DEBUG -eq 1 ] && ${ECHO} "set up shadow update" - LDAP_ENABLE_SHADOW_UPDATE=TRUE - # display alternate messages - EXISTING_PROFILE=1 - # Set Profile Name. - LDAP_PROFILE_NAME=$ANS - return 0 # set up credentials for shadow update. - fi + STR[LDAP_PROFILE_NAME]="${ANS}" +} - get_confirm_nodef "Are you sure you want to overwrite profile cn=${ANS}?" - if [ $? -eq 1 ]; then - DEL_OLD_PROFILE=1 - return 0 # Replace old profile name. - else - ${ECHO} "Please re-enter a new profile name." - fi - else - break # Unique profile name. - fi - done - - # Set Profile Name. - LDAP_PROFILE_NAME=$ANS +Man.addFunc getACIs '' '[+NAME?getACIs - get ACIs for the current base DN.] +[+DESCRIPTION?Get all ACIs for \abaseDN\a and append them to the indexed array of strings \avname\a. If \abaseDN\a is not given, \bSTR[LDAP_BASEDN]]\b will be used instead. If \ascope\a is set to \bGlobal\b this method queries for "ds-cfg-global-aci" instead of "aci" unless \bTMPF[IS_OPENDJ]]\b is \b0\b.] +[+RETURN VALUES]{ + [+-1?invalid function usage.] + [+0?on success.] + [+1?an error occured when executing the ldapsearch operation.] } +\n\n\avname\a [\abaseDN\a] [\ascope\a] +' +function getACIs { + set -o pipefail # wanna have the exit code of ldapsearch + [[ -z $1 ]] && Log.warn "${.sh.fun}(): invalid function usage" && \ + return -1 + typeset -n ACILIST=$1 + typeset LINE + typeset DN="$2" ACI='aci' + [[ -z ${DN} ]] && DN="${STR[LDAP_BASEDN]}" + [[ $3 == 'Global' ]] && (( TMPF[IS_OPENDJ] )) && ACI='ds-cfg-global-aci' - -# -# get_srv_list(): Get the default server list. -# -get_srv_list() -{ - # If LDAP_SERVER_LIST is NULL, then set, otherwise leave alone. - if [ -z "${LDAP_SERVER_LIST}" ]; then - LDAP_SERVER_LIST=`getent hosts ${IDS_SERVER} | awk '{print $1}'` - if [ ${IDS_PORT} -ne 389 ]; then - LDAP_SERVER_LIST="${LDAP_SERVER_LIST}:${IDS_PORT}" + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${DN}" -s base \ + 'objectclass=*' ${ACI} 2>/dev/null | \ + while read LINE ; do + # NOTE: the || : is required to always get success (pipe doesn't break) + [[ ${LINE:0:${#ACI}+1} == "${ACI}=" ]] && \ + ACILIST+=( "${LINE:${#ACI}+1}" ) || : + done + if (( $? )); then + Log.fatal "Unable to get aci list for '${STR[LDAP_BASEDN]}'" + return 1 fi - fi - - # Prompt for new LDAP_SERVER_LIST. - while : - do - get_ans "Default server list (h=help):" $LDAP_SERVER_LIST - - # If help continue, otherwise break. - case "$ANS" in - [Hh] | help | Help | \?) display_msg def_srvlist_help ;; - * ) break ;; - esac - done - LDAP_SERVER_LIST=$ANS + return 0 } - -# -# get_pref_srv(): The preferred server list (Overrides the server list) -# -get_pref_srv() -{ - while : - do - get_ans "Preferred server list (h=help):" $LDAP_PREF_SRVLIST - - # If help continue, otherwise break. - case "$ANS" in - [Hh] | help | Help | \?) display_msg pref_srvlist_help ;; - * ) break ;; - esac - done - LDAP_PREF_SRVLIST=$ANS +Man.addFunc findACI '' '[+NAME?findACI - find an ACI in the given list.] +[+DESCRIPTION?Find the first ACI which matches \aregex\a in the indexed array of ACIs \avname\a. If a match occurs, print out a message using the given \aaciName\a prefixed with \ascope\a. If \ascope\a is not given or empty, \bBaseDN\b will be used instead.] +[+RETURN VALUES]{ + [+0?no ACI matched.] + [+>=1?the index+1 of the ACI matched.] } - - -# -# get_search_scope(): Get the search scope from the user. -# -get_search_scope() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_search_scope()" - - _MENU_CHOICE=0 - while : - do - get_ans "Choose desired search scope (one, sub, h=help): " "one" - _MENU_CHOICE=$ANS - case "$_MENU_CHOICE" in - one) LDAP_SEARCH_SCOPE="one" - return 1 ;; - sub) LDAP_SEARCH_SCOPE="sub" - return 2 ;; - h) display_msg srch_scope_help ;; - *) ${ECHO} "Please enter \"one\", \"sub\", or \"h\"." ;; - esac - done - +[+SEE ALSO?\bgetACIs()\b, \bfindACI()\b, \bfind_and_delete_ACI\b()] +\n\n\avname\a \aaciName\a \aregex\a [\ascope\a] +' +function findACI { + typeset -n LIST=$1 + typeset NAME="$2" REGEX="$3" SCOPE="$4" + integer MAX=${#LIST[@]} I + [[ -z ${SCOPE} ]] && SCOPE='BaseDN' + for (( I=0; I < MAX; I++ )); do + if [[ ${LIST[${I}]} =~ ${REGEX} ]]; then + showProgress "${SCOPE} ACI '${NAME}' exists." + return $((I+1)) + fi + done + return 0 } - -# -# get_cred_level(): Function to display menu to user and get the -# credential level. -# -get_cred_level() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_cred_level()" - - _MENU_CHOICE=0 - display_msg cred_level_menu - while : - do - if [ $GSSAPI_ENABLE -eq 1 ]; then - ${ECHO} '"self" is needed for GSSAPI profile' - fi - get_ans "Choose Credential level [h=help]:" "1" - _MENU_CHOICE=$ANS - case "$_MENU_CHOICE" in - 1) LDAP_CRED_LEVEL="anonymous" - return 1 ;; - 2) LDAP_CRED_LEVEL="proxy" - return 2 ;; - 3) LDAP_CRED_LEVEL="proxy anonymous" - return 3 ;; - 4) LDAP_CRED_LEVEL="self" - return 4 ;; - h) display_msg cred_lvl_help ;; - *) ${ECHO} "Please enter 1, 2, 3 or 4." ;; - esac - done +Man.addFunc allow_proxy_read_pw '' '[+NAME?allow_proxy_read_pw - add Proxy Agent read permission for password.] +[+DESCRIPTION?Add the ACI \bPROXY_ACI_NAME\b for \bSTR[LDAP_PROXYAGENT]]\b to \bSTR[LDAP_BASEDN]]\b if it not already exists.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage PROXY_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (ACI already exists or added successfully).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bgetACIs()\b, \bldapmodify\b(1).] +' +function allow_proxy_read_pw { + typeset ACI PATTERN TARGET + typeset -a LIST=( ) + getACIs LIST || return 66 -# -# srvauth_menu_handler(): Enter the Service Authentication method. -# -srvauth_menu_handler() -{ - # Display Auth menu - display_msg srvauth_method_menu + PATTERN='acl[ ]+"?('"${PROXY_ACI_NAME}|${PROXY_ACI_NAME// /_}"')"?' + findACI LIST "${PROXY_ACI_NAME}" "${PATTERN}" || return 0 - # Get a Valid choice. - while : - do - # Display appropriate prompt and get answer. - if [ $_FIRST -eq 1 ]; then - get_ans "Choose Service Authentication Method:" "1" + PATTERN='userPassword' + if (( TMPF[IS_OPENDJ] )); then + PATTERN+=' || authPassword' + # At least in OpenDJ specifying the same target as where the ACI gets + # attached is redundant wrt. the result and causes minimal overhead + TARGET='' else - get_ans "Choose Service Authentication Method (0=reset):" + TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' fi + + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +add: aci +aci: '"${TARGET}"'(targetattr = "'"${PATTERN}"'") + ( + version 3.0; acl "'"${PROXY_ACI_NAME}"'"; + allow (compare,read,search) userdn = "ldap:///'"${STR[LDAP_PROXYAGENT]}"'"; + ) +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${PROXY_ACI_NAME}' ACI failed" + return 66 + fi - # Determine choice. - _MENU_CHOICE=$ANS - case "$_MENU_CHOICE" in - 1) _AUTHMETHOD="simple" - break ;; - 2) _AUTHMETHOD="sasl/DIGEST-MD5" - break ;; - 3) _AUTHMETHOD="tls:simple" - break ;; - 4) _AUTHMETHOD="tls:sasl/DIGEST-MD5" - break ;; - 5) _AUTHMETHOD="sasl/GSSAPI" - break ;; - 0) _AUTHMETHOD="" - _FIRST=1 - break ;; - *) ${ECHO} "Please enter 1-5 or 0 to reset." ;; - esac - done + showProgress "ACI '${PROXY_ACI_NAME}' added." + return 0 } - -# -# auth_menu_handler(): Enter the Authentication method. -# -auth_menu_handler() -{ - # Display Auth menu - display_msg auth_method_menu - - # Get a Valid choice. - while : - do - if [ $GSSAPI_ENABLE -eq 1 ]; then - ${ECHO} '"sasl/GSSAPI" is needed for GSSAPI profile' - fi - # Display appropriate prompt and get answer. - if [ $_FIRST -eq 1 ]; then - get_ans "Choose Authentication Method (h=help):" "1" - else - get_ans "Choose Authentication Method (0=reset, h=help):" - fi - - # Determine choice. - _MENU_CHOICE=$ANS - case "$_MENU_CHOICE" in - 1) _AUTHMETHOD="none" - break ;; - 2) _AUTHMETHOD="simple" - break ;; - 3) _AUTHMETHOD="sasl/DIGEST-MD5" - break ;; - 4) _AUTHMETHOD="tls:simple" - break ;; - 5) _AUTHMETHOD="tls:sasl/DIGEST-MD5" - break ;; - 6) _AUTHMETHOD="sasl/GSSAPI" - break ;; - 0) _AUTHMETHOD="" - _FIRST=1 - break ;; - h) display_msg auth_help ;; - *) ${ECHO} "Please enter 1-6, 0=reset, or h=help." ;; - esac - done +Man.addFunc delete_proxy_read_pw '' '[+NAME?delete_proxy_read_pw - delete Proxy Agent read permission for password.] +[+DESCRIPTION?Remove the ACI \bPROXY_ACI_NAME\b from \bSTR[LDAP_BASEDN]]\b if such an ACI exists.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage PROXY_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (no delete required or ACI deleted).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bgetACIs()\b, \bget_menu_choice()\b, \bldapmodify\b(1).] +' +function delete_proxy_read_pw { + typeset PROXY_ACI='' ACI PATTERN X + typeset -a LIST=( ) + getACIs LIST || return 66 -# -# get_auth(): Enter the Authentication method. -# -get_auth() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_auth()" + # Search for ACI_NAME - no findACI() because we need ALL matching ACIs + typeset -a ACILIST=( ) + PATTERN='acl[ ]+"?('"${PROXY_ACI_NAME}|${PROXY_ACI_NAME// /_}"')"?' + for ACI in "${LIST[@]}" ; do + [[ ${ACI} =~ ${PATTERN} ]] && ACILIST+=( "${ACI#*=}" ) + done - _FIRST=1 # Flag for first time. - _MENU_CHOICE=0 - _AUTHMETHOD="" # Tmp method. + # We need to remove proxy agent's read access to user passwords, + # but We do not know the value of the ${LDAP_PROXYAGENT} here, so + # 1. if only one match found, delete it + # 2. if more than one matches found, ask the user which one to delete + if (( ${#ACILIST[@]} == 0 )); then + Log.warn "ACI '${PROXY_ACI_NAME}' does not exist for" \ + "'${STR[LDAP_BASEDN]}'" + return 0 + fi + if (( ${#ACILIST[@]} == 1 ));then + PROXY_ACI="${ACI[0]}" + else + X=' +Proxy agent is not allowed to read user passwords when shadow +update is enabled. There are more than one proxy agents found. +Please select the proxy agent currently being used, so that +'"${PROG}"' can remove its read access to user passwords. - while : - do - # Call Menu handler - auth_menu_handler +The proxy agents are: +' + # print the list of ACIs for selection + integer I MAX + MAX=${#ACILIST[@]} + for (( I=0; I < MAX; I++ )) ; do + ACI="${ACILIST[${I}]}" + if (( ! Log.VERBOSE )); then + # older versions used + # sed -e 's|.*ldap:///.*ldap:///||' -e 's|";)||' + ACI=${ACI##*ldap:///} + ACI=${ACI%\"*} + fi + X+=${ printf '%2d: %s\n' $((I+1)) "${ACI}" ; } + done + Log.warn "${X}" - # Add Auth Method to list. - if [ $_FIRST -eq 1 ]; then - LDAP_AUTHMETHOD="${_AUTHMETHOD}" - _FIRST=0 - else - LDAP_AUTHMETHOD="${LDAP_AUTHMETHOD};${_AUTHMETHOD}" + # ask the user to pick one + get_menu_choice "Select the proxy agent (1-${MAX}): " 1 ${MAX} + I=$(( $? - 1 )) + PROXY_ACI="${ACILIST[$I]}" fi - # Display current Authentication Method. - ${ECHO} "" - ${ECHO} "Current authenticationMethod: ${LDAP_AUTHMETHOD}" - ${ECHO} "" + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +delete: aci +aci: '"${PROXY_ACI}"' +' >${TMP[FILE]} - # Prompt for another Auth Method, or break out. - get_confirm_nodef "Do you want to add another Authentication Method?" - if [ $? -eq 0 ]; then - break; + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Remove of '${PROXY_ACI_NAME}' ACI failed" + cat ${TMP[FILE]} + return 66 fi - done + + Log.info "Removed '${PROXY_ACI_NAME}' ACI" + return 0 } - -# -# get_followref(): Whether or not to follow referrals. -# -get_followref() -{ - get_confirm "Do you want the clients to follow referrals (y/n/h)?" "n" "referrals_help" - if [ $? -eq 1 ]; then - LDAP_FOLLOWREF="TRUE" - else - LDAP_FOLLOWREF="FALSE" - fi +Man.addFunc allow_host_read_write_shadow '' '[+NAME?allow_host_read_write_shadow - add host principal read/write permission for shadow data.] +[+DESCRIPTION?Add the ACI \bHOST_ACI_NAME\b to \bSTR[LDAP_BASEDN]]\b unless it already exists.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage HOST_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (ACI already exists or added successfully).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bgetACIs()\b, \bldapmodify\b(1).] +' +function allow_host_read_write_shadow { + typeset ACI PATTERN TARGET='' + typeset -a LIST=( ) + getACIs LIST || return 66 -# -# get_timelimit(): Set the time limit. -1 is max time. -# -get_timelimit() -{ - # Get current timeout value from cn=config. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-timelimit > ${TMPDIR}/chk_timeout 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Could not reach LDAP server to check current timeout!" - cleanup - exit 1 - fi - CURR_TIMELIMIT=`${GREP} timelimit ${TMPDIR}/chk_timeout | cut -f2 -d=` - - get_negone_num "Enter the time limit for iDS (current=${CURR_TIMELIMIT}):" "-1" - IDS_TIMELIMIT=$NUM -} + PATTERN='acl[ ]+"?('"${HOST_ACI_NAME}|${HOST_ACI_NAME// /_}"')"?' + findACI LIST "${HOST_ACI_NAME}" "${PATTERN}" || return 0 + (( ! TMPF[IS_OPENDJ] )) && \ + TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant -# -# get_sizelimit(): Set the size limit. -1 is max size. -# -get_sizelimit() -{ - # Get current sizelimit value from cn=config. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-sizelimit > ${TMPDIR}/chk_sizelimit 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Could not reach LDAP server to check current sizelimit!" - cleanup - exit 1 - fi - CURR_SIZELIMIT=`${GREP} sizelimit ${TMPDIR}/chk_sizelimit | cut -f2 -d=` + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +add: aci +aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword || loginShell || homeDirectory || gecos") + ( + version 3.0; acl '"${HOST_ACI_NAME}"'; + allow (write,compare,read,search) authmethod = "sasl GSSAPI" and userdn = "ldap:///cn=*+ipHostNumber=*,ou=Hosts,'"${STR[LDAP_BASEDN]}"'; + ) +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Remove of '${ACI_NAME}' ACI failed' + return 66 + fi - get_negone_num "Enter the size limit for iDS (current=${CURR_SIZELIMIT}):" "-1" - IDS_SIZELIMIT=$NUM + showProgress "ACI '${ACI_NAME}' added." + return 0 } - -# -# get_want_crypt(): Ask user if want to store passwords in crypt? -# -get_want_crypt() -{ - get_confirm "Do you want to store passwords in \"crypt\" format (y/n/h)?" "n" "crypt_help" - if [ $? -eq 1 ]; then - NEED_CRYPT="TRUE" - else - NEED_CRYPT="FALSE" - fi +Man.addFunc find_and_delete_ACI '' '[+NAME?find_and_delete_ACI - find and delete an ACI in the given list.] +[+DESCRIPTION?Find the first ACI which matches \aregex\a in the indexed array of ACIs \avname\a and delete it. If no ACI matches, the function does nothing and returns. Otherwise it tries to delete it from \bSTR[LDAP_BASEDN]]\b and prints out a message using \aaciName\a.] +[+RETURN VALUES]{ + [+0?on success (no ACI matched or ACI removed successfully).] + [+66?an error occured when removing the matched ACI.] } +[+SEE ALSO?\bgetACIs()\b, \bldapmodify\b(1)] +\n\n\avname\a \aaciName\a \aregex\a +' +function find_and_delete_ACI { + typeset -n LIST=$1 + typeset ACI_NAME="$2" REGEX="$3" DEL='' ACI + for ACI in "${LIST[@]}" ; do + [[ ${ACI} =~ ${REGEX} ]] && DEL="${ACI}" && break + done + [[ -z ${DEL} ]] && return 0 -# -# get_srv_authMethod_pam(): Get the Service Auth Method for pam_ldap from user. -# -# NOTE: This function is base on get_auth(). -# -get_srv_authMethod_pam() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_srv_authMethod_pam()" + nextFile modify "${0}-${ACI_NAME}" + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +delete: aci +aci: '"${DEL}"' +' > ${TMP[FILE]} - _FIRST=1 # Flag for first time. - _MENU_CHOICE=0 - _AUTHMETHOD="" # Tmp method. - - while : - do - # Call Menu handler - srvauth_menu_handler - - # Add Auth Method to list. - if [ $_FIRST -eq 1 ]; then - if [ "$_AUTHMETHOD" = "" ]; then - LDAP_SRV_AUTHMETHOD_PAM="" - else - LDAP_SRV_AUTHMETHOD_PAM="pam_ldap:${_AUTHMETHOD}" - fi - _FIRST=0 - else - LDAP_SRV_AUTHMETHOD_PAM="${LDAP_SRV_AUTHMETHOD_PAM};${_AUTHMETHOD}" + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Remove of '${ACI_NAME}' ACI failed" + return 66 fi - # Display current Authentication Method. - ${ECHO} "" - ${ECHO} "Current authenticationMethod: ${LDAP_SRV_AUTHMETHOD_PAM}" - ${ECHO} "" + showProgress "ACI '${ACI_NAME}' deleted." + return 0 +} - # Prompt for another Auth Method, or break out. - get_confirm_nodef "Do you want to add another Authentication Method?" - if [ $? -eq 0 ]; then - break; - fi - done - - # Check in case user reset string and exited loop. - if [ "$LDAP_SRV_AUTHMETHOD_PAM" = "" ]; then - NEED_SRVAUTH_PAM=0 - fi +Man.addFunc deny_non_host_shadow_access '' '[+NAME?deny_non_host_shadow_access - add a deny non-host access to shadow data.] +[+DESCRIPTION?Add the ACI \bNON_HOST_ACI_NAME\b to \bSTR[LDAP_BASEDN]]\b unless it already exists. If it does not exists the ACI \bNON_ADMIN_ACI_NAME\b gets deleted first if available.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NON_HOST_ACI_NAME NON_ADMIN_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (ACI already exists or added successfully).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bgetACIs()\b, \bfind_and_delete_ACI()\b, \bldapmodify\b(1).] +' +function deny_non_host_shadow_access { + typeset ACI PATTERN TARGET='' + typeset -a LIST=( ) + getACIs LIST || return 66 -# -# get_srv_authMethod_key(): Get the Service Auth Method for keyserv from user. -# -# NOTE: This function is base on get_auth(). -# -get_srv_authMethod_key() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_srv_authMethod_key()" + # If an ACI with ${NON_HOST_ACI_NAME} already exists, we are done + PATTERN='acl[ ]+"?('"${NON_HOST_ACI_NAME}|${NON_HOST_ACI_NAME// /_}"')"?' + findACI LIST "${NON_HOST_ACI_NAME}" "${PATTERN}" || return 0 - _FIRST=1 # Flag for first time. - _MENU_CHOICE=0 - _AUTHMETHOD="" # Tmp method. + # The deny_non_admin_shadow_access and deny_non_host_shadow_access ACIs + # should be mutually exclusive, so if the former exists, delete it + PATTERN='acl[ ]+"?('"${NON_ADMIN_ACI_NAME}|${NON_ADMIN_ACI_NAME// /_}"')"?' + find_and_delete_ACI LIST "${NON_ADMIN_ACI_NAME}" "${PATTERN}" || return 67 - while : - do - # Call Menu handler - srvauth_menu_handler + (( ! TMPF[IS_OPENDJ] )) && \ + TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant - # Add Auth Method to list. - if [ $_FIRST -eq 1 ]; then - if [ "$_AUTHMETHOD" = "" ]; then - LDAP_SRV_AUTHMETHOD_KEY="" - else - LDAP_SRV_AUTHMETHOD_KEY="keyserv:${_AUTHMETHOD}" - fi - _FIRST=0 - else - LDAP_SRV_AUTHMETHOD_KEY="${LDAP_SRV_AUTHMETHOD_KEY};${_AUTHMETHOD}" + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +add: aci +aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax ||shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword") + ( + version 3.0; acl "'"${NON_HOST_ACI_NAME}"'"; + deny (write,read,search,compare) userdn != "ldap:///cn=*+ipHostNumber=*,ou=Hosts,'"${STR[LDAP_BASEDN]}"'"; + ) +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding ACI '${NON_HOST_ACI_NAME}' failed" + return 67 fi - # Display current Authentication Method. - ${ECHO} "" - ${ECHO} "Current authenticationMethod: ${LDAP_SRV_AUTHMETHOD_KEY}" - ${ECHO} "" + showProgress "ACI '${NON_HOST_ACI_NAME}' added." + return 0 +} - # Prompt for another Auth Method, or break out. - get_confirm_nodef "Do you want to add another Authentication Method?" - if [ $? -eq 0 ]; then - break; - fi - done +Man.addFunc get_adminDN '' '[+NAME?get_adminDN - ask user for Admin DN.] +[+DESCRIPTION?Ask the user for the Admin DN and store it into \bSTR[LDAP_BASEDN]]\b.] +[+SEE ALSO?\bget_ans()\b.] +' +function get_adminDN { + get_ans 'Enter DN for the administrator:' \ + "cn=admin,ou=profile,${STR[LDAP_BASEDN]}" + STR[LDAP_ADMINDN]="${ANS}" +} - # Check in case user reset string and exited loop. - if [ "$LDAP_SRV_AUTHMETHOD_KEY" = "" ]; then - NEED_SRVAUTH_KEY=0 - fi +Man.addFunc get_admin_pw '' '[+NAME?get_admin_pw - ask user for Admin password] +[+DESCRIPTION?Ask the user for the Admin password and store it into \bSTR[LDAP_ADMIN_CRED]]\b.] +[+SEE ALSO?\bget_passwd()\b.] +' +function get_admin_pw { + get_passwd 'Enter passwd for the administrator:' + STR[LDAP_ADMIN_CRED]="${ANS}" } +Man.addFunc add_admin '' '[+NAME?add_admin - Add administrator identity.] +[+DESCRIPTION?Add the \bSTR[LDAP_ADMINDN]]\b entry with the password \bSTR[LDAP_ADMIN_CRED]]\b unless it already exists.] +[+RETURN VALUES]{ + [+0?on success (entry already exists or added successfully).] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] +' +function add_admin { + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${STR[LDAP_ADMINDN]}" \ + -s base 'objectclass=*' >/dev/null 2>&1 + then + showProgress "Administrator identity exists." + return 0 + fi -# -# get_srv_authMethod_cmd(): Get the Service Auth Method for passwd-cmd from user. -# -# NOTE: This function is base on get_auth(). -# -get_srv_authMethod_cmd() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_srv_authMethod_cmd()" + typeset CN="${STR[LDAP_ADMINDN]%%,*}" + CN="${CN#*=}" - _FIRST=1 # Flag for first time. - _MENU_CHOICE=0 - _AUTHMETHOD="" # Tmp method. + nextFile add $0 + print ' +dn: '"${STR[LDAP_ADMINDN]}"' +cn: '"${CN}"' +sn: '"${CN}"' +objectclass: top +objectclass: person +userpassword: '"${STR[LDAP_ADMIN_CRED]}"' +' > ${TMP[FILE]} - while : - do - # Call Menu handler - srvauth_menu_handler - - # Add Auth Method to list. - if [ $_FIRST -eq 1 ]; then - if [ "$_AUTHMETHOD" = "" ]; then - LDAP_SRV_AUTHMETHOD_CMD="" - else - LDAP_SRV_AUTHMETHOD_CMD="passwd-cmd:${_AUTHMETHOD}" - fi - _FIRST=0 - else - LDAP_SRV_AUTHMETHOD_CMD="${LDAP_SRV_AUTHMETHOD_CMD};${_AUTHMETHOD}" + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Adding administrator identity failed' + return 66 fi - # Display current Authentication Method. - ${ECHO} "" - ${ECHO} "Current authenticationMethod: ${LDAP_SRV_AUTHMETHOD_CMD}" - ${ECHO} "" - - # Prompt for another Auth Method, or break out. - get_confirm_nodef "Do you want to add another Authentication Method?" - if [ $? -eq 0 ]; then - break; - fi - done - - # Check in case user reset string and exited loop. - if [ "$LDAP_SRV_AUTHMETHOD_CMD" = "" ]; then - NEED_SRVAUTH_CMD=0 - fi + showProgress 'Administrator identity added.' + return 0 } - -# -# get_srch_time(): Amount of time to search. -# -get_srch_time() -{ - get_negone_num "Client search time limit in seconds (h=help):" "$LDAP_SEARCH_TIME_LIMIT" "srchtime_help" - LDAP_SEARCH_TIME_LIMIT=$NUM +Man.addFunc allow_admin_read_write_shadow '' '[+NAME?allow_admin_read_write_shadow - add Admin read/write permission for shadow, rbac and mount data.] +[+DESCRIPTION?Add the ACI \bADMIN_ACI_NAME\b to \bSTR[LDAP_BASEDN]]\b unless it already exists. Also tries to remove old similar ACIs if they exist.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ADMIN_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (aci already exists or addition was successfull).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bgetACIs()\b, \bfind_and_delete_ACI()\b, \bldapmodify\b(1).] +' +function allow_admin_read_write_shadow { + typeset PERMS='write,compare,read,search,add,delete' \ + ACI RBAC PATTERN TARGET='' + typeset -a LIST=( ) + getACIs LIST || return 66 -# -# get_prof_ttl(): The profile time to live (TTL) -# -get_prof_ttl() -{ - get_negone_num "Profile Time To Live in seconds (h=help):" "$LDAP_PROFILE_TTL" "profttl_help" - LDAP_PROFILE_TTL=$NUM -} + # If the ACI already exists, we are done + PATTERN='acl[ ]+"?('"${ADMIN_ACI_NAME}|${ADMIN_ACI_NAME// /_}"')"?' + PATTERN+='.*\('"${PERMS}"'\).*'"${STR[LDAP_ADMINDN]}" + findACI LIST "${ADMIN_ACI_NAME}" "${PATTERN}" || return 0 + # If an ACI with ${ADMIN_ACI_NAME} and "(write|write,compare,read,search)" + # and ${LDAP_ADMINDN} exists, delete it + PATTERN='acl[ ]+"?('"${ADMIN_ACI_NAME}|${ADMIN_ACI_NAME// /_}"')"?' + PATTERN+='.*\(write\).*'"${STR[LDAP_ADMINDN]}" + find_and_delete_ACI LIST "${ADMIN_ACI_NAME}" "${PATTERN}" || return 67 -# -# get_bind_limit(): Bind time limit -# -get_bind_limit() -{ - get_negone_num "Bind time limit in seconds (h=help):" "$LDAP_BIND_LIMIT" "bindlim_help" - LDAP_BIND_LIMIT=$NUM -} + PATTERN='acl[ ]+"?('"${ADMIN_ACI_NAME}|${ADMIN_ACI_NAME// /_}"')"?' + PATTERN+='.*\(write,compare,read,search\).*'"${STR[LDAP_ADMINDN]}" + find_and_delete_ACI LIST "${ADMIN_ACI_NAME}" "${PATTERN}" || return 68 + RBAC='objectClass || uid || uidNumber || gidNumber || cn' + RBAC+=' || SolarisAttrKeyValue || SolarisAttrShortDesc' + RBAC+=' || SolarisAttrLongDesc || SolarisKernelSecurityPolicy' + RBAC+=' || SolarisProfileType || SolarisProfileId || SolarisUserQualifier' + RBAC+=' || SolarisReserved1 || SolarisReserved2' + RBAC+=' || SolarisAttrReserved1 || SolarisAttrReserved2' + RBAC+=' || SolarisProjectID || SolarisProjectName' + RBAC+=' || SolarisProjectAttr || memberUid || memberGid || description' + RBAC+=' || automountKey || automountMapName || automountInformation' + RBAC+=' || ipTnetTemplateName || ipTnetNumber' -###################################################################### -# FUNCTIONS FOR Service Search Descriptor's START HERE. -###################################################################### + (( ! TMPF[IS_OPENDJ] )) && \ + TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant - -# -# add_ssd(): Get SSD's from user and add to file. -# -add_ssd() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_ssd()" - - # Enter the service id. Loop til unique. - while : - do - get_ans "Enter the service id:" - _SERV_ID=$ANS - - # Grep for name existing. - ${GREP} -i "^$ANS:" ${SSD_FILE} > /dev/null 2>&1 - if [ $? -eq 1 ]; then - break + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +add: aci +aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword || loginShell || homeDirectory || gecos || '"${RBAC}"'") + ( + version 3.0; acl "'${ADMIN_ACI_NAME}'"; + allow ('"${PERMS}"') userdn = "ldap:///'"${STR[LDAP_ADMINDN]}"'"; + ) +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${ADMIN_ACI_NAME}' ACI failed" + return 69 fi - # Name exists, print message, let user decide. - ${ECHO} "ERROR: Service id ${ANS} already exists." - done - - get_ans "Enter the base:" - _BASE=$ANS + showProgress "ACI '${ADMIN_ACI_NAME}' added." + return 0 +} - # Get the scope and verify that its one or sub. - while : - do - get_ans "Enter the scope:" - _SCOPE=$ANS - case `${ECHO} ${_SCOPE} | tr '[A-Z]' '[a-z]'` in - one) break ;; - sub) break ;; - *) ${ECHO} "${_SCOPE} is Not valid - Enter 'one' or 'sub'" ;; - esac - done - - # Build SSD to add to file. - _SSD="${_SERV_ID}:${_BASE}?${_SCOPE}" - - # Add the SSD to the file. - ${ECHO} "${_SSD}" >> ${SSD_FILE} +Man.addFunc deny_non_admin_shadow_access '' '[+NAME?deny_non_admin_shadow_access - add a deny Admin access to shadow data.] +[+DESCRIPTION?Add the ACI \bNON_ADMIN_ACI_NAME\b to \bSTR[LDAP_BASEDN]]\b unless it already exists. If it does not exists the ACI \bNON_HOST_ACI_NAME\b gets deleted first if available.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NON_ADMIN_ACI_NAME NON_HOST_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (ACI already exists or added successfully).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bgetACIs()\b, \bfind_and_delete_ACI()\b, \bldapmodify\b(1).] +' +function deny_non_admin_shadow_access { + typeset TARGET='' + typeset -a LIST=( ) + getACIs LIST || return 66 + # If the ACI already exists, we are done + PATTERN='acl[ ]+"?('"${NON_ADMIN_ACI_NAME}|${NON_ADMIN_ACI_NAME// /_}"')"?' + findACI LIST "${NON_ADMIN_ACI_NAME}" "${PATTERN}" || return 0 -# -# delete_ssd(): Delete a SSD from the list. -# -delete_ssd() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In delete_ssd()" + # The deny_non_admin_shadow_access and deny_non_host_shadow_access ACIs + # should be mutually exclusive, so if the latter exists, delete it + PATTERN='acl[ ]+"?('"${NON_HOST_ACI_NAME}|${NON_HOST_ACI_NAME// /_}"')"?' + find_and_delete_ACI LIST "${NON_HOST_ACI_NAME}" "${PATTERN}" || return 67 - # Get service id name from user for SSD to delete. - get_ans_req "Enter service id to delete:" + (( ! TMPF[IS_OPENDJ] )) && \ + TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant - # Make sure service id exists. - ${GREP} "$ANS" ${SSD_FILE} > /dev/null 2>&1 - if [ $? -eq 1 ]; then - ${ECHO} "Invalid service id: $ANS not present in list." - return - fi + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' +changetype: modify +add: aci +aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword") + ( + version 3.0; acl "'"${NON_ADMIN_ACI_NAME}"'"; + deny (write,read,search,compare,add,delete) userdn != "ldap:///'"${STR[LDAP_ADMINDN]}"'"; + ) +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${NON_ADMIN_ACI_NAME}' ACI failed" + ${CAT} ${TMP[FILE]} + return 68 + fi - # Create temporary back SSD file. - cp ${SSD_FILE} ${SSD_FILE}.bak - if [ $? -eq 1 ]; then - ${ECHO} "ERROR: could not create file: ${SSD_FILE}.bak" - exit 1 - fi + showProgress "ACI '${NON_ADMIN_ACI_NAME}' added." + return 0 +} - # Use ${GREP} to remove the SSD. Read from temp file - # and write to the orig file. - ${GREP} -v "$ANS" ${SSD_FILE}.bak > ${SSD_FILE} +Man.addFunc setup_shadow_update '' '[+NAME?setup_shadow_update - set up shadow update.] +[+DESCRIPTION?Check whether the profile \bSTR[LDAP_PROFILE_NAME]]\b for \bSTR[LDAP_BASEDN]]\b exists. If not simply notify the user and return. Otherwise check whether the profile uses authenticationMethod "GSSAPI" and credentialLevel "self". If so ask the user whether to use host or "Admin" user principal for shadow updates and collect the related info. Sets \bINT[NEED_HOSTACL]]\b if the host principal should be used for shadow update (requires that \bTMPF[GSSAPI_AUTH_MAY_BE_USED]]\b is already set).] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66? a fatal error occured.] } +[+SEE ALSO?\bldapsearch\b(1), \bget_confirm()\b, \bdelete_proxy_read_pw()\b, \ballow_host_read_write_shadow()\b, \bdeny_non_host_shadow_access()\b, \bget_adminDN()\b, \bget_admin_pw()\b, \badd_admin()\b, \ballow_admin_read_write_shadow()\b, \bdeny_non_admin_shadow_access()\b.] +' +function setup_shadow_update { + # get content of the profile + integer CN=0 GSSAPI=0 SELF=0 + typeset -l LINE + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "cn=${STR[LDAP_PROFILE_NAME]},ou=profile,${STR[LDAP_BASEDN]}" \ + -s base 'objectclass=*' 2>/dev/null | \ + while read LINE ; do + [[ ${LINE:0:3} == 'cn=' ]] && CN=1 + [[ ${LINE:0:21} == 'authenticationmethod=' && ${LINE:21} == 'GSSAPI' ]]\ + && GSSAPI=1 + [[ ${LINE:0:16} == 'credentiallevel=' && ${LINE:16} == 'self' ]] \ + && SELF=1 + done + if (( ! CN )); then + Log.verbose "Profile '${STR[LDAP_PROFILE_NAME]}' does not exist" + return 0 + fi -# -# modify_ssd(): Allow user to modify a SSD. -# -modify_ssd() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In modify_ssd()" + # If authenticationMethod has 'GSSAPI' and credentialLevel + # has 'self', ask to use the host principal for shadow update + if (( TMPF[GSSAPI_AUTH_MAY_BE_USED] && GSSAPI && SELF )); then + INT[NEED_HOSTACL]=1 + fi + Log.verbose "NEED_HOSTACL = ${INT[NEED_HOSTACL]}" - # Prompt user for service id. - get_ans_req "Enter service id to modify:" - - # Put into temp _LINE. - _LINE=`${GREP} "^$ANS:" ${SSD_FILE}` - if [ "$_LINE" = "" ]; then - ${ECHO} "Invalid service id: $ANS" - return - fi + if (( INT[NEED_HOSTACL] )); then + get_confirm 'Use host principal for shadow data update (y/n/h)?' 'y' \ + 'use_host_principal_help' + if (( $? )); then + delete_proxy_read_pw || return 66 + allow_host_read_write_shadow || return 67 + deny_non_host_shadow_access || return 68 + Log.info 'Shadow update has been enabled' + else + Log.warn 'Shadow update may not work' + fi + return 0 + fi - # Display current filter for user to see. - ${ECHO} "" - ${ECHO} "Current SSD: $_LINE" - ${ECHO} "" - - # Get the defaults. - _CURR_BASE=`${ECHO} $_LINE | cut -d: -f2 | cut -d'?' -f 1` - _CURR_SCOPE=`${ECHO} $_LINE | cut -d: -f2 | cut -d'?' -f 2` + get_confirm 'Add the administrator identity (y/n/h)?' 'y' \ + 'add_admin_cred_help' + if (( $? )); then + get_adminDN + get_admin_pw + add_admin || return 69 + delete_proxy_read_pw || return 66 + allow_admin_read_write_shadow || return 70 + deny_non_admin_shadow_access || return 71 + Log.info 'Shadow update has been enabled' + else + Log.warn 'No administrator identity specified, shadow update may not' \ + 'work' + fi + return 0 +} - # Create temporary back SSD file. - cp ${SSD_FILE} ${SSD_FILE}.bak - if [ $? -eq 1 ]; then - ${ECHO} "ERROR: could not create file: ${SSD_FILE}.bak" - cleanup - exit 1 - fi +Man.addFunc get_srv_list '' '[+NAME?get_srv_list - Ask user for default server list.] +[+DESCRIPTION?Ask the user for the default server list and store it into \bSTR[LDAP_SERVER_LIST]]\b.] +[+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] +' +function get_srv_list { + # If LDAP_SERVER_LIST is NULL, then set, otherwise leave alone + if [[ -z ${STR[LDAP_SERVER_LIST]} ]]; then + ANS=${ ${GETENT} hosts ${STR[DS_HOST]} ; } + STR[LDAP_SERVER_LIST]=${ANS%%[[:space:]]*} + if (( INT[DS_PORT] != 389 )); then + STR[LDAP_SERVER_LIST]="${STR[LDAP_SERVER_LIST]}:${INT[DS_PORT]}" + fi + fi - # Removed the old line. - ${GREP} -v "^$ANS:" ${SSD_FILE}.bak > ${SSD_FILE} 2>&1 - - # New Entry - _SERV_ID=$ANS - get_ans_req "Enter the base:" "$_CURR_BASE" - _BASE=$ANS - get_ans_req "Enter the scope:" "$_CURR_SCOPE" - _SCOPE=$ANS + while : ; do + get_ans 'Default server list (h=help):' "${STR[LDAP_SERVER_LIST]}" + case "${ANS}" in + [Hh] | help | Help | '?') display_msg 'def_srvlist_help' ;; + * ) break ;; + esac + done + STR[LDAP_SERVER_LIST]="${ANS}" +} - # Build the new SSD. - _SSD="${_SERV_ID}:${_BASE}?${_SCOPE}" +Man.addFunc get_pref_srv '' '[+NAME?get_pref_srv - Ask user for preferred server list.] +[+DESCRIPTION?Ask the user for the preferred server list and store it into \bSTR[LDAP_PREF_SRVLIST]]\b (overrides the server list).] +[+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] +' +function get_pref_srv { + while : ; do + get_ans 'Preferred server list (h=help):' "${STR[LDAP_PREF_SRVLIST]}" + case "${ANS}" in + [Hh] | help | Help | '?') display_msg 'pref_srvlist_help' ;; + * ) break ;; + esac + done + STR[LDAP_PREF_SRVLIST]="${ANS}" +} - # Add the SSD to the file. - ${ECHO} "${_SSD}" >> ${SSD_FILE} +Man.addFunc get_search_scope '' '[+NAME?get_search_scope - Ask user for search scope.] +[+DESCRIPTION?Ask the user for the search scope and store it into \bSTR[LDAP_SEARCH_SCOPE]]\b.] +[+RETURN VALUES]{ + [+1?search scope "one" selected.] + [+2?search scope "sub" selected.] } +[+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] +' +function get_search_scope { + integer RES + get_menu_choice 'Choose desired search scope (1=one, 2=sub, h=help): ' \ + 1 2 1 srch_scope_help + (( $? == 2 )) && { STR[LDAP_SEARCH_SCOPE]='sub' ; return 2 ; } + STR[LDAP_SEARCH_SCOPE]='one' + return 1 +} +Man.addFunc get_cred_level '' '[+NAME?get_cred_level - Ask user for credential level.] +[+DESCRIPTION?Ask the user for the client credential level to use and store it into \bSTR[LDAP_CRED_LEVEL]]\b.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage CRED_LEVELS STR ; }" '} +[+RETURN VALUES?The index wrt. \bCRED_LEVELS\b of the selected credential level \b+1\b.] +[+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] +' +function get_cred_level { + integer RES + typeset PROMPT='' + (( INT[GSSAPI_ENABLE] )) && PROMPT='"self" is needed for GSSAPI profile.\n' + PROMPT+='Choose Credential level (identity) [h=help]:' -# -# display_ssd(): Display the current SSD list. -# -display_ssd() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In display_ssd()" + get_menu_choice "${PROMPT}" 1 4 1 'cred_lvl_help' 'cred_level_menu' + RES=$? + STR[LDAP_CRED_LEVEL]="${CRED_LEVELS[RES-1]}" + return ${RES} +} - ${ECHO} "" - ${ECHO} "Current Service Search Descriptors:" - ${ECHO} "==================================" - cat ${SSD_FILE} - ${ECHO} "" - ${ECHO} "Hit return to continue." - read __A +Man.addFunc get_auth '' '[+NAME?get_auth - ask user for authentication methods.] +[+DESCRIPTION?Ask the user for authentication methods to enable and store them as a semicolon separated list into \bSTR[LDAP_AUTHMETHOD | LDAP_SRV_AUTHMETHOD_{PAM|KEY|CMD}}]]\b and set \bINT[NEED_SRVAUTH_{PAM|KEY|CMD}]]\b to 0 depending on the given \atype\a (either "client", "pam_ldap", "keyserv", or "passwd-cmd") and selected methods.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage AUTH_METHODS STR ; }" '} +[+RETURN VALUES]{ + [+0?on success.] + [+-1?if an invalid \atype\a was given.] } +[+SEE ALSO?\bget_menu_choice()\b, \bget_confirm_nodef()\b.] +\n\n\atype\a +' +function get_auth { + typeset TYPE=$1 VNAME HELPTAG MENU + integer RES MAX=${#AUTH_METHODS[@]} OFFSET=0 + typeset -a SELECTED=( ) + typeset -u UTYPE=${TYPE} + case "${TYPE}" in + client) VNAME=LDAP_AUTHMETHOD ; OFFSET=1 ; UTYPE='DEFAULT' ;; + pam_ldap) VNAME=LDAP_SRV_AUTHMETHOD_PAM ;; + keyserv) VNAME=LDAP_SRV_AUTHMETHOD_KEY ;; + passwd-cmd) VNAME=LDAP_SRV_AUTHMETHOD_CMD ;; + *) print -u2 "${.sh.fun}() bug: invalid type argument" && return -1 ;; + esac + + typeset ASK='' VAL='' M='' + if (( OFFSET )); then + # client + (( INT[GSSAPI_ENABLE] )) && \ + ASK='"sasl/GSSAPI" is needed for GSSAPI profile.\n' + ASK+='Choose DEFAULT authentication (bind) method (0=reset, h=help):' + MENU='auth_method_menu' + HELPTAG='auth_help' + else + ASK="Choose ${UTYPE} authentication (bind) method (0=reset, h=help):" + (( MAX-- )) + MENU='srvauth_method_menu' + HELPTAG='srvauth_help' + fi -# -# prompt_ssd(): Get SSD's from user. -# -prompt_ssd() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In prompt_ssd()" - # See if user wants SSD's? - get_confirm "Do you wish to setup Service Search Descriptors (y/n/h)?" "n" "ssd_help" - [ "$?" -eq 0 ] && return + while : ; do + get_menu_choice "${ASK}" 0 ${MAX} 1 ${HELPTAG} ${MENU} + RES=$? + if (( RES )); then + M=${AUTH_METHODS[RES - OFFSET]} + # avoid doubles + [[ ${VAL} != ~(E)[ ]${M}[ ] ]] && SELECTED+=( ${M} ) + VAL=" ${SELECTED[@]} " + M=${VAL// /\;} + M=${M#\;} + else + typeset -a SELECTED=( ) # reset + VAL='' + M='' + fi + print && Log.info "Current authentication method(s): ${M%\;}\n" - # Display menu for SSD choices. - while : - do - display_msg prompt_ssd_menu - get_ans "Enter menu choice:" "Quit" - case "$ANS" in - [Aa] | add) add_ssd ;; - [Dd] | delete) delete_ssd ;; - [Mm] | modify) modify_ssd ;; - [Pp] | print | display) display_ssd ;; - [Xx] | reset | clear) reset_ssd_file ;; - [Hh] | Help | help) display_msg ssd_menu_help - ${ECHO} " Press return to continue." - read __A ;; - [Qq] | Quit | quit) return ;; - *) ${ECHO} "Invalid choice: $ANS please re-enter from menu." ;; - esac - done + get_confirm_nodef "Do you want to add another ${UTYPE} authentication method (y/n)?" \ + && break + done + M=${M%\;} + if (( ! OFFSET )); then + M=${M//\;/\;${TYPE}:} + if [[ -n ${M} ]]; then + STR[${VNAME}]="${TYPE}:${M}" + else + STR[${VNAME}]='' + INT[NEED_SRVAUTH_${TYPE##*_}]=0 + fi + else + STR[${VNAME}]="${M}" + fi + Log.verbose "Selected ${UTYPE} authentication methods: '${STR[${VNAME}]}'" + return 0 } +Man.addFunc get_followref '' '[+NAME?get_followref - Ask user whether or not to follow referrals.] +[+DESCRIPTION?Ask the user whether the client should follow referrals. Result gets stored into \bINT[LDAP_FOLLOWREF]]\b.] +[+SEE ALSO?\bget_confirm()\b, \bldapclient\b(1M).] +' +function get_followref { + get_confirm 'Do you want the clients to follow referrals (y/n/h)?' 'n' \ + 'referrals_help' + INT[LDAP_FOLLOWREF]=$? +} -# -# reset_ssd_file(): Blank out current SSD file. -# -reset_ssd_file() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In reset_ssd_file()" - - rm -f ${SSD_FILE} - touch ${SSD_FILE} +Man.addFunc get_timelimit '' '[+NAME?get_timelimit - Ask user for search time limit.] +[+DESCRIPTION?Ask the user for the max. time allowed to process a search operation and store it into \bINT[DS_TIMELIMIT]]\b.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bis_numeric()\b, \bget_negone_num()\b.] +' +function get_timelimit { + typeset NAME='nsslapd-timelimit' VAL='' + (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-time-limit' # min val = 0 + # Get current timeout value from cn=config + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b 'cn=config' -s base \ + 'objectclass=*' "${NAME}" 2>/dev/null | \ + while read LINE; do + if [[ ${LINE%%=*} == ${NAME} ]]; then + VAL=${LINE#*=} + break + fi + done + if [[ -z ${VAL} ]]; then + Log.fatal 'Unable to check current search processing timeout of the DS' + return 66 + fi + VAL=${VAL%%[[:space:]]*} + is_numeric ${VAL} && NUM=${VAL} || NUM=INT[DS_TIMELIMIT] + + get_negone_num "Enter the DS search processing time limit in seconds (current=${NUM})" \ + '30' + (( TMPF[IS_OPENDJ] && NUM < 0 )) && NUM=0 # unlimited -# -# create_ssd_file(): Create a temporary file for SSD's. -# -create_ssd_file() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In create_ssd_file()" + INT[DS_TIMELIMIT]=${NUM} + return 0 +} - # Build a list of SSD's and store in temp file. - ${GREP} "LDAP_SERV_SRCH_DES=" ${INPUT_FILE} | \ - sed 's/LDAP_SERV_SRCH_DES=//' \ - > ${SSD_FILE} +Man.addFunc get_sizelimit '' '[+NAME?get_sizelimit - Ask user for search size limit.] +[+DESCRIPTION?Ask the user for the max. number of entries to return for a single search operation and store it into \bINT[DS_SIZELIMIT]]\b.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bis_numeric()\b, \bget_negone_num()\b.] +' +function get_sizelimit { + typeset NAME='nsslapd-sizelimit' VAL='' + (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-size-limit' # min val = 0 + # Get current search sizelimit + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b 'cn=config' -s base \ + 'objectclass=*' "${NAME}" 2>/dev/null | \ + while read LINE; do + if [[ ${LINE%%=*} == ${NAME} ]]; then + VAL=${LINE#*=} + break + fi + done + if [[ -z ${VAL} ]]; then + Log.fatal 'Unable to check current search entry result limit of the DS' + return 66 + fi + is_numeric ${VAL} && NUM=${VAL} || NUM=INT[DS_SIZELIMIT] -# -# ssd_2_config(): Append the SSD file to the output file. -# -ssd_2_config() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In ssd_2_config()" - - # Convert to config file format using sed. - sed -e "s/^/LDAP_SERV_SRCH_DES=/" ${SSD_FILE} >> ${OUTPUT_FILE} + get_negone_num "Enter max. entries per search the DS returns (current=${NUM})" '1000' + (( TMPF[IS_OPENDJ] && NUM < 0 )) && NUM=0 # unlimited + INT[DS_SIZELIMIT]=${NUM} + return 0 } +Man.addFunc get_want_crypt '' '[+NAME?get_want_crypt - Ask user whether to store passwords in crypt.] +[+DESCRIPTION?Ask the user whether the default password storage scheme should be set to "CRYPT" and stores the result into \bINT[NEED_CRYPT]]\b.] +[+SEE ALSO?\bget_confirm()\b.] +' +function get_want_crypt { + get_confirm 'Do you want to store passwords in "CRYPT" formats (y/n/h)?' \ + 'n' 'crypt_help' + INT[NEED_CRYPT]=$? +} -# -# ssd_2_profile(): Add SSD's to the GEN_CMD string. -# -ssd_2_profile() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In ssd_2_profile()" +Man.addFunc get_srch_time '' '[+NAME?get_srch_time - Ask user for client search time limit.] +[+DESCRIPTION?Ask the user for the max. time in seconds a client waits for a search result and store it into \bINT[LDAP_SEARCH_TIME_LIMIT]]\b.] +[+SEE ALSO?\bget_negone_num()\b, \bldapclient\b(1M).] +' +function get_srch_time { + get_negone_num 'Client search time limit in seconds (h=help):' \ + ${INT[LDAP_SEARCH_TIME_LIMIT]} 'srchtime_help' + INT[LDAP_SEARCH_TIME_LIMIT]=${NUM} +} - GEN_TMPFILE=${TMPDIR}/ssd_tmpfile - touch ${GEN_TMPFILE} - - # Add and convert each SSD to string. - while read SSD_LINE - do - ${ECHO} " -a \"serviceSearchDescriptor=${SSD_LINE}\"\c" >> ${GEN_TMPFILE} - done <${SSD_FILE} - - # Add SSD's to GEN_CMD. - GEN_CMD="${GEN_CMD} `cat ${GEN_TMPFILE}`" +Man.addFunc get_prof_ttl '' '[+NAME?get_prof_ttl - Ask user for client profile time to live (TTL).] +[+DESCRIPTION?Ask the user for the max. time in seconds a client may cache its profile before refreshing again and store it into \bINT[LDAP_PROFILE_TTL]]\b.] +[+SEE ALSO?\bget_negone_num()\b, \bldapclient\b(1M).] +' +function get_prof_ttl { + get_negone_num 'Client Profile Time To Live in seconds (h=help):' \ + ${INT[LDAP_PROFILE_TTL]} 'profttl_help' + INT[LDAP_PROFILE_TTL]=${NUM} } -# -# get_adminDN(): Get the admin DN. -# -get_adminDN() -{ - LDAP_ADMINDN="cn=admin,ou=profile,${LDAP_BASEDN}" # default - get_ans "Enter DN for the administrator:" "$LDAP_ADMINDN" - LDAP_ADMINDN=$ANS - [ $DEBUG -eq 1 ] && ${ECHO} "LDAP_ADMINDN = $LDAP_ADMINDN" +Man.addFunc get_bind_limit '' '[+NAME?get_bind_limit - Ask user for client bind time limit.] +[+DESCRIPTION?Ask the user for the max. time in seconds a client may try to bind to the server and store it into \bINT[LDAP_BIND_LIMIT]]\b.] +[+SEE ALSO?\bget_negone_num()\b, \bldapclient\b(1M).] +' +function get_bind_limit { + get_negone_num 'Client bind time limit in seconds (h=help):' \ + ${INT[LDAP_BIND_LIMIT]} 'bindlim_help' + INT[LDAP_BIND_LIMIT]=${NUM} } -# -# get_admin_pw(): Get the admin passwd. -# -get_admin_pw() -{ - get_passwd "Enter passwd for the administrator:" - LDAP_ADMIN_CRED=$ANS - [ $DEBUG -eq 1 ] && ${ECHO} "LDAP_ADMIN_CRED = $LDAP_ADMIN_CRED" +Man.addFunc get_want_shadow_update '' '[+NAME?get_want_shadow_update - Ask user whether to enable shadow update.] +[+DESCRIPTION?Ask the user whether to enable shadow update. Set \bINT[LDAP_ENABLE_SHADOW_UPDATE]]\b to 0 if not desired, to 1 otherwise.] +[+SEE ALSO?\bget_confirm()\b.] +' +function get_want_shadow_update { + get_confirm 'Do you want to enable shadow update (y/n/h)?' 'n' \ + 'enable_shadow_update_help' + INT[LDAP_ENABLE_SHADOW_UPDATE]=$? } -# -# add_admin(): Add an admin entry for nameservice for updating shadow data. -# -add_admin() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_admin()" + ###################################################################### + # FUNCTIONS FOR Service Search Descriptor's START HERE. + ###################################################################### +Man.addFunc add_ssd '' '[+NAME?add_ssd - Ask user for SSD to add.] +[+DESCRIPTION?Ask the user for the ID:BASE:SCOPE and add it to \bSSD\b list.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} +[+SEE ALSO?\bget_ans\b, \bget_ans_req()\b.] +' +function add_ssd { + typeset ID='' BASE='' X + typeset -l SCOPE='' + integer FOUND - # Check if the admin user already exists. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_ADMINDN}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then - MSG="Administrator ${LDAP_ADMINDN} already exists." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " NOT ADDED: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` - fi - return 0 - fi + while : ; do + get_ans 'Enter the service name (e.g. passwd):' + [[ -z ${ANS} ]] && return + [[ ${ANS} =~ [:] ]] && print 'Invalid service name' && continue - # Get cn and sn names from LDAP_ADMINDN. - cn_tmp=`${ECHO} ${LDAP_ADMINDN} | cut -f1 -d, | cut -f2 -d=` - - # Create the tmp file to add. - ( cat < ${TMPDIR}/admin + FOUND=0 + for X in "${SSD[@]}" ; do + [[ ${X%%:*} == ${ANS} ]] && FOUND=1 && break + done + (( ! FOUND )) && break + Log.warn "An SSD for '${ANS}' already exists:\n${X}\n" + done + ID="${ANS}" - # Add the entry. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/admin ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding administrator identity failed!" - cleanup - exit 1 - fi + while : ; do + get_ans_req 'Enter the search base (e.g. ou=people,o=inet):' + [[ ${ANS} != ~(E)[:] ]] && break + Log.warn 'Invalid search base' && continue + done + BASE="${ANS}" ${RM} -f ${TMPDIR}/admin @@ -2471,1058 +3025,686 @@ exit 1 fi - ${RM} -f ${TMPDIR}/admin + while : ; do + get_ans_req "Enter the search scope (one|sub):" + SCOPE=${ANS} + [[ ${SCOPE} == 'one' || ${SCOPE} == 'sub' ]] && break + Log.warn "'${ANS}' is NOT valid - Enter 'one' or 'sub'" + done - # Display message that the administrator identity is added. - MSG="Administrator identity ${LDAP_ADMINDN}" - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " ADDED: $MSG." - else - ${ECHO} " ${STEP}. $MSG added." - STEP=`expr $STEP + 1` - fi + SSD+=( "${ID}:${BASE}?${SCOPE}" ) } -# -# allow_admin_read_write_shadow(): Give Admin read/write permission -# to shadow data. -# -allow_admin_read_write_shadow() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In allow_admin_read_write_shadow()" +Man.addFunc delete_ssd '' '[+NAME?delete_ssd - Ask user which SSD to delete.] +[+DESCRIPTION?Ask the user for the ID of the SSD to delete and remove it from \bSSD\b list.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} +[+SEE ALSO?\bget_ans()\b.] +' +function delete_ssd { + get_ans 'Enter the service name of the SSD to delete (e.g. passwd):' + [[ -z ${ANS} ]] && return - # Set ACI Name - ADMIN_ACI_NAME="LDAP_Naming_Services_admin_shadow_write" + typeset X + integer I FOUND=-1 MAX=${#SSD[@]} - # Search for ACI_NAME - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" \ - -s base objectclass=* aci > ${TMPDIR}/chk_adminwrite_aci 2>&1" - - # if an ACI with ${ADMIN_ACI_NAME} and "write,compare,read,search" - # and ${LDAP_ADMINDN} already exists, we are done - ${EGREP} ".*${ADMIN_ACI_NAME}.*write,compare,read,search.*${LDAP_ADMINDN}.*" \ - ${TMPDIR}/chk_adminwrite_aci 2>&1 > /dev/null - if [ $? -eq 0 ]; then - MSG="Admin ACI ${ADMIN_ACI_NAME} already exists for ${LDAP_BASEDN}." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " NOT SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` + for (( I=0; I < MAX ; I++ )); do + X=${SSD[I]} + [[ ${X%%:*} == ${ANS} ]] && FOUND=$I && break + done + if (( FOUND == -1 )); then + Log.warn "Invalid service name: '${ANS}' not present in list" + return fi - return 0 - fi + (( FOUND == 0 )) && unset SSD[0] && return - # If an ACI with ${ADMIN_ACI_NAME} and "(write)" and ${LDAP_ADMINDN} - # already exists, delete it first. - find_and_delete_ACI ".*${ADMIN_ACI_NAME}.*(write).*${LDAP_ADMINDN}.*" \ - ${TMPDIR}/chk_adminwrite_aci ${ADMIN_ACI_NAME} + # need to do a little bit more, since ksh allows to remove the 1st entry, + # only (i.e. like "shift 1" for positional params) + for (( I=FOUND; I > 0; I-- )); do + SSD[${I}]="${SSD[I-1]}" + done + unset SSD[0] +} - # Create the tmp file to add. - ( cat < ${TMPDIR}/admin_write +Man.addFunc modify_ssd '' '[+NAME?modify_ssd - Allow user to modify an SSD.] +[+DESCRIPTION?Ask the user for the ID of the SSD to modify, let the user modify it and finally save changes back to the \bSSD\b list.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} +[+SEE ALSO?\bget_ans()\b, \bread\b(1).] +' +function modify_ssd { + get_ans 'Enter the service name of the SSD to modify:' + [[ -z ${ANS} ]] && return - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/admin_write ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Allow ${LDAP_ADMINDN} read/write access to shadow data failed!" - cleanup - exit 1 - fi + typeset ID BASE + typeset -l SCOPE + integer I FOUND=-1 MAX=${#SSD[@]} + for (( I=0; I < MAX ; I++ )); do + X=${SSD[I]} + [[ ${X%%:*} == ${ANS} ]] && FOUND=$I && break + done + if (( FOUND == -1 )); then + Log.warn "Invalid service name: '${ANS}'" + return 0 + fi - ${RM} -f ${TMPDIR}/admin_write - # Display message that the administrator ACL is set. - MSG="Give ${LDAP_ADMINDN} read/write access to shadow data." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " ACI SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` - fi -} + # most users expect emacs edit behavior (don't have a proper profile) + set -o emacs + read -v X?'Current SSD: ' -# -# allow_host_read_write_shadow(): Give host principal read/write permission -# for shadow data. -# -allow_host_read_write_shadow() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In allow_host_read_write_shadow()" - - # Set ACI Name - HOST_ACI_NAME="LDAP_Naming_Services_host_shadow_write" - - # Search for ACI_NAME - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_hostwrite_aci 2>&1" - ${GREP} "${HOST_ACI_NAME}" ${TMPDIR}/chk_hostwrite_aci > /dev/null 2>&1 - if [ $? -eq 0 ]; then - MSG="Host ACI ${HOST_ACI_NAME} already exists for ${LDAP_BASEDN}." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " NOT ADDED: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` + # now verify + ID=${X%%:*} + BASE=${.sh.match#:} + SCOPE=${BASE#*$'?'} + BASE=${.sh.match%$'?'} + if [[ -z ${ID} || -z ${BASE} || -z ${SCOPE} ]] || \ + [[ ${SCOPE} != 'one' && ${SCOPE} != 'sub' ]] + then + Log.warn 'SSD is invalid. Change skipped' + return fi - return 0 - fi + SSD[${FOUND}]="${ID}:${BASE}?${SCOPE}" +} - # Create the tmp file to add. - ( cat < ${TMPDIR}/host_read_write - - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/host_read_write ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Allow Host Principal to write shadow data failed!" - cleanup - exit 1 - fi +Man.addFunc reset_ssd '' '[+NAME?reset_ssd - Blank out current list of SSDs.] +[+DESCRIPTION?Clear the \bSSD\b array.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} +' +function reset_ssd { + SSD=( ) + unset SSD[0] # required otherwise it would contain a single '\n'. Bug? +} - ${RM} -f ${TMPDIR}/host_read_write - MSG="Give host principal read/write permission for shadow." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " ACI SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` - fi +Man.addFunc display_ssd '' '[+NAME?display_ssd - Display current SSD list.] +[+DESCRIPTION?Display the current \bSSD\b list and return, when the user hit the enter key.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} +' +function display_ssd { + typeset X + print ' +Current Service Search Descriptors: +==================================' + for X in "${SSD[@]}" ; do + print " ${X}" + done + print '\nHit return to continue.' + read } -# -# Set up shadow update -# -setup_shadow_update() { - [ $DEBUG -eq 1 ] && ${ECHO} "In setup_shadow_update()" - - # get content of the profile - PROFILE_OUT=${TMPDIR}/prof_tmpfile - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" > $PROFILE_OUT 2>&1" - ${GREP} -i cn $PROFILE_OUT >/dev/null 2>&1 - if [ $? -ne 0 ]; then - [ $DEBUG -eq 1 ] && ${ECHO} "Profile ${LDAP_PROFILE_NAME} does not exist" - ${RM} ${PROFILE_OUT} - return - fi - - # Search to see if authenticationMethod has 'GSSAPI' and - # credentialLevel has 'self'. If so, ask to use the - # host principal for shadow update - if [ $GSSAPI_AUTH_MAY_BE_USED -eq 1 ]; then - if ${GREP} authenticationMethod $PROFILE_OUT | ${GREP} GSSAPI >/dev/null 2>&1 +Man.addFunc prompt_ssd '' '[+NAME?prompt_ssd - Get SSDs from user.] +[+DESCRIPTION?Ask the user whether to add/delete/modify/display one or more SSDs currently set and execute the corresponding action.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} +[+SEE ALSO?\bdisplay_msg()\b, \bget_ans()\b, \badd_ssd()\b, \bdelete_ssd()\b, \bmodify_ssd()\b, \bdisplay_ssd()\b, \breset_ssd()\b.] +' +function prompt_ssd { + if get_confirm 'Do you wish to setup Service Search Descriptors (y/n/h)?' \ + 'n' 'ssd_help' then - if ${GREP} credentialLevel $PROFILE_OUT | ${GREP} self >/dev/null 2>&1 - then - NEED_HOSTACL=1 - fi + return fi - ${RM} ${PROFILE_OUT} - [ $DEBUG -eq 1 ] && ${ECHO} "NEED_HOSTACL = $NEED_HOSTACL" - if [ $NEED_HOSTACL -eq 1 ]; then - MSG="Use host principal for shadow data update (y/n/h)?" - get_confirm "$MSG" "y" "use_host_principal_help" - if [ $? -eq 1 ]; then - delete_proxy_read_pw - allow_host_read_write_shadow - deny_non_host_shadow_access - ${ECHO} "" - ${ECHO} " Shadow update has been enabled." - else - ${ECHO} "" - ${ECHO} " Shadow update may not work." - fi - return - fi - fi + # Display menu for SSD choices + while : ; do + display_msg prompt_ssd_menu + get_ans 'Enter menu choice:' 'Quit' + case "${ANS}" in + [Qq] | Quit | quit) return ;; + [Aa] | add) add_ssd ;; + [Dd] | delete) delete_ssd ;; + [Mm] | modify) modify_ssd ;; + [Pp] | print | display) display_ssd ;; + [Xx] | reset | clear) reset_ssd ;; + [Hh] | Help | help) + display_msg 'ssd_menu_help ' + print ' Press return to continue.' + read + ;; + *) Log.warn "Invalid choice: '${ANS}' please re-enter from menu!" ;; + esac + done +} - MSG="Add the administrator identity (y/n/h)?" - get_confirm "$MSG" "y" "add_admin_cred_help" - if [ $? -eq 1 ]; then - get_adminDN - get_admin_pw - add_admin - delete_proxy_read_pw - allow_admin_read_write_shadow - deny_non_admin_shadow_access - ${ECHO} "" - ${ECHO} " Shadow update has been enabled." - return - fi - - ${ECHO} " No administrator identity specified, shadow update may not work." + ###################################################################### + # End Of FUNCTIONS FOR Service Search Descriptor's + ###################################################################### +Man.addFunc prompt_config_info '' '[+NAME?prompt_config_info - Ask user for missing config info.] +[+DESCRIPTION?Ask the user for the config info not yet available/read from an '"${PROG}"' config file.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} +[+RETURN VALUES]{ + [+0?on success (all info aquired).] + [+>= 66?a fatal error occured.] } +' +function prompt_config_info { + # Prompt for DS server name + get_ids_server + # Prompt for DS port number + get_ids_port -# -# prompt_config_info(): This function prompts the user for the config -# info that is not specified in the input file. -# -prompt_config_info() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In prompt_config_info()" + # Check DS version for compatibility + chk_ids_version || return 66 - # Prompt for iDS server name. - get_ids_server + # Check if the DS supports the VLV + chk_vlv_indexes || return 67 - # Prompt for iDS port number. - get_ids_port + # Get the Directory manager DN and passwd + get_dirmgr_dn + get_dirmgr_pw - # Check iDS version for compatibility. - chk_ids_version + get_confirm 'Do you want to modify the DS timelimit for processing searches (y/n/h)?' 'n' 'tlim_help' + INT[NEED_TIME]=$? + if (( INT[NEED_TIME] )); then + get_timelimit || return 70 + fi - # Check if the server supports the VLV. - chk_vlv_indexes + get_confirm 'Do you want to modify the DS sizelimit - max. entries to return (y/n/h)?' 'n' 'slim_help' + INT[NEED_SIZE]=$? + if (( INT[NEED_SIZE] )); then + get_sizelimit || return 71 + fi - # Get the Directory manager DN and passwd. - get_dirmgr_dn - get_dirmgr_pw - # - # LDAP CLIENT PROFILE SPECIFIC INFORMATION. - # (i.e. The fields that show up in the profile.) - # - get_domain "domain_help" + # LDAP CLIENT PROFILE SPECIFIC INFORMATION + # (i.e. the fields that show up in the profile) + get_domain + get_basedn || return 68 - get_basedn - + # Check/Get Kerberos infos gssapi_setup get_profile_name - if [ "$LDAP_ENABLE_SHADOW_UPDATE" = "TRUE" ];then - setup_shadow_update - cleanup - exit 0 - fi + if (( INT[LDAP_ENABLE_SHADOW_UPDATE] ));then + setup_shadow_update || return 69 + return 0 + fi - get_srv_list - get_pref_srv - get_search_scope - - # If cred is "anonymous", make auth == "none" - get_cred_level - if [ "$LDAP_CRED_LEVEL" != "anonymous" ]; then - get_auth - fi - + get_srv_list + get_pref_srv + get_search_scope get_followref - # Query user about timelimt. - get_confirm "Do you want to modify the server timelimit value (y/n/h)?" "n" "tlim_help" - NEED_TIME=$? - [ $NEED_TIME -eq 1 ] && get_timelimit + # Store passwords in crypt format? + get_want_crypt - # Query user about sizelimit. - get_confirm "Do you want to modify the server sizelimit value (y/n/h)?" "n" "slim_help" - NEED_SIZE=$? - [ $NEED_SIZE -eq 1 ] && get_sizelimit + # If cred is "anonymous", make auth == "none" + get_cred_level + [[ ${STR[LDAP_CRED_LEVEL]} != anonymous ]] && get_auth 'client' - # Does the user want to store passwords in crypt format? - get_want_crypt + get_confirm 'Do you want to setup any non-default Authentication Methods (y/n/h)?' 'n' 'srvauth_help' + if (( $? )); then + get_confirm 'Do you want to setup Authentication Methods for "pam_ldap" (y/n/h)?' 'n' 'pam_ldap_help' + INT[NEED_SRVAUTH_PAM]=$? + (( INT[NEED_SRVAUTH_PAM] )) && get_auth 'pam_ldap' - # Prompt for any Service Authentication Methods? - get_confirm "Do you want to setup a Service Authentication Methods (y/n/h)?" "n" "srvauth_help" - if [ $? -eq 1 ]; then - # Does the user want to set Service Authentication Method for pam_ldap? - get_confirm "Do you want to setup a Service Auth. Method for \"pam_ldap\" (y/n/h)?" "n" "pam_ldap_help" - NEED_SRVAUTH_PAM=$? - [ $NEED_SRVAUTH_PAM -eq 1 ] && get_srv_authMethod_pam + get_confirm 'Do you want to setup Authentication Methods for "keyserv" (y/n/h)?' 'n' 'keyserv_help' + INT[NEED_SRVAUTH_KEY]=$? + (( INT[NEED_SRVAUTH_KEY] )) && get_auth 'keyserv' - # Does the user want to set Service Authentication Method for keyserv? - get_confirm "Do you want to setup a Service Auth. Method for \"keyserv\" (y/n/h)?" "n" "keyserv_help" - NEED_SRVAUTH_KEY=$? - [ $NEED_SRVAUTH_KEY -eq 1 ] && get_srv_authMethod_key + get_confirm 'Do you want to setup Authentication Methods for "passwd-cmd (y/n/h)?' 'n' 'passwd-cmd_help' + INT[NEED_SRVAUTH_CMD]=$? + (( INT[NEED_SRVAUTH_CMD] )) && get_auth 'passwd-cmd' + fi - # Does the user want to set Service Authentication Method for passwd-cmd? - get_confirm "Do you want to setup a Service Auth. Method for \"passwd-cmd\" (y/n/h)?" "n" "passwd-cmd_help" - NEED_SRVAUTH_CMD=$? - [ $NEED_SRVAUTH_CMD -eq 1 ] && get_srv_authMethod_cmd - fi - + # Get client timeouts + get_srch_time + get_prof_ttl + get_bind_limit - # Get Timeouts - get_srch_time - get_prof_ttl - get_bind_limit + get_want_shadow_update - # Ask whether to enable shadow update - get_want_shadow_update + reset_ssd + prompt_ssd - # Reset the sdd_file and prompt user for SSD. Will use menus - # to build an SSD File. - reset_ssd_file - prompt_ssd + show_vars - # Display FULL debugging info. - disp_full_debug - - # Extra blank line to separate prompt lines from steps. - ${ECHO} " " + Log.printMarker + return 0 } +###################################################################### +# End Of FUNCTIONS FOR prompt_config_info() +###################################################################### ###################################################################### # FUNCTIONS FOR display_summary() START HERE. ###################################################################### - - -# -# get_proxyagent(): Get the proxyagent DN. -# -get_proxyagent() -{ - LDAP_PROXYAGENT="cn=proxyagent,ou=profile,${LDAP_BASEDN}" # default - get_ans "Enter DN for proxy agent:" "$LDAP_PROXYAGENT" - LDAP_PROXYAGENT=$ANS +Man.addFunc get_proxyagent '' '[+NAME?get_proxyagent - Ask user for the proxy agent DN and password.] +[+DESCRIPTION?Ask the user for the proxy agent DN and password to use and store it into \bSTR[LDAP_PROXYAGENT]]\b, \bSTR[LDAP_PROXYAGENT_CRED]]\b.] +[+SEE ALSO?\bget_ans()\b.] +' +function get_proxyagent { + STR[LDAP_PROXYAGENT]="cn=proxyagent,ou=profile,${STR[LDAP_BASEDN]}" + get_ans 'Enter DN for proxy agent:' "${STR[LDAP_PROXYAGENT]}" + STR[LDAP_PROXYAGENT]="${ANS}" + get_passwd 'Enter passwd for proxyagent:' + STR[LDAP_PROXYAGENT_CRED]="${ANS}" } +Man.addFunc display_summary '' '[+NAME?display_summary - Display a summary of values entered and let the user modify values at will.] +[+DESCRIPTION?Display a summary of all configuration relevant info and let the user re-enter data if needed.] +' +function display_summary { + # Create lookup table for function names. Needs to be in sync with + # helpTag 'summary_menu' in display_msg()! + typeset -a FN=( 'dummy' ) # dummy for commit and quit + FN+=( 'get_domain' 'get_basedn' 'get_profile_name' ) + FN+=( 'get_srv_list' 'get_pref_srv' 'get_search_scope' 'get_cred_level' ) + FN+=( 'get_auth client' 'get_followref' ) + FN+=( 'get_timelimit' 'get_sizelimit' 'get_want_crypt' ) + FN+=( 'get_auth pam_ldap' 'get_auth keyserv' 'get_auth passwd-cmd' ) + FN+=( 'get_srch_time' 'get_prof_ttl' 'get_bind_limit' ) + FN+=( 'get_want_shadow_update' ) + FN+=( 'prompt_ssd' ) -# -# get_proxy_pw(): Get the proxyagent passwd. -# -get_proxy_pw() -{ - get_passwd "Enter passwd for proxyagent:" - LDAP_PROXYAGENT_CRED=$ANS -} + # Since menu prompt string is long, set here + typeset PROMPT='Enter config value to change: (1-20 0=commit changes)' + integer RES MAX=${#FN[@]} + (( MAX-- )) -# -# display_summary(): Display a summary of values entered and let the -# user modify values at will. -# -display_summary() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In display_summary()" + while : ; do + # Display menu and get value in range + get_menu_choice "${PROMPT}" '0' ${MAX} '0' '' 'summary_menu' + RES=$? + + # Make sure where not exiting + (( RES == 0 )) && break # quit selected - # Create lookup table for function names. First entry is dummy for - # shift. - TBL1="dummy" - TBL2="get_domain get_basedn get_profile_name" - TBL3="get_srv_list get_pref_srv get_search_scope get_cred_level" - TBL4="get_auth get_followref" - TBL5="get_timelimit get_sizelimit get_want_crypt" - TBL6="get_srv_authMethod_pam get_srv_authMethod_key get_srv_authMethod_cmd" - TBL7="get_srch_time get_prof_ttl get_bind_limit" - TBL8="get_want_shadow_update" - TBL9="prompt_ssd" - FUNC_TBL="$TBL1 $TBL2 $TBL3 $TBL4 $TBL5 $TBL6 $TBL7 $TBL8 $TBL9" + # Call appropriate function from function table + ${FN[RES]} + done - # Since menu prompt string is long, set here. - _MENU_PROMPT="Enter config value to change: (1-20 0=commit changes)" + # If credlevel is still proxy see if user wants a change? + if [[ ${STR[LDAP_CRED_LEVEL]:0:5} == 'proxy' ]]; then + if [[ ${STR[LDAP_AUTHMETHOD]} != none ]]; then + INT[NEED_PROXY]=1 + get_proxyagent + else + Log.warn 'Since Authentication method is "none",' \ + 'credential level will be set to "anonymous"' + STR[LDAP_CRED_LEVEL]='anonymous' + fi + fi - # Infinite loop. Test for 0, and break in loop. - while : - do - # Display menu and get value in range. - display_msg summary_menu - get_menu_choice "${_MENU_PROMPT}" "0" "20" "0" - _CH=$MN_CH - - # Make sure where not exiting. - if [ $_CH -eq 0 ]; then - break # Break out of loop if 0 selected. + # If shadow update is enabled, set up administrator credential + if (( INT[LDAP_ENABLE_SHADOW_UPDATE] )); then + INT[NEED_ADMIN]=1 + if [[ ${STR[LDAP_CRED_LEVEL]} == 'self' && \ + ${STR[LDAP_AUTHMETHOD]} == 'sasl/GSSAPI' ]]; + then + INT[NEED_HOSTACL]=1 + INT[NEED_ADMIN]=0 + fi + if (( INT[NEED_ADMIN] )); then + get_adminDN + get_admin_pw + fi fi - # Call appropriate function from function table. - set $FUNC_TBL - shift $_CH - $1 # Call the appropriate function. - done + show_vars - # If cred level is still see if user wants a change? - if ${ECHO} "$LDAP_CRED_LEVEL" | ${GREP} "proxy" > /dev/null 2>&1 - then - if [ "$LDAP_AUTHMETHOD" != "none" ]; then - NEED_PROXY=1 # I assume integer test is faster? - get_proxyagent - get_proxy_pw - else - ${ECHO} "WARNING: Since Authentication method is 'none'." - ${ECHO} " Credential level will be set to 'anonymous'." - LDAP_CRED_LEVEL="anonymous" + get_confirm_nodef ' +WARNING: About to start committing changes. (y=continue, n=EXIT)' + if (( ! $? )); then + Log.info 'Terminating setup without making changes at users request' + return 1 fi - fi - # If shadow update is enabled, set up administrator credential - if [ "$LDAP_ENABLE_SHADOW_UPDATE" = "TRUE" ]; then - NEED_ADMIN=1 - if ${ECHO} "$LDAP_CRED_LEVEL" | ${GREP} "self" > /dev/null 2>&1; then - if ${ECHO} "$LDAP_AUTHMETHOD" | ${GREP} "GSSAPI" > /dev/null 2>&1; then - NEED_HOSTACL=1 - NEED_ADMIN=0 - fi + Log.printMarker + return 0 +} +###################################################################### +# End Of FUNCTIONS FOR display_summary() +###################################################################### + +Man.addFunc modify_cn '' '[+NAME?modify_cn - modify objectclass "ipNetwork" to RFC 2307bis.] +[+DESCRIPTION?Change the cn from MUST to MAY in ipNetwork objectclass.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occurred.] +} +[+SEE ALSO?\bldapmodify\b(1).] +' +function modify_cn { + getDSobjectclasses || return 66 + typeset NEWDEF="'ipNetwork' SUP top STRUCTURAL DESC 'Abstraction of a network. The distinguished value of the cn attribute denotes the canonical name of the network' MUST ipNetworkNumber MAY "'( cn $ ipNetmaskNumber $ l $ description $ manager )'" X-ORIGIN 'draft-howard-rfc2307bis'" + + # bis-delta: MUST ( -cn ) MAY ( +cn ) + typeset DEF=${OID2ODEF['1.3.6.1.1.1.2.7']} + if [[ -n ${DEF} ]]; then + DEF=${DEF##*MUST*([[:space:]])} + if [[ ${DEF:0:9} == 'ipNetwork' ]]; then + # assume no need to fix, old definition would start with a '(' + showProgress 'Schema definition of ipNetwork ok (RFC2307bis).' + return 0 + fi fi - [ $DEBUG -eq 1 ] && ${ECHO} "NEED_HOSTACL = $NEED_HOSTACL" - [ $DEBUG -eq 1 ] && ${ECHO} "NEED_ADMIN = $NEED_ADMIN" - if [ $NEED_ADMIN -eq 1 ]; then - get_adminDN - get_admin_pw - fi - fi - # Display FULL debugging info. - disp_full_debug + nextFile modify $0 + print ' +dn: cn=schema +changetype: modify +add: objectclasses +objectclasses: ( 1.3.6.1.1.1.2.7 NAME '"${NEWDEF}"') +' > ${TMP[FILE]} - # Final confirmation message. (ARE YOU SURE!) - ${ECHO} " " - get_confirm_nodef "WARNING: About to start committing changes. (y=continue, n=EXIT)" - if [ $? -eq 0 ]; then - ${ECHO} "Terminating setup without making changes at users request." - cleanup - exit 1 - fi + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Schema update of ipNetwork to RFC2307bis failed' + return 67 + fi + showProgress 'Schema update of ipNetwork to RFC2307bis done.' + OID2ODEF['1.3.6.1.1.1.2.7']="${NEWDEF}" + return 0 +} - # Print newline - ${ECHO} " " +Man.addFunc modify_timelimit '' '[+NAME?modify_timelimit - Set the DS timelimit.] +[+DESCRIPTION?Set the DS timelimit to \bINT[DS_TIMELIMIT]]\b.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occurred.] } +[+SEE ALSO?\bldapmodify\b(1).] +' +function modify_timelimit { + typeset NAME='nsslapd-timelimit' + (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-time-limit' + nextFile modify $0 + print ' +dn: cn=config +changetype: modify +replace: '"${NAME}"' +'"${NAME}"': '"${INT[DS_TIMELIMIT]}"' s +' > ${TMP[FILE]} -# -# create_config_file(): Write config data to config file specified. -# -create_config_file() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In create_config_file()" + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Update of ${NAME} failed' + return 66 + fi - # If output file exists, delete it. - [ -f $OUTPUT_FILE ] && rm $OUTPUT_FILE + showProgress "Changed ${NAME} to ${INT[DS_TIMELIMIT]} in 'cn=config'." + return 0 +} - # Create output file. - cat > $OUTPUT_FILE <= 66?a fatal error occurred.] +} +[+SEE ALSO?\bldapmodify\b(1).] +' +function modify_sizelimit { + typeset NAME='nsslapd-sizelimit' + (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-size-limit' -# Internal program variables that need to be set. -NEED_PROXY=$NEED_PROXY -NEED_TIME=$NEED_TIME -NEED_SIZE=$NEED_SIZE -NEED_CRYPT=$NEED_CRYPT -NEED_ADMIN=$NEED_ADMIN -NEED_HOSTACL=$NEED_HOSTACL -EXISTING_PROFILE=$EXISTING_PROFILE + nextFile modify $0 + print ' +dn: cn=config +changetype: modify +replace: '"${NAME}"' +'"${NAME}"': '"${INT[DS_SIZELIMIT]}"' +' > ${TMP[FILE]} -# LDAP PROFILE related defaults -LDAP_PROFILE_NAME="$LDAP_PROFILE_NAME" -DEL_OLD_PROFILE=1 -LDAP_BASEDN="$LDAP_BASEDN" -LDAP_SERVER_LIST="$LDAP_SERVER_LIST" -LDAP_AUTHMETHOD="$LDAP_AUTHMETHOD" -LDAP_FOLLOWREF=$LDAP_FOLLOWREF -LDAP_SEARCH_SCOPE="$LDAP_SEARCH_SCOPE" -NEED_SRVAUTH_PAM=$NEED_SRVAUTH_PAM -NEED_SRVAUTH_KEY=$NEED_SRVAUTH_KEY -NEED_SRVAUTH_CMD=$NEED_SRVAUTH_CMD -LDAP_SRV_AUTHMETHOD_PAM="$LDAP_SRV_AUTHMETHOD_PAM" -LDAP_SRV_AUTHMETHOD_KEY="$LDAP_SRV_AUTHMETHOD_KEY" -LDAP_SRV_AUTHMETHOD_CMD="$LDAP_SRV_AUTHMETHOD_CMD" -LDAP_SEARCH_TIME_LIMIT=$LDAP_SEARCH_TIME_LIMIT -LDAP_PREF_SRVLIST="$LDAP_PREF_SRVLIST" -LDAP_PROFILE_TTL=$LDAP_PROFILE_TTL -LDAP_CRED_LEVEL="$LDAP_CRED_LEVEL" -LDAP_BIND_LIMIT=$LDAP_BIND_LIMIT + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Update of ${NAME} failed" + return 66 + fi -# Proxy Agent -LDAP_PROXYAGENT="$LDAP_PROXYAGENT" -LDAP_PROXYAGENT_CRED=$LDAP_PROXYAGENT_CRED + showProgress "Changed ${NAME} to ${INT[DS_SIZELIMIT]} in 'cn=config'." + return 0 +} -# enableShadowUpdate flag and Administrator credential -LDAP_ENABLE_SHADOW_UPDATE=$LDAP_ENABLE_SHADOW_UPDATE -LDAP_ADMINDN="$LDAP_ADMINDN" -LDAP_ADMIN_CRED=$LDAP_ADMIN_CRED +Man.addFunc modify_pwd_crypt '' '[+NAME?modify_pwd_crypt - modify the passwd storage scheme to use CRYPT.] +[+DESCRIPTION?Set the default password policy of the DS to CRYPT.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occurred.] +} +[+SEE ALSO?\bldapmodify\b(1).] +' +function modify_pwd_crypt { + # DS 5.2 moved passwordchangescheme off to a new data structure + typeset -a INFO=( ${TMP[DS_INFO]} ) + typeset DN='cn=config' TARGET='passwordStorageScheme' VALUE='CRYPT' MSG='' + typeset FMT='dn: %s\nchangetype: modify\nreplace: %s\n%s: %s\n\n' + nextFile modify $0 -# Export all the variables (just in case) -export IDS_HOME IDS_PORT LDAP_ROOTDN LDAP_ROOTPWD LDAP_SERVER_LIST LDAP_BASEDN -export LDAP_DOMAIN LDAP_SUFFIX LDAP_PROXYAGENT LDAP_PROXYAGENT_CRED -export NEED_PROXY -export LDAP_ENABLE_SHADOW_UPDATE LDAP_ADMINDN LDAP_ADMIN_CRED -export NEED_ADMIN NEED_HOSTACL EXISTING_PROFILE -export LDAP_PROFILE_NAME LDAP_BASEDN LDAP_SERVER_LIST -export LDAP_AUTHMETHOD LDAP_FOLLOWREF LDAP_SEARCH_SCOPE LDAP_SEARCH_TIME_LIMIT -export LDAP_PREF_SRVLIST LDAP_PROFILE_TTL LDAP_CRED_LEVEL LDAP_BIND_LIMIT -export NEED_SRVAUTH_PAM NEED_SRVAUTH_KEY NEED_SRVAUTH_CMD -export LDAP_SRV_AUTHMETHOD_PAM LDAP_SRV_AUTHMETHOD_KEY LDAP_SRV_AUTHMETHOD_CMD -export LDAP_SERV_SRCH_DES SSD_FILE GSSAPI_ENABLE LDAP_KRB_REALM + if (( TMPF[IS_OPENDJ] )); then + DN='cn=Default Password Policy,cn=Password Policies,cn=config' + TARGET='ds-cfg-default-password-storage-scheme' + VALUE='cn=CRYPT,cn=Password Storage Schemes,cn=config' + else + # DSEE + integer VER=$(( ${INFO[1]} * 1000 + ${INFO[2]} )) + (( VER >= 5002 )) && DN='cn=Password Policy,cn=config' + fi + printf "${FMT}" "${DN}" "${TARGET}" "${TARGET}" "${VALUE}" >${TMP[FILE]} + MSG="Changed '${TARGET}' to 'CRYPT'" -# Service Search Descriptors start here if present: -EOF - # Add service search descriptors. - ssd_2_config "${OUTPUT_FILE}" + if (( TMPF[IS_OPENDJ] )); then + if (( INT[NEED_CRYPT_IMPORT] )); then + TARGET='ds-cfg-allow-pre-encoded-passwords' + printf "${FMT}" "${DN}" "${TARGET}" "${TARGET}" 'true'>>${TMP[FILE]} + MSG+="and '${TARGET}' to 'true'" + fi + DN='cn=Password Policy Import,cn=Plugins,cn=config' + TARGET='ds-cfg-default-user-password-storage-scheme' + printf "${FMT}" "${DN}" "${TARGET}" "${TARGET}" "${VALUE}">>${TMP[FILE]} + fi - # Add LDAP suffix preferences - print_suffix_config >> "${OUTPUT_FILE}" + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Update of passwordStorageScheme failed' + return 66 + fi - # Add the end of FILE tag. - ${ECHO} "" >> ${OUTPUT_FILE} - ${ECHO} "# End of $OUTPUT_FILE" >> ${OUTPUT_FILE} + showProgress "${MSG}." + return 0 } - -# -# chk_vlv_indexes(): Do ldapsearch to see if server supports VLV. -# -chk_vlv_indexes() -{ - # Do ldapsearch to see if server supports VLV. - ${LDAPSEARCH} ${SERVER_ARGS} -b "" -s base "objectclass=*" > ${TMPDIR}/checkVLV 2>&1 - eval "${GREP} 2.16.840.1.113730.3.4.9 ${TMPDIR}/checkVLV ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} "ERROR: VLV is not supported on LDAP server!" - cleanup - exit 1 - fi - [ $DEBUG -eq 1 ] && ${ECHO} " VLV controls found on LDAP server." +Man.addFunc update_schema_attr '' '[+NAME?update_schema_attr - Update DS schema to support Naming Services.] +[+DESCRIPTION?Update the schema of the DS with the attribute types required for the Solaris Naming Services. It just checks, whether the OID is defined in the server schema. If already defined, it is left as is, otherwise added.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occurred.] } +[+SEE ALSO?\bldapmodify\b(1), \bgetDSattributes()\b.] +' +function update_schema_attr { -# -# get_backend(): this function gets the relevant backend -# (database) for LDAP_BASED. -# Description: set IDS_DATABASE; exit on failure. -# Prerequisite: LDAP_BASEDN and LDAP_SUFFIX are -# valid. -# -# backend is retrieved from suffixes and subsuffixes -# defined under "cn=mapping tree,cn=config". The -# nsslapd-state attribute of these suffixes entries -# is filled with either Backend, Disabled or referrals -# related values. We only want those that have a true -# backend database to select the relevant backend. -# -get_backend() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_backend()" + # incorporate former 'keep_backward_compatibility()' + getDSattributes || return 66 + typeset MEMBERGID_OID='1.3.6.1.4.1.42.2.27.5.1.30' + typeset DEF=${ANAME2OID['membergid-oid']} + [[ -n ${DEF} ]] && MEMBERGID_OID='memberGid-oid' + typeset RFC822MAILMEMBER_OID='1.3.6.1.4.1.42.2.27.2.1.15' + DEF=${ANAME2OID['rfc822mailmember-oid']} + [[ -n ${DEF} ]] && RFC822MAILMEMBER_OID='rfc822mailMember-oid' - cur_suffix=${LDAP_BASEDN} - prev_suffix= - IDS_DATABASE= - while [ "${cur_suffix}" != "${prev_suffix}" ] - do - [ $DEBUG -eq 1 ] && ${ECHO} "testing LDAP suffix: ${cur_suffix}" - eval "${LDAPSEARCH} ${LDAP_ARGS} " \ - "-b \"cn=\\\"${cur_suffix}\\\",cn=mapping tree,cn=config\" " \ - "-s base nsslapd-state=Backend nsslapd-backend 2>&1 " \ - "| ${GREP} 'nsslapd-backend=' " \ - "> ${TMPDIR}/ids_database_name 2>&1" - NUM_DBS=`wc -l ${TMPDIR}/ids_database_name | awk '{print $1}'` - case ${NUM_DBS} in - 0) # not a suffix, or suffix not activated; try next - prev_suffix=${cur_suffix} - cur_suffix=`${ECHO} ${cur_suffix} | cut -f2- -d','` - ;; - 1) # suffix found; get database name - IDS_DATABASE=`cat ${TMPDIR}/ids_database_name | cut -d= -f2` - ;; - *) # can not handle more than one database per suffix - ${ECHO} "ERROR: More than one database is configured " - ${ECHO} " for $LDAP_SUFFIX!" - ${ECHO} " $PROG can not configure suffixes where " - ${ECHO} " more than one database is used for one suffix." - cleanup - exit 1 - ;; - esac - if [ -n "${IDS_DATABASE}" ]; then - break - fi - done + # EE and OD definitions seem to be compatible wrt. to Solaris. However, the + # OD defs usually specifies explicitly the server behavior (what happens, if + # EQUALITY|SUBSTR is not specified => caseIgnore[Substring]Match)) and + # sometimes a different kind of String to use (see RFC 4517): + # IA5 String: ASCII character in the range of 0..127 + # Octet String: byte collection (0..255), usually not human readable, + # bytewise comparision + # Directory String: an UTF-8 string + # DN: basically an UTF-8 string, but with some constraints wrt. backslash + # escaping '\0', '"', '+', ',', ';', '<', '>', and '\' + # Boolean String: TRUE|FALSE + typeset -A EE=( ) + typeset -A OD=( ) + EE['1.3.6.1.1.1.1.28']="'nisPublickey' DESC 'NIS public key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.1.1.1.28']="'nisPublicKey' DESC 'NIS public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE X-ORIGIN 'draft-howard-rfc2307bis'" # Directory vs. Octet String - if [ -z "${IDS_DATABASE}" ]; then - # should not happen, since LDAP_BASEDN is supposed to be valid - ${ECHO} "Could not find a valid backend for ${LDAP_BASEDN}." - ${ECHO} "Exiting." - cleanup - exit 1 - fi + EE['1.3.6.1.1.1.1.29']="'nisSecretkey' DESC 'NIS secret key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.1.1.1.29']="'nisSecretKey' DESC 'NIS secret key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE X-ORIGIN 'draft-howard-rfc2307bis'" # Directory vs. Octet String - [ $DEBUG -eq 1 ] && ${ECHO} "IDS_DATABASE: ${IDS_DATABASE}" -} + EE['1.3.6.1.1.1.1.30']="'nisDomain' DESC 'NIS domain' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.1.1.1.30']="'nisDomain' DESC 'NIS domain' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'draft-howard-rfc2307bis'" # Directory vs. IA5 String -# -# validate_suffix(): This function validates ${LDAP_SUFFIX} -# THIS FUNCTION IS FOR THE LOAD CONFIG FILE OPTION. -# -validate_suffix() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In validate_suffix()" + EE['1.3.6.1.1.1.1.31']="'automountMapName' DESC 'automount Map Name' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.1.1.1.31']="'automountMapName' DESC 'automount Map Name' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'draft-howard-rfc2307bis'" - # Check LDAP_SUFFIX is not null - if [ -z "${LDAP_SUFFIX}" ]; then - ${ECHO} "Invalid suffix (null suffix)" - cleanup - exit 1 - fi + EE['1.3.6.1.1.1.1.32']="'automountKey' DESC 'automount Key Value' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.1.1.1.32']="'automountKey' DESC 'Automount Key value' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'draft-howard-rfc2307bis'" - # Check LDAP_SUFFIX and LDAP_BASEDN are consistent - # Convert to lower case for basename. - format_string "${LDAP_BASEDN}" - LOWER_BASEDN="${FMT_STR}" - format_string "${LDAP_SUFFIX}" - LOWER_SUFFIX="${FMT_STR}" + EE['1.3.6.1.1.1.1.33']="'automountInformation' DESC 'automount information' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.1.1.1.33']="'automountInformation' DESC 'Automount information' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'draft-howard-rfc2307bis'" - [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_BASEDN: ${LOWER_BASEDN}" - [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_SUFFIX: ${LOWER_SUFFIX}" + EE['1.3.6.1.4.1.42.2.27.1.1.12']="'nisNetIdUser' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" + OD['1.3.6.1.4.1.42.2.27.1.1.12']="'nisNetIdUser' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" - if [ "${LOWER_BASEDN}" != "${LOWER_SUFFIX}" ]; then - sub_basedn=`basename "${LOWER_BASEDN}" "${LOWER_SUFFIX}"` - if [ "$sub_basedn" = "${LOWER_BASEDN}" ]; then - ${ECHO} "Invalid suffix ${LOWER_SUFFIX}" - ${ECHO} "for Base DN ${LOWER_BASEDN}" - cleanup - exit 1 - fi - fi + EE['1.3.6.1.4.1.42.2.27.1.1.13']="'nisNetIdGroup' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" + OD['1.3.6.1.4.1.42.2.27.1.1.13']="'nisNetIdGroup' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" - # Check LDAP_SUFFIX does exist - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_SUFFIX}\" -s base \"objectclass=*\" > ${TMPDIR}/checkSuffix 2>&1" && return 0 + EE['1.3.6.1.4.1.42.2.27.1.1.14']="'nisNetIdHost' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" + OD['1.3.6.1.4.1.42.2.27.1.1.14']="'nisNetIdHost' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" - # Well, suffix does not exist, try to prepare create it ... - NEED_CREATE_SUFFIX=1 - prep_create_sfx_entry || - { - cleanup - exit 1 - } - [ -n "${NEED_CREATE_BACKEND}" ] && - { - # try to use id attr value of the suffix as a database name - IDS_DATABASE=${_VAL} - prep_create_sfx_backend - case $? in - 1) # cann't use the name we want, so we can either exit or use - # some another available name - doing the last ... - IDS_DATABASE=${IDS_DATABASE_AVAIL} - ;; - 2) # unable to determine database name - cleanup - exit 1 - ;; - esac - } + EE[${RFC822MAILMEMBER_OID}]="'rfc822mailMember' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" + OD[${RFC822MAILMEMBER_OID}]="'rfc822mailMember' DESC 'rfc822 mail addresss of group member' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" - [ $DEBUG -eq 1 ] && ${ECHO} "Suffix $LDAP_SUFFIX, Database $IDS_DATABASE" -} + EE['2.16.840.1.113730.3.1.30']="'mgrpRFC822MailMember' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['2.16.840.1.113730.3.1.30']="'mgrpRFC822MailMember' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Solaris Specific'" -# -# validate_info(): This function validates the basic info collected -# So that some problems are caught right away. -# THIS FUNCTION IS FOR THE LOAD CONFIG FILE OPTION. -# -validate_info() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In validate_info()" + EE['1.3.6.1.4.1.42.2.27.5.1.15']="'SolarisLDAPServers' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.42.2.27.5.1.15']="'SolarisLDAPServers' DESC 'LDAP Server address eg. 76.234.3.1:389' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - # Set SERVER_ARGS, AUTH_ARGS, and LDAP_ARGS for the config file. - SERVER_ARGS="-h ${IDS_SERVER} -p ${IDS_PORT}" - AUTH_ARGS="-D \"${LDAP_ROOTDN}\" -j ${LDAP_ROOTPWF}" - LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}" - export SERVER_ARGS + EE['1.3.6.1.4.1.42.2.27.5.1.16']="'SolarisSearchBaseDN' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.16']="'SolarisSearchBaseDN' DESC 'Search Base Distinguished Name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # DN vs. Directory String - # Check the Root DN and Root DN passwd. - # Use eval instead of $EVAL because not part of setup. (validate) - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" > ${TMPDIR}/checkDN 2>&1" - if [ $? -ne 0 ]; then - eval "${GREP} credential ${TMPDIR}/checkDN ${VERB}" - if [ $? -eq 0 ]; then - ${ECHO} "ERROR: Root DN passwd is invalid." - else - ${ECHO} "ERROR2: Invalid Root DN <${LDAP_ROOTDN}>." - fi - cleanup - exit 1 - fi - [ $DEBUG -eq 1 ] && ${ECHO} " RootDN ... OK" - [ $DEBUG -eq 1 ] && ${ECHO} " RootDN passwd ... OK" + EE['1.3.6.1.4.1.42.2.27.5.1.17']="'SolarisCacheTTL' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.17']="'SolarisCacheTTL' DESC 'TTL value for the Domain information eg. 1w, 2d, 3h, 10m, or 5s' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - # Check if the server supports the VLV. - chk_vlv_indexes - [ $DEBUG -eq 1 ] && ${ECHO} " VLV indexes ... OK" + EE['1.3.6.1.4.1.42.2.27.5.1.18']="'SolarisBindDN' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.18']="'SolarisBindDN' DESC 'DN to be used to bind to the directory as proxy' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # DN vs. Directory String - # Check LDAP suffix - validate_suffix - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP suffix ... OK" -} + EE['1.3.6.1.4.1.42.2.27.5.1.19']="'SolarisBindPassword' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.19']="'SolarisBindPassword' DESC 'Password for bindDN to authenticate to the directory' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # IA5 vs. Octet String -# -# format_string(): take a string as argument and set FMT_STR -# to be the same string formatted as follow: -# - only lower case characters -# - no unnecessary spaces around , and = -# -format_string() -{ - FMT_STR=`${ECHO} "$1" | tr '[A-Z]' '[a-z]' | - sed -e 's/[ ]*,[ ]*/,/g' -e 's/[ ]*=[ ]*/=/g'` -} + EE['1.3.6.1.4.1.42.2.27.5.1.20']="'SolarisAuthMethod' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.42.2.27.5.1.20']="'SolarisAuthMethod' DESC 'Authentication method to be used eg. \"NS_LDAP_AUTH_NONE\", \"NS_LDAP_AUTH_SIMPLE\" or \"NS_LDAP_AUTH_SASL_CRAM_MD5\"' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String -# -# prepare for the suffix entry creation -# -# input : LDAP_BASEDN, LDAP_SUFFIX - base dn and suffix; -# in/out : LDAP_SUFFIX_OBJ, LDAP_SUFFIX_ACI - initially may come from config. -# output : NEED_CREATE_BACKEND - backend for this suffix needs to be created; -# _RDN, _ATT, _VAL - suffix's RDN, id attribute name and its value. -# return : 0 - success, otherwise error. -# -prep_create_sfx_entry() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In prep_create_sfx_entry()" + EE['1.3.6.1.4.1.42.2.27.5.1.21']="'SolarisTransportSecurity' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.42.2.27.5.1.21']="'SolarisTransportSecurity' DESC 'Transport Level Security method to be used eg. \"NS_LDAP_SEC_NONE\" or \"NS_LDAP_SEC_SASL_TLS\"' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - # check whether suffix corresponds to base dn - format_string "${LDAP_BASEDN}" - ${ECHO} ",${FMT_STR}" | ${GREP} ",${LDAP_SUFFIX}$" >/dev/null 2>&1 || - { - display_msg sfx_not_suitable - return 1 - } + EE['1.3.6.1.4.1.42.2.27.5.1.22']="'SolarisCertificatePath' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.22']="'SolarisCertificatePath' DESC 'Path to certificate file/device' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" - # parse LDAP_SUFFIX - _RDN=`${ECHO} "${LDAP_SUFFIX}" | cut -d, -f1` - _ATT=`${ECHO} "${_RDN}" | cut -d= -f1` - _VAL=`${ECHO} "${_RDN}" | cut -d= -f2-` + EE['1.3.6.1.4.1.42.2.27.5.1.23']="'SolarisCertificatePassword' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.23']="'SolarisCertificatePassword' DESC 'Password or PIN that grants access to certificate.' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # IA5 vs. Octet String - # find out an objectclass for suffix entry if it is not defined yet - [ -z "${LDAP_SUFFIX_OBJ}" ] && - { - get_objectclass ${_ATT} - [ -z "${_ATTR_NAME}" ] && - { - display_msg obj_not_found - return 1 - } - LDAP_SUFFIX_OBJ=${_ATTR_NAME} - } - [ $DEBUG -eq 1 ] && ${ECHO} "Suffix entry object is ${LDAP_SUFFIX_OBJ}" + EE['1.3.6.1.4.1.42.2.27.5.1.24']="'SolarisDataSearchDN' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.42.2.27.5.1.24']="'SolarisDataSearchDN' DESC 'Search DN for data lookup in \":(DN0),(DN1),...\" format' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Solaris Specific'" - # find out an aci for suffix entry if it is not defined yet - [ -z "${LDAP_SUFFIX_ACI}" ] && - { - # set Directory Server default aci - LDAP_SUFFIX_ACI=`cat </dev/null`" ] && - { - display_msg sfx_config_incons - return 1 - } + EE['1.3.6.1.4.1.42.2.27.5.1.28']="'SolarisPreferredServerOnly' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.28']="'SolarisPreferredServerOnly' DESC 'Boolean flag for use of preferredServer or not' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. Boolean String - # ok, no suffix mapping, no ldbm database - [ $DEBUG -eq 1 ] && ${ECHO} "DEBUG: backend needs to be created ..." - NEED_CREATE_BACKEND=1 - return 0 -} + EE['1.3.6.1.4.1.42.2.27.5.1.29']="'SolarisSearchReferral' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.29']="'SolarisSearchReferral' DESC 'referral chasing option eg. \"NS_LDAP_NOREF\" or \"NS_LDAP_FOLLOWREF\"' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String -# -# prepare for the suffix backend creation -# -# input : IDS_DATABASE - requested ldbm db name (must be not null) -# in/out : IDS_DATABASE_AVAIL - available ldbm db name -# return : 0 - ldbm db name ok -# 1 - IDS_DATABASE exists, -# so IDS_DATABASE_AVAIL contains available name -# 2 - unable to find any available name -# -prep_create_sfx_backend() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In prep_create_sfx_backend()" + EE['1.3.6.1.4.1.42.2.27.5.1.4']="'SolarisAttrKeyValue' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.4']="'SolarisAttrKeyValue' DESC 'Semi-colon separated key=value pairs of attributes' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - # check if requested name available - [ "${IDS_DATABASE}" = "${IDS_DATABASE_AVAIL}" ] && return 0 + EE['1.3.6.1.4.1.42.2.27.5.1.5']="'SolarisAuditAlways' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.5']="'SolarisAuditAlways' DESC 'Always audited attributes per-user' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - # get the list of database names start with a requested name - _LDBM_DBS=`${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \ - -b 'cn=ldbm database,cn=plugins,cn=config' \ - -s one 'cn=${IDS_DATABASE}*' cn"` 2>/dev/null + EE['1.3.6.1.4.1.42.2.27.5.1.6']="'SolarisAuditNever' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.6']="'SolarisAuditNever' DESC 'Never audited attributes per-user' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - # find available db name based on a requested name - _i=""; _i_MAX=10 - while [ ${_i:-0} -lt ${_i_MAX} ] - do - _name="${IDS_DATABASE}${_i}" - ${ECHO} "${_LDBM_DBS}" | ${GREP} -i "^cn=${_name}$" >/dev/null 2>&1 || - { - IDS_DATABASE_AVAIL="${_name}" - break - } - _i=`expr ${_i:-0} + 1` - done + EE['1.3.6.1.4.1.42.2.27.5.1.7']="'SolarisAttrShortDesc' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.7']="'SolarisAttrShortDesc' DESC 'Short description about an entry, used by GUIs' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - [ "${IDS_DATABASE}" = "${IDS_DATABASE_AVAIL}" ] && return 0 + EE['1.3.6.1.4.1.42.2.27.5.1.8']="'SolarisAttrLongDesc' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.8']="'SolarisAttrLongDesc' DESC 'Detail description about an entry' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - [ -n "${IDS_DATABASE_AVAIL}" ] && - { - display_msg ldbm_db_exist - return 1 - } + EE['1.3.6.1.4.1.42.2.27.5.1.9']="'SolarisKernelSecurityPolicy' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.9']="'SolarisKernelSecurityPolicy' DESC 'Solaris kernel security policy' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - display_msg unable_find_db_name - return 2 -} + EE['1.3.6.1.4.1.42.2.27.5.1.10']="'SolarisProfileType' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.10']="'SolarisProfileType' DESC 'Type of object defined in profile'EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String -# -# add suffix if needed, -# suffix entry and backend MUST be prepared by -# prep_create_sfx_entry and prep_create_sfx_backend correspondingly -# -# input : NEED_CREATE_SUFFIX, LDAP_SUFFIX, LDAP_SUFFIX_OBJ, _ATT, _VAL -# LDAP_SUFFIX_ACI, NEED_CREATE_BACKEND, IDS_DATABASE -# return : 0 - suffix successfully created, otherwise error occured -# -add_suffix() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_suffix()" + EE['1.3.6.1.4.1.42.2.27.5.1.11']="'SolarisProfileId' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.11']="'SolarisProfileId' DESC 'Identifier of object defined in profile' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" - [ -n "${NEED_CREATE_SUFFIX}" ] || return 0 + EE['1.3.6.1.4.1.42.2.27.5.1.12']="'SolarisUserQualifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.12']="'SolarisUserQualifier' DESC 'Per-user login attributes' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String - [ -n "${NEED_CREATE_BACKEND}" ] && - { - ${EVAL} "${LDAPADD} ${LDAP_ARGS} ${VERB}" < /dev/null 2>&1 - if [ $? -eq 0 ]; then - break - else - prev_ldap_entry=${cur_ldap_entry} - cur_ldap_entry=`${ECHO} ${cur_ldap_entry} | cut -f2- -d','` - fi - done + EE['1.3.6.1.4.1.11.1.3.1.1.10']="'credentialLevel' DESC 'Identifies type of credentials a DUA should use when binding to the LDAP server' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.11.1.3.1.1.10']="'credentialLevel' DESC 'Identifies type of credentials either used, required, or supported by an agent or service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'RFC 4876'" # Directory vs. IA5 String - if [ "${cur_ldap_entry}" = "${prev_ldap_entry}" ]; then - ${ECHO} " No valid suffixes were found for Base DN ${LDAP_BASEDN}" + EE['1.3.6.1.4.1.11.1.3.1.1.11']="'objectclassMap' DESC 'Objectclass mappings used by a Naming-DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.11.1.3.1.1.11']="'objectclassMap' DESC 'Object class mappings used, required, or supported by an agent or service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 4876'" # Directory vs. IA5 String - NEED_CREATE_SUFFIX=1 - return 0 + EE['1.3.6.1.4.1.11.1.3.1.1.12']="'defaultSearchScope' DESC 'Default search scope used by a DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.11.1.3.1.1.12']="'defaultSearchScope' DESC 'Default scope used when performing a search' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'RFC 4876'" # Directory vs. IA5 String - else - [ $DEBUG -eq 1 ] && ${ECHO} "found valid LDAP entry: ${cur_ldap_entry}" + EE['1.3.6.1.4.1.11.1.3.1.1.13']="'serviceCredentialLevel' DESC 'Search scope used by a service of the DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" + OD['1.3.6.1.4.1.11.1.3.1.1.13']="'serviceCredentialLevel' DESC 'Specifies the type of credentials either used, required, or supported by a specific service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 4876'" # Now looking for relevant suffix for this entry. # LDAP_SUFFIX will then be used to add necessary @@ -3524,890 +3706,856 @@ else [ $DEBUG -eq 1 ] && ${ECHO} "found valid LDAP entry: ${cur_ldap_entry}" - # Now looking for relevant suffix for this entry. - # LDAP_SUFFIX will then be used to add necessary - # base objects. See add_base_objects(). - format_string "${cur_ldap_entry}" - lower_entry="${FMT_STR}" - [ $DEBUG -eq 1 ] && ${ECHO} "final suffix list: ${LDAP_SUFFIX_LIST}" - oIFS=$IFS - [ $DEBUG -eq 1 ] && ${ECHO} "setting IFS to new line" - IFS=' -' - for suff in ${LDAP_SUFFIX_LIST} - do - [ $DEBUG -eq 1 ] && ${ECHO} "testing suffix: ${suff}" - format_string "${suff}" - lower_suff="${FMT_STR}" - if [ "${lower_entry}" = "${lower_suff}" ]; then - LDAP_SUFFIX="${suff}" - break - else - dcstmp=`basename "${lower_entry}" "${lower_suff}"` - if [ "${dcstmp}" = "${lower_entry}" ]; then - # invalid suffix, try next one - continue - else - # valid suffix found - LDAP_SUFFIX="${suff}" - break - fi - fi - done - [ $DEBUG -eq 1 ] && ${ECHO} "setting IFS to original value" - IFS=$oIFS + EE['1.3.6.1.4.1.11.1.3.1.1.15']="'serviceAuthenticationMethod' DESC 'Authentication Method used by a service of the DUA' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.11.1.3.1.1.15']="'serviceAuthenticationMethod' DESC 'Specifies types authentication methods either used, required, or supported by a particular service' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'RFC 4876'" - [ $DEBUG -eq 1 ] && ${ECHO} "LDAP_SUFFIX: ${LDAP_SUFFIX}" + EE['1.3.18.0.2.4.1140']="'printer-uri' DESC 'A URI supported by this printer. This URI SHOULD be used as a relative distinguished name (RDN). If printer-xri-supported is implemented, then this URI value MUST be listed in a member value of printer-xri-supported.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.18.0.2.4.1140']="'printer-uri' DESC 'A URI supported by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'RFC 3712'" - if [ -z "${LDAP_SUFFIX}" ]; then - # should not happen, since we found the entry - ${ECHO} "Could not find a valid suffix for ${LDAP_BASEDN}." - ${ECHO} "Exiting." - return 1 - fi - - # Getting relevant database (backend) - # IDS_DATABASE will then be used to create indexes. - get_backend + EE['1.3.18.0.2.4.1107']="'printer-xri-supported' DESC 'The unordered list of XRI (extended resource identifiers) supported by this printer. Each member of the list consists of a URI (uniform resource identifier) followed by optional authentication and security metaparameters.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.18.0.2.4.1107']="'printer-xri-supported' DESC 'The unordered list of XRI (extended resource identifiers) supported by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'RFC 3712'" - return 0 - fi -} + EE['1.3.18.0.2.4.1135']="'printer-name' DESC 'The site-specific administrative name of this printer, more end-user friendly than a URI.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1135']="'printer-name' DESC 'The site-specific administrative name of this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" -# -# discover_serv_suffix(): This function queries the server to find -# suffixes available -# return: 0: OK, suffix found -# 1: suffix not determined -discover_serv_suffix() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In discover_serv_suffix()" + EE['1.3.18.0.2.4.1119']="'printer-natural-language-configured' DESC 'The configured language in which error and status messages will be generated (by default) by this printer. Also, a possible language for printer string attributes set by operator, system administrator, or manufacturer. Also, the (declared) language of the \"printer-name\", \"printer-location\", \"printer-info\", and \"printer-make-and-model\" attributes of this printer. For example: \"en-us\" (US English) or \"fr-fr\" (French in France) Legal values of language tags conform to [RFC3066] \"Tags for the Identification of Languages\".' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1119']="'printer-natural-language-configured' DESC 'The configured natural language in which error and status messages will be generated (by default) by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" - # Search the server for the TOP of the TREE. - ${LDAPSEARCH} ${SERVER_ARGS} -b "" -s base "objectclass=*" > ${TMPDIR}/checkTOP 2>&1 - ${GREP} -i namingcontexts ${TMPDIR}/checkTOP | \ - ${GREP} -i -v NetscapeRoot > ${TMPDIR}/treeTOP - NUM_TOP=`wc -l ${TMPDIR}/treeTOP | awk '{print $1}'` - case $NUM_TOP in - 0) - [ $DEBUG -eq 1 ] && ${ECHO} "DEBUG: No suffix found in LDAP tree" - return 1 - ;; - *) # build the list of suffixes; take out 'namingContexts=' in - # each line of ${TMPDIR}/treeTOP - LDAP_SUFFIX_LIST=`cat ${TMPDIR}/treeTOP | - awk '{ printf("%s\n",substr($0,16,length-15)) }'` - ;; - esac + EE['1.3.18.0.2.4.1136']="'printer-location' DESC 'Identifies the location of the printer. This could include things like: \"in Room 123A\", \"second floor of building XYZ\".' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1136']="'printer-location' DESC 'The physical location of this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" - [ $DEBUG -eq 1 ] && ${ECHO} " LDAP_SUFFIX_LIST = $LDAP_SUFFIX_LIST" - return 0 -} + EE['1.3.18.0.2.4.1139']="'printer-info' DESC 'Identifies the descriptive information about this printer. This could include things like: \"This printer can be used for printing color transparencies for HR presentations\", or \"Out of courtesy for others, please print only small (1-5 page) jobs at this printer\", or even \"This printer is going away on July 1, 1997, please find a new printer\".' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1139']="'printer-info' DESC 'Descriptive information about this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" + EE['1.3.18.0.2.4.1134']="'printer-more-info' DESC 'A URI used to obtain more information about this specific printer. For example, this could be an HTTP type URI referencing an HTML page accessible to a Web Browser. The information obtained from this URI is intended for end user consumption.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.18.0.2.4.1134']="'printer-more-info' DESC 'A URI for more information about this specific printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'RFC 3712'" -# -# modify_cn(): Change the cn from MUST to MAY in ipNetwork. -# -modify_cn() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In modify_cn()" + EE['1.3.18.0.2.4.1138']="'printer-make-and-model' DESC 'Identifies the make and model of the device. The device manufacturer MAY initially populate this attribute.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1138']="'printer-make-and-model' DESC 'Make and model of this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" - ( cat < ${TMPDIR}/ipNetwork_cn + EE['1.3.18.0.2.4.1133']="'printer-ipp-versions-supported' DESC 'Identifies the IPP protocol version(s) that this printer supports, including major and minor versions, i.e., the version numbers for which this Printer implementation meets the conformance requirements.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1133']="'printer-ipp-versions-supported' DESC 'IPP protocol version(s) that this printer supports.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" - # Modify the cn for ipNetwork. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ipNetwork_cn ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of cn for ipNetwork failed!" - cleanup - exit 1 - fi -} + EE['1.3.18.0.2.4.1132']="'printer-multiple-document-jobs-supported' DESC 'Indicates whether or not the printer supports more than one document per job, i.e., more than one Send-Document or Send-Data operation with document data.' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE" + OD['1.3.18.0.2.4.1132']="'printer-multiple-document-jobs-supported' DESC 'Indicates whether or not this printer supports more than one document per job.' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'RFC 3712'" + EE['1.3.18.0.2.4.1109']="'printer-charset-configured' DESC 'The configured charset in which error and status messages will be generated (by default) by this printer. Also, a possible charset for printer string attributes set by operator, system administrator, or manufacturer. For example: \"utf-8\" (ISO 10646/Unicode) or \"iso-8859-1\" (Latin1). Legal values are defined by the IANA Registry of Coded Character Sets and the \"(preferred MIME name)\" SHALL be used as the tag. For coherence with IPP Model, charset tags in this attribute SHALL be lowercase normalized. This attribute SHOULD be static (time of registration) and SHOULD NOT be dynamically refreshed attributetypes: (subsequently).' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{63} SINGLE-VALUE" + OD['1.3.18.0.2.4.1109']="'printer-charset-configured' DESC 'The configured charset in which error and status messages will be generated (by default) by this printer.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{63} SINGLE-VALUE X-ORIGIN 'RFC 3712'" -# modify_timelimit(): Modify timelimit to user value. -modify_timelimit() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In modify_timelimit()" + EE['1.3.18.0.2.4.1131']="'printer-charset-supported' DESC 'Identifies the set of charsets supported for attribute type values of type Directory String for this directory entry. For example: \"utf-8\" (ISO 10646/Unicode) or \"iso-8859-1\" (Latin1). Legal values are defined by the IANA Registry of Coded Character Sets and the preferred MIME name.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{63}" + OD['1.3.18.0.2.4.1131']="'printer-charset-supported' DESC 'Set of charsets supported for the attribute values of syntax DirectoryString for this directory entry.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{63} X-ORIGIN 'RFC 3712'" - # Here doc to modify timelimit. - ( cat < ${TMPDIR}/ids_timelimit + EE['1.3.18.0.2.4.1137']="'printer-generated-natural-language-supported' DESC 'Identifies the natural language(s) supported for this directory entry. For example: \"en-us\" (US English) or \"fr-fr\" (French in France). Legal values conform to [RFC3066], Tags for the Identification of Languages.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{63}" + OD['1.3.18.0.2.4.1137']="'printer-generated-natural-language-supported' DESC 'Natural language(s) supported for this directory entry.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{63} X-ORIGIN 'RFC 3712'" - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ids_timelimit ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of nsslapd-timelimit failed!" - cleanup - exit 1 - fi + EE['1.3.18.0.2.4.1130']="'printer-document-format-supported' DESC 'The possible document formats in which data may be interpreted and printed by this printer. Legal values are MIME types come from the IANA Registry of Internet Media Types.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1130']="'printer-document-format-supported' DESC 'The possible source document formats which may be interpreted and printed by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" - # Display messages for modifications made in patch. - ${ECHO} " ${STEP}. Changed timelimit to ${IDS_TIMELIMIT} in cn=config." - STEP=`expr $STEP + 1` -} + EE['1.3.18.0.2.4.1129']="'printer-color-supported' DESC 'Indicates whether this printer is capable of any type of color printing at all, including highlight color.' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE" + OD['1.3.18.0.2.4.1129']="'printer-color-supported' DESC 'Indicates whether this printer is capable of any type of color printing at all, including highlight color.' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'RFC 3712'" + EE['1.3.18.0.2.4.1128']="'printer-compression-supported' DESC 'Compression algorithms supported by this printer. For example: \"deflate, gzip\". Legal values include; \"none\", \"deflate\" attributetypes: (public domain ZIP), \"gzip\" (GNU ZIP), \"compress\" (UNIX).' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255}" + OD['1.3.18.0.2.4.1128']="'printer-compression-supported' DESC 'Compression algorithms supported by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} X-ORIGIN 'RFC 3712'" -# modify_sizelimit(): Modify sizelimit to user value. -modify_sizelimit() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In modify_sizelimit()" + EE['1.3.18.0.2.4.1127']="'printer-pages-per-minute' DESC 'The nominal number of pages per minute which may be output by this printer (e.g., a simplex or black-and-white printer). This attribute is informative, NOT a service guarantee. Typically, it is the value used in marketing literature to describe this printer.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" + OD['1.3.18.0.2.4.1127']="'printer-pages-per-minute' DESC 'The nominal number of pages per minute which may be output by this printer.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 3712'" - # Here doc to modify sizelimit. - ( cat < ${TMPDIR}/ids_sizelimit + EE['1.3.18.0.2.4.1126']="'printer-pages-per-minute-color' DESC 'The nominal number of color pages per minute which may be output by this printer (e.g., a simplex or color printer). This attribute is informative, NOT a service guarantee. Typically, it is the value used in marketing literature to describe this printer.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" + OD['1.3.18.0.2.4.1126']="'printer-pages-per-minute-color' DESC 'The nominal number of color pages per minute which may be output by this printer.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 3712'" - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ids_sizelimit ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of nsslapd-sizelimit failed!" - cleanup - exit 1 - fi + EE['1.3.18.0.2.4.1125']="'printer-finishings-supported' DESC 'The possible finishing operations supported by this printer. Legal values include; \"none\", \"staple\", \"punch\", \"cover\", \"bind\", \"saddle-stitch\", \"edge-stitch\", \"staple-top-left\", \"staple-bottom-left\", \"staple-top-right\", \"staple-bottom-right\", \"edge-stitch-left\", \"edge-stitch-top\", \"edge-stitch-right\", \"edge-stitch-bottom\", \"staple-dual-left\", \"staple-dual-top\", \"staple-dual-right\", \"staple-dual-bottom\".' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255}" + OD['1.3.18.0.2.4.1125']="'printer-finishings-supported' DESC 'The possible finishing operations supported by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} X-ORIGIN 'RFC 3712'" - # Display messages for modifications made in patch. - ${ECHO} " ${STEP}. Changed sizelimit to ${IDS_SIZELIMIT} in cn=config." - STEP=`expr $STEP + 1` -} + EE['1.3.18.0.2.4.1124']="'printer-number-up-supported' DESC 'The possible numbers of print-stream pages to impose upon a single side of an instance of a selected medium. Legal values include; 1, 2, and 4. Implementations may support other values.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27" + OD['1.3.18.0.2.4.1124']="'printer-number-up-supported' DESC 'The possible numbers of print-stream pages to impose upon a single side of an instance of a selected medium.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 X-ORIGIN 'RFC 3712'" + EE['1.3.18.0.2.4.1123']="'printer-sides-supported' DESC 'The number of impression sides (one or two) and the two-sided impression rotations supported by this printer. Legal values include; \"one-sided\", \"two-sided-long-edge\", \"two-sided-short-edge\".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1123']="'printer-sides-supported' DESC 'The number of impression sides (one or two) and the two-sided impression rotations supported by this printer.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" -# modify_pwd_crypt(): Modify the passwd storage scheme to support CRYPT. -modify_pwd_crypt() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In modify_pwd_crypt()" + EE['1.3.18.0.2.4.1122']="'printer-media-supported' DESC 'The standard names/types/sizes (and optional color suffixes) of the media supported by this printer. For example: \"iso-a4\", \"envelope\", or \"na-letter-white\". Legal values conform to ISO 10175, Document Printing Application (DPA), and any IANA registered extensions.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255}" + OD['1.3.18.0.2.4.1122']="'printer-media-supported' DESC 'The standard names/types/sizes (and optional color suffixes) of the media supported by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} X-ORIGIN 'RFC 3712'" - # Here doc to modify passwordstoragescheme. - # IDS 5.2 moved passwordchangesceme off to a new data structure. - if [ $IDS_MAJVER -le 5 ] && [ $IDS_MINVER -le 1 ]; then - ( cat < ${TMPDIR}/ids_crypt - else - ( cat < ${TMPDIR}/ids_crypt - fi + EE['1.3.18.0.2.4.1117']="'printer-media-local-supported' DESC 'Site-specific names of media supported by this printer, in the language in \"printer-natural-language-configured\". For example: \"purchasing-form\" (site-specific name) as opposed to (in \"printer-media-supported\"): \"na-letter\" (standard keyword from ISO 10175).' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255}" + OD['1.3.18.0.2.4.1117']="'printer-media-local-supported' DESC 'Site-specific names of media supported by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} X-ORIGIN 'RFC 3712'" - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ids_crypt ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of passwordstoragescheme failed!" - cleanup - exit 1 - fi + EE['1.3.18.0.2.4.1121']="'printer-resolution-supported' DESC 'List of resolutions supported for printing documents by this printer. Each resolution value is a string with 3 fields: 1) Cross feed direction resolution (positive integer), 2) Feed direction resolution (positive integer), 3) Resolution unit. Legal values are \"dpi\" (dots per inch) and \"dpcm\" (dots per centimeter). Each resolution field is delimited by \">\". For example: \"300> 300> dpi>\".' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255}" + OD['1.3.18.0.2.4.1121']="'printer-resolution-supported' DESC 'List of resolutions supported for printing documents by this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} X-ORIGIN 'RFC 3712'" - # Display messages for modifications made in patch. - ${ECHO} " ${STEP}. Changed passwordstoragescheme to \"crypt\" in cn=config." - STEP=`expr $STEP + 1` -} + EE['1.3.18.0.2.4.1120']="'printer-print-quality-supported' DESC 'List of print qualities supported for printing documents on this printer. For example: \"draft, normal\". Legal values include; \"unknown\", \"draft\", \"normal\", \"high\".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1120']="'printer-print-quality-supported' DESC 'List of print qualities supported for printing documents on this printer.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" + EE['1.3.18.0.2.4.1110']="'printer-job-priority-supported' DESC 'Indicates the number of job priority levels supported. An IPP conformant printer which supports job priority must always support a full range of priorities from \"1\" to \"100\" (to ensure consistent behavior), therefore this attribute describes the \"granularity\". Legal values of this attribute are from \"1\" to \"100\".' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" + OD['1.3.18.0.2.4.1110']="'printer-job-priority-supported' DESC 'Indicates the number of job priority levels supported by this printer.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 3712'" -# -# add_eq_indexes(): Add indexes to improve search performance. -# -add_eq_indexes() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_eq_indexes()" + EE['1.3.18.0.2.4.1118']="'printer-copies-supported' DESC 'The maximum number of copies of a document that may be printed as a single job. A value of \"0\" indicates no maximum limit. A value of \"-1\" indicates unknown.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" + OD['1.3.18.0.2.4.1118']="'printer-copies-supported' DESC 'The maximum number of copies of a document that may be printed as a single job on this printer.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 3712'" - # Set eq indexes to add. - _INDEXES="uidNumber ipNetworkNumber gidnumber oncrpcnumber automountKey" + EE['1.3.18.0.2.4.1111']="'printer-job-k-octets-supported' DESC 'The maximum size in kilobytes (1,024 octets actually) incoming print job that this printer will accept. A value of \"0\" indicates no maximum limit. A value of \"-1\" indicates unknown.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" + OD['1.3.18.0.2.4.1111']="'printer-job-k-octets-supported' DESC 'The maximum size in kilobytes (1,024 octets actually) incoming print job that this printer will accept.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 3712'" - if [ -z "${IDS_DATABASE}" ]; then - get_backend - fi + EE['1.3.18.0.2.4.1112']="'printer-current-operator' DESC 'The name of the current human operator responsible for operating this printer. It is suggested that this string include information that would enable other humans to reach the operator, such as a phone number.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1112']="'printer-current-operator' DESC 'The identity of the current human operator responsible for operating this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" - # Set _EXT to use as shortcut. - _EXT="cn=index,cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config" + EE['1.3.18.0.2.4.1113']="'printer-service-person' DESC 'The name of the current human service person responsible for servicing this printer. It is suggested that this string include information that would enable other humans to reach the service person, such as a phone number.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE" + OD['1.3.18.0.2.4.1113']="'printer-service-person' DESC 'The identity of the current human service person responsible for servicing this printer.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE X-ORIGIN 'RFC 3712'" - # Display message to id current step. - ${ECHO} " ${STEP}. Processing eq,pres indexes:" - STEP=`expr $STEP + 1` + EE['1.3.18.0.2.4.1114']="'printer-delivery-orientation-supported' DESC 'The possible delivery orientations of pages as they are printed and ejected from this printer. Legal values include; \"unknown\", \"face-up\", and \"face-down\".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1114']="'printer-delivery-orientation-supported' DESC 'The possible delivery orientations of pages as they are printed and ejected from this printer.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" - # For loop to create indexes. - for i in ${_INDEXES}; do - [ $DEBUG -eq 1 ] && ${ECHO} " Adding index for ${i}" + EE['1.3.18.0.2.4.1115']="'printer-stacking-order-supported' DESC 'The possible stacking order of pages as they are printed and ejected from this printer. Legal values include; \"unknown\", \"first-to-last\", \"last-to-first\".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1115']="'printer-stacking-order-supported' DESC 'The possible stacking order of pages as they are printed and ejected from this printer.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" - # Check if entry exists first, if so, skip to next. - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${i},${_EXT}\" -s base \ - \"objectclass=*\" > /dev/null 2>&1" - if [ $? -eq 0 ]; then - # Display index skipped. - ${ECHO} " ${i} (eq,pres) skipped already exists" - continue - fi + EE['1.3.18.0.2.4.1116']="'printer-output-features-supported' DESC 'The possible output features supported by this printer. Legal values include; \"unknown\", \"bursting\", \"decollating\", \"page-collating\", \"offset-stacking\".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1116']="'printer-output-features-supported' DESC 'The possible output features supported by this printer.' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" - # Here doc to create LDIF. - ( cat < ${TMPDIR}/index_${i} + EE['1.3.18.0.2.4.1108']="'printer-aliases' DESC 'Site-specific administrative names of this printer in addition the printer name specified for printer-name.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127}" + OD['1.3.18.0.2.4.1108']="'printer-aliases' DESC 'List of site-specific administrative names of this printer in addition to the value specified for printer-name.' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} X-ORIGIN 'RFC 3712'" - # Add the index. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/index_${i} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding EQ,PRES index for ${i} failed!" - cleanup - exit 1 - fi + EE['1.3.6.1.4.1.42.2.27.5.1.63']="'sun-printer-bsdaddr' DESC 'Sets the server, print queue destination name and whether the client generates protocol extensions. \"Solaris\" specifies a Solaris print server extension. The value is represented by the following value: server \",\" destination \", Solaris\".' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.63']="'sun-printer-bsdaddr' DESC 'Sets the server, print queue destination name and whether the client generates protocol extensions. \"Solaris\" specifies a Solaris print server extension. The value is represented by the following value: server \",\" destination \", Solaris\".' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" - # Build date for task name. - _YR=`date '+%y'` - _MN=`date '+%m'` - _DY=`date '+%d'` - _H=`date '+%H'` - _M=`date '+%M'` - _S=`date '+%S'` + EE['1.3.6.1.4.1.42.2.27.5.1.64']="'sun-printer-kvp' DESC 'This attribute contains a set of key value pairs which may have meaning to the print subsystem or may be user defined. Each value is represented by the following: key \"=\" value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" + OD['1.3.6.1.4.1.42.2.27.5.1.64']="'sun-printer-kvp' DESC 'This attribute contains a set of key value pairs which may have meaning to the print subsystem or may be user defined. Each value is represented by the following: key \"=\" value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Solaris Specific'" - # Build task name - TASKNAME="${i}_${_YR}_${_MN}_${_DY}_${_H}_${_M}_${_S}" + EE['1.3.6.1.4.1.42.2.27.5.1.57']="'nisplusTimeZone' DESC 'tzone column from NIS+ timezone table' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.57']="'nisplusTimeZone' DESC 'tzone column from NIS+ timezone table' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" - # Build the task entry to add. - ( cat < ${TMPDIR}/task_${i} + EE['1.3.6.1.4.1.42.2.27.5.1.67']="'ipTnetTemplateName' DESC 'Trusted Solaris network template template_name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.67']="'ipTnetTemplateName' DESC 'Trusted Solaris network template template_name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" - # Add the task. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/task_${i} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding task for ${i} failed!" - cleanup - exit 1 + EE['1.3.6.1.4.1.42.2.27.5.1.68']="'ipTnetNumber' DESC 'Trusted Solaris network template ip_address' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" + OD['1.3.6.1.4.1.42.2.27.5.1.68']="'ipTnetNumber' DESC 'Trusted Solaris network template ip_address' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" + + # obey CLI syntax force option (if any) + if (( TMPF[SYNTAX] == 1 )); then + typeset -n MAP=OD + elif (( TMPF[SYNTAX] == 2 )); then + typeset -n MAP=EE + elif (( TMPF[IS_OPENDJ] )); then + typeset -n MAP=OD + else + typeset -n MAP=EE fi - # Wait for task to finish, display current status. - while : - do - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \ - -b \"cn=${TASKNAME}, cn=index, cn=tasks, cn=config\" -s base \ - \"objectclass=*\" nstaskstatus > \"${TMPDIR}/istask_${i}\" 2>&1" - ${GREP} "${TASKNAME}" "${TMPDIR}/istask_${i}" > /dev/null 2>&1 - if [ $? -ne 0 ]; then - break - fi - TASK_STATUS=`${GREP} -i nstaskstatus "${TMPDIR}/istask_${i}" | - head -1 | cut -d: -f2` - ${ECHO} " ${i} (eq,pres) $TASK_STATUS \r\c" - ${ECHO} "$TASK_STATUS" | ${GREP} "Finished" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - break - fi - sleep 2 + typeset TODO='' OID X + for OID in ${!MAP[@]}; do + [[ -n ${OID2ADEF[${OID}]} ]] && continue + X="${MAP[${OID}]}" + TODO+='attributetypes: ( '"${OID} NAME ${X}"' )\n' + OID2ADEF["${OID}"]="${X}" + X=${X:1} + X=${X%%"'"*} # we have no aliases above + ANAME2OID["${X}"]="${OID}" done - # Print newline because of \c. - ${ECHO} " " - done + if [[ -z ${TODO} ]]; then + X='Schema contains all required attribute definitions' + else + nextFile add $0 + print 'dn: cn=schema\nchangetype: modify\nadd: attributetypes' \ + "\n${TODO}" >${TMP[FILE]} + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Adding attribute definitions to schema failed' + return 67 + fi + X='Schema attribute definitions added' + fi + showProgress "${X}." + return 0 } +Man.addFunc update_schema_obj '' '[+NAME?update_schema_obj - Update DS schema to support Naming Services.] +[+DESCRIPTION?Update the schema of the DS with the objectclasses required for the Solaris Naming Services. It just checks, whether the OID is already defined on the server. If already defined, it is left as is, otherwise added.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occurred.] +} +[+SEE ALSO?\bupdate_schema_attr()\b, \bldapmodify\b(1).] +' +function update_schema_obj { + getDSobjectclasses || return 66 -# -# add_sub_indexes(): Add indexes to improve search performance. -# -add_sub_indexes() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_sub_indexes()" + typeset -A EE=( ) + typeset -A OD=( ) + EE['1.3.6.1.1.1.2.14']="'NisKeyObject' SUP top MUST "'( cn $ nisPublickey $ nisSecretkey ) MAY ( uidNumber $ description )' + OD['1.3.6.1.1.1.2.14']="'nisKeyObject' SUP top AUXILIARY DESC 'An object with a public and secret key' MUST "'( cn $ nisPublicKey $ nisSecretKey ) MAY ( uidNumber $ description )'" X-ORIGIN 'draft-howard-rfc2307bis'" - # Set eq indexes to add. - _INDEXES="ipHostNumber membernisnetgroup nisnetgrouptriple" + EE['1.3.6.1.1.1.2.15']="'nisDomainObject' SUP top MUST nisDomain" + OD['1.3.6.1.1.1.2.15']="'nisDomainObject' SUP top AUXILIARY DESC 'Associates a NIS domain with a naming context' MUST nisDomain X-ORIGIN 'draft-howard-rfc2307bis'" - # Set _EXT to use as shortcut. - _EXT="cn=index,cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config" + EE['1.3.6.1.1.1.2.16']="'automountMap' SUP top MUST automountMapName MAY description" + OD['1.3.6.1.1.1.2.16']="'automountMap' SUP top STRUCTURAL MUST ( automountMapName ) MAY description X-ORIGIN 'draft-howard-rfc2307bis'" + EE['1.3.6.1.1.1.2.17']="'automount' SUP top MUST "'( automountKey $ automountInformation ) MAY description' + OD['1.3.6.1.1.1.2.17']="'automount' SUP top STRUCTURAL DESC 'Automount information' MUST "'( automountKey $ automountInformation )'" MAY description X-ORIGIN 'draft-howard-rfc2307bis'" - # Display message to id current step. - ${ECHO} " ${STEP}. Processing eq,pres,sub indexes:" - STEP=`expr $STEP + 1` + EE['1.3.6.1.4.1.42.2.27.5.2.7']="'SolarisNamingProfile' SUP top MUST "'( cn $ SolarisLDAPservers $ SolarisSearchBaseDN ) MAY ( SolarisBindDN $ SolarisBindPassword $ SolarisAuthMethod $ SolarisTransportSecurity $ SolarisCertificatePath $ SolarisCertificatePassword $ SolarisDataSearchDN $ SolarisSearchScope $ SolarisSearchTimeLimit $ SolarisPreferredServer $ SolarisPreferredServerOnly $ SolarisCacheTTL $ SolarisSearchReferral )' + OD['1.3.6.1.4.1.42.2.27.5.2.7']="'SolarisNamingProfile' SUP top STRUCTURAL DESC 'Solaris LDAP Naming client profile objectClass' MUST "'( cn $ SolarisLDAPServers $ SolarisSearchBaseDN ) MAY ( SolarisBindDN $ SolarisBindPassword $ SolarisAuthMethod $ SolarisTransportSecurity $ SolarisCertificatePath $ SolarisCertificatePassword $ SolarisDataSearchDN $ SolarisSearchScope $ SolarisSearchTimeLimit $ SolarisPreferredServer $ SolarisPreferredServerOnly $ SolarisCacheTTL $ SolarisSearchReferral $ SolarisBindTimeLimit )'" X-ORIGIN 'Solaris Specific'" - # For loop to create indexes. - for i in ${_INDEXES}; do - [ $DEBUG -eq 1 ] && ${ECHO} " Adding index for ${i}" + EE['2.16.840.1.113730.3.2.4']="'mailGroup' SUP top MUST mail MAY "'( cn $ mgrpRFC822MailMember )' + OD['2.16.840.1.113730.3.2.4']="'mailGroup' SUP top STRUCTURAL MUST mail MAY "'( cn $ mgrpRFC822MailMember )'" X-ORIGIN 'Solaris Specific'" - # Check if entry exists first, if so, skip to next. - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${i},${_EXT}\" \ - -s base \"objectclass=*\" > /dev/null 2>&1" - if [ $? -eq 0 ]; then - # Display index skipped. - ${ECHO} " ${i} (eq,pres,sub) skipped already exists" - continue - fi + EE['1.3.6.1.4.1.42.2.27.1.2.5']="'nisMailAlias' SUP top MUST cn MAY rfc822mailMember" + OD['1.3.6.1.4.1.42.2.27.1.2.5']="'nisMailAlias' SUP top MUST cn MAY rfc822mailMember X-ORIGIN 'Solaris Specific'" - # Here doc to create LDIF. - ( cat < ${TMPDIR}/index_${i} + EE['1.3.6.1.4.1.42.2.27.1.2.6']="'nisNetId' SUP top MUST cn MAY "'( nisNetIdUser $ nisNetIdGroup $ nisNetIdHost )' + OD['1.3.6.1.4.1.42.2.27.1.2.6']="'nisNetId' SUP top MUST cn MAY "'( nisNetIdUser $ nisNetIdGroup $ nisNetIdHost )'" X-ORIGIN 'Solaris Specific'" - # Add the index. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/index_${i} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding EQ,PRES,SUB index for ${i} failed!" - cleanup - exit 1 - fi + EE['1.3.6.1.4.1.42.2.27.5.2.2']="'SolarisAuditUser' SUP top AUXILIARY MAY "'( SolarisAuditAlways $ SolarisAuditNever )' + OD['1.3.6.1.4.1.42.2.27.5.2.2']="'SolarisAuditUser' SUP top AUXILIARY MAY "'( SolarisAuditAlways $ SolarisAuditNever )'" X-ORIGIN 'Solaris Specific'" - # Build date for task name. - _YR=`date '+%y'` - _MN=`date '+%m'` - _DY=`date '+%d'` - _H=`date '+%H'` - _M=`date '+%M'` - _S=`date '+%S'` + EE['1.3.6.1.4.1.42.2.27.5.2.3']="'SolarisUserAttr' SUP top AUXILIARY MAY "'( SolarisUserQualifier $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrKeyValue )' + OD['1.3.6.1.4.1.42.2.27.5.2.3']="'SolarisUserAttr' SUP top AUXILIARY DESC 'User attributes' MAY "'( SolarisUserQualifier $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrKeyValue )'" X-ORIGIN 'Solaris Specific'" - # Build task name - TASKNAME="${i}_${_YR}_${_MN}_${_DY}_${_H}_${_M}_${_S}" + EE['1.3.6.1.4.1.42.2.27.5.2.4']="'SolarisAuthAttr' SUP top MUST cn MAY "'( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrShortDesc $ SolarisAttrLongDesc $ SolarisAttrKeyValue )' + OD['1.3.6.1.4.1.42.2.27.5.2.4']="'SolarisAuthAttr' SUP top STRUCTURAL DESC 'Authorizations data' MUST cn MAY "'( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrShortDesc $ SolarisAttrLongDesc $ SolarisAttrKeyValue )'" X-ORIGIN 'Solaris Specific'" - # Build the task entry to add. - ( cat < ${TMPDIR}/task_${i} + EE['1.3.6.1.4.1.42.2.27.5.2.5']="'SolarisProfAttr' SUP top MUST cn MAY "'( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrLongDesc $ SolarisAttrKeyValue )' + OD['1.3.6.1.4.1.42.2.27.5.2.5']="'SolarisProfAttr' SUP top STRUCTURAL DESC 'Profiles data' MUST cn MAY "'( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrLongDesc $ SolarisAttrKeyValue )'" X-ORIGIN 'Solaris Specific'" - # Add the task. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/task_${i} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding task for ${i} failed!" - cleanup - exit 1 - fi + EE['1.3.6.1.4.1.42.2.27.5.2.6']="'SolarisExecAttr' SUP top AUXILIARY MAY "'( SolarisKernelSecurityPolicy $ SolarisProfileType $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisProfileID $ SolarisAttrKeyValue )' + OD['1.3.6.1.4.1.42.2.27.5.2.6']="'SolarisExecAttr' SUP top AUXILIARY DESC 'Profiles execution attributes' MAY "'( SolarisKernelSecurityPolicy $ SolarisProfileType $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisProfileId $ SolarisAttrKeyValue )'" X-ORIGIN 'Solaris Specific'" - # Wait for task to finish, display current status. - while : - do - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \ - -b \"cn=${TASKNAME}, cn=index, cn=tasks, cn=config\" -s base \ - \"objectclass=*\" nstaskstatus > \"${TMPDIR}/istask_${i}\" 2>&1" - ${GREP} "${TASKNAME}" "${TMPDIR}/istask_${i}" > /dev/null 2>&1 - if [ $? -ne 0 ]; then - break - fi - TASK_STATUS=`${GREP} -i nstaskstatus "${TMPDIR}/istask_${i}" | - head -1 | cut -d: -f2` - ${ECHO} " ${i} (eq,pres,sub) $TASK_STATUS \r\c" - ${ECHO} "$TASK_STATUS" | ${GREP} "Finished" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - break - fi - sleep 2 - done + EE['1.3.6.1.4.1.42.2.27.5.2.1']="'SolarisProject' SUP top MUST "'( SolarisProjectID $ SolarisProjectName ) MAY ( memberUid $ memberGid $ description $ SolarisProjectAttr )' + OD['1.3.6.1.4.1.42.2.27.5.2.1']="'SolarisProject' SUP top STRUCTURAL MUST "'( SolarisProjectID $ SolarisProjectName ) MAY ( memberUid $ memberGid $ description $ SolarisProjectAttr )'" X-ORIGIN 'Solaris Specific'" - # Print newline because of \c. - ${ECHO} " " - done -} + EE['1.3.6.1.4.1.11.1.3.1.2.4']="'DUAConfigProfile' SUP top DESC 'Abstraction of a base configuration for a DUA' MUST cn MAY "'( defaultServerList $ preferredServerList $ defaultSearchBase $ defaultSearchScope $ searchTimeLimit $ bindTimeLimit $ credentialLevel $ authenticationMethod $ followReferrals $ serviceSearchDescriptor $ serviceCredentialLevel $ serviceAuthenticationMethod $ objectclassMap $ attributeMap $ profileTTL )' + OD['1.3.6.1.4.1.11.1.3.1.2.4']="'DUAConfigProfile' SUP top STRUCTURAL DESC 'Abstraction of a base configuration for a DUA' MUST "'( cn ) MAY ( defaultServerList $ preferredServerList $ defaultSearchBase $ defaultSearchScope $ searchTimeLimit $ bindTimeLimit $ credentialLevel $ authenticationMethod $ followReferrals $ dereferenceAliases $ serviceSearchDescriptor $ serviceCredentialLevel $ serviceAuthenticationMethod $ objectclassMap $ attributeMap $ profileTTL )'" X-ORIGIN 'RFC 4876'" + EE['1.3.18.0.2.6.2549']="'slpService' DESC 'DUMMY definition' SUP top MUST objectclass" + OD['1.3.18.0.2.6.2549']="'slpService' DESC 'parent superclass for SLP services' SUP top ABSTRACT MUST "'( template-major-version-number $ template-minor-version-number $ description $ template-url-syntax $ service-advert-service-type $ service-advert-scopes ) MAY ( service-advert-url-authenticator $ service-advert-attribute-authenticator )'" X-ORIGIN 'RFC 2926'" -# -# add_vlv_indexes(): Add VLV indexes to improve search performance. -# -add_vlv_indexes() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_vlv_indexes()" + EE['1.3.18.0.2.6.254']="'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' SUP slpService AUXILIARY" + OD['1.3.18.0.2.6.254']="'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' SUP slpService AUXILIARY X-ORIGIN 'RFC 3712'" - # Set eq indexes to add. - # Note semi colon separators because some filters contain colons - _INDEX1="${LDAP_DOMAIN}.getgrent;${LDAP_DOMAIN}_group_vlv_index;ou=group;objectClass=posixGroup" - _INDEX2="${LDAP_DOMAIN}.gethostent;${LDAP_DOMAIN}_hosts_vlv_index;ou=hosts;objectClass=ipHost" - _INDEX3="${LDAP_DOMAIN}.getnetent;${LDAP_DOMAIN}_networks_vlv_index;ou=networks;objectClass=ipNetwork" - _INDEX4="${LDAP_DOMAIN}.getpwent;${LDAP_DOMAIN}_passwd_vlv_index;ou=people;objectClass=posixAccount" - _INDEX5="${LDAP_DOMAIN}.getrpcent;${LDAP_DOMAIN}_rpc_vlv_index;ou=rpc;objectClass=oncRpc" - _INDEX6="${LDAP_DOMAIN}.getspent;${LDAP_DOMAIN}_shadow_vlv_index;ou=people;objectClass=shadowAccount" + EE['1.3.18.0.2.6.258']="'printerAbstract' DESC 'Printer related information.' SUP top ABSTRACT MAY "'( printer-name $ printer-natural-language-configured $ printer-location $ printer-info $ printer-more-info $ printer-make-and-model $ printer-multiple-document-jobs-supported $ printer-charset-configured $ printer-charset-supported $ printer-generated-natural-language-supported $ printer-document-format-supported $ printer-color-supported $ printer-compression-supported $ printer-pages-per-minute $ printer-pages-per-minute-color $ printer-finishings-supported $ printer-number-up-supported $ printer-sides-supported $ printer-media-supported $ printer-media-local-supported $ printer-resolution-supported $ printer-print-quality-supported $ printer-job-priority-supported $ printer-copies-supported $ printer-job-k-octets-supported $ printer-current-operator $ printer-service-person $ printer-delivery-orientation-supported $ printer-stacking-order-supported $ printer-output-features-supported )' + OD['1.3.18.0.2.6.258']="'printerAbstract' DESC 'Printer related information.' SUP top ABSTRACT MAY "'( printer-name $ printer-natural-language-configured $ printer-location $ printer-info $ printer-more-info $ printer-make-and-model $ printer-multiple-document-jobs-supported $ printer-charset-configured $ printer-charset-supported $ printer-generated-natural-language-supported $ printer-document-format-supported $ printer-color-supported $ printer-compression-supported $ printer-pages-per-minute $ printer-pages-per-minute-color $ printer-finishings-supported $ printer-number-up-supported $ printer-sides-supported $ printer-media-supported $ printer-media-local-supported $ printer-resolution-supported $ printer-print-quality-supported $ printer-job-priority-supported $ printer-copies-supported $ printer-job-k-octets-supported $ printer-current-operator $ printer-service-person $ printer-delivery-orientation-supported $ printer-stacking-order-supported $ printer-output-features-supported )'" X-ORIGIN 'RFC 3712' )" - # Indexes added during NIS to LDAP transition - _INDEX7="${LDAP_DOMAIN}.getauhoent;${LDAP_DOMAIN}_auho_vlv_index;automountmapname=auto_home;objectClass=automount" - _INDEX8="${LDAP_DOMAIN}.getsoluent;${LDAP_DOMAIN}_solu_vlv_index;ou=people;objectClass=SolarisUserAttr" - _INDEX10="${LDAP_DOMAIN}.getauthent;${LDAP_DOMAIN}_auth_vlv_index;ou=SolarisAuthAttr;objectClass=SolarisAuthAttr" - _INDEX11="${LDAP_DOMAIN}.getexecent;${LDAP_DOMAIN}_exec_vlv_index;ou=SolarisProfAttr;&(objectClass=SolarisExecAttr)(SolarisKernelSecurityPolicy=*)" - _INDEX12="${LDAP_DOMAIN}.getprofent;${LDAP_DOMAIN}_prof_vlv_index;ou=SolarisProfAttr;&(objectClass=SolarisProfAttr)(SolarisAttrLongDesc=*)" - _INDEX13="${LDAP_DOMAIN}.getmailent;${LDAP_DOMAIN}_mail_vlv_index;ou=aliases;objectClass=mailGroup" - _INDEX14="${LDAP_DOMAIN}.getbootent;${LDAP_DOMAIN}__boot_vlv_index;ou=ethers;&(objectClass=bootableDevice)(bootParameter=*)" - _INDEX15="${LDAP_DOMAIN}.getethent;${LDAP_DOMAIN}_ethers_vlv_index;ou=ethers;&(objectClass=ieee802Device)(macAddress=*)" - _INDEX16="${LDAP_DOMAIN}.getngrpent;${LDAP_DOMAIN}_netgroup_vlv_index;ou=netgroup;objectClass=nisNetgroup" - _INDEX17="${LDAP_DOMAIN}.getipnent;${LDAP_DOMAIN}_ipn_vlv_index;ou=networks;&(objectClass=ipNetwork)(cn=*)" - _INDEX18="${LDAP_DOMAIN}.getmaskent;${LDAP_DOMAIN}_mask_vlv_index;ou=networks;&(objectClass=ipNetwork)(ipNetmaskNumber=*)" - _INDEX19="${LDAP_DOMAIN}.getprent;${LDAP_DOMAIN}_pr_vlv_index;ou=printers;objectClass=printerService" - _INDEX20="${LDAP_DOMAIN}.getip4ent;${LDAP_DOMAIN}_ip4_vlv_index;ou=hosts;&(objectClass=ipHost)(ipHostNumber=*.*)" - _INDEX21="${LDAP_DOMAIN}.getip6ent;${LDAP_DOMAIN}_ip6_vlv_index;ou=hosts;&(objectClass=ipHost)(ipHostNumber=*:*)" + EE['1.3.18.0.2.6.255']="'printerService' DESC 'Printer information.' SUP printerAbstract STRUCTURAL MAY "'( printer-uri $ printer-xri-supported )' + OD['1.3.18.0.2.6.255']="'printerService' DESC 'Printer information.' SUP printerAbstract STRUCTURAL MAY "'( printer-uri $ printer-xri-supported )'" X-ORIGIN 'RFC 3712'" - _INDEXES="$_INDEX1 $_INDEX2 $_INDEX3 $_INDEX4 $_INDEX5 $_INDEX6 $_INDEX7 $_INDEX8 $_INDEX9 $_INDEX10 $_INDEX11 $_INDEX12 $_INDEX13 $_INDEX14 $_INDEX15 $_INDEX16 $_INDEX17 $_INDEX18 $_INDEX19 $_INDEX20 $_INDEX21 " + EE['1.3.18.0.2.6.257']="'printerServiceAuxClass' DESC 'Printer information.' SUP printerAbstract AUXILIARY MAY "'( printer-uri $ printer-xri-supported )' + OD['1.3.18.0.2.6.257']="'printerServiceAuxClass' DESC 'Printer information.' SUP printerAbstract AUXILIARY MAY "'( printer-uri $ printer-xri-supported )'" X-ORIGIN 'RFC 3712'" + EE['1.3.18.0.2.6.256']="'printerIPP' DESC 'Internet Printing Protocol (IPP) information.' SUP top AUXILIARY MAY "'( printer-ipp-versions-supported $ printer-multiple-document-jobs-supported )' + OD['1.3.18.0.2.6.256']="'printerIPP' DESC 'Internet Printing Protocol (IPP) information.' SUP top AUXILIARY MAY "'( printer-ipp-versions-supported $ printer-multiple-document-jobs-supported )'" X-ORIGIN 'RFC 3712'" - # Set _EXT to use as shortcut. - _EXT="cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config" + EE['1.3.18.0.2.6.253']="'printerLPR' DESC 'LPR information.' SUP top AUXILIARY MUST printer-name MAY printer-aliases" + OD['1.3.18.0.2.6.253']="'printerLPR' DESC 'LPR information.' SUP top AUXILIARY MUST ( printer-name ) MAY ( printer-aliases ) X-ORIGIN 'RFC 3712'" + EE['1.3.6.1.4.1.42.2.27.5.2.14']="'sunPrinter' DESC 'Sun printer information' SUP top AUXILIARY MUST printer-name MAY "'( sun-printer-bsdaddr $ sun-printer-kvp )' + OD['1.3.6.1.4.1.42.2.27.5.2.14']="'sunPrinter' DESC 'Sun printer information' SUP top AUXILIARY MUST printer-name MAY "'(sun-printer-bsdaddr $ sun-printer-kvp)'" X-ORIGIN 'Solaris Specific'" - # Display message to id current step. - ${ECHO} " ${STEP}. Processing VLV indexes:" - STEP=`expr $STEP + 1` + EE['1.3.6.1.4.1.42.2.27.5.2.12']="'nisplusTimeZoneData' DESC 'NIS+ timezone table data' SUP top STRUCTURAL MUST cn MAY "'( nisplusTimeZone $ description )' + OD['1.3.6.1.4.1.42.2.27.5.2.12']="'nisplusTimeZoneData' DESC 'NIS+ timezone table data' SUP top STRUCTURAL MUST cn MAY "'( nisplusTimeZone $ description )'" X-ORIGIN 'Solaris Specific'" - # Reset temp file for vlvindex commands. - [ -f ${TMPDIR}/ds5_vlvindex_list ] && rm ${TMPDIR}/ds5_vlvindex_list - touch ${TMPDIR}/ds5_vlvindex_list - [ -f ${TMPDIR}/ds6_vlvindex_list ] && rm ${TMPDIR}/ds6_vlvindex_list - touch ${TMPDIR}/ds6_vlvindex_list + EE['1.3.6.1.4.1.42.2.27.5.2.8']="'ipTnetTemplate' DESC 'Object class for TSOL network templates' SUP top MUST ipTnetTemplateName MAY SolarisAttrKeyValue" + OD['1.3.6.1.4.1.42.2.27.5.2.8']="'ipTnetTemplate' DESC 'Object class for TSOL network templates' SUP top STRUCTURAL MUST ipTnetTemplateName MAY SolarisAttrKeyValue X-ORIGIN 'Solaris Specific'" - # Get the instance name from iDS server. - _INSTANCE="" # Default to old output. + EE['1.3.6.1.4.1.42.2.27.5.2.9']="'ipTnetHost' DESC 'Associates an IP address or wildcard with a TSOL template_name' SUP top AUXILIARY MUST ipTnetNumber" + OD['1.3.6.1.4.1.42.2.27.5.2.9']="'ipTnetHost' DESC 'Associates an IP address or wildcard with a TSOL template_name' SUP top AUXILIARY MUST ipTnetNumber X-ORIGIN 'Solaris Specific'" - eval "${LDAPSEARCH} -v ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-instancedir | ${GREP} 'nsslapd-instancedir=' | cut -d'=' -f2- > ${TMPDIR}/instance_name 2>&1" + # obey CLI syntax force option (if any) + if (( TMPF[SYNTAX] == 1 )); then + typeset -n MAP=OD + elif (( TMPF[SYNTAX] == 2 )); then + typeset -n MAP=EE + elif (( TMPF[IS_OPENDJ] )); then + typeset -n MAP=OD + else + typeset -n MAP=EE + fi - ${GREP} "slapd-" ${TMPDIR}/instance_name > /dev/null 2>&1 # Check if seems right? - if [ $? -eq 0 ]; then # If success, grab name after "slapd-". - _INST_DIR=`cat ${TMPDIR}/instance_name` - _INSTANCE=`basename "${_INST_DIR}" | cut -d'-' -f2-` - fi + # we need to preserve order, so 2 passes + typeset TODO='' OID + for OID in ${!MAP[@]}; do + [[ -n ${OID2ODEF[${OID}]} ]] && continue + TODO+="${OID} " + done - # For loop to create indexes. - for p in ${_INDEXES}; do - [ $DEBUG -eq 1 ] && ${ECHO} " Adding index for ${i}" + if [[ -z ${TODO} ]]; then + X='Schema contains all required objectclass defintions' + else + # 2nd pass + typeset X SORTED + SORTED=${ print ${TODO// /$'\n'} | sort -n -t. ; } + TODO='' TODO2='' + for OID in ${SORTED} ; do + X="${MAP[${OID}]}" + OID2ODEF["${OID}"]="${X}" + # 1.3.18.0.2.6.254 requires 1.3.18.0.2.6.2549 + # 1.3.18.0.2.6.255,1.3.18.0.2.6.257 require 1.3.18.0.2.6.258 + [[ ${OID} == '1.3.18.0.2.6.254' \ + || ${OID} == '1.3.18.0.2.6.255' \ + || ${OID} == '1.3.18.0.2.6.257' ]] \ + && TODO2+='objectclasses: ( '"${OID} NAME ${X}"' )\n' \ + || TODO+='objectclasses: ( '"${OID} NAME ${X}"' )\n' + done - # Break p (pair) into i and j parts. - i=`${ECHO} $p | cut -d';' -f1` - j=`${ECHO} $p | cut -d';' -f2` - k=`${ECHO} $p | cut -d';' -f3` - m=`${ECHO} $p | cut -d';' -f4` - - # Set _jEXT to use as shortcut. - _jEXT="cn=${j},${_EXT}" - - # Check if entry exists first, if so, skip to next. - ${LDAPSEARCH} ${SERVER_ARGS} -b "cn=${i},${_jEXT}" -s base "objectclass=*" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - # Display index skipped. - ${ECHO} " ${i} vlv_index skipped already exists" - continue + nextFile modify $0 + print 'dn: cn=schema\nchangetype: modify\nadd: objectclasses' \ + "\n${TODO}${TODO2}" >${TMP[FILE]} + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Adding objectclass definitions to schema failed' + return 67 + fi + X='Schema objectclass definitions added' fi - # Compute the VLV Scope from the LDAP_SEARCH_SCOPE. - # NOTE: A value of "base (0)" does not make sense. - case "$LDAP_SEARCH_SCOPE" in - sub) VLV_SCOPE="2" ;; - *) VLV_SCOPE="1" ;; - esac + showProgress "${X}." + return 0 +} - # Here doc to create LDIF. - ( cat <${TMP[FILE]} + else + print ' +dn: cn="'"${STR[LDAP_SUFFIX]}"'",cn=mapping tree,cn=config +objectclass: top +objectclass: extensibleObject +objectclass: nsMappingTree +cn: '"${STR[LDAP_SUFFIX]}"' +nsslapd-state: backend +nsslapd-backend: '"${STR[DS_DB]}"' -dn: cn=${i},${_jEXT} -cn: ${i} -vlvSort: cn uid +dn: cn='"${STR[DS_DB]}"',cn=ldbm database,cn=plugins,cn=config objectclass: top -objectclass: vlvIndex -EOF -) > ${TMPDIR}/vlv_index_${i} +objectclass: extensibleObject +objectclass: nsBackendInstance +cn: '"${STR[DS_DB]}"' +nsslapd-suffix: '"${STR[LDAP_SUFFIX]}"' +' >${TMP[FILE]} + fi - # Add the index. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/vlv_index_${i} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding VLV index for ${i} failed!" - cleanup - exit 1 + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Unable to create backend' "'${STR[DS_DB]}'" \ + 'with suffix' "'${STR[LDAP_SUFFIX]}'" 'due to server error.' + return 1 + fi + showProgress "Database backend created." fi - # Print message that index was created. - ${ECHO} " ${i} vlv_index Entry created" - - # Add command to list of vlvindex commands to run. - ${ECHO} " directoryserver -s ${_INSTANCE} vlvindex -n ${IDS_DATABASE} -T ${i}" >> ${TMPDIR}/ds5_vlvindex_list - ${ECHO} " /bin/dsadm reindex -l -t ${i} ${LDAP_SUFFIX}" >> ${TMPDIR}/ds6_vlvindex_list - done + if (( ! TMPF[NEED_CREATE_SUFFIX] )); then + showProgress "Suffix exists." + else + nextFile add $0 + print ' +dn: '"${STR[LDAP_SUFFIX]}"' +objectclass: top +objectclass: '"${TMP[SUFFIX_OBJ]}"' +'"${TMP[SUFFIX_ATT]}: ${TMP[SUFFIX_VAL]}"' +' >${TMP[FILE]} + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.warn 'Unable to create entry' "'${STR[LDAP_SUFFIX]}'" 'of' \ + "'${TMP[SUFFIX_OBJ]}'" 'class' + return 1 + fi + showProgress "Suffix added." + fi + return 0 } +function add_suffix_aci { + typeset PW_STATE PW_STATE_RO ANY_NO_READ SELF_NO_WRITE + typeset ADMIN_DN ADMIN_GROUP_DN + if (( TMPF[IS_OPENDJ] )); then + # TBD: include Password Policy State extended ops ? -# -# display_vlv_cmds(): Display VLV index commands to run on server. -# -display_vlv_cmds() -{ - if [ -s "${TMPDIR}/ds5_vlvindex_list" -o \ - -s "${TMPDIR}/ds6_vlvindex_list" ]; then - display_msg display_vlv_list - fi + # password state information aka ds-pwp-*, pwd* and ds-pta-* WITHOUT + # attrs, which are schema flagged with NO-USER-MODIFICATION + PW_STATE=' ds-pwp-account-disabled || ds-pwp-account-expiration-time + || ds-pwp-last-login-time || ds-pwp-password-changed-by-required-time + || ds-pwp-password-policy-dn || ds-pwp-reset-time || ds-pwp-warned-time + || ds-privilege-name || pwdReset' + # password state information flagged with NO-USER-MODIFICATION + # ds-pwp-password-expiration-time is the same as pwdExpirationTime + # so save some parse time ;-) + PW_STATE_RO=' pwdExpirationTime || pwdChangedTime || pwdGraceUseTime + || pwdFailureTime || pwdHistory || pwdAccountLockedTime' + # People using PassThroughAuthentication probably wish that too: + #PW_STATE_RO+='|| ds-pta-cached-password || ds-pta-cached-password-time' - if [ -s "${TMPDIR}/ds5_vlvindex_list" ]; then - cat ${TMPDIR}/ds5_vlvindex_list - fi + # passwords + password state + ANY_NO_READ="userPassword || authPassword ||${PW_STATE}||${PW_STATE_RO}" + # aci + search limits aka ds-rlim-* + RW password state/policy + SELF_NO_WRITE='aci || ds-rlim-idle-time-limit + || ds-rlim-lookthrough-limit || ds-rlim-size-limit || ds-rlim-time-limit + || pwdPolicySubentry ||'"${PW_STATE}" + ADMIN_DN='uid=admin,cn=Administrators' + ADMIN_GROUP_DN='cn=Administrators' + ADMIN_DN+=',cn=admin data' + ADMIN_GROUP_DN+=',cn=admin data' + else + # DSEE + # password state information aka passwordObject + PW_STATE=' accountUnlockTime || passwordAllowChangeTime + || passwordExpWarned || passwordExpirationTime || passwordHistory + || passwordRetryCount || retryCountResetTime' - cat << EOF + # passwords + password state + ANY_NO_READ="userPassword ||${PW_STATE}" + # aci + search limits + password state/policy + SELF_NO_WRITE='aci || nsIdleTimeout || nsLookThroughLimit + || nsSizeLimit || nsTimeLimit || nsroledn || passwordPolicySubentry + ||'"${PW_STATE}" + ADMIN_DN='uid=admin,ou=Administrators' + ADMIN_GROUP_DN='cn=Configuration Administrators,ou=Groups' + ADMIN_DN+=',ou=TopologyManagement,o=NetscapeRoot' + ADMIN_GROUP_DN+=',ou=TopologyManagement,o=NetscapeRoot"' + fi + typeset -A RULES=( + [SFX_ANYONE_ACI_NAME]='aci: (targetattr != "'"${ANY_NO_READ//$'\n'}"'") + ( + version 3.0; acl "'"${SFX_ANYONE_ACI_NAME}"'"; + allow (read, search, compare) userdn = "ldap:///anyone"; + )' + [SFX_SELF_ACI_NAME]='aci: (targetattr != "'"${SELF_NO_WRITE//$'\n'}"'") + ( + version 3.0; acl "'"${SFX_SELF_ACI_NAME}"'"; + allow (write) userdn = "ldap:///self"; + )' + [SFX_ADMIN_ACI_NAME]='aci: (targetattr = "*") + ( + version 3.0; acl "'"${SFX_ADMIN_ACI_NAME}"'"; + allow (all) userdn = "ldap:///'"${ADMIN_DN}"'"; + )' + [SFX_ADMINGRP_ACI_NAME]='aci: (targetattr ="*") + ( + version 3.0; acl "'"${SFX_ADMINGRP_ACI_NAME}"'"; + allow (all) groupdn = "ldap:///'"${ADMIN_GROUP_DN}"'"; + )' + ) -EOF + # Check and set if doesn't already exist + typeset -a LIST=( ) + getACIs LIST "${STR[LDAP_SUFFIX]}" - if [ -s "${TMPDIR}/ds6_vlvindex_list" ]; then - cat ${TMPDIR}/ds6_vlvindex_list - fi + for ACI in "${!RULES[@]}" ; do + typeset -n NAME=${ACI} + [[ -z ${NAME} ]] && continue # if it has no name, ignore it + PATTERN='acl[ ]+"?'"${NAME}"'"?' + findACI LIST "${NAME}" "${PATTERN}" 'Suffix' || continue # exists + nextFile add "${0}-${NAME}" + print ' +dn: '"${STR[LDAP_SUFFIX]}"' +changetype: modify +add: aci +'"${RULES[${ACI}]}"' +' >${TMP[FILE]} + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.warn "Adding '${NAME}' suffix ACI failed" + return 1 + fi + showProgress "Suffix ACI '${NAME}' added." + done + return 0 } -# -# keep_backward_compatibility(): Modify schema for the backward compatibility if -# there are the incompatible attributes already -# -keep_backward_compatibility() -{ - ${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \ - \"objectclass=*\" attributeTypes | ${GREP} -i memberGid-oid ${VERB}" - if [ $? -eq 0 ]; then - ${SED} -e 's/1\.3\.6\.1\.4\.1\.42\.2\.27\.5\.1\.30\ /memberGid-oid\ /' \ - ${TMPDIR}/schema_attr > ${TMPDIR}/schema_attr.new - ${MV} ${TMPDIR}/schema_attr.new ${TMPDIR}/schema_attr - fi - - ${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \ - \"objectclass=*\" attributeTypes | ${GREP} -i rfc822mailMember-oid \ - ${VERB}" - if [ $? -eq 0 ]; then - ${SED} -e \ - 's/1\.3\.6\.1\.4\.1\.42\.2\.27\.2\.1\.15\ /rfc822mailMember-oid\ /' \ - ${TMPDIR}/schema_attr > ${TMPDIR}/schema_attr.new - ${MV} ${TMPDIR}/schema_attr.new ${TMPDIR}/schema_attr - fi +Man.addFunc add_base_objects '' '[+NAME?add_base_objects - add possibly missing, necessary base objects.] +[+DESCRIPTION?Determine all RDNs required to form the \bSTR[LDAP_BASEDN]]\b using the suffix \bSTR[LDAP_SUFFIX]]\b (RDN1,RDN2,...,suffix == baseDN) and create the missing entries.] +[+RETURN VALUES]{ + [+0?on success (all entries available or created successfully).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bnormalizeDN()\b, \bldapsearch\b(1), \bldapmodify\b(1).] +' +function add_base_objects { + # typeset -A STR + # STR[LDAP_BASEDN]=dc=x,dc=my,dc=do,dc=main,dc=de + # STR[LDAP_SUFFIX]=dc=do,dc=main,dc=de -# -# update_schema_attr(): Update Schema to support Naming. -# -update_schema_attr() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In update_schema_attr()" + # Convert to lower case for basename. + typeset LC_DN=','${ normalizeDN "${STR[LDAP_BASEDN]}" l ; } + typeset LC_SFX=','${ normalizeDN "${STR[LDAP_SUFFIX]}" l ; } - ( cat <". For example: "300> 300> dpi>".' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} ) -attributetypes: ( 1.3.18.0.2.4.1120 NAME 'printer-print-quality-supported' DESC 'List of print qualities supported for printing documents on this printer. For example: "draft, normal". Legal values include; "unknown", "draft", "normal", "high".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} ) -attributetypes: ( 1.3.18.0.2.4.1110 NAME 'printer-job-priority-supported' DESC 'Indicates the number of job priority levels supported. An IPP conformant printer which supports job priority must always support a full range of priorities from "1" to "100" (to ensure consistent behavior), therefore this attribute describes the "granularity". Legal values of this attribute are from "1" to "100".' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) -attributetypes: ( 1.3.18.0.2.4.1118 NAME 'printer-copies-supported' DESC 'The maximum number of copies of a document that may be printed as a single job. A value of "0" indicates no maximum limit. A value of "-1" indicates unknown.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) -attributetypes: ( 1.3.18.0.2.4.1111 NAME 'printer-job-k-octets-supported' DESC 'The maximum size in kilobytes (1,024 octets actually) incoming print job that this printer will accept. A value of "0" indicates no maximum limit. A value of "-1" indicates unknown.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) -attributetypes: ( 1.3.18.0.2.4.1112 NAME 'printer-current-operator' DESC 'The name of the current human operator responsible for operating this printer. It is suggested that this string include information that would enable other humans to reach the operator, such as a phone number.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE ) -attributetypes: ( 1.3.18.0.2.4.1113 NAME 'printer-service-person' DESC 'The name of the current human service person responsible for servicing this printer. It is suggested that this string include information that would enable other humans to reach the service person, such as a phone number.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE ) -attributetypes: ( 1.3.18.0.2.4.1114 NAME 'printer-delivery-orientation-supported' DESC 'The possible delivery orientations of pages as they are printed and ejected from this printer. Legal values include; "unknown", "face-up", and "face-down".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} ) -attributetypes: ( 1.3.18.0.2.4.1115 NAME 'printer-stacking-order-supported' DESC 'The possible stacking order of pages as they are printed and ejected from this printer. Legal values include; "unknown", "first-to-last", "last-to-first".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} ) -attributetypes: ( 1.3.18.0.2.4.1116 NAME 'printer-output-features-supported' DESC 'The possible output features supported by this printer. Legal values include; "unknown", "bursting", "decollating", "page-collating", "offset-stacking".' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} ) -attributetypes: ( 1.3.18.0.2.4.1108 NAME 'printer-aliases' DESC 'Site-specific administrative names of this printer in addition the printer name specified for printer-name.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{127} ) -attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.63 NAME 'sun-printer-bsdaddr' DESC 'Sets the server, print queue destination name and whether the client generates protocol extensions. "Solaris" specifies a Solaris print server extension. The value is represented by the following value: server "," destination ", Solaris".' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) -attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.64 NAME 'sun-printer-kvp' DESC 'This attribute contains a set of key value pairs which may have meaning to the print subsystem or may be user defined. Each value is represented by the following: key "=" value.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) -attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.57 NAME 'nisplusTimeZone' DESC 'tzone column from NIS+ timezone table' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) -attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.67 NAME 'ipTnetTemplateName' DESC 'Trusted Solaris network template template_name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) -attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.68 NAME 'ipTnetNumber' DESC 'Trusted Solaris network template ip_address' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) -EOF -) > ${TMPDIR}/schema_attr + # first, test that baseDN ends with suffix + if [[ ${LC_DN: -${#LC_SFX}} != ${LC_SFX} ]]; then + # should not happen since check_basedn_suffix() succeeded + Log.fatal "Invalid suffix '${LC_SFX}' for Base DN '${LC_DN}'" + return 66 + fi + # Save the stuff before LC_SFX w/o leading ',' -> further called prefix + LC_DN=${LC_DN:1:${#LC_DN}-${#LC_SFX}-1} - keep_backward_compatibility + # Remove redundant spaces around ',' and '=' first from LDAP_BASEDN + typeset DN=${ normalizeDN ${STR[LDAP_BASEDN]} ; } - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/schema_attr ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of schema attributes failed!" - cleanup - exit 1 - fi + # Now LC_DN and DN differ at most in lower vs. uppercase character and + # we can get the prefix of the baseDN by just copying corresponding chars + DN=",${DN:0:${#LC_DN}}" - # Display message that schema is updated. - ${ECHO} " ${STEP}. Schema attributes have been updated." - STEP=`expr $STEP + 1` -} + if [[ ${DN} == ',' ]]; then + X='No need to create a DN component (baseDN equals suffix)' + else + typeset LAST=${ normalizeDN ${STR[LDAP_SUFFIX]} ; } DC KEY VAL CLASS + X="Created DN components for ${DN#,}" + while [[ -n ${DN} ]]; do + # Get trailing key=val (DC) from DN and strip it off + DN=${DN%,*} + DC="${.sh.match#,}" + LAST="${DC},${LAST}" + + # Check if entry exists first, if so, skip to next. + ${LDAPSEARCH} ${CON_ARGS} -b "${LAST}" -s base \ + 'objectclass=*' >/dev/null 2>&1 && continue + + VAL=${DC#*=} + KEY=${.sh.match%=} + # Determine the objectclass for the entry. + CLASS=${ get_objectclass ${KEY} ; } + if [[ -z ${CLASS} ]]; then + Log.fatal "Unable to determine objectClass for '${KEY}'." \ + "Please create the following entry and re-run ${PROG}:" \ + "${KEY}=${VAL},${LAST}" + return 66 + fi + nextFile add "${0}-${LAST}" + print ' +dn: '"${LAST}"' +'"${KEY}: ${VAL}"' +objectClass: top +objectClass: '"${CLASS}"' +' > ${TMP[FILE]} -# -# update_schema_obj(): Update the schema objectclass definitions. -# -update_schema_obj() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In update_schema_obj()" + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]}\ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Update of base objects '${DC}' failed" + return 66 + fi + done + fi + showProgress "${X}." + return 0 +} - # Add the objectclass definitions. - ( cat <= 66?a fatal error occured.] +} +[+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] +' +function set_nisdomain { + # Check if nisDomain is already set + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${STR[LDAP_BASEDN]}" \ + -s base 'objectclass=*' 2>/dev/null | \ + while read LINE ; do + if [[ ${LINE} == ~(Ei)^nisDomain= ]]; then + showProgress 'NisDomainObject was already set for' \ + "'${STR[LDAP_BASEDN]}'." + return 0 + fi + done -dn: cn=schema + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.1.1.2.15 NAME 'nisDomainObject' SUP top MUST nisDomain ) +objectclass: nisDomainObject +nisdomain: '"${STR[LDAP_DOMAIN]}"' +' >${TMP[FILE]} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.1.1.2.16 NAME 'automountMap' SUP top MUST automountMapName MAY description ) + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Update of NisDomainObject in '${STR[LDAP_BASEDN]}' failed" + return 66 + fi -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.1.1.2.17 NAME 'automount' SUP top MUST ( automountKey $ automountInformation ) MAY description ) + showProgress "NisDomainObject added to '${STR[LDAP_BASEDN]}'." + return 0 +} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.7 NAME 'SolarisNamingProfile' SUP top MUST ( cn $ SolarisLDAPservers $ SolarisSearchBaseDN ) MAY ( SolarisBindDN $ SolarisBindPassword $ SolarisAuthMethod $ SolarisTransportSecurity $ SolarisCertificatePath $ SolarisCertificatePassword $ SolarisDataSearchDN $ SolarisSearchScope $ SolarisSearchTimeLimit $ SolarisPreferredServer $ SolarisPreferredServerOnly $ SolarisCacheTTL $ SolarisSearchReferral ) ) +Man.addFunc add_new_containers '' '[+NAME?add_new_containers - Add top level containers to the base DN.] +[+DESCRIPTION?Add the Name Service Switch top level containers to the \bSTR[LDAP_BASEDN]]\b unless they already exist.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\b/etc/nsswitch.ldap\b, \bldapsearch\b(1), \bldapmodify\b(1).] +' +function add_new_containers { + typeset OU -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 2.16.840.1.113730.3.2.4 NAME 'mailGroup' SUP top MUST mail MAY ( cn $ mgrpRFC822MailMember ) ) + for OU in people group rpc protocols networks netgroup \ + aliases hosts services ethers profile printers projects \ + SolarisAuthAttr SolarisProfAttr Timezone ipTnet + do + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "ou=${OU},${STR[LDAP_BASEDN]}" -s base 'objectclass=*' \ + >/dev/null 2>&1 + then + showProgress "'ou=${OU}' exists." + continue + fi -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' SUP top MUST cn MAY rfc822mailMember ) + nextFile add "${0}-${OU}" + print ' +dn: ou='"${OU},${STR[LDAP_BASEDN]}"' +ou: '"${OU}"' +objectClass: top +objectClass: organizationalUnit +' > ${TMP[FILE]} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.1.2.6 NAME 'nisNetId' SUP top MUST cn MAY ( nisNetIdUser $ nisNetIdGroup $ nisNetIdHost ) ) + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding 'ou=${OU}' failed" + return 66 + fi + showProgress "'ou=${OU}' added." + done -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.2 NAME 'SolarisAuditUser' SUP top AUXILIARY MAY ( SolarisAuditAlways $ SolarisAuditNever ) ) + return 0 +} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.3 NAME 'SolarisUserAttr' SUP top AUXILIARY MAY ( SolarisUserQualifier $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrKeyValue ) ) +Man.addFunc add_auto_maps '' '[+NAME?add_auto_maps - Add the automount map entries.] +[+DESCRIPTION?Add the automount maps \bauto_home\b, \bauto_direct\b, \bauto_master\b, \bauto_shared\b to \bSTR[LDAP_BASEDN]]\b unless they already exist.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] +' +function add_auto_maps { + # AUTO_MAPS for maps to create + typeset AUTO_MAPS="auto_home auto_direct auto_master auto_shared" MAP -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.4 NAME 'SolarisAuthAttr' SUP top MUST cn MAY ( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrShortDesc $ SolarisAttrLongDesc $ SolarisAttrKeyValue ) ) + for MAP in ${AUTO_MAPS}; do + # Check if automap already exist + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "automountMapName=${MAP},${STR[LDAP_BASEDN]}" -s base \ + 'objectclass=*' >/dev/null 2>&1 + then + showProgress "'${MAP}' automount exists." + continue + fi -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.5 NAME 'SolarisProfAttr' SUP top MUST cn MAY ( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrLongDesc $ SolarisAttrKeyValue ) ) + nextFile add "${0}-${MAP}" + print ' +dn: automountMapName='"${MAP},${STR[LDAP_BASEDN]}"' +automountMapName: '"${MAP}"' +objectClass: top +objectClass: automountMap +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${MAP}' automount map failed" + return 66 + fi + showProgress "'${MAP}' automount added." + done -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.6 NAME 'SolarisExecAttr' SUP top AUXILIARY MAY ( SolarisKernelSecurityPolicy $ SolarisProfileType $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisProfileID $ SolarisAttrKeyValue ) ) + return 0 +} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.1 NAME 'SolarisProject' SUP top MUST ( SolarisProjectID $ SolarisProjectName ) MAY ( memberUid $ memberGid $ description $ SolarisProjectAttr ) ) +Man.addFunc modify_top_aci '' '[+NAME?modify_top_aci - add a deny self modify ACI.] +[+DESCRIPTION?Add the ACI \bUSER_ACI_NAME\b to \bSTR[LDAP_BASEDN]]\b to disable self modify of user attributes like uid, uidNumber, gidNumber, shadowExpire, etc. This does nothing if an ACI with the same name already exists for this entry.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage USER_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (ACI already exists or added successfully).] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\bgetACIs()\b, \bfindACI()\b, \bldapmodify\b(1).] +' +function modify_top_aci { + typeset PATTERN -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.11.1.3.1.2.4 NAME 'DUAConfigProfile' SUP top DESC 'Abstraction of a base configuration for a DUA' MUST cn MAY ( defaultServerList $ preferredServerList $ defaultSearchBase $ defaultSearchScope $ searchTimeLimit $ bindTimeLimit $ credentialLevel $ authenticationMethod $ followReferrals $ serviceSearchDescriptor $ serviceCredentialLevel $ serviceAuthenticationMethod $ objectclassMap $ attributeMap $ profileTTL ) ) + typeset -a LIST=( ) + getACIs LIST || return 66 -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.2549 NAME 'slpService' DESC 'DUMMY definition' SUP top MUST objectclass ) + # check, whether ACI already exists + PATTERN='acl[ ]+"?('"${USER_ACI_NAME}|${USER_ACI_NAME// /_}"')"?' + findACI LIST "${USER_ACI_NAME}" "${PATTERN}" || return 0 # exists -dn: cn=schema + # Create LDIF for top level ACI + nextFile modify $0 + print ' +dn: '"${STR[LDAP_BASEDN]}"' changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.254 NAME 'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' SUP slpService AUXILIARY ) +add: aci +aci: (targetattr = "cn || uid || uidNumber || gidNumber || homeDirectory || shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || memberUid || SolarisAttrKeyValue || SolarisAttrReserved1 || SolarisAttrReserved2 || SolarisUserQualifier") + ( + version 3.0; acl "'"${USER_ACI_NAME}"'"; + deny (write) userdn = "ldap:///self"; + ) +' > ${TMP[FILE]} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.258 NAME 'printerAbstract' DESC 'Printer related information.' SUP top ABSTRACT MAY ( printer-name $ printer-natural-language-configured $ printer-location $ printer-info $ printer-more-info $ printer-make-and-model $ printer-multiple-document-jobs-supported $ printer-charset-configured $ printer-charset-supported $ printer-generated-natural-language-supported $ printer-document-format-supported $ printer-color-supported $ printer-compression-supported $ printer-pages-per-minute $ printer-pages-per-minute-color $ printer-finishings-supported $ printer-number-up-supported $ printer-sides-supported $ printer-media-supported $ printer-media-local-supported $ printer-resolution-supported $ printer-print-quality-supported $ printer-job-priority-supported $ printer-copies-supported $ printer-job-k-octets-supported $ printer-current-operator $ printer-service-person $ printer-delivery-orientation-supported $ printer-stacking-order-supported $ printer-output-features-supported ) ) + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Deny user to change non-password attributes failed' + return 66 + fi -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.255 NAME 'printerService' DESC 'Printer information.' SUP printerAbstract STRUCTURAL MAY ( printer-uri $ printer-xri-supported ) ) + showProgress "Self modify for non-password attributes disabled." + return 0 +} -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.257 NAME 'printerServiceAuxClass' DESC 'Printer information.' SUP printerAbstract AUXILIARY MAY ( printer-uri $ printer-xri-supported ) ) +Man.addFunc add_vlv_aci '' '[+NAME?add_vlv_aci - Add ACI for VLV.] +[+DESCRIPTION?Add the global ACI to allow everyone read-only access to VLVs.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage VLV_ACI_NAME ; }" '} +[+RETURN VALUES]{ + [+0?on success (ACI added successfully).] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\bldapmodify\b(1).] +' +function add_vlv_aci { + # VLV Request = 2.16.840.1.113730.3.4.9 + typeset RULE='version 3.0; acl "'"${VLV_ACI_NAME}"'";\n\t' + RULE+='allow (read,search,compare) userdn = "ldap:///anyone";' + typeset DN='oid=2.16.840.1.113730.3.4.9,cn=features,cn=config' + (( TMPF[IS_OPENDJ] )) && DN='cn=Access Control Handler,cn=config' -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.256 NAME 'printerIPP' DESC 'Internet Printing Protocol (IPP) information.' SUP top AUXILIARY MAY ( printer-ipp-versions-supported $ printer-multiple-document-jobs-supported ) ) + typeset -a LIST=( ) + getACIs LIST "${DN}" 'Global' || return 66 -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.18.0.2.6.253 NAME 'printerLPR' DESC 'LPR information.' SUP top AUXILIARY MUST printer-name MAY printer-aliases ) + PATTERN='acl[ ]+"?('"${VLV_ACI_NAME}|${VLV_ACI_NAME// /_}"')"?' + findACI LIST "${VLV_ACI_NAME}" "${PATTERN}" 'Global' || return 0 -dn: cn=schema + nextFile modify $0 + if (( TMPF[IS_OPENDJ] )); then + print ' +dn: '"${DN}"' changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.14 NAME 'sunPrinter' DESC 'Sun printer information' SUP top AUXILIARY MUST printer-name MAY ( sun-printer-bsdaddr $ sun-printer-kvp ) ) - -dn: cn=schema +add: ds-cfg-global-aci +ds-cfg-global-aci: (targetcontrol = "2.16.840.1.113730.3.4.9")(targetattr != "aci") + ( + '"${RULE}"' + ) +' > ${TMP[FILE]} + else + print ' +dn: '"${DN}"' changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.12 NAME 'nisplusTimeZoneData' DESC 'NIS+ timezone table data' SUP top STRUCTURAL MUST cn MAY ( nisplusTimeZone $ description ) ) +replace: aci +aci: (targetattr != "aci") + ( + '"${RULE}"' + ) +' > ${TMP[FILE]} + fi -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.8 NAME 'ipTnetTemplate' DESC 'Object class for TSOL network templates' SUP top MUST ipTnetTemplateName MAY SolarisAttrKeyValue ) + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${VLV_ACI_NAME}' global ACI failed" + return 66 + fi -dn: cn=schema -changetype: modify -add: objectclasses -objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.9 NAME 'ipTnetHost' DESC 'Associates an IP address or wildcard with a TSOL template_name' SUP top AUXILIARY MUST ipTnetNumber ) -EOF -) > ${TMPDIR}/schema_obj + showProgress "Global ACI '${VLV_ACI_NAME}' added." + return 0 +} - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/schema_obj ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of schema objectclass definitions failed!" - cleanup - exit 1 - fi - - # Display message that schema is updated. - ${ECHO} " ${STEP}. Schema objectclass definitions have been added." - STEP=`expr $STEP + 1` +Man.addFunc add_proxyagent '' '[+NAME?add_proxyagent - Add proxy agent user to DS.] +[+DESCRIPTION?Add the proxy agent user \bSTR[LDAP_PROXYAGENT]]\b to the DS unless it already exists to allow nameservice access to the server.] +[+RETURN VALUES]{ + [+0?on success (entry already exists or added successfully).] + [+>= 66?a fatal error occured.] } +[+SEE ALSO?\bldapmodify\b(1).] +' +function add_proxyagent { + # Check if proxy agent already exists + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "${STR[LDAP_PROXYAGENT]}" -s base 'objectclass=*' >/dev/null 2>&1 + then + showProgress "Proxyagent identity exists." + return 0 + fi -# -# modify_top_aci(): Modify the ACI for the top entry to disable self modify -# of user attributes. -# -modify_top_aci() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In modify_top_aci()" + # Get cn and sn names from LDAP_PROXYAGENT. + typeset NAME="${STR[LDAP_PROXYAGENT]%%,*}" + NAME=${NAME#*=} - # Set ACI Name - ACI_NAME="LDAP_Naming_Services_deny_write_access" + # Add the entry + nextFile add $0 + print ' +dn: '"${STR[LDAP_PROXYAGENT]}"' +cn: '"${NAME}"' +sn: '"${NAME}"' +objectclass: top +objectclass: person +userpassword: '"${STR[LDAP_PROXYAGENT_CRED]}"' +' > ${TMP[FILE]} + + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Adding proxyagent identity failed' + return 66 + fi # Search for ACI_NAME eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_top_aci 2>&1" @@ -4409,966 +4557,1087 @@ # Set ACI Name ACI_NAME="LDAP_Naming_Services_deny_write_access" - # Search for ACI_NAME - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_top_aci 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} "Error searching aci for ${LDAP_BASEDN}" - cat ${TMPDIR}/chk_top_aci - cleanup - exit 1 - fi - ${GREP} "${ACI_NAME}" ${TMPDIR}/chk_top_aci > /dev/null 2>&1 - if [ $? -eq 0 ]; then - ${ECHO} " ${STEP}. Top level ACI ${ACI_NAME} already exists for ${LDAP_BASEDN}." - STEP=`expr $STEP + 1` + showProgress 'Proxyagent identity added.' return 0 - fi +} - # Crate LDIF for top level ACI. - ( cat < ${TMPDIR}/top_aci - - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/top_aci ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Modify of top level ACI failed! (restricts self modify)" - cleanup - exit 1 +Man.addFunc add_entry_by_DN '' '[+NAME?add_entry_by_DN - Add an ldif file by DN.] +[+DESCRIPTION?Add the entries in the given LDIF \afile\a to the DS unless a base entry for \aDN\a already exists.] +[+RETURN VALUES]{ + [+0?on success (\aDN\a already exists or added successfully).] + [+1?on error (failed to add entries).] +} +[+SEE ALSO?\bldapsearch\b(1), \bldapadd\b(1).] +\n\n\aDN\a \afile\a +' +function add_entry_by_DN { + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "$1" -s base \ + 'objectclass=*' >/dev/null 2>&1 + then + showProgress "'${1}' exists." + return 0 fi - - # Display message that ACI is updated. - MSG="ACI for ${LDAP_BASEDN} modified to disable self modify." - if [ $EXISTING_PROFILE -eq 1 ];then - ${ECHO} " ACI SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` - fi + if ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f "$2" >&${TMPF[FD]} 2>&1 + then + showProgress "'${1}' added." + return 0 + fi + Log.fatal "Adding '${1}' failed" + return 1 } -# -# find_and_delete_ACI(): Find an ACI in file $2 with a matching pattern $1. -# Delete the ACI and print a message using $3 as the ACI name. $3 is needed -# because it could have a different value than that of $1. -find_and_delete_ACI() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In find_and_delete_ACI" +Man.addFunc add_id_mapping_rules '' '[+NAME?add_id_mapping_rules - Add Kerberos principal to DN mapping rules to the DS.] +[+DESCRIPTION?Add GSSAPI identity mapping rules for host credentails and user credentials to the DS config unless they already exists. Hosts are matched against \b*.STR[LDAP_DOMAIN]]@STR[LDAP_KRB_REALM]]\b and ou=hosts in \bSTR[LDAP_BASEDN]]\b, users against \b*.@STR[LDAP_KRB_REALM]]\b and uid=$1,ou=People in \bSTR[LDAP_BASEDN]]\b.] +[+SEE ALSO?\badd_entry_by_DN()\b.] +' +function add_id_mapping_rules { + Log.info 'Adding Kerberos principal to DN mapping rules ...' + typeset C_DN='cn=GSSAPI,cn=identity mapping,cn=config' OC='nsContainer' + if (( TMPF[IS_OPENDJ] )); then + # we create a new branch instead of reusing the default, possibly unused + # 'cn=Regular Expression,cn=Identity Mappers,cn=config' GSSAPI ID mapper + C_DN='cn=GSSAPI,cn=Identity Mappers,cn=config' + OC='ds-cfg-branch' + fi - # if an ACI with pattern $1 exists in file $2, delete it from ${LDAP_BASEDN} - ${EGREP} $1 $2 | ${SED} -e 's/aci=//' > ${TMPDIR}/grep_find_delete_aci 2>&1 - if [ -s ${TMPDIR}/grep_find_delete_aci ]; then - aci_to_delete=`${CAT} ${TMPDIR}/grep_find_delete_aci` + nextFile add "${0}-krbPrincipal" + print ' +dn: cn='"${C_DN}"' +objectClass: top +objectClass: '"${OC}"' +cn: GSSAPI +' > ${TMP[FILE]} + add_entry_by_DN "${C_DN}" ${TMP[FILE]} || return - # Create the tmp file to delete the ACI. - ( cat < ${TMPDIR}/find_delete_aci - - # Delete the ACI - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/find_delete_aci ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Remove of $3 ACI failed!" - cleanup - exit 1 + typeset H_CN="host_auth_${STR[LDAP_KRB_REALM]}" + typeset H_DN="cn=${H_CN}, ${C_DN}" + nextFile add "${0}-krbHostAuth" + if (( TMPF[IS_OPENDJ] )); then + print ' +# For now (2.6.0) OpenDJ supports a single-valued ds-cfg-identity-mapper, only. +# So either you merge this and the next Identity Mapper to something more useful +# together or just enable one of them until it gets fixed. For more information +# see https://bugster.forgerock.org/jira/browse/OPENDJ-521 +dn: '"${H_DN}"' +cn: '"${H_CN}"' +objectClass: top +objectClass=ds-cfg-identity-mapper +objectClass=ds-cfg-regular-expression-identity-mapper +ds-cfg-java-class=org.opends.server.extensions.RegularExpressionIdentityMapper +ds-cfg-enabled=false +ds-cfg-match-base-dn=ou=hosts,'"${STR[LDAP_BASEDN]}"' +ds-cfg-match-pattern=host\/(.*).'"${STR[LDAP_DOMAIN]}@${STR[LDAP_KRB_REALM]}"' +ds-cfg-replace-pattern=$1 +ds-cfg-match-attribute=cn +' > ${TMP[FILE]} + else + print ' +dn: '"${H_DN}"' +objectClass: top +objectClass: nsContainer +objectClass: dsIdentityMapping +objectClass: dsPatternMatching +cn: '"${H_CN}"' +dsMatching-pattern: ${Principal} +dsMatching-regexp: host\/(.*).'"${STR[LDAP_DOMAIN]}@${STR[LDAP_KRB_REALM]}"' +dsSearchBaseDN: ou=hosts,'"${STR[LDAP_BASEDN]}"' +dsSearchFilter: (&(objectClass=ipHost)(cn=$1)) +dsSearchScope: one +' > ${TMP[FILE]} fi + add_entry_by_DN "${H_DN}" ${TMP[FILE]} - ${RM} -f ${TMPDIR}/find_delete_aci - # Display message that an ACL is deleted. - MSG="ACI $3 deleted." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " ACI DELETED: $MSG" + typeset U_CN="user_auth_${STR[LDAP_KRB_REALM]}" + typeset U_DN="cn=${U_CN}, ${C_DN}" + nextFile add "${0}-krbUserAuth" + if (( TMPF[IS_OPENDJ] )); then + print ' +dn: '"${U_DN}"' +cn: '"${U_CN}"' +objectClass: top +objectClass=ds-cfg-identity-mapper +objectClass=ds-cfg-regular-expression-identity-mapper +ds-cfg-java-class=org.opends.server.extensions.RegularExpressionIdentityMapper +ds-cfg-enabled=true +ds-cfg-match-base-dn=ou=People,'"${STR[LDAP_BASEDN]}"' +ds-cfg-match-pattern=(.*)@'"${STR[LDAP_KRB_REALM]}"' +ds-cfg-replace-pattern=$1 +ds-cfg-match-attribute=uid +' > ${TMP[FILE]} else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` + print ' +dn: '"${U_DN}"' +objectClass: top +objectClass: nsContainer +objectClass: dsIdentityMapping +objectClass: dsPatternMatching +cn: '"${U_CN}"' +dsMatching-pattern: ${Principal} +dsMatching-regexp: (.*)@'"${STR[LDAP_KRB_REALM]}"' +dsMappedDN: uid=$1,ou=People,'"${STR[LDAP_BASEDN]}"' +' > ${TMP[FILE]} fi - fi + add_entry_by_DN "${U_DN}" ${TMP[FILE]} } -# -# Add an ACI to deny non-admin access to shadow data when -# shadow update is enabled. -# -deny_non_admin_shadow_access() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In deny_non_admin_shadow_access()" +Man.addFunc modify_userpassword_acl_for_gssapi '' '[+NAME?modify_userpassword_acl_for_gssapi - Allow hosts and user to read password(s).] +[+DESCRIPTION?Modify ACL to allow hosts to read all and a user to read its own password only, when sasl/GSSAPI bind is used.] +[+RETURN VALUES]{ + [+0?on success (entries already exist or added successfully).] + [+>= 66?a fatal error occured.] +} +[+SEE ALSO?\badd_entry_by_DN()\b.] +' +function modify_userpassword_acl_for_gssapi { + typeset P_DN="ou=People,${STR[LDAP_BASEDN]}" + typeset H_DN="ou=Hosts,${STR[LDAP_BASEDN]}" - # Set ACI Names - ACI_TO_ADD="LDAP_Naming_Services_deny_non_admin_shadow_access" - ACI_TO_DEL="LDAP_Naming_Services_deny_non_host_shadow_access" + if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${P_DN}" -s base \ + 'objectclass=*' > /dev/null 2>&1 + then + Log.verbose "'${P_DN}' does not exist" - # Search for ACI_TO_ADD - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_aci_non_admin 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} "Error searching aci for ${LDAP_BASEDN}" - cleanup - exit 1 - fi + nextFile add $0 + print ' +dn: '"${P_DN}"' +ou: People +objectClass: top +objectClass: organizationalUnit +' > ${TMP[FILE]} - # If an ACI with ${ACI_TO_ADD} already exists, we are done. - ${EGREP} ${ACI_TO_ADD} ${TMPDIR}/chk_aci_non_admin 2>&1 > /dev/null - if [ $? -eq 0 ]; then - MSG="ACI ${ACI_TO_ADD} already set for ${LDAP_BASEDN}." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " NOT SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` + add_entry_by_DN "${P_DN}" ${TMP[FILE]} + else + Log.verbose "'${P_DN}' already exists" fi - return 0 - fi + typeset TARGET='userPassword' PATTERN + (( TMPF[IS_OPENDJ] )) && TARGET+=' || authPassword' - # The deny_non_admin_shadow_access and deny_non_host_shadow_access ACIs - # should be mutually exclusive, so if the latter exists, delete it. - find_and_delete_ACI ${ACI_TO_DEL} ${TMPDIR}/chk_aci_non_admin ${ACI_TO_DEL} + typeset -A RULES=( + [SELF_GSS_ACI_NAME]='aci: (targetattr = "'"${TARGET}"'") + ( + version 3.0; acl self-read-pwd; + allow (read,search) userdn="ldap:///self" and authmethod="sasl GSSAPI"; + )' + [HOST_GSS_ACI_NAME]='aci: (targetattr = "'"${TARGET}"'") + ( + version 3.0; acl host-read-pwd; + allow (read,search) userdn="ldap:///cn=*+ipHostNumber=*,ou=Hosts,'"${STR[LDAP_BASEDN]}"'" and authmethod="sasl GSSAPI"; + )' + ) - # Create the tmp file to add. - ( cat < ${TMPDIR}/non_admin_aci_write - - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/non_admin_aci_write ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding ACI ${ACI_TO_ADD} failed!" - ${CAT} ${TMPDIR}/non_admin_aci_write - cleanup - exit 1 - fi +'"${RULES[${ACI}]}"' +' > ${TMP[FILE]} - ${RM} -f ${TMPDIR}/non_admin_aci_write - # Display message that the non-admin access to shadow data is denied. - MSG="Non-Admin access to shadow data denied." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " ACI SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` - fi + if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.warn "Adding '${NAME}' ou=people ACI failed." + return 67 + fi + showProgress "ou=people ACI '${NAME}' added." + done + return 0 } -# -# Add an ACI to deny non-host access to shadow data when -# shadow update is enabled and auth Method if gssapi. -# -deny_non_host_shadow_access() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In deny_non_host_shadow_access()" +Man.addFunc add_profile '' '[+NAME?add_profile - Add client profile to server.] +[+DESCRIPTION?Generates a client profile and adds it to the DS as "\bcn=STR[LDAP_PROFILE_NAME]],ou=profile,STR[LDAP_BASEDN]]\b" unless it already exists and \bTMPF[DEL_OLD_PROFILE]]\b is not set. Other profile related variables are:]{ + [+?STR[LDAP_SERVER_LIST]] ] + [+?STR[LDAP_SEARCH_SCOPE]] ] + [+?STR[LDAP_CRED_LEVEL]] ] + [+?STR[LDAP_AUTHMETHOD]] ] + [+?INT[LDAP_FOLLOWREF]] ] + [+?INT[LDAP_SEARCH_TIME_LIMIT]] ] + [+?INT[LDAP_PROFILE_TTL]] ] + [+?INT[LDAP_BIND_LIMIT]] ] + [+?STR[LDAP_PREF_SRVLIST]] ] + [+?STR[LDAP_SRV_AUTHMETHOD_PAM]] ] + [+?STR[LDAP_SRV_AUTHMETHOD_KEY]] ] + [+?STR[LDAP_SRV_AUTHMETHOD_CMD]] ] + [+?SSD] +} +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?fatal error.] +} +[+SEE ALSO?\bldapclient\b(1M), \bldapdelete\b(1), \bldapmodify\b(1).] +' +function add_profile { + # If profile name already exists, DELETE it, and add new one + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "cn=${STR[LDAP_PROFILE_NAME]},ou=profile,${STR[LDAP_BASEDN]}" \ + -s base 'objectclass=*' >/dev/null 2>&1 + then + if (( ! TMPF[DEL_OLD_PROFILE] )); then + Log.fatal "Adding client profile name '${STR[LDAP_PROFILE_NAME]}'" \ + 'failed (entry already exists)' + return 66 + fi - # Set ACI Names - ACI_TO_ADD="LDAP_Naming_Services_deny_non_host_shadow_access" - ACI_TO_DEL="LDAP_Naming_Services_deny_non_admin_shadow_access" - - # Search for ACI_TO_ADD - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_aci_non_host 2>&1" - if [ $? -ne 0 ]; then - ${ECHO} "Error searching aci for ${LDAP_BASEDN}" - cleanup - exit 1 - fi - - # If an ACI with ${ACI_TO_ADD} already exists, we are done. - ${EGREP} ${ACI_TO_ADD} ${TMPDIR}/chk_aci_non_host 2>&1 > /dev/null - if [ $? -eq 0 ]; then - MSG="ACI ${ACI_TO_ADD} already set for ${LDAP_BASEDN}." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " NOT SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` + nextFile delete $0 + print "cn=${STR[LDAP_PROFILE_NAME]},ou=profile,${STR[LDAP_BASEDN]}" \ + >${TMP[FILE]} + if ! ${LDAPDELETE} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Attempt to DELETE old client profile' \ + "'${STR[LDAP_PROFILE_NAME]}' failed" + return 67 + fi fi - return 0 - fi - # The deny_non_admin_shadow_access and deny_non_host_shadow_access ACIs - # should be mutually exclusive, so if the former exists, delete it. - find_and_delete_ACI ${ACI_TO_DEL} ${TMPDIR}/chk_aci_non_host ${ACI_TO_DEL} + # Build the "ldapclient genprofile" command string to execute + typeset -a ARG=( ) + ARG+=( genprofile '-a' "profileName=${STR[LDAP_PROFILE_NAME]}" ) - # Create the tmp file to add. - ( cat < ${TMPDIR}/non_host_aci_write - - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/non_host_aci_write ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding ACI ${ACI_TO_ADD} failed!" - ${CAT} ${TMPDIR}/non_host_aci_write - cleanup - exit 1 - fi + # Add required argument defaultSearchBase + ARG+=( -a "defaultSearchBase=${STR[LDAP_BASEDN]}" ) - ${RM} -f ${TMPDIR}/non_host_aci_write - # Display message that the non-host access to shadow data is denied. - MSG="Non-host access to shadow data is denied." - if [ $EXISTING_PROFILE -eq 1 ]; then - ${ECHO} " ACI SET: $MSG" - else - ${ECHO} " ${STEP}. $MSG" - STEP=`expr $STEP + 1` - fi -} + # Add optional parameters + [[ -n ${STR[LDAP_SERVER_LIST]} ]] && \ + ARG+=( -a "defaultServerList=${STR[LDAP_SERVER_LIST]}" ) + [[ -n ${STR[LDAP_SEARCH_SCOPE]} ]] && \ + ARG+=( -a "defaultSearchScope=${STR[LDAP_SEARCH_SCOPE]}" ) + [[ -n ${STR[LDAP_CRED_LEVEL]} ]] && \ + ARG+=( -a "credentialLevel=${STR[LDAP_CRED_LEVEL]}" ) + [[ -n ${STR[LDAP_AUTHMETHOD]} ]] && \ + ARG+=( -a "authenticationMethod=${STR[LDAP_AUTHMETHOD]}" ) + (( INT[LDAP_FOLLOWREF] )) && X='TRUE"' || X='FALSE"' + ARG+=( -a "followReferrals=${X}" ) + (( INT[LDAP_SEARCH_TIME_LIMIT] )) && \ + ARG+=( -a "searchTimeLimit=${INT[LDAP_SEARCH_TIME_LIMIT]}" ) + (( INT[LDAP_PROFILE_TTL] )) && \ + ARG+=( -a "profileTTL=${INT[LDAP_PROFILE_TTL]}" ) + [[ -n ${INT[LDAP_BIND_LIMIT]} ]] && \ + ARG+=( -a "bindTimeLimit=${INT[LDAP_BIND_LIMIT]}" ) + [[ -n ${STR[LDAP_PREF_SRVLIST]} ]] && \ + ARG+=( -a "preferredServerList=${STR[LDAP_PREF_SRVLIST]}" ) + [[ -n ${STR[LDAP_SRV_AUTHMETHOD_PAM]} ]] && \ + ARG+=(-a "serviceAuthenticationMethod=${STR[LDAP_SRV_AUTHMETHOD_PAM]}") + [[ -n ${STR[LDAP_SRV_AUTHMETHOD_KEY]} ]] && \ + ARG+=(-a "serviceAuthenticationMethod=${STR[LDAP_SRV_AUTHMETHOD_KEY]}") + [[ -n ${STR[LDAP_SRV_AUTHMETHOD_CMD]} ]] && \ + ARG+=(-a "serviceAuthenticationMethod=${STR[LDAP_SRV_AUTHMETHOD_CMD]}") -# -# add_vlv_aci(): Add access control information (aci) for VLV. -# -add_vlv_aci() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_vlv_aci()" + # Add SSDs + typeset X + for X in "${SSD[@]}" ; do + ARG+=( -a "serviceSearchDescriptor=${X}" ) + done - # Add the VLV ACI. - ( cat < ${TMPDIR}/vlv_aci + # Execute "ldapclient genprofile" to create profile + nextFile add $0 + if ! ${LDAPCLIENT} "${ARG[@]}" >${TMP[FILE]} 2>${TMP[DIR]}/gen_profile.err + then + Log.fatal 'ldapclient genprofile failed' + return 68 + fi - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/vlv_aci ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Add of VLV ACI failed!" - cleanup - exit 1 - fi + # Add the generated profile + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + log.fatal 'Attempt to add profile failed!' + return 69 + fi - # Display message that schema is updated. - ${ECHO} " ${STEP}. Add of VLV Access Control Information (ACI)." - STEP=`expr $STEP + 1` + showProgress 'Client profile generated and pushed to the DS.' + return 0 } - -# -# set_nisdomain(): Add the NisDomainObject to the Base DN. -# -set_nisdomain() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In set_nisdomain()" - - # Check if nisDomain is already set. - ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base \ - \"objectclass=*\"" > ${TMPDIR}/chk_nisdomain 2>&1 - ${EVAL} "${GREP} -i nisDomain ${TMPDIR}/chk_nisdomain ${VERB}" - if [ $? -eq 0 ]; then - ${ECHO} " ${STEP}. NisDomainObject for ${LDAP_BASEDN} was already set." - STEP=`expr $STEP + 1` - return 0 - fi - - # Add the new top level containers. - ( cat < ${TMPDIR}/nis_domain - - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/nis_domain ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of NisDomainObject in ${LDAP_BASEDN} failed." - cleanup - exit 1 - fi - - # Display message that schema is updated. - ${ECHO} " ${STEP}. NisDomainObject added to ${LDAP_BASEDN}." - STEP=`expr $STEP + 1` +Man.addFunc checkTaskCompletion '' '[+NAME?checkTaskCompletion - Wait and check completion of a DS task.] +[+DESCRIPTION?Checks the state of the DS task with the given \adn\a periodically until it got stopped due to an error or has been finished successfully, or the timeout has been hit. Timeout is an integer and specifies the number of seconds to max. wait for completion. If not given or invalid 60 will be used instead.] +[+RETURN VALUES]{ + [+0?on success.] + [+1?task failed.] + [+2?timeout has been hit, task not yet finished or has an unknown state.] } +[+See Also?\bldapsearch\b(1)] +\n\n\adn\a [\atimeout\a] +' +function checkTaskCompletion { + typeset DN="$1" STATUS='nstaskstatus=' TASKID X + integer TIMEOUT=${2:-${INT[TASK_TIMEOUT]}} RESULT=2 SEEN + (( TIMEOUT < 10 )) && TIMEOUT=60 + (( TMPF[IS_OPENDJ] )) && STATUS='ds-task-state=' + TASKID=${DN%%,*} + # Wait for task to finish, display current status. + while (( TIMEOUT > 0 )) ; do + SEEN=0 X='' + ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "${DN}" -s base 'objectclass=*' ${STATUS%=} 2>/dev/null | \ + while read LINE ; do + [[ ${LINE%%,*} == ${TASKID} ]] && SEEN=1 && continue + [[ -z ${X} && ${LINE:0:${#STATUS}} == ${STATUS} ]] && \ + X=${LINE:${#STATUS}} + done + (( ! SEEN )) && break # an error occured + print -n '.' + [[ ${X} =~ Finished || ${X} == 'COMPLETED_SUCCESSFULLY' ]] && \ + RESULT=0 && break + [[ ${X:0:12} == 'Index failed' || ${X:016} == 'STOPPED_BY_ERROR' ]] && \ + RESULT=1 && break + sleep 1 + (( TIMEOUT-=1 )) + done + # DSEE removes non-recurring tasks automagically, OpenDJ not + (( RESULT < 2 && TMPF[IS_OPENDJ] )) && \ + ${LDAPDELETE} ${CON_ARGS} "${AUTH_ARGS[@]}" "${DN}" >&${TMPF[FD]} 2>&1 + return ${RESULT} +} - -# -# check_attrName(): Check that the attribute name is valid. -# $1 Key to check. -# Returns 0 : valid name 1 : invalid name -# -check_attrName() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In check_attrName()" - [ $DEBUG -eq 1 ] && ${ECHO} "check_attrName: Input Param = $1" - - ${ECHO} $1 | ${EGREP} '^[0-9]+(\.[0-9]+)*$' > /dev/null 2>&1 - if [ $? -eq 0 ]; then - ${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \"objectclass=*\" \ - attributeTypes | ${EGREP} -i '^attributetypes[ ]*=[ ]*\([ ]*$1 ' ${VERB}" - else - ${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \"objectclass=*\" \ - attributeTypes | ${EGREP} -i \"'$1'\" ${VERB}" - fi - - if [ $? -ne 0 ]; then - return 1 - else - return 0 - fi +Man.addFunc rebuildIndex '' '[+NAME?rebuildIndex - Rebuild a single index.] +[+DESCRIPTION?Instruct the DS to rebuild the index with the name \aname\a. If the \aname\a starts with "vlv." it indicates a Virtual List View, which is supported for OpenDJ, only. Wrt. to OpenDJ and VLVs a return code of \b0\b does not necessaryly mean, that the index is finally in a non-degraded state. It just means, the task has been executed successfully.] +[+RETURN VALUES]{ + [+0?on success.] + [+1?if a VLV index was specified, but the current DS is != OpenDJ/OpenDS.] + [+2?if the task failed or did not complete within 60 seconds.] + [+66?if scheduling a rebuild task on the DS failed.] } +[+SEE ALSO?\bcheckTaskCompletion()\b, \bldapsearch\b(1), \bldapmodify\b(1).] +\n\n\aname\a +' +function rebuildIndex { + typeset IDX=$1 + TASKNAME=${IDX}_${ date "+%Y_%m_%d_%H_%M_%S" ; } + if [[ ${IDX:0:4} == 'vlv.' ]] && (( ! TMPF[IS_OPENDJ] )) ; then + Log.warn 'Non-OpenDJ/OpenDS servers do not support VLV rebuildTasks' + return 1 + fi -# -# get_objectclass(): Determine the objectclass for the given attribute name -# $1 Attribute name to check. -# _ATTR_NAME Return value, Object Name or NULL if unknown to idsconfig. -# -# NOTE: An attribute name can be valid but still we might not be able -# to determine the objectclass from the table. -# In such cases, the user needs to create the necessary object(s). -# -get_objectclass() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In get_objectclass()" - [ $DEBUG -eq 1 ] && ${ECHO} "get_objectclass: Input Param = $1" + nextFile add "${0}-task-${TASKNAME}" + if (( TMPF[IS_OPENDJ] )); then + # NOTE: rebuildTask requires ldif-import LDAP privilege. + # It's a little bit inefficient: one could schedule a single task at + # the end of the function and use 'rebuildall' or a space separated + # list of all attributes in ds-task-rebuild-index to do it at once. + DN='ds-task-id='"${TASKNAME}"',cn=Scheduled Tasks,cn=Tasks' + print ' +# rebuild-index --index '"${IDX}"' --baseDN '"${STR[LDAP_BASEDN]}"' \ +# '"${CON_ARGS} ${AUTH_ARGS[@]}"' -X - # Set return value to NULL string. - _ATTR_NAME="" +dn: '"${DN}"' +ds-task-id: '"${TASKNAME}"' +objectClass: top +objectClass: ds-task +objectClass: ds-task-rebuild +ds-task-class-name: org.opends.server.tasks.RebuildTask +ds-task-rebuild-base-dn: '"${STR[LDAP_SUFFIX]}"' +ds-task-rebuild-index: '"${IDX}"' +#ds-task-rebuild-tmp-directory: +' > ${TMP[FILE]} + else + DN='cn='"${TASKNAME}"',cn=index,cn=tasks,cn=config' + print ' +dn: '"${DN}"' +cn: '"${TASKNAME}"' +objectclass: top +objectclass: extensibleObject +nsInstance: '"${STR[DS_DB]}"' +nsIndexAttribute: '"${IDX}"' +' > ${TMP[FILE]} + fi - # Test key for type: - case `${ECHO} ${1} | tr '[A-Z]' '[a-z]'` in - ou | organizationalunitname | 2.5.4.11) _ATTR_NAME="organizationalUnit" ;; - dc | domaincomponent | 0.9.2342.19200300.100.1.25) _ATTR_NAME="domain" ;; - o | organizationname | 2.5.4.10) _ATTR_NAME="organization" ;; - c | countryname | 2.5.4.6) _ATTR_NAME="country" ;; - *) _ATTR_NAME="" ;; - esac + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding task for '${IDX}' failed" + return 66 + fi - [ $DEBUG -eq 1 ] && ${ECHO} "get_objectclass: _ATTR_NAME = $_ATTR_NAME" + checkTaskCompletion "${DN}" && print "" || { Log.warn 'FAILED'; return 2; } + return 0 } +Man.addFunc add_indexes '' '[+NAME?add_indexes - Add indexes.] +[+DESCRIPTION?Add indexes of the given \atypes\a (e.g. "pres eq") for the given \aattributes\a (e.g. "uidNumber gidnumber") and schedule corresponding tasks to actually create the indexes unless they already exist. Both arguments are handle as a whitespace separated list.] +[+RETURN VALUES]{ + [+-1?syntax error (insufficient parameters).] + [+0?on success.] + [+>= 66?fatal error.] +} +[+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] +\n\n\atypes\a \aattributes\a +' +function add_indexes { + if [[ -z ${STR[DS_DB]} ]]; then + get_backend || return 66 + fi + [[ -z $1 || -z $2 ]] && Log.warn "${.sh.fun}(): syntax error" && return -1 -# -# add_base_objects(): Add any necessary base objects. -# -add_base_objects() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_base_objects()" + typeset -l TYPES=${1//,/ } # we allow comma separated lists as well + typeset IDX=${2//,/ } - # Convert to lower case for basename. - format_string "${LDAP_BASEDN}" - LOWER_BASEDN="${FMT_STR}" - format_string "${LDAP_SUFFIX}" - LOWER_SUFFIX="${FMT_STR}" + Log.verbose "Processing '${TYPES// /,}' indexes ..." - [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_BASEDN: ${LOWER_BASEDN}" - [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_SUFFIX: ${LOWER_SUFFIX}" + typeset DN="cn=index,cn=${STR[DS_DB]},cn=ldbm database,cn=plugins,cn=config" + typeset LTYPE="nsIndexType: ${TYPES// /$'\n'nsIndexType: }" + typeset ATTR='cn' OC='extensibleObject' DST TASKNAME LINE X + integer SEEN + if (( TMPF[IS_OPENDJ] )); then + OC='ds-cfg-branch' + DN="cn=Index,ds-cfg-backend-id=${STR[DS_DB]},cn=Backends,cn=config" + ATTR='ds-cfg-attribute' + LTYPE='' + for X in ${TYPES}; do + [[ $X == 'eq' ]] && LTYPE+='ds-cfg-index-type: equality\n' + [[ $X == 'pres' ]] && LTYPE+='ds-cfg-index-type: presence\n' + [[ $X == 'sub' ]] && LTYPE+='ds-cfg-index-type: substring\n' + done + fi - # Create additional components. - if [ "${LOWER_BASEDN}" = "${LOWER_SUFFIX}" ]; then - [ $DEBUG -eq 1 ] && ${ECHO} "Base DN and Suffix equivalent" - else - # first, test that the suffix is valid - dcstmp=`basename "${LOWER_BASEDN}" "${LOWER_SUFFIX}"` - if [ "$dcstmp" = "${LOWER_BASEDN}" ]; then - # should not happen since check_basedn_suffix() succeeded - ${ECHO} "Invalid suffix ${LOWER_SUFFIX}" - ${ECHO} "for Base DN ${LOWER_BASEDN}" - cleanup - exit 1 + # check, whether Index container exists - if not, create it + if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${DN}" \ + -s base 'objectclass=*' > /dev/null 2>&1 + then + nextFile add $0 + X=${DN%%,*} + print ' +dn: '"${DN}"' +objectClass: top +objectClass: '"${OC}"' +cn: '"${X#*=}"' +' >${TMP[FILE]} + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Failed to add backend Index base' + return 66 + fi fi - # OK, suffix is valid, start working with LDAP_BASEDN - # field separator is ',' (i.e., space is a valid character) - dcstmp2="`${ECHO} ${LDAP_BASEDN} | - sed -e 's/[ ]*,[ ]*/,/g' -e 's/[ ]*=[ ]*/=/g'`" - dcs="" - # use dcstmp to count the loop, and dcstmp2 to get the correct - # string case - # dcs should be in reverse order, only for these components - # that need to be added - while [ -n "${dcstmp}" ] - do - i2=`${ECHO} "$dcstmp2" | cut -f1 -d','` - dk=`${ECHO} $i2 | awk -F= '{print $1}'` - dc=`${ECHO} $i2 | awk -F= '{print $2}'` - dcs="$dk=$dc,$dcs"; - dcstmp2=`${ECHO} "$dcstmp2" | cut -f2- -d','` - dcstmp=`${ECHO} "$dcstmp" | cut -f2- -d','` - [ $DEBUG -eq 1 ] && \ - ${ECHO} "dcs: ${dcs}\ndcstmp: ${dcstmp}\ndcstmp2: ${dcstmp2}\n" - done + for DST in ${IDX} ; do + # Check if entry exists first. If so, skip to next + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ + -b "${ATTR}=${DST},${DN}" -s base 'objectclass=*' > /dev/null 2>&1 + then + showProgress "'${DST}' index exists." + continue + fi + nextFile add "${0}-${DST}" + if (( TMPF[IS_OPENDJ] )); then + X=${LTYPE//'\n'/ } + X=${X//: /:} + print ' +# dsconfig create-local-db-index '"${CON_ARGS} ${AUTH_ARGS[@]}"' -X \ +# '"${X//ds-cfg-/--set }"' \ +# --backend-name '"${STR[DS_DB]}"' --index-name '"${DST}"' - lastdc=${LDAP_SUFFIX} - dc=`${ECHO} "${dcs}" | cut -f1 -d','` - dcstmp=`${ECHO} "${dcs}" | cut -f2- -d','` - while [ -n "${dc}" ]; do - # Get Key and component from $dc. - dk2=`${ECHO} $dc | awk -F= '{print $1}'` - dc2=`${ECHO} $dc | awk -F= '{print $2}'` +dn: ds-cfg-attribute='"${DST},${DN}"' +ds-cfg-attribute: '"${DST}"' +objectClass: top +objectClass: ds-cfg-local-db-index +'"${LTYPE}"' +' > ${TMP[FILE]} + else + print ' +dn: cn='"${DST},${DN}"' +objectClass: top +objectClass: nsIndex +cn: '"${DST}"' +nsSystemIndex: false +'"${LTYPE}"' +' > ${TMP[FILE]} + fi + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${DST}' backend index failed" + return 67 + fi + showProgress "'${DST}' backend index added." + rebuildIndex ${DST} + done + return 0 +} - # At this point, ${dk2} is a valid attribute name +Man.addFunc add_eq_indexes '' '[+NAME?add_eq_indexes - Add indexes of type pres and eq.] +[+DESCRIPTION?Add eq,pres indexes and schedule corresponding tasks to actually create the indexes unless they already exist.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?fatal error.] +} +[+SEE ALSO?\badd_indexes()\b.] +' +function add_eq_indexes { + add_indexes "pres eq" \ + "uidNumber ipNetworkNumber gidnumber oncrpcnumber automountKey" +} - # Check if entry exists first, if so, skip to next. - ${LDAPSEARCH} ${SERVER_ARGS} -b "${dk2}=${dc2},$lastdc" -s base "objectclass=*" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - # Set the $lastdc to new dc. - lastdc="${dk2}=${dc2},$lastdc" +Man.addFunc add_sub_indexes '' '[+NAME?add_sub_indexes - Add indexes of type pres, eq and sub.] +[+DESCRIPTION?Add eq,pres,sub indexes and schedule corresponding tasks to actually create the indexes unless they already exist.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?fatal error.] +} +[+SEE ALSO?\badd_indexes()\b.] +' +function add_sub_indexes { + add_indexes "pres eq sub" \ + "ipHostNumber membernisnetgroup nisnetgrouptriple" +} - # Process next component. - dc=`${ECHO} "${dcstmp}" | cut -f1 -d','` - dcstmp=`${ECHO} "${dcstmp}" | cut -f2- -d','` - continue +Man.addFunc add_vlv_indexes_OpenDJ '' '[+NAME?add_vlv_indexes_OpenDJ - Add VLV indexes to OpenDS/OpenDJ.] +[+DESCRIPTION?OpenDS/OpenDJ specialized part of \badd_vlv_indexes()\b (pulled out for easier maintenance).] +[+SEE ALSO?\badd_vlv_indexes()\b, https://blogs.oracle.com/kanthi/entry/ldap_paged_results_more] +[+NOTES?\bpagedResultsControl\b and \bVLV\b is per default allowed for authenticated users, only. To check, try something like this:]{ + [+?ldapsearch -r -j /tmp/pw -D "cn=Directory Manager" -h ldaphost \] + [+? -b "cn=Access Control Handler,cn=config" "objectclass=*" | \] + [+? egrep "1.2.840.113556.1.4.319|2.16.840.1.113730.3.4.9"] +} +\n\n\avname\a \afile\a +' +function add_vlv_indexes_OpenDJ { + typeset -n INDEX_TABLE=$1 + typeset OUT="$2" ENTRY IDX_NAME CN SCOPE='single-level' BASE FILTER - fi + [[ ${STR[LDAP_SEARCH_SCOPE]} == sub ]] && SCOPE='subordinate-subtree' + BACKEND="cn=VLV Index,ds-cfg-backend-id=${STR[DS_DB]},cn=Backends,cn=config" - # Determine the objectclass for the entry. - get_objectclass $dk2 - OBJ_Name=${_ATTR_NAME} - if [ "${OBJ_Name}" = "" ]; then - ${ECHO} "Cannot determine objectclass for $dk2" - ${ECHO} "Please create ${dk2}=${dc2},$lastdc entry and rerun idsconfig" - exit 1 - fi - - # Add the new container. - ( cat < /dev/null 2>&1 + then + nextFile add $0 + print ' +dn: '"${BACKEND}"' objectClass: top -objectClass: ${OBJ_Name} -EOF -) > ${TMPDIR}/base_objects +objectClass: ds-cfg-branch +cn: VLV Index +' >${TMP[FILE]} + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal 'Adding VLV Index base' + return 66 + fi + fi + integer COUNT=0 + # create index entries + for ENTRY in "${INDEX_TABLE[@]}" ; do + typeset -a F=( ${ENTRY} ) # split columns + + IDX_NAME="${STR[LDAP_DOMAIN]}.get${F[0]}" + BASE="ou=${F[2]},${STR[LDAP_BASEDN]}" + [[ ${F[2]} =~ = ]] && BASE="${BASE:3}" # cut out ou= + FILTER="objectClass=${F[3]}" + [[ ${F[3]:0:1} == '&' ]] && FILTER="&(objectClass=${F[3]:2}" - # Set the $lastdc to new dc. - lastdc="${dk2}=${dc2},$lastdc" + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -s base \ + -b "ds-cfg-name=${IDX_NAME},${BACKEND}" 'objectclass=*' \ + > /dev/null 2>&1 + then + showProgress "'${IDX_NAME}' VLV index exists." + continue + fi - # Add the entry. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/base_objects ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: update of base objects ${dc} failed." - cleanup - exit 1 - fi + nextFile add "${0}-${IDX_NAME}" + print ' +# dsconfig create-local-db-vlv-index '"${CON_ARGS} ${AUTH_ARGS[@]}"' -X \ +# --backend-name "'"${STR[DS_DB]}"'" \ +# --set "base-dn:'"${BASE}"'" \ +# --index-name '"${IDX_NAME}"' --set "sort-order:cn uid" \ +# --set "scope:'"${SCOPE}"'" --set "filter:'"${FILTER}"'" - # Display message that schema is updated. - ${ECHO} " ${STEP}. Created DN component ${dc}." - STEP=`expr $STEP + 1` +dn: ds-cfg-name='"${IDX_NAME},${BACKEND}"' +objectClass: top +objectClass: ds-cfg-local-db-vlv-index +ds-cfg-name: '"${IDX_NAME}"' +ds-cfg-base-dn: '"${BASE}"' +ds-cfg-scope: '"${SCOPE}"' +ds-cfg-filter: '"${FILTER}"' +ds-cfg-sort-order: cn uid +aci: (targetattr = "*") + ( + version 3.0; acl "Config"; + allow (read,search,compare) userdn="ldap:///anyone"; + ) +' > ${TMP[FILE]} - # Process next component. - dc=`${ECHO} "${dcstmp}" | cut -f1 -d','` - dcstmp=`${ECHO} "${dcstmp}" | cut -f2- -d','` + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${IDX_NAME}' VLV index failed" + return 67 + fi + showProgress "'${IDX_NAME}' VLV index added." + + if rebuildIndex "vlv.${IDX_NAME}" ; then + (( COUNT++ )) + else + print "rebuild-index -b '${STR[LDAP_SUFFIX]}' " \ + "--index 'vlv.${IDX_NAME}'" >>${OUT} + fi done - fi + if (( COUNT )); then + # Bug or feature: Even if scheduled as task, VLV idx rebuild leaves + # the index in degraded state. So: + showProgress 'Rebuilding degraded indexes.' + if ! rebuildIndex rebuilddegraded ; then + print "svcadm disable opendj@VERS@\n" \ + "rebuild-index -b '${STR[LDAP_SUFFIX]}' --rebuildDegraded\n" \ + "svcadm enable opendj@VERS@" + >>${OUT} + fi + fi } +Man.addFunc add_vlv_indexes_DSEE '' '[+NAME?add_vlv_indexes_DSEE - Add VLV indexes to DSEE.] +[+DESCRIPTION?DSEE specialized part of \badd_vlv_indexes()\b (pulled out for easier maintenance).] +[+SEE ALSO?\b\badd_vlv_indexes()\b.] +\n\n\avname\a \afile\a +' +function add_vlv_indexes_DSEE { + typeset -n INDEX_TABLE=$1 + typeset OUT="$2" ENTRY IDX_NAME CN SCOPE=1 BASE FILTER -# -# add_new_containers(): Add the top level classes. -# -# $1 = Base DN -# -add_new_containers() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_new_containers()" + typeset INFO=( ${TMP[DS_INFO]} ) + integer MAJOR=${INFO[${#INFO[@]}-2]} + typeset INSTANCE="@serverInstance@" LINE + ${LDAPSEARCH} -v ${CON_ARGS} "${AUTH_ARGS[@]}" -b 'cn=config' \ + -s base 'objectclass=*' nsslapd-instancedir 2>/dev/null | \ + while read LINE ; do + [[ ${LINE:0:20} == 'nsslapd-instancedir=' ]] && LINE=${LINE:20} && break + done + if (( MAJOR < 6 )); then + # DSEE 5.x only. + [[ ${LINE} =~ slapd- ]] && LINE=${LINE#*/} && INSTANCE=${LINE#*-} + else + # 6+ - the instance path + INSTANCE="${LINE}" + fi + [[ ${STR[LDAP_SEARCH_SCOPE]} == sub ]] && SCOPE=2 + BACKEND="cn=${STR[DS_DB]},cn=ldbm database,cn=plugins,cn=config" - for ou in people group rpc protocols networks netgroup \ - aliases hosts services ethers profile printers projects \ - SolarisAuthAttr SolarisProfAttr Timezone ipTnet ; do + # create index entries + for ENTRY in "${INDEX_TABLE[@]}" ; do + typeset -a F=( ${ENTRY} ) # split columns + + IDX_NAME="${STR[LDAP_DOMAIN]}.get${F[0]}" + CN="${STR[LDAP_DOMAIN]}_${F[1]}_vlv_index" + BASE="ou=${F[2]},${STR[LDAP_BASEDN]}" + [[ ${F[2]} =~ = ]] && BASE="${BASE:3}" # cut out ou= + FILTER="objectClass=${F[3]}" + [[ ${F[3]:0:1} == '&' ]] && FILTER="&(objectClass=${F[3]:2}" - # Check if nismaps already exist. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"ou=${ou},${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then - continue - fi + if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -s base \ + -b "cn=${IDX_NAME},cn=${CN},${BACKEND}" 'objectclass=*' \ + > /dev/null 2>&1 + then + showProgress "'${IDX_NAME}' VLV index exists." + continue + fi - # Create TMP file to add. - ( cat < ${TMPDIR}/toplevel.${ou} +objectClass: vlvSearch +cn: '"${CN}"' +vlvbase: '"${BASE}"' +vlvscope: '${SCOPE}' +vlvfilter: ('"${FILTER}"') +aci: (target = "ldap:///'"cn=${CN},${BACKEND}"'") (targetattr = "*") + ( + version 3.0; acl "Config"; + allow (read,search,compare) userdn="ldap:///anyone"; + ) - # Add the entry. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/toplevel.${ou} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Add of ou=${ou} container failed!" - cleanup - exit 1 - fi - done +dn: cn='"${IDX_NAME},cn=${CN},${BACKEND}"' +cn: '"${IDX_NAME}"' +vlvSort: cn uid +objectclass: top +objectclass: vlvIndex +' > ${TMP[FILE]} - # Display message that top level OU containers complete. - ${ECHO} " ${STEP}. Top level \"ou\" containers complete." - STEP=`expr $STEP + 1` + if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ + >&${TMPF[FD]} 2>&1 + then + Log.fatal "Adding '${IDX_NAME}' VLV index failed" + return 66 + fi + showProgress "'${IDX_NAME}' VLV index added." + + if (( MAJOR < 6 )); then + # DSEE 5.x + print "directoryserver -s '${INSTANCE}' vlvindex" \ + "-n '${STR[DS_DB]}' -T '${IDX_NAME}'" >> ${OUT} + else + # assume DSEE 6+ + print "dsadm reindex -l -t '${IDX_NAME}'" \ + "'${INSTANCE}' '${STR[LDAP_SUFFIX]}'" >> ${OUT} + fi + done } +Man.addFunc add_vlv_indexes '' '[+NAME?add_vlv_indexes - Add VLV indexes.] +[+DESCRIPTION?Create VLV indexes entries on the \bSTR[DS_DB]]\b for \bSTR[LDAP_DOMAIN]]\b with \bSTR[LDAP_SEARCH_SCOPE]]\b and create the script \bTMP[DIR]]/do_vlv_index\b to be started on the DS to actually re-index the DB.] +[+RETURN VALUES]{ + [+0?on success.] + [+>= 66?fatal error.] +} +[+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] +' +function add_vlv_indexes { + Log.verbose 'Processing VLV indexes ...' -# -# add_auto_maps(): Add the automount map entries. -# -# auto_home, auto_direct, auto_master, auto_shared -# -add_auto_maps() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_auto_maps()" + # F[0] F[1] F[2] F[3] + # ${LDAP_DOMAIN}.get ${LDAP_DOMAIN}_%s_vlv_index ou=%s objectClass=$1||&(%s) + typeset -a INDEXES=( + 'grent group group posixGroup' + 'hostent hosts hosts ipHost' + 'netent networks networks ipNetwork' + 'pwent passwd people posixAccount' + 'rpcent rpc rpc oncRpc' + 'spent shadow people shadowAccount' + # Indexes added during NIS to LDAP transition + 'auhoent auho automountMapName=auto_home automount' + 'soluent solu people SolarisUserAttr' + 'authent auth SolarisAuthAttr SolarisAuthAttr' + 'execent exec SolarisProfAttr &(SolarisExecAttr)(SolarisKernelSecurityPolicy=*)' + 'profent prof SolarisProfAttr &(SolarisProfAttr)(SolarisAttrLongDesc=*)' + 'mailent mail aliases mailGroup' + 'bootent _boot ethers &(bootableDevice)(bootParameter=*)' + 'ethent ethers ethers &(ieee802Device)(macAddress=*)' + 'ngrpent netgroup netgroup nisNetgroup' + 'ipnent ipn networks &(ipNetwork)(cn=*)' + 'maskent mask networks &(ipNetwork)(ipNetmaskNumber=*)' + 'prent pr printers printerService' + 'ip4ent ip4 hosts &(ipHost)(ipHostNumber=*.*)' + 'ip6ent ip6 hosts &(ipHost)(ipHostNumber=*:*)' + ) - # Set AUTO_MAPS for maps to create. - AUTO_MAPS="auto_home auto_direct auto_master auto_shared" + # temp file for vlvindex commands + typeset OUT=${TMP[DIR]}/tmp + rm -f ${OUT} && touch ${OUT} - for automap in $AUTO_MAPS; do - # Check if automaps already exist. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"automountMapName=${automap},${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then - continue + if (( TMPF[IS_OPENDJ] )); then + add_vlv_indexes_OpenDJ INDEXES ${OUT} + else + add_vlv_indexes_DSEE INDEXES ${OUT} fi - - # Create the tmp file to add. - ( cat < ${TMPDIR}/automap.${automap} - - # Add the entry. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/automap.${automap} ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Add of automap ${automap} failed!" - cleanup - exit 1 - fi - done - - # Display message that automount entries are updated. - ${ECHO} " ${STEP}. automount maps: $AUTO_MAPS processed." - STEP=`expr $STEP + 1` + nextFile sh + mv ${OUT} ${TMP[FILE]} + TMP[VLV_CMDS]=${TMP[FILE]} } +Man.addFunc display_vlv_cmds '' '[+NAME?display_vlv_cmds - Display VLV index commands to run on server.] +[+DESCRIPTION?Display VLV index commands to run on server and save the file to /var/tmp/doIndexVLV-\bSTR[DS_HOST]].sh\b.] +' +function display_vlv_cmds { + typeset SAV="/var/tmp/doIndexVLV-${STR[DS_HOST]}.sh" -# -# add_proxyagent(): Add entry for nameservice to use to access server. -# -add_proxyagent() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_proxyagent()" + [[ -s ${TMP[VLV_CMDS]} ]] || return + # some problems occured, so the file is not empty + [[ -e ${SAV} ]] && SAV="${SAV%.sh}-$$.sh" + cp -p ${TMP[VLV_CMDS]} "${SAV}" - # Check if proxy agent already exists. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_PROXYAGENT}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then - ${ECHO} " ${STEP}. Proxy Agent ${LDAP_PROXYAGENT} already exists." - STEP=`expr $STEP + 1` - return 0 + typeset MSG='NOTE: '"${PROG}"'configured the entries for VLV indexes.' + if (( TMPF[IS_OPENDJ] )); then + MSG+=' +To create the actual VLV indexes, you need to stop the DS on the host +'"'${STR[DS_HOST]}'"', e.g. using "svcadm disable -t opendj@VERS@" +or, if manually started, "ds-stop" and than run the commands shown in the file +'"'${SAV}'"'. Than restart the DS e.g. using +"svcadm enable opendj@VERS@" or "ds-start". +' + else + typeset INFO=( ${DS_INFO[@]} ) + if (( INFO[1] >= 6 )); then + MSG+=' +Use the dsadm command delivered with the DS on the host '"'${STR[DS_HOST]}'"' +to stop the server. Then, using dsadm, follow the dsadm examples shown in the +file '"'${SAV}'"' to create the actual VLV indexes. +' + else + MSG+=' +Use the directoryserver(1m) script on the host '"'${STR[DS_HOST]}'"' +to stop the server. Then, using directoryserver, follow the directoryserver +examples shown in the file '"'${SAV}'"' to create the +actual VLV indexes. +' + fi fi - - # Get cn and sn names from LDAP_PROXYAGENT. - cn_tmp=`${ECHO} ${LDAP_PROXYAGENT} | cut -f1 -d, | cut -f2 -d=` - - # Create the tmp file to add. - ( cat < ${TMPDIR}/proxyagent - - # Add the entry. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/proxyagent ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Adding proxyagent failed!" - cleanup - exit 1 - fi - - # Display message that schema is updated. - ${ECHO} " ${STEP}. Proxy Agent ${LDAP_PROXYAGENT} added." - STEP=`expr $STEP + 1` + Log.info "\n\n${MSG}" } -# -# allow_proxy_read_pw(): Give Proxy Agent read permission for password. -# -allow_proxy_read_pw() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In allow_proxy_read_pw()" +Man.addFunc doMain '' '[+NAME?doMain - the main entry point.] +[+DESCRIPTION?The entry point, where the real work starts.] +' +function doMain { + # Initialize the variables that need to be set to NULL, or some + # other initial value before the rest of the functions can be called + init || return 1 + show_vars - # Search for ACI_NAME - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_proxyread_aci 2>&1" - ${GREP} "${PROXY_ACI_NAME}" ${TMPDIR}/chk_proxyread_aci > /dev/null 2>&1 - if [ $? -eq 0 ]; then - ${ECHO} " ${STEP}. Proxy ACI ${PROXY_ACI_NAME=} already exists for ${LDAP_BASEDN}." - STEP=`expr $STEP + 1` - return 0 - fi + # Print extra line to separate from prompt + print - # Create the tmp file to add. - ( cat < ${TMPDIR}/proxy_read - - # Add the entry. - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/proxy_read ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Allow ${LDAP_PROXYAGENT} to read password failed!" - cleanup - exit 1 - fi + # Either load the user specified config file or prompt user for config info + if [[ -n ${TMP[IN]} ]]; then + load_config_file "${TMP[IN]}" + validate_info || return 1 # Validate basic info in file + else + display_msg 'backup_server' + get_confirm 'Do you wish to continue with server setup (y/n/h)?' \ + 'n' 'backup_help' && return 1 - # Display message that schema is updated. - ${ECHO} " ${STEP}. Give ${LDAP_PROXYAGENT} read permission for password." - STEP=`expr $STEP + 1` -} + # Ask for all required infos + prompt_config_info || return 1 + (( INT[LDAP_ENABLE_SHADOW_UPDATE] && INT[EXISTING_PROFILE] )) && \ + return 0 # just enabled shadow update in an existing profile + # Allow user to modify results + integer RES + display_summary + RES=$? + # save the work for now, so that even on exit/errors it can be re-used + [[ -n ${TMP[OUT]} ]] && create_config_file "${TMP[OUT]}" + (( RES )) && return 1 + fi -# Delete Proxy Agent read permission for password. -delete_proxy_read_pw() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In delete_proxy_read_pw()" + if (( INT[NEED_TIME] )); then + modify_timelimit || return 1 + fi - # Search for ACI_NAME - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_proxyread_aci 2>&1" - ${GREP} "${PROXY_ACI_NAME}" ${TMPDIR}/chk_proxyread_aci | \ - ${SED} -e 's/aci=//' > ${TMPDIR}/grep_proxyread_aci 2>&1 - if [ $? -ne 0 ]; then - ${ECHO} "Proxy ACI ${PROXY_ACI_NAME} does not exist for ${LDAP_BASEDN}." - return 0 - fi + if (( INT[NEED_SIZE] )); then + modify_sizelimit || return 1 + fi - # We need to remove proxy agent's read access to user passwords, - # but We do not know the value of the ${LDAP_PROXYAGENT} here, so - # 1. if only one match found, delete it - # 2. if more than one matches found, ask the user which one to delete - HOWMANY=`${WC} -l ${TMPDIR}/grep_proxyread_aci | ${NAWK} '{print $1}'` - if [ $HOWMANY -eq 0 ]; then - ${ECHO} "Proxy ACI ${PROXY_ACI_NAME} does not exist for ${LDAP_BASEDN}." - return 0 - fi - if [ $HOWMANY -eq 1 ];then - proxy_aci=`${CAT} ${TMPDIR}/grep_proxyread_aci` - else - ${CAT} << EOF + # Modify the password storage scheme to support CRYPT + if (( INT[NEED_CRYPT] )); then + modify_pwd_crypt || return 1 + fi -Proxy agent is not allowed to read user passwords when shadow -update is enabled. There are more than one proxy agents found. -Please select the currently proxy agent being used, so that -idsconfig can remove its read access to user passwords. + # schema modifications + modify_cn || return 1 + update_schema_attr || return 1 + update_schema_obj || return 1 -The proxy agents are: + add_suffix || return 1 -EOF - # generate the proxy agent list - ${SED} -e "s/.*ldap:\/\/\/.*ldap:\/\/\///" \ - ${TMPDIR}/grep_proxyread_aci | ${SED} -e "s/\";)//" > \ - ${TMPDIR}/proxy_agent_list + # Add missing suffix ACIs + add_suffix_aci || return 1 - # print the proxy agent list - ${NAWK} '{print NR ": " $0}' ${TMPDIR}/proxy_agent_list + # Add base objects (if needed) + add_base_objects || return 1 - # ask the user to pick one - _MENU_PROMPT="Select the proxy agent (1-$HOWMANY): " - get_menu_choice "${_MENU_PROMPT}" "0" "$HOWMANY" - _CH=$MN_CH - proxy_aci=`${SED} -n "$_CH p" ${TMPDIR}/grep_proxyread_aci` - fi + # Update the NisDomainObject. + # The Base DN might of just been created, so this MUST happen after + # the base objects have been added! + set_nisdomain || return 1 - # Create the tmp file to delete the ACI. - ( cat < ${TMPDIR}/proxy_delete + # Add top level classes (new containers) + add_new_containers || return 1 - # Delete the ACI - ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/proxy_delete ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Remove of ${PROXY_ACI_NAME} ACI failed!" - cat ${TMPDIR}/proxy_delete - cleanup - exit 1 - fi + add_auto_maps || return 1 - # Display message that ACI is updated. - MSG="Removed ${PROXY_ACI_NAME} ACI for proxyagent read permission for password." - ${ECHO} " " - ${ECHO} " ACI REMOVED: $MSG" - ${ECHO} " The ACI removed is $proxy_aci" - ${ECHO} " " -} + modify_top_aci || return 1 -# -# add_profile(): Add client profile to server. -# -add_profile() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In add_profile()" + add_vlv_aci || return 1 - # If profile name already exists, DELETE it, and add new one. - eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}" - if [ $? -eq 0 ]; then - # Create Delete file. - ( cat < ${TMPDIR}/del_profile + if (( INT[NEED_PROXY] )); then + add_proxyagent || return 1 + if (( ! INT[LDAP_ENABLE_SHADOW_UPDATE] )); then + allow_proxy_read_pw || return 1 + fi + fi - # Check if DEL_OLD_PROFILE is set. (If not ERROR) - if [ $DEL_OLD_PROFILE -eq 0 ]; then - ${ECHO} "ERROR: Profile name ${LDAP_PROFILE_NAME} exists! Add failed!" - exit 1 + if (( INT[NEED_ADMIN] )); then + add_admin || return 1 + allow_admin_read_write_shadow || return 1 + deny_non_admin_shadow_access || return 1 fi - # Delete the OLD profile. - ${EVAL} "${LDAPDELETE} ${LDAP_ARGS} -f ${TMPDIR}/del_profile ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Attempt to DELETE profile failed!" - cleanup - exit 1 + if (( INT[GSSAPI_ENABLE] )); then + add_id_mapping_rules + # do not modify ACI if "sasl/GSSAPI" and "self" are not selected + if [[ ${STR[LDAP_CRED_LEVEL]} == 'self' && \ + ${STR[LDAP_AUTHMETHOD]} == 'sasl/GSSAPI' ]] + then + modify_userpassword_acl_for_gssapi || return 1 + else + Log.warn 'ACL for GSSAPI was not set because of incompatibility' \ + 'in profile' + fi fi - fi - # Build the "ldapclient genprofile" command string to execute. - GEN_CMD="ldapclient genprofile -a \"profileName=${LDAP_PROFILE_NAME}\"" + if (( INT[NEED_HOSTACL] )); then + allow_host_read_write_shadow || return 66 + deny_non_host_shadow_access || return 67 + fi - # Add required argument defaultSearchBase. - GEN_CMD="${GEN_CMD} -a \"defaultSearchBase=${LDAP_BASEDN}\"" - # Add optional parameters. - [ -n "$LDAP_SERVER_LIST" ] && \ - GEN_CMD="${GEN_CMD} -a \"defaultServerList=${LDAP_SERVER_LIST}\"" - [ -n "$LDAP_SEARCH_SCOPE" ] && \ - GEN_CMD="${GEN_CMD} -a \"defaultSearchScope=${LDAP_SEARCH_SCOPE}\"" - [ -n "$LDAP_CRED_LEVEL" ] && \ - GEN_CMD="${GEN_CMD} -a \"credentialLevel=${LDAP_CRED_LEVEL}\"" - [ -n "$LDAP_AUTHMETHOD" ] && \ - GEN_CMD="${GEN_CMD} -a \"authenticationMethod=${LDAP_AUTHMETHOD}\"" - [ -n "$LDAP_FOLLOWREF" ] && \ - GEN_CMD="${GEN_CMD} -a \"followReferrals=${LDAP_FOLLOWREF}\"" - [ -n "$LDAP_SEARCH_TIME_LIMIT" ] && \ - GEN_CMD="${GEN_CMD} -a \"searchTimeLimit=${LDAP_SEARCH_TIME_LIMIT}\"" - [ -n "$LDAP_PROFILE_TTL" ] && \ - GEN_CMD="${GEN_CMD} -a \"profileTTL=${LDAP_PROFILE_TTL}\"" - [ -n "$LDAP_BIND_LIMIT" ] && \ - GEN_CMD="${GEN_CMD} -a \"bindTimeLimit=${LDAP_BIND_LIMIT}\"" - [ -n "$LDAP_PREF_SRVLIST" ] && \ - GEN_CMD="${GEN_CMD} -a \"preferredServerList=${LDAP_PREF_SRVLIST}\"" - [ -n "$LDAP_SRV_AUTHMETHOD_PAM" ] && \ - GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_PAM}\"" - [ -n "$LDAP_SRV_AUTHMETHOD_KEY" ] && \ - GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_KEY}\"" - [ -n "$LDAP_SRV_AUTHMETHOD_CMD" ] && \ - GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_CMD}\"" + # Generate client profile and add it to the server + add_profile || return 1 - # Check if there are any service search descriptors to ad. - if [ -s "${SSD_FILE}" ]; then - ssd_2_profile - fi + # Add Indexes to improve search performance + add_eq_indexes || return 1 + add_sub_indexes || return 1 + add_vlv_indexes || return 1 - # Execute "ldapclient genprofile" to create profile. - eval ${GEN_CMD} > ${TMPDIR}/gen_profile 2> ${TMPDIR}/gen_profile_ERR - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: ldapclient genprofile failed!" - cleanup - exit 1 - fi + # Display setup complete message + Log.printMarker + Log.info 'Setup of DS server' "'${STR[DS_HOST]}'" 'is complete.' + Log.printMarker - # Add the generated profile.. - ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/gen_profile ${VERB}" - if [ $? -ne 0 ]; then - ${ECHO} " ERROR: Attempt to add profile failed!" - cleanup - exit 1 - fi + # Display VLV index commands to be executed on server (if any) + display_vlv_cmds - # Display message that schema is updated. - ${ECHO} " ${STEP}. Generated client profile and loaded on server." - STEP=`expr $STEP + 1` + # Create final config file if requested + [[ -n ${TMP[OUT]} ]] && create_config_file "${TMP[OUT]}" + return 0 } +Man.addFunc cleanup '' '[+NAME?cleanup - cleanup tempfiles, tty and exit.] +[+DESCRIPTION?Removes \aTMP[DIR]]\a unless \aTMPF[DEBUG]]\a is set and restores the echo for the tty.] +[+ENVIRONMENT VARIABLES]{' "${ Man.varUsage TMP TMPF ; }" '} +' +function cleanup { + (( TMPF[CLEANUP_DONE] )) && return + if (( TMPF[DEBUG] )); then + typeset X=${ ls -1 ${TMP[DIR]} 2>/dev/null ; } + if [[ -z $X || $X == 'rootPWD' ]]; then + rm -rf ${TMP[DIR]} + else + Log.info "Leaving temp dir ${TMP[DIR]} as is. Remove it manually" + fi + elif (( TMPF[KEEP] )); then + typeset X=${ ls -1 ${TMP[DIR]} 2>/dev/null ; } + if [[ -z $X || $X == 'rootPWD' ]]; then + rm -rf ${TMP[DIR]} + else + Log.info "Keeping setup scripts and files in ${TMP[DIR]}" + fi + elif [[ -d ${TMP[DIR]} ]]; then + rm -rf ${TMP[DIR]} + fi + ${STTY:-/usr/bin/stty} echo + trap - EXIT 1 2 3 6 15 + # Might be trapped in the scope of a function which will stop executing + # the function itself but not the script. So: + [[ -z $1 ]] && kill $$ + TMPF[CLEANUP_DONE]=1 +} -# -# cleanup(): Remove the TMPDIR and all files in it. -# -cleanup() -{ - [ $DEBUG -eq 1 ] && ${ECHO} "In cleanup()" - - rm -fr ${TMPDIR} +# debug helper function. Required to be not a 'function'! +showCommand() { + typeset CMD="${.sh.command}" + [[ -z ${CMD} ]] && return + print -n -u2 '\E[0;30;47m' + print -u2 -r -n "DEBUG: ${.sh.fun} '${CMD}'" + print '\E[0m' } - -# -# * * * MAIN * * * -# -# Description: -# This script assumes that the iPlanet Directory Server (iDS) is -# installed and that setup has been run. This script takes the -# iDS server from that point and sets up the infrastructure for -# LDAP Naming Services. After running this script, ldapaddent(1M) -# or some other tools can be used to populate data. - -# Initialize the variables that need to be set to NULL, or some -# other initial value before the rest of the functions can be called. -init - -# Parse command line arguments. -parse_arg $* -shift $? - -# Print extra line to separate from prompt. -${ECHO} " " - -# Either Load the user specified config file -# or prompt user for config info. -if [ -n "$INPUT_FILE" ] -then - load_config_file - INTERACTIVE=0 # Turns off prompts that occur later. - validate_info # Validate basic info in file. - chk_ids_version # Check iDS version for compatibility. -else - # Display BACKUP warning to user. - display_msg backup_server - get_confirm "Do you wish to continue with server setup (y/n/h)?" "n" "backup_help" - if [ $? -eq 0 ]; then # if No, cleanup and exit. - cleanup ; exit 1 - fi - - # Prompt for values. - prompt_config_info - display_summary # Allow user to modify results. - INTERACTIVE=1 # Insures future prompting. -fi - -# Modify slapd.oc.conf to ALLOW cn instead of REQUIRE. -modify_cn - -# Modify timelimit to user value. -[ $NEED_TIME -eq 1 ] && modify_timelimit - -# Modify sizelimit to user value. -[ $NEED_SIZE -eq 1 ] && modify_sizelimit - -# Modify the password storage scheme to support CRYPT. -if [ "$NEED_CRYPT" = "TRUE" ]; then - modify_pwd_crypt -fi - -# Update the schema (Attributes, Objectclass Definitions) -if [ ${SCHEMA_UPDATED} -eq 0 ]; then - update_schema_attr - update_schema_obj -fi - -# Add suffix together with its root entry (if needed) -add_suffix || -{ - cleanup - exit 1 +Man.addFunc MAIN '' '[+NAME?'"${PROG}"' - setup the infrastructure of a directory server to provide data and security required by the Solaris (or similar OS) LDAP Naming Services.] +[+DESCRIPTION?This script assumes that the Sun/Oracle Directory Server Enterprise Edition (DSEE), or Sun OpenDS or Forgerock OpenDJ is installed and that its setup has been run. This script takes the directory server from that point and sets up the infrastructure for LDAP Naming Services. After running this script, \bldapaddent\b(1M) or some other tools can be used to populate data.] +[h:help?Print this help and exit.] +[F:functions?Print out a list of names of all currently defined script functions and exit (see \btypeset +f\b).] +[H:usage]:[function?Show the usage info for the function with the given name if available and exit. (see also option \b-F\b).] +[T:trace]:[fname_list?A comma or whitespace separated list of function names, which should be traced when running this script. If the list consist of the single word "ALL" or "*" all known functions get traced. "MAIN" is the special word to enable the trace of the main script. Gets activated as soon as this option gets processed, so option order might be important.] +[d:debug?Enable really verbose debug info.] +[C:no-color?Do not use ANSI escape codes to color the output.] +[k:keep?Keep the files used to modify the DS. Files are enumerated and prefixed with the command to use for executing it so that one is able to do the same manually what this script did and thus may help to fine tune the setup of your DS. Actually it is recommended to run this script against a virgin DS and than re-use/adjust everything you need on your production system.] +[I:importpwp?If specified, the storage scheme for Password Policy Import will be changed in the same way as the storage scheme for the Default Password Policy. If not specified, the storage scheme for Password Policy Import stays untouched. This option gets ignored for non-OpenDS/OpenDJ directory servers.] +[i:in]:[file?Get setup info from the given file.] +[o:out]:[file?Generate a server configuration output file.] +[s:djSyntax?If given, the OpenDJ schema defintions will be used to complement the servers schema if needed, no matter which kind of server has been detected.] +[S:eeSyntax?If given, the Sun DSEE schema defintions will be used to complement the servers schema if needed, no matter which kind of server has been detected. +Wrt. Solaris it should not make a difference, whether OpenDJ or DSEE syntax is used. OpenDJ syntax is usually a little bit more precise.] +[t:timeout]:[seconds:=60?When this script creates an index, it also schedules a task to rebuild the index immediately. The \atimeout\a specifies, how long to wait for task completion. If the task gets not finished within the given time, the script treats that as an error and continues its work.] +[v:verbose?Verbose mode - makes the script a little bit more chatty.] +[+NOTES?This script should work for the following DS:]{ + [+?Sun DS 5.x] + [+?Sun DSEE 6.x/7.x] + [+?Oracle ODSEE 11g] + [+?Sun OpenDS 2.x] + [+?Forgerock OpenDJ 2.x] } +' -# Add base objects (if needed) -add_base_objects +TMPF[DEBUG]=0 TMPF[KEEP]=0 TMPF[FD]=4 -# Update the NisDomainObject. -# The Base DN might of just been created, so this MUST happen after -# the base objects have been added! -set_nisdomain +X="${ print ${Man.FUNC[MAIN]} ; }" +while getopts "${X}" option ; do + case "${option}" in + '?'|h) showUsage ${PROG} MAIN ; exit 0 ;; + F) typeset +f ; exit 0 ;; + T) if [[ ${OPTARG} == '*' || ${OPTARG} == 'ALL' ]]; then + typeset -ft ${ typeset +f ; } + elif [[ ${OPTARG} == 'MAIN' ]]; then + set -x + else + typeset -ft ${OPTARG//,/ } + fi + ;; + H) if [[ ${OPTARG%_t} != $OPTARG ]]; then + $OPTARG --man # self-defined types + else + showUsage "$OPTARG" "$OPTARG" # functions + fi + exit 0 + ;; + C) Log.COLORED=0 ;; + d) TMPF[DEBUG]=1 ;; + k) TMPF[KEEP]=1 ;; + I) INT[NEED_CRYPT_IMPORT]=1 ;; + i) TMP[IN]="${OPTARG}" ;; + o) TMP[OUT]="${OPTARG}" ;; + S) TMPF[SYNTAX]=2 ; print DSEE ;; + s) TMPF[SYNTAX]=1 ; print DJ ;; + t) is_numeric "${OPTARG}" && INT[TASK_TIMEOUT]=${OPTARG} ;; + v) Log.VERBOSE=1 ; TMPF[FD]=1 ;; + *) Log.fatal '**Internal ERROR: Supported option missing handler' + exit 1 + ;; + esac +done +X=$(( OPTIND - 1 )) +shift $X +(( TMPF[DEBUG] )) && Man.listVars -# Add top level classes (new containers) -add_new_containers +unset LC_ALL LC_LANG +export LC_CTYPE=C # avoid any surprises -# Add common nismaps. -add_auto_maps - -# Modify top ACI. -modify_top_aci - -# Add Access Control Information for VLV. -add_vlv_aci - -# if Proxy needed, Add Proxy Agent and give read permission for password. -if [ $NEED_PROXY -eq 1 ]; then - add_proxyagent - if [ "$LDAP_ENABLE_SHADOW_UPDATE" != "TRUE" ]; then - allow_proxy_read_pw - fi +# Create TMP[DIR] +TMP[DIR]=${ mktemp -d /tmp/${PROG}.XXXXX ; } +if [[ -z ${TMP[DIR]} ]]; then + Log.fatal 'Unable to create a safe temporary directory' + exit 1 fi - -# If admin needed for shadow update, Add the administrator identity and -# give read/write permission for shadow, and deny all others read/write -# access to it. -if [ $NEED_ADMIN -eq 1 ]; then - add_admin - allow_admin_read_write_shadow - # deny non-admin access to shadow data - deny_non_admin_shadow_access +trap cleanup EXIT 1 2 3 6 15 # and register to cleanup on exit/signal +if (( TMPF[DEBUG] )); then + trap 'typeset _CMD_="${.sh.command}" + print -u2 -n "\E[0;30;47m" + print -u2 -n -r "${.sh.fun:-MAIN}(): ${_CMD_}" + print -u2 "\E[0m" + ' TMPF[DEBUG] fi -if [ $GSSAPI_ENABLE -eq 1 ]; then - add_id_mapping_rules - # do not modify ACI if "sasl/GSSAPI" and "self" are not selected - if [ "$LDAP_CRED_LEVEL" = "self" -a "$LDAP_AUTHMETHOD" = "sasl/GSSAPI" ]; then - modify_userpassword_acl_for_gssapi - else - ${ECHO} " ACL for GSSAPI was not set because of incompatibility in profile." - fi -fi +# Prevent new files from being read by group or others +umask 077 -# If use host principal for shadow update, give read/write permission for -# shadow, and deny all others' read/write access to it. -if [ $NEED_HOSTACL -eq 1 ]; then - allow_host_read_write_shadow - # deny non-host access to shadow data - deny_non_host_shadow_access -fi +doMain +X=$? +cleanup NOKILL -# Generate client profile and add it to the server. -add_profile +exit ${X} -# Add Indexes to improve Search Performance. -add_eq_indexes -add_sub_indexes -add_vlv_indexes - -# Display setup complete message -display_msg setup_complete - -# Display VLV index commands to be executed on server. -display_vlv_cmds - -# Create config file if requested. -[ -n "$OUTPUT_FILE" ] && create_config_file - -# Removed the TMPDIR and all files in it. -cleanup - -exit 0 -# end of MAIN. +# vim:ts=4 filetype=sh +# :syntax sync fromstart